Merge tag 'vulkan-sdk-1.3.268.0'

Bug:b/319117470

Change-Id: I4bc29354d96067c265a8bd5123d6b44f5c680029
diff --git a/.bazelrc b/.bazelrc
new file mode 100644
index 0000000..ff6db81
--- /dev/null
+++ b/.bazelrc
@@ -0,0 +1,4 @@
+build --enable_platform_specific_config
+build:linux --cxxopt=-std=c++17
+build:macos --cxxopt=-std=c++17
+build:windows --cxxopt=/std:c++17
diff --git a/.bazelversion b/.bazelversion
new file mode 100644
index 0000000..0062ac9
--- /dev/null
+++ b/.bazelversion
@@ -0,0 +1 @@
+5.0.0
diff --git a/kokoro/windows-msvc-2015-release/continuous.cfg b/.github/dependabot.yml
similarity index 62%
rename from kokoro/windows-msvc-2015-release/continuous.cfg
rename to .github/dependabot.yml
index 3e47e52..dca857a 100644
--- a/kokoro/windows-msvc-2015-release/continuous.cfg
+++ b/.github/dependabot.yml
@@ -1,10 +1,10 @@
-# Copyright (c) 2018 Google LLC.
+# Copyright 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
+#     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 +12,14 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Continuous build configuration.
-build_file: "SPIRV-Tools/kokoro/windows-msvc-2015-release/build.bat"
+version: 2
+updates:
+  - package-ecosystem: github-actions
+    directory: /
+    schedule:
+      interval: daily
+    groups:
+      github-actions:
+        patterns:
+          - "*"
+    open-pull-requests-limit: 3
diff --git a/.github/workflows/autoroll.yml b/.github/workflows/autoroll.yml
new file mode 100644
index 0000000..ab38975
--- /dev/null
+++ b/.github/workflows/autoroll.yml
@@ -0,0 +1,56 @@
+name: Update dependencies
+permissions:
+  contents: read
+
+on:
+  schedule:
+    - cron: '0 2 * * *'
+  workflow_dispatch:
+
+jobs:
+  update-dependencies:
+    permissions:
+      contents: write
+      pull-requests: write
+    name: Update dependencies
+    runs-on: ubuntu-latest
+
+    steps:
+      - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
+
+      # Checkout the depot tools they are needed by roll_deps.sh
+      - name: Checkout depot tools
+        run: git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
+
+      - name: Update PATH
+        run: echo "$(pwd)/depot_tools" >> $GITHUB_PATH
+
+      - name: Download dependencies
+        run: python3 utils/git-sync-deps
+
+      - name: Setup git user information
+        run: |
+          git config user.name "GitHub Actions[bot]"
+          git config user.email "<>"
+          git checkout -b roll_deps
+
+      - name: Update dependencies
+        run: |
+          utils/roll_deps.sh
+          if [[ `git diff HEAD..origin/main --name-only | wc -l` == 0 ]]; then
+            echo "changed=false" >> $GITHUB_OUTPUT
+          else
+            echo "changed=true" >> $GITHUB_OUTPUT
+          fi
+        id: update_dependencies
+      - name: Push changes and create PR
+        if: steps.update_dependencies.outputs.changed == 'true'
+        run: |
+          git push --force --set-upstream origin roll_deps
+          # Create a PR. If it aready exists, the command fails, so ignore the return code.
+          gh pr create --base main -f || true 
+          # Add the 'kokoro:run' label so that the kokoro tests will be run.
+          gh pr edit --add-label 'kokoro:run'
+          gh pr merge --auto --squash
+        env:
+          GITHUB_TOKEN: ${{ github.token }}
diff --git a/.github/workflows/bazel.yml b/.github/workflows/bazel.yml
new file mode 100644
index 0000000..7ab0f0e
--- /dev/null
+++ b/.github/workflows/bazel.yml
@@ -0,0 +1,34 @@
+name: Build and Test with Bazel
+permissions:
+  contents: read
+
+on:
+  push:
+    branches:
+      - 'main'
+  pull_request:
+
+jobs:
+  build:
+    timeout-minutes: 120
+    strategy:
+      matrix:
+        os: [ubuntu-latest, macos-latest, windows-2019]
+
+    runs-on: ${{matrix.os}}
+
+    steps:
+      - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
+        with:
+          fetch-depth: '0'
+      - name: Download dependencies
+        run: python3 utils/git-sync-deps
+      - name: Mount Bazel cache
+        uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
+        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 //...
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..ac8bade
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,24 @@
+name: Create a release branch from release tag
+permissions:
+  contents: write
+
+on:
+  push:
+    tags:
+      - 'v[0-9]+.[0-9]+'
+      - '!v[0-9]+.[0-9]+.rc*'
+
+jobs:
+  prepare-release-job:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
+      - name: Prepare CHANGELOG for version
+        run: |
+          python utils/generate_changelog.py CHANGES "${{ github.ref_name }}" VERSION_CHANGELOG
+      - name: Create release
+        run: |
+          gh release create -t "Release ${{ github.ref_name }}" -F VERSION_CHANGELOG "${{ github.ref_name }}"
+        env:
+          GITHUB_TOKEN: ${{ github.token }}
+
diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml
new file mode 100644
index 0000000..b45d091
--- /dev/null
+++ b/.github/workflows/scorecard.yml
@@ -0,0 +1,53 @@
+name: Scorecard supply-chain security
+on:
+  # For Branch-Protection check. Only the default branch is supported. See
+  # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection
+  branch_protection_rule:
+  # To guarantee Maintained check is occasionally updated. See
+  # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained
+  schedule:
+    - cron: '36 17 * * 5'
+  push:
+    branches: [ "main" ]
+
+# Declare default permissions as read only.
+permissions: read-all
+
+jobs:
+  analysis:
+    name: Scorecard analysis
+    runs-on: ubuntu-latest
+    permissions:
+      security-events: write # to upload the results to code-scanning dashboard
+      id-token: write # to publish results and get a badge
+
+    steps:
+      - name: "Checkout code"
+        uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0
+        with:
+          persist-credentials: false
+
+      - name: "Run analysis"
+        uses: ossf/scorecard-action@e38b1902ae4f44df626f11ba0734b14fb91f8f86 # v2.1.2
+        with:
+          results_file: results.sarif
+          results_format: sarif
+          # To enable Branch-Protection uncomment the `repo_token` line below
+          # To create the Fine-grained PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-fine-grained-pat-optional.
+          # repo_token: ${{ secrets.SCORECARD_TOKEN }}
+          publish_results: true # allows the repo to include the Scorecard badge
+
+      # 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@3cea5372237819ed00197afe530f5a7ea3e805c8 # v3.1.0
+        with:
+          name: SARIF file
+          path: results.sarif
+          retention-days: 5
+
+      # Upload the results to GitHub's code scanning dashboard.
+      - name: "Upload to code-scanning"
+        uses: github/codeql-action/upload-sarif@17573ee1cc1b9d061760f3a006fc4aac4f944fd5 # v2.2.4
+        with:
+          sarif_file: results.sarif
diff --git a/.github/workflows/wasm.yml b/.github/workflows/wasm.yml
index d9a9c5c..f031e6c 100644
--- a/.github/workflows/wasm.yml
+++ b/.github/workflows/wasm.yml
@@ -1,14 +1,18 @@
 name: Wasm Build
+permissions:
+  contents: read
 
-on: [ push, pull_request ]
+on: [push, pull_request]
 
 jobs:
   build:
     runs-on: ubuntu-latest
 
     steps:
-      - uses: actions/checkout@v2
+      - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
+        with:
+          fetch-depth: '0'
       - name: Build web
-        run: docker-compose up
+        run: docker-compose -f source/wasm/docker-compose.yml --project-directory . up
       - name: Run tests
         run: node test/wasm/test.js
diff --git a/.gitignore b/.gitignore
index ec709ba..d9c6a1a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,7 @@
 compile_commands.json
 /build*/
 /buildtools/
+/external/abseil_cpp/
 /external/googletest
 /external/SPIRV-Headers
 /external/spirv-headers
diff --git a/Android.mk b/Android.mk
index 80c61b0..afa0403 100644
--- a/Android.mk
+++ b/Android.mk
@@ -71,6 +71,7 @@
 		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_type.cpp
@@ -78,6 +79,7 @@
 SPVTOOLS_OPT_SRC_FILES := \
 		source/opt/aggressive_dead_code_elim_pass.cpp \
 		source/opt/amd_ext_to_khr.cpp \
+		source/opt/analyze_live_input_pass.cpp \
 		source/opt/basic_block.cpp \
 		source/opt/block_merge_pass.cpp \
 		source/opt/block_merge_util.cpp \
@@ -109,8 +111,9 @@
 		source/opt/eliminate_dead_constant_pass.cpp \
 		source/opt/eliminate_dead_functions_pass.cpp \
 		source/opt/eliminate_dead_functions_util.cpp \
-		source/opt/eliminate_dead_input_components_pass.cpp \
+		source/opt/eliminate_dead_io_components_pass.cpp \
 		source/opt/eliminate_dead_members_pass.cpp \
+		source/opt/eliminate_dead_output_stores_pass.cpp \
 		source/opt/feature_manager.cpp \
 		source/opt/fix_func_call_arguments.cpp \
 		source/opt/fix_storage_class.cpp \
@@ -133,9 +136,11 @@
 		source/opt/instrument_pass.cpp \
 		source/opt/interface_var_sroa.cpp \
 		source/opt/interp_fixup_pass.cpp \
+		source/opt/invocation_interlock_placement_pass.cpp \
 		source/opt/ir_context.cpp \
 		source/opt/ir_loader.cpp \
 		source/opt/licm_pass.cpp \
+		source/opt/liveness.cpp \
 		source/opt/local_access_chain_convert_pass.cpp \
 		source/opt/local_redundancy_elimination.cpp \
 		source/opt/local_single_block_elim_pass.cpp \
@@ -178,6 +183,8 @@
 		source/opt/strip_debug_info_pass.cpp \
 		source/opt/strip_nonsemantic_info_pass.cpp \
 		source/opt/struct_cfg_analysis.cpp \
+		source/opt/switch_descriptorset_pass.cpp \
+		source/opt/trim_capabilities_pass.cpp \
 		source/opt/type_manager.cpp \
 		source/opt/types.cpp \
 		source/opt/unify_const_pass.cpp \
@@ -217,7 +224,8 @@
 		                --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
+		                --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
@@ -291,7 +299,8 @@
 		                --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
+		                --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.
@@ -334,7 +343,7 @@
 		$(SPVTOOLS_OUT_PATH)
 LOCAL_EXPORT_C_INCLUDES := \
 		$(LOCAL_PATH)/include
-LOCAL_CXXFLAGS:=-std=c++11 -fno-exceptions -fno-rtti -Werror
+LOCAL_CXXFLAGS:=-std=c++17 -fno-exceptions -fno-rtti -Werror
 LOCAL_SRC_FILES:= $(SPVTOOLS_SRC_FILES)
 include $(BUILD_STATIC_LIBRARY)
 
@@ -345,7 +354,7 @@
 		$(LOCAL_PATH)/source \
 		$(SPVHEADERS_LOCAL_PATH)/include \
 		$(SPVTOOLS_OUT_PATH)
-LOCAL_CXXFLAGS:=-std=c++11 -fno-exceptions -fno-rtti -Werror
+LOCAL_CXXFLAGS:=-std=c++17 -fno-exceptions -fno-rtti -Werror
 LOCAL_STATIC_LIBRARIES:=SPIRV-Tools
 LOCAL_SRC_FILES:= $(SPVTOOLS_OPT_SRC_FILES)
 include $(BUILD_STATIC_LIBRARY)
diff --git a/BUILD.bazel b/BUILD.bazel
index 35dfd66..a234277 100644
--- a/BUILD.bazel
+++ b/BUILD.bazel
@@ -1,27 +1,24 @@
 load(
     ":build_defs.bzl",
+    "CLDEBUGINFO100_GRAMMAR_JSON_FILE",
     "COMMON_COPTS",
     "DEBUGINFO_GRAMMAR_JSON_FILE",
-    "CLDEBUGINFO100_GRAMMAR_JSON_FILE",
     "SHDEBUGINFO100_GRAMMAR_JSON_FILE",
     "TEST_COPTS",
-    "base_test",
     "generate_core_tables",
     "generate_enum_string_mapping",
     "generate_extinst_lang_headers",
     "generate_glsl_tables",
     "generate_opencl_tables",
     "generate_vendor_tables",
-    "link_test",
-    "lint_test",
-    "opt_test",
-    "reduce_test",
-    "util_test",
-    "val_test",
+    "incompatible_with",
 )
 
 package(
     default_visibility = ["//visibility:private"],
+    features = [
+        "layering_check",
+    ],
 )
 
 licenses(["notice"])
@@ -41,35 +38,50 @@
     srcs = ["utils/generate_language_headers.py"],
 )
 
-generate_core_tables("unified1")
+generate_core_tables(version = "unified1")
 
-generate_enum_string_mapping("unified1")
+generate_enum_string_mapping(version = "unified1")
 
-generate_opencl_tables("unified1")
+generate_opencl_tables(version = "unified1")
 
-generate_glsl_tables("unified1")
+generate_glsl_tables(version = "unified1")
 
-generate_vendor_tables("spv-amd-shader-explicit-vertex-parameter")
+generate_vendor_tables(extension = "spv-amd-shader-explicit-vertex-parameter")
 
-generate_vendor_tables("spv-amd-shader-trinary-minmax")
+generate_vendor_tables(extension = "spv-amd-shader-trinary-minmax")
 
-generate_vendor_tables("spv-amd-gcn-shader")
+generate_vendor_tables(extension = "spv-amd-gcn-shader")
 
-generate_vendor_tables("spv-amd-shader-ballot")
+generate_vendor_tables(extension = "spv-amd-shader-ballot")
 
-generate_vendor_tables("debuginfo")
+generate_vendor_tables(extension = "debuginfo")
 
-generate_vendor_tables("opencl.debuginfo.100", "CLDEBUG100_")
+generate_vendor_tables(extension = "nonsemantic.clspvreflection")
 
-generate_vendor_tables("nonsemantic.shader.debuginfo.100", "SHDEBUG100_")
+generate_vendor_tables(
+    extension = "opencl.debuginfo.100",
+    operand_kind_prefix = "CLDEBUG100_",
+)
 
-generate_vendor_tables("nonsemantic.clspvreflection")
+generate_vendor_tables(
+    extension = "nonsemantic.shader.debuginfo.100",
+    operand_kind_prefix = "SHDEBUG100_",
+)
 
-generate_extinst_lang_headers("DebugInfo", DEBUGINFO_GRAMMAR_JSON_FILE)
+generate_extinst_lang_headers(
+    name = "DebugInfo",
+    grammar = DEBUGINFO_GRAMMAR_JSON_FILE,
+)
 
-generate_extinst_lang_headers("OpenCLDebugInfo100", CLDEBUGINFO100_GRAMMAR_JSON_FILE)
+generate_extinst_lang_headers(
+    name = "OpenCLDebugInfo100",
+    grammar = CLDEBUGINFO100_GRAMMAR_JSON_FILE,
+)
 
-generate_extinst_lang_headers("NonSemanticShaderDebugInfo100", SHDEBUGINFO100_GRAMMAR_JSON_FILE)
+generate_extinst_lang_headers(
+    name = "NonSemanticShaderDebugInfo100",
+    grammar = SHDEBUGINFO100_GRAMMAR_JSON_FILE,
+)
 
 py_binary(
     name = "generate_registry_tables",
@@ -77,12 +89,12 @@
 )
 
 genrule(
-    name = "gen_registry_tables",
+    name = "generators_inc",
     srcs = ["@spirv_headers//:spirv_xml_registry"],
     outs = ["generators.inc"],
-    cmd = "$(location generate_registry_tables) --xml=$(location @spirv_headers//:spirv_xml_registry) --generator-output=$(location generators.inc)",
-    cmd_bat = "$(location //:generate_registry_tables) --xml=$(location @spirv_headers//:spirv_xml_registry) --generator-output=$(location generators.inc)",
-    exec_tools = [":generate_registry_tables"],
+    cmd = "$(location :generate_registry_tables) --xml=$(location @spirv_headers//:spirv_xml_registry) --generator-output=$(location generators.inc)",
+    cmd_bat = "$(location :generate_registry_tables) --xml=$(location @spirv_headers//:spirv_xml_registry) --generator-output=$(location generators.inc)",
+    tools = [":generate_registry_tables"],
 )
 
 py_binary(
@@ -91,120 +103,103 @@
 )
 
 genrule(
-    name = "gen_build_version",
+    name = "build_version_inc",
     srcs = ["CHANGES"],
     outs = ["build-version.inc"],
-    cmd = "SOURCE_DATE_EPOCH=0 $(location update_build_version) $(location CHANGES) $(location build-version.inc)",
-    cmd_bat = "set SOURCE_DATE_EPOCH=0  && $(location //:update_build_version) $(location CHANGES) $(location build-version.inc)",
-    exec_tools = [":update_build_version"],
+    cmd = "SOURCE_DATE_EPOCH=0 $(location :update_build_version) $(location CHANGES) $(location build-version.inc)",
+    cmd_bat = "set SOURCE_DATE_EPOCH=0  && $(location :update_build_version) $(location CHANGES) $(location build-version.inc)",
+    local = True,
+    tools = [":update_build_version"],
 )
 
 # Libraries
 
 cc_library(
-    name = "generated_headers",
+    name = "spirv_tools",
     hdrs = [
-        ":gen_build_version",
+        "include/spirv-tools/libspirv.h",
+        "include/spirv-tools/libspirv.hpp",
+    ],
+    copts = COMMON_COPTS,
+    includes = ["include"],
+    linkstatic = 1,
+    visibility = ["//visibility:public"],
+    deps = [
+        ":spirv_tools_internal",
+    ],
+)
+
+cc_library(
+    name = "spirv_tools_internal",
+    srcs = glob([
+        "source/*.cpp",
+        "source/util/*.cpp",
+        "source/val/*.cpp",
+    ]) + [
+        ":build_version_inc",
         ":gen_core_tables_unified1",
         ":gen_enum_string_mapping",
         ":gen_extinst_lang_headers_DebugInfo",
-        ":gen_extinst_lang_headers_OpenCLDebugInfo100",
         ":gen_extinst_lang_headers_NonSemanticShaderDebugInfo100",
+        ":gen_extinst_lang_headers_OpenCLDebugInfo100",
         ":gen_glsl_tables_unified1",
         ":gen_opencl_tables_unified1",
-        ":gen_registry_tables",
         ":gen_vendor_tables_debuginfo",
         ":gen_vendor_tables_nonsemantic_clspvreflection",
-        ":gen_vendor_tables_opencl_debuginfo_100",
         ":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",
     ],
-    copts = COMMON_COPTS,
-)
-
-cc_library(
-    name = "spirv_tools_headers",
-    hdrs = glob([
+    hdrs = [
         "include/spirv-tools/libspirv.h",
         "include/spirv-tools/libspirv.hpp",
+        ":gen_extinst_lang_headers_DebugInfo",
+        ":gen_extinst_lang_headers_NonSemanticShaderDebugInfo100",
+        ":gen_extinst_lang_headers_OpenCLDebugInfo100",
+    ] + glob([
         "source/*.h",
         "source/util/*.h",
         "source/val/*.h",
     ]),
     copts = COMMON_COPTS,
-    includes = ["source"],
-    deps = [
-        "@spirv_headers//:spirv_c_headers",
-    ],
-)
-
-cc_library(
-    name = "spirv_tools",
-    srcs = glob([
-        "source/*.cpp",
-        "source/util/*.cpp",
-        "source/val/*.cpp",
-    ]),
-    hdrs = [
-        "include/spirv-tools/libspirv.h",
-        "include/spirv-tools/libspirv.hpp",
-    ],
-    copts = COMMON_COPTS + select({
-        "@bazel_tools//src/conditions:windows": [""],
-        "//conditions:default": ["-Wno-implicit-fallthrough"],
-    }),
     includes = ["include"],
-    linkstatic = 1,
-    visibility = ["//visibility:public"],
     deps = [
-        ":generated_headers",
-        ":spirv_tools_headers",
-        "@spirv_headers//:spirv_c_headers",
         "@spirv_headers//:spirv_common_headers",
+        "@spirv_headers//:spirv_cpp11_headers",
     ],
 )
 
 cc_library(
-    name = "spirv_tools_comp",
-    srcs = glob([
-        "source/comp/*.cpp",
-        "source/comp/*.h",
-    ]),
-    copts = COMMON_COPTS,
-    linkstatic = 1,
-    visibility = ["//visibility:public"],
-    deps = [
-        ":generated_headers",
-        ":spirv_tools",
-        ":spirv_tools_headers",
-        "@spirv_headers//:spirv_common_headers",
-    ],
-)
-
-cc_library(
-    name = "spirv_tools_opt_headers",
-    hdrs = glob(["source/opt/*.h"]),
-    copts = COMMON_COPTS,
-)
-
-cc_library(
     name = "spirv_tools_opt",
-    srcs = glob(["source/opt/*.cpp"]),
     hdrs = [
         "include/spirv-tools/instrument.hpp",
         "include/spirv-tools/optimizer.hpp",
     ],
     copts = COMMON_COPTS,
-    includes = ["include"],
     linkstatic = 1,
     visibility = ["//visibility:public"],
     deps = [
         ":spirv_tools",
-        ":spirv_tools_headers",
-        ":spirv_tools_opt_headers",
+        ":spirv_tools_opt_internal",
+    ],
+)
+
+cc_library(
+    name = "spirv_tools_opt_internal",
+    srcs = glob(["source/opt/*.cpp"]) + [
+        ":gen_vendor_tables_spv_amd_shader_ballot",
+    ],
+    hdrs = glob(["source/opt/*.h"]) + [
+        "include/spirv-tools/instrument.hpp",
+        "include/spirv-tools/optimizer.hpp",
+    ],
+    copts = COMMON_COPTS,
+    deps = [
+        ":spirv_tools_internal",
         "@spirv_headers//:spirv_common_headers",
     ],
 )
@@ -214,11 +209,9 @@
     srcs = glob(["source/reduce/*.cpp"]),
     hdrs = glob(["source/reduce/*.h"]),
     copts = COMMON_COPTS,
-    linkstatic = 1,
-    visibility = ["//visibility:public"],
     deps = [
-        ":spirv_tools",
-        ":spirv_tools_opt",
+        ":spirv_tools_internal",
+        ":spirv_tools_opt_internal",
     ],
 )
 
@@ -230,21 +223,38 @@
     linkstatic = 1,
     visibility = ["//visibility:public"],
     deps = [
-        ":spirv_tools",
-        ":spirv_tools_opt",
+        ":spirv_tools_internal",
+        ":spirv_tools_opt_internal",
+    ],
+)
+
+cc_library(
+    name = "spirv_tools_lint_internal",
+    srcs = glob([
+        "source/lint/*.cpp",
+        "source/lint/*.h",
+    ]),
+    hdrs = ["include/spirv-tools/linter.hpp"] + glob([
+        "source/lint/*.h",
+    ]),
+    copts = COMMON_COPTS,
+    includes = ["include"],
+    deps = [
+        ":spirv_tools_internal",
+        ":spirv_tools_opt_internal",
     ],
 )
 
 cc_library(
     name = "spirv_tools_lint",
-    srcs = glob(["source/lint/*.cpp", "source/lint/*.h"]),
     hdrs = ["include/spirv-tools/linter.hpp"],
     copts = COMMON_COPTS,
+    includes = ["include"],
     linkstatic = 1,
     visibility = ["//visibility:public"],
     deps = [
         ":spirv_tools",
-        ":spirv_tools_opt",
+        ":spirv_tools_lint_internal",
     ],
 )
 
@@ -253,23 +263,28 @@
     srcs = glob(["tools/util/*.cpp"]),
     hdrs = glob(["tools/util/*.h"]),
     copts = COMMON_COPTS,
-    linkstatic = 1,
-    visibility = ["//visibility:public"],
     deps = [":spirv_tools"],
 )
 
+cc_library(
+    name = "tools_io",
+    hdrs = ["tools/io.h"],
+    copts = COMMON_COPTS,
+)
+
 # Tools
 
 cc_binary(
     name = "spirv-as",
     srcs = [
         "tools/as/as.cpp",
-        "tools/io.h",
     ],
     copts = COMMON_COPTS,
     visibility = ["//visibility:public"],
     deps = [
-        ":spirv_tools",
+        ":spirv_tools_internal",
+        ":tools_io",
+        ":tools_util",
     ],
 )
 
@@ -277,25 +292,44 @@
     name = "spirv-dis",
     srcs = [
         "tools/dis/dis.cpp",
-        "tools/io.h",
     ],
     copts = COMMON_COPTS,
     visibility = ["//visibility:public"],
     deps = [
         ":spirv_tools",
+        ":tools_io",
+        ":tools_util",
+    ],
+)
+
+cc_binary(
+    name = "spirv-objdump",
+    srcs = [
+        "tools/objdump/extract_source.cpp",
+        "tools/objdump/extract_source.h",
+        "tools/objdump/objdump.cpp",
+    ],
+    copts = COMMON_COPTS,
+    visibility = ["//visibility:public"],
+    deps = [
+        ":spirv_tools_internal",
+        ":spirv_tools_opt_internal",
+        ":tools_io",
+        ":tools_util",
+        "@spirv_headers//:spirv_cpp_headers",
     ],
 )
 
 cc_binary(
     name = "spirv-val",
     srcs = [
-        "tools/io.h",
         "tools/val/val.cpp",
     ],
     copts = COMMON_COPTS,
     visibility = ["//visibility:public"],
     deps = [
-        ":spirv_tools",
+        ":spirv_tools_internal",
+        ":tools_io",
         ":tools_util",
     ],
 )
@@ -303,14 +337,14 @@
 cc_binary(
     name = "spirv-opt",
     srcs = [
-        "tools/io.h",
         "tools/opt/opt.cpp",
     ],
     copts = COMMON_COPTS,
     visibility = ["//visibility:public"],
     deps = [
-        ":spirv_tools",
-        ":spirv_tools_opt",
+        ":spirv_tools_internal",
+        ":spirv_tools_opt_internal",
+        ":tools_io",
         ":tools_util",
     ],
 )
@@ -318,15 +352,15 @@
 cc_binary(
     name = "spirv-reduce",
     srcs = [
-        "tools/io.h",
         "tools/reduce/reduce.cpp",
     ],
     copts = COMMON_COPTS,
     visibility = ["//visibility:public"],
     deps = [
-        ":spirv_tools",
-        ":spirv_tools_opt",
+        ":spirv_tools_internal",
+        ":spirv_tools_opt_internal",
         ":spirv_tools_reduce",
+        ":tools_io",
         ":tools_util",
     ],
 )
@@ -334,28 +368,29 @@
 cc_binary(
     name = "spirv-link",
     srcs = [
-        "tools/io.h",
         "tools/link/linker.cpp",
     ],
     copts = COMMON_COPTS,
     visibility = ["//visibility:public"],
     deps = [
-        ":spirv_tools",
+        ":spirv_tools_internal",
         ":spirv_tools_link",
+        ":tools_io",
+        ":tools_util",
     ],
 )
 
 cc_binary(
     name = "spirv-lint",
     srcs = [
-        "tools/io.h",
         "tools/lint/lint.cpp",
     ],
     copts = COMMON_COPTS,
     visibility = ["//visibility:public"],
     deps = [
-        ":spirv_tools",
         ":spirv_tools_lint",
+        ":spirv_tools_opt_internal",
+        ":tools_io",
         ":tools_util",
     ],
 )
@@ -366,50 +401,157 @@
         "tools/cfg/bin_to_dot.cpp",
         "tools/cfg/bin_to_dot.h",
         "tools/cfg/cfg.cpp",
-        "tools/io.h",
     ],
     copts = COMMON_COPTS,
     visibility = ["//visibility:public"],
-    deps = [":spirv_tools"],
+    deps = [
+        ":spirv_tools_internal",
+        ":tools_io",
+        ":tools_util",
+    ],
 )
 
 # Unit tests
 
 cc_library(
-    name = "test_common",
+    name = "test_lib",
     testonly = 1,
     srcs = [
-        "test/test_fixture.h",
         "test/unit_spirv.cpp",
+    ],
+    hdrs = [
+        "test/test_fixture.h",
         "test/unit_spirv.h",
     ],
-    compatible_with = [],
     copts = TEST_COPTS,
-    includes = ["test"],
-    linkstatic = 1,
     deps = [
-        ":spirv_tools",
+        ":spirv_tools_internal",
         "@com_google_googletest//:gtest",
     ],
 )
 
-cc_library(
-    name = "link_test_common",
-    testonly = 1,
-    srcs = ["test/link/linker_fixture.h"],
-    compatible_with = [],
+# PCH (precompiled header) tests only work when using CMake and MSVC on Windows,
+# so they will be skipped in the Bazel builds.
+
+[cc_test(
+    name = "base_{testcase}_test".format(testcase = f[len("test/"):-len("_test.cpp")]),
+    size = "small",
+    srcs = [f],
+    copts = TEST_COPTS + ["-DTESTING"],
+    linkstatic = 1,
+    target_compatible_with = {
+        "test/timer_test.cpp": incompatible_with(["@bazel_tools//src/conditions:windows"]),
+    }.get(f, []),
+    deps = [
+        "tools_util",
+        ":spirv_tools_internal",
+        ":test_lib",
+        "@com_google_googletest//:gtest",
+        "@com_google_googletest//:gtest_main",
+    ],
+) for f in glob(
+    [
+        "test/*_test.cpp",
+        "test/tools/*_test.cpp",
+    ],
+    exclude = [
+        "test/cpp_interface_test.cpp",
+        "test/log_test.cpp",
+        "test/pch_test.cpp",
+    ],
+)]
+
+cc_test(
+    name = "base_cpp_interface_test",
+    size = "small",
+    srcs = ["test/cpp_interface_test.cpp"],
+    linkstatic = 1,
+    deps = [
+        ":spirv_tools_opt_internal",
+        "@com_google_googletest//:gtest",
+        "@com_google_googletest//:gtest_main",
+        "@spirv_headers//:spirv_cpp11_headers",
+    ],
+)
+
+cc_test(
+    name = "base_ilist_test",
+    size = "small",
+    srcs = ["test/util/ilist_test.cpp"],
     copts = TEST_COPTS,
     linkstatic = 1,
     deps = [
-        ":spirv_tools_link",
-        ":test_common",
+        ":spirv_tools_internal",
+        "@com_google_googletest//:gtest",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
+cc_test(
+    name = "base_log_test",
+    size = "small",
+    srcs = ["test/log_test.cpp"],
+    copts = TEST_COPTS,
+    linkstatic = 1,
+    deps = [
+        ":spirv_tools_opt_internal",
+        "@com_google_googletest//:gtest",
+        "@com_google_googletest//:gtest_main",
     ],
 )
 
 cc_library(
-    name = "opt_test_common",
+    name = "link_test_lib",
     testonly = 1,
-    srcs = ["test/opt/pass_utils.cpp"],
+    hdrs = ["test/link/linker_fixture.h"],
+    copts = TEST_COPTS,
+    deps = [
+        ":spirv_tools_internal",
+        ":spirv_tools_link",
+        ":test_lib",
+        "@com_google_effcee//:effcee",
+        "@com_googlesource_code_re2//:re2",
+    ],
+)
+
+[cc_test(
+    name = "link_{testcase}_test".format(testcase = f[len("test/link/"):-len("_test.cpp")]),
+    size = "small",
+    srcs = [f],
+    copts = TEST_COPTS,
+    linkstatic = 1,
+    deps = [
+        ":link_test_lib",
+        "@com_google_googletest//:gtest",
+        "@com_google_googletest//:gtest_main",
+    ],
+) for f in glob(
+    ["test/link/*_test.cpp"],
+)]
+
+[cc_test(
+    name = "lint_{testcase}_test".format(testcase = f[len("test/lint/"):-len("_test.cpp")]),
+    size = "small",
+    srcs = [f],
+    copts = TEST_COPTS,
+    linkstatic = 1,
+    deps = [
+        ":spirv_tools",
+        ":spirv_tools_lint_internal",
+        ":spirv_tools_opt_internal",
+        "@com_google_googletest//:gtest",
+        "@com_google_googletest//:gtest_main",
+    ],
+) for f in glob(
+    ["test/lint/*_test.cpp"],
+)]
+
+cc_library(
+    name = "opt_test_lib",
+    testonly = 1,
+    srcs = [
+        "test/opt/pass_utils.cpp",
+    ],
     hdrs = [
         "test/opt/assembly_builder.h",
         "test/opt/function_utils.h",
@@ -417,143 +559,181 @@
         "test/opt/pass_fixture.h",
         "test/opt/pass_utils.h",
     ],
-    compatible_with = [],
+    copts = TEST_COPTS,
+    deps = [
+        ":spirv_tools_internal",
+        ":spirv_tools_opt_internal",
+        "@com_google_effcee//:effcee",
+        "@com_google_googletest//:gtest",
+    ],
+)
+
+[cc_test(
+    name = "opt_{testcase}_test".format(testcase = f[len("test/opt/"):-len("_test.cpp")]),
+    size = "small",
+    srcs = [f],
     copts = TEST_COPTS,
     linkstatic = 1,
     deps = [
-        ":spirv_tools_opt",
-        ":test_common",
+        ":opt_test_lib",
+        ":spirv_tools_internal",
+        ":spirv_tools_opt_internal",
+        ":test_lib",
+        "@com_google_effcee//:effcee",
+        "@com_google_googletest//:gtest",
+        "@com_google_googletest//:gtest_main",
     ],
-)
+) for f in glob(["test/opt/*_test.cpp"])]
 
-cc_library(
-    name = "reduce_test_common",
-    testonly = 1,
-    srcs = [
-        "test/reduce/reduce_test_util.cpp",
-        "tools/io.h",
-    ],
-    hdrs = ["test/reduce/reduce_test_util.h"],
-    compatible_with = [],
+[cc_test(
+    name = "opt_dom_tree_{testcase}_test".format(testcase = f[len("test/opt/dominator_tree/"):-len(".cpp")]),
+    size = "small",
+    srcs = [f],
     copts = TEST_COPTS,
     linkstatic = 1,
     deps = [
-        ":spirv_tools_reduce",
-        ":test_common",
+        ":opt_test_lib",
+        ":spirv_tools_opt_internal",
+        "@com_google_googletest//:gtest",
+        "@com_google_googletest//:gtest_main",
     ],
-)
-
-cc_library(
-    name = "val_test_common",
-    testonly = 1,
-    srcs = [
-        "test/val/val_code_generator.cpp",
-        "test/val/val_fixtures.h",
-    ],
-    hdrs = [
-        "test/val/val_code_generator.h",
-    ],
-    compatible_with = [],
-    copts = TEST_COPTS,
-    linkstatic = 1,
-    deps = [":test_common"],
-)
-
-# PCH (precompiled header) tests only work when using CMake and MSVC on Windows,
-# so they will be skipped in the Bazel builds.
-
-[base_test(
-    name = f[5:-4],  # strip test/, .cpp
-    srcs = [f],
-) for f in glob(
-    ["test/*.cpp"],
-    exclude = [
-        "test/cpp_interface_test.cpp", # has its own base_test below.
-        "test/log_test.cpp", # has its own base_test below.
-        "test/pch_test.cpp", # pch tests are skipped.
-        "test/timer_test.cpp", # has its own base_test below.
-    ],
-)]
-
-# This test uses unistd.h and does not run on Windows.
-base_test(
-    name = "timer_test",
-    srcs = select({
-        "@bazel_tools//src/conditions:windows": [],
-        "//conditions:default": ["test/timer_test.cpp"],
-    }),
-)
-
-base_test(
-    name = "cpp_interface_test",
-    srcs = ["test/cpp_interface_test.cpp"],
-    deps = [":spirv_tools_opt"],
-)
-
-base_test(
-    name = "log_test",
-    srcs = ["test/log_test.cpp"],
-    deps = [":spirv_tools_opt"],
-)
-
-[link_test(
-    name = f[10:-4],  # strip test/link/, .cpp
-    srcs = [f],
-) for f in glob(
-    ["test/link/*.cpp"],
-)]
-
-[lint_test(
-    name = f[10:-4],  # strip test/lint/, .cpp
-    srcs = [f],
-) for f in glob(
-    ["test/lint/*.cpp"],
-)]
-
-[opt_test(
-    name = f[9:-4],  # strip test/opt/, .cpp
-    srcs = [f],
-) for f in glob(
-    ["test/opt/*.cpp"],
-    # pch tests are skipped.
-    exclude = ["test/opt/pch_test_opt.cpp"],
-)]
-
-[opt_test(
-    name = "dom_tree_" + f[24:-4],  # strip test/opt/dominator_tree/, .cpp
-    srcs = [f],
 ) for f in glob(
     ["test/opt/dominator_tree/*.cpp"],
-    # pch tests are skipped.
     exclude = ["test/opt/dominator_tree/pch_test_opt_dom.cpp"],
 )]
 
-[opt_test(
-    name = "loop_" + f[28:-4],  # strip test/opt/loop_optimizations/, .cpp
+[cc_test(
+    name = "opt_loop_{testcase}_test".format(testcase = f[len("test/opt/loop_optimizations/"):-len(".cpp")]),
+    size = "small",
     srcs = [f],
+    copts = TEST_COPTS,
+    linkstatic = 1,
+    deps = [
+        ":opt_test_lib",
+        ":spirv_tools",
+        ":spirv_tools_opt_internal",
+        "@com_google_effcee//:effcee",
+        "@com_google_googletest//:gtest",
+        "@com_google_googletest//:gtest_main",
+    ],
 ) for f in glob(
     ["test/opt/loop_optimizations/*.cpp"],
-    # pch tests are skipped.
     exclude = ["test/opt/loop_optimizations/pch_test_opt_loop.cpp"],
 )]
 
-[reduce_test(
-    name = f[12:-4],  # strip test/reduce/, .cpp
-    srcs = [f],
-) for f in glob(["test/reduce/*.cpp"])]
+cc_library(
+    name = "reduce_test_lib",
+    testonly = 1,
+    srcs = [
+        "test/reduce/reduce_test_util.cpp",
+    ],
+    hdrs = ["test/reduce/reduce_test_util.h"],
+    copts = TEST_COPTS,
+    deps = [
+        ":spirv_tools",
+        ":spirv_tools_opt_internal",
+        ":spirv_tools_reduce",
+        ":test_lib",
+        ":tools_io",
+        "@com_google_googletest//:gtest",
+    ],
+)
 
-[util_test(
-    name = f[10:-4],  # strip test/util/, .cpp
+[cc_test(
+    name = "reduce_{testcase}_test".format(testcase = f[len("test/reduce/"):-len("_test.cpp")]),
+    size = "small",
     srcs = [f],
-) for f in glob(["test/util/*.cpp"])]
+    copts = TEST_COPTS,
+    linkstatic = 1,
+    deps = [
+        ":reduce_test_lib",
+        ":spirv_tools_internal",
+        ":spirv_tools_opt_internal",
+        ":spirv_tools_reduce",
+        "@com_google_googletest//:gtest_main",
+    ],
+) for f in glob(["test/reduce/*_test.cpp"])]
 
-[val_test(
-    name = f[9:-4],  # strip test/val/, .cpp
+[cc_test(
+    name = "util_{testcase}_test".format(testcase = f[len("test/util/"):-len("_test.cpp")]),
+    size = "small",
     srcs = [f],
+    copts = TEST_COPTS,
+    linkstatic = 1,
+    deps = [
+        ":spirv_tools_internal",
+        "@com_google_googletest//:gtest",
+        "@com_google_googletest//:gtest_main",
+    ],
+) for f in glob(["test/util/*_test.cpp"])]
+
+cc_library(
+    name = "val_test_lib",
+    testonly = 1,
+    srcs = [
+        "test/val/val_code_generator.cpp",
+    ],
+    hdrs = [
+        "test/val/val_code_generator.h",
+        "test/val/val_fixtures.h",
+    ],
+    copts = TEST_COPTS,
+    deps = [
+        ":spirv_tools_internal",
+        ":test_lib",
+    ],
+)
+
+[cc_test(
+    name = "val_{testcase}_test".format(testcase = f[len("test/val/val_"):-len("_test.cpp")]),
+    size = "small",
+    srcs = [f],
+    copts = TEST_COPTS,
+    linkstatic = 1,
+    deps = [
+        ":spirv_tools_internal",
+        ":test_lib",
+        ":val_test_lib",
+        "@com_google_googletest//:gtest",
+        "@com_google_googletest//:gtest_main",
+    ],
 ) for f in glob(
-    ["test/val/*.cpp"],
+    ["test/val/val_*_test.cpp"],
     exclude = [
-        "test/val/pch_test_val.cpp", # pch tests are skipped.
+        "test/val/val_capability_test.cpp",
+        "test/val/val_limits_test.cpp",
     ],
 )]
 
+cc_test(
+    name = "val_capability_test",
+    size = "large",
+    timeout = "long",
+    srcs = ["test/val/val_capability_test.cpp"],
+    copts = TEST_COPTS + ["-O3"],
+    linkstatic = 1,
+    deps = [
+        ":spirv_tools_internal",
+        ":test_lib",
+        ":val_test_lib",
+        "@com_google_googletest//:gtest",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
+cc_test(
+    name = "val_limits_test",
+    size = "large",
+    timeout = "long",
+    srcs = ["test/val/val_limits_test.cpp"],
+    copts = TEST_COPTS + [
+        "-O3",
+    ],
+    linkstatic = 1,
+    deps = [
+        ":test_lib",
+        ":val_test_lib",
+        "@com_google_googletest//:gtest",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
diff --git a/BUILD.gn b/BUILD.gn
index ab9b70d..88d24cd 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -70,6 +70,8 @@
       rebase_path(cldebuginfo100_insts_file, root_build_dir),
       "--operand-kinds-output",
       rebase_path(operand_kinds_file, root_build_dir),
+      "--output-language",
+      "c++"
     ]
   }
 }
@@ -102,6 +104,8 @@
       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,
@@ -143,6 +147,8 @@
       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,
@@ -385,6 +391,15 @@
   } else if (!is_win) {
     # Work around a false-positive on a Skia GCC 10 builder.
     cflags += [ "-Wno-format-truncation" ]
+  } else {
+    # Make MSVC report the correct value for __cplusplus
+    cflags += [ "/Zc:__cplusplus" ]
+  }
+
+  if (!is_win) {
+    cflags += [ "-std=c++17" ]
+  } else {
+    cflags += [ "/std:c++17" ]
   }
 }
 
@@ -554,6 +569,7 @@
     "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_scopes.h",
     "source/val/validate_small_type_uses.cpp",
@@ -583,6 +599,8 @@
     "source/opt/aggressive_dead_code_elim_pass.h",
     "source/opt/amd_ext_to_khr.cpp",
     "source/opt/amd_ext_to_khr.h",
+    "source/opt/analyze_live_input_pass.cpp",
+    "source/opt/analyze_live_input_pass.h",
     "source/opt/basic_block.cpp",
     "source/opt/basic_block.h",
     "source/opt/block_merge_pass.cpp",
@@ -645,10 +663,12 @@
     "source/opt/eliminate_dead_functions_pass.h",
     "source/opt/eliminate_dead_functions_util.cpp",
     "source/opt/eliminate_dead_functions_util.h",
-    "source/opt/eliminate_dead_input_components_pass.cpp",
-    "source/opt/eliminate_dead_input_components_pass.h",
+    "source/opt/eliminate_dead_io_components_pass.cpp",
+    "source/opt/eliminate_dead_io_components_pass.h",
     "source/opt/eliminate_dead_members_pass.cpp",
     "source/opt/eliminate_dead_members_pass.h",
+    "source/opt/eliminate_dead_output_stores_pass.cpp",
+    "source/opt/eliminate_dead_output_stores_pass.h",
     "source/opt/empty_pass.h",
     "source/opt/feature_manager.cpp",
     "source/opt/feature_manager.h",
@@ -694,6 +714,8 @@
     "source/opt/interface_var_sroa.h",
     "source/opt/interp_fixup_pass.cpp",
     "source/opt/interp_fixup_pass.h",
+    "source/opt/invocation_interlock_placement_pass.cpp",
+    "source/opt/invocation_interlock_placement_pass.h",
     "source/opt/ir_builder.h",
     "source/opt/ir_context.cpp",
     "source/opt/ir_context.h",
@@ -702,6 +724,8 @@
     "source/opt/iterator.h",
     "source/opt/licm_pass.cpp",
     "source/opt/licm_pass.h",
+    "source/opt/liveness.cpp",
+    "source/opt/liveness.h",
     "source/opt/local_access_chain_convert_pass.cpp",
     "source/opt/local_access_chain_convert_pass.h",
     "source/opt/local_redundancy_elimination.cpp",
@@ -788,7 +812,11 @@
     "source/opt/strip_nonsemantic_info_pass.h",
     "source/opt/struct_cfg_analysis.cpp",
     "source/opt/struct_cfg_analysis.h",
+    "source/opt/switch_descriptorset_pass.cpp",
+    "source/opt/switch_descriptorset_pass.h",
     "source/opt/tree_iterator.h",
+    "source/opt/trim_capabilities_pass.cpp",
+    "source/opt/trim_capabilities_pass.h",
     "source/opt/type_manager.cpp",
     "source/opt/type_manager.h",
     "source/opt/types.cpp",
@@ -1435,15 +1463,6 @@
   }
 }
 
-source_set("spvtools_util_cli_consumer") {
-  sources = [
-    "tools/util/cli_consumer.cpp",
-    "tools/util/cli_consumer.h",
-  ]
-  deps = [ ":spvtools_headers" ]
-  configs += [ ":spvtools_internal_config" ]
-}
-
 source_set("spvtools_software_version") {
   sources = [ "source/software_version.cpp" ]
   deps = [
@@ -1453,12 +1472,23 @@
   configs += [ ":spvtools_internal_config" ]
 }
 
+source_set("spvtools_tools_util") {
+  sources = [
+    "tools/util/flags.cpp",
+    "tools/util/cli_consumer.cpp",
+    "tools/util/cli_consumer.h",
+  ]
+  deps = [ ":spvtools_headers" ]
+  configs += [ ":spvtools_internal_config" ]
+}
+
 if (spvtools_build_executables) {
   executable("spirv-as") {
     sources = [ "tools/as/as.cpp" ]
     deps = [
       ":spvtools",
       ":spvtools_software_version",
+      ":spvtools_tools_util",
     ]
     configs += [ ":spvtools_internal_config" ]
   }
@@ -1468,6 +1498,7 @@
     deps = [
       ":spvtools",
       ":spvtools_software_version",
+      ":spvtools_tools_util",
     ]
     configs += [ ":spvtools_internal_config" ]
   }
@@ -1477,7 +1508,7 @@
     deps = [
       ":spvtools",
       ":spvtools_software_version",
-      ":spvtools_util_cli_consumer",
+      ":spvtools_tools_util",
       ":spvtools_val",
     ]
     configs += [ ":spvtools_internal_config" ]
@@ -1492,6 +1523,7 @@
     deps = [
       ":spvtools",
       ":spvtools_software_version",
+      ":spvtools_tools_util",
     ]
     configs += [ ":spvtools_internal_config" ]
   }
@@ -1502,7 +1534,7 @@
       ":spvtools",
       ":spvtools_opt",
       ":spvtools_software_version",
-      ":spvtools_util_cli_consumer",
+      ":spvtools_tools_util",
       ":spvtools_val",
     ]
     configs += [ ":spvtools_internal_config" ]
@@ -1515,6 +1547,7 @@
       ":spvtools_link",
       ":spvtools_opt",
       ":spvtools_software_version",
+      ":spvtools_tools_util",
       ":spvtools_val",
     ]
     configs += [ ":spvtools_internal_config" ]
@@ -1534,7 +1567,7 @@
       ":spvtools_opt",
       ":spvtools_reduce",
       ":spvtools_software_version",
-      ":spvtools_util_cli_consumer",
+      ":spvtools_tools_util",
       ":spvtools_val",
       "//third_party/protobuf:protobuf_full",
     ]
@@ -1553,7 +1586,7 @@
       ":spvtools_opt",
       ":spvtools_reduce",
       ":spvtools_software_version",
-      ":spvtools_util_cli_consumer",
+      ":spvtools_tools_util",
       ":spvtools_val",
     ]
     configs += [ ":spvtools_internal_config" ]
diff --git a/CHANGES b/CHANGES
index 56a2d52..a7929eb 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,5 +1,133 @@
 Revision history for SPIRV-Tools
 
+v2023.5 2023-10-15
+  - General
+    - Support 2 Intel extensions (#5357)
+    - SPV_QCOM_image_processing support (#5223)
+  - Optimizer
+    - opt: fix StorageInputOutput16 trimming. (#5359)
+    - opt: add StoragePushConstant16 to trim pass (#5366)
+    - opt: enable StorageUniform16 (#5371)
+    - opt: add bitmask support for capability trimming (#5372)
+    - opt: Add SwitchDescriptorSetPass (#5375)
+    - opt: add FragmentShader*InterlockEXT to capability trim pass (#5390)
+    - opt: add Int64 capability to trim pass (#5398)
+    - opt: add Float64 capability to trim pass (#5428)
+    - opt: add raytracing/rayquery to trim pass (#5397)
+    - opt: add ImageMSArray capability to trim pass. (#5395)
+    - Add SPV_KHR_physical_storage_buffer to allowlists (#5402)
+    - Add SPV_EXT_fragment_shader_interlock to allow lists (#5393)
+    - Make sure that fragment shader interlock instructions are not removed by DCE (#5400)
+    - instrument: Use Import linkage for instrumentation functions (#5355)
+    - Add a new legalization pass to dedupe invocation interlock instructions (#5409)
+    - instrument: Ensure linking works even of nothing is changed (#5419)
+  - Validator
+    - Move token version/cap/ext checks from parsing to validation (#5370)
+    - val: re-add ImageMSArray validation (#5394)
+  - Linker
+    - linker: Add --use-highest-version option
+
+v2023.4 2023-07-17
+  - General
+    - Set cmake_policy CMP0128 (#5341)
+    - Add python3 requirement for the script (#5326)
+    - Add support for LiteralFloat type (#5323)
+    - SPV_KHR_cooperative_matrix (#5286)
+    - Allow OpTypeBool in UniformConstant (#5237)
+    - Allow physical storage buffer pointer in IO (#5251)
+    - Remove const zero image operands (#5232)
+  - Optimizer
+    - Enable vector constant folding (#4913) (#5272)
+    - Fold negation of integer vectors (#5269)
+    - Add folding rule for OpTranspose (#5241)
+    - Add SPV_NV_bindless_texture to spirv optimizations (#5231)
+    - Fix incorrect half float conversion (#5349)
+    - Add SPV_EXT_shader_atomic_float_add to allow lists (#5348)
+  - Instrument
+    - instrument: Cast gl_VertexIndex and InstanceIndex to uint (#5319)
+    - instrument: Fix buffer address length calculations (#5257)
+    - instrument: Reduce number of inst_bindless_stream_write_6 calls (#5327)
+  - Validator
+    - Validate GroupNonUniform instructions (#5296)
+    - spirv-val: Label SPV_KHR_cooperative_matrix VUID (#5301)
+    - Validate layouts for PhysicalStorageBuffer pointers (#5291)
+    - spirv-val: Remove VUID from 1.3.251 spec (#5244)
+  - Diff
+    - spirv-diff: Update test expectations (#5264)
+    - spirv-diff: Leave undefined ids unpaired. (#5262)
+    - spirv-diff: Properly match SPV_KHR_ray_query types. (#5259)
+    - diff: Don't give up entry point matching too early. (#5224)
+
+v2023.3 2023-05-15
+  - General
+    - Update spirv_headers to include SPV_KHR_ray_tracing_position_fetch (#5205)
+    - spirv-tools: Add support for QNX (#5211)
+    - build: set std=c++17 for BUILD.gn (#5162)
+  - Optimizer
+    - Run ADCE when the printf extension is used. (#5215)
+    - Don't convert struct members to half (#5201)
+    - Apply scalar replacement on vars with Pointer decorations (#5208)
+    - opt: Fix null deref in OpMatrixTimesVector and OpVectorTimesMatrix (#5199)
+    - instrument: Add set and binding to bindless error records (#5204)
+    - instrument: Change descriptor state storage format (#5178)
+    - Fix LICMPass (#5087)
+    - Add Vulkan memory model to allow lists (#5173)
+    - Do not remove control barrier after spv1.3 (#5174)
+  - Validator
+    - spirv-val: Label Interface Location/Component VUIDs (#5221)
+    - Add support for SPV_EXT_shader_tile_image (#5188)
+    - Fix vector OpConstantComposite type validation (#5191)
+    - spirv-val: Label new Vulkan VUID 07951 (#5154)
+  - Fuzz
+    - Do not define GOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE if it is already defined. (#5200)
+
+v2023.2 2023-03-10
+  - General
+    - build: move from c++11 to c++17 (#4983)
+    - tools: refactorize tools flags parsing. (#5111)
+    - Add C interface for Optimizer (#5030)
+    - libspirv.cpp: adds c++ api for spvBinaryParse (#5109)
+    - build: change the way we set cxx version for bazel. (#5114)
+  - Optimizer
+    - Fix null pointer in FoldInsertWithConstants. (#5093)
+    - Fix removal of dependent non-semantic instructions (#5122)
+    - Remove duplicate lists of constant and type opcodes (#5106)
+    - opt: fix spirv ABI on Linux again. (#5113)
+  - Validator
+    - Validate decoration of structs with RuntimeArray (#5094)
+    - Validate operand type before operating on it (#5092)
+    - spirv-val: Conditional Branch without an exit is invalid in loop header (#5069)
+    - spirv-val: Initial SPV_EXT_mesh_shader builtins (#5080)
+
+v2023.1 2023-01-17
+  - General
+    - Renamed "master" to "main" (issue#5051)
+    - Validate version 5 of clspv reflection (#5050)
+    - Remove testing support for VS2015 (#5027)
+    - Fix undef behaviour in hex float parsing (#5025)
+    - Require C++11 *or later* (#5020)
+  - Instrument
+    - Instrument: Fix bindless checking for BufferDeviceAddress (#5049)
+  - Optimizer
+    - Optimize allocation of spvtools::opt::Instruction::operands_ (#5024)
+    - spirv-opt: Fix OpCompositeInsert with Null Constant (#5008)
+    - spirv-opt: Handle null CompositeInsert (#4998)
+    - Add option to ADCE to remove output variables from interface. (#4994)
+    - Add support for tesc, tese and geom to EliminateDead*Components (#4990)
+    - Add pass to eliminate dead output components (#4982)
+    - spirv-opt: Add const folding for CompositeInsert (#4943)
+    - Add passes to eliminate dead output stores (#4970)
+    - Prevent eliminating case constructs in block merging (#4976)
+  - Validator
+    - Fix layout validation (#5015)
+    - Fix use of invalid analysis (#5013)
+    - Fix infinite loop in validator (#5006)
+    - Add validation support for SPV_NV_shader_invocation_reorder. (#4979)
+    - Only validate full layout in Vulkan environments (#4972)
+    - spirv-val: Label new Vulkan OpPtrAccessChain VUs (#4975)
+    - spirv-val: Add OpPtrAccessChain Base checks (#4965)
+
+
 v2022.4 2022-10-12
   - General
     - Support Narrow Types in BitCast Folding Rule (#4941)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1b8fe92..375229b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2015-2016 The Khronos Group Inc.
+# Copyright (c) 2015-2023 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.
@@ -12,26 +12,38 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-cmake_minimum_required(VERSION 2.8.12)
-if (POLICY CMP0048)
-  cmake_policy(SET CMP0048 NEW)
-endif()
-if (POLICY CMP0054)
-  # Avoid dereferencing variables or interpret keywords that have been
-  # quoted or bracketed.
-  # https://cmake.org/cmake/help/v3.1/policy/CMP0054.html
-  cmake_policy(SET CMP0054 NEW)
-endif()
-set_property(GLOBAL PROPERTY USE_FOLDERS ON)
+cmake_minimum_required(VERSION 3.17.2)
 
 project(spirv-tools)
+
+# Avoid a bug in CMake 3.22.1. By default it will set -std=c++11 for
+# targets in test/*, when those tests need -std=c++17.
+# https://github.com/KhronosGroup/SPIRV-Tools/issues/5340
+# The bug is fixed in CMake 3.22.2
+if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.22.1")
+  if (${CMAKE_VERSION} VERSION_LESS "3.22.2")
+    cmake_policy(SET CMP0128 NEW)
+  endif()
+endif()
+
+set_property(GLOBAL PROPERTY USE_FOLDERS ON)
+
 enable_testing()
 set(SPIRV_TOOLS "SPIRV-Tools")
 
 include(GNUInstallDirs)
 
 set(CMAKE_POSITION_INDEPENDENT_CODE ON)
-set(CMAKE_CXX_STANDARD 11)
+
+# Require at least C++17
+if(NOT CMAKE_CXX_STANDARD)
+  set(CMAKE_CXX_STANDARD 17)
+endif()
+if(${CMAKE_CXX_STANDARD} LESS 17)
+  message(FATAL_ERROR "SPIRV-Tools requires C++17 or later, but is configured for C++${CMAKE_CXX_STANDARD})")
+endif()
+set(CMAKE_CXX_EXTENSIONS OFF)
+
 
 option(ENABLE_RTTI "Enables RTTI" OFF)
 option(SPIRV_ALLOW_TIMERS "Allow timers via clock_gettime on supported platforms" ON)
@@ -51,6 +63,8 @@
   add_definitions(-DSPIRV_IOS)
 elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "tvOS")
   add_definitions(-DSPIRV_TVOS)
+elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "visionOS")
+  add_definitions(-DSPIRV_VISIONOS)
 elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Android")
   add_definitions(-DSPIRV_ANDROID)
   set(SPIRV_TIMER_ENABLED ${SPIRV_ALLOW_TIMERS})
@@ -62,6 +76,8 @@
   add_definitions(-DSPIRV_FUCHSIA)
 elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "GNU")
   add_definitions(-DSPIRV_GNU)
+elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "QNX")
+  add_definitions(-DSPIRV_QNX)
 else()
   message(FATAL_ERROR "Your platform '${CMAKE_SYSTEM_NAME}' is not supported!")
 endif()
@@ -188,10 +204,9 @@
   target_compile_options(${TARGET} PRIVATE ${SPIRV_WARNINGS})
 
   if (${COMPILER_IS_LIKE_GNU})
-    target_compile_options(${TARGET} PRIVATE -std=c++11 -fno-exceptions)
     target_compile_options(${TARGET} PRIVATE
       -Wall -Wextra -Wno-long-long -Wshadow -Wundef -Wconversion
-      -Wno-sign-conversion)
+      -Wno-sign-conversion -fno-exceptions)
 
     if(NOT ENABLE_RTTI)
         add_compile_options(-fno-rtti)
@@ -200,7 +215,7 @@
     if(NOT "${SPIRV_PERF}" STREQUAL "")
       target_compile_options(${TARGET} PRIVATE -fno-omit-frame-pointer)
     endif()
-    if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
+    if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang")
       set(SPIRV_USE_SANITIZER "" CACHE STRING
         "Use the clang sanitizer [address|memory|thread|...]")
       if(NOT "${SPIRV_USE_SANITIZER}" STREQUAL "")
@@ -228,7 +243,7 @@
   # For MinGW cross compile, statically link to the C++ runtime.
   # But it still depends on MSVCRT.dll.
   if (${CMAKE_SYSTEM_NAME} MATCHES "Windows")
-    if (${CMAKE_CXX_COMPILER_ID} MATCHES "GNU")
+    if (NOT MSVC)
       set_target_properties(${TARGET} PROPERTIES
         LINK_FLAGS -static -static-libgcc -static-libstdc++)
     endif()
@@ -247,7 +262,7 @@
 endif()
 
 # Tests require Python3
-find_host_package(PythonInterp 3 REQUIRED)
+find_host_package(Python3 REQUIRED)
 
 # Check for symbol exports on Linux.
 # At the moment, this check will fail on the OSX build machines for the Android NDK.
@@ -256,7 +271,7 @@
   macro(spvtools_check_symbol_exports TARGET)
     if (NOT "${SPIRV_SKIP_TESTS}")
       add_test(NAME spirv-tools-symbol-exports-${TARGET}
-               COMMAND ${PYTHON_EXECUTABLE}
+               COMMAND Python3::Interpreter
                ${spirv-tools_SOURCE_DIR}/utils/check_symbol_exports.py "$<TARGET_FILE:${TARGET}>")
     endif()
   endmacro()
@@ -269,7 +284,7 @@
 endif()
 
 if(ENABLE_SPIRV_TOOLS_INSTALL)
-  if(WIN32)
+  if(WIN32 AND NOT MINGW)
     macro(spvtools_config_package_dir TARGET PATH)
       set(${PATH} ${TARGET}/cmake)
     endmacro()
@@ -355,7 +370,7 @@
 
 if (NOT "${SPIRV_SKIP_TESTS}")
   add_test(NAME spirv-tools-copyrights
-           COMMAND ${PYTHON_EXECUTABLE} utils/check_copyright.py
+           COMMAND Python3::Interpreter utils/check_copyright.py
            WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
 endif()
 
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 1eb8b68..11fb4e2 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -2,9 +2,8 @@
 
 ## For users: Reporting bugs and requesting features
 
-We organize known future work in GitHub projects. See [Tracking SPIRV-Tools work
-with GitHub
-projects](https://github.com/KhronosGroup/SPIRV-Tools/blob/master/docs/projects.md)
+We organize known future work in GitHub projects. See
+[Tracking SPIRV-Tools work with GitHub projects](https://github.com/KhronosGroup/SPIRV-Tools/blob/main/docs/projects.md)
 for more.
 
 To report a new bug or request a new feature, please file a GitHub issue. Please
@@ -36,9 +35,9 @@
 
 ## For developers: Contributing a patch
 
-Before we can use your code, you must sign the [Khronos Open Source Contributor
-License Agreement](https://cla-assistant.io/KhronosGroup/SPIRV-Tools) (CLA),
-which you can do online. The CLA is necessary mainly because you own the
+Before we can use your code, you must sign the
+[Khronos Open Source Contributor License Agreement](https://cla-assistant.io/KhronosGroup/SPIRV-Tools)
+(CLA), which you can do online. The CLA is necessary mainly because you own the
 copyright to your changes, even after your contribution becomes part of our
 codebase, so we need your permission to use and distribute your code. We also
 need to be sure of various other things -- for instance that you'll tell us if
@@ -47,20 +46,20 @@
 approved it, but you must do it before we can put your code into our codebase.
 
 See
-[README.md](https://github.com/KhronosGroup/SPIRV-Tools/blob/master/README.md)
+[README.md](https://github.com/KhronosGroup/SPIRV-Tools/blob/main/README.md)
 for instruction on how to get, build, and test the source. Once you have made
 your changes:
 
-*   Ensure the code follows the [Google C++ Style
-    Guide](https://google.github.io/styleguide/cppguide.html). Running
-    `clang-format -style=file -i [modified-files]` can help.
+*   Ensure the code follows the
+    [Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html).
+    Running `clang-format -style=file -i [modified-files]` can help.
 *   Create a pull request (PR) with your patch.
 *   Make sure the PR description clearly identified the problem, explains the
     solution, and references the issue if applicable.
 *   If your patch completely fixes bug 1234, the commit message should say
-    `Fixes https://github.com/KhronosGroup/SPIRV-Tools/issues/1234`
-    When you do this, the issue will be closed automatically when the commit
-    goes into master.  Also, this helps us update the [CHANGES](CHANGES) file.
+    `Fixes https://github.com/KhronosGroup/SPIRV-Tools/issues/1234` When you do
+    this, the issue will be closed automatically when the commit goes into
+    main. Also, this helps us update the [CHANGES](CHANGES) file.
 *   Watch the continuous builds to make sure they pass.
 *   Request a code review.
 
@@ -82,8 +81,8 @@
 The formal code reviews are done on GitHub. Reviewers are to look for all of the
 usual things:
 
-*   Coding style follows the [Google C++ Style
-    Guide](https://google.github.io/styleguide/cppguide.html)
+*   Coding style follows the
+    [Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html)
 *   Identify potential functional problems.
 *   Identify code duplication.
 *   Ensure the unit tests have enough coverage.
@@ -102,84 +101,49 @@
     updated. For example, a new instruction is added, but the def-use manager is
     not updated. Later on, it is possible that the def-use manager will be used,
     and give wrong results.
+*   If a pass gets the id of a type from the type manager, make sure the type is
+    not a struct or array. It there are two structs that look the same, the type
+    manager can return the wrong one.
 
 ## For maintainers: Merging a PR
 
-We intend to maintain a linear history on the GitHub master branch, and the
+We intend to maintain a linear history on the GitHub main branch, and the
 build and its tests should pass at each commit in that history. A linear
 always-working history is easier to understand and to bisect in case we want to
-find which commit introduced a bug.
+find which commit introduced a bug. The
+[Squash and Merge](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/incorporating-changes-from-a-pull-request/about-pull-request-merges#squash-and-merge-your-commits)
+button on the GitHub web interface. All other ways of merging on the web
+interface have been disabled.
 
-### Initial merge setup
+Before merging, we generally require:
 
-The following steps should be done exactly once (when you are about to merge a
-PR for the first time):
+1.  All tests except for the smoke test pass. See
+    [failing smoke test](#failing-smoke-test).
+1.  The PR is approved by at least one of the maintainers. If the PR modifies
+    different parts of the code, then multiple reviewers might be necessary.
 
-*   It is assumed that upstream points to
-    [git@github.com](mailto:git@github.com):KhronosGroup/SPIRV-Tools.git or
-    https://github.com/KhronosGroup/SPIRV-Tools.git.
+The squash-and-merge button will turn green when these requirements are met.
+Maintainers have the to power to merge even if the button is not green, but that
+is discouraged.
 
-*   Find out the local name for the main github repo in your git configuration.
-    For example, in this configuration, it is labeled `upstream`.
+### Failing smoke test
 
-    ```
-    git remote -v
-    [ ... ]
-    upstream https://github.com/KhronosGroup/SPIRV-Tools.git (fetch)
-    upstream https://github.com/KhronosGroup/SPIRV-Tools.git (push)
-    ```
+The purpose of the smoke test is to let us know if
+[shaderc](https://github.com/google/shaderc) fails to build with the change. If
+it fails, the maintainer needs to determine if the reason for the failure is a
+problem in the current PR or if another repository needs to be changed. Most of
+the time [Glslang](https://github.com/KhronosGroup/glslang) needs to be updated
+to account for the change in SPIR-V Tools.
 
-*   Make sure that the `upstream` remote is set to fetch from the `refs/pull`
-    namespace:
+The PR can still be merged if the problem is not with that PR.
 
-    ```
-    git config --get-all remote.upstream.fetch
-    +refs/heads/*:refs/remotes/upstream/*
-    +refs/pull/*/head:refs/remotes/upstream/pr/*
-    ```
+## For maintainers: Running tests
 
-*   If the line `+refs/pull/*/head:refs/remotes/upstream/pr/*` is not present in
-    your configuration, you can add it with the command:
+For security reasons, not all tests will run automatically. When they do not, a
+maintainer will have to start the tests.
 
-    ```
-    git config --local --add remote.upstream.fetch '+refs/pull/*/head:refs/remotes/upstream/pr/*'
-    ```
+If the Github actions tests do not run on a PR, they can be initiated by closing
+and reopening the PR.
 
-### Merge workflow
-
-The following steps should be done for every PR that you intend to merge:
-
-*   Make sure your local copy of the master branch is up to date:
-
-    ```
-    git checkout master
-    git pull
-    ```
-
-*   Fetch all pull requests refs:
-
-    ```
-    git fetch upstream
-    ```
-
-*   Checkout the particular pull request you are going to review:
-
-    ```
-    git checkout pr/1048
-    ```
-
-*   Rebase the PR on top of the master branch. If there are conflicts, send it
-    back to the author and ask them to rebase. During the interactive rebase be
-    sure to squash all of the commits down to a single commit.
-
-    ```
-    git rebase -i master
-    ```
-
-*   **Build and test the PR.**
-
-*   If all of the tests pass, push the commit `git push upstream HEAD:master`
-
-*   Close the PR and add a comment saying it was push using the commit that you
-    just pushed. See https://github.com/KhronosGroup/SPIRV-Tools/pull/935 as an
-    example.
+If the kokoro tests are not run, they can be run by adding the label
+`kokoro:run` to the PR.
diff --git a/DEPS b/DEPS
index 3a5f614..3d6f331 100644
--- a/DEPS
+++ b/DEPS
@@ -3,19 +3,32 @@
 vars = {
   'github': 'https://github.com',
 
-  'effcee_revision': '35912e1b7778ec2ddcff7e7188177761539e59e0',
-  'googletest_revision': 'd9bb8412d60b993365abb53f00b6dad9b2c01b62',
-  're2_revision': 'd2836d1b1c34c4e330a85a1006201db474bf2c8a',
-  'spirv_headers_revision': '85a1ed200d50660786c1a88d9166e871123cce39',
+  'abseil_revision': '79ca5d7aad63973c83a4962a66ab07cd623131ea',
+
+  'effcee_revision': '19b4aa87af25cb4ee779a071409732f34bfc305c',
+
+  'googletest_revision': '2dd1c131950043a8ad5ab0d2dda0e0970596586a',
+
+  # Use protobufs before they gained the dependency on abseil
+  'protobuf_revision': 'v21.12',
+
+  're2_revision': 'b673de35837aad63146957b9c305dbdbd82ce6b7',
+  'spirv_headers_revision': 'e867c06631767a2d96424cbec530f9ee5e78180f',
 }
 
 deps = {
+  'external/abseil_cpp':
+      Var('github') + '/abseil/abseil-cpp.git@' + Var('abseil_revision'),
+
   'external/effcee':
       Var('github') + '/google/effcee.git@' + Var('effcee_revision'),
 
   'external/googletest':
       Var('github') + '/google/googletest.git@' + Var('googletest_revision'),
 
+  'external/protobuf':
+      Var('github') + '/protocolbuffers/protobuf.git@' + Var('protobuf_revision'),
+
   'external/re2':
       Var('github') + '/google/re2.git@' + Var('re2_revision'),
 
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index dd3117f..847deb7 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -18,6 +18,8 @@
 for more details about the presubmit API built into depot_tools.
 """
 
+USE_PYTHON3 = True
+
 LINT_FILTERS = [
   "-build/storage_class",
   "-readability/casting",
diff --git a/README.md b/README.md
index e951e37..042b83d 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,7 @@
 # SPIR-V Tools
+[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/KhronosGroup/SPIRV-Tools/badge)](https://securityscorecards.dev/viewer/?uri=github.com/KhronosGroup/SPIRV-Tools)
+
+NEWS 2023-01-11: Development occurs on the `main` branch.
 
 ## Overview
 
@@ -23,7 +26,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_vs2017_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)
 
 [More downloads](docs/downloads.md)
 
@@ -96,10 +99,10 @@
 
 *Note*: The validator checks some Universal Limits, from section 2.17 of the SPIR-V spec.
 The validator will fail on a module that exceeds those minimum upper bound limits.
-It is [future work](https://github.com/KhronosGroup/SPIRV-Tools/projects/1#card-1052403)
-to parameterize the validator to allow larger
-limits accepted by a more than minimally capable SPIR-V consumer.
+The validator has been parameterized to allow larger values, for use when targeting 
+a more-than-minimally-capable SPIR-V consumer.
 
+See [`tools/val/val.cpp`](tools/val/val.cpp) or run `spirv-val --help` for the command-line help.
 
 ### Optimizer
 
@@ -271,7 +274,7 @@
   `clang-format version 5.0.0` for SPIRV-Tools. Settings are defined by
   the included [.clang-format](.clang-format) file.
 
-We intend to maintain a linear history on the GitHub `master` branch.
+We intend to maintain a linear history on the GitHub `main` branch.
 
 ### Getting the source
 
@@ -290,16 +293,18 @@
     git clone https://github.com/google/googletest.git          spirv-tools/external/googletest
     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
 
 #### Dependency on Effcee
 
 Some tests depend on the [Effcee][effcee] library for stateful matching.
-Effcee itself depends on [RE2][re2].
+Effcee itself depends on [RE2][re2], and RE2 depends on [Abseil][abseil-cpp].
 
 * If SPIRV-Tools is configured as part of a larger project that already uses
   Effcee, then that project should include Effcee before SPIRV-Tools.
-* Otherwise, SPIRV-Tools expects Effcee sources to appear in `external/effcee`
-  and RE2 sources to appear in `external/re2`.
+* Otherwise, SPIRV-Tools expects Effcee sources to appear in `external/effcee`,
+  RE2 sources to appear in `external/re2`, and Abseil sources to appear in 
+  `external/abseil_cpp`.
 
 ### Source code organization
 
@@ -311,6 +316,9 @@
 * `external/re2`: Location of [RE2][re2] sources, if the `re2` library is not already
   configured by an enclosing project.
   (The Effcee project already requires RE2.)
+* `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.)
 * `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
@@ -378,10 +386,11 @@
 
 ### Build using Bazel
 You can also use [Bazel](https://bazel.build/) to build the project.
+
 ```sh
-cd <spirv-dir>
 bazel build :all
 ```
+
 ### Build a node.js package using Emscripten
 
 The SPIRV-Tools core library can be built to a WebAssembly [node.js](https://nodejs.org)
@@ -432,10 +441,13 @@
 - AppleClang 11.0
 
 On Windows
-- Visual Studio 2015
 - Visual Studio 2017
+- Visual Studio 2019
+- Visual Studio 2022
 
-Other compilers or later versions may work, but they are not tested.
+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.
 
 ### CMake options
 
@@ -467,12 +479,12 @@
 ### Android ndk-build
 
 SPIR-V Tools supports building static libraries `libSPIRV-Tools.a` and
-`libSPIRV-Tools-opt.a` for Android:
+`libSPIRV-Tools-opt.a` for Android.  Using the Android NDK r25c or later:
 
 ```
 cd <spirv-dir>
 
-export ANDROID_NDK=/path/to/your/ndk
+export ANDROID_NDK=/path/to/your/ndk   # NDK r25c or later
 
 mkdir build && cd build
 mkdir libs
@@ -496,7 +508,7 @@
 
 ### Usage
 
-The internals of the library use C++11 features, and are exposed via both a C
+The internals of the library use C++17 features, and are exposed via both a C
 and C++ API.
 
 In order to use the library from an application, the include path should point
@@ -718,10 +730,16 @@
 To run a single test target, specify `:my_test_target` instead of `:all`. Test target
 names get printed when you run `bazel test :all`. For example, you can run
 `opt_def_use_test` with:
+
+on linux:
 ```shell
-bazel test :opt_def_use_test
+bazel test --cxxopt=-std=c++17 :opt_def_use_test
 ```
 
+on windows:
+```shell
+bazel test --cxxopt=/std:c++17 :opt_def_use_test
+```
 
 ## Future Work
 <a name="future"></a>
@@ -779,6 +797,7 @@
 [googletest-issue-610]: https://github.com/google/googletest/issues/610
 [effcee]: https://github.com/google/effcee
 [re2]: https://github.com/google/re2
+[abseil-cpp]: https://github.com/abseil/abseil-cpp
 [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/SECURITY.md b/SECURITY.md
new file mode 100644
index 0000000..99c5f44
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,13 @@
+# Security Policy
+
+## Supported Versions
+
+Security updates are applied only to the latest release.
+
+## Reporting a Vulnerability
+
+If you have discovered a security vulnerability in this project, please report it privately. **Do not disclose it as a public issue.** This gives us time to work with you to fix the issue before public exposure, reducing the chance that the exploit will be used before a patch is released.
+
+Please disclose it at [security advisory](https://github.com/KhronosGroup/SPIRV-Tools/security/advisories/new).
+
+This project is maintained by a team of volunteers on a reasonable-effort basis. As such, please give us at least 90 days to work on a fix before public exposure.
diff --git a/WORKSPACE b/WORKSPACE
index 5abfc98..589dc12 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -1,3 +1,11 @@
+load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
+
+http_archive(
+    name = "bazel_skylib",
+    strip_prefix = "bazel-skylib-main",
+    urls = ["https://github.com/bazelbuild/bazel-skylib/archive/main.zip"],
+)
+
 local_repository(
     name = "spirv_headers",
     path = "external/spirv-headers",
@@ -17,3 +25,8 @@
     name = "com_google_effcee",
     path = "external/effcee",
 )
+
+local_repository(
+    name = "com_google_absl",
+    path = "external/abseil_cpp",
+)
diff --git a/android_test/Android.mk b/android_test/Android.mk
index dbaf93b..b9a0014 100644
--- a/android_test/Android.mk
+++ b/android_test/Android.mk
@@ -5,7 +5,7 @@
 LOCAL_SRC_FILES:=test.cpp
 LOCAL_MODULE:=spirvtools_test
 LOCAL_LDLIBS:=-landroid
-LOCAL_CXXFLAGS:=-std=c++11 -fno-exceptions -fno-rtti -Werror
+LOCAL_CXXFLAGS:=-std=c++17 -fno-exceptions -fno-rtti -Werror
 LOCAL_STATIC_LIBRARIES=SPIRV-Tools SPIRV-Tools-opt
 include $(BUILD_SHARED_LIBRARY)
 
diff --git a/android_test/jni/Application.mk b/android_test/jni/Application.mk
index 4e66465..47c0acf 100644
--- a/android_test/jni/Application.mk
+++ b/android_test/jni/Application.mk
@@ -1,5 +1,5 @@
 APP_ABI := all
 APP_BUILD_SCRIPT := Android.mk
 APP_STL := c++_static
-APP_PLATFORM := android-9
+APP_PLATFORM := android-24
 NDK_TOOLCHAIN_VERSION := 4.9
diff --git a/build_defs.bzl b/build_defs.bzl
index 7189137..76bf3e7 100644
--- a/build_defs.bzl
+++ b/build_defs.bzl
@@ -1,20 +1,21 @@
+"""Constants and macros for spirv-tools BUILD."""
+
 COMMON_COPTS = [
-        "-DSPIRV_CHECK_CONTEXT",
-        "-DSPIRV_COLOR_TERMINAL",
-    ] + select({
-    "@bazel_tools//src/conditions:windows": [""],
+    "-DSPIRV_CHECK_CONTEXT",
+    "-DSPIRV_COLOR_TERMINAL",
+] + select({
+    "@platforms//os:windows": [],
     "//conditions:default": [
         "-DSPIRV_LINUX",
         "-DSPIRV_TIMER_ENABLED",
+        "-fvisibility=hidden",
+        "-fno-exceptions",
+        "-fno-rtti",
         "-Wall",
         "-Wextra",
         "-Wnon-virtual-dtor",
         "-Wno-missing-field-initializers",
         "-Werror",
-        "-std=c++11",
-        "-fvisibility=hidden",
-        "-fno-exceptions",
-        "-fno-rtti",
         "-Wno-long-long",
         "-Wshadow",
         "-Wundef",
@@ -23,324 +24,211 @@
     ],
 })
 
-TEST_COPTS = COMMON_COPTS + select({
-    "@bazel_tools//src/conditions:windows": [
+TEST_COPTS = COMMON_COPTS + [
+] + select({
+    "@platforms//os:windows": [
         # Disable C4503 "decorated name length exceeded" warning,
         # triggered by some heavily templated types.
         # We don't care much about that in test code.
         # Important to do since we have warnings-as-errors.
-        "/wd4503"
+        "/wd4503",
     ],
     "//conditions:default": [
         "-Wno-undef",
         "-Wno-self-assign",
         "-Wno-shadow",
-        "-Wno-unused-parameter"
+        "-Wno-unused-parameter",
     ],
 })
 
+def incompatible_with(incompatible_constraints):
+    return select(_merge_dicts([{"//conditions:default": []}, {
+        constraint: ["@platforms//:incompatible"]
+        for constraint in incompatible_constraints
+    }]))
+
 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"
 
-def generate_core_tables(version = None):
+def _merge_dicts(dicts):
+    merged = {}
+    for d in dicts:
+        merged.update(d)
+    return merged
+
+def generate_core_tables(version):
     if not version:
         fail("Must specify version", "version")
-    grammars = [
-        "@spirv_headers//:spirv_core_grammar_" + version,
-        DEBUGINFO_GRAMMAR_JSON_FILE,
-        CLDEBUGINFO100_GRAMMAR_JSON_FILE,
-    ]
-    outs = [
-        "core.insts-{}.inc".format(version),
-        "operand.kinds-{}.inc".format(version),
-    ]
-    fmtargs = grammars + outs
+
+    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(
+        core_insts_output = "core.insts-{}.inc".format(version),
+        operand_kinds_output = "operand.kinds-{}.inc".format(version),
+    )
+
+    cmd = (
+        "$(location :generate_grammar_tables)" +
+        " --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++"
+    ).format(**_merge_dicts([grammars, outs]))
+
     native.genrule(
         name = "gen_core_tables_" + version,
-        srcs = grammars,
-        outs = outs,
-        cmd = (
-            "$(location :generate_grammar_tables) " +
-            "--spirv-core-grammar=$(location {0}) " +
-            "--extinst-debuginfo-grammar=$(location {1}) " +
-            "--extinst-cldebuginfo100-grammar=$(location {2}) " +
-            "--core-insts-output=$(location {3}) " +
-            "--operand-kinds-output=$(location {4})"
-        ).format(*fmtargs),
-        cmd_bat = (
-            "$(location :generate_grammar_tables) " +
-            "--spirv-core-grammar=$(location {0}) " +
-            "--extinst-debuginfo-grammar=$(location {1}) " +
-            "--extinst-cldebuginfo100-grammar=$(location {2}) " +
-            "--core-insts-output=$(location {3}) " +
-            "--operand-kinds-output=$(location {4})"
-        ).format(*fmtargs),
-        exec_tools = [":generate_grammar_tables"],
+        srcs = grammars.values(),
+        outs = outs.values(),
+        cmd = cmd,
+        cmd_bat = cmd,
+        tools = [":generate_grammar_tables"],
         visibility = ["//visibility:private"],
     )
 
-def generate_enum_string_mapping(version = None):
+def generate_enum_string_mapping(version):
     if not version:
         fail("Must specify version", "version")
-    grammars = [
-        "@spirv_headers//:spirv_core_grammar_" + version,
-        DEBUGINFO_GRAMMAR_JSON_FILE,
-        CLDEBUGINFO100_GRAMMAR_JSON_FILE,
-    ]
-    outs = [
-        "extension_enum.inc",
-        "enum_string_mapping.inc",
-    ]
-    fmtargs = grammars + outs
+
+    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,
-        outs = outs,
-        cmd = (
-            "$(location :generate_grammar_tables) " +
-            "--spirv-core-grammar=$(location {0}) " +
-            "--extinst-debuginfo-grammar=$(location {1}) " +
-            "--extinst-cldebuginfo100-grammar=$(location {2}) " +
-            "--extension-enum-output=$(location {3}) " +
-            "--enum-string-mapping-output=$(location {4})"
-        ).format(*fmtargs),
-        cmd_bat = (
-            "$(location :generate_grammar_tables) " +
-            "--spirv-core-grammar=$(location {0}) " +
-            "--extinst-debuginfo-grammar=$(location {1}) " +
-            "--extinst-cldebuginfo100-grammar=$(location {2}) " +
-            "--extension-enum-output=$(location {3}) " +
-            "--enum-string-mapping-output=$(location {4})"
-        ).format(*fmtargs),
-        exec_tools = [":generate_grammar_tables"],
+        srcs = grammars.values(),
+        outs = outs.values(),
+        cmd = cmd,
+        cmd_bat = cmd,
+        tools = [":generate_grammar_tables"],
         visibility = ["//visibility:private"],
     )
 
-def generate_opencl_tables(version = None):
+def generate_opencl_tables(version):
     if not version:
         fail("Must specify version", "version")
-    grammars = [
-        "@spirv_headers//:spirv_opencl_grammar_" + version,
-    ]
-    outs = ["opencl.std.insts.inc"]
-    fmtargs = grammars + outs
+
+    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,
-        outs = outs,
-        cmd = (
-            "$(location :generate_grammar_tables) " +
-            "--extinst-opencl-grammar=$(location {0}) " +
-            "--opencl-insts-output=$(location {1})"
-        ).format(*fmtargs),
-        cmd_bat = (
-            "$(location :generate_grammar_tables) " +
-            "--extinst-opencl-grammar=$(location {0}) " +
-            "--opencl-insts-output=$(location {1})"
-        ).format(*fmtargs),
-        exec_tools = [":generate_grammar_tables"],
+        srcs = grammars.values(),
+        outs = outs.values(),
+        cmd = cmd,
+        cmd_bat = cmd,
+        tools = [":generate_grammar_tables"],
         visibility = ["//visibility:private"],
     )
 
-def generate_glsl_tables(version = None):
+def generate_glsl_tables(version):
     if not version:
         fail("Must specify version", "version")
-    grammars = [
-        "@spirv_headers//:spirv_glsl_grammar_" + version,
-    ]
-    outs = ["glsl.std.450.insts.inc"]
-    fmtargs = grammars + outs
+
+    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,
-        outs = outs,
-        cmd = (
-            "$(location :generate_grammar_tables) " +
-            "--extinst-glsl-grammar=$(location {0}) " +
-            "--glsl-insts-output=$(location {1})"
-        ).format(*fmtargs),
-        cmd_bat = (
-            "$(location :generate_grammar_tables) " +
-            "--extinst-glsl-grammar=$(location {0}) " +
-            "--glsl-insts-output=$(location {1})"
-        ).format(*fmtargs),
-        exec_tools = [":generate_grammar_tables"],
+        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 = ["@spirv_headers//:spirv_ext_inst_{}_grammar_unified1".format(extension_rule)]
-    outs = ["{}.insts.inc".format(extension)]
-    prefices = [operand_kind_prefix]
-    fmtargs = grammars + outs + prefices
+    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,
-        outs = outs,
-        cmd = (
-            "$(location :generate_grammar_tables) " +
-            "--extinst-vendor-grammar=$(location {0}) " +
-            "--vendor-insts-output=$(location {1}) " +
-            "--vendor-operand-kind-prefix={2}"
-        ).format(*fmtargs),
-        cmd_bat = (
-            "$(location :generate_grammar_tables) " +
-            "--extinst-vendor-grammar=$(location {0}) " +
-            "--vendor-insts-output=$(location {1}) " +
-            "--vendor-operand-kind-prefix={2}"
-        ).format(*fmtargs),
-        exec_tools = [":generate_grammar_tables"],
+        srcs = grammars.values(),
+        outs = outs.values(),
+        cmd = cmd,
+        cmd_bat = cmd,
+        tools = [":generate_grammar_tables"],
         visibility = ["//visibility:private"],
     )
 
 def generate_extinst_lang_headers(name, grammar = None):
     if not grammar:
         fail("Must specify grammar", "grammar")
-    outs = [name + ".h"]
-    fmtargs = outs
+    outs = dict(
+        extinst_output_path = name + ".h",
+    )
+    cmd = (
+        "$(location :generate_language_headers)" +
+        " --extinst-grammar=$<" +
+        " --extinst-output-path=$(location {extinst_output_path})"
+    ).format(**outs)
+
     native.genrule(
-        name = "gen_extinst_lang_headers_" + name,
+        name = "gen_extinst_lang_headers_{}".format(name),
         srcs = [grammar],
-        outs = outs,
-        cmd = (
-            "$(location :generate_language_headers) " +
-            "--extinst-grammar=$< " +
-            "--extinst-output-path=$(location {0})"
-        ).format(*fmtargs),
-        cmd_bat = (
-            "$(location :generate_language_headers) " +
-            "--extinst-grammar=$< " +
-            "--extinst-output-path=$(location {0})"
-        ).format(*fmtargs),
-        exec_tools = [":generate_language_headers"],
+        outs = outs.values(),
+        cmd = cmd,
+        cmd_bat = cmd,
+        tools = [":generate_language_headers"],
         visibility = ["//visibility:private"],
     )
-
-def base_test(name, srcs, deps = []):
-    if srcs == []:
-        return
-    if name[-5:] != "_test":
-        name = name + "_test"
-    native.cc_test(
-        name = "base_" + name,
-        srcs = srcs,
-        compatible_with = [],
-        copts = TEST_COPTS,
-        size = "large",
-        deps = [
-            ":test_common",
-            "@com_google_googletest//:gtest_main",
-            "@com_google_googletest//:gtest",
-            "@com_google_effcee//:effcee",
-        ] + deps,
-    )
-
-def lint_test(name, srcs, deps = []):
-    if name[-5:] != "_test":
-        name = name + "_test"
-    native.cc_test(
-        name = "lint_" + name,
-        srcs = srcs,
-        compatible_with = [],
-        copts = TEST_COPTS,
-        size = "large",
-        deps = [
-            ":spirv_tools_lint",
-            "@com_google_googletest//:gtest_main",
-            "@com_google_googletest//:gtest",
-            "@com_google_effcee//:effcee",
-        ] + deps,
-    )
-
-def link_test(name, srcs, deps = []):
-    if name[-5:] != "_test":
-        name = name + "_test"
-    native.cc_test(
-        name = "link_" + name,
-        srcs = srcs,
-        compatible_with = [],
-        copts = TEST_COPTS,
-        size = "large",
-        deps = [
-            ":link_test_common",
-            "@com_google_googletest//:gtest_main",
-            "@com_google_googletest//:gtest",
-            "@com_google_effcee//:effcee",
-        ] + deps,
-    )
-
-def opt_test(name, srcs, deps = []):
-    if name[-5:] != "_test":
-        name = name + "_test"
-    native.cc_test(
-        name = "opt_" + name,
-        srcs = srcs,
-        compatible_with = [],
-        copts = TEST_COPTS,
-        size = "large",
-        deps = [
-            ":opt_test_common",
-            "@com_google_googletest//:gtest_main",
-            "@com_google_googletest//:gtest",
-            "@com_google_effcee//:effcee",
-        ] + deps,
-    )
-
-def reduce_test(name, srcs, deps = []):
-    if name[-5:] != "_test":
-        name = name + "_test"
-    native.cc_test(
-        name = "reduce_" + name,
-        srcs = srcs,
-        compatible_with = [],
-        copts = TEST_COPTS,
-        size = "large",
-        deps = [
-            ":reduce_test_common",
-            ":spirv_tools_reduce",
-            "@com_google_googletest//:gtest_main",
-            "@com_google_googletest//:gtest",
-            "@com_google_effcee//:effcee",
-        ] + deps,
-    )
-
-def util_test(name, srcs, deps = []):
-    if name[-5:] != "_test":
-        name = name + "_test"
-    native.cc_test(
-        name = "util_" + name,
-        srcs = srcs,
-        compatible_with = [],
-        copts = TEST_COPTS,
-        size = "large",
-        deps = [
-            ":opt_test_common",
-            "@com_google_googletest//:gtest_main",
-            "@com_google_googletest//:gtest",
-            "@com_google_effcee//:effcee",
-        ] + deps,
-    )
-
-def val_test(name, srcs = [], copts = [], deps = [], **kwargs):
-    if name[-5:] != "_test":
-        name = name + "_test"
-    if name[:4] != "val_":
-        name = "val_" + name
-    native.cc_test(
-        name = name,
-        srcs = srcs,
-        compatible_with = [],
-        copts = TEST_COPTS + copts,
-        size = "large",
-        deps = [
-            ":val_test_common",
-            "@com_google_googletest//:gtest_main",
-            "@com_google_googletest//:gtest",
-            "@com_google_effcee//:effcee",
-        ] + deps,
-        **kwargs
-    )
diff --git a/docs/downloads.md b/docs/downloads.md
index 168937a..f56b6fe 100644
--- a/docs/downloads.md
+++ b/docs/downloads.md
@@ -2,7 +2,7 @@
 
 ## Latest builds
 
-Download the latest builds of the [master](https://github.com/KhronosGroup/SPIRV-Tools/tree/master) branch.
+Download the latest builds of the [main](https://github.com/KhronosGroup/SPIRV-Tools/tree/main) branch.
 
 ### Release build
 | Windows | Linux | MacOS |
diff --git a/docs/projects.md b/docs/projects.md
index 8f7f0bc..cc88cb3 100644
--- a/docs/projects.md
+++ b/docs/projects.md
@@ -34,7 +34,7 @@
   ones.
 * They determine if the work for a card has been completed.
 * Normally they are the person (or persons) who can approve and merge a pull
-  request into the `master` branch.
+  request into the `main` branch.
 
 Our projects organize cards into the following columns:
 * `Ideas`: Work which could be done, captured either as Cards or Notes.
@@ -51,7 +51,7 @@
   claimed by someone.
 * `Done`: Issues which have been resolved, by completing their work.
   * The changes have been applied to the repository, typically by being pushed
-  into the `master` branch.
+  into the `main` branch.
   * Other kinds of work could update repository settings, for example.
 * `Rejected ideas`: Work which has been considered, but which we don't want
   implemented.
diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt
index 179a401..5d8a3da 100644
--- a/external/CMakeLists.txt
+++ b/external/CMakeLists.txt
@@ -30,11 +30,7 @@
   # This allows flexible position of the SPIRV-Headers repo.
   set(SPIRV_HEADER_DIR ${SPIRV-Headers_SOURCE_DIR})
 else()
-  if (IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/SPIRV-Headers)
-    set(SPIRV_HEADER_DIR ${CMAKE_CURRENT_SOURCE_DIR}/SPIRV-Headers)
-  else()
-    set(SPIRV_HEADER_DIR ${CMAKE_CURRENT_SOURCE_DIR}/spirv-headers)
-  endif()
+  set(SPIRV_HEADER_DIR ${CMAKE_CURRENT_SOURCE_DIR}/spirv-headers)
 endif()
 
 if (IS_DIRECTORY ${SPIRV_HEADER_DIR})
@@ -45,8 +41,6 @@
   # Do this so enclosing projects can use SPIRV-Headers_SOURCE_DIR to find
   # headers to include.
   if (NOT DEFINED SPIRV-Headers_SOURCE_DIR)
-    set(SPIRV_HEADERS_SKIP_INSTALL ON)
-    set(SPIRV_HEADERS_SKIP_EXAMPLES ON)
     add_subdirectory(${SPIRV_HEADER_DIR})
   endif()
 else()
@@ -60,7 +54,9 @@
   if (TARGET gmock)
     message(STATUS "Google Mock already configured")
   else()
-    set(GMOCK_DIR ${CMAKE_CURRENT_SOURCE_DIR}/googletest)
+    if (NOT GMOCK_DIR)
+      set(GMOCK_DIR ${CMAKE_CURRENT_SOURCE_DIR}/googletest)
+    endif()
     if(EXISTS ${GMOCK_DIR})
       if(MSVC)
         # Our tests use ::testing::Combine.  Work around a compiler
@@ -77,7 +73,7 @@
       # gtest requires special defines for building as a shared
       # library, simply always build as static.
       push_variable(BUILD_SHARED_LIBS 0)
-      add_subdirectory(${GMOCK_DIR} EXCLUDE_FROM_ALL)
+      add_subdirectory(${GMOCK_DIR} ${CMAKE_CURRENT_BINARY_DIR}/googletest EXCLUDE_FROM_ALL)
       pop_variable(BUILD_SHARED_LIBS)
     endif()
   endif()
@@ -95,10 +91,22 @@
 
   # Find Effcee and RE2, for testing.
 
+  # RE2 depends on Abseil. We set absl_SOURCE_DIR if it is not already set, so
+  # that effcee can find abseil.
+  if(NOT TARGET absl::base)
+    if (NOT absl_SOURCE_DIR)
+      if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/abseil_cpp)
+        set(absl_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/abseil_cpp" CACHE STRING "Abseil source dir" )
+      endif()
+    endif()
+  endif()
+
   # First find RE2, since Effcee depends on it.
   # If already configured, then use that.  Otherwise, prefer to find it under 're2'
   # in this directory.
   if (NOT TARGET re2)
+
+
     # If we are configuring RE2, then turn off its testing.  It takes a long time and
     # does not add much value for us.  If an enclosing project configured RE2, then it
     # has already chosen whether to enable RE2 testing.
@@ -156,7 +164,7 @@
 
   if(NOT TARGET protobuf::libprotobuf OR NOT TARGET protobuf::protoc)
 
-    set(SPIRV_TOOLS_PROTOBUF_DIR ${CMAKE_CURRENT_SOURCE_DIR}/protobuf/cmake)
+    set(SPIRV_TOOLS_PROTOBUF_DIR ${CMAKE_CURRENT_SOURCE_DIR}/protobuf)
     if (NOT IS_DIRECTORY ${SPIRV_TOOLS_PROTOBUF_DIR})
       message(
           FATAL_ERROR
diff --git a/include/spirv-tools/instrument.hpp b/include/spirv-tools/instrument.hpp
index a19491f..ae9278b 100644
--- a/include/spirv-tools/instrument.hpp
+++ b/include/spirv-tools/instrument.hpp
@@ -36,16 +36,25 @@
 // generated by InstrumentPass::GenDebugStreamWrite. This method is utilized
 // by InstBindlessCheckPass, InstBuffAddrCheckPass, and InstDebugPrintfPass.
 //
-// The first member of the debug output buffer contains the next available word
+// The 1st member of the debug output buffer contains a set of flags
+// controlling the behavior of instrumentation code.
+static const int kDebugOutputFlagsOffset = 0;
+
+// Values stored at kDebugOutputFlagsOffset
+enum kInstFlags : unsigned int {
+  kInstBufferOOBEnable = 0x1,
+};
+
+// The 2nd member of the debug output buffer contains the next available word
 // in the data stream to be written. Shaders will atomically read and update
 // this value so as not to overwrite each others records. This value must be
 // initialized to zero
-static const int kDebugOutputSizeOffset = 0;
+static const int kDebugOutputSizeOffset = 1;
 
-// The second member of the output buffer is the start of the stream of records
+// The 3rd member of the output buffer is the start of the stream of records
 // written by the instrumented shaders. Each record represents a validation
 // error. The format of the records is documented below.
-static const int kDebugOutputDataOffset = 1;
+static const int kDebugOutputDataOffset = 2;
 
 // Common Stream Record Offsets
 //
@@ -124,72 +133,6 @@
 // Size of Common and Stage-specific Members
 static const int kInstStageOutCnt = kInstCommonOutCnt + 3;
 
-// Validation Error Code Offset
-//
-// This identifies the validation error. It also helps to identify
-// how many words follow in the record and their meaning.
-static const int kInstValidationOutError = kInstStageOutCnt;
-
-// Validation-specific Output Record Offsets
-//
-// Each different validation will generate a potentially different
-// number of words at the end of the record giving more specifics
-// about the validation error.
-//
-// A bindless bounds error will output the index and the bound.
-static const int kInstBindlessBoundsOutDescIndex = kInstStageOutCnt + 1;
-static const int kInstBindlessBoundsOutDescBound = kInstStageOutCnt + 2;
-static const int kInstBindlessBoundsOutUnused = kInstStageOutCnt + 3;
-static const int kInstBindlessBoundsOutCnt = kInstStageOutCnt + 4;
-
-// A descriptor uninitialized error will output the index.
-static const int kInstBindlessUninitOutDescIndex = kInstStageOutCnt + 1;
-static const int kInstBindlessUninitOutUnused = kInstStageOutCnt + 2;
-static const int kInstBindlessUninitOutUnused2 = kInstStageOutCnt + 3;
-static const int kInstBindlessUninitOutCnt = kInstStageOutCnt + 4;
-
-// A buffer out-of-bounds error will output the descriptor
-// index, the buffer offset and the buffer size
-static const int kInstBindlessBuffOOBOutDescIndex = kInstStageOutCnt + 1;
-static const int kInstBindlessBuffOOBOutBuffOff = kInstStageOutCnt + 2;
-static const int kInstBindlessBuffOOBOutBuffSize = kInstStageOutCnt + 3;
-static const int kInstBindlessBuffOOBOutCnt = kInstStageOutCnt + 4;
-
-// A buffer address unalloc error will output the 64-bit pointer in
-// two 32-bit pieces, lower bits first.
-static const int kInstBuffAddrUnallocOutDescPtrLo = kInstStageOutCnt + 1;
-static const int kInstBuffAddrUnallocOutDescPtrHi = kInstStageOutCnt + 2;
-static const int kInstBuffAddrUnallocOutCnt = kInstStageOutCnt + 3;
-
-// Maximum Output Record Member Count
-static const int kInstMaxOutCnt = kInstStageOutCnt + 4;
-
-// Validation Error Codes
-//
-// These are the possible validation error codes.
-static const int kInstErrorBindlessBounds = 0;
-static const int kInstErrorBindlessUninit = 1;
-static const int kInstErrorBuffAddrUnallocRef = 2;
-// Deleted: static const int kInstErrorBindlessBuffOOB = 3;
-// This comment will will remain for 2 releases to allow
-// for the transition of all builds. Buffer OOB is
-// generating the following four differentiated codes instead:
-static const int kInstErrorBuffOOBUniform = 4;
-static const int kInstErrorBuffOOBStorage = 5;
-static const int kInstErrorBuffOOBUniformTexel = 6;
-static const int kInstErrorBuffOOBStorageTexel = 7;
-static const int kInstErrorMax = kInstErrorBuffOOBStorageTexel;
-
-// Direct Input Buffer Offsets
-//
-// The following values provide member offsets into the input buffers
-// consumed by InstrumentPass::GenDebugDirectRead(). This method is utilized
-// by InstBindlessCheckPass.
-//
-// The only object in an input buffer is a runtime array of unsigned
-// integers. Each validation will have its own formatting of this array.
-static const int kDebugInputDataOffset = 0;
-
 // Debug Buffer Bindings
 //
 // These are the bindings for the different buffers which are
@@ -208,52 +151,6 @@
 // This is the output buffer written by InstDebugPrintfPass.
 static const int kDebugOutputPrintfStream = 3;
 
-// Bindless Validation Input Buffer Format
-//
-// An input buffer for bindless validation consists of a single array of
-// unsigned integers we will call Data[]. This array is formatted as follows.
-//
-// At offset kDebugInputBindlessInitOffset in Data[] is a single uint which
-// gives an offset to the start of the bindless initialization data. More
-// specifically, if the following value is zero, we know that the descriptor at
-// (set = s, binding = b, index = i) is not initialized; if the value is
-// non-zero, and the descriptor points to a buffer, the value is the length of
-// the buffer in bytes and can be used to check for out-of-bounds buffer
-// references:
-// Data[ i + Data[ b + Data[ s + Data[ kDebugInputBindlessInitOffset ] ] ] ]
-static const int kDebugInputBindlessInitOffset = 0;
-
-// At offset kDebugInputBindlessOffsetLengths is some number of uints which
-// provide the bindless length data. More specifically, the number of
-// descriptors at (set=s, binding=b) is:
-// Data[ Data[ s + kDebugInputBindlessOffsetLengths ] + b ]
-static const int kDebugInputBindlessOffsetLengths = 1;
-
-// Buffer Device Address Input Buffer Format
-//
-// An input buffer for buffer device address validation consists of a single
-// array of unsigned 64-bit integers we will call Data[]. This array is
-// formatted as follows:
-//
-// At offset kDebugInputBuffAddrPtrOffset is a list of sorted valid buffer
-// addresses. The list is terminated with the address 0xffffffffffffffff.
-// If 0x0 is not a valid buffer address, this address is inserted at the
-// start of the list.
-//
-static const int kDebugInputBuffAddrPtrOffset = 1;
-//
-// At offset kDebugInputBuffAddrLengthOffset in Data[] is a single uint64 which
-// gives an offset to the start of the buffer length data. More
-// specifically, for a buffer whose pointer is located at input buffer offset
-// i, the length is located at:
-//
-// Data[ i - kDebugInputBuffAddrPtrOffset
-//         + Data[ kDebugInputBuffAddrLengthOffset ] ]
-//
-// The length associated with the 0xffffffffffffffff address is zero. If
-// not a valid buffer, the length associated with the 0x0 address is zero.
-static const int kDebugInputBuffAddrLengthOffset = 0;
-
 }  // namespace spvtools
 
 #endif  // INCLUDE_SPIRV_TOOLS_INSTRUMENT_HPP_
diff --git a/include/spirv-tools/libspirv.h b/include/spirv-tools/libspirv.h
index b549efb..b70f084 100644
--- a/include/spirv-tools/libspirv.h
+++ b/include/spirv-tools/libspirv.h
@@ -143,6 +143,7 @@
   // may be larger than 32, which would require such a typed literal value to
   // occupy multiple SPIR-V words.
   SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER,
+  SPV_OPERAND_TYPE_LITERAL_FLOAT,  // Always 32-bit float.
 
   // Set 3:  The literal string operand type.
   SPV_OPERAND_TYPE_LITERAL_STRING,
@@ -285,6 +286,22 @@
   // An optional packed vector format
   SPV_OPERAND_TYPE_OPTIONAL_PACKED_VECTOR_FORMAT,
 
+  // Concrete operand types for cooperative matrix.
+  SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_OPERANDS,
+  // An optional cooperative matrix operands
+  SPV_OPERAND_TYPE_OPTIONAL_COOPERATIVE_MATRIX_OPERANDS,
+  SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_LAYOUT,
+  SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_USE,
+
+  // Enum type from SPV_INTEL_global_variable_fpga_decorations
+  SPV_OPERAND_TYPE_INITIALIZATION_MODE_QUALIFIER,
+  // Enum type from SPV_INTEL_global_variable_host_access
+  SPV_OPERAND_TYPE_HOST_ACCESS_QUALIFIER,
+  // Enum type from SPV_INTEL_cache_controls
+  SPV_OPERAND_TYPE_LOAD_CACHE_CONTROL,
+  // Enum type from SPV_INTEL_cache_controls
+  SPV_OPERAND_TYPE_STORE_CACHE_CONTROL,
+
   // This is a sentinel value, and does not represent an operand type.
   // It should come last.
   SPV_OPERAND_TYPE_NUM_OPERAND_TYPES,
@@ -402,6 +419,19 @@
   uint16_t num_operands;
 } spv_parsed_instruction_t;
 
+typedef struct spv_parsed_header_t {
+  // The magic number of the SPIR-V module.
+  uint32_t magic;
+  // Version number.
+  uint32_t version;
+  // Generator's magic number.
+  uint32_t generator;
+  // IDs bound for this module (0 < id < bound).
+  uint32_t bound;
+  // reserved.
+  uint32_t reserved;
+} spv_parsed_header_t;
+
 typedef struct spv_const_binary_t {
   const uint32_t* code;
   const size_t wordCount;
@@ -441,6 +471,8 @@
 
 typedef struct spv_fuzzer_options_t spv_fuzzer_options_t;
 
+typedef struct spv_optimizer_t spv_optimizer_t;
+
 // Type Definitions
 
 typedef spv_const_binary_t* spv_const_binary;
@@ -900,6 +932,63 @@
     const size_t num_words, spv_parsed_header_fn_t parse_header,
     spv_parsed_instruction_fn_t parse_instruction, spv_diagnostic* diagnostic);
 
+// The optimizer interface.
+
+// A pointer to a function that accepts a log message from an optimizer.
+typedef void (*spv_message_consumer)(
+    spv_message_level_t, const char*, const spv_position_t*, const char*);
+
+// Creates and returns an optimizer object.  This object must be passed to
+// optimizer APIs below and is valid until passed to spvOptimizerDestroy.
+SPIRV_TOOLS_EXPORT spv_optimizer_t* spvOptimizerCreate(spv_target_env env);
+
+// Destroys the given optimizer object.
+SPIRV_TOOLS_EXPORT void spvOptimizerDestroy(spv_optimizer_t* optimizer);
+
+// Sets an spv_message_consumer on an optimizer object.
+SPIRV_TOOLS_EXPORT void spvOptimizerSetMessageConsumer(
+    spv_optimizer_t* optimizer, spv_message_consumer consumer);
+
+// Registers passes that attempt to legalize the generated code.
+SPIRV_TOOLS_EXPORT void spvOptimizerRegisterLegalizationPasses(
+    spv_optimizer_t* optimizer);
+
+// Registers passes that attempt to improve performance of generated code.
+SPIRV_TOOLS_EXPORT void spvOptimizerRegisterPerformancePasses(
+    spv_optimizer_t* optimizer);
+
+// Registers passes that attempt to improve the size of generated code.
+SPIRV_TOOLS_EXPORT void spvOptimizerRegisterSizePasses(
+    spv_optimizer_t* optimizer);
+
+// Registers a pass specified by a flag in an optimizer object.
+SPIRV_TOOLS_EXPORT bool spvOptimizerRegisterPassFromFlag(
+    spv_optimizer_t* optimizer, const char* flag);
+
+// Registers passes specified by length number of flags in an optimizer object.
+SPIRV_TOOLS_EXPORT bool spvOptimizerRegisterPassesFromFlags(
+    spv_optimizer_t* optimizer, const char** flags, const size_t flag_count);
+
+// Optimizes the SPIR-V code of size |word_count| pointed to by |binary| and
+// returns an optimized spv_binary in |optimized_binary|.
+//
+// Returns SPV_SUCCESS on successful optimization, whether or not the module is
+// modified.  Returns an SPV_ERROR_* if the module fails to validate or if
+// errors occur when processing using any of the registered passes.  In that
+// case, no further passes are executed and the |optimized_binary| contents may
+// be invalid.
+//
+// By default, the binary is validated before any transforms are performed,
+// and optionally after each transform.  Validation uses SPIR-V spec rules
+// for the SPIR-V version named in the binary's header (at word offset 1).
+// Additionally, if the target environment is a client API (such as
+// Vulkan 1.1), then validate for that client API version, to the extent
+// that it is verifiable from data in the binary itself, or from the
+// validator options set on the optimizer options.
+SPIRV_TOOLS_EXPORT spv_result_t spvOptimizerRun(
+    spv_optimizer_t* optimizer, const uint32_t* binary, const size_t word_count,
+    spv_binary* optimized_binary, const spv_optimizer_options options);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/spirv-tools/libspirv.hpp b/include/spirv-tools/libspirv.hpp
index 408e3eb..ee6c846 100644
--- a/include/spirv-tools/libspirv.hpp
+++ b/include/spirv-tools/libspirv.hpp
@@ -31,6 +31,11 @@
     const spv_position_t& /* position */, const char* /* message */
     )>;
 
+using HeaderParser = std::function<spv_result_t(
+    const spv_endianness_t endianess, const spv_parsed_header_t& instruction)>;
+using InstructionParser =
+    std::function<spv_result_t(const spv_parsed_instruction_t& instruction)>;
+
 // C++ RAII wrapper around the C context object spv_context.
 class Context {
  public:
@@ -336,6 +341,23 @@
                    std::string* text,
                    uint32_t options = kDefaultDisassembleOption) const;
 
+  // Parses a SPIR-V binary, specified as counted sequence of 32-bit words.
+  // Parsing feedback is provided via two callbacks provided as std::function.
+  // In a valid parse the parsed-header callback is called once, and
+  // then the parsed-instruction callback is called once for each instruction
+  // in the stream.
+  // Returns true on successful parsing.
+  // If diagnostic is non-null, a diagnostic is emitted on failed parsing.
+  // If diagnostic is null the context's message consumer
+  // will be used to emit any errors. If a callback returns anything other than
+  // SPV_SUCCESS, then that status code is returned, no further callbacks are
+  // issued, and no additional diagnostics are emitted.
+  // This is a wrapper around the C API spvBinaryParse.
+  bool Parse(const std::vector<uint32_t>& binary,
+             const HeaderParser& header_parser,
+             const InstructionParser& instruction_parser,
+             spv_diagnostic* diagnostic = nullptr);
+
   // Validates the given SPIR-V |binary|. Returns true if no issues are found.
   // Otherwise, returns false and communicates issues via the message consumer
   // registered.
diff --git a/include/spirv-tools/linker.hpp b/include/spirv-tools/linker.hpp
index d2f3e72..5b60cb9 100644
--- a/include/spirv-tools/linker.hpp
+++ b/include/spirv-tools/linker.hpp
@@ -26,11 +26,6 @@
 
 class LinkerOptions {
  public:
-  LinkerOptions()
-      : create_library_(false),
-        verify_ids_(false),
-        allow_partial_linkage_(false) {}
-
   // Returns whether a library or an executable should be produced by the
   // linking phase.
   //
@@ -63,10 +58,16 @@
     allow_partial_linkage_ = allow_partial_linkage;
   }
 
+  bool GetUseHighestVersion() const { return use_highest_version_; }
+  void SetUseHighestVersion(bool use_highest_vers) {
+    use_highest_version_ = use_highest_vers;
+  }
+
  private:
-  bool create_library_;
-  bool verify_ids_;
-  bool allow_partial_linkage_;
+  bool create_library_{false};
+  bool verify_ids_{false};
+  bool allow_partial_linkage_{false};
+  bool use_highest_version_{false};
 };
 
 // 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 9497356..53ebc59 100644
--- a/include/spirv-tools/optimizer.hpp
+++ b/include/spirv-tools/optimizer.hpp
@@ -19,6 +19,7 @@
 #include <ostream>
 #include <string>
 #include <unordered_map>
+#include <unordered_set>
 #include <utility>
 #include <vector>
 
@@ -96,12 +97,24 @@
   // Registers passes that attempt to improve performance of generated code.
   // This sequence of passes is subject to constant review and will change
   // from time to time.
+  //
+  // If |preserve_interface| is true, all non-io variables in the entry point
+  // interface are considered live and are not eliminated.
+  // |preserve_interface| should be true if HLSL is generated
+  // from the SPIR-V bytecode.
   Optimizer& RegisterPerformancePasses();
+  Optimizer& RegisterPerformancePasses(bool preserve_interface);
 
   // Registers passes that attempt to improve the size of generated code.
   // This sequence of passes is subject to constant review and will change
   // from time to time.
+  //
+  // If |preserve_interface| is true, all non-io variables in the entry point
+  // interface are considered live and are not eliminated.
+  // |preserve_interface| should be true if HLSL is generated
+  // from the SPIR-V bytecode.
   Optimizer& RegisterSizePasses();
+  Optimizer& RegisterSizePasses(bool preserve_interface);
 
   // Registers passes that attempt to legalize the generated code.
   //
@@ -111,7 +124,13 @@
   //
   // This sequence of passes is subject to constant review and will change
   // from time to time.
+  //
+  // If |preserve_interface| is true, all non-io variables in the entry point
+  // interface are considered live and are not eliminated.
+  // |preserve_interface| should be true if HLSL is generated
+  // from the SPIR-V bytecode.
   Optimizer& RegisterLegalizationPasses();
+  Optimizer& RegisterLegalizationPasses(bool preserve_interface);
 
   // Register passes specified in the list of |flags|.  Each flag must be a
   // string of a form accepted by Optimizer::FlagHasValidForm().
@@ -520,8 +539,14 @@
 // interface are considered live and are not eliminated. This mode is needed
 // by GPU-Assisted validation instrumentation, where a change in the interface
 // is not allowed.
+//
+// If |remove_outputs| is true, allow outputs to be removed from the interface.
+// This is only safe if the caller knows that there is no corresponding input
+// variable in the following shader. It is false by default.
 Optimizer::PassToken CreateAggressiveDCEPass();
 Optimizer::PassToken CreateAggressiveDCEPass(bool preserve_interface);
+Optimizer::PassToken CreateAggressiveDCEPass(bool preserve_interface,
+                                             bool remove_outputs);
 
 // Creates a remove-unused-interface-variables pass.
 // Removes variables referenced on the |OpEntryPoint| instruction that are not
@@ -741,19 +766,9 @@
 // potentially de-optimizing the instrument code, for example, inlining
 // the debug record output function throughout the module.
 //
-// The instrumentation will read and write buffers in debug
-// descriptor set |desc_set|. It will write |shader_id| in each output record
+// The instrumentation will write |shader_id| in each output record
 // to identify the shader module which generated the record.
-// |desc_length_enable| controls instrumentation of runtime descriptor array
-// references, |desc_init_enable| controls instrumentation of descriptor
-// initialization checking, and |buff_oob_enable| controls instrumentation
-// of storage and uniform buffer bounds checking, all of which require input
-// buffer support. |texbuff_oob_enable| controls instrumentation of texel
-// buffers, which does not require input buffer support.
-Optimizer::PassToken CreateInstBindlessCheckPass(
-    uint32_t desc_set, uint32_t shader_id, bool desc_length_enable = false,
-    bool desc_init_enable = false, bool buff_oob_enable = false,
-    bool texbuff_oob_enable = false);
+Optimizer::PassToken CreateInstBindlessCheckPass(uint32_t shader_id);
 
 // Create a pass to instrument physical buffer address checking
 // This pass instruments all physical buffer address references to check that
@@ -774,8 +789,7 @@
 // The instrumentation will read and write buffers in debug
 // descriptor set |desc_set|. It will write |shader_id| in each output record
 // to identify the shader module which generated the record.
-Optimizer::PassToken CreateInstBuffAddrCheckPass(uint32_t desc_set,
-                                                 uint32_t shader_id);
+Optimizer::PassToken CreateInstBuffAddrCheckPass(uint32_t shader_id);
 
 // Create a pass to instrument OpDebugPrintf instructions.
 // This pass replaces all OpDebugPrintf instructions with instructions to write
@@ -887,12 +901,59 @@
 Optimizer::PassToken CreateInterpolateFixupPass();
 
 // Removes unused components from composite input variables. Current
-// implementation just removes trailing unused components from input arrays.
-// The pass performs best after maximizing dead code removal. A subsequent dead
-// code elimination pass would be beneficial in removing newly unused component
-// types.
+// implementation just removes trailing unused components from input arrays
+// and structs. The pass performs best after maximizing dead code removal.
+// A subsequent dead code elimination pass would be beneficial in removing
+// newly unused component types.
+//
+// WARNING: This pass can only be safely applied standalone to vertex shaders
+// as it can otherwise cause interface incompatibilities with the preceding
+// shader in the pipeline. If applied to non-vertex shaders, the user should
+// follow by applying EliminateDeadOutputStores and
+// EliminateDeadOutputComponents to the preceding shader.
 Optimizer::PassToken CreateEliminateDeadInputComponentsPass();
 
+// Removes unused components from composite output variables. Current
+// implementation just removes trailing unused components from output arrays
+// and structs. The pass performs best after eliminating dead output stores.
+// A subsequent dead code elimination pass would be beneficial in removing
+// newly unused component types. Currently only supports vertex and fragment
+// shaders.
+//
+// WARNING: This pass cannot be safely applied standalone as it can cause
+// interface incompatibility with the following shader in the pipeline. The
+// user should first apply EliminateDeadInputComponents to the following
+// shader, then apply EliminateDeadOutputStores to this shader.
+Optimizer::PassToken CreateEliminateDeadOutputComponentsPass();
+
+// Removes unused components from composite input variables. This safe
+// version will not cause interface incompatibilities since it only changes
+// vertex shaders. The current implementation just removes trailing unused
+// components from input structs and input arrays. The pass performs best
+// after maximizing dead code removal. A subsequent dead code elimination
+// pass would be beneficial in removing newly unused component types.
+Optimizer::PassToken CreateEliminateDeadInputComponentsSafePass();
+
+// Analyzes shader and populates |live_locs| and |live_builtins|. Best results
+// will be obtained if shader has all dead code eliminated first. |live_locs|
+// and |live_builtins| are subsequently used when calling
+// CreateEliminateDeadOutputStoresPass on the preceding shader. Currently only
+// supports tesc, tese, geom, and frag shaders.
+Optimizer::PassToken CreateAnalyzeLiveInputPass(
+    std::unordered_set<uint32_t>* live_locs,
+    std::unordered_set<uint32_t>* live_builtins);
+
+// Removes stores to output locations not listed in |live_locs| or
+// |live_builtins|. Best results are obtained if constant propagation is
+// performed first. A subsequent call to ADCE will eliminate any dead code
+// created by the removal of the stores. A subsequent call to
+// CreateEliminateDeadOutputComponentsPass will eliminate any dead output
+// components created by the elimination of the stores. Currently only supports
+// vert, tesc, tese, and geom shaders.
+Optimizer::PassToken CreateEliminateDeadOutputStoresPass(
+    std::unordered_set<uint32_t>* live_locs,
+    std::unordered_set<uint32_t>* live_builtins);
+
 // Creates a convert-to-sampled-image pass to convert images and/or
 // samplers with given pairs of descriptor set and binding to sampled image.
 // If a pair of an image and a sampler have the same pair of descriptor set and
@@ -917,6 +978,28 @@
 // object, currently the pass would remove accesschain pointer argument passed
 // to the function
 Optimizer::PassToken CreateFixFuncCallArgumentsPass();
+
+// Creates a trim-capabilities pass.
+// This pass removes unused capabilities for a given module, and if possible,
+// associated extensions.
+// See `trim_capabilities.h` for the list of supported capabilities.
+//
+// If the module contains unsupported capabilities, this pass will ignore them.
+// This should be fine in most cases, but could yield to incorrect results if
+// the unknown capability interacts with one of the trimmed capabilities.
+Optimizer::PassToken CreateTrimCapabilitiesPass();
+
+// Creates a switch-descriptorset pass.
+// This pass changes any DescriptorSet decorations with the value |ds_from| to
+// use the new value |ds_to|.
+Optimizer::PassToken CreateSwitchDescriptorSetPass(uint32_t ds_from,
+                                                   uint32_t ds_to);
+
+// Creates an invocation interlock placement pass.
+// This pass ensures that an entry point will have at most one
+// OpBeginInterlockInvocationEXT and one OpEndInterlockInvocationEXT, in that
+// order.
+Optimizer::PassToken CreateInvocationInterlockPlacementPass();
 }  // namespace spvtools
 
 #endif  // INCLUDE_SPIRV_TOOLS_OPTIMIZER_HPP_
diff --git a/kokoro/check-format/build.sh b/kokoro/check-format/build.sh
index 8a5df9a..96603e4 100644
--- a/kokoro/check-format/build.sh
+++ b/kokoro/check-format/build.sh
@@ -23,6 +23,11 @@
 BUILD_ROOT=$PWD
 SRC=$PWD/github/SPIRV-Tools
 
+# 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
diff --git a/kokoro/macos-clang-debug/build.sh b/kokoro/macos-clang-debug/build.sh
index 8d9a062..fca76fc 100644
--- a/kokoro/macos-clang-debug/build.sh
+++ b/kokoro/macos-clang-debug/build.sh
@@ -22,4 +22,3 @@
 
 SCRIPT_DIR=`dirname "$BASH_SOURCE"`
 source $SCRIPT_DIR/../scripts/macos/build.sh Debug
-
diff --git a/kokoro/macos-clang-release-bazel/build.sh b/kokoro/macos-clang-release-bazel/build.sh
index c62611a..74f9e23 100644
--- a/kokoro/macos-clang-release-bazel/build.sh
+++ b/kokoro/macos-clang-release-bazel/build.sh
@@ -24,21 +24,22 @@
 CXX=clang++
 SRC=$PWD/github/SPIRV-Tools
 
+# 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
+
 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
+/usr/bin/python3 utils/git-sync-deps --treeless
 
 # Get bazel 5.0.0
 gsutil cp gs://bazel/5.0.0/release/bazel-5.0.0-darwin-x86_64 .
 chmod +x bazel-5.0.0-darwin-x86_64
 
 echo $(date): Build everything...
-./bazel-5.0.0-darwin-x86_64 build :all
+./bazel-5.0.0-darwin-x86_64 build --cxxopt=-std=c++17 :all
 echo $(date): Build completed.
 
 echo $(date): Starting bazel test...
-./bazel-5.0.0-darwin-x86_64 test :all
+./bazel-5.0.0-darwin-x86_64 test --cxxopt=-std=c++17 :all
 echo $(date): Bazel test completed.
diff --git a/kokoro/macos-clang-release/build.sh b/kokoro/macos-clang-release/build.sh
index ccc8b16..b1460a9 100644
--- a/kokoro/macos-clang-release/build.sh
+++ b/kokoro/macos-clang-release/build.sh
@@ -22,4 +22,3 @@
 
 SCRIPT_DIR=`dirname "$BASH_SOURCE"`
 source $SCRIPT_DIR/../scripts/macos/build.sh RelWithDebInfo
-
diff --git a/kokoro/scripts/linux/build-docker.sh b/kokoro/scripts/linux/build-docker.sh
index 80043b8..a0dc96a 100755
--- a/kokoro/scripts/linux/build-docker.sh
+++ b/kokoro/scripts/linux/build-docker.sh
@@ -20,6 +20,11 @@
 # Display commands being run.
 set -x
 
+# 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
+
 . /bin/using.sh # Declare the bash `using` function for configuring toolchains.
 
 if [ $COMPILER = "clang" ]; then
@@ -30,14 +35,6 @@
 
 cd $ROOT_DIR
 
-function clone_if_missing() {
-  url=$1
-  dir=$2
-  if [[ ! -d "$dir" ]]; then
-    git clone ${@:3} "$url" "$dir"
-  fi
-}
-
 function clean_dir() {
   dir=$1
   if [[ -d "$dir" ]]; then
@@ -46,12 +43,10 @@
   mkdir "$dir"
 }
 
-clone_if_missing https://github.com/KhronosGroup/SPIRV-Headers external/spirv-headers --depth=1
-clone_if_missing https://github.com/google/googletest          external/googletest
-pushd external/googletest; git reset --hard 1fb1bb23bb8418dc73a5a9a82bbed31dc610fec7; popd
-clone_if_missing https://github.com/google/effcee              external/effcee        --depth=1
-clone_if_missing https://github.com/google/re2                 external/re2           --depth=1
-clone_if_missing https://github.com/protocolbuffers/protobuf   external/protobuf      --branch v3.13.0.1
+if [ $TOOL != "cmake-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
@@ -136,6 +131,7 @@
   git clone https://github.com/KhronosGroup/SPIRV-Headers.git spirv-headers
   git clone https://github.com/google/re2
   git clone https://github.com/google/effcee
+  git clone https://github.com/abseil/abseil-cpp abseil_cpp
 
   cd $SHADERC_DIR
   mkdir build
@@ -146,7 +142,7 @@
   cmake -GNinja -DRE2_BUILD_TESTING=OFF -DCMAKE_BUILD_TYPE="Release" ..
 
   echo $(date): Build glslang...
-  ninja glslangValidator
+  ninja glslang-standalone
 
   echo $(date): Build everything...
   ninja
@@ -160,7 +156,7 @@
   echo $(date): ctest completed.
 elif [ $TOOL = "cmake-android-ndk" ]; then
   using cmake-3.17.2
-  using ndk-r21d
+  using ndk-r25c
   using ninja-1.10.0
 
   clean_dir "$ROOT_DIR/build"
@@ -168,7 +164,7 @@
 
   echo $(date): Starting build...
   cmake -DCMAKE_BUILD_TYPE=Release \
-        -DANDROID_NATIVE_API_LEVEL=android-16 \
+        -DANDROID_NATIVE_API_LEVEL=android-24 \
         -DANDROID_ABI="armeabi-v7a with NEON" \
         -DSPIRV_SKIP_TESTS=ON \
         -DCMAKE_TOOLCHAIN_FILE="$ANDROID_NDK_HOME/build/cmake/android.toolchain.cmake" \
@@ -180,7 +176,7 @@
   ninja
   echo $(date): Build completed.
 elif [ $TOOL = "android-ndk-build" ]; then
-  using ndk-r21d
+  using ndk-r25c
 
   clean_dir "$ROOT_DIR/build"
   cd "$ROOT_DIR/build"
@@ -198,10 +194,10 @@
   using bazel-5.0.0
 
   echo $(date): Build everything...
-  bazel build :all
+  bazel build --cxxopt=-std=c++17 :all
   echo $(date): Build completed.
 
   echo $(date): Starting bazel test...
-  bazel test :all
+  bazel test --cxxopt=-std=c++17 :all
   echo $(date): Bazel test completed.
 fi
diff --git a/kokoro/scripts/linux/build.sh b/kokoro/scripts/linux/build.sh
index 85d4b61..688ba79 100644
--- a/kokoro/scripts/linux/build.sh
+++ b/kokoro/scripts/linux/build.sh
@@ -26,6 +26,18 @@
 TOOL=$3
 BUILD_SHA=${KOKORO_GITHUB_COMMIT:-$KOKORO_GITHUB_PULL_REQUEST_COMMIT}
 
+# chown the given directory to the current user, if it exists.
+# Docker creates files with the root user - this can upset the Kokoro artifact copier.
+function chown_dir() {
+  dir=$1
+  if [[ -d "$dir" ]]; then
+    sudo chown -R "$(id -u):$(id -g)" "$dir"
+  fi
+}
+
+set +e
+# Allow build failures
+
 # "--privileged" is required to run ptrace in the asan builds.
 docker run --rm -i \
   --privileged \
@@ -41,16 +53,11 @@
   --env BUILD_SHA="${BUILD_SHA}" \
   --entrypoint "${SCRIPT_DIR}/build-docker.sh" \
   "gcr.io/shaderc-build/radial-build:latest"
+RESULT=$?
 
-
-# chown the given directory to the current user, if it exists.
-# Docker creates files with the root user - this can upset the Kokoro artifact copier.
-function chown_dir() {
-  dir=$1
-  if [[ -d "$dir" ]]; then
-    sudo chown -R "$(id -u):$(id -g)" "$dir"
-  fi
-}
-
+# This is important. If the permissions are not fixed, kokoro will fail
+# to pull build artifacts, and put the build in tool-failure state, which
+# blocks the logs.
 chown_dir "${ROOT_DIR}/build"
 chown_dir "${ROOT_DIR}/external"
+exit $RESULT
diff --git a/kokoro/scripts/macos/build.sh b/kokoro/scripts/macos/build.sh
index 4612823..8381f87 100644
--- a/kokoro/scripts/macos/build.sh
+++ b/kokoro/scripts/macos/build.sh
@@ -24,6 +24,11 @@
 SRC=$PWD/github/SPIRV-Tools
 BUILD_TYPE=$1
 
+# 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 NINJA.
 wget -q https://github.com/ninja-build/ninja/releases/download/v1.8.2/ninja-mac.zip
 unzip -q ninja-mac.zip
@@ -31,23 +36,16 @@
 export PATH="$PWD:$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
-git clone --depth=1 --branch v3.13.0.1 https://github.com/protocolbuffers/protobuf external/protobuf
+python3 utils/git-sync-deps --treeless
 
 mkdir build && cd $SRC/build
 
 # Invoke the build.
 BUILD_SHA=${KOKORO_GITHUB_COMMIT:-$KOKORO_GITHUB_PULL_REQUEST_COMMIT}
 echo $(date): Starting build...
-# We need Python 3.  At the moment python3.7 is the newest Python on Kokoro.
 cmake \
   -GNinja \
   -DCMAKE_INSTALL_PREFIX=$KOKORO_ARTIFACTS_DIR/install \
-  -DPYTHON_EXECUTABLE:FILEPATH=/usr/local/bin/python3.7 \
   -DCMAKE_C_COMPILER=clang \
   -DCMAKE_CXX_COMPILER=clang++ \
   -DCMAKE_BUILD_TYPE=$BUILD_TYPE \
diff --git a/kokoro/scripts/windows/build.bat b/kokoro/scripts/windows/build.bat
index 8c9d689..fe15f2d 100644
--- a/kokoro/scripts/windows/build.bat
+++ b/kokoro/scripts/windows/build.bat
@@ -24,26 +24,23 @@
 :: Force usage of python 3.6
 set PATH=C:\python36;"C:\Program Files\cmake-3.23.1-windows-x86_64\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
-git clone --depth=1 --branch v3.13.0.1 https://github.com/protocolbuffers/protobuf external/protobuf
-
 :: #########################################
 :: 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..."
-) else if %VS_VERSION% == 2015 (
-  call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x64
-  echo "Using VS 2015..."
+
+  :: 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..."
 )
 
 cd %SRC%
+python utils/git-sync-deps --treeless
+
 mkdir build
 cd build
 
@@ -62,6 +59,10 @@
 :: Build spirv-fuzz
 set CMAKE_FLAGS=%CMAKE_FLAGS% -DSPIRV_BUILD_FUZZER=ON
 
+if "%BUILD_TESTS%" == "NO" (
+  set CMAKE_FLAGS=-DSPIRV_SKIP_TESTS=ON %CMAKE_FLAGS%
+) 
+
 cmake %CMAKE_FLAGS% ..
 
 if %ERRORLEVEL% NEQ 0 exit /b %ERRORLEVEL%
@@ -77,10 +78,12 @@
 :: ################################################
 :: Run the tests
 :: ################################################
-echo "Running Tests... %DATE% %TIME%"
-ctest -C %BUILD_TYPE% --output-on-failure --timeout 300
-if !ERRORLEVEL! NEQ 0 exit /b !ERRORLEVEL!
-echo "Tests Completed %DATE% %TIME%"
+if "%BUILD_TESTS%" NEQ "NO" (
+  echo "Running Tests... %DATE% %TIME%"
+  ctest -C %BUILD_TYPE% --output-on-failure --timeout 300
+  if !ERRORLEVEL! NEQ 0 exit /b !ERRORLEVEL!
+  echo "Tests Completed %DATE% %TIME%"
+)
 
 :: ################################################
 :: Install and package.
diff --git a/kokoro/windows-msvc-2013-release/build.bat b/kokoro/windows-msvc-2013-release/build.bat
deleted file mode 100644
index e77172a..0000000
--- a/kokoro/windows-msvc-2013-release/build.bat
+++ /dev/null
@@ -1,24 +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.
-::
-:: 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 2013
-
diff --git a/kokoro/windows-msvc-2013-release/presubmit.cfg b/kokoro/windows-msvc-2013-release/presubmit.cfg
deleted file mode 100644
index 7d3b238..0000000
--- a/kokoro/windows-msvc-2013-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-2013-release/build.bat"
diff --git a/kokoro/windows-msvc-2015-release-bazel/build.bat b/kokoro/windows-msvc-2015-release-bazel/build.bat
deleted file mode 100644
index de20b0a..0000000
--- a/kokoro/windows-msvc-2015-release-bazel/build.bat
+++ /dev/null
@@ -1,59 +0,0 @@
-:: Copyright (c) 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.
-::
-:: Windows Build Script.
-
-@echo on
-
-set SRC=%cd%\github\SPIRV-Tools
-
-:: Force usage of python 3.6
-set PATH=C:\python36;%PATH%
-
-:: Get dependencies
-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
-
-:: REM Install Bazel.
-wget -q https://github.com/bazelbuild/bazel/releases/download/5.0.0/bazel-5.0.0-windows-x86_64.zip
-unzip -q bazel-5.0.0-windows-x86_64.zip
-
-:: Set up MSVC
-call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x64
-set BAZEL_VS=C:\Program Files (x86)\Microsoft Visual Studio 14.0
-set BAZEL_VC=C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC
-set BAZEL_PYTHON=c:\tools\python2\python.exe
-
-:: #########################################
-:: Start building.
-:: #########################################
-echo "Build everything... %DATE% %TIME%"
-bazel.exe build :all
-if %ERRORLEVEL% NEQ 0 exit /b %ERRORLEVEL%
-echo "Build Completed %DATE% %TIME%"
-
-:: ##############
-:: Run the tests
-:: ##############
-echo "Running Tests... %DATE% %TIME%"
-bazel.exe test :all
-if %ERRORLEVEL% NEQ 0 exit /b %ERRORLEVEL%
-echo "Tests Completed %DATE% %TIME%"
-
-exit /b 0
-
diff --git a/kokoro/windows-msvc-2015-release-bazel/continuous.cfg b/kokoro/windows-msvc-2015-release-bazel/continuous.cfg
deleted file mode 100644
index f72cf05..0000000
--- a/kokoro/windows-msvc-2015-release-bazel/continuous.cfg
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright (c) 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.
-
-# Continuous build configuration.
-build_file: "SPIRV-Tools/kokoro/windows-msvc-2015-release-bazel/build.bat"
diff --git a/kokoro/windows-msvc-2015-release-bazel/presubmit.cfg b/kokoro/windows-msvc-2015-release-bazel/presubmit.cfg
deleted file mode 100644
index 148972c..0000000
--- a/kokoro/windows-msvc-2015-release-bazel/presubmit.cfg
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright (c) 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.
-
-# Presubmit build configuration.
-build_file: "SPIRV-Tools/kokoro/windows-msvc-2015-release-bazel/build.bat"
diff --git a/kokoro/windows-msvc-2017-debug/build.bat b/kokoro/windows-msvc-2019-debug/build.bat
similarity index 88%
rename from kokoro/windows-msvc-2017-debug/build.bat
rename to kokoro/windows-msvc-2019-debug/build.bat
index 25783a9..7ad94c1 100644
--- a/kokoro/windows-msvc-2017-debug/build.bat
+++ b/kokoro/windows-msvc-2019-debug/build.bat
@@ -1,4 +1,4 @@
-:: Copyright (c) 2018 Google LLC.
+:: 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.
@@ -20,4 +20,4 @@
 set SCRIPT_DIR=%~dp0
 
 :: Call with correct parameter
-call %SCRIPT_DIR%\..\scripts\windows\build.bat Debug 2017
+call %SCRIPT_DIR%\..\scripts\windows\build.bat Debug 2019
diff --git a/kokoro/windows-msvc-2017-debug/continuous.cfg b/kokoro/windows-msvc-2019-debug/continuous.cfg
similarity index 86%
copy from kokoro/windows-msvc-2017-debug/continuous.cfg
copy to kokoro/windows-msvc-2019-debug/continuous.cfg
index 25c5e11..e3a7863 100644
--- a/kokoro/windows-msvc-2017-debug/continuous.cfg
+++ b/kokoro/windows-msvc-2019-debug/continuous.cfg
@@ -1,4 +1,4 @@
-# Copyright (c) 2018 Google LLC.
+# 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.
@@ -13,7 +13,7 @@
 # limitations under the License.
 
 # Continuous build configuration.
-build_file: "SPIRV-Tools/kokoro/windows-msvc-2017-debug/build.bat"
+build_file: "SPIRV-Tools/kokoro/windows-msvc-2019-debug/build.bat"
 
 action {
   define_artifacts {
diff --git a/kokoro/windows-msvc-2017-debug/presubmit.cfg b/kokoro/windows-msvc-2019-debug/presubmit.cfg
similarity index 85%
rename from kokoro/windows-msvc-2017-debug/presubmit.cfg
rename to kokoro/windows-msvc-2019-debug/presubmit.cfg
index a7a553a..0ed3594 100644
--- a/kokoro/windows-msvc-2017-debug/presubmit.cfg
+++ b/kokoro/windows-msvc-2019-debug/presubmit.cfg
@@ -1,4 +1,4 @@
-# Copyright (c) 2018 Google LLC.
+# 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.
@@ -13,4 +13,4 @@
 # limitations under the License.
 
 # Presubmit build configuration.
-build_file: "SPIRV-Tools/kokoro/windows-msvc-2017-debug/build.bat"
+build_file: "SPIRV-Tools/kokoro/windows-msvc-2019-debug/build.bat"
diff --git a/kokoro/windows-msvc-2015-release/build.bat b/kokoro/windows-msvc-2019-release/build.bat
similarity index 95%
rename from kokoro/windows-msvc-2015-release/build.bat
rename to kokoro/windows-msvc-2019-release/build.bat
index c0e4bd3..8212924 100644
--- a/kokoro/windows-msvc-2015-release/build.bat
+++ b/kokoro/windows-msvc-2019-release/build.bat
@@ -1,4 +1,4 @@
-:: Copyright (c) 2018 Google LLC.
+:: 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.
@@ -20,5 +20,5 @@
 set SCRIPT_DIR=%~dp0
 
 :: Call with correct parameter
-call %SCRIPT_DIR%\..\scripts\windows\build.bat RelWithDebInfo 2015
+call %SCRIPT_DIR%\..\scripts\windows\build.bat RelWithDebInfo 2019
 
diff --git a/kokoro/windows-msvc-2017-debug/continuous.cfg b/kokoro/windows-msvc-2019-release/continuous.cfg
similarity index 86%
rename from kokoro/windows-msvc-2017-debug/continuous.cfg
rename to kokoro/windows-msvc-2019-release/continuous.cfg
index 25c5e11..624ccbd 100644
--- a/kokoro/windows-msvc-2017-debug/continuous.cfg
+++ b/kokoro/windows-msvc-2019-release/continuous.cfg
@@ -1,4 +1,4 @@
-# Copyright (c) 2018 Google LLC.
+# 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.
@@ -13,7 +13,7 @@
 # limitations under the License.
 
 # Continuous build configuration.
-build_file: "SPIRV-Tools/kokoro/windows-msvc-2017-debug/build.bat"
+build_file: "SPIRV-Tools/kokoro/windows-msvc-2019-release/build.bat"
 
 action {
   define_artifacts {
diff --git a/kokoro/windows-msvc-2015-release/presubmit.cfg b/kokoro/windows-msvc-2019-release/presubmit.cfg
similarity index 85%
rename from kokoro/windows-msvc-2015-release/presubmit.cfg
rename to kokoro/windows-msvc-2019-release/presubmit.cfg
index 85a1625..4c578e0 100644
--- a/kokoro/windows-msvc-2015-release/presubmit.cfg
+++ b/kokoro/windows-msvc-2019-release/presubmit.cfg
@@ -1,4 +1,4 @@
-# Copyright (c) 2018 Google LLC.
+# 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.
@@ -13,4 +13,4 @@
 # limitations under the License.
 
 # Presubmit build configuration.
-build_file: "SPIRV-Tools/kokoro/windows-msvc-2015-release/build.bat"
+build_file: "SPIRV-Tools/kokoro/windows-msvc-2019-release/build.bat"
diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt
index 668579a..748fbf2 100644
--- a/source/CMakeLists.txt
+++ b/source/CMakeLists.txt
@@ -31,12 +31,13 @@
   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 ${PYTHON_EXECUTABLE} ${GRAMMAR_PROCESSING_SCRIPT}
+    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}
@@ -52,12 +53,13 @@
   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 ${PYTHON_EXECUTABLE} ${GRAMMAR_PROCESSING_SCRIPT}
+    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}
@@ -73,7 +75,7 @@
   set(OPENCL_GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/${CONFIG_VERSION}/extinst.opencl.std.100.grammar.json")
   set(VIMSYNTAX_FILE "${spirv-tools_BINARY_DIR}/spvasm.vim")
   add_custom_command(OUTPUT ${VIMSYNTAX_FILE}
-      COMMAND ${PYTHON_EXECUTABLE} ${VIMSYNTAX_PROCESSING_SCRIPT}
+      COMMAND Python3::Interpreter ${VIMSYNTAX_PROCESSING_SCRIPT}
       --spirv-core-grammar=${GRAMMAR_JSON_FILE}
       --extinst-debuginfo-grammar=${DEBUGINFO_GRAMMAR_JSON_FILE}
       --extinst-glsl-grammar=${GLSL_GRAMMAR_JSON_FILE}
@@ -89,9 +91,10 @@
   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 ${PYTHON_EXECUTABLE} ${GRAMMAR_PROCESSING_SCRIPT}
+    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})
@@ -102,7 +105,7 @@
   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 ${PYTHON_EXECUTABLE} ${GRAMMAR_PROCESSING_SCRIPT}
+    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}
@@ -117,7 +120,7 @@
     set(GRAMMAR_FILE "${spirv-tools_SOURCE_DIR}/source/extinst.${VENDOR_TABLE}.grammar.json")
   endif()
   add_custom_command(OUTPUT ${INSTS_FILE}
-    COMMAND ${PYTHON_EXECUTABLE} ${GRAMMAR_PROCESSING_SCRIPT}
+    COMMAND Python3::Interpreter ${GRAMMAR_PROCESSING_SCRIPT}
       --extinst-vendor-grammar=${GRAMMAR_FILE}
       --vendor-insts-output=${INSTS_FILE}
       --vendor-operand-kind-prefix=${OPERAND_KIND_PREFIX}
@@ -131,7 +134,7 @@
 macro(spvtools_extinst_lang_headers NAME GRAMMAR_FILE)
   set(OUT_H ${spirv-tools_BINARY_DIR}/${NAME}.h)
   add_custom_command(OUTPUT ${OUT_H}
-    COMMAND ${PYTHON_EXECUTABLE} ${LANG_HEADER_PROCESSING_SCRIPT}
+    COMMAND Python3::Interpreter ${LANG_HEADER_PROCESSING_SCRIPT}
       --extinst-grammar=${GRAMMAR_FILE}
       --extinst-output-path=${OUT_H}
     DEPENDS ${LANG_HEADER_PROCESSING_SCRIPT} ${GRAMMAR_FILE}
@@ -165,7 +168,7 @@
 set(GENERATOR_INC_FILE ${spirv-tools_BINARY_DIR}/generators.inc)
 set(SPIRV_XML_REGISTRY_FILE ${SPIRV_HEADER_INCLUDE_DIR}/spirv/spir-v.xml)
 add_custom_command(OUTPUT ${GENERATOR_INC_FILE}
-  COMMAND ${PYTHON_EXECUTABLE} ${XML_REGISTRY_PROCESSING_SCRIPT}
+  COMMAND Python3::Interpreter ${XML_REGISTRY_PROCESSING_SCRIPT}
     --xml=${SPIRV_XML_REGISTRY_FILE}
     --generator-output=${GENERATOR_INC_FILE}
   DEPENDS ${XML_REGISTRY_PROCESSING_SCRIPT} ${SPIRV_XML_REGISTRY_FILE}
@@ -195,7 +198,7 @@
 set(SPIRV_TOOLS_CHANGES_FILE
   ${spirv-tools_SOURCE_DIR}/CHANGES)
 add_custom_command(OUTPUT ${SPIRV_TOOLS_BUILD_VERSION_INC}
-   COMMAND ${PYTHON_EXECUTABLE}
+   COMMAND Python3::Interpreter
            ${SPIRV_TOOLS_BUILD_VERSION_INC_GENERATOR}
            ${SPIRV_TOOLS_CHANGES_FILE} ${SPIRV_TOOLS_BUILD_VERSION_INC}
    DEPENDS ${SPIRV_TOOLS_BUILD_VERSION_INC_GENERATOR}
@@ -325,6 +328,7 @@
   ${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
+  ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_ray_tracing_reorder.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_scopes.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_small_type_uses.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_type.cpp
@@ -421,10 +425,7 @@
 endif()
 
 if(ENABLE_SPIRV_TOOLS_INSTALL)
-  install(TARGETS ${SPIRV_TOOLS_TARGETS} EXPORT ${SPIRV_TOOLS}Targets
-    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
-    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
-    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
+  install(TARGETS ${SPIRV_TOOLS_TARGETS} EXPORT ${SPIRV_TOOLS}Targets)
   export(EXPORT ${SPIRV_TOOLS}Targets FILE ${SPIRV_TOOLS}Target.cmake)
 
   spvtools_config_package_dir(${SPIRV_TOOLS} PACKAGE_DIR)
diff --git a/source/assembly_grammar.cpp b/source/assembly_grammar.cpp
index 4f5942a..0092d01 100644
--- a/source/assembly_grammar.cpp
+++ b/source/assembly_grammar.cpp
@@ -21,6 +21,7 @@
 #include "source/ext_inst.h"
 #include "source/opcode.h"
 #include "source/operand.h"
+#include "source/spirv_target_env.h"
 #include "source/table.h"
 
 namespace spvtools {
@@ -78,16 +79,16 @@
 
 // Associates an opcode with its name.
 struct SpecConstantOpcodeEntry {
-  SpvOp opcode;
+  spv::Op opcode;
   const char* name;
 };
 
 // All the opcodes allowed as the operation for OpSpecConstantOp.
-// The name does not have the usual "Op" prefix. For example opcode SpvOpIAdd
-// is associated with the name "IAdd".
+// The name does not have the usual "Op" prefix. For example opcode
+// spv::Op::IAdd is associated with the name "IAdd".
 //
 // clang-format off
-#define CASE(NAME) { SpvOp##NAME, #NAME }
+#define CASE(NAME) { spv::Op::Op##NAME, #NAME }
 const SpecConstantOpcodeEntry kOpSpecConstantOpcodes[] = {
     // Conversion
     CASE(SConvert),
@@ -154,11 +155,12 @@
     CASE(InBoundsAccessChain),
     CASE(PtrAccessChain),
     CASE(InBoundsPtrAccessChain),
-    CASE(CooperativeMatrixLengthNV)
+    CASE(CooperativeMatrixLengthNV),
+    CASE(CooperativeMatrixLengthKHR)
 };
 
 // The 60 is determined by counting the opcodes listed in the spec.
-static_assert(60 == sizeof(kOpSpecConstantOpcodes)/sizeof(kOpSpecConstantOpcodes[0]),
+static_assert(61 == sizeof(kOpSpecConstantOpcodes)/sizeof(kOpSpecConstantOpcodes[0]),
               "OpSpecConstantOp opcode table is incomplete");
 #undef CASE
 // clang-format on
@@ -173,17 +175,20 @@
 }
 
 CapabilitySet AssemblyGrammar::filterCapsAgainstTargetEnv(
-    const SpvCapability* cap_array, uint32_t count) const {
+    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 cap_desc = {};
+    spv_operand_desc entry = {};
     if (SPV_SUCCESS == lookupOperand(SPV_OPERAND_TYPE_CAPABILITY,
                                      static_cast<uint32_t>(cap_array[i]),
-                                     &cap_desc)) {
-      // spvOperandTableValueLookup() filters capabilities internally
-      // according to the current target environment by itself. So we
-      // should be safe to add this capability if the lookup succeeds.
-      cap_set.Add(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) {
+        cap_set.insert(cap_array[i]);
+      }
     }
   }
   return cap_set;
@@ -194,7 +199,7 @@
   return spvOpcodeTableNameLookup(target_env_, opcodeTable_, name, desc);
 }
 
-spv_result_t AssemblyGrammar::lookupOpcode(SpvOp opcode,
+spv_result_t AssemblyGrammar::lookupOpcode(spv::Op opcode,
                                            spv_opcode_desc* desc) const {
   return spvOpcodeTableValueLookup(target_env_, opcodeTable_, opcode, desc);
 }
@@ -214,7 +219,7 @@
 }
 
 spv_result_t AssemblyGrammar::lookupSpecConstantOpcode(const char* name,
-                                                       SpvOp* opcode) const {
+                                                       spv::Op* opcode) const {
   const auto* last = kOpSpecConstantOpcodes + kNumOpSpecConstantOpcodes;
   const auto* found =
       std::find_if(kOpSpecConstantOpcodes, last,
@@ -226,7 +231,7 @@
   return SPV_SUCCESS;
 }
 
-spv_result_t AssemblyGrammar::lookupSpecConstantOpcode(SpvOp opcode) const {
+spv_result_t AssemblyGrammar::lookupSpecConstantOpcode(spv::Op opcode) const {
   const auto* last = kOpSpecConstantOpcodes + kNumOpSpecConstantOpcodes;
   const auto* found =
       std::find_if(kOpSpecConstantOpcodes, last,
diff --git a/source/assembly_grammar.h b/source/assembly_grammar.h
index 17c2bd3..36fdd08 100644
--- a/source/assembly_grammar.h
+++ b/source/assembly_grammar.h
@@ -41,7 +41,7 @@
 
   // Removes capabilities not available in the current target environment and
   // returns the rest.
-  CapabilitySet filterCapsAgainstTargetEnv(const SpvCapability* cap_array,
+  CapabilitySet filterCapsAgainstTargetEnv(const spv::Capability* cap_array,
                                            uint32_t count) const;
 
   // Fills in the desc parameter with the information about the opcode
@@ -52,7 +52,7 @@
   // 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(SpvOp opcode, spv_opcode_desc* desc) const;
+  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
@@ -82,11 +82,12 @@
   // the integer add opcode for OpSpecConstantOp.  On success, returns
   // SPV_SUCCESS and sends the discovered operation code through the opcode
   // parameter.  On failure, returns SPV_ERROR_INVALID_LOOKUP.
-  spv_result_t lookupSpecConstantOpcode(const char* name, SpvOp* opcode) const;
+  spv_result_t lookupSpecConstantOpcode(const char* name,
+                                        spv::Op* opcode) const;
 
   // Returns SPV_SUCCESS if the given opcode is valid as the opcode operand
   // to OpSpecConstantOp.
-  spv_result_t lookupSpecConstantOpcode(SpvOp opcode) const;
+  spv_result_t lookupSpecConstantOpcode(spv::Op opcode) const;
 
   // Parses a mask expression string for the given operand type.
   //
diff --git a/source/binary.cpp b/source/binary.cpp
index 24d32f8..3cfdee0 100644
--- a/source/binary.cpp
+++ b/source/binary.cpp
@@ -156,7 +156,7 @@
   // Issues a diagnostic describing an exhaustion of input condition when
   // trying to decode an instruction operand, and returns
   // SPV_ERROR_INVALID_BINARY.
-  spv_result_t exhaustedInputDiagnostic(size_t inst_offset, SpvOp opcode,
+  spv_result_t exhaustedInputDiagnostic(size_t inst_offset, spv::Op opcode,
                                         spv_operand_type_t type) {
     return diagnostic() << "End of input reached while decoding Op"
                         << spvOpcodeString(opcode) << " starting at word "
@@ -318,7 +318,7 @@
                         << inst_word_count;
   }
   spv_opcode_desc opcode_desc;
-  if (grammar_.lookupOpcode(static_cast<SpvOp>(inst.opcode), &opcode_desc))
+  if (grammar_.lookupOpcode(static_cast<spv::Op>(inst.opcode), &opcode_desc))
     return diagnostic() << "Invalid opcode: " << inst.opcode;
 
   // Advance past the opcode word.  But remember the of the start
@@ -418,7 +418,7 @@
                                   std::vector<uint32_t>* words,
                                   std::vector<spv_parsed_operand_t>* operands,
                                   spv_operand_pattern_t* expected_operands) {
-  const SpvOp opcode = static_cast<SpvOp>(inst->opcode);
+  const spv::Op opcode = static_cast<spv::Op>(inst->opcode);
   // We'll fill in this result as we go along.
   spv_parsed_operand_t parsed_operand;
   parsed_operand.offset = uint16_t(_.word_index - inst_offset);
@@ -473,7 +473,7 @@
       if (!word) return diagnostic(SPV_ERROR_INVALID_ID) << "Id is 0";
       parsed_operand.type = SPV_OPERAND_TYPE_ID;
 
-      if (opcode == SpvOpExtInst && parsed_operand.offset == 3) {
+      if (opcode == spv::Op::OpExtInst && parsed_operand.offset == 3) {
         // The current word is the extended instruction set Id.
         // Set the extended instruction set type for the current instruction.
         auto ext_inst_type_iter = _.import_id_to_ext_inst_type.find(word);
@@ -494,7 +494,7 @@
       break;
 
     case SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER: {
-      assert(SpvOpExtInst == opcode);
+      assert(spv::Op::OpExtInst == 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) ==
@@ -516,14 +516,14 @@
     } break;
 
     case SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER: {
-      assert(SpvOpSpecConstantOp == opcode);
-      if (word > static_cast<uint32_t>(SpvOp::SpvOpMax) ||
-          grammar_.lookupSpecConstantOpcode(SpvOp(word))) {
+      assert(spv::Op::OpSpecConstantOp == opcode);
+      if (word > static_cast<uint32_t>(spv::Op::Max) ||
+          grammar_.lookupSpecConstantOpcode(spv::Op(word))) {
         return diagnostic()
                << "Invalid " << spvOperandTypeStr(type) << ": " << word;
       }
       spv_opcode_desc opcode_entry = nullptr;
-      if (grammar_.lookupOpcode(SpvOp(word), &opcode_entry)) {
+      if (grammar_.lookupOpcode(spv::Op(word), &opcode_entry)) {
         return diagnostic(SPV_ERROR_INTERNAL)
                << "OpSpecConstant opcode table out of sync";
       }
@@ -546,10 +546,17 @@
       parsed_operand.number_bit_width = 32;
       break;
 
+    case SPV_OPERAND_TYPE_LITERAL_FLOAT:
+      // These are regular single-word literal float operands.
+      parsed_operand.type = SPV_OPERAND_TYPE_LITERAL_FLOAT;
+      parsed_operand.number_kind = SPV_NUMBER_FLOATING;
+      parsed_operand.number_bit_width = 32;
+      break;
+
     case SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER:
     case SPV_OPERAND_TYPE_OPTIONAL_TYPED_LITERAL_INTEGER:
       parsed_operand.type = SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER;
-      if (opcode == SpvOpSwitch) {
+      if (opcode == spv::Op::OpSwitch) {
         // The literal operands have the same type as the value
         // referenced by the selector Id.
         const uint32_t selector_id = peekAt(inst_offset + 1);
@@ -575,7 +582,8 @@
                               << " is not a scalar integer";
         }
       } else {
-        assert(opcode == SpvOpConstant || opcode == SpvOpSpecConstant);
+        assert(opcode == spv::Op::OpConstant ||
+               opcode == spv::Op::OpSpecConstant);
         // The literal number type is determined by the type Id for the
         // constant.
         assert(inst->type_id);
@@ -607,7 +615,7 @@
       parsed_operand.num_words = uint16_t(string_num_words);
       parsed_operand.type = SPV_OPERAND_TYPE_LITERAL_STRING;
 
-      if (SpvOpExtInstImport == opcode) {
+      if (spv::Op::OpExtInstImport == opcode) {
         // Record the extended instruction type for the ID for this import.
         // There is only one string literal argument to OpExtInstImport,
         // so it's sufficient to guard this just on the opcode.
@@ -625,7 +633,6 @@
     } break;
 
     case SPV_OPERAND_TYPE_CAPABILITY:
-    case SPV_OPERAND_TYPE_SOURCE_LANGUAGE:
     case SPV_OPERAND_TYPE_EXECUTION_MODEL:
     case SPV_OPERAND_TYPE_ADDRESSING_MODEL:
     case SPV_OPERAND_TYPE_MEMORY_MODEL:
@@ -682,6 +689,21 @@
       spvPushOperandTypes(entry->operandTypes, expected_operands);
     } break;
 
+    case SPV_OPERAND_TYPE_SOURCE_LANGUAGE: {
+      spv_operand_desc entry;
+      if (grammar_.lookupOperand(type, word, &entry)) {
+        return diagnostic()
+               << "Invalid " << spvOperandTypeStr(parsed_operand.type)
+               << " operand: " << word
+               << ", if you are creating a new source language please use "
+                  "value 0 "
+                  "(Unknown) and when ready, add your source language to "
+                  "SPRIV-Headers";
+      }
+      // Prepare to accept operands to this operand, if needed.
+      spvPushOperandTypes(entry->operandTypes, expected_operands);
+    } break;
+
     case SPV_OPERAND_TYPE_FP_FAST_MATH_MODE:
     case SPV_OPERAND_TYPE_FUNCTION_CONTROL:
     case SPV_OPERAND_TYPE_LOOP_CONTROL:
@@ -690,7 +712,9 @@
     case SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS:
     case SPV_OPERAND_TYPE_SELECTION_CONTROL:
     case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS:
-    case SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS: {
+    case SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS:
+    case SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_OPERANDS:
+    case SPV_OPERAND_TYPE_OPTIONAL_COOPERATIVE_MATRIX_OPERANDS: {
       // This operand is a mask.
 
       // Map an optional operand type to its corresponding concrete type.
@@ -698,6 +722,8 @@
         parsed_operand.type = SPV_OPERAND_TYPE_IMAGE;
       else if (type == SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS)
         parsed_operand.type = SPV_OPERAND_TYPE_MEMORY_ACCESS;
+      if (type == SPV_OPERAND_TYPE_OPTIONAL_COOPERATIVE_MATRIX_OPERANDS)
+        parsed_operand.type = SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_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
@@ -789,14 +815,14 @@
 
 void Parser::recordNumberType(size_t inst_offset,
                               const spv_parsed_instruction_t* inst) {
-  const SpvOp opcode = static_cast<SpvOp>(inst->opcode);
+  const spv::Op opcode = static_cast<spv::Op>(inst->opcode);
   if (spvOpcodeGeneratesType(opcode)) {
     NumberType info = {SPV_NUMBER_NONE, 0};
-    if (SpvOpTypeInt == opcode) {
+    if (spv::Op::OpTypeInt == opcode) {
       const bool is_signed = peekAt(inst_offset + 3) != 0;
       info.type = is_signed ? SPV_NUMBER_SIGNED_INT : SPV_NUMBER_UNSIGNED_INT;
       info.bit_width = peekAt(inst_offset + 2);
-    } else if (SpvOpTypeFloat == opcode) {
+    } else if (spv::Op::OpTypeFloat == opcode) {
       info.type = SPV_NUMBER_FLOATING;
       info.bit_width = peekAt(inst_offset + 2);
     }
diff --git a/source/cfa.h b/source/cfa.h
index 2743ab4..9ae3e39 100644
--- a/source/cfa.h
+++ b/source/cfa.h
@@ -275,10 +275,16 @@
 
   std::vector<std::pair<bb_ptr, bb_ptr>> out;
   for (auto idom : idoms) {
+    // At this point if there is no dominator for the node, just make it
+    // reflexive.
+    auto dominator = std::get<1>(idom).dominator;
+    if (dominator == undefined_dom) {
+      dominator = std::get<1>(idom).postorder_index;
+    }
     // NOTE: performing a const cast for convenient usage with
     // UpdateImmediateDominators
     out.push_back({const_cast<BB*>(std::get<0>(idom)),
-                   const_cast<BB*>(postorder[std::get<1>(idom).dominator])});
+                   const_cast<BB*>(postorder[dominator])});
   }
 
   // Sort by postorder index to generate a deterministic ordering of edges.
diff --git a/source/diff/CMakeLists.txt b/source/diff/CMakeLists.txt
index 1328699..52f18f2 100644
--- a/source/diff/CMakeLists.txt
+++ b/source/diff/CMakeLists.txt
@@ -39,10 +39,7 @@
 spvtools_check_symbol_exports(SPIRV-Tools-diff)
 
 if(ENABLE_SPIRV_TOOLS_INSTALL)
-  install(TARGETS SPIRV-Tools-diff EXPORT SPIRV-Tools-diffTargets
-    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
-    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
-    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
+  install(TARGETS SPIRV-Tools-diff 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 7ed41de..6269af5 100644
--- a/source/diff/diff.cpp
+++ b/source/diff/diff.cpp
@@ -44,8 +44,8 @@
 // different implementations produce identical results.
 using IdGroupMapByName = std::map<std::string, IdGroup>;
 using IdGroupMapByTypeId = std::map<uint32_t, IdGroup>;
-using IdGroupMapByOp = std::map<SpvOp, IdGroup>;
-using IdGroupMapByStorageClass = std::map<SpvStorageClass, IdGroup>;
+using IdGroupMapByOp = std::map<spv::Op, IdGroup>;
+using IdGroupMapByStorageClass = std::map<spv::StorageClass, IdGroup>;
 
 // A set of potential id mappings that haven't been resolved yet.  Any id in src
 // may map in any id in dst.  Note that ids are added in the same order as they
@@ -101,9 +101,12 @@
     return from < id_map_.size() && id_map_[from] != 0;
   }
 
-  // Map any ids in src and dst that have not been mapped to new ids in dst and
-  // src respectively.
-  void MapUnmatchedIds(IdMap& other_way);
+  bool IsMapped(const opt::Instruction* from_inst) const {
+    assert(from_inst != nullptr);
+    assert(!from_inst->HasResultId());
+
+    return inst_map_.find(from_inst) != inst_map_.end();
+  }
 
   // Some instructions don't have result ids.  Those are mapped by pointer.
   void MapInsts(const opt::Instruction* from_inst,
@@ -117,6 +120,12 @@
 
   uint32_t IdBound() const { return static_cast<uint32_t>(id_map_.size()); }
 
+  // Generate a fresh id in this mapping's domain.
+  uint32_t MakeFreshId() {
+    id_map_.push_back(0);
+    return static_cast<uint32_t>(id_map_.size()) - 1;
+  }
+
  private:
   // Given an id, returns the corresponding id in the other module, or 0 if not
   // matched yet.
@@ -150,10 +159,16 @@
 
   bool IsSrcMapped(uint32_t src) { return src_to_dst_.IsMapped(src); }
   bool IsDstMapped(uint32_t dst) { return dst_to_src_.IsMapped(dst); }
+  bool IsDstMapped(const opt::Instruction* dst_inst) {
+    return dst_to_src_.IsMapped(dst_inst);
+  }
 
   // Map any ids in src and dst that have not been mapped to new ids in dst and
-  // src respectively.
-  void MapUnmatchedIds();
+  // src respectively. Use src_insn_defined and dst_insn_defined to ignore ids
+  // that are simply never defined. (Since we assume the inputs are valid
+  // SPIR-V, this implies they are also never used.)
+  void MapUnmatchedIds(std::function<bool(uint32_t)> src_insn_defined,
+                       std::function<bool(uint32_t)> dst_insn_defined);
 
   // Some instructions don't have result ids.  Those are mapped by pointer.
   void MapInsts(const opt::Instruction* src_inst,
@@ -203,6 +218,11 @@
 
   void MapIdToInstruction(uint32_t id, const opt::Instruction* inst);
 
+  // Return true if id is mapped to any instruction, false otherwise.
+  bool IsDefined(uint32_t id) {
+    return id < inst_map_.size() && inst_map_[id] != nullptr;
+  }
+
   void MapIdsToInstruction(
       opt::IteratorRange<opt::Module::const_inst_iterator> section);
   void MapIdsToInfos(
@@ -301,10 +321,10 @@
   // 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);
-  SpvStorageClass GroupIdsHelperGetTypePointerStorageClass(
+  spv::StorageClass GroupIdsHelperGetTypePointerStorageClass(
       const IdInstructions& id_to, uint32_t id);
-  SpvOp GroupIdsHelperGetTypePointerTypeOp(const IdInstructions& id_to,
-                                           uint32_t id);
+  spv::Op GroupIdsHelperGetTypePointerTypeOp(const IdInstructions& id_to,
+                                             uint32_t id);
 
   // Given a list of ids, groups them based on some value.  The `get_group`
   // function extracts a piece of information corresponding to each id, and the
@@ -338,6 +358,59 @@
       std::function<void(const IdGroup& src_group, const IdGroup& dst_group)>
           match_group);
 
+  // Bucket `src_ids` and `dst_ids` by the key ids returned by `get_group`, and
+  // then call `match_group` on pairs of buckets whose key ids are matched with
+  // each other.
+  //
+  // For example, suppose we want to pair up groups of instructions with the
+  // same type. Naturally, the source instructions refer to their types by their
+  // ids in the source, and the destination instructions use destination type
+  // ids, so simply comparing source and destination type ids as integers, as
+  // `GroupIdsAndMatch` would do, is meaningless. But if a prior call to
+  // `MatchTypeIds` has established type matches between the two modules, then
+  // we can consult those to pair source and destination buckets whose types are
+  // equivalent.
+  //
+  // Suppose our input groups are as follows:
+  //
+  // - src_ids: { 1 -> 100, 2 -> 300, 3 -> 100, 4 -> 200 }
+  // - dst_ids: { 5 -> 10, 6 -> 20, 7 -> 10, 8 -> 300 }
+  //
+  // Here, `X -> Y` means that the instruction with SPIR-V id `X` is a member of
+  // the group, and `Y` is the id of its type. If we use
+  // `Differ::GroupIdsHelperGetTypeId` for `get_group`, then
+  // `get_group(X) == Y`.
+  //
+  // These instructions are bucketed by type as follows:
+  //
+  // - source:      [1, 3] -> 100
+  //                [4] -> 200
+  //                [2] -> 300
+  //
+  // - destination: [5, 7] -> 10
+  //                [6] -> 20
+  //                [8] -> 300
+  //
+  // Now suppose that we have previously matched up src type 100 with dst type
+  // 10, and src type 200 with dst type 20, but no other types are matched.
+  //
+  // Then `match_group` is called twice:
+  // - Once with ([1,3], [5, 7]), corresponding to 100/10
+  // - Once with ([4],[6]), corresponding to 200/20
+  //
+  // The source type 300 isn't matched with anything, so the fact that there's a
+  // destination type 300 is irrelevant, and thus 2 and 8 are never passed to
+  // `match_group`.
+  //
+  // This function isn't specific to types; it simply buckets by the ids
+  // returned from `get_group`, and consults existing matches to pair up the
+  // resulting buckets.
+  void GroupIdsAndMatchByMappedId(
+      const IdGroup& src_ids, const IdGroup& dst_ids,
+      uint32_t (Differ::*get_group)(const IdInstructions&, uint32_t),
+      std::function<void(const IdGroup& src_group, const IdGroup& dst_group)>
+          match_group);
+
   // Helper functions that determine if two instructions match
   bool DoIdsMatch(uint32_t src_id, uint32_t dst_id);
   bool DoesOperandMatch(const opt::Operand& src_operand,
@@ -414,8 +487,8 @@
   // Helper functions to retrieve information pertaining to an id
   const opt::Instruction* GetInst(const IdInstructions& id_to, uint32_t id);
   uint32_t GetConstantUint(const IdInstructions& id_to, uint32_t constant_id);
-  SpvExecutionModel GetExecutionModel(const opt::Module* module,
-                                      uint32_t entry_point_id);
+  spv::ExecutionModel GetExecutionModel(const opt::Module* module,
+                                        uint32_t entry_point_id);
   bool HasName(const IdInstructions& id_to, uint32_t id);
   // Get the OpName associated with an id
   std::string GetName(const IdInstructions& id_to, uint32_t id, bool* has_name);
@@ -424,20 +497,21 @@
   // string, and this improves diff between SPIR-V from those tools and others.
   std::string GetSanitizedName(const IdInstructions& id_to, uint32_t id);
   uint32_t GetVarTypeId(const IdInstructions& id_to, uint32_t var_id,
-                        SpvStorageClass* storage_class);
+                        spv::StorageClass* storage_class);
   bool GetDecorationValue(const IdInstructions& id_to, uint32_t id,
-                          SpvDecoration decoration, uint32_t* decoration_value);
+                          spv::Decoration decoration,
+                          uint32_t* decoration_value);
   const opt::Instruction* GetForwardPointerInst(const IdInstructions& id_to,
                                                 uint32_t id);
   bool IsIntType(const IdInstructions& id_to, uint32_t type_id);
   bool IsFloatType(const IdInstructions& id_to, uint32_t type_id);
   bool IsConstantUint(const IdInstructions& id_to, uint32_t id);
   bool IsVariable(const IdInstructions& id_to, uint32_t pointer_id);
-  bool IsOp(const IdInstructions& id_to, uint32_t id, SpvOp opcode);
+  bool IsOp(const IdInstructions& id_to, uint32_t id, spv::Op opcode);
   bool IsPerVertexType(const IdInstructions& id_to, uint32_t type_id);
   bool IsPerVertexVariable(const IdInstructions& id_to, uint32_t type_id);
-  SpvStorageClass GetPerVertexStorageClass(const opt::Module* module,
-                                           uint32_t type_id);
+  spv::StorageClass GetPerVertexStorageClass(const opt::Module* module,
+                                             uint32_t type_id);
   spv_ext_inst_type_t GetExtInstType(const IdInstructions& id_to,
                                      uint32_t set_id);
   spv_number_kind_t GetNumberKind(const IdInstructions& id_to,
@@ -503,36 +577,27 @@
   FunctionMap dst_funcs_;
 };
 
-void IdMap::MapUnmatchedIds(IdMap& other_way) {
-  const uint32_t src_id_bound = static_cast<uint32_t>(id_map_.size());
-  const uint32_t dst_id_bound = static_cast<uint32_t>(other_way.id_map_.size());
-
-  uint32_t next_src_id = src_id_bound;
-  uint32_t next_dst_id = dst_id_bound;
+void SrcDstIdMap::MapUnmatchedIds(
+    std::function<bool(uint32_t)> src_insn_defined,
+    std::function<bool(uint32_t)> dst_insn_defined) {
+  const uint32_t src_id_bound = static_cast<uint32_t>(src_to_dst_.IdBound());
+  const uint32_t dst_id_bound = static_cast<uint32_t>(dst_to_src_.IdBound());
 
   for (uint32_t src_id = 1; src_id < src_id_bound; ++src_id) {
-    if (!IsMapped(src_id)) {
-      MapIds(src_id, next_dst_id);
-
-      other_way.id_map_.push_back(0);
-      other_way.MapIds(next_dst_id++, src_id);
+    if (!src_to_dst_.IsMapped(src_id) && src_insn_defined(src_id)) {
+      uint32_t fresh_dst_id = dst_to_src_.MakeFreshId();
+      MapIds(src_id, fresh_dst_id);
     }
   }
 
   for (uint32_t dst_id = 1; dst_id < dst_id_bound; ++dst_id) {
-    if (!other_way.IsMapped(dst_id)) {
-      id_map_.push_back(0);
-      MapIds(next_src_id, dst_id);
-
-      other_way.MapIds(dst_id, next_src_id++);
+    if (!dst_to_src_.IsMapped(dst_id) && dst_insn_defined(dst_id)) {
+      uint32_t fresh_src_id = src_to_dst_.MakeFreshId();
+      MapIds(fresh_src_id, dst_id);
     }
   }
 }
 
-void SrcDstIdMap::MapUnmatchedIds() {
-  src_to_dst_.MapUnmatchedIds(dst_to_src_);
-}
-
 void IdInstructions::MapIdToInstruction(uint32_t id,
                                         const opt::Instruction* inst) {
   assert(id != 0);
@@ -561,19 +626,19 @@
     uint32_t id_operand = 0;
 
     switch (inst.opcode()) {
-      case SpvOpName:
+      case spv::Op::OpName:
         info_map = &name_map_;
         break;
-      case SpvOpMemberName:
+      case spv::Op::OpMemberName:
         info_map = &name_map_;
         break;
-      case SpvOpDecorate:
+      case spv::Op::OpDecorate:
         info_map = &decoration_map_;
         break;
-      case SpvOpMemberDecorate:
+      case spv::Op::OpMemberDecorate:
         info_map = &decoration_map_;
         break;
-      case SpvOpTypeForwardPointer: {
+      case spv::Op::OpTypeForwardPointer: {
         uint32_t id = inst.GetSingleWordOperand(0);
         assert(id != 0);
 
@@ -731,10 +796,10 @@
   // Instead of comparing OpExecutionMode entry point ids as ids, compare them
   // through their corresponding execution model.  This simplifies traversing
   // the sorted list of instructions between src and dst modules.
-  if (a->opcode() == SpvOpExecutionMode) {
-    const SpvExecutionModel src_model =
+  if (a->opcode() == spv::Op::OpExecutionMode) {
+    const spv::ExecutionModel src_model =
         GetExecutionModel(src_inst_module, a->GetSingleWordOperand(0));
-    const SpvExecutionModel dst_model =
+    const spv::ExecutionModel dst_model =
         GetExecutionModel(dst_inst_module, b->GetSingleWordOperand(0));
 
     if (src_model < dst_model) {
@@ -818,17 +883,17 @@
   return GetInst(id_to, id)->type_id();
 }
 
-SpvStorageClass Differ::GroupIdsHelperGetTypePointerStorageClass(
+spv::StorageClass Differ::GroupIdsHelperGetTypePointerStorageClass(
     const IdInstructions& id_to, uint32_t id) {
   const opt::Instruction* inst = GetInst(id_to, id);
-  assert(inst && inst->opcode() == SpvOpTypePointer);
-  return SpvStorageClass(inst->GetSingleWordInOperand(0));
+  assert(inst && inst->opcode() == spv::Op::OpTypePointer);
+  return spv::StorageClass(inst->GetSingleWordInOperand(0));
 }
 
-SpvOp Differ::GroupIdsHelperGetTypePointerTypeOp(const IdInstructions& id_to,
-                                                 uint32_t id) {
+spv::Op Differ::GroupIdsHelperGetTypePointerTypeOp(const IdInstructions& id_to,
+                                                   uint32_t id) {
   const opt::Instruction* inst = GetInst(id_to, id);
-  assert(inst && inst->opcode() == SpvOpTypePointer);
+  assert(inst && inst->opcode() == spv::Op::OpTypePointer);
 
   const uint32_t type_id = inst->GetSingleWordInOperand(1);
   const opt::Instruction* type_inst = GetInst(id_to, type_id);
@@ -888,6 +953,37 @@
   }
 }
 
+void Differ::GroupIdsAndMatchByMappedId(
+    const IdGroup& src_ids, const IdGroup& dst_ids,
+    uint32_t (Differ::*get_group)(const IdInstructions&, uint32_t),
+    std::function<void(const IdGroup& src_group, const IdGroup& dst_group)>
+        match_group) {
+  // Group the ids based on a key (get_group)
+  std::map<uint32_t, IdGroup> src_groups;
+  std::map<uint32_t, IdGroup> dst_groups;
+
+  GroupIds<uint32_t>(src_ids, true, &src_groups, get_group);
+  GroupIds<uint32_t>(dst_ids, false, &dst_groups, get_group);
+
+  // Iterate over pairs of groups whose keys map to each other.
+  for (const auto& iter : src_groups) {
+    const uint32_t& src_key = iter.first;
+    const IdGroup& src_group = iter.second;
+
+    if (src_key == 0) {
+      continue;
+    }
+
+    if (id_map_.IsSrcMapped(src_key)) {
+      const uint32_t& dst_key = id_map_.MappedDstId(src_key);
+      const IdGroup& dst_group = dst_groups[dst_key];
+
+      // Let the caller match the groups as appropriate.
+      match_group(src_group, dst_group);
+    }
+  }
+}
+
 bool Differ::DoIdsMatch(uint32_t src_id, uint32_t dst_id) {
   assert(dst_id != 0);
   return id_map_.MappedDstId(src_id) == dst_id;
@@ -1020,7 +1116,7 @@
   }
   // For external instructions, make sure the set and opcode of the external
   // instruction matches too.
-  if (src_inst->opcode() == SpvOpExtInst) {
+  if (src_inst->opcode() == spv::Op::OpExtInst) {
     if (!DoOperandsMatch(src_inst, dst_inst, 0, 2)) {
       return false;
     }
@@ -1064,26 +1160,26 @@
   }
 
   switch (src_inst->opcode()) {
-    case SpvOpString:
-    case SpvOpSourceExtension:
-    case SpvOpModuleProcessed:
+    case spv::Op::OpString:
+    case spv::Op::OpSourceExtension:
+    case spv::Op::OpModuleProcessed:
       return DoesOperandMatch(src_inst->GetOperand(0), dst_inst->GetOperand(0));
-    case SpvOpSource:
+    case spv::Op::OpSource:
       return DoOperandsMatch(src_inst, dst_inst, 0, 2);
-    case SpvOpSourceContinued:
+    case spv::Op::OpSourceContinued:
       return true;
-    case SpvOpName:
+    case spv::Op::OpName:
       return DoOperandsMatch(src_inst, dst_inst, 0, 1);
-    case SpvOpMemberName:
+    case spv::Op::OpMemberName:
       return DoOperandsMatch(src_inst, dst_inst, 0, 2);
-    case SpvOpDecorate:
+    case spv::Op::OpDecorate:
       return DoOperandsMatch(src_inst, dst_inst, 0, 2);
-    case SpvOpMemberDecorate:
+    case spv::Op::OpMemberDecorate:
       return DoOperandsMatch(src_inst, dst_inst, 0, 3);
-    case SpvOpExtInst:
-    case SpvOpDecorationGroup:
-    case SpvOpGroupDecorate:
-    case SpvOpGroupMemberDecorate:
+    case spv::Op::OpExtInst:
+    case spv::Op::OpDecorationGroup:
+    case spv::Op::OpGroupDecorate:
+    case spv::Op::OpGroupMemberDecorate:
       return false;
     default:
       return false;
@@ -1095,9 +1191,9 @@
   // Variables must match by their built-in decorations.
   uint32_t src_built_in_decoration = 0, dst_built_in_decoration = 0;
   const bool src_is_built_in = GetDecorationValue(
-      src_id_to_, src_id, SpvDecorationBuiltIn, &src_built_in_decoration);
+      src_id_to_, src_id, spv::Decoration::BuiltIn, &src_built_in_decoration);
   const bool dst_is_built_in = GetDecorationValue(
-      dst_id_to_, dst_id, SpvDecorationBuiltIn, &dst_built_in_decoration);
+      dst_id_to_, dst_id, spv::Decoration::BuiltIn, &dst_built_in_decoration);
 
   if (src_is_built_in != dst_is_built_in) {
     return false;
@@ -1107,7 +1203,7 @@
   }
 
   // Check their types and storage classes.
-  SpvStorageClass src_storage_class, dst_storage_class;
+  spv::StorageClass src_storage_class, dst_storage_class;
   const uint32_t src_type_id =
       GetVarTypeId(src_id_to_, src_id, &src_storage_class);
   const uint32_t dst_type_id =
@@ -1127,12 +1223,14 @@
         // Allow one of the two to be Private while the other is Input or
         // Output, this allows matching in/out variables that have been turned
         // global as part of linking two stages (as done in ANGLE).
-        const bool src_is_io = src_storage_class == SpvStorageClassInput ||
-                               src_storage_class == SpvStorageClassOutput;
-        const bool dst_is_io = dst_storage_class == SpvStorageClassInput ||
-                               dst_storage_class == SpvStorageClassOutput;
-        const bool src_is_private = src_storage_class == SpvStorageClassPrivate;
-        const bool dst_is_private = dst_storage_class == SpvStorageClassPrivate;
+        const bool src_is_io = src_storage_class == spv::StorageClass::Input ||
+                               src_storage_class == spv::StorageClass::Output;
+        const bool dst_is_io = dst_storage_class == spv::StorageClass::Input ||
+                               dst_storage_class == spv::StorageClass::Output;
+        const bool src_is_private =
+            src_storage_class == spv::StorageClass::Private;
+        const bool dst_is_private =
+            dst_storage_class == spv::StorageClass::Private;
 
         if (!((src_is_io && dst_is_private) || (src_is_private && dst_is_io))) {
           return false;
@@ -1277,16 +1375,16 @@
   // Otherwise, match them by SpecId.
   uint32_t src_spec_id, dst_spec_id;
 
-  if (GetDecorationValue(src_id_to_, src_id, SpvDecorationSpecId,
+  if (GetDecorationValue(src_id_to_, src_id, spv::Decoration::SpecId,
                          &src_spec_id) &&
-      GetDecorationValue(dst_id_to_, dst_id, SpvDecorationSpecId,
+      GetDecorationValue(dst_id_to_, dst_id, spv::Decoration::SpecId,
                          &dst_spec_id)) {
     return src_spec_id == dst_spec_id;
   }
 
   // There is no SpecId decoration, while not practical, still valid.
   // SpecConstantOp don't have SpecId and can be matched by operands
-  if (src_inst->opcode() == SpvOpSpecConstantOp) {
+  if (src_inst->opcode() == spv::Op::OpSpecConstantOp) {
     if (src_inst->NumInOperandWords() == dst_inst->NumInOperandWords()) {
       return DoOperandsMatch(src_inst, dst_inst, 0,
                              src_inst->NumInOperandWords());
@@ -1327,13 +1425,13 @@
   // built-in decorations.
   uint32_t src_built_in_decoration;
   const bool src_is_built_in = GetDecorationValue(
-      src_id_to_, src_id, SpvDecorationBuiltIn, &src_built_in_decoration);
+      src_id_to_, src_id, spv::Decoration::BuiltIn, &src_built_in_decoration);
 
   if (src_is_built_in && AreVariablesMatchable(src_id, dst_id, flexibility)) {
     return true;
   }
 
-  SpvStorageClass src_storage_class, dst_storage_class;
+  spv::StorageClass src_storage_class, dst_storage_class;
   GetVarTypeId(src_id_to_, src_id, &src_storage_class);
   GetVarTypeId(dst_id_to_, dst_id, &dst_storage_class);
 
@@ -1348,13 +1446,13 @@
     uint32_t src_binding = 0, dst_binding = 0;
 
     const bool src_has_set = GetDecorationValue(
-        src_id_to_, src_id, SpvDecorationDescriptorSet, &src_set);
+        src_id_to_, src_id, spv::Decoration::DescriptorSet, &src_set);
     const bool dst_has_set = GetDecorationValue(
-        dst_id_to_, dst_id, SpvDecorationDescriptorSet, &dst_set);
-    const bool src_has_binding =
-        GetDecorationValue(src_id_to_, src_id, SpvDecorationBinding, &src_set);
-    const bool dst_has_binding =
-        GetDecorationValue(dst_id_to_, dst_id, SpvDecorationBinding, &dst_set);
+        dst_id_to_, dst_id, spv::Decoration::DescriptorSet, &dst_set);
+    const bool src_has_binding = GetDecorationValue(
+        src_id_to_, src_id, spv::Decoration::Binding, &src_set);
+    const bool dst_has_binding = GetDecorationValue(
+        dst_id_to_, dst_id, spv::Decoration::Binding, &dst_set);
 
     if (src_has_set && dst_has_set && src_has_binding && dst_has_binding) {
       return src_set == dst_set && src_binding == dst_binding;
@@ -1367,9 +1465,9 @@
     uint32_t src_location, dst_location;
 
     const bool src_has_location = GetDecorationValue(
-        src_id_to_, src_id, SpvDecorationLocation, &src_location);
+        src_id_to_, src_id, spv::Decoration::Location, &src_location);
     const bool dst_has_location = GetDecorationValue(
-        dst_id_to_, dst_id, SpvDecorationLocation, &dst_location);
+        dst_id_to_, dst_id, spv::Decoration::Location, &dst_location);
 
     if (src_has_location && dst_has_location) {
       return src_location == dst_location;
@@ -1384,25 +1482,25 @@
   // For gl_PerVertex, find the type pointer of this type (array) and make sure
   // the storage classes of src and dst match; geometry and tessellation shaders
   // have two instances of gl_PerVertex.
-  SpvStorageClass src_storage_class =
+  spv::StorageClass src_storage_class =
       GetPerVertexStorageClass(src_, src_type_id);
-  SpvStorageClass dst_storage_class =
+  spv::StorageClass dst_storage_class =
       GetPerVertexStorageClass(dst_, dst_type_id);
 
-  assert(src_storage_class == SpvStorageClassInput ||
-         src_storage_class == SpvStorageClassOutput);
-  assert(dst_storage_class == SpvStorageClassInput ||
-         dst_storage_class == SpvStorageClassOutput);
+  assert(src_storage_class == spv::StorageClass::Input ||
+         src_storage_class == spv::StorageClass::Output);
+  assert(dst_storage_class == spv::StorageClass::Input ||
+         dst_storage_class == spv::StorageClass::Output);
 
   return src_storage_class == dst_storage_class;
 }
 
 bool Differ::MatchPerVertexVariable(const opt::Instruction* src_inst,
                                     const opt::Instruction* dst_inst) {
-  SpvStorageClass src_storage_class =
-      SpvStorageClass(src_inst->GetSingleWordInOperand(0));
-  SpvStorageClass dst_storage_class =
-      SpvStorageClass(dst_inst->GetSingleWordInOperand(0));
+  spv::StorageClass src_storage_class =
+      spv::StorageClass(src_inst->GetSingleWordInOperand(0));
+  spv::StorageClass dst_storage_class =
+      spv::StorageClass(dst_inst->GetSingleWordInOperand(0));
 
   return src_storage_class == dst_storage_class;
 }
@@ -1416,7 +1514,6 @@
   GroupIdsAndMatch<std::string>(
       src, dst, "", &Differ::GetSanitizedName,
       [this](const IdGroup& src_group, const IdGroup& dst_group) {
-
         // Match only if there's a unique forward declaration with this debug
         // name.
         if (src_group.size() == 1 && dst_group.size() == 1) {
@@ -1479,7 +1576,7 @@
   InstructionList body;
   function.WhileEachInst(
       [&body](const opt::Instruction* inst) {
-        if (inst->opcode() == SpvOpLabel) {
+        if (inst->opcode() == spv::Op::OpLabel) {
           return false;
         }
         body.push_back(inst);
@@ -1571,6 +1668,8 @@
 
     id_map_.MapIds(match_result.src_id, match_result.dst_id);
 
+    MatchFunctionParamIds(src_funcs_[match_result.src_id],
+                          dst_funcs_[match_result.dst_id]);
     MatchIdsInFunctionBodies(src_func_insts.at(match_result.src_id),
                              dst_func_insts.at(match_result.dst_id),
                              match_result.src_match, match_result.dst_match, 0);
@@ -1595,7 +1694,6 @@
   GroupIdsAndMatch<std::string>(
       src_params, dst_params, "", &Differ::GetSanitizedName,
       [this](const IdGroup& src_group, const IdGroup& dst_group) {
-
         // There shouldn't be two parameters with the same name, so the ids
         // should match. There is nothing restricting the SPIR-V however to have
         // two parameters with the same name, so be resilient against that.
@@ -1606,17 +1704,17 @@
 
   // Then match the parameters by their type.  If there are multiple of them,
   // match them by their order.
-  GroupIdsAndMatch<uint32_t>(
-      src_params, dst_params, 0, &Differ::GroupIdsHelperGetTypeId,
+  GroupIdsAndMatchByMappedId(
+      src_params, dst_params, &Differ::GroupIdsHelperGetTypeId,
       [this](const IdGroup& src_group_by_type_id,
              const IdGroup& dst_group_by_type_id) {
-
         const size_t shared_param_count =
             std::min(src_group_by_type_id.size(), dst_group_by_type_id.size());
 
         for (size_t param_index = 0; param_index < shared_param_count;
              ++param_index) {
-          id_map_.MapIds(src_group_by_type_id[0], dst_group_by_type_id[0]);
+          id_map_.MapIds(src_group_by_type_id[param_index],
+                         dst_group_by_type_id[param_index]);
         }
       });
 }
@@ -1694,12 +1792,12 @@
     default:
       // TODO: match functions based on OpFunctionCall?
       break;
-    case SpvOpAccessChain:
-    case SpvOpInBoundsAccessChain:
-    case SpvOpPtrAccessChain:
-    case SpvOpInBoundsPtrAccessChain:
-    case SpvOpLoad:
-    case SpvOpStore:
+    case spv::Op::OpAccessChain:
+    case spv::Op::OpInBoundsAccessChain:
+    case spv::Op::OpPtrAccessChain:
+    case spv::Op::OpInBoundsPtrAccessChain:
+    case spv::Op::OpLoad:
+    case spv::Op::OpStore:
       const uint32_t src_pointer_id = src_inst->GetSingleWordInOperand(0);
       const uint32_t dst_pointer_id = dst_inst->GetSingleWordInOperand(0);
       if (IsVariable(src_id_to_, src_pointer_id) &&
@@ -1727,23 +1825,24 @@
 uint32_t Differ::GetConstantUint(const IdInstructions& id_to,
                                  uint32_t constant_id) {
   const opt::Instruction* constant_inst = GetInst(id_to, constant_id);
-  assert(constant_inst->opcode() == SpvOpConstant);
-  assert(GetInst(id_to, constant_inst->type_id())->opcode() == SpvOpTypeInt);
+  assert(constant_inst->opcode() == spv::Op::OpConstant);
+  assert(GetInst(id_to, constant_inst->type_id())->opcode() ==
+         spv::Op::OpTypeInt);
 
   return constant_inst->GetSingleWordInOperand(0);
 }
 
-SpvExecutionModel Differ::GetExecutionModel(const opt::Module* module,
-                                            uint32_t entry_point_id) {
+spv::ExecutionModel Differ::GetExecutionModel(const opt::Module* module,
+                                              uint32_t entry_point_id) {
   for (const opt::Instruction& inst : module->entry_points()) {
-    assert(inst.opcode() == SpvOpEntryPoint);
+    assert(inst.opcode() == spv::Op::OpEntryPoint);
     if (inst.GetSingleWordOperand(1) == entry_point_id) {
-      return SpvExecutionModel(inst.GetSingleWordOperand(0));
+      return spv::ExecutionModel(inst.GetSingleWordOperand(0));
     }
   }
 
   assert(false && "Unreachable");
-  return SpvExecutionModel(0xFFF);
+  return spv::ExecutionModel(0xFFF);
 }
 
 bool Differ::HasName(const IdInstructions& id_to, uint32_t id) {
@@ -1751,7 +1850,7 @@
   assert(id < id_to.name_map_.size());
 
   for (const opt::Instruction* inst : id_to.name_map_[id]) {
-    if (inst->opcode() == SpvOpName) {
+    if (inst->opcode() == spv::Op::OpName) {
       return true;
     }
   }
@@ -1765,7 +1864,7 @@
   assert(id < id_to.name_map_.size());
 
   for (const opt::Instruction* inst : id_to.name_map_[id]) {
-    if (inst->opcode() == SpvOpName) {
+    if (inst->opcode() == spv::Op::OpName) {
       *has_name = true;
       return inst->GetOperand(1).AsString();
     }
@@ -1788,11 +1887,11 @@
 }
 
 uint32_t Differ::GetVarTypeId(const IdInstructions& id_to, uint32_t var_id,
-                              SpvStorageClass* storage_class) {
+                              spv::StorageClass* storage_class) {
   const opt::Instruction* var_inst = GetInst(id_to, var_id);
-  assert(var_inst->opcode() == SpvOpVariable);
+  assert(var_inst->opcode() == spv::Op::OpVariable);
 
-  *storage_class = SpvStorageClass(var_inst->GetSingleWordInOperand(0));
+  *storage_class = spv::StorageClass(var_inst->GetSingleWordInOperand(0));
 
   // Get the type pointer from the variable.
   const uint32_t type_pointer_id = var_inst->type_id();
@@ -1803,15 +1902,15 @@
 }
 
 bool Differ::GetDecorationValue(const IdInstructions& id_to, uint32_t id,
-                                SpvDecoration decoration,
+                                spv::Decoration decoration,
                                 uint32_t* decoration_value) {
   assert(id != 0);
   assert(id < id_to.decoration_map_.size());
 
   for (const opt::Instruction* inst : id_to.decoration_map_[id]) {
-    if (inst->opcode() == SpvOpDecorate &&
+    if (inst->opcode() == spv::Op::OpDecorate &&
         inst->GetSingleWordOperand(0) == id &&
-        inst->GetSingleWordOperand(1) == decoration) {
+        spv::Decoration(inst->GetSingleWordOperand(1)) == decoration) {
       *decoration_value = inst->GetSingleWordOperand(2);
       return true;
     }
@@ -1828,28 +1927,28 @@
 }
 
 bool Differ::IsIntType(const IdInstructions& id_to, uint32_t type_id) {
-  return IsOp(id_to, type_id, SpvOpTypeInt);
+  return IsOp(id_to, type_id, spv::Op::OpTypeInt);
 }
 
 bool Differ::IsFloatType(const IdInstructions& id_to, uint32_t type_id) {
-  return IsOp(id_to, type_id, SpvOpTypeFloat);
+  return IsOp(id_to, type_id, spv::Op::OpTypeFloat);
 }
 
 bool Differ::IsConstantUint(const IdInstructions& id_to, uint32_t id) {
   const opt::Instruction* constant_inst = GetInst(id_to, id);
-  if (constant_inst->opcode() != SpvOpConstant) {
+  if (constant_inst->opcode() != spv::Op::OpConstant) {
     return false;
   }
 
   const opt::Instruction* type_inst = GetInst(id_to, constant_inst->type_id());
-  return type_inst->opcode() == SpvOpTypeInt;
+  return type_inst->opcode() == spv::Op::OpTypeInt;
 }
 
 bool Differ::IsVariable(const IdInstructions& id_to, uint32_t pointer_id) {
-  return IsOp(id_to, pointer_id, SpvOpVariable);
+  return IsOp(id_to, pointer_id, spv::Op::OpVariable);
 }
 
-bool Differ::IsOp(const IdInstructions& id_to, uint32_t id, SpvOp op) {
+bool Differ::IsOp(const IdInstructions& id_to, uint32_t id, spv::Op op) {
   return GetInst(id_to, id)->opcode() == op;
 }
 
@@ -1858,17 +1957,18 @@
   assert(type_id < id_to.decoration_map_.size());
 
   for (const opt::Instruction* inst : id_to.decoration_map_[type_id]) {
-    if (inst->opcode() == SpvOpMemberDecorate &&
+    if (inst->opcode() == spv::Op::OpMemberDecorate &&
         inst->GetSingleWordOperand(0) == type_id &&
-        inst->GetSingleWordOperand(2) == SpvDecorationBuiltIn) {
-      SpvBuiltIn built_in = SpvBuiltIn(inst->GetSingleWordOperand(3));
+        spv::Decoration(inst->GetSingleWordOperand(2)) ==
+            spv::Decoration::BuiltIn) {
+      spv::BuiltIn built_in = spv::BuiltIn(inst->GetSingleWordOperand(3));
 
       // Only gl_PerVertex can have, and it can only have, the following
       // built-in decorations.
-      return built_in == SpvBuiltInPosition ||
-             built_in == SpvBuiltInPointSize ||
-             built_in == SpvBuiltInClipDistance ||
-             built_in == SpvBuiltInCullDistance;
+      return built_in == spv::BuiltIn::Position ||
+             built_in == spv::BuiltIn::PointSize ||
+             built_in == spv::BuiltIn::ClipDistance ||
+             built_in == spv::BuiltIn::CullDistance;
     }
   }
 
@@ -1877,12 +1977,12 @@
 
 bool Differ::IsPerVertexVariable(const IdInstructions& id_to, uint32_t var_id) {
   // Get the type from the type pointer.
-  SpvStorageClass storage_class;
+  spv::StorageClass storage_class;
   uint32_t type_id = GetVarTypeId(id_to, var_id, &storage_class);
   const opt::Instruction* type_inst = GetInst(id_to, type_id);
 
   // If array, get the element type.
-  if (type_inst->opcode() == SpvOpTypeArray) {
+  if (type_inst->opcode() == spv::Op::OpTypeArray) {
     type_id = type_inst->GetSingleWordInOperand(0);
   }
 
@@ -1890,21 +1990,21 @@
   return IsPerVertexType(id_to, type_id);
 }
 
-SpvStorageClass Differ::GetPerVertexStorageClass(const opt::Module* module,
-                                                 uint32_t type_id) {
+spv::StorageClass Differ::GetPerVertexStorageClass(const opt::Module* module,
+                                                   uint32_t type_id) {
   for (const opt::Instruction& inst : module->types_values()) {
     switch (inst.opcode()) {
-      case SpvOpTypeArray:
+      case spv::Op::OpTypeArray:
         // The gl_PerVertex instance could be an array, look for a variable of
         // the array type instead.
         if (inst.GetSingleWordInOperand(0) == type_id) {
           type_id = inst.result_id();
         }
         break;
-      case SpvOpTypePointer:
+      case spv::Op::OpTypePointer:
         // Find the storage class of the pointer to this type.
         if (inst.GetSingleWordInOperand(1) == type_id) {
-          return SpvStorageClass(inst.GetSingleWordInOperand(0));
+          return spv::StorageClass(inst.GetSingleWordInOperand(0));
         }
         break;
       default:
@@ -1915,7 +2015,7 @@
   // gl_PerVertex is declared, but is unused.  Return either of Input or Output
   // classes just so it matches one in the other module.  This should be highly
   // unlikely, perhaps except for ancient GS-used-to-emulate-CS scenarios.
-  return SpvStorageClassOutput;
+  return spv::StorageClass::Output;
 }
 
 spv_ext_inst_type_t Differ::GetExtInstType(const IdInstructions& id_to,
@@ -1938,12 +2038,16 @@
       // Always unsigned integers.
       *number_bit_width = 32;
       return SPV_NUMBER_UNSIGNED_INT;
+    case SPV_OPERAND_TYPE_LITERAL_FLOAT:
+      // Always float.
+      *number_bit_width = 32;
+      return SPV_NUMBER_FLOATING;
     case SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER:
     case SPV_OPERAND_TYPE_OPTIONAL_TYPED_LITERAL_INTEGER:
       switch (inst.opcode()) {
-        case SpvOpSwitch:
-        case SpvOpConstant:
-        case SpvOpSpecConstant:
+        case spv::Op::OpSwitch:
+        case spv::Op::OpConstant:
+        case spv::Op::OpSpecConstant:
           // Same kind of number as the selector (OpSwitch) or the type
           // (Op*Constant).
           return GetTypeNumberKind(id_to, inst.GetSingleWordOperand(0),
@@ -1969,12 +2073,12 @@
   }
 
   switch (type_inst->opcode()) {
-    case SpvOpTypeInt:
+    case spv::Op::OpTypeInt:
       *number_bit_width = type_inst->GetSingleWordOperand(1);
       return type_inst->GetSingleWordOperand(2) == 0 ? SPV_NUMBER_UNSIGNED_INT
                                                      : SPV_NUMBER_SIGNED_INT;
       break;
-    case SpvOpTypeFloat:
+    case spv::Op::OpTypeFloat:
       *number_bit_width = type_inst->GetSingleWordOperand(1);
       return SPV_NUMBER_FLOATING;
     default:
@@ -2059,9 +2163,10 @@
     }
 
     // Otherwise match them by name.
-    bool matched = false;
     for (const opt::Instruction* src_inst : src_insts) {
       for (const opt::Instruction* dst_inst : dst_insts) {
+        if (id_map_.IsDstMapped(dst_inst)) continue;
+
         const opt::Operand& src_name = src_inst->GetOperand(2);
         const opt::Operand& dst_name = dst_inst->GetOperand(2);
 
@@ -2070,13 +2175,9 @@
           uint32_t dst_id = dst_inst->GetSingleWordOperand(1);
           id_map_.MapIds(src_id, dst_id);
           id_map_.MapInsts(src_inst, dst_inst);
-          matched = true;
           break;
         }
       }
-      if (matched) {
-        break;
-      }
     }
   }
 }
@@ -2092,7 +2193,7 @@
     return inst.GetSingleWordOperand(0);
   };
   auto accept_type_forward_pointer_ops = [](const opt::Instruction& inst) {
-    return inst.opcode() == SpvOpTypeForwardPointer;
+    return inst.opcode() == spv::Op::OpTypeForwardPointer;
   };
 
   PoolPotentialIds(src_->types_values(), potential_id_map.src_ids, true,
@@ -2116,20 +2217,18 @@
   //     - If leftover is unique, match
 
   // Group forwarded pointers by storage class first and loop over them.
-  GroupIdsAndMatch<SpvStorageClass>(
-      potential_id_map.src_ids, potential_id_map.dst_ids, SpvStorageClassMax,
-      &Differ::GroupIdsHelperGetTypePointerStorageClass,
+  GroupIdsAndMatch<spv::StorageClass>(
+      potential_id_map.src_ids, potential_id_map.dst_ids,
+      spv::StorageClass::Max, &Differ::GroupIdsHelperGetTypePointerStorageClass,
       [this](const IdGroup& src_group_by_storage_class,
              const IdGroup& dst_group_by_storage_class) {
-
         // Group them further by the type they are pointing to and loop over
         // them.
-        GroupIdsAndMatch<SpvOp>(
-            src_group_by_storage_class, dst_group_by_storage_class, SpvOpMax,
-            &Differ::GroupIdsHelperGetTypePointerTypeOp,
+        GroupIdsAndMatch<spv::Op>(
+            src_group_by_storage_class, dst_group_by_storage_class,
+            spv::Op::Max, &Differ::GroupIdsHelperGetTypePointerTypeOp,
             [this](const IdGroup& src_group_by_type_op,
                    const IdGroup& dst_group_by_type_op) {
-
               // Group them even further by debug info, if possible and match by
               // debug name.
               MatchTypeForwardPointersByName(src_group_by_type_op,
@@ -2182,8 +2281,8 @@
     MatchIds(potential_id_map, [this, flexibility](
                                    const opt::Instruction* src_inst,
                                    const opt::Instruction* dst_inst) {
-      const SpvOp src_op = src_inst->opcode();
-      const SpvOp dst_op = dst_inst->opcode();
+      const spv::Op src_op = src_inst->opcode();
+      const spv::Op dst_op = dst_inst->opcode();
 
       // Don't match if the opcode is not the same.
       if (src_op != dst_op) {
@@ -2191,26 +2290,28 @@
       }
 
       switch (src_op) {
-        case SpvOpTypeVoid:
-        case SpvOpTypeBool:
-        case SpvOpTypeSampler:
-          // void, bool and sampler are unique, match them.
+        case spv::Op::OpTypeVoid:
+        case spv::Op::OpTypeBool:
+        case spv::Op::OpTypeSampler:
+        case spv::Op::OpTypeAccelerationStructureNV:
+        case spv::Op::OpTypeRayQueryKHR:
+          // the above types have no operands and are unique, match them.
           return true;
-        case SpvOpTypeInt:
-        case SpvOpTypeFloat:
-        case SpvOpTypeVector:
-        case SpvOpTypeMatrix:
-        case SpvOpTypeSampledImage:
-        case SpvOpTypeRuntimeArray:
-        case SpvOpTypePointer:
+        case spv::Op::OpTypeInt:
+        case spv::Op::OpTypeFloat:
+        case spv::Op::OpTypeVector:
+        case spv::Op::OpTypeMatrix:
+        case spv::Op::OpTypeSampledImage:
+        case spv::Op::OpTypeRuntimeArray:
+        case spv::Op::OpTypePointer:
           // Match these instructions when all operands match.
           assert(src_inst->NumInOperandWords() ==
                  dst_inst->NumInOperandWords());
           return DoOperandsMatch(src_inst, dst_inst, 0,
                                  src_inst->NumInOperandWords());
 
-        case SpvOpTypeFunction:
-        case SpvOpTypeImage:
+        case spv::Op::OpTypeFunction:
+        case spv::Op::OpTypeImage:
           // Match function types only if they have the same number of operands,
           // and they all match.
           // Match image types similarly, expecting the optional final parameter
@@ -2221,7 +2322,7 @@
           return DoOperandsMatch(src_inst, dst_inst, 0,
                                  src_inst->NumInOperandWords());
 
-        case SpvOpTypeArray:
+        case spv::Op::OpTypeArray:
           // Match arrays only if the element type and length match.  The length
           // is an id of a constant, so the actual constant it's defining is
           // compared instead.
@@ -2238,7 +2339,7 @@
           // example if a spec contant is used).
           return DoOperandsMatch(src_inst, dst_inst, 1, 1);
 
-        case SpvOpTypeStruct:
+        case spv::Op::OpTypeStruct:
           return MatchOpTypeStruct(src_inst, dst_inst, flexibility);
 
         default:
@@ -2270,8 +2371,8 @@
     MatchIds(potential_id_map, [this, flexibility](
                                    const opt::Instruction* src_inst,
                                    const opt::Instruction* dst_inst) {
-      const SpvOp src_op = src_inst->opcode();
-      const SpvOp dst_op = dst_inst->opcode();
+      const spv::Op src_op = src_inst->opcode();
+      const spv::Op dst_op = dst_inst->opcode();
 
       // Don't match if the opcode is not the same.
       if (src_op != dst_op) {
@@ -2279,14 +2380,14 @@
       }
 
       switch (src_op) {
-        case SpvOpConstantTrue:
-        case SpvOpConstantFalse:
+        case spv::Op::OpConstantTrue:
+        case spv::Op::OpConstantFalse:
           // true and false are unique, match them.
           return true;
-        case SpvOpConstant:
+        case spv::Op::OpConstant:
           return MatchOpConstant(src_inst, dst_inst, flexibility);
-        case SpvOpConstantComposite:
-        case SpvOpSpecConstantComposite:
+        case spv::Op::OpConstantComposite:
+        case spv::Op::OpSpecConstantComposite:
           // Composite constants must match in type and value.
           //
           // TODO: match OpConstantNull with OpConstantComposite with all zeros
@@ -2299,7 +2400,7 @@
                                   dst_inst->GetOperand(0)) &&
                  DoOperandsMatch(src_inst, dst_inst, 0,
                                  src_inst->NumInOperandWords());
-        case SpvOpConstantSampler:
+        case spv::Op::OpConstantSampler:
           // Match sampler constants exactly.
           // TODO: Allow flexibility in parameters to better diff shaders where
           // the sampler param has changed.
@@ -2307,15 +2408,15 @@
                  dst_inst->NumInOperandWords());
           return DoOperandsMatch(src_inst, dst_inst, 0,
                                  src_inst->NumInOperandWords());
-        case SpvOpConstantNull:
+        case spv::Op::OpConstantNull:
           // Match null constants as long as the type matches.
           return DoesOperandMatch(src_inst->GetOperand(0),
                                   dst_inst->GetOperand(0));
 
-        case SpvOpSpecConstantTrue:
-        case SpvOpSpecConstantFalse:
-        case SpvOpSpecConstant:
-        case SpvOpSpecConstantOp:
+        case spv::Op::OpSpecConstantTrue:
+        case spv::Op::OpSpecConstantFalse:
+        case spv::Op::OpSpecConstant:
+        case spv::Op::OpSpecConstantOp:
           // Match spec constants by name if available, then by the SpecId
           // decoration.
           return MatchOpSpecConstant(src_inst, dst_inst);
@@ -2334,7 +2435,7 @@
     return inst.result_id();
   };
   auto accept_type_ops = [](const opt::Instruction& inst) {
-    return inst.opcode() == SpvOpVariable;
+    return inst.opcode() == spv::Op::OpVariable;
   };
 
   PoolPotentialIds(src_->types_values(), potential_id_map.src_ids, true,
@@ -2350,8 +2451,8 @@
     MatchIds(potential_id_map,
              [this, flexibility](const opt::Instruction* src_inst,
                                  const opt::Instruction* dst_inst) {
-               assert(src_inst->opcode() == SpvOpVariable);
-               assert(dst_inst->opcode() == SpvOpVariable);
+               assert(src_inst->opcode() == spv::Op::OpVariable);
+               assert(dst_inst->opcode() == spv::Op::OpVariable);
 
                return MatchOpVariable(src_inst, dst_inst, flexibility);
              });
@@ -2373,7 +2474,6 @@
   GroupIdsAndMatch<std::string>(
       src_func_ids, dst_func_ids, "", &Differ::GetSanitizedName,
       [this](const IdGroup& src_group, const IdGroup& dst_group) {
-
         // If there is a single function with this name in src and dst, it's a
         // definite match.
         if (src_group.size() == 1 && dst_group.size() == 1) {
@@ -2387,7 +2487,6 @@
                                    &Differ::GroupIdsHelperGetTypeId,
                                    [this](const IdGroup& src_group_by_type_id,
                                           const IdGroup& dst_group_by_type_id) {
-
                                      if (src_group_by_type_id.size() == 1 &&
                                          dst_group_by_type_id.size() == 1) {
                                        id_map_.MapIds(src_group_by_type_id[0],
@@ -2432,7 +2531,6 @@
       src_func_ids, dst_func_ids, 0, &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,
                                  src_func_insts_, dst_func_insts_);
       });
@@ -2597,7 +2695,7 @@
   parsed_inst->num_words = static_cast<uint16_t>(inst_binary.size());
   parsed_inst->opcode = static_cast<uint16_t>(inst.opcode());
   parsed_inst->ext_inst_type =
-      inst.opcode() == SpvOpExtInst
+      inst.opcode() == spv::Op::OpExtInst
           ? GetExtInstType(id_to, original_inst.GetSingleWordInOperand(0))
           : SPV_EXT_INST_TYPE_NONE;
   parsed_inst->type_id =
@@ -2642,7 +2740,9 @@
 }
 
 spv_result_t Differ::Output() {
-  id_map_.MapUnmatchedIds();
+  id_map_.MapUnmatchedIds(
+      [this](uint32_t src_id) { return src_id_to_.IsDefined(src_id); },
+      [this](uint32_t dst_id) { return dst_id_to_.IsDefined(dst_id); });
   src_id_to_.inst_map_.resize(id_map_.SrcToDstMap().IdBound(), nullptr);
   dst_id_to_.inst_map_.resize(id_map_.DstToSrcMap().IdBound(), nullptr);
 
diff --git a/source/disassemble.cpp b/source/disassemble.cpp
index 1d61b9f..5173fbf 100644
--- a/source/disassemble.cpp
+++ b/source/disassemble.cpp
@@ -244,7 +244,7 @@
 
 void InstructionDisassembler::EmitInstruction(
     const spv_parsed_instruction_t& inst, size_t inst_byte_offset) {
-  auto opcode = static_cast<SpvOp>(inst.opcode);
+  auto opcode = static_cast<spv::Op>(inst.opcode);
 
   if (inst.result_id) {
     SetBlue();
@@ -268,7 +268,7 @@
     EmitOperand(inst, i);
   }
 
-  if (comment_ && opcode == SpvOpName) {
+  if (comment_ && opcode == spv::Op::OpName) {
     const spv_parsed_operand_t& operand = inst.operands[0];
     const uint32_t word = inst.words[operand.offset];
     stream_ << "  ; id %" << word;
@@ -290,8 +290,8 @@
 void InstructionDisassembler::EmitSectionComment(
     const spv_parsed_instruction_t& inst, bool& inserted_decoration_space,
     bool& inserted_debug_space, bool& inserted_type_space) {
-  auto opcode = static_cast<SpvOp>(inst.opcode);
-  if (comment_ && opcode == SpvOpFunction) {
+  auto opcode = static_cast<spv::Op>(inst.opcode);
+  if (comment_ && opcode == spv::Op::OpFunction) {
     stream_ << std::endl;
     stream_ << std::string(indent_, ' ');
     stream_ << "; Function " << name_mapper_(inst.result_id) << std::endl;
@@ -351,13 +351,14 @@
     } break;
     case SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER: {
       spv_opcode_desc opcode_desc;
-      if (grammar_.lookupOpcode(SpvOp(word), &opcode_desc))
+      if (grammar_.lookupOpcode(spv::Op(word), &opcode_desc))
         assert(false && "should have caught this earlier");
       SetRed();
       stream_ << opcode_desc->name;
     } break;
     case SPV_OPERAND_TYPE_LITERAL_INTEGER:
-    case SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER: {
+    case SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER:
+    case SPV_OPERAND_TYPE_LITERAL_FLOAT: {
       SetRed();
       EmitNumericLiteral(&stream_, inst, operand);
       ResetColor();
diff --git a/source/enum_set.h b/source/enum_set.h
index d4d31e3..a375138 100644
--- a/source/enum_set.h
+++ b/source/enum_set.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2016 Google Inc.
+// Copyright (c) 2023 Google Inc.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -12,196 +12,457 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include <algorithm>
+#include <cassert>
+#include <cstdint>
+#include <functional>
+#include <initializer_list>
+#include <limits>
+#include <type_traits>
+#include <vector>
+
 #ifndef SOURCE_ENUM_SET_H_
 #define SOURCE_ENUM_SET_H_
 
-#include <cstdint>
-#include <functional>
-#include <memory>
-#include <set>
-#include <utility>
-
 #include "source/latest_version_spirv_header.h"
-#include "source/util/make_unique.h"
 
 namespace spvtools {
 
-// A set of values of a 32-bit enum type.
-// It is fast and compact for the common case, where enum values
-// are at most 63.  But it can represent enums with larger values,
-// as may appear in extensions.
-template <typename EnumType>
+// This container is optimized to store and retrieve unsigned enum values.
+// The base model for this implementation is an open-addressing hashtable with
+// linear probing. For small enums (max index < 64), all operations are O(1).
+//
+// - Enums are stored in buckets (64 contiguous values max per bucket)
+// - Buckets ranges don't overlap, but don't have to be contiguous.
+// - Enums are packed into 64-bits buckets, using 1 bit per enum value.
+//
+// Example:
+//  - MyEnum { A = 0, B = 1, C = 64, D = 65 }
+//  - 2 buckets are required:
+//      - bucket 0, storing values in the range [ 0;  64[
+//      - bucket 1, storing values in the range [64; 128[
+//
+// - Buckets are stored in a sorted vector (sorted by bucket range).
+// - Retrieval is done by computing the theoretical bucket index using the enum
+// value, and
+//   doing a linear scan from this position.
+// - Insertion is done by retrieving the bucket and either:
+//   - inserting a new bucket in the sorted vector when no buckets has a
+//   compatible range.
+//   - setting the corresponding bit in the bucket.
+//   This means insertion in the middle/beginning can cause a memmove when no
+//   bucket is available. In our case, this happens at most 23 times for the
+//   largest enum we have (Opcodes).
+template <typename T>
 class EnumSet {
  private:
-  // The ForEach method will call the functor on enum values in
-  // enum value order (lowest to highest).  To make that easier, use
-  // an ordered set for the overflow values.
-  using OverflowSetType = std::set<uint32_t>;
+  using BucketType = uint64_t;
+  using ElementType = std::underlying_type_t<T>;
+  static_assert(std::is_enum_v<T>, "EnumSets only works with enums.");
+  static_assert(std::is_signed_v<ElementType> == false,
+                "EnumSet doesn't supports signed enums.");
+
+  // Each bucket can hold up to `kBucketSize` distinct, contiguous enum values.
+  // The first value a bucket can hold must be aligned on `kBucketSize`.
+  struct Bucket {
+    // bit mask to store `kBucketSize` enums.
+    BucketType data;
+    // 1st enum this bucket can represent.
+    T start;
+
+    friend bool operator==(const Bucket& lhs, const Bucket& rhs) {
+      return lhs.start == rhs.start && lhs.data == rhs.data;
+    }
+  };
+
+  // How many distinct values can a bucket hold? 1 bit per value.
+  static constexpr size_t kBucketSize = sizeof(BucketType) * 8ULL;
 
  public:
-  // Construct an empty set.
-  EnumSet() {}
-  // Construct an set with just the given enum value.
-  explicit EnumSet(EnumType c) { Add(c); }
-  // Construct an set from an initializer list of enum values.
-  EnumSet(std::initializer_list<EnumType> cs) {
-    for (auto c : cs) Add(c);
-  }
-  EnumSet(uint32_t count, const EnumType* ptr) {
-    for (uint32_t i = 0; i < count; ++i) Add(ptr[i]);
-  }
-  // Copy constructor.
-  EnumSet(const EnumSet& other) { *this = other; }
-  // Move constructor.  The moved-from set is emptied.
-  EnumSet(EnumSet&& other) {
-    mask_ = other.mask_;
-    overflow_ = std::move(other.overflow_);
-    other.mask_ = 0;
-    other.overflow_.reset(nullptr);
-  }
-  // Assignment operator.
-  EnumSet& operator=(const EnumSet& other) {
-    if (&other != this) {
-      mask_ = other.mask_;
-      overflow_.reset(other.overflow_ ? new OverflowSetType(*other.overflow_)
-                                      : nullptr);
+  class Iterator {
+   public:
+    typedef Iterator self_type;
+    typedef T value_type;
+    typedef T& reference;
+    typedef T* pointer;
+    typedef std::forward_iterator_tag iterator_category;
+    typedef size_t difference_type;
+
+    Iterator(const Iterator& other)
+        : set_(other.set_),
+          bucketIndex_(other.bucketIndex_),
+          bucketOffset_(other.bucketOffset_) {}
+
+    Iterator& operator++() {
+      do {
+        if (bucketIndex_ >= set_->buckets_.size()) {
+          bucketIndex_ = set_->buckets_.size();
+          bucketOffset_ = 0;
+          break;
+        }
+
+        if (bucketOffset_ + 1 == kBucketSize) {
+          bucketOffset_ = 0;
+          ++bucketIndex_;
+        } else {
+          ++bucketOffset_;
+        }
+
+      } while (bucketIndex_ < set_->buckets_.size() &&
+               !set_->HasEnumAt(bucketIndex_, bucketOffset_));
+      return *this;
     }
+
+    Iterator operator++(int) {
+      Iterator old = *this;
+      operator++();
+      return old;
+    }
+
+    T operator*() const {
+      assert(set_->HasEnumAt(bucketIndex_, bucketOffset_) &&
+             "operator*() called on an invalid iterator.");
+      return GetValueFromBucket(set_->buckets_[bucketIndex_], bucketOffset_);
+    }
+
+    bool operator!=(const Iterator& other) const {
+      return set_ != other.set_ || bucketOffset_ != other.bucketOffset_ ||
+             bucketIndex_ != other.bucketIndex_;
+    }
+
+    bool operator==(const Iterator& other) const {
+      return !(operator!=(other));
+    }
+
+    Iterator& operator=(const Iterator& other) {
+      set_ = other.set_;
+      bucketIndex_ = other.bucketIndex_;
+      bucketOffset_ = other.bucketOffset_;
+      return *this;
+    }
+
+   private:
+    Iterator(const EnumSet* set, size_t bucketIndex, ElementType bucketOffset)
+        : set_(set), bucketIndex_(bucketIndex), bucketOffset_(bucketOffset) {}
+
+   private:
+    const EnumSet* set_ = nullptr;
+    // Index of the bucket in the vector.
+    size_t bucketIndex_ = 0;
+    // Offset in bits in the current bucket.
+    ElementType bucketOffset_ = 0;
+
+    friend class EnumSet;
+  };
+
+  // Required to allow the use of std::inserter.
+  using value_type = T;
+  using const_iterator = Iterator;
+  using iterator = Iterator;
+
+ public:
+  iterator cbegin() const noexcept {
+    auto it = iterator(this, /* bucketIndex= */ 0, /* bucketOffset= */ 0);
+    if (buckets_.size() == 0) {
+      return it;
+    }
+
+    // The iterator has the logic to find the next valid bit. If the value 0
+    // is not stored, use it to find the next valid bit.
+    if (!HasEnumAt(it.bucketIndex_, it.bucketOffset_)) {
+      ++it;
+    }
+
+    return it;
+  }
+
+  iterator begin() const noexcept { return cbegin(); }
+
+  iterator cend() const noexcept {
+    return iterator(this, buckets_.size(), /* bucketOffset= */ 0);
+  }
+
+  iterator end() const noexcept { return cend(); }
+
+  // Creates an empty set.
+  EnumSet() : buckets_(0), size_(0) {}
+
+  // Creates a set and store `value` in it.
+  EnumSet(T value) : EnumSet() { insert(value); }
+
+  // Creates a set and stores each `values` in it.
+  EnumSet(std::initializer_list<T> values) : EnumSet() {
+    for (auto item : values) {
+      insert(item);
+    }
+  }
+
+  // Creates a set, and insert `count` enum values pointed by `array` in it.
+  EnumSet(ElementType count, const T* array) : EnumSet() {
+    for (ElementType i = 0; i < count; i++) {
+      insert(array[i]);
+    }
+  }
+
+  // Creates a set initialized with the content of the range [begin; end[.
+  template <class InputIt>
+  EnumSet(InputIt begin, InputIt end) : EnumSet() {
+    for (; begin != end; ++begin) {
+      insert(*begin);
+    }
+  }
+
+  // Copies the EnumSet `other` into a new EnumSet.
+  EnumSet(const EnumSet& other)
+      : buckets_(other.buckets_), size_(other.size_) {}
+
+  // Moves the EnumSet `other` into a new EnumSet.
+  EnumSet(EnumSet&& other)
+      : buckets_(std::move(other.buckets_)), size_(other.size_) {}
+
+  // Deep-copies the EnumSet `other` into this EnumSet.
+  EnumSet& operator=(const EnumSet& other) {
+    buckets_ = other.buckets_;
+    size_ = other.size_;
     return *this;
   }
 
-  friend bool operator==(const EnumSet& a, const EnumSet& b) {
-    if (a.mask_ != b.mask_) {
-      return false;
+  // Matches std::unordered_set::insert behavior.
+  std::pair<iterator, bool> insert(const T& value) {
+    const size_t index = FindBucketForValue(value);
+    const ElementType offset = ComputeBucketOffset(value);
+
+    if (index >= buckets_.size() ||
+        buckets_[index].start != ComputeBucketStart(value)) {
+      size_ += 1;
+      InsertBucketFor(index, value);
+      return std::make_pair(Iterator(this, index, offset), true);
     }
 
-    if (a.overflow_ == nullptr && b.overflow_ == nullptr) {
+    auto& bucket = buckets_[index];
+    const auto mask = ComputeMaskForValue(value);
+    if (bucket.data & mask) {
+      return std::make_pair(Iterator(this, index, offset), false);
+    }
+
+    size_ += 1;
+    bucket.data |= ComputeMaskForValue(value);
+    return std::make_pair(Iterator(this, index, offset), true);
+  }
+
+  // Inserts `value` in the set if possible.
+  // Similar to `std::unordered_set::insert`, except the hint is ignored.
+  // Returns an iterator to the inserted element, or the element preventing
+  // insertion.
+  iterator insert(const_iterator, const T& value) {
+    return insert(value).first;
+  }
+
+  // Inserts `value` in the set if possible.
+  // Similar to `std::unordered_set::insert`, except the hint is ignored.
+  // Returns an iterator to the inserted element, or the element preventing
+  // insertion.
+  iterator insert(const_iterator, T&& value) { return insert(value).first; }
+
+  // Inserts all the values in the range [`first`; `last[.
+  // Similar to `std::unordered_set::insert`.
+  template <class InputIt>
+  void insert(InputIt first, InputIt last) {
+    for (auto it = first; it != last; ++it) {
+      insert(*it);
+    }
+  }
+
+  // Removes the value `value` into the set.
+  // Similar to `std::unordered_set::erase`.
+  // Returns the number of erased elements.
+  size_t erase(const T& value) {
+    const size_t index = FindBucketForValue(value);
+    if (index >= buckets_.size() ||
+        buckets_[index].start != ComputeBucketStart(value)) {
+      return 0;
+    }
+
+    auto& bucket = buckets_[index];
+    const auto mask = ComputeMaskForValue(value);
+    if (!(bucket.data & mask)) {
+      return 0;
+    }
+
+    size_ -= 1;
+    bucket.data &= ~mask;
+    if (bucket.data == 0) {
+      buckets_.erase(buckets_.cbegin() + index);
+    }
+    return 1;
+  }
+
+  // Returns true if `value` is present in the set.
+  bool contains(T value) const {
+    const size_t index = FindBucketForValue(value);
+    if (index >= buckets_.size() ||
+        buckets_[index].start != ComputeBucketStart(value)) {
+      return false;
+    }
+    auto& bucket = buckets_[index];
+    return bucket.data & ComputeMaskForValue(value);
+  }
+
+  // Returns the 1 if `value` is present in the set, `0` otherwise.
+  inline size_t count(T value) const { return contains(value) ? 1 : 0; }
+
+  // Returns true if the set is holds no values.
+  inline bool empty() const { return size_ == 0; }
+
+  // Returns the number of enums stored in this set.
+  size_t size() const { return size_; }
+
+  // Returns true if this set contains at least one value contained in `in_set`.
+  // Note: If `in_set` is empty, this function returns true.
+  bool HasAnyOf(const EnumSet<T>& in_set) const {
+    if (in_set.empty()) {
       return true;
     }
 
-    if (a.overflow_ == nullptr || b.overflow_ == nullptr) {
-      return false;
-    }
+    auto lhs = buckets_.cbegin();
+    auto rhs = in_set.buckets_.cbegin();
 
-    return *a.overflow_ == *b.overflow_;
-  }
+    while (lhs != buckets_.cend() && rhs != in_set.buckets_.cend()) {
+      if (lhs->start == rhs->start) {
+        if (lhs->data & rhs->data) {
+          // At least 1 bit is shared. Early return.
+          return true;
+        }
 
-  friend bool operator!=(const EnumSet& a, const EnumSet& b) {
-    return !(a == b);
-  }
+        lhs++;
+        rhs++;
+        continue;
+      }
 
-  // Adds the given enum value to the set.  This has no effect if the
-  // enum value is already in the set.
-  void Add(EnumType c) { AddWord(ToWord(c)); }
+      // LHS bucket is smaller than the current RHS bucket. Catching up on RHS.
+      if (lhs->start < rhs->start) {
+        lhs++;
+        continue;
+      }
 
-  // Removes the given enum value from the set.  This has no effect if the
-  // enum value is not in the set.
-  void Remove(EnumType c) { RemoveWord(ToWord(c)); }
-
-  // Returns true if this enum value is in the set.
-  bool Contains(EnumType c) const { return ContainsWord(ToWord(c)); }
-
-  // Applies f to each enum in the set, in order from smallest enum
-  // value to largest.
-  void ForEach(std::function<void(EnumType)> f) const {
-    for (uint32_t i = 0; i < 64; ++i) {
-      if (mask_ & AsMask(i)) f(static_cast<EnumType>(i));
-    }
-    if (overflow_) {
-      for (uint32_t c : *overflow_) f(static_cast<EnumType>(c));
-    }
-  }
-
-  // Returns true if the set is empty.
-  bool IsEmpty() const {
-    if (mask_) return false;
-    if (overflow_ && !overflow_->empty()) return false;
-    return true;
-  }
-
-  // Returns true if the set contains ANY of the elements of |in_set|,
-  // or if |in_set| is empty.
-  bool HasAnyOf(const EnumSet<EnumType>& in_set) const {
-    if (in_set.IsEmpty()) return true;
-
-    if (mask_ & in_set.mask_) return true;
-
-    if (!overflow_ || !in_set.overflow_) return false;
-
-    for (uint32_t item : *in_set.overflow_) {
-      if (overflow_->find(item) != overflow_->end()) return true;
+      // Otherwise, RHS needs to catch up on LHS.
+      rhs++;
     }
 
     return false;
   }
 
  private:
-  // Adds the given enum value (as a 32-bit word) to the set.  This has no
-  // effect if the enum value is already in the set.
-  void AddWord(uint32_t word) {
-    if (auto new_bits = AsMask(word)) {
-      mask_ |= new_bits;
-    } else {
-      Overflow().insert(word);
+  // Returns the index of the last bucket in which `value` could be stored.
+  static constexpr inline size_t ComputeLargestPossibleBucketIndexFor(T value) {
+    return static_cast<size_t>(value) / kBucketSize;
+  }
+
+  // Returns the smallest enum value that could be contained in the same bucket
+  // as `value`.
+  static constexpr inline T ComputeBucketStart(T value) {
+    return static_cast<T>(kBucketSize *
+                          ComputeLargestPossibleBucketIndexFor(value));
+  }
+
+  //  Returns the index of the bit that corresponds to `value` in the bucket.
+  static constexpr inline ElementType ComputeBucketOffset(T value) {
+    return static_cast<ElementType>(value) % kBucketSize;
+  }
+
+  // Returns the bitmask used to represent the enum `value` in its bucket.
+  static constexpr inline BucketType ComputeMaskForValue(T value) {
+    return 1ULL << ComputeBucketOffset(value);
+  }
+
+  // Returns the `enum` stored in `bucket` at `offset`.
+  // `offset` is the bit-offset in the bucket storage.
+  static constexpr inline T GetValueFromBucket(const Bucket& bucket,
+                                               BucketType offset) {
+    return static_cast<T>(static_cast<ElementType>(bucket.start) + offset);
+  }
+
+  // For a given enum `value`, finds the bucket index that could contain this
+  // value. If no such bucket is found, the index at which the new bucket should
+  // be inserted is returned.
+  size_t FindBucketForValue(T value) const {
+    // Set is empty, insert at 0.
+    if (buckets_.size() == 0) {
+      return 0;
     }
-  }
 
-  // Removes the given enum value (as a 32-bit word) from the set.  This has no
-  // effect if the enum value is not in the set.
-  void RemoveWord(uint32_t word) {
-    if (auto new_bits = AsMask(word)) {
-      mask_ &= ~new_bits;
-    } else {
-      auto itr = Overflow().find(word);
-      if (itr != Overflow().end()) Overflow().erase(itr);
+    const T wanted_start = ComputeBucketStart(value);
+    assert(buckets_.size() > 0 &&
+           "Size must not be 0 here. Has the code above changed?");
+    size_t index = std::min(buckets_.size() - 1,
+                            ComputeLargestPossibleBucketIndexFor(value));
+
+    // This loops behaves like std::upper_bound with a reverse iterator.
+    // Buckets are sorted. 3 main cases:
+    //  - The bucket matches
+    //    => returns the bucket index.
+    //  - The found bucket is larger
+    //    => scans left until it finds the correct bucket, or insertion point.
+    //  - The found bucket is smaller
+    //    => We are at the end, so we return past-end index for insertion.
+    for (; buckets_[index].start >= wanted_start; index--) {
+      if (index == 0) {
+        return 0;
+      }
     }
+
+    return index + 1;
   }
 
-  // Returns true if the enum represented as a 32-bit word is in the set.
-  bool ContainsWord(uint32_t word) const {
-    // We shouldn't call Overflow() since this is a const method.
-    if (auto bits = AsMask(word)) {
-      return (mask_ & bits) != 0;
-    } else if (auto overflow = overflow_.get()) {
-      return overflow->find(word) != overflow->end();
+  // Creates a new bucket to store `value` and inserts it at `index`.
+  // If the `index` is past the end, the bucket is inserted at the end of the
+  // vector.
+  void InsertBucketFor(size_t index, T value) {
+    const T bucket_start = ComputeBucketStart(value);
+    Bucket bucket = {1ULL << ComputeBucketOffset(value), bucket_start};
+    auto it = buckets_.emplace(buckets_.begin() + index, std::move(bucket));
+#if defined(NDEBUG)
+    (void)it;  // Silencing unused variable warning.
+#else
+    assert(std::next(it) == buckets_.end() ||
+           std::next(it)->start > bucket_start);
+    assert(it == buckets_.begin() || std::prev(it)->start < bucket_start);
+#endif
+  }
+
+  // Returns true if the bucket at `bucketIndex/ stores the enum at
+  // `bucketOffset`, false otherwise.
+  bool HasEnumAt(size_t bucketIndex, BucketType bucketOffset) const {
+    assert(bucketIndex < buckets_.size());
+    assert(bucketOffset < kBucketSize);
+    return buckets_[bucketIndex].data & (1ULL << bucketOffset);
+  }
+
+  // Returns true if `lhs` and `rhs` hold the exact same values.
+  friend bool operator==(const EnumSet& lhs, const EnumSet& rhs) {
+    if (lhs.size_ != rhs.size_) {
+      return false;
     }
-    // The word is large, but the set doesn't have large members, so
-    // it doesn't have an overflow set.
-    return false;
-  }
 
-  // Returns the enum value as a uint32_t.
-  uint32_t ToWord(EnumType value) const {
-    static_assert(sizeof(EnumType) <= sizeof(uint32_t),
-                  "EnumType must statically castable to uint32_t");
-    return static_cast<uint32_t>(value);
-  }
-
-  // Determines whether the given enum value can be represented
-  // as a bit in a uint64_t mask. If so, then returns that mask bit.
-  // Otherwise, returns 0.
-  uint64_t AsMask(uint32_t word) const {
-    if (word > 63) return 0;
-    return uint64_t(1) << word;
-  }
-
-  // Ensures that overflow_set_ references a set.  A new empty set is
-  // allocated if one doesn't exist yet.  Returns overflow_set_.
-  OverflowSetType& Overflow() {
-    if (overflow_.get() == nullptr) {
-      overflow_ = MakeUnique<OverflowSetType>();
+    if (lhs.buckets_.size() != rhs.buckets_.size()) {
+      return false;
     }
-    return *overflow_;
+    return lhs.buckets_ == rhs.buckets_;
   }
 
-  // Enums with values up to 63 are stored as bits in this mask.
-  uint64_t mask_ = 0;
-  // Enums with values larger than 63 are stored in this set.
-  // This set should normally be empty or very small.
-  std::unique_ptr<OverflowSetType> overflow_ = {};
+  // Returns true if `lhs` and `rhs` hold at least 1 different value.
+  friend bool operator!=(const EnumSet& lhs, const EnumSet& rhs) {
+    return !(lhs == rhs);
+  }
+
+  // Storage for the buckets.
+  std::vector<Bucket> buckets_;
+  // How many enums is this set storing.
+  size_t size_ = 0;
 };
 
-// A set of SpvCapability, optimized for small capability values.
-using CapabilitySet = EnumSet<SpvCapability>;
+// A set of spv::Capability.
+using CapabilitySet = EnumSet<spv::Capability>;
 
 }  // namespace spvtools
 
diff --git a/source/enum_string_mapping.h b/source/enum_string_mapping.h
index af8f56b..b136584 100644
--- a/source/enum_string_mapping.h
+++ b/source/enum_string_mapping.h
@@ -29,7 +29,7 @@
 const char* ExtensionToString(Extension extension);
 
 // Returns text string corresponding to |capability|.
-const char* CapabilityToString(SpvCapability capability);
+const char* CapabilityToString(spv::Capability capability);
 
 }  // namespace spvtools
 
diff --git a/source/extensions.cpp b/source/extensions.cpp
index 049a3ad..ac987fc 100644
--- a/source/extensions.cpp
+++ b/source/extensions.cpp
@@ -24,7 +24,9 @@
 namespace spvtools {
 
 std::string GetExtensionString(const spv_parsed_instruction_t* inst) {
-  if (inst->opcode != SpvOpExtension) return "ERROR_not_op_extension";
+  if (inst->opcode != static_cast<uint16_t>(spv::Op::OpExtension)) {
+    return "ERROR_not_op_extension";
+  }
 
   assert(inst->num_operands == 1);
 
@@ -38,8 +40,9 @@
 
 std::string ExtensionSetToString(const ExtensionSet& extensions) {
   std::stringstream ss;
-  extensions.ForEach(
-      [&ss](Extension ext) { ss << ExtensionToString(ext) << " "; });
+  for (auto extension : extensions) {
+    ss << ExtensionToString(extension) << " ";
+  }
   return ss.str();
 }
 
diff --git a/source/extensions.h b/source/extensions.h
index 8023444..cda4924 100644
--- a/source/extensions.h
+++ b/source/extensions.h
@@ -15,6 +15,7 @@
 #ifndef SOURCE_EXTENSIONS_H_
 #define SOURCE_EXTENSIONS_H_
 
+#include <cstdint>
 #include <string>
 
 #include "source/enum_set.h"
@@ -23,7 +24,7 @@
 namespace spvtools {
 
 // The known SPIR-V extensions.
-enum Extension {
+enum Extension : uint32_t {
 #include "extension_enum.inc"
 };
 
diff --git a/source/fuzz/CMakeLists.txt b/source/fuzz/CMakeLists.txt
index dd674dd..86ee657 100644
--- a/source/fuzz/CMakeLists.txt
+++ b/source/fuzz/CMakeLists.txt
@@ -470,10 +470,7 @@
   spvtools_check_symbol_exports(SPIRV-Tools-fuzz)
 
   if(ENABLE_SPIRV_TOOLS_INSTALL)
-      install(TARGETS SPIRV-Tools-fuzz EXPORT SPIRV-Tools-fuzzTargets
-            RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
-            LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
-            ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
+      install(TARGETS SPIRV-Tools-fuzz 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/added_function_reducer.cpp b/source/fuzz/added_function_reducer.cpp
index e7cb027..95cf9d6 100644
--- a/source/fuzz/added_function_reducer.cpp
+++ b/source/fuzz/added_function_reducer.cpp
@@ -130,7 +130,7 @@
                   binary_under_reduction.size());
   assert(ir_context != nullptr && "The binary should be parsable.");
   for (auto& type_or_value : ir_context->module()->types_values()) {
-    if (type_or_value.opcode() != SpvOpVariable) {
+    if (type_or_value.opcode() != spv::Op::OpVariable) {
       continue;
     }
     if (irrelevant_pointee_global_variables.count(type_or_value.result_id())) {
@@ -202,7 +202,7 @@
   auto* ir_context = replay_result.transformed_module.get();
 
   for (auto& type_or_value : ir_context->module()->types_values()) {
-    if (type_or_value.opcode() != SpvOpVariable) {
+    if (type_or_value.opcode() != spv::Op::OpVariable) {
       continue;
     }
     if (replay_result.transformation_context->GetFactManager()
diff --git a/source/fuzz/call_graph.cpp b/source/fuzz/call_graph.cpp
index c52bc34..d61a5f8 100644
--- a/source/fuzz/call_graph.cpp
+++ b/source/fuzz/call_graph.cpp
@@ -54,7 +54,7 @@
     // Consider every function call instruction in every block.
     for (auto& block : function) {
       for (auto& instruction : block) {
-        if (instruction.opcode() != SpvOpFunctionCall) {
+        if (instruction.opcode() != spv::Op::OpFunctionCall) {
           continue;
         }
         // Get the id of the function being called.
diff --git a/source/fuzz/fact_manager/constant_uniform_facts.cpp b/source/fuzz/fact_manager/constant_uniform_facts.cpp
index a629c0d..461859b 100644
--- a/source/fuzz/fact_manager/constant_uniform_facts.cpp
+++ b/source/fuzz/fact_manager/constant_uniform_facts.cpp
@@ -63,7 +63,7 @@
 bool ConstantUniformFacts::DataMatches(
     const opt::Instruction& constant_instruction,
     const protobufs::FactConstantUniform& constant_uniform_fact) {
-  assert(constant_instruction.opcode() == SpvOpConstant);
+  assert(constant_instruction.opcode() == spv::Op::OpConstant);
   std::vector<uint32_t> data_in_constant;
   for (uint32_t i = 0; i < constant_instruction.NumInOperands(); i++) {
     data_in_constant.push_back(constant_instruction.GetSingleWordInOperand(i));
@@ -95,7 +95,7 @@
     uint32_t constant_id) const {
   std::vector<protobufs::UniformBufferElementDescriptor> result;
   auto constant_inst = ir_context_->get_def_use_mgr()->GetDef(constant_id);
-  assert(constant_inst->opcode() == SpvOpConstant &&
+  assert(constant_inst->opcode() == spv::Op::OpConstant &&
          "The given id must be that of a constant");
   auto type_id = constant_inst->type_id();
   for (auto& fact_and_type_id : facts_and_type_ids_) {
@@ -175,8 +175,9 @@
     return false;
   }
 
-  assert(SpvOpVariable == uniform_variable->opcode());
-  assert(SpvStorageClassUniform == uniform_variable->GetSingleWordInOperand(0));
+  assert(spv::Op::OpVariable == uniform_variable->opcode());
+  assert(spv::StorageClass::Uniform ==
+         spv::StorageClass(uniform_variable->GetSingleWordInOperand(0)));
 
   auto should_be_uniform_pointer_type =
       ir_context_->get_type_mgr()->GetType(uniform_variable->type_id());
@@ -184,7 +185,7 @@
     return false;
   }
   if (should_be_uniform_pointer_type->AsPointer()->storage_class() !=
-      SpvStorageClassUniform) {
+      spv::StorageClass::Uniform) {
     return false;
   }
   auto should_be_uniform_pointer_instruction =
diff --git a/source/fuzz/fact_manager/data_synonym_and_id_equation_facts.cpp b/source/fuzz/fact_manager/data_synonym_and_id_equation_facts.cpp
index a2c1f2c..b43b8ed 100644
--- a/source/fuzz/fact_manager/data_synonym_and_id_equation_facts.cpp
+++ b/source/fuzz/fact_manager/data_synonym_and_id_equation_facts.cpp
@@ -23,7 +23,7 @@
 size_t DataSynonymAndIdEquationFacts::OperationHash::operator()(
     const Operation& operation) const {
   std::u32string hash;
-  hash.push_back(operation.opcode);
+  hash.push_back(uint32_t(operation.opcode));
   for (auto operand : operation.operands) {
     hash.push_back(static_cast<uint32_t>(DataDescriptorHash()(operand)));
   }
@@ -104,7 +104,8 @@
   }
 
   // Now add the fact.
-  AddEquationFactRecursive(lhs_dd, static_cast<SpvOp>(fact.opcode()), rhs_dds);
+  AddEquationFactRecursive(lhs_dd, static_cast<spv::Op>(fact.opcode()),
+                           rhs_dds);
   return true;
 }
 
@@ -119,7 +120,7 @@
 }
 
 void DataSynonymAndIdEquationFacts::AddEquationFactRecursive(
-    const protobufs::DataDescriptor& lhs_dd, SpvOp opcode,
+    const protobufs::DataDescriptor& lhs_dd, spv::Op opcode,
     const std::vector<const protobufs::DataDescriptor*>& rhs_dds) {
   assert(synonymous_.Exists(lhs_dd) &&
          "The LHS must be known to the equivalence relation.");
@@ -155,21 +156,21 @@
   // Now try to work out corollaries implied by the new equation and existing
   // facts.
   switch (opcode) {
-    case SpvOpConvertSToF:
-    case SpvOpConvertUToF:
+    case spv::Op::OpConvertSToF:
+    case spv::Op::OpConvertUToF:
       ComputeConversionDataSynonymFacts(*rhs_dds[0]);
       break;
-    case SpvOpBitcast: {
+    case spv::Op::OpBitcast: {
       assert(DataDescriptorsAreWellFormedAndComparable(lhs_dd, *rhs_dds[0]) &&
              "Operands of OpBitcast equation fact must have compatible types");
       if (!synonymous_.IsEquivalent(lhs_dd, *rhs_dds[0])) {
         AddDataSynonymFactRecursive(lhs_dd, *rhs_dds[0]);
       }
     } break;
-    case SpvOpIAdd: {
+    case spv::Op::OpIAdd: {
       // Equation form: "a = b + c"
       for (const auto& equation : GetEquations(rhs_dds[0])) {
-        if (equation.opcode == SpvOpISub) {
+        if (equation.opcode == spv::Op::OpISub) {
           // Equation form: "a = (d - e) + c"
           if (synonymous_.IsEquivalent(*equation.operands[1], *rhs_dds[1])) {
             // Equation form: "a = (d - c) + c"
@@ -179,7 +180,7 @@
         }
       }
       for (const auto& equation : GetEquations(rhs_dds[1])) {
-        if (equation.opcode == SpvOpISub) {
+        if (equation.opcode == spv::Op::OpISub) {
           // Equation form: "a = b + (d - e)"
           if (synonymous_.IsEquivalent(*equation.operands[1], *rhs_dds[0])) {
             // Equation form: "a = b + (d - b)"
@@ -190,10 +191,10 @@
       }
       break;
     }
-    case SpvOpISub: {
+    case spv::Op::OpISub: {
       // Equation form: "a = b - c"
       for (const auto& equation : GetEquations(rhs_dds[0])) {
-        if (equation.opcode == SpvOpIAdd) {
+        if (equation.opcode == spv::Op::OpIAdd) {
           // Equation form: "a = (d + e) - c"
           if (synonymous_.IsEquivalent(*equation.operands[0], *rhs_dds[1])) {
             // Equation form: "a = (c + e) - c"
@@ -207,34 +208,34 @@
           }
         }
 
-        if (equation.opcode == SpvOpISub) {
+        if (equation.opcode == spv::Op::OpISub) {
           // Equation form: "a = (d - e) - c"
           if (synonymous_.IsEquivalent(*equation.operands[0], *rhs_dds[1])) {
             // Equation form: "a = (c - e) - c"
             // We can thus infer "a = -e"
-            AddEquationFactRecursive(lhs_dd, SpvOpSNegate,
+            AddEquationFactRecursive(lhs_dd, spv::Op::OpSNegate,
                                      {equation.operands[1]});
           }
         }
       }
 
       for (const auto& equation : GetEquations(rhs_dds[1])) {
-        if (equation.opcode == SpvOpIAdd) {
+        if (equation.opcode == spv::Op::OpIAdd) {
           // Equation form: "a = b - (d + e)"
           if (synonymous_.IsEquivalent(*equation.operands[0], *rhs_dds[0])) {
             // Equation form: "a = b - (b + e)"
             // We can thus infer "a = -e"
-            AddEquationFactRecursive(lhs_dd, SpvOpSNegate,
+            AddEquationFactRecursive(lhs_dd, spv::Op::OpSNegate,
                                      {equation.operands[1]});
           }
           if (synonymous_.IsEquivalent(*equation.operands[1], *rhs_dds[0])) {
             // Equation form: "a = b - (d + b)"
             // We can thus infer "a = -d"
-            AddEquationFactRecursive(lhs_dd, SpvOpSNegate,
+            AddEquationFactRecursive(lhs_dd, spv::Op::OpSNegate,
                                      {equation.operands[0]});
           }
         }
-        if (equation.opcode == SpvOpISub) {
+        if (equation.opcode == spv::Op::OpISub) {
           // Equation form: "a = b - (d - e)"
           if (synonymous_.IsEquivalent(*equation.operands[0], *rhs_dds[0])) {
             // Equation form: "a = b - (b - e)"
@@ -245,8 +246,8 @@
       }
       break;
     }
-    case SpvOpLogicalNot:
-    case SpvOpSNegate: {
+    case spv::Op::OpLogicalNot:
+    case spv::Op::OpSNegate: {
       // Equation form: "a = !b" or "a = -b"
       for (const auto& equation : GetEquations(rhs_dds[0])) {
         if (equation.opcode == opcode) {
@@ -321,9 +322,9 @@
 
       for (const auto& equation : fact.second) {
         if (synonymous_.IsEquivalent(*equation.operands[0], dd)) {
-          if (equation.opcode == SpvOpConvertSToF) {
+          if (equation.opcode == spv::Op::OpConvertSToF) {
             convert_s_to_f_lhs.push_back(*dd_it);
-          } else if (equation.opcode == SpvOpConvertUToF) {
+          } else if (equation.opcode == spv::Op::OpConvertUToF) {
             convert_u_to_f_lhs.push_back(*dd_it);
           }
         }
@@ -808,9 +809,9 @@
   }
   // Neither end type is allowed to be void.
   if (ir_context_->get_def_use_mgr()->GetDef(end_type_id_1)->opcode() ==
-          SpvOpTypeVoid ||
+          spv::Op::OpTypeVoid ||
       ir_context_->get_def_use_mgr()->GetDef(end_type_id_2)->opcode() ==
-          SpvOpTypeVoid) {
+          spv::Op::OpTypeVoid) {
     return false;
   }
   // If the end types are the same, the data descriptors are comparable.
diff --git a/source/fuzz/fact_manager/data_synonym_and_id_equation_facts.h b/source/fuzz/fact_manager/data_synonym_and_id_equation_facts.h
index 6652f30..36d92ca 100644
--- a/source/fuzz/fact_manager/data_synonym_and_id_equation_facts.h
+++ b/source/fuzz/fact_manager/data_synonym_and_id_equation_facts.h
@@ -79,7 +79,7 @@
   // This helper struct represents the right hand side of an equation as an
   // operator applied to a number of data descriptor operands.
   struct Operation {
-    SpvOp opcode;
+    spv::Op opcode;
     std::vector<const protobufs::DataDescriptor*> operands;
   };
 
@@ -144,7 +144,7 @@
   // corollaries, in the form of data synonym or equation facts, that follow
   // from this and other known facts.
   void AddEquationFactRecursive(
-      const protobufs::DataDescriptor& lhs_dd, SpvOp opcode,
+      const protobufs::DataDescriptor& lhs_dd, spv::Op opcode,
       const std::vector<const protobufs::DataDescriptor*>& rhs_dds);
 
   // Returns true if and only if |dd.object()| still exists in the module.
diff --git a/source/fuzz/fact_manager/fact_manager.cpp b/source/fuzz/fact_manager/fact_manager.cpp
index 40c0865..c99f690 100644
--- a/source/fuzz/fact_manager/fact_manager.cpp
+++ b/source/fuzz/fact_manager/fact_manager.cpp
@@ -64,7 +64,7 @@
 std::string ToString(const protobufs::FactIdEquation& fact) {
   std::stringstream stream;
   stream << fact.lhs_id();
-  stream << " " << static_cast<SpvOp>(fact.opcode());
+  stream << " " << fact.opcode();
   for (auto rhs_id : fact.rhs_id()) {
     stream << " " << rhs_id;
   }
@@ -255,11 +255,11 @@
   assert(success && "|result_id| is invalid");
 }
 
-void FactManager::AddFactIdEquation(uint32_t lhs_id, SpvOp opcode,
+void FactManager::AddFactIdEquation(uint32_t lhs_id, spv::Op opcode,
                                     const std::vector<uint32_t>& rhs_id) {
   protobufs::FactIdEquation fact;
   fact.set_lhs_id(lhs_id);
-  fact.set_opcode(opcode);
+  fact.set_opcode(uint32_t(opcode));
   for (auto an_rhs_id : rhs_id) {
     fact.add_rhs_id(an_rhs_id);
   }
diff --git a/source/fuzz/fact_manager/fact_manager.h b/source/fuzz/fact_manager/fact_manager.h
index ce28ae4..4453e44 100644
--- a/source/fuzz/fact_manager/fact_manager.h
+++ b/source/fuzz/fact_manager/fact_manager.h
@@ -83,7 +83,7 @@
   //   |lhs_id| = |opcode| |rhs_id[0]| ... |rhs_id[N-1]|
   //
   // Neither |lhs_id| nor any of |rhs_id| may be irrelevant.
-  void AddFactIdEquation(uint32_t lhs_id, SpvOp opcode,
+  void AddFactIdEquation(uint32_t lhs_id, spv::Op opcode,
                          const std::vector<uint32_t>& rhs_id);
 
   // Inspects all known facts and adds corollary facts; e.g. if we know that
diff --git a/source/fuzz/force_render_red.cpp b/source/fuzz/force_render_red.cpp
index 3267487..191fd71 100644
--- a/source/fuzz/force_render_red.cpp
+++ b/source/fuzz/force_render_red.cpp
@@ -36,8 +36,9 @@
   // Check that this is a fragment shader
   bool found_capability_shader = false;
   for (auto& capability : ir_context->capabilities()) {
-    assert(capability.opcode() == SpvOpCapability);
-    if (capability.GetSingleWordInOperand(0) == SpvCapabilityShader) {
+    assert(capability.opcode() == spv::Op::OpCapability);
+    if (spv::Capability(capability.GetSingleWordInOperand(0)) ==
+        spv::Capability::Shader) {
       found_capability_shader = true;
       break;
     }
@@ -51,7 +52,8 @@
 
   opt::Instruction* fragment_entry_point = nullptr;
   for (auto& entry_point : ir_context->module()->entry_points()) {
-    if (entry_point.GetSingleWordInOperand(0) == SpvExecutionModelFragment) {
+    if (spv::ExecutionModel(entry_point.GetSingleWordInOperand(0)) ==
+        spv::ExecutionModel::Fragment) {
       fragment_entry_point = &entry_point;
       break;
     }
@@ -81,8 +83,9 @@
                                          MessageConsumer message_consumer) {
   opt::Instruction* output_variable = nullptr;
   for (auto& inst : ir_context->types_values()) {
-    if (inst.opcode() == SpvOpVariable &&
-        inst.GetSingleWordInOperand(0) == SpvStorageClassOutput) {
+    if (inst.opcode() == spv::Op::OpVariable &&
+        spv::StorageClass(inst.GetSingleWordInOperand(0)) ==
+            spv::StorageClass::Output) {
       if (output_variable != nullptr) {
         message_consumer(SPV_MSG_ERROR, nullptr, {},
                          "Only one output variable can be handled at present; "
@@ -144,10 +147,11 @@
                                uint32_t greater_than_instruction,
                                uint32_t in_operand_index) {
   return MakeUnique<TransformationReplaceConstantWithUniform>(
-      MakeIdUseDescriptor(constant_id,
-                          MakeInstructionDescriptor(greater_than_instruction,
-                                                    SpvOpFOrdGreaterThan, 0),
-                          in_operand_index),
+      MakeIdUseDescriptor(
+          constant_id,
+          MakeInstructionDescriptor(greater_than_instruction,
+                                    spv::Op::OpFOrdGreaterThan, 0),
+          in_operand_index),
       fact_manager.GetUniformDescriptorsForConstant(constant_id)[0],
       ir_context->TakeNextId(), ir_context->TakeNextId());
 }
@@ -204,20 +208,21 @@
   // Make the new exit block
   auto new_exit_block_id = ir_context->TakeNextId();
   {
-    auto label = MakeUnique<opt::Instruction>(ir_context.get(), SpvOpLabel, 0,
-                                              new_exit_block_id,
-                                              opt::Instruction::OperandList());
+    auto label = MakeUnique<opt::Instruction>(
+        ir_context.get(), spv::Op::OpLabel, 0, new_exit_block_id,
+        opt::Instruction::OperandList());
     auto new_exit_block = MakeUnique<opt::BasicBlock>(std::move(label));
-    new_exit_block->AddInstruction(MakeUnique<opt::Instruction>(
-        ir_context.get(), SpvOpReturn, 0, 0, opt::Instruction::OperandList()));
+    new_exit_block->AddInstruction(
+        MakeUnique<opt::Instruction>(ir_context.get(), spv::Op::OpReturn, 0, 0,
+                                     opt::Instruction::OperandList()));
     entry_point_function->AddBasicBlock(std::move(new_exit_block));
   }
 
   // Make the new entry block
   {
-    auto label = MakeUnique<opt::Instruction>(ir_context.get(), SpvOpLabel, 0,
-                                              ir_context->TakeNextId(),
-                                              opt::Instruction::OperandList());
+    auto label = MakeUnique<opt::Instruction>(
+        ir_context.get(), spv::Op::OpLabel, 0, ir_context->TakeNextId(),
+        opt::Instruction::OperandList());
     auto new_entry_block = MakeUnique<opt::BasicBlock>(std::move(label));
 
     // Make an instruction to construct vec4(1.0, 0.0, 0.0, 1.0), representing
@@ -229,7 +234,7 @@
     auto temp_vec4 = opt::analysis::Vector(float_type, 4);
     auto vec4_id = ir_context->get_type_mgr()->GetId(&temp_vec4);
     auto red = MakeUnique<opt::Instruction>(
-        ir_context.get(), SpvOpCompositeConstruct, vec4_id,
+        ir_context.get(), spv::Op::OpCompositeConstruct, vec4_id,
         ir_context->TakeNextId(), op_composite_construct_operands);
     auto red_id = red->result_id();
     new_entry_block->AddInstruction(std::move(red));
@@ -241,7 +246,7 @@
     opt::Instruction::OperandList op_store_operands = {variable_to_store_into,
                                                        value_to_be_stored};
     new_entry_block->AddInstruction(MakeUnique<opt::Instruction>(
-        ir_context.get(), SpvOpStore, 0, 0, op_store_operands));
+        ir_context.get(), spv::Op::OpStore, 0, 0, op_store_operands));
 
     // We are going to attempt to construct 'false' as an expression of the form
     // 'literal1 > literal2'. If we succeed, we will later replace each literal
@@ -313,7 +318,7 @@
               {SPV_OPERAND_TYPE_ID, {smaller_constant}},
               {SPV_OPERAND_TYPE_ID, {larger_constant}}};
           new_entry_block->AddInstruction(MakeUnique<opt::Instruction>(
-              ir_context.get(), SpvOpFOrdGreaterThan,
+              ir_context.get(), spv::Op::OpFOrdGreaterThan,
               ir_context->get_type_mgr()->GetId(registered_bool_type),
               id_guaranteed_to_be_false, greater_than_operands));
 
@@ -344,9 +349,9 @@
     opt::Operand else_block = {SPV_OPERAND_TYPE_ID, {new_exit_block_id}};
     opt::Instruction::OperandList op_branch_conditional_operands = {
         false_condition, then_block, else_block};
-    new_entry_block->AddInstruction(
-        MakeUnique<opt::Instruction>(ir_context.get(), SpvOpBranchConditional,
-                                     0, 0, op_branch_conditional_operands));
+    new_entry_block->AddInstruction(MakeUnique<opt::Instruction>(
+        ir_context.get(), spv::Op::OpBranchConditional, 0, 0,
+        op_branch_conditional_operands));
 
     entry_point_function->InsertBasicBlockBefore(
         std::move(new_entry_block), entry_point_function->entry().get());
diff --git a/source/fuzz/fuzzer_pass.cpp b/source/fuzz/fuzzer_pass.cpp
index 6a87985..02d8aa1 100644
--- a/source/fuzz/fuzzer_pass.cpp
+++ b/source/fuzz/fuzzer_pass.cpp
@@ -131,14 +131,15 @@
     // should skip when searching from 'base' for the desired instruction.
     // (An instruction that has a result id is represented by its own opcode,
     // itself as 'base', and a skip-count of 0.)
-    std::vector<std::tuple<uint32_t, SpvOp, uint32_t>> base_opcode_skip_triples;
+    std::vector<std::tuple<uint32_t, spv::Op, uint32_t>>
+        base_opcode_skip_triples;
 
     // The initial base instruction is the block label.
     uint32_t base = block->id();
 
     // Counts the number of times we have seen each opcode since we reset the
     // base instruction.
-    std::map<SpvOp, uint32_t> skip_count;
+    std::map<spv::Op, uint32_t> skip_count;
 
     // Consider every instruction in the block.  The label is excluded: it is
     // only necessary to consider it as a base in case the first instruction
@@ -151,7 +152,7 @@
         base = inst_it->result_id();
         skip_count.clear();
       }
-      const SpvOp opcode = inst_it->opcode();
+      const spv::Op opcode = inst_it->opcode();
 
       // Invoke the provided function, which might apply a transformation.
       action(block, inst_it,
@@ -330,7 +331,7 @@
 }
 
 uint32_t FuzzerPass::FindOrCreatePointerType(uint32_t base_type_id,
-                                             SpvStorageClass storage_class) {
+                                             spv::StorageClass storage_class) {
   // We do not use the type manager here, due to problems related to isomorphic
   // but distinct structs not being regarded as different.
   auto existing_id = fuzzerutil::MaybeGetPointerType(
@@ -345,7 +346,7 @@
 }
 
 uint32_t FuzzerPass::FindOrCreatePointerToIntegerType(
-    uint32_t width, bool is_signed, SpvStorageClass storage_class) {
+    uint32_t width, bool is_signed, spv::StorageClass storage_class) {
   return FindOrCreatePointerType(FindOrCreateIntegerType(width, is_signed),
                                  storage_class);
 }
@@ -432,7 +433,7 @@
 
 uint32_t FuzzerPass::FindOrCreateGlobalUndef(uint32_t type_id) {
   for (auto& inst : GetIRContext()->types_values()) {
-    if (inst.opcode() == SpvOpUndef && inst.type_id() == type_id) {
+    if (inst.opcode() == spv::Op::OpUndef && inst.type_id() == type_id) {
       return inst.result_id();
     }
   }
@@ -464,7 +465,7 @@
 
 std::pair<std::vector<uint32_t>, std::map<uint32_t, std::vector<uint32_t>>>
 FuzzerPass::GetAvailableBasicTypesAndPointers(
-    SpvStorageClass storage_class) const {
+    spv::StorageClass storage_class) const {
   // Records all of the basic types available in the module.
   std::set<uint32_t> basic_types;
 
@@ -480,23 +481,23 @@
     // For pointer types with basic pointee types, associate the pointer type
     // with the basic type.
     switch (inst.opcode()) {
-      case SpvOpTypeBool:
-      case SpvOpTypeFloat:
-      case SpvOpTypeInt:
-      case SpvOpTypeMatrix:
-      case SpvOpTypeVector:
+      case spv::Op::OpTypeBool:
+      case spv::Op::OpTypeFloat:
+      case spv::Op::OpTypeInt:
+      case spv::Op::OpTypeMatrix:
+      case spv::Op::OpTypeVector:
         // These are all basic types.
         basic_types.insert(inst.result_id());
         basic_type_to_pointers.insert({inst.result_id(), {}});
         break;
-      case SpvOpTypeArray:
+      case spv::Op::OpTypeArray:
         // An array type is basic if its base type is basic.
         if (basic_types.count(inst.GetSingleWordInOperand(0))) {
           basic_types.insert(inst.result_id());
           basic_type_to_pointers.insert({inst.result_id(), {}});
         }
         break;
-      case SpvOpTypeStruct: {
+      case spv::Op::OpTypeStruct: {
         // A struct type is basic if it does not have the Block/BufferBlock
         // decoration, and if all of its members are basic.
         if (!fuzzerutil::HasBlockOrBufferBlockDecoration(GetIRContext(),
@@ -515,11 +516,12 @@
         }
         break;
       }
-      case SpvOpTypePointer: {
+      case spv::Op::OpTypePointer: {
         // We are interested in the pointer if its pointee type is basic and it
         // has the right storage class.
         auto pointee_type = inst.GetSingleWordInOperand(1);
-        if (inst.GetSingleWordInOperand(0) == storage_class &&
+        if (spv::StorageClass(inst.GetSingleWordInOperand(0)) ==
+                storage_class &&
             basic_types.count(pointee_type)) {
           // The pointer has the desired storage class, and its pointee type is
           // a basic type, so we are interested in it.  Associate it with its
@@ -541,22 +543,22 @@
       GetIRContext()->get_def_use_mgr()->GetDef(scalar_or_composite_type_id);
   assert(type_instruction && "The type instruction must exist.");
   switch (type_instruction->opcode()) {
-    case SpvOpTypeBool:
+    case spv::Op::OpTypeBool:
       return FindOrCreateBoolConstant(false, is_irrelevant);
-    case SpvOpTypeFloat: {
+    case spv::Op::OpTypeFloat: {
       auto width = type_instruction->GetSingleWordInOperand(0);
       auto num_words = (width + 32 - 1) / 32;
       return FindOrCreateFloatConstant(std::vector<uint32_t>(num_words, 0),
                                        width, is_irrelevant);
     }
-    case SpvOpTypeInt: {
+    case spv::Op::OpTypeInt: {
       auto width = type_instruction->GetSingleWordInOperand(0);
       auto num_words = (width + 32 - 1) / 32;
       return FindOrCreateIntegerConstant(
           std::vector<uint32_t>(num_words, 0), width,
           type_instruction->GetSingleWordInOperand(1), is_irrelevant);
     }
-    case SpvOpTypeArray: {
+    case spv::Op::OpTypeArray: {
       auto component_type_id = type_instruction->GetSingleWordInOperand(0);
       auto num_components =
           fuzzerutil::GetArraySize(*type_instruction, GetIRContext());
@@ -566,8 +568,8 @@
               FindOrCreateZeroConstant(component_type_id, is_irrelevant)),
           scalar_or_composite_type_id, is_irrelevant);
     }
-    case SpvOpTypeMatrix:
-    case SpvOpTypeVector: {
+    case spv::Op::OpTypeMatrix:
+    case spv::Op::OpTypeVector: {
       auto component_type_id = type_instruction->GetSingleWordInOperand(0);
       auto num_components = type_instruction->GetSingleWordInOperand(1);
       return FindOrCreateCompositeConstant(
@@ -576,7 +578,7 @@
               FindOrCreateZeroConstant(component_type_id, is_irrelevant)),
           scalar_or_composite_type_id, is_irrelevant);
     }
-    case SpvOpTypeStruct: {
+    case spv::Op::OpTypeStruct: {
       assert(!fuzzerutil::HasBlockOrBufferBlockDecoration(
                  GetIRContext(), scalar_or_composite_type_id) &&
              "We do not construct constants of struct types decorated with "
@@ -646,7 +648,7 @@
 
     // |maybe_preheader| is a preheader if it branches unconditionally to
     // the header. We also require it not to be a loop header.
-    if (maybe_preheader->terminator()->opcode() == SpvOpBranch &&
+    if (maybe_preheader->terminator()->opcode() == spv::Op::OpBranch &&
         !maybe_preheader->IsLoopHeader()) {
       return maybe_preheader;
     }
@@ -683,8 +685,8 @@
 
   // Find the first non-OpPhi and non-OpVariable instruction.
   auto non_phi_or_var_inst = &*block->begin();
-  while (non_phi_or_var_inst->opcode() == SpvOpPhi ||
-         non_phi_or_var_inst->opcode() == SpvOpVariable) {
+  while (non_phi_or_var_inst->opcode() == spv::Op::OpPhi ||
+         non_phi_or_var_inst->opcode() == spv::Op::OpVariable) {
     non_phi_or_var_inst = non_phi_or_var_inst->NextNode();
   }
 
@@ -706,7 +708,7 @@
   (void)pointer_type;
   assert(pointer_type && pointer_type->AsPointer() &&
          pointer_type->AsPointer()->storage_class() ==
-             SpvStorageClassFunction &&
+             spv::StorageClass::Function &&
          "The pointer_type_id must refer to a defined pointer type with "
          "storage class Function");
   auto function = fuzzerutil::FindFunction(GetIRContext(), function_id);
@@ -715,7 +717,7 @@
   // First we try to find a suitable existing variable.
   // All of the local variable declarations are located in the first block.
   for (auto& instruction : *function->begin()) {
-    if (instruction.opcode() != SpvOpVariable) {
+    if (instruction.opcode() != spv::Op::OpVariable) {
       continue;
     }
     // The existing OpVariable must have type |pointer_type_id|.
@@ -749,15 +751,16 @@
   (void)pointer_type;
   assert(
       pointer_type && pointer_type->AsPointer() &&
-      (pointer_type->AsPointer()->storage_class() == SpvStorageClassPrivate ||
+      (pointer_type->AsPointer()->storage_class() ==
+           spv::StorageClass::Private ||
        pointer_type->AsPointer()->storage_class() ==
-           SpvStorageClassWorkgroup) &&
+           spv::StorageClass::Workgroup) &&
       "The pointer_type_id must refer to a defined pointer type with storage "
       "class Private or Workgroup");
 
   // First we try to find a suitable existing variable.
   for (auto& instruction : GetIRContext()->module()->types_values()) {
-    if (instruction.opcode() != SpvOpVariable) {
+    if (instruction.opcode() != spv::Op::OpVariable) {
       continue;
     }
     // The existing OpVariable must have type |pointer_type_id|.
@@ -781,13 +784,13 @@
   uint32_t result_id = GetFuzzerContext()->GetFreshId();
 
   // A variable with storage class Workgroup shouldn't have an initializer.
-  if (storage_class == SpvStorageClassWorkgroup) {
+  if (storage_class == spv::StorageClass::Workgroup) {
     ApplyTransformation(TransformationAddGlobalVariable(
-        result_id, pointer_type_id, SpvStorageClassWorkgroup, 0,
+        result_id, pointer_type_id, spv::StorageClass::Workgroup, 0,
         pointee_value_is_irrelevant));
   } else {
     ApplyTransformation(TransformationAddGlobalVariable(
-        result_id, pointer_type_id, SpvStorageClassPrivate,
+        result_id, pointer_type_id, spv::StorageClass::Private,
         FindOrCreateZeroConstant(pointee_type_id, pointee_value_is_irrelevant),
         pointee_value_is_irrelevant));
   }
diff --git a/source/fuzz/fuzzer_pass.h b/source/fuzz/fuzzer_pass.h
index 2655b54..5c76be1 100644
--- a/source/fuzz/fuzzer_pass.h
+++ b/source/fuzz/fuzzer_pass.h
@@ -159,14 +159,14 @@
   // already exist) and storage class |storage_class|.  A transformation is
   // applied to add the pointer if it does not already exist.
   uint32_t FindOrCreatePointerType(uint32_t base_type_id,
-                                   SpvStorageClass storage_class);
+                                   spv::StorageClass storage_class);
 
   // Returns the id of an OpTypePointer instruction, with a integer base
   // type of width and signedness specified by |width| and |is_signed|,
   // respectively.  If the pointer type or required integer base type do not
   // exist, transformations are applied to add them.
   uint32_t FindOrCreatePointerToIntegerType(uint32_t width, bool is_signed,
-                                            SpvStorageClass storage_class);
+                                            spv::StorageClass storage_class);
 
   // Returns the id of an OpConstant instruction, with a integer type of
   // width and signedness specified by |width| and |is_signed|, respectively,
@@ -239,7 +239,7 @@
   //   storage class, and the sequence will have multiple elements if there are
   //   repeated pointer declarations for the same basic type and storage class.
   std::pair<std::vector<uint32_t>, std::map<uint32_t, std::vector<uint32_t>>>
-  GetAvailableBasicTypesAndPointers(SpvStorageClass storage_class) const;
+  GetAvailableBasicTypesAndPointers(spv::StorageClass storage_class) const;
 
   // Given a type id, |scalar_or_composite_type_id|, which must correspond to
   // some scalar or composite type, returns the result id of an instruction
diff --git a/source/fuzz/fuzzer_pass_add_access_chains.cpp b/source/fuzz/fuzzer_pass_add_access_chains.cpp
index 39f193d..85ca57d 100644
--- a/source/fuzz/fuzzer_pass_add_access_chains.cpp
+++ b/source/fuzz/fuzzer_pass_add_access_chains.cpp
@@ -34,15 +34,16 @@
              opt::BasicBlock::iterator inst_it,
              const protobufs::InstructionDescriptor& instruction_descriptor)
           -> void {
-        assert(inst_it->opcode() ==
-                   instruction_descriptor.target_instruction_opcode() &&
-               "The opcode of the instruction we might insert before must be "
-               "the same as the opcode in the descriptor for the instruction");
+        assert(
+            inst_it->opcode() ==
+                spv::Op(instruction_descriptor.target_instruction_opcode()) &&
+            "The opcode of the instruction we might insert before must be "
+            "the same as the opcode in the descriptor for the instruction");
 
         // Check whether it is legitimate to insert an access chain
         // instruction before this instruction.
-        if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpAccessChain,
-                                                          inst_it)) {
+        if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(
+                spv::Op::OpAccessChain, inst_it)) {
           return;
         }
 
@@ -64,8 +65,8 @@
                     return false;
                   }
                   switch (instruction->opcode()) {
-                    case SpvOpConstantNull:
-                    case SpvOpUndef:
+                    case spv::Op::OpConstantNull:
+                    case spv::Op::OpUndef:
                       // Do not allow making an access chain from a null or
                       // undefined pointer.  (We can eliminate these cases
                       // before actually checking that the instruction is a
@@ -78,7 +79,7 @@
                   // make an access chain from it.
                   return context->get_def_use_mgr()
                              ->GetDef(instruction->type_id())
-                             ->opcode() == SpvOpTypePointer;
+                             ->opcode() == spv::Op::OpTypePointer;
                 });
 
         // At this point, |relevant_instructions| contains all the pointers
@@ -112,14 +113,14 @@
           }
           uint32_t bound;
           switch (subobject_type->opcode()) {
-            case SpvOpTypeArray:
+            case spv::Op::OpTypeArray:
               bound = fuzzerutil::GetArraySize(*subobject_type, GetIRContext());
               break;
-            case SpvOpTypeMatrix:
-            case SpvOpTypeVector:
+            case spv::Op::OpTypeMatrix:
+            case spv::Op::OpTypeVector:
               bound = subobject_type->GetSingleWordInOperand(1);
               break;
-            case SpvOpTypeStruct:
+            case spv::Op::OpTypeStruct:
               bound = fuzzerutil::GetNumberOfStructMembers(*subobject_type);
               break;
             default:
@@ -140,9 +141,9 @@
               GetFuzzerContext()->GetRandomIndexForAccessChain(bound);
 
           switch (subobject_type->opcode()) {
-            case SpvOpTypeArray:
-            case SpvOpTypeMatrix:
-            case SpvOpTypeVector: {
+            case spv::Op::OpTypeArray:
+            case spv::Op::OpTypeMatrix:
+            case spv::Op::OpTypeVector: {
               // The index will be clamped
 
               bool is_signed = GetFuzzerContext()->ChooseEven();
@@ -164,7 +165,7 @@
               subobject_type_id = subobject_type->GetSingleWordInOperand(0);
 
             } break;
-            case SpvOpTypeStruct:
+            case spv::Op::OpTypeStruct:
               index_ids.push_back(FindOrCreateIntegerConstant(
                   {index_value}, 32, GetFuzzerContext()->ChooseEven(), false));
               subobject_type_id =
@@ -178,7 +179,7 @@
         // pointer suitable for the access chain's result type exists, so we
         // create one if it does not.
         FindOrCreatePointerType(subobject_type_id,
-                                static_cast<SpvStorageClass>(
+                                static_cast<spv::StorageClass>(
                                     pointer_type->GetSingleWordInOperand(0)));
         // Apply the transformation to add an access chain.
         ApplyTransformation(TransformationAccessChain(
diff --git a/source/fuzz/fuzzer_pass_add_composite_extract.cpp b/source/fuzz/fuzzer_pass_add_composite_extract.cpp
index dbbec0c..c33ae44 100644
--- a/source/fuzz/fuzzer_pass_add_composite_extract.cpp
+++ b/source/fuzz/fuzzer_pass_add_composite_extract.cpp
@@ -53,8 +53,8 @@
           opt::Function* /*unused*/, opt::BasicBlock* /*unused*/,
           opt::BasicBlock::iterator inst_it,
           const protobufs::InstructionDescriptor& instruction_descriptor) {
-        if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCompositeExtract,
-                                                          inst_it)) {
+        if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(
+                spv::Op::OpCompositeExtract, inst_it)) {
           return;
         }
 
@@ -97,15 +97,15 @@
             assert(type_inst && "Composite instruction has invalid type id");
 
             switch (type_inst->opcode()) {
-              case SpvOpTypeArray:
+              case spv::Op::OpTypeArray:
                 number_of_members =
                     fuzzerutil::GetArraySize(*type_inst, GetIRContext());
                 break;
-              case SpvOpTypeVector:
-              case SpvOpTypeMatrix:
+              case spv::Op::OpTypeVector:
+              case spv::Op::OpTypeMatrix:
                 number_of_members = type_inst->GetSingleWordInOperand(1);
                 break;
-              case SpvOpTypeStruct:
+              case spv::Op::OpTypeStruct:
                 number_of_members = type_inst->NumInOperands();
                 break;
               default:
@@ -122,12 +122,12 @@
                     number_of_members));
 
             switch (type_inst->opcode()) {
-              case SpvOpTypeArray:
-              case SpvOpTypeVector:
-              case SpvOpTypeMatrix:
+              case spv::Op::OpTypeArray:
+              case spv::Op::OpTypeVector:
+              case spv::Op::OpTypeMatrix:
                 type_id = type_inst->GetSingleWordInOperand(0);
                 break;
-              case SpvOpTypeStruct:
+              case spv::Op::OpTypeStruct:
                 type_id = type_inst->GetSingleWordInOperand(indices.back());
                 break;
               default:
diff --git a/source/fuzz/fuzzer_pass_add_composite_inserts.cpp b/source/fuzz/fuzzer_pass_add_composite_inserts.cpp
index 2ac12de..048cdfd 100644
--- a/source/fuzz/fuzzer_pass_add_composite_inserts.cpp
+++ b/source/fuzz/fuzzer_pass_add_composite_inserts.cpp
@@ -36,10 +36,11 @@
              opt::BasicBlock::iterator instruction_iterator,
              const protobufs::InstructionDescriptor& instruction_descriptor)
           -> void {
-        assert(instruction_iterator->opcode() ==
-                   instruction_descriptor.target_instruction_opcode() &&
-               "The opcode of the instruction we might insert before must be "
-               "the same as the opcode in the descriptor for the instruction");
+        assert(
+            instruction_iterator->opcode() ==
+                spv::Op(instruction_descriptor.target_instruction_opcode()) &&
+            "The opcode of the instruction we might insert before must be "
+            "the same as the opcode in the descriptor for the instruction");
 
         // Randomly decide whether to try adding an OpCompositeInsert
         // instruction.
@@ -51,7 +52,7 @@
         // It must be possible to insert an OpCompositeInsert instruction
         // before |instruction_iterator|.
         if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(
-                SpvOpCompositeInsert, instruction_iterator)) {
+                spv::Op::OpCompositeInsert, instruction_iterator)) {
           return;
         }
 
diff --git a/source/fuzz/fuzzer_pass_add_composite_types.cpp b/source/fuzz/fuzzer_pass_add_composite_types.cpp
index af36ad0..bb90991 100644
--- a/source/fuzz/fuzzer_pass_add_composite_types.cpp
+++ b/source/fuzz/fuzzer_pass_add_composite_types.cpp
@@ -114,15 +114,15 @@
   std::vector<uint32_t> candidates;
   for (auto& inst : GetIRContext()->types_values()) {
     switch (inst.opcode()) {
-      case SpvOpTypeArray:
-      case SpvOpTypeBool:
-      case SpvOpTypeFloat:
-      case SpvOpTypeInt:
-      case SpvOpTypeMatrix:
-      case SpvOpTypeVector:
+      case spv::Op::OpTypeArray:
+      case spv::Op::OpTypeBool:
+      case spv::Op::OpTypeFloat:
+      case spv::Op::OpTypeInt:
+      case spv::Op::OpTypeMatrix:
+      case spv::Op::OpTypeVector:
         candidates.push_back(inst.result_id());
         break;
-      case SpvOpTypeStruct: {
+      case spv::Op::OpTypeStruct: {
         if (!fuzzerutil::MembersHaveBuiltInDecoration(GetIRContext(),
                                                       inst.result_id()) &&
             !fuzzerutil::HasBlockOrBufferBlockDecoration(GetIRContext(),
diff --git a/source/fuzz/fuzzer_pass_add_copy_memory.cpp b/source/fuzz/fuzzer_pass_add_copy_memory.cpp
index 6551f49..d54d4ad 100644
--- a/source/fuzz/fuzzer_pass_add_copy_memory.cpp
+++ b/source/fuzz/fuzzer_pass_add_copy_memory.cpp
@@ -36,7 +36,7 @@
              opt::BasicBlock::iterator inst_it,
              const protobufs::InstructionDescriptor& instruction_descriptor) {
         // Check that we can insert an OpCopyMemory before this instruction.
-        if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCopyMemory,
+        if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(spv::Op::OpCopyMemory,
                                                           inst_it)) {
           return;
         }
@@ -61,8 +61,8 @@
 
         // Decide whether to create global or local variable.
         auto storage_class = GetFuzzerContext()->ChooseEven()
-                                 ? SpvStorageClassPrivate
-                                 : SpvStorageClassFunction;
+                                 ? spv::StorageClass::Private
+                                 : spv::StorageClass::Function;
 
         auto pointee_type_id = fuzzerutil::GetPointeeTypeIdFromPointerType(
             GetIRContext(), inst->type_id());
diff --git a/source/fuzz/fuzzer_pass_add_equation_instructions.cpp b/source/fuzz/fuzzer_pass_add_equation_instructions.cpp
index 4bbded8..e720c4e 100644
--- a/source/fuzz/fuzzer_pass_add_equation_instructions.cpp
+++ b/source/fuzz/fuzzer_pass_add_equation_instructions.cpp
@@ -29,12 +29,14 @@
       return true;
     case 64:
       return ir_context->get_feature_mgr()->HasCapability(
-                 SpvCapabilityFloat64) &&
-             ir_context->get_feature_mgr()->HasCapability(SpvCapabilityInt64);
+                 spv::Capability::Float64) &&
+             ir_context->get_feature_mgr()->HasCapability(
+                 spv::Capability::Int64);
     case 16:
       return ir_context->get_feature_mgr()->HasCapability(
-                 SpvCapabilityFloat16) &&
-             ir_context->get_feature_mgr()->HasCapability(SpvCapabilityInt16);
+                 spv::Capability::Float16) &&
+             ir_context->get_feature_mgr()->HasCapability(
+                 spv::Capability::Int16);
     default:
       return false;
   }
@@ -66,7 +68,8 @@
         // as an example opcode for this check, to be representative of *some*
         // opcode that defines an equation, even though we may choose a
         // different opcode below.
-        if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpIAdd, inst_it)) {
+        if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(spv::Op::OpIAdd,
+                                                          inst_it)) {
           return;
         }
 
@@ -78,7 +81,7 @@
                 [this](opt::IRContext* /*unused*/,
                        opt::Instruction* instruction) -> bool {
                   return instruction->result_id() && instruction->type_id() &&
-                         instruction->opcode() != SpvOpUndef &&
+                         instruction->opcode() != spv::Op::OpUndef &&
                          !GetTransformationContext()
                               ->GetFactManager()
                               ->IdIsIrrelevant(instruction->result_id());
@@ -86,15 +89,16 @@
 
         // Try the opcodes for which we know how to make ids at random until
         // something works.
-        std::vector<SpvOp> candidate_opcodes = {
-            SpvOpIAdd,        SpvOpISub,        SpvOpLogicalNot, SpvOpSNegate,
-            SpvOpConvertUToF, SpvOpConvertSToF, SpvOpBitcast};
+        std::vector<spv::Op> candidate_opcodes = {
+            spv::Op::OpIAdd,    spv::Op::OpISub,        spv::Op::OpLogicalNot,
+            spv::Op::OpSNegate, spv::Op::OpConvertUToF, spv::Op::OpConvertSToF,
+            spv::Op::OpBitcast};
         do {
           auto opcode =
               GetFuzzerContext()->RemoveAtRandomIndex(&candidate_opcodes);
           switch (opcode) {
-            case SpvOpConvertSToF:
-            case SpvOpConvertUToF: {
+            case spv::Op::OpConvertSToF:
+            case spv::Op::OpConvertUToF: {
               std::vector<const opt::Instruction*> candidate_instructions;
               for (const auto* inst :
                    GetIntegerInstructions(available_instructions)) {
@@ -144,7 +148,7 @@
                   {operand->result_id()}, instruction_descriptor));
               return;
             }
-            case SpvOpBitcast: {
+            case spv::Op::OpBitcast: {
               const auto candidate_instructions =
                   GetNumericalInstructions(available_instructions);
 
@@ -197,8 +201,8 @@
                 return;
               }
             } break;
-            case SpvOpIAdd:
-            case SpvOpISub: {
+            case spv::Op::OpIAdd:
+            case spv::Op::OpISub: {
               // Instructions of integer (scalar or vector) result type are
               // suitable for these opcodes.
               auto integer_instructions =
@@ -251,7 +255,7 @@
               }
               break;
             }
-            case SpvOpLogicalNot: {
+            case spv::Op::OpLogicalNot: {
               // Choose any available instruction of boolean scalar/vector
               // result type and equate its negation with a fresh id.
               auto boolean_instructions =
@@ -268,7 +272,7 @@
               }
               break;
             }
-            case SpvOpSNegate: {
+            case spv::Op::OpSNegate: {
               // Similar to OpLogicalNot, but for signed integer negation.
               auto integer_instructions =
                   GetIntegerInstructions(available_instructions);
diff --git a/source/fuzz/fuzzer_pass_add_function_calls.cpp b/source/fuzz/fuzzer_pass_add_function_calls.cpp
index 033f4a2..70b8657 100644
--- a/source/fuzz/fuzzer_pass_add_function_calls.cpp
+++ b/source/fuzz/fuzzer_pass_add_function_calls.cpp
@@ -39,8 +39,8 @@
           -> void {
         // Check whether it is legitimate to insert a function call before the
         // instruction.
-        if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpFunctionCall,
-                                                          inst_it)) {
+        if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(
+                spv::Op::OpFunctionCall, inst_it)) {
           return;
         }
 
@@ -112,8 +112,8 @@
   auto available_pointers = FindAvailableInstructions(
       caller_function, caller_block, caller_inst_it,
       [this, caller_block](opt::IRContext* /*unused*/, opt::Instruction* inst) {
-        if (inst->opcode() != SpvOpVariable ||
-            inst->opcode() != SpvOpFunctionParameter) {
+        if (inst->opcode() != spv::Op::OpVariable ||
+            inst->opcode() != spv::Op::OpFunctionParameter) {
           // Function parameters and variables are the only
           // kinds of pointer that can be used as actual
           // parameters.
@@ -172,15 +172,15 @@
     auto storage_class = param_type->AsPointer()->storage_class();
     auto pointee_type_id = fuzzerutil::GetPointeeTypeIdFromPointerType(
         GetIRContext(), param->type_id());
-    if (storage_class == SpvStorageClassFunction) {
+    if (storage_class == spv::StorageClass::Function) {
       // Add a new zero-initialized local variable to the current
       // function, noting that its pointee value is irrelevant.
       ApplyTransformation(TransformationAddLocalVariable(
           fresh_variable_id, param->type_id(), caller_function->result_id(),
           FindOrCreateZeroConstant(pointee_type_id, false), true));
     } else {
-      assert((storage_class == SpvStorageClassPrivate ||
-              storage_class == SpvStorageClassWorkgroup) &&
+      assert((storage_class == spv::StorageClass::Private ||
+              storage_class == spv::StorageClass::Workgroup) &&
              "Only Function, Private and Workgroup storage classes are "
              "supported at present.");
       // Add a new global variable to the module, zero-initializing it if
@@ -188,7 +188,7 @@
       // irrelevant.
       ApplyTransformation(TransformationAddGlobalVariable(
           fresh_variable_id, param->type_id(), storage_class,
-          storage_class == SpvStorageClassPrivate
+          storage_class == spv::StorageClass::Private
               ? FindOrCreateZeroConstant(pointee_type_id, false)
               : 0,
           true));
diff --git a/source/fuzz/fuzzer_pass_add_global_variables.cpp b/source/fuzz/fuzzer_pass_add_global_variables.cpp
index 061f44d..4106856 100644
--- a/source/fuzz/fuzzer_pass_add_global_variables.cpp
+++ b/source/fuzz/fuzzer_pass_add_global_variables.cpp
@@ -29,16 +29,17 @@
                  transformations, ignore_inapplicable_transformations) {}
 
 void FuzzerPassAddGlobalVariables::Apply() {
-  SpvStorageClass variable_storage_class = SpvStorageClassPrivate;
+  spv::StorageClass variable_storage_class = spv::StorageClass::Private;
   for (auto& entry_point : GetIRContext()->module()->entry_points()) {
     // If the execution model of some entry point is GLCompute,
     // then the variable storage class may be Workgroup.
-    if (entry_point.GetSingleWordInOperand(0) == SpvExecutionModelGLCompute) {
+    if (spv::ExecutionModel(entry_point.GetSingleWordInOperand(0)) ==
+        spv::ExecutionModel::GLCompute) {
       variable_storage_class =
           GetFuzzerContext()->ChoosePercentage(
               GetFuzzerContext()->GetChanceOfChoosingWorkgroupStorageClass())
-              ? SpvStorageClassWorkgroup
-              : SpvStorageClassPrivate;
+              ? spv::StorageClass::Workgroup
+              : spv::StorageClass::Private;
       break;
     }
   }
@@ -87,7 +88,7 @@
     ApplyTransformation(TransformationAddGlobalVariable(
         GetFuzzerContext()->GetFreshId(), pointer_type_id,
         variable_storage_class,
-        variable_storage_class == SpvStorageClassPrivate
+        variable_storage_class == spv::StorageClass::Private
             ? FindOrCreateZeroConstant(basic_type, false)
             : 0,
         true));
diff --git a/source/fuzz/fuzzer_pass_add_loads.cpp b/source/fuzz/fuzzer_pass_add_loads.cpp
index ab91543..3660328 100644
--- a/source/fuzz/fuzzer_pass_add_loads.cpp
+++ b/source/fuzz/fuzzer_pass_add_loads.cpp
@@ -34,10 +34,11 @@
              opt::BasicBlock::iterator inst_it,
              const protobufs::InstructionDescriptor& instruction_descriptor)
           -> void {
-        assert(inst_it->opcode() ==
-                   instruction_descriptor.target_instruction_opcode() &&
-               "The opcode of the instruction we might insert before must be "
-               "the same as the opcode in the descriptor for the instruction");
+        assert(
+            inst_it->opcode() ==
+                spv::Op(instruction_descriptor.target_instruction_opcode()) &&
+            "The opcode of the instruction we might insert before must be "
+            "the same as the opcode in the descriptor for the instruction");
 
         // Randomly decide whether to try inserting a load here.
         if (!GetFuzzerContext()->ChoosePercentage(
@@ -47,10 +48,11 @@
 
         // Check whether it is legitimate to insert a load or atomic load before
         // this instruction.
-        if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad, inst_it)) {
+        if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(spv::Op::OpLoad,
+                                                          inst_it)) {
           return;
         }
-        if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpAtomicLoad,
+        if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(spv::Op::OpAtomicLoad,
                                                           inst_it)) {
           return;
         }
@@ -64,8 +66,8 @@
                     return false;
                   }
                   switch (instruction->opcode()) {
-                    case SpvOpConstantNull:
-                    case SpvOpUndef:
+                    case spv::Op::OpConstantNull:
+                    case spv::Op::OpUndef:
                       // Do not allow loading from a null or undefined pointer;
                       // this might be OK if the block is dead, but for now we
                       // conservatively avoid it.
@@ -75,7 +77,7 @@
                   }
                   return context->get_def_use_mgr()
                              ->GetDef(instruction->type_id())
-                             ->opcode() == SpvOpTypePointer;
+                             ->opcode() == spv::Op::OpTypePointer;
                 });
 
         // At this point, |relevant_instructions| contains all the pointers
@@ -92,25 +94,25 @@
         uint32_t memory_scope_id = 0;
         uint32_t memory_semantics_id = 0;
 
-        auto storage_class = static_cast<SpvStorageClass>(
+        auto storage_class = static_cast<spv::StorageClass>(
             GetIRContext()
                 ->get_def_use_mgr()
                 ->GetDef(chosen_instruction->type_id())
                 ->GetSingleWordInOperand(0));
 
         switch (storage_class) {
-          case SpvStorageClassStorageBuffer:
-          case SpvStorageClassPhysicalStorageBuffer:
-          case SpvStorageClassWorkgroup:
-          case SpvStorageClassCrossWorkgroup:
-          case SpvStorageClassAtomicCounter:
-          case SpvStorageClassImage:
+          case spv::StorageClass::StorageBuffer:
+          case spv::StorageClass::PhysicalStorageBuffer:
+          case spv::StorageClass::Workgroup:
+          case spv::StorageClass::CrossWorkgroup:
+          case spv::StorageClass::AtomicCounter:
+          case spv::StorageClass::Image:
             if (GetFuzzerContext()->ChoosePercentage(
                     GetFuzzerContext()->GetChanceOfAddingAtomicLoad())) {
               is_atomic_load = true;
 
               memory_scope_id = FindOrCreateConstant(
-                  {SpvScopeInvocation},
+                  {uint32_t(spv::Scope::Invocation)},
                   FindOrCreateIntegerType(32, GetFuzzerContext()->ChooseEven()),
                   false);
 
diff --git a/source/fuzz/fuzzer_pass_add_local_variables.cpp b/source/fuzz/fuzzer_pass_add_local_variables.cpp
index a4e739f..f467f46 100644
--- a/source/fuzz/fuzzer_pass_add_local_variables.cpp
+++ b/source/fuzz/fuzzer_pass_add_local_variables.cpp
@@ -31,7 +31,7 @@
 
 void FuzzerPassAddLocalVariables::Apply() {
   auto basic_type_ids_and_pointers =
-      GetAvailableBasicTypesAndPointers(SpvStorageClassFunction);
+      GetAvailableBasicTypesAndPointers(spv::StorageClass::Function);
 
   // These are the basic types that are available to this fuzzer pass.
   auto& basic_types = basic_type_ids_and_pointers.first;
@@ -64,7 +64,7 @@
         // use it.
         pointer_type = GetFuzzerContext()->GetFreshId();
         ApplyTransformation(TransformationAddTypePointer(
-            pointer_type, SpvStorageClassFunction, basic_type));
+            pointer_type, spv::StorageClass::Function, basic_type));
         available_pointers_to_basic_type.push_back(pointer_type);
       } else {
         // There is - grab one.
diff --git a/source/fuzz/fuzzer_pass_add_opphi_synonyms.cpp b/source/fuzz/fuzzer_pass_add_opphi_synonyms.cpp
index 73b6b0a..d0b1275 100644
--- a/source/fuzz/fuzzer_pass_add_opphi_synonyms.cpp
+++ b/source/fuzz/fuzzer_pass_add_opphi_synonyms.cpp
@@ -176,8 +176,8 @@
     // - OpFunction does not yield a value;
     // - OpUndef yields an undefined value at each use, so it should never be a
     //   synonym of another id.
-    if (pair.second->opcode() == SpvOpFunction ||
-        pair.second->opcode() == SpvOpUndef) {
+    if (pair.second->opcode() == spv::Op::OpFunction ||
+        pair.second->opcode() == spv::Op::OpUndef) {
       continue;
     }
 
diff --git a/source/fuzz/fuzzer_pass_add_parameters.cpp b/source/fuzz/fuzzer_pass_add_parameters.cpp
index 1cb6a79..e663d89 100644
--- a/source/fuzz/fuzzer_pass_add_parameters.cpp
+++ b/source/fuzz/fuzzer_pass_add_parameters.cpp
@@ -79,7 +79,7 @@
         auto storage_class = fuzzerutil::GetStorageClassFromPointerType(
             GetIRContext(), current_type_id);
         switch (storage_class) {
-          case SpvStorageClassFunction: {
+          case spv::StorageClass::Function: {
             // In every caller find or create a local variable that has the
             // selected type.
             for (auto* instr :
@@ -91,8 +91,8 @@
               call_parameter_ids[instr->result_id()] = variable_id;
             }
           } break;
-          case SpvStorageClassPrivate:
-          case SpvStorageClassWorkgroup: {
+          case spv::StorageClass::Private:
+          case spv::StorageClass::Workgroup: {
             // If there exists at least one caller, find or create a global
             // variable that has the selected type.
             std::vector<opt::Instruction*> callers =
diff --git a/source/fuzz/fuzzer_pass_add_stores.cpp b/source/fuzz/fuzzer_pass_add_stores.cpp
index 606e4a6..0de02a5 100644
--- a/source/fuzz/fuzzer_pass_add_stores.cpp
+++ b/source/fuzz/fuzzer_pass_add_stores.cpp
@@ -34,10 +34,11 @@
              opt::BasicBlock::iterator inst_it,
              const protobufs::InstructionDescriptor& instruction_descriptor)
           -> void {
-        assert(inst_it->opcode() ==
-                   instruction_descriptor.target_instruction_opcode() &&
-               "The opcode of the instruction we might insert before must be "
-               "the same as the opcode in the descriptor for the instruction");
+        assert(
+            inst_it->opcode() ==
+                spv::Op(instruction_descriptor.target_instruction_opcode()) &&
+            "The opcode of the instruction we might insert before must be "
+            "the same as the opcode in the descriptor for the instruction");
 
         // Randomly decide whether to try inserting a store here.
         if (!GetFuzzerContext()->ChoosePercentage(
@@ -47,12 +48,12 @@
 
         // Check whether it is legitimate to insert a store before this
         // instruction.
-        if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpStore,
+        if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(spv::Op::OpStore,
                                                           inst_it)) {
           return;
         }
-        if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpAtomicStore,
-                                                          inst_it)) {
+        if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(
+                spv::Op::OpAtomicStore, inst_it)) {
           return;
         }
 
@@ -67,7 +68,7 @@
                   }
                   auto type_inst = context->get_def_use_mgr()->GetDef(
                       instruction->type_id());
-                  if (type_inst->opcode() != SpvOpTypePointer) {
+                  if (type_inst->opcode() != spv::Op::OpTypePointer) {
                     // Not a pointer.
                     return false;
                   }
@@ -76,8 +77,8 @@
                     return false;
                   }
                   switch (instruction->opcode()) {
-                    case SpvOpConstantNull:
-                    case SpvOpUndef:
+                    case spv::Op::OpConstantNull:
+                    case spv::Op::OpUndef:
                       // Do not allow storing to a null or undefined pointer;
                       // this might be OK if the block is dead, but for now we
                       // conservatively avoid it.
@@ -126,24 +127,24 @@
         uint32_t memory_semantics_id = 0;
 
         auto storage_class =
-            static_cast<SpvStorageClass>(GetIRContext()
-                                             ->get_def_use_mgr()
-                                             ->GetDef(pointer->type_id())
-                                             ->GetSingleWordInOperand(0));
+            static_cast<spv::StorageClass>(GetIRContext()
+                                               ->get_def_use_mgr()
+                                               ->GetDef(pointer->type_id())
+                                               ->GetSingleWordInOperand(0));
 
         switch (storage_class) {
-          case SpvStorageClassStorageBuffer:
-          case SpvStorageClassPhysicalStorageBuffer:
-          case SpvStorageClassWorkgroup:
-          case SpvStorageClassCrossWorkgroup:
-          case SpvStorageClassAtomicCounter:
-          case SpvStorageClassImage:
+          case spv::StorageClass::StorageBuffer:
+          case spv::StorageClass::PhysicalStorageBuffer:
+          case spv::StorageClass::Workgroup:
+          case spv::StorageClass::CrossWorkgroup:
+          case spv::StorageClass::AtomicCounter:
+          case spv::StorageClass::Image:
             if (GetFuzzerContext()->ChoosePercentage(
                     GetFuzzerContext()->GetChanceOfAddingAtomicStore())) {
               is_atomic_store = true;
 
               memory_scope_id = FindOrCreateConstant(
-                  {SpvScopeInvocation},
+                  {uint32_t(spv::Scope::Invocation)},
                   FindOrCreateIntegerType(32, GetFuzzerContext()->ChooseEven()),
                   false);
 
diff --git a/source/fuzz/fuzzer_pass_add_synonyms.cpp b/source/fuzz/fuzzer_pass_add_synonyms.cpp
index 1d188de..5782732 100644
--- a/source/fuzz/fuzzer_pass_add_synonyms.cpp
+++ b/source/fuzz/fuzzer_pass_add_synonyms.cpp
@@ -44,7 +44,8 @@
         // Skip |inst_it| if we can't insert anything above it. OpIAdd is just
         // a representative of some instruction that might be produced by the
         // transformation.
-        if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpIAdd, inst_it)) {
+        if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(spv::Op::OpIAdd,
+                                                          inst_it)) {
           return;
         }
 
diff --git a/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp b/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp
index a29d1d3..4cddf55 100644
--- a/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp
+++ b/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp
@@ -35,10 +35,11 @@
              opt::BasicBlock::iterator instruction_iterator,
              const protobufs::InstructionDescriptor& instruction_descriptor)
           -> void {
-        assert(instruction_iterator->opcode() ==
-                   instruction_descriptor.target_instruction_opcode() &&
-               "The opcode of the instruction we might insert before must be "
-               "the same as the opcode in the descriptor for the instruction");
+        assert(
+            instruction_iterator->opcode() ==
+                spv::Op(instruction_descriptor.target_instruction_opcode()) &&
+            "The opcode of the instruction we might insert before must be "
+            "the same as the opcode in the descriptor for the instruction");
 
         // Randomly decide whether to try adding an OpVectorShuffle instruction.
         if (!GetFuzzerContext()->ChoosePercentage(
@@ -49,7 +50,7 @@
         // It must be valid to insert an OpVectorShuffle instruction
         // before |instruction_iterator|.
         if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(
-                SpvOpVectorShuffle, instruction_iterator)) {
+                spv::Op::OpVectorShuffle, instruction_iterator)) {
           return;
         }
 
diff --git a/source/fuzz/fuzzer_pass_adjust_branch_weights.cpp b/source/fuzz/fuzzer_pass_adjust_branch_weights.cpp
index 94428f7..6bf46e1 100644
--- a/source/fuzz/fuzzer_pass_adjust_branch_weights.cpp
+++ b/source/fuzz/fuzzer_pass_adjust_branch_weights.cpp
@@ -33,7 +33,7 @@
   // For all OpBranchConditional instructions,
   // randomly applies the transformation.
   GetIRContext()->module()->ForEachInst([this](opt::Instruction* instruction) {
-    if (instruction->opcode() == SpvOpBranchConditional &&
+    if (instruction->opcode() == spv::Op::OpBranchConditional &&
         GetFuzzerContext()->ChoosePercentage(
             GetFuzzerContext()->GetChanceOfAdjustingBranchWeights())) {
       ApplyTransformation(TransformationAdjustBranchWeights(
diff --git a/source/fuzz/fuzzer_pass_adjust_function_controls.cpp b/source/fuzz/fuzzer_pass_adjust_function_controls.cpp
index 1c2bc8c..363edc7 100644
--- a/source/fuzz/fuzzer_pass_adjust_function_controls.cpp
+++ b/source/fuzz/fuzzer_pass_adjust_function_controls.cpp
@@ -40,21 +40,21 @@
       // For the new mask, we first randomly select one of three basic masks:
       // None, Inline or DontInline.  These are always valid (and are mutually
       // exclusive).
-      std::vector<uint32_t> basic_function_control_masks = {
-          SpvFunctionControlMaskNone, SpvFunctionControlInlineMask,
-          SpvFunctionControlDontInlineMask};
+      std::vector<spv::FunctionControlMask> basic_function_control_masks = {
+          spv::FunctionControlMask::MaskNone, spv::FunctionControlMask::Inline,
+          spv::FunctionControlMask::DontInline};
       uint32_t new_function_control_mask =
-          basic_function_control_masks[GetFuzzerContext()->RandomIndex(
-              basic_function_control_masks)];
+          uint32_t(basic_function_control_masks[GetFuzzerContext()->RandomIndex(
+              basic_function_control_masks)]);
 
       // We now consider the Pure and Const mask bits.  If these are already
       // set on the function then it's OK to keep them, but also interesting
       // to consider dropping them, so we decide randomly in each case.
       for (auto mask_bit :
-           {SpvFunctionControlPureMask, SpvFunctionControlConstMask}) {
-        if ((existing_function_control_mask & mask_bit) &&
+           {spv::FunctionControlMask::Pure, spv::FunctionControlMask::Const}) {
+        if ((existing_function_control_mask & uint32_t(mask_bit)) &&
             GetFuzzerContext()->ChooseEven()) {
-          new_function_control_mask |= mask_bit;
+          new_function_control_mask |= uint32_t(mask_bit);
         }
       }
 
diff --git a/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp b/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp
index fe855ca..53dbe54 100644
--- a/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp
+++ b/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp
@@ -34,7 +34,7 @@
     for (auto& block : function) {
       if (auto merge_inst = block.GetMergeInst()) {
         // Ignore the instruction if it is not a loop merge.
-        if (merge_inst->opcode() != SpvOpLoopMerge) {
+        if (merge_inst->opcode() != spv::Op::OpLoopMerge) {
           continue;
         }
 
@@ -48,9 +48,10 @@
             TransformationSetLoopControl::kLoopControlMaskInOperandIndex);
 
         // First, set the new mask to one of None, Unroll or DontUnroll.
-        std::vector<uint32_t> basic_masks = {SpvLoopControlMaskNone,
-                                             SpvLoopControlUnrollMask,
-                                             SpvLoopControlDontUnrollMask};
+        std::vector<uint32_t> basic_masks = {
+            uint32_t(spv::LoopControlMask::MaskNone),
+            uint32_t(spv::LoopControlMask::Unroll),
+            uint32_t(spv::LoopControlMask::DontUnroll)};
         uint32_t new_mask =
             basic_masks[GetFuzzerContext()->RandomIndex(basic_masks)];
 
@@ -58,19 +59,20 @@
         // does, check which of these were present in the existing mask and
         // randomly decide whether to keep them.  They are just hints, so
         // removing them should not change the semantics of the module.
-        for (auto mask_bit :
-             {SpvLoopControlDependencyInfiniteMask,
-              SpvLoopControlDependencyLengthMask,
-              SpvLoopControlMinIterationsMask, SpvLoopControlMaxIterationsMask,
-              SpvLoopControlIterationMultipleMask}) {
-          if ((existing_mask & mask_bit) && GetFuzzerContext()->ChooseEven()) {
+        for (auto mask_bit : {spv::LoopControlMask::DependencyInfinite,
+                              spv::LoopControlMask::DependencyLength,
+                              spv::LoopControlMask::MinIterations,
+                              spv::LoopControlMask::MaxIterations,
+                              spv::LoopControlMask::IterationMultiple}) {
+          if ((existing_mask & uint32_t(mask_bit)) &&
+              GetFuzzerContext()->ChooseEven()) {
             // The mask bits we are considering are not available in all SPIR-V
             // versions.  However, we only include a mask bit if it was present
             // in the original loop control mask, and we work under the
             // assumption that we are transforming a valid module, thus we don't
             // need to actually check whether the SPIR-V version being used
             // supports these loop control mask bits.
-            new_mask |= mask_bit;
+            new_mask |= uint32_t(mask_bit);
           }
         }
 
@@ -81,14 +83,14 @@
 
         // PeelCount and PartialCount are not compatible with DontUnroll, so
         // we check whether DontUnroll is set.
-        if (!(new_mask & SpvLoopControlDontUnrollMask)) {
+        if (!(new_mask & uint32_t(spv::LoopControlMask::DontUnroll))) {
           // If PeelCount is supported by this SPIR-V version, randomly choose
           // whether to set it.  If it was set in the original mask and is not
           // selected for setting here, that amounts to dropping it.
           if (TransformationSetLoopControl::PeelCountIsSupported(
                   GetIRContext()) &&
               GetFuzzerContext()->ChooseEven()) {
-            new_mask |= SpvLoopControlPeelCountMask;
+            new_mask |= uint32_t(spv::LoopControlMask::PeelCount);
             // The peel count is chosen randomly - if PeelCount was already set
             // this will overwrite whatever peel count was previously used.
             peel_count = GetFuzzerContext()->GetRandomLoopControlPeelCount();
@@ -97,7 +99,7 @@
           if (TransformationSetLoopControl::PartialCountIsSupported(
                   GetIRContext()) &&
               GetFuzzerContext()->ChooseEven()) {
-            new_mask |= SpvLoopControlPartialCountMask;
+            new_mask |= uint32_t(spv::LoopControlMask::PartialCount);
             partial_count =
                 GetFuzzerContext()->GetRandomLoopControlPartialCount();
           }
diff --git a/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp b/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp
index d2ff40e..efae7d6 100644
--- a/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp
+++ b/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp
@@ -47,8 +47,8 @@
         // From SPIR-V 1.4 onwards, OpCopyMemory and OpCopyMemorySized have a
         // second mask.
         switch (inst_it->opcode()) {
-          case SpvOpCopyMemory:
-          case SpvOpCopyMemorySized:
+          case spv::Op::OpCopyMemory:
+          case spv::Op::OpCopyMemorySized:
             if (TransformationSetMemoryOperandsMask::
                     MultipleMemoryOperandMasksAreSupported(GetIRContext())) {
               indices_of_available_masks_to_adjust.push_back(1);
@@ -75,24 +75,26 @@
               existing_mask_in_operand_index < inst_it->NumInOperands()
                   ? inst_it->GetSingleWordInOperand(
                         existing_mask_in_operand_index)
-                  : static_cast<uint32_t>(SpvMemoryAccessMaskNone);
+                  : static_cast<uint32_t>(spv::MemoryAccessMask::MaskNone);
 
           // There are two things we can do to a mask:
           // - add Volatile if not already present
           // - toggle Nontemporal
           // The following ensures that we do at least one of these
-          bool add_volatile = !(existing_mask & SpvMemoryAccessVolatileMask) &&
-                              GetFuzzerContext()->ChooseEven();
+          bool add_volatile =
+              !(existing_mask & uint32_t(spv::MemoryAccessMask::Volatile)) &&
+              GetFuzzerContext()->ChooseEven();
           bool toggle_nontemporal =
               !add_volatile || GetFuzzerContext()->ChooseEven();
 
           // These bitwise operations use '|' to add Volatile if desired, and
           // '^' to toggle Nontemporal if desired.
           uint32_t new_mask =
-              (existing_mask | (add_volatile ? SpvMemoryAccessVolatileMask
-                                             : SpvMemoryAccessMaskNone)) ^
-              (toggle_nontemporal ? SpvMemoryAccessNontemporalMask
-                                  : SpvMemoryAccessMaskNone);
+              (existing_mask |
+               (add_volatile ? uint32_t(spv::MemoryAccessMask::Volatile)
+                             : uint32_t(spv::MemoryAccessMask::MaskNone))) ^
+              (toggle_nontemporal ? uint32_t(spv::MemoryAccessMask::Nontemporal)
+                                  : uint32_t(spv::MemoryAccessMask::MaskNone));
 
           TransformationSetMemoryOperandsMask transformation(
               MakeInstructionDescriptor(block, inst_it), new_mask, mask_index);
diff --git a/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp b/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp
index 7d8e6b5..fe0cf7a 100644
--- a/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp
+++ b/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp
@@ -34,7 +34,7 @@
     for (auto& block : function) {
       if (auto merge_inst = block.GetMergeInst()) {
         // Ignore the instruction if it is not a selection merge.
-        if (merge_inst->opcode() != SpvOpSelectionMerge) {
+        if (merge_inst->opcode() != spv::Op::OpSelectionMerge) {
           continue;
         }
 
@@ -48,13 +48,14 @@
         // The choices to change the selection control to are the set of valid
         // controls, minus the current control.
         std::vector<uint32_t> choices;
-        for (auto control :
-             {SpvSelectionControlMaskNone, SpvSelectionControlFlattenMask,
-              SpvSelectionControlDontFlattenMask}) {
-          if (control == merge_inst->GetSingleWordOperand(1)) {
+        for (auto control : {spv::SelectionControlMask::MaskNone,
+                             spv::SelectionControlMask::Flatten,
+                             spv::SelectionControlMask::DontFlatten}) {
+          if (control ==
+              spv::SelectionControlMask(merge_inst->GetSingleWordOperand(1))) {
             continue;
           }
-          choices.push_back(control);
+          choices.push_back(uint32_t(control));
         }
 
         // Apply the transformation and add it to the output transformation
diff --git a/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp b/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp
index 5c3b86b..0367a26 100644
--- a/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp
+++ b/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp
@@ -107,9 +107,9 @@
         // which case we need to be able to add an extract instruction to get
         // that element out.
         if (synonym_to_try->index_size() > 0 &&
-            !fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCompositeExtract,
-                                                          use_inst) &&
-            use_inst->opcode() != SpvOpPhi) {
+            !fuzzerutil::CanInsertOpcodeBeforeInstruction(
+                spv::Op::OpCompositeExtract, use_inst) &&
+            use_inst->opcode() != spv::Op::OpPhi) {
           // We cannot insert an extract before this instruction, so this
           // synonym is no good.
           continue;
@@ -132,7 +132,7 @@
           id_with_which_to_replace_use = GetFuzzerContext()->GetFreshId();
           opt::Instruction* instruction_to_insert_before = nullptr;
 
-          if (use_inst->opcode() != SpvOpPhi) {
+          if (use_inst->opcode() != spv::Op::OpPhi) {
             instruction_to_insert_before = use_inst;
           } else {
             auto parent_block_id =
@@ -182,7 +182,7 @@
 }
 
 bool FuzzerPassApplyIdSynonyms::DataDescriptorsHaveCompatibleTypes(
-    SpvOp opcode, uint32_t use_in_operand_index,
+    spv::Op opcode, uint32_t use_in_operand_index,
     const protobufs::DataDescriptor& dd1,
     const protobufs::DataDescriptor& dd2) {
   auto base_object_type_id_1 =
diff --git a/source/fuzz/fuzzer_pass_apply_id_synonyms.h b/source/fuzz/fuzzer_pass_apply_id_synonyms.h
index 3da9c5d..d1a0e1a 100644
--- a/source/fuzz/fuzzer_pass_apply_id_synonyms.h
+++ b/source/fuzz/fuzzer_pass_apply_id_synonyms.h
@@ -38,7 +38,7 @@
   // with respect to the type. Concretely, returns true if |dd1| and |dd2| have
   // the same type or both |dd1| and |dd2| are either a numerical or a vector
   // type of integral components with possibly different signedness.
-  bool DataDescriptorsHaveCompatibleTypes(SpvOp opcode,
+  bool DataDescriptorsHaveCompatibleTypes(spv::Op opcode,
                                           uint32_t use_in_operand_index,
                                           const protobufs::DataDescriptor& dd1,
                                           const protobufs::DataDescriptor& dd2);
diff --git a/source/fuzz/fuzzer_pass_construct_composites.cpp b/source/fuzz/fuzzer_pass_construct_composites.cpp
index ff022fc..0ad630c 100644
--- a/source/fuzz/fuzzer_pass_construct_composites.cpp
+++ b/source/fuzz/fuzzer_pass_construct_composites.cpp
@@ -81,7 +81,7 @@
         // Check whether it is legitimate to insert a composite construction
         // before the instruction.
         if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(
-                SpvOpCompositeConstruct, inst_it)) {
+                spv::Op::OpCompositeConstruct, inst_it)) {
           return;
         }
 
@@ -121,19 +121,19 @@
         auto composite_type_inst =
             GetIRContext()->get_def_use_mgr()->GetDef(chosen_composite_type);
         switch (composite_type_inst->opcode()) {
-          case SpvOpTypeArray:
+          case spv::Op::OpTypeArray:
             constructor_arguments = FindComponentsToConstructArray(
                 *composite_type_inst, type_id_to_available_instructions);
             break;
-          case SpvOpTypeMatrix:
+          case spv::Op::OpTypeMatrix:
             constructor_arguments = FindComponentsToConstructMatrix(
                 *composite_type_inst, type_id_to_available_instructions);
             break;
-          case SpvOpTypeStruct:
+          case spv::Op::OpTypeStruct:
             constructor_arguments = FindComponentsToConstructStruct(
                 *composite_type_inst, type_id_to_available_instructions);
             break;
-          case SpvOpTypeVector:
+          case spv::Op::OpTypeVector:
             constructor_arguments = FindComponentsToConstructVector(
                 *composite_type_inst, type_id_to_available_instructions);
             break;
@@ -156,7 +156,7 @@
 FuzzerPassConstructComposites::FindComponentsToConstructArray(
     const opt::Instruction& array_type_instruction,
     const TypeIdToInstructions& type_id_to_available_instructions) {
-  assert(array_type_instruction.opcode() == SpvOpTypeArray &&
+  assert(array_type_instruction.opcode() == spv::Op::OpTypeArray &&
          "Precondition: instruction must be an array type.");
 
   // Get the element type for the array.
@@ -191,7 +191,7 @@
 FuzzerPassConstructComposites::FindComponentsToConstructMatrix(
     const opt::Instruction& matrix_type_instruction,
     const TypeIdToInstructions& type_id_to_available_instructions) {
-  assert(matrix_type_instruction.opcode() == SpvOpTypeMatrix &&
+  assert(matrix_type_instruction.opcode() == spv::Op::OpTypeMatrix &&
          "Precondition: instruction must be a matrix type.");
 
   // Get the element type for the matrix.
@@ -221,7 +221,7 @@
 FuzzerPassConstructComposites::FindComponentsToConstructStruct(
     const opt::Instruction& struct_type_instruction,
     const TypeIdToInstructions& type_id_to_available_instructions) {
-  assert(struct_type_instruction.opcode() == SpvOpTypeStruct &&
+  assert(struct_type_instruction.opcode() == spv::Op::OpTypeStruct &&
          "Precondition: instruction must be a struct type.");
   std::vector<uint32_t> result;
   // Consider the type of each field of the struct.
@@ -251,7 +251,7 @@
 FuzzerPassConstructComposites::FindComponentsToConstructVector(
     const opt::Instruction& vector_type_instruction,
     const TypeIdToInstructions& type_id_to_available_instructions) {
-  assert(vector_type_instruction.opcode() == SpvOpTypeVector &&
+  assert(vector_type_instruction.opcode() == spv::Op::OpTypeVector &&
          "Precondition: instruction must be a vector type.");
 
   // Get details of the type underlying the vector, and the width of the vector,
diff --git a/source/fuzz/fuzzer_pass_copy_objects.cpp b/source/fuzz/fuzzer_pass_copy_objects.cpp
index 80cc2a5..725c33e 100644
--- a/source/fuzz/fuzzer_pass_copy_objects.cpp
+++ b/source/fuzz/fuzzer_pass_copy_objects.cpp
@@ -35,10 +35,11 @@
              opt::BasicBlock::iterator inst_it,
              const protobufs::InstructionDescriptor& instruction_descriptor)
           -> void {
-        assert(inst_it->opcode() ==
-                   instruction_descriptor.target_instruction_opcode() &&
-               "The opcode of the instruction we might insert before must be "
-               "the same as the opcode in the descriptor for the instruction");
+        assert(
+            inst_it->opcode() ==
+                spv::Op(instruction_descriptor.target_instruction_opcode()) &&
+            "The opcode of the instruction we might insert before must be "
+            "the same as the opcode in the descriptor for the instruction");
 
         if (GetTransformationContext()->GetFactManager()->BlockIsDead(
                 block->id())) {
@@ -48,7 +49,7 @@
 
         // Check whether it is legitimate to insert a copy before this
         // instruction.
-        if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCopyObject,
+        if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(spv::Op::OpCopyObject,
                                                           inst_it)) {
           return;
         }
diff --git a/source/fuzz/fuzzer_pass_donate_modules.cpp b/source/fuzz/fuzzer_pass_donate_modules.cpp
index 29ede58..1696c74 100644
--- a/source/fuzz/fuzzer_pass_donate_modules.cpp
+++ b/source/fuzz/fuzzer_pass_donate_modules.cpp
@@ -88,7 +88,7 @@
   // module.
   for (const auto& capability_inst : donor_ir_context->capabilities()) {
     auto capability =
-        static_cast<SpvCapability>(capability_inst.GetSingleWordInOperand(0));
+        static_cast<spv::Capability>(capability_inst.GetSingleWordInOperand(0));
     if (!GetIRContext()->get_feature_mgr()->HasCapability(capability)) {
       return;
     }
@@ -122,27 +122,27 @@
   //  kinds of decoration.
 }
 
-SpvStorageClass FuzzerPassDonateModules::AdaptStorageClass(
-    SpvStorageClass donor_storage_class) {
+spv::StorageClass FuzzerPassDonateModules::AdaptStorageClass(
+    spv::StorageClass donor_storage_class) {
   switch (donor_storage_class) {
-    case SpvStorageClassFunction:
-    case SpvStorageClassPrivate:
-    case SpvStorageClassWorkgroup:
+    case spv::StorageClass::Function:
+    case spv::StorageClass::Private:
+    case spv::StorageClass::Workgroup:
       // We leave these alone
       return donor_storage_class;
-    case SpvStorageClassInput:
-    case SpvStorageClassOutput:
-    case SpvStorageClassUniform:
-    case SpvStorageClassUniformConstant:
-    case SpvStorageClassPushConstant:
-    case SpvStorageClassImage:
-    case SpvStorageClassStorageBuffer:
+    case spv::StorageClass::Input:
+    case spv::StorageClass::Output:
+    case spv::StorageClass::Uniform:
+    case spv::StorageClass::UniformConstant:
+    case spv::StorageClass::PushConstant:
+    case spv::StorageClass::Image:
+    case spv::StorageClass::StorageBuffer:
       // We change these to Private
-      return SpvStorageClassPrivate;
+      return spv::StorageClass::Private;
     default:
       // Handle other cases on demand.
       assert(false && "Currently unsupported storage class.");
-      return SpvStorageClassMax;
+      return spv::StorageClass::Max;
   }
 }
 
@@ -200,14 +200,14 @@
   // that its component types will have been considered previously, and that
   // |original_id_to_donated_id| will already contain an entry for them.
   switch (type_or_value.opcode()) {
-    case SpvOpTypeImage:
-    case SpvOpTypeSampledImage:
-    case SpvOpTypeSampler:
+    case spv::Op::OpTypeImage:
+    case spv::Op::OpTypeSampledImage:
+    case spv::Op::OpTypeSampler:
       // We do not donate types and variables that relate to images and
       // samplers, so we skip these types and subsequently skip anything that
       // depends on them.
       return;
-    case SpvOpTypeVoid: {
+    case spv::Op::OpTypeVoid: {
       // Void has to exist already in order for us to have an entry point.
       // Get the existing id of void.
       opt::analysis::Void void_type;
@@ -216,7 +216,7 @@
              "The module being transformed will always have 'void' type "
              "declared.");
     } break;
-    case SpvOpTypeBool: {
+    case spv::Op::OpTypeBool: {
       // Bool cannot be declared multiple times, so use its existing id if
       // present, or add a declaration of Bool with a fresh id if not.
       opt::analysis::Bool bool_type;
@@ -228,7 +228,7 @@
         ApplyTransformation(TransformationAddTypeBoolean(new_result_id));
       }
     } break;
-    case SpvOpTypeInt: {
+    case spv::Op::OpTypeInt: {
       // Int cannot be declared multiple times with the same width and
       // signedness, so check whether an existing identical Int type is
       // present and use its id if so.  Otherwise add a declaration of the
@@ -246,8 +246,8 @@
             TransformationAddTypeInt(new_result_id, width, is_signed));
       }
     } break;
-    case SpvOpTypeFloat: {
-      // Similar to SpvOpTypeInt.
+    case spv::Op::OpTypeFloat: {
+      // Similar to spv::Op::OpTypeInt.
       const uint32_t width = type_or_value.GetSingleWordInOperand(0);
       opt::analysis::Float float_type(width);
       auto float_type_id = GetIRContext()->get_type_mgr()->GetId(&float_type);
@@ -258,7 +258,7 @@
         ApplyTransformation(TransformationAddTypeFloat(new_result_id, width));
       }
     } break;
-    case SpvOpTypeVector: {
+    case spv::Op::OpTypeVector: {
       // It is not legal to have two Vector type declarations with identical
       // element types and element counts, so check whether an existing
       // identical Vector type is present and use its id if so.  Otherwise add
@@ -282,8 +282,8 @@
             new_result_id, component_type_id, component_count));
       }
     } break;
-    case SpvOpTypeMatrix: {
-      // Similar to SpvOpTypeVector.
+    case spv::Op::OpTypeMatrix: {
+      // Similar to spv::Op::OpTypeVector.
       uint32_t column_type_id = original_id_to_donated_id->at(
           type_or_value.GetSingleWordInOperand(0));
       auto column_type =
@@ -302,7 +302,7 @@
       }
 
     } break;
-    case SpvOpTypeArray: {
+    case spv::Op::OpTypeArray: {
       // It is OK to have multiple structurally identical array types, so
       // we go ahead and add a remapped version of the type declared by the
       // donor.
@@ -318,7 +318,7 @@
           original_id_to_donated_id->at(
               type_or_value.GetSingleWordInOperand(1))));
     } break;
-    case SpvOpTypeRuntimeArray: {
+    case spv::Op::OpTypeRuntimeArray: {
       // A runtime array is allowed as the final member of an SSBO.  During
       // donation we turn runtime arrays into fixed-size arrays.  For dead
       // code donations this is OK because the array is never indexed into at
@@ -341,8 +341,8 @@
               {GetFuzzerContext()->GetRandomSizeForNewArray()}, 32, false,
               false)));
     } break;
-    case SpvOpTypeStruct: {
-      // Similar to SpvOpTypeArray.
+    case spv::Op::OpTypeStruct: {
+      // Similar to spv::Op::OpTypeArray.
       std::vector<uint32_t> member_type_ids;
       for (uint32_t i = 0; i < type_or_value.NumInOperands(); i++) {
         auto component_type_id = type_or_value.GetSingleWordInOperand(i);
@@ -358,8 +358,8 @@
       ApplyTransformation(
           TransformationAddTypeStruct(new_result_id, member_type_ids));
     } break;
-    case SpvOpTypePointer: {
-      // Similar to SpvOpTypeArray.
+    case spv::Op::OpTypePointer: {
+      // Similar to spv::Op::OpTypeArray.
       uint32_t pointee_type_id = type_or_value.GetSingleWordInOperand(1);
       if (!original_id_to_donated_id->count(pointee_type_id)) {
         // We did not donate the pointee type for this pointer type, so we
@@ -369,11 +369,11 @@
       new_result_id = GetFuzzerContext()->GetFreshId();
       ApplyTransformation(TransformationAddTypePointer(
           new_result_id,
-          AdaptStorageClass(static_cast<SpvStorageClass>(
+          AdaptStorageClass(static_cast<spv::StorageClass>(
               type_or_value.GetSingleWordInOperand(0))),
           original_id_to_donated_id->at(pointee_type_id)));
     } break;
-    case SpvOpTypeFunction: {
+    case spv::Op::OpTypeFunction: {
       // It is not OK to have multiple function types that use identical ids
       // for their return and parameter types.  We thus go through all
       // existing function types to look for a match.  We do not use the
@@ -425,10 +425,11 @@
             argument_type_ids));
       }
     } break;
-    case SpvOpSpecConstantOp: {
+    case spv::Op::OpSpecConstantOp: {
       new_result_id = GetFuzzerContext()->GetFreshId();
       auto type_id = original_id_to_donated_id->at(type_or_value.type_id());
-      auto opcode = static_cast<SpvOp>(type_or_value.GetSingleWordInOperand(0));
+      auto opcode =
+          static_cast<spv::Op>(type_or_value.GetSingleWordInOperand(0));
 
       // Make sure we take into account |original_id_to_donated_id| when
       // computing operands for OpSpecConstantOp.
@@ -447,20 +448,20 @@
       ApplyTransformation(TransformationAddSpecConstantOp(
           new_result_id, type_id, opcode, std::move(operands)));
     } break;
-    case SpvOpSpecConstantTrue:
-    case SpvOpSpecConstantFalse:
-    case SpvOpConstantTrue:
-    case SpvOpConstantFalse: {
+    case spv::Op::OpSpecConstantTrue:
+    case spv::Op::OpSpecConstantFalse:
+    case spv::Op::OpConstantTrue:
+    case spv::Op::OpConstantFalse: {
       // It is OK to have duplicate definitions of True and False, so add
       // these to the module, using a remapped Bool type.
       new_result_id = GetFuzzerContext()->GetFreshId();
-      auto value = type_or_value.opcode() == SpvOpConstantTrue ||
-                   type_or_value.opcode() == SpvOpSpecConstantTrue;
+      auto value = type_or_value.opcode() == spv::Op::OpConstantTrue ||
+                   type_or_value.opcode() == spv::Op::OpSpecConstantTrue;
       ApplyTransformation(
           TransformationAddConstantBoolean(new_result_id, value, false));
     } break;
-    case SpvOpSpecConstant:
-    case SpvOpConstant: {
+    case spv::Op::OpSpecConstant:
+    case spv::Op::OpConstant: {
       // It is OK to have duplicate constant definitions, so add this to the
       // module using a remapped result type.
       new_result_id = GetFuzzerContext()->GetFreshId();
@@ -472,8 +473,8 @@
           new_result_id, original_id_to_donated_id->at(type_or_value.type_id()),
           data_words, false));
     } break;
-    case SpvOpSpecConstantComposite:
-    case SpvOpConstantComposite: {
+    case spv::Op::OpSpecConstantComposite:
+    case spv::Op::OpConstantComposite: {
       assert(original_id_to_donated_id->count(type_or_value.type_id()) &&
              "Composite types for which it is possible to create a constant "
              "should have been donated.");
@@ -495,7 +496,7 @@
           new_result_id, original_id_to_donated_id->at(type_or_value.type_id()),
           constituent_ids, false));
     } break;
-    case SpvOpConstantNull: {
+    case spv::Op::OpConstantNull: {
       if (!original_id_to_donated_id->count(type_or_value.type_id())) {
         // We did not donate the type associated with this null constant, so
         // we cannot donate the null constant.
@@ -509,7 +510,7 @@
           new_result_id,
           original_id_to_donated_id->at(type_or_value.type_id())));
     } break;
-    case SpvOpVariable: {
+    case spv::Op::OpVariable: {
       if (!original_id_to_donated_id->count(type_or_value.type_id())) {
         // We did not donate the pointer type associated with this variable,
         // so we cannot donate the variable.
@@ -536,11 +537,11 @@
       uint32_t remapped_pointer_type =
           original_id_to_donated_id->at(type_or_value.type_id());
       uint32_t initializer_id;
-      SpvStorageClass storage_class =
-          static_cast<SpvStorageClass>(type_or_value.GetSingleWordInOperand(
-              0)) == SpvStorageClassWorkgroup
-              ? SpvStorageClassWorkgroup
-              : SpvStorageClassPrivate;
+      spv::StorageClass storage_class =
+          static_cast<spv::StorageClass>(type_or_value.GetSingleWordInOperand(
+              0)) == spv::StorageClass::Workgroup
+              ? spv::StorageClass::Workgroup
+              : spv::StorageClass::Private;
       if (type_or_value.NumInOperands() == 1) {
         // The variable did not have an initializer.  Initialize it to zero
         // if it has Private storage class (to limit problems associated with
@@ -551,7 +552,7 @@
         //  could initialize Workgroup variables at the start of an entry
         //  point, and should do so if their uninitialized nature proves
         //  problematic.
-        initializer_id = storage_class == SpvStorageClassWorkgroup
+        initializer_id = storage_class == spv::StorageClass::Workgroup
                              ? 0
                              : FindOrCreateZeroConstant(
                                    fuzzerutil::GetPointeeTypeIdFromPointerType(
@@ -566,7 +567,7 @@
           TransformationAddGlobalVariable(new_result_id, remapped_pointer_type,
                                           storage_class, initializer_id, true));
     } break;
-    case SpvOpUndef: {
+    case spv::Op::OpUndef: {
       if (!original_id_to_donated_id->count(type_or_value.type_id())) {
         // We did not donate the type associated with this undef, so we cannot
         // donate the undef.
@@ -638,7 +639,7 @@
         [this, &donated_instructions, donor_ir_context,
          &original_id_to_donated_id,
          &skipped_instructions](const opt::Instruction* instruction) {
-          if (instruction->opcode() == SpvOpArrayLength) {
+          if (instruction->opcode() == spv::Op::OpArrayLength) {
             // We treat OpArrayLength specially.
             HandleOpArrayLength(*instruction, original_id_to_donated_id,
                                 &donated_instructions);
@@ -682,70 +683,70 @@
   // Now consider instructions we specifically want to skip because we do not
   // yet support them.
   switch (instruction.opcode()) {
-    case SpvOpAtomicLoad:
-    case SpvOpAtomicStore:
-    case SpvOpAtomicExchange:
-    case SpvOpAtomicCompareExchange:
-    case SpvOpAtomicCompareExchangeWeak:
-    case SpvOpAtomicIIncrement:
-    case SpvOpAtomicIDecrement:
-    case SpvOpAtomicIAdd:
-    case SpvOpAtomicISub:
-    case SpvOpAtomicSMin:
-    case SpvOpAtomicUMin:
-    case SpvOpAtomicSMax:
-    case SpvOpAtomicUMax:
-    case SpvOpAtomicAnd:
-    case SpvOpAtomicOr:
-    case SpvOpAtomicXor:
+    case spv::Op::OpAtomicLoad:
+    case spv::Op::OpAtomicStore:
+    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:
       // We conservatively ignore all atomic instructions at present.
       // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3276): Consider
       //  being less conservative here.
-    case SpvOpImageSampleImplicitLod:
-    case SpvOpImageSampleExplicitLod:
-    case SpvOpImageSampleDrefImplicitLod:
-    case SpvOpImageSampleDrefExplicitLod:
-    case SpvOpImageSampleProjImplicitLod:
-    case SpvOpImageSampleProjExplicitLod:
-    case SpvOpImageSampleProjDrefImplicitLod:
-    case SpvOpImageSampleProjDrefExplicitLod:
-    case SpvOpImageFetch:
-    case SpvOpImageGather:
-    case SpvOpImageDrefGather:
-    case SpvOpImageRead:
-    case SpvOpImageWrite:
-    case SpvOpImageSparseSampleImplicitLod:
-    case SpvOpImageSparseSampleExplicitLod:
-    case SpvOpImageSparseSampleDrefImplicitLod:
-    case SpvOpImageSparseSampleDrefExplicitLod:
-    case SpvOpImageSparseSampleProjImplicitLod:
-    case SpvOpImageSparseSampleProjExplicitLod:
-    case SpvOpImageSparseSampleProjDrefImplicitLod:
-    case SpvOpImageSparseSampleProjDrefExplicitLod:
-    case SpvOpImageSparseFetch:
-    case SpvOpImageSparseGather:
-    case SpvOpImageSparseDrefGather:
-    case SpvOpImageSparseRead:
-    case SpvOpImageSampleFootprintNV:
-    case SpvOpImage:
-    case SpvOpImageQueryFormat:
-    case SpvOpImageQueryLevels:
-    case SpvOpImageQueryLod:
-    case SpvOpImageQueryOrder:
-    case SpvOpImageQuerySamples:
-    case SpvOpImageQuerySize:
-    case SpvOpImageQuerySizeLod:
-    case SpvOpSampledImage:
+    case spv::Op::OpImageSampleImplicitLod:
+    case spv::Op::OpImageSampleExplicitLod:
+    case spv::Op::OpImageSampleDrefImplicitLod:
+    case spv::Op::OpImageSampleDrefExplicitLod:
+    case spv::Op::OpImageSampleProjImplicitLod:
+    case spv::Op::OpImageSampleProjExplicitLod:
+    case spv::Op::OpImageSampleProjDrefImplicitLod:
+    case spv::Op::OpImageSampleProjDrefExplicitLod:
+    case spv::Op::OpImageFetch:
+    case spv::Op::OpImageGather:
+    case spv::Op::OpImageDrefGather:
+    case spv::Op::OpImageRead:
+    case spv::Op::OpImageWrite:
+    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::OpImageSparseRead:
+    case spv::Op::OpImageSampleFootprintNV:
+    case spv::Op::OpImage:
+    case spv::Op::OpImageQueryFormat:
+    case spv::Op::OpImageQueryLevels:
+    case spv::Op::OpImageQueryLod:
+    case spv::Op::OpImageQueryOrder:
+    case spv::Op::OpImageQuerySamples:
+    case spv::Op::OpImageQuerySize:
+    case spv::Op::OpImageQuerySizeLod:
+    case spv::Op::OpSampledImage:
       // We ignore all instructions related to accessing images, since we do not
       // donate images.
       return false;
-    case SpvOpLoad:
+    case spv::Op::OpLoad:
       switch (donor_ir_context->get_def_use_mgr()
                   ->GetDef(instruction.type_id())
                   ->opcode()) {
-        case SpvOpTypeImage:
-        case SpvOpTypeSampledImage:
-        case SpvOpTypeSampler:
+        case spv::Op::OpTypeImage:
+        case spv::Op::OpTypeSampledImage:
+        case spv::Op::OpTypeSampler:
           // Again, we ignore instructions that relate to accessing images.
           return false;
         default:
@@ -783,13 +784,13 @@
 bool FuzzerPassDonateModules::IsBasicType(
     const opt::Instruction& instruction) const {
   switch (instruction.opcode()) {
-    case SpvOpTypeArray:
-    case SpvOpTypeBool:
-    case SpvOpTypeFloat:
-    case SpvOpTypeInt:
-    case SpvOpTypeMatrix:
-    case SpvOpTypeStruct:
-    case SpvOpTypeVector:
+    case spv::Op::OpTypeArray:
+    case spv::Op::OpTypeBool:
+    case spv::Op::OpTypeFloat:
+    case spv::Op::OpTypeInt:
+    case spv::Op::OpTypeMatrix:
+    case spv::Op::OpTypeStruct:
+    case spv::Op::OpTypeVector:
       return true;
     default:
       return false;
@@ -800,7 +801,7 @@
     const opt::Instruction& instruction,
     std::map<uint32_t, uint32_t>* original_id_to_donated_id,
     std::vector<protobufs::Instruction>* donated_instructions) const {
-  assert(instruction.opcode() == SpvOpArrayLength &&
+  assert(instruction.opcode() == spv::Op::OpArrayLength &&
          "Precondition: instruction must be OpArrayLength.");
   uint32_t donated_variable_id =
       original_id_to_donated_id->at(instruction.GetSingleWordInOperand(0));
@@ -809,12 +810,12 @@
   auto pointer_to_struct_instruction =
       GetIRContext()->get_def_use_mgr()->GetDef(
           donated_variable_instruction->type_id());
-  assert(pointer_to_struct_instruction->opcode() == SpvOpTypePointer &&
+  assert(pointer_to_struct_instruction->opcode() == spv::Op::OpTypePointer &&
          "Type of variable must be pointer.");
   auto donated_struct_type_instruction =
       GetIRContext()->get_def_use_mgr()->GetDef(
           pointer_to_struct_instruction->GetSingleWordInOperand(1));
-  assert(donated_struct_type_instruction->opcode() == SpvOpTypeStruct &&
+  assert(donated_struct_type_instruction->opcode() == spv::Op::OpTypeStruct &&
          "Pointee type of pointer used by OpArrayLength must be struct.");
   assert(donated_struct_type_instruction->NumInOperands() ==
              instruction.GetSingleWordInOperand(1) + 1 &&
@@ -825,7 +826,7 @@
           donated_struct_type_instruction->NumInOperands() - 1);
   auto fixed_size_array_type_instruction =
       GetIRContext()->get_def_use_mgr()->GetDef(fixed_size_array_type_id);
-  assert(fixed_size_array_type_instruction->opcode() == SpvOpTypeArray &&
+  assert(fixed_size_array_type_instruction->opcode() == spv::Op::OpTypeArray &&
          "The donated array type must be fixed-size.");
   auto array_size_id =
       fixed_size_array_type_instruction->GetSingleWordInOperand(1);
@@ -837,7 +838,8 @@
   }
 
   donated_instructions->push_back(MakeInstructionMessage(
-      SpvOpCopyObject, original_id_to_donated_id->at(instruction.type_id()),
+      spv::Op::OpCopyObject,
+      original_id_to_donated_id->at(instruction.type_id()),
       original_id_to_donated_id->at(instruction.result_id()),
       opt::Instruction::OperandList({{SPV_OPERAND_TYPE_ID, {array_size_id}}})));
 }
@@ -892,7 +894,7 @@
   // more interesting value later.
   auto zero_constant = FindOrCreateZeroConstant(remapped_type_id, true);
   donated_instructions->push_back(MakeInstructionMessage(
-      SpvOpCopyObject, remapped_type_id,
+      spv::Op::OpCopyObject, remapped_type_id,
       original_id_to_donated_id->at(instruction.result_id()),
       opt::Instruction::OperandList({{SPV_OPERAND_TYPE_ID, {zero_constant}}})));
 }
@@ -926,8 +928,8 @@
         (void)(donor_ir_context);
         assert((donor_ir_context->get_def_use_mgr()
                         ->GetDef(operand_id)
-                        ->opcode() == SpvOpLabel ||
-                instruction.opcode() == SpvOpPhi) &&
+                        ->opcode() == spv::Op::OpLabel ||
+                instruction.opcode() == spv::Op::OpPhi) &&
                "Unsupported forward reference.");
         original_id_to_donated_id->insert(
             {operand_id, GetFuzzerContext()->GetFreshId()});
@@ -942,7 +944,7 @@
     input_operands.push_back({in_operand.type, operand_data});
   }
 
-  if (instruction.opcode() == SpvOpVariable &&
+  if (instruction.opcode() == spv::Op::OpVariable &&
       instruction.NumInOperands() == 1) {
     // This is an uninitialized local variable.  Initialize it to zero.
     input_operands.push_back(
@@ -1017,7 +1019,7 @@
 
   // Adjust OpPhi instructions in the |merge_block|.
   for (const auto& inst : *merge_block) {
-    if (inst.opcode() != SpvOpPhi) {
+    if (inst.opcode() != spv::Op::OpPhi) {
       break;
     }
 
@@ -1070,7 +1072,8 @@
   // live-safe.  Add them if not already present.
   FindOrCreateBoolType();  // Needed for comparisons
   FindOrCreatePointerToIntegerType(
-      32, false, SpvStorageClassFunction);  // Needed for adding loop limiters
+      32, false,
+      spv::StorageClass::Function);  // Needed for adding loop limiters
   FindOrCreateIntegerConstant({0}, 32, false,
                               false);  // Needed for initializing loop limiters
   FindOrCreateIntegerConstant({1}, 32, false,
@@ -1107,8 +1110,8 @@
   for (auto& block : function_to_donate) {
     for (auto& inst : block) {
       switch (inst.opcode()) {
-        case SpvOpAccessChain:
-        case SpvOpInBoundsAccessChain: {
+        case spv::Op::OpAccessChain:
+        case spv::Op::OpInBoundsAccessChain: {
           protobufs::AccessChainClampingInfo clamping_info;
           clamping_info.set_access_chain_id(
               original_id_to_donated_id.at(inst.result_id()));
@@ -1118,7 +1121,8 @@
           assert(base_object && "The base object must exist.");
           auto pointer_type = donor_ir_context->get_def_use_mgr()->GetDef(
               base_object->type_id());
-          assert(pointer_type && pointer_type->opcode() == SpvOpTypePointer &&
+          assert(pointer_type &&
+                 pointer_type->opcode() == spv::Op::OpTypePointer &&
                  "The base object must have pointer type.");
 
           auto should_be_composite_type =
@@ -1138,7 +1142,8 @@
 
             // Get the bound for the component being indexed into.
             uint32_t bound;
-            if (should_be_composite_type->opcode() == SpvOpTypeRuntimeArray) {
+            if (should_be_composite_type->opcode() ==
+                spv::Op::OpTypeRuntimeArray) {
               // The donor is indexing into a runtime array.  We do not
               // donate runtime arrays.  Instead, we donate a corresponding
               // fixed-size array for every runtime array.  We should thus
@@ -1148,7 +1153,7 @@
                   GetIRContext()->get_def_use_mgr()->GetDef(
                       original_id_to_donated_id.at(
                           should_be_composite_type->result_id()));
-              assert(fixed_size_array_type->opcode() == SpvOpTypeArray &&
+              assert(fixed_size_array_type->opcode() == spv::Op::OpTypeArray &&
                      "A runtime array type in the donor should have been "
                      "replaced by a fixed-sized array in the recipient.");
               // The size of this fixed-size array is a suitable bound.
@@ -1163,12 +1168,12 @@
                 donor_ir_context->get_def_use_mgr()->GetDef(index_id);
             auto index_type_inst = donor_ir_context->get_def_use_mgr()->GetDef(
                 index_inst->type_id());
-            assert(index_type_inst->opcode() == SpvOpTypeInt);
+            assert(index_type_inst->opcode() == spv::Op::OpTypeInt);
             opt::analysis::Integer* index_int_type =
                 donor_ir_context->get_type_mgr()
                     ->GetType(index_type_inst->result_id())
                     ->AsInteger();
-            if (index_inst->opcode() != SpvOpConstant) {
+            if (index_inst->opcode() != spv::Op::OpConstant) {
               // We will have to clamp this index, so we need a constant
               // whose value is one less than the bound, to compare
               // against and to use as the clamped value.
@@ -1194,7 +1199,7 @@
   uint32_t kill_unreachable_return_value_id = 0;
   auto function_return_type_inst =
       donor_ir_context->get_def_use_mgr()->GetDef(function_to_donate.type_id());
-  if (function_return_type_inst->opcode() != SpvOpTypeVoid &&
+  if (function_return_type_inst->opcode() != spv::Op::OpTypeVoid &&
       fuzzerutil::FunctionContainsOpKillOrUnreachable(function_to_donate)) {
     kill_unreachable_return_value_id = FindOrCreateZeroConstant(
         original_id_to_donated_id.at(function_return_type_inst->result_id()),
diff --git a/source/fuzz/fuzzer_pass_donate_modules.h b/source/fuzz/fuzzer_pass_donate_modules.h
index 924dd35..004f158 100644
--- a/source/fuzz/fuzzer_pass_donate_modules.h
+++ b/source/fuzz/fuzzer_pass_donate_modules.h
@@ -45,7 +45,8 @@
  private:
   // Adapts a storage class coming from a donor module so that it will work
   // in a recipient module, e.g. by changing Uniform to Private.
-  static SpvStorageClass AdaptStorageClass(SpvStorageClass donor_storage_class);
+  static spv::StorageClass AdaptStorageClass(
+      spv::StorageClass donor_storage_class);
 
   // Identifies all external instruction set imports in |donor_ir_context| and
   // populates |original_id_to_donated_id| with a mapping from the donor's id
diff --git a/source/fuzz/fuzzer_pass_expand_vector_reductions.cpp b/source/fuzz/fuzzer_pass_expand_vector_reductions.cpp
index 5bf0461..fecd82e 100644
--- a/source/fuzz/fuzzer_pass_expand_vector_reductions.cpp
+++ b/source/fuzz/fuzzer_pass_expand_vector_reductions.cpp
@@ -40,8 +40,8 @@
         }
 
         // |instruction| must be OpAny or OpAll.
-        if (instruction.opcode() != SpvOpAny &&
-            instruction.opcode() != SpvOpAll) {
+        if (instruction.opcode() != spv::Op::OpAny &&
+            instruction.opcode() != spv::Op::OpAll) {
           continue;
         }
 
diff --git a/source/fuzz/fuzzer_pass_flatten_conditional_branches.cpp b/source/fuzz/fuzzer_pass_flatten_conditional_branches.cpp
index 70fa6a1..86ffff4 100644
--- a/source/fuzz/fuzzer_pass_flatten_conditional_branches.cpp
+++ b/source/fuzz/fuzzer_pass_flatten_conditional_branches.cpp
@@ -48,8 +48,8 @@
       // Only consider this block if it is the header of a conditional, with a
       // non-irrelevant condition.
       if (block.GetMergeInst() &&
-          block.GetMergeInst()->opcode() == SpvOpSelectionMerge &&
-          block.terminator()->opcode() == SpvOpBranchConditional &&
+          block.GetMergeInst()->opcode() == spv::Op::OpSelectionMerge &&
+          block.terminator()->opcode() == spv::Op::OpBranchConditional &&
           !GetTransformationContext()->GetFactManager()->IdIsIrrelevant(
               block.terminator()->GetSingleWordInOperand(0))) {
         selection_headers.emplace_back(&block);
@@ -94,11 +94,11 @@
                                    ->get_def_use_mgr()
                                    ->GetDef(phi_instruction->type_id())
                                    ->opcode()) {
-                         case SpvOpTypeBool:
-                         case SpvOpTypeInt:
-                         case SpvOpTypeFloat:
-                         case SpvOpTypePointer:
-                         case SpvOpTypeVector:
+                         case spv::Op::OpTypeBool:
+                         case spv::Op::OpTypeInt:
+                         case spv::Op::OpTypeFloat:
+                         case spv::Op::OpTypePointer:
+                         case spv::Op::OpTypeVector:
                            return true;
                          default:
                            return false;
@@ -143,7 +143,7 @@
                 GetIRContext()->get_def_use_mgr()->GetDef(
                     phi_instruction->type_id());
             switch (type_instruction->opcode()) {
-              case SpvOpTypeVector: {
+              case spv::Op::OpTypeVector: {
                 uint32_t dimension =
                     type_instruction->GetSingleWordInOperand(1);
                 switch (dimension) {
diff --git a/source/fuzz/fuzzer_pass_inline_functions.cpp b/source/fuzz/fuzzer_pass_inline_functions.cpp
index 4024096..6839bbe 100644
--- a/source/fuzz/fuzzer_pass_inline_functions.cpp
+++ b/source/fuzz/fuzzer_pass_inline_functions.cpp
@@ -64,7 +64,7 @@
     auto* function_call_block =
         GetIRContext()->get_instr_block(function_call_instruction);
     if ((function_call_instruction != &*--function_call_block->tail() ||
-         function_call_block->terminator()->opcode() != SpvOpBranch) &&
+         function_call_block->terminator()->opcode() != spv::Op::OpBranch) &&
         !MaybeApplyTransformation(TransformationSplitBlock(
             MakeInstructionDescriptor(GetIRContext(),
                                       function_call_instruction->NextNode()),
diff --git a/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.cpp b/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.cpp
index b755d23..ec5fc4b 100644
--- a/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.cpp
+++ b/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.cpp
@@ -47,18 +47,20 @@
         }
 
         // Make sure |instruction| has only one indexing operand.
-        assert(instruction.NumInOperands() ==
-                   (instruction.opcode() == SpvOpCompositeExtract ? 2 : 3) &&
-               "FuzzerPassMakeVectorOperationsDynamic: the composite "
-               "instruction must have "
-               "only one indexing operand.");
+        assert(
+            instruction.NumInOperands() ==
+                (instruction.opcode() == spv::Op::OpCompositeExtract ? 2 : 3) &&
+            "FuzzerPassMakeVectorOperationsDynamic: the composite "
+            "instruction must have "
+            "only one indexing operand.");
 
         // Applies the make vector operation dynamic transformation.
         ApplyTransformation(TransformationMakeVectorOperationDynamic(
             instruction.result_id(),
             FindOrCreateIntegerConstant(
                 {instruction.GetSingleWordInOperand(
-                    instruction.opcode() == SpvOpCompositeExtract ? 1 : 2)},
+                    instruction.opcode() == spv::Op::OpCompositeExtract ? 1
+                                                                        : 2)},
                 32, GetFuzzerContext()->ChooseEven(), false)));
       }
     }
diff --git a/source/fuzz/fuzzer_pass_merge_function_returns.cpp b/source/fuzz/fuzzer_pass_merge_function_returns.cpp
index 220f707..48c1861 100644
--- a/source/fuzz/fuzzer_pass_merge_function_returns.cpp
+++ b/source/fuzz/fuzzer_pass_merge_function_returns.cpp
@@ -64,11 +64,11 @@
         [this, function](
             opt::BasicBlock* /*unused*/, opt::BasicBlock::iterator inst_it,
             const protobufs::InstructionDescriptor& instruction_descriptor) {
-          const SpvOp opcode = inst_it->opcode();
+          const spv::Op opcode = inst_it->opcode();
           switch (opcode) {
-            case SpvOpKill:
-            case SpvOpUnreachable:
-            case SpvOpTerminateInvocation: {
+            case spv::Op::OpKill:
+            case spv::Op::OpUnreachable:
+            case spv::Op::OpTerminateInvocation: {
               // This is an early termination instruction - we need to wrap it
               // so that it becomes a return.
               if (TransformationWrapEarlyTerminatorInFunction::
@@ -85,7 +85,7 @@
                   GetIRContext()->get_def_use_mgr()->GetDef(
                       function->type_id());
               uint32_t returned_value_id;
-              if (function_return_type->opcode() == SpvOpTypeVoid) {
+              if (function_return_type->opcode() == spv::Op::OpTypeVoid) {
                 // No value is needed.
                 returned_value_id = 0;
               } else if (fuzzerutil::CanCreateConstant(
@@ -130,7 +130,7 @@
 
     // If the entry block does not branch unconditionally to another block,
     // split it.
-    if (function->entry()->terminator()->opcode() != SpvOpBranch) {
+    if (function->entry()->terminator()->opcode() != spv::Op::OpBranch) {
       SplitBlockAfterOpPhiOrOpVariable(function->entry()->id());
     }
 
@@ -149,9 +149,9 @@
       if (GetIRContext()
               ->get_instr_block(merge_block)
               ->WhileEachInst([](opt::Instruction* inst) {
-                return inst->opcode() == SpvOpLabel ||
-                       inst->opcode() == SpvOpPhi ||
-                       inst->opcode() == SpvOpBranch;
+                return inst->opcode() == spv::Op::OpLabel ||
+                       inst->opcode() == spv::Op::OpPhi ||
+                       inst->opcode() == spv::Op::OpBranch;
               })) {
         actual_merge_blocks.emplace_back(merge_block);
         continue;
@@ -324,7 +324,8 @@
 
 bool FuzzerPassMergeFunctionReturns::IsEarlyTerminatorWrapper(
     const opt::Function& function) const {
-  for (SpvOp opcode : {SpvOpKill, SpvOpUnreachable, SpvOpTerminateInvocation}) {
+  for (spv::Op opcode : {spv::Op::OpKill, spv::Op::OpUnreachable,
+                         spv::Op::OpTerminateInvocation}) {
     if (TransformationWrapEarlyTerminatorInFunction::MaybeGetWrapperFunction(
             GetIRContext(), opcode) == &function) {
       return true;
diff --git a/source/fuzz/fuzzer_pass_mutate_pointers.cpp b/source/fuzz/fuzzer_pass_mutate_pointers.cpp
index bbe0540..a7e9fdc 100644
--- a/source/fuzz/fuzzer_pass_mutate_pointers.cpp
+++ b/source/fuzz/fuzzer_pass_mutate_pointers.cpp
@@ -39,7 +39,8 @@
           return;
         }
 
-        if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad, inst_it)) {
+        if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(spv::Op::OpLoad,
+                                                          inst_it)) {
           return;
         }
 
diff --git a/source/fuzz/fuzzer_pass_obfuscate_constants.cpp b/source/fuzz/fuzzer_pass_obfuscate_constants.cpp
index f60c1b4..48ac589 100644
--- a/source/fuzz/fuzzer_pass_obfuscate_constants.cpp
+++ b/source/fuzz/fuzzer_pass_obfuscate_constants.cpp
@@ -37,21 +37,21 @@
 
 void FuzzerPassObfuscateConstants::ObfuscateBoolConstantViaConstantPair(
     uint32_t depth, const protobufs::IdUseDescriptor& bool_constant_use,
-    const std::vector<SpvOp>& greater_than_opcodes,
-    const std::vector<SpvOp>& less_than_opcodes, uint32_t constant_id_1,
+    const std::vector<spv::Op>& greater_than_opcodes,
+    const std::vector<spv::Op>& less_than_opcodes, uint32_t constant_id_1,
     uint32_t constant_id_2, bool first_constant_is_larger) {
   auto bool_constant_opcode = GetIRContext()
                                   ->get_def_use_mgr()
                                   ->GetDef(bool_constant_use.id_of_interest())
                                   ->opcode();
-  assert((bool_constant_opcode == SpvOpConstantFalse ||
-          bool_constant_opcode == SpvOpConstantTrue) &&
+  assert((bool_constant_opcode == spv::Op::OpConstantFalse ||
+          bool_constant_opcode == spv::Op::OpConstantTrue) &&
          "Precondition: this must be a usage of a boolean constant.");
 
   // Pick an opcode at random.  First randomly decide whether to generate
   // a 'greater than' or 'less than' kind of opcode, and then select a
   // random opcode from the resulting subset.
-  SpvOp comparison_opcode;
+  spv::Op comparison_opcode;
   if (GetFuzzerContext()->ChooseEven()) {
     comparison_opcode = greater_than_opcodes[GetFuzzerContext()->RandomIndex(
         greater_than_opcodes)];
@@ -68,9 +68,9 @@
                 comparison_opcode) != greater_than_opcodes.end();
   uint32_t lhs_id;
   uint32_t rhs_id;
-  if ((bool_constant_opcode == SpvOpConstantTrue &&
+  if ((bool_constant_opcode == spv::Op::OpConstantTrue &&
        first_constant_is_larger == is_greater_than_opcode) ||
-      (bool_constant_opcode == SpvOpConstantFalse &&
+      (bool_constant_opcode == spv::Op::OpConstantFalse &&
        first_constant_is_larger != is_greater_than_opcode)) {
     lhs_id = constant_id_1;
     rhs_id = constant_id_2;
@@ -147,12 +147,12 @@
     first_constant_is_larger =
         float_constant_1->GetDouble() > float_constant_2->GetDouble();
   }
-  std::vector<SpvOp> greater_than_opcodes{
-      SpvOpFOrdGreaterThan, SpvOpFOrdGreaterThanEqual, SpvOpFUnordGreaterThan,
-      SpvOpFUnordGreaterThanEqual};
-  std::vector<SpvOp> less_than_opcodes{
-      SpvOpFOrdGreaterThan, SpvOpFOrdGreaterThanEqual, SpvOpFUnordGreaterThan,
-      SpvOpFUnordGreaterThanEqual};
+  std::vector<spv::Op> greater_than_opcodes{
+      spv::Op::OpFOrdGreaterThan, spv::Op::OpFOrdGreaterThanEqual,
+      spv::Op::OpFUnordGreaterThan, spv::Op::OpFUnordGreaterThanEqual};
+  std::vector<spv::Op> less_than_opcodes{
+      spv::Op::OpFOrdGreaterThan, spv::Op::OpFOrdGreaterThanEqual,
+      spv::Op::OpFUnordGreaterThan, spv::Op::OpFUnordGreaterThanEqual};
 
   ObfuscateBoolConstantViaConstantPair(
       depth, bool_constant_use, greater_than_opcodes, less_than_opcodes,
@@ -190,9 +190,10 @@
     first_constant_is_larger =
         signed_int_constant_1->GetS64() > signed_int_constant_2->GetS64();
   }
-  std::vector<SpvOp> greater_than_opcodes{SpvOpSGreaterThan,
-                                          SpvOpSGreaterThanEqual};
-  std::vector<SpvOp> less_than_opcodes{SpvOpSLessThan, SpvOpSLessThanEqual};
+  std::vector<spv::Op> greater_than_opcodes{spv::Op::OpSGreaterThan,
+                                            spv::Op::OpSGreaterThanEqual};
+  std::vector<spv::Op> less_than_opcodes{spv::Op::OpSLessThan,
+                                         spv::Op::OpSLessThanEqual};
 
   ObfuscateBoolConstantViaConstantPair(
       depth, bool_constant_use, greater_than_opcodes, less_than_opcodes,
@@ -232,9 +233,10 @@
     first_constant_is_larger =
         unsigned_int_constant_1->GetU64() > unsigned_int_constant_2->GetU64();
   }
-  std::vector<SpvOp> greater_than_opcodes{SpvOpUGreaterThan,
-                                          SpvOpUGreaterThanEqual};
-  std::vector<SpvOp> less_than_opcodes{SpvOpULessThan, SpvOpULessThanEqual};
+  std::vector<spv::Op> greater_than_opcodes{spv::Op::OpUGreaterThan,
+                                            spv::Op::OpUGreaterThanEqual};
+  std::vector<spv::Op> less_than_opcodes{spv::Op::OpULessThan,
+                                         spv::Op::OpULessThanEqual};
 
   ObfuscateBoolConstantViaConstantPair(
       depth, bool_constant_use, greater_than_opcodes, less_than_opcodes,
@@ -379,7 +381,7 @@
       uniform_descriptor.index());
   assert(element_type_id && "Type of uniform variable is invalid");
 
-  FindOrCreatePointerType(element_type_id, SpvStorageClassUniform);
+  FindOrCreatePointerType(element_type_id, spv::StorageClass::Uniform);
 
   // Create, apply and record a transformation to replace the constant use with
   // the result of a load from the chosen uniform.
@@ -394,11 +396,11 @@
               ->get_def_use_mgr()
               ->GetDef(constant_use.id_of_interest())
               ->opcode()) {
-    case SpvOpConstantTrue:
-    case SpvOpConstantFalse:
+    case spv::Op::OpConstantTrue:
+    case spv::Op::OpConstantFalse:
       ObfuscateBoolConstant(depth, constant_use);
       break;
-    case SpvOpConstant:
+    case spv::Op::OpConstant:
       ObfuscateScalarConstant(depth, constant_use);
       break;
     default:
@@ -410,7 +412,7 @@
 void FuzzerPassObfuscateConstants::MaybeAddConstantIdUse(
     const opt::Instruction& inst, uint32_t in_operand_index,
     uint32_t base_instruction_result_id,
-    const std::map<SpvOp, uint32_t>& skipped_opcode_count,
+    const std::map<spv::Op, uint32_t>& skipped_opcode_count,
     std::vector<protobufs::IdUseDescriptor>* constant_uses) {
   if (inst.GetInOperand(in_operand_index).type != SPV_OPERAND_TYPE_ID) {
     // The operand is not an id, so it cannot be a constant id.
@@ -420,15 +422,15 @@
   auto operand_definition =
       GetIRContext()->get_def_use_mgr()->GetDef(operand_id);
   switch (operand_definition->opcode()) {
-    case SpvOpConstantFalse:
-    case SpvOpConstantTrue:
-    case SpvOpConstant: {
+    case spv::Op::OpConstantFalse:
+    case spv::Op::OpConstantTrue:
+    case spv::Op::OpConstant: {
       // The operand is a constant id, so make an id use descriptor and record
       // it.
       protobufs::IdUseDescriptor id_use_descriptor;
       id_use_descriptor.set_id_of_interest(operand_id);
       id_use_descriptor.mutable_enclosing_instruction()
-          ->set_target_instruction_opcode(inst.opcode());
+          ->set_target_instruction_opcode(uint32_t(inst.opcode()));
       id_use_descriptor.mutable_enclosing_instruction()
           ->set_base_instruction_result_id(base_instruction_result_id);
       id_use_descriptor.mutable_enclosing_instruction()
@@ -461,7 +463,7 @@
       // opcode need to be skipped in order to find the instruction of interest
       // from the base instruction. We maintain a mapping that records a skip
       // count for each relevant opcode.
-      std::map<SpvOp, uint32_t> skipped_opcode_count;
+      std::map<spv::Op, uint32_t> skipped_opcode_count;
 
       // Go through each instruction in the block.
       for (auto& inst : block) {
@@ -478,7 +480,7 @@
         // The instruction must not be an OpVariable, the only id that an
         // OpVariable uses is an initializer id, which has to remain
         // constant.
-        if (inst.opcode() != SpvOpVariable) {
+        if (inst.opcode() != spv::Op::OpVariable) {
           // Consider each operand of the instruction, and add a constant id
           // use for the operand if relevant.
           for (uint32_t in_operand_index = 0;
diff --git a/source/fuzz/fuzzer_pass_obfuscate_constants.h b/source/fuzz/fuzzer_pass_obfuscate_constants.h
index 30e64d2..bfef597 100644
--- a/source/fuzz/fuzzer_pass_obfuscate_constants.h
+++ b/source/fuzz/fuzzer_pass_obfuscate_constants.h
@@ -85,8 +85,8 @@
   // (similar for |less_than_opcodes|).
   void ObfuscateBoolConstantViaConstantPair(
       uint32_t depth, const protobufs::IdUseDescriptor& bool_constant_use,
-      const std::vector<SpvOp>& greater_than_opcodes,
-      const std::vector<SpvOp>& less_than_opcodes, uint32_t constant_id_1,
+      const std::vector<spv::Op>& greater_than_opcodes,
+      const std::vector<spv::Op>& less_than_opcodes, uint32_t constant_id_1,
       uint32_t constant_id_2, bool first_constant_is_larger);
 
   // A helper method to determine whether input operand |in_operand_index| of
@@ -96,7 +96,7 @@
   void MaybeAddConstantIdUse(
       const opt::Instruction& inst, uint32_t in_operand_index,
       uint32_t base_instruction_result_id,
-      const std::map<SpvOp, uint32_t>& skipped_opcode_count,
+      const std::map<spv::Op, uint32_t>& skipped_opcode_count,
       std::vector<protobufs::IdUseDescriptor>* constant_uses);
 
   // Returns a vector of unique words that denote constants. Every such constant
diff --git a/source/fuzz/fuzzer_pass_outline_functions.cpp b/source/fuzz/fuzzer_pass_outline_functions.cpp
index b90c12d..e64373a 100644
--- a/source/fuzz/fuzzer_pass_outline_functions.cpp
+++ b/source/fuzz/fuzzer_pass_outline_functions.cpp
@@ -137,12 +137,12 @@
          "The entry block cannot be a loop header at this point.");
 
   // If the entry block starts with OpPhi or OpVariable, try to split it.
-  if (entry_block->begin()->opcode() == SpvOpPhi ||
-      entry_block->begin()->opcode() == SpvOpVariable) {
+  if (entry_block->begin()->opcode() == spv::Op::OpPhi ||
+      entry_block->begin()->opcode() == spv::Op::OpVariable) {
     // Find the first non-OpPhi and non-OpVariable instruction.
     auto non_phi_or_var_inst = &*entry_block->begin();
-    while (non_phi_or_var_inst->opcode() == SpvOpPhi ||
-           non_phi_or_var_inst->opcode() == SpvOpVariable) {
+    while (non_phi_or_var_inst->opcode() == spv::Op::OpPhi ||
+           non_phi_or_var_inst->opcode() == spv::Op::OpVariable) {
       non_phi_or_var_inst = non_phi_or_var_inst->NextNode();
     }
 
@@ -175,7 +175,7 @@
 
     // Find the first non-OpPhi instruction, after which to split.
     auto split_before = &*exit_block->begin();
-    while (split_before->opcode() == SpvOpPhi) {
+    while (split_before->opcode() == spv::Op::OpPhi) {
       split_before = split_before->NextNode();
     }
 
diff --git a/source/fuzz/fuzzer_pass_permute_function_variables.cpp b/source/fuzz/fuzzer_pass_permute_function_variables.cpp
index f8b9b45..2313f42 100644
--- a/source/fuzz/fuzzer_pass_permute_function_variables.cpp
+++ b/source/fuzz/fuzzer_pass_permute_function_variables.cpp
@@ -47,7 +47,7 @@
 
     std::vector<opt::Instruction*> variables;
     for (auto& instruction : *first_block) {
-      if (instruction.opcode() == SpvOpVariable) {
+      if (instruction.opcode() == spv::Op::OpVariable) {
         variables.push_back(&instruction);
       }
     }
diff --git a/source/fuzz/fuzzer_pass_permute_phi_operands.cpp b/source/fuzz/fuzzer_pass_permute_phi_operands.cpp
index 5fac981..7fbdd3b 100644
--- a/source/fuzz/fuzzer_pass_permute_phi_operands.cpp
+++ b/source/fuzz/fuzzer_pass_permute_phi_operands.cpp
@@ -40,7 +40,7 @@
              const protobufs::InstructionDescriptor& /*unused*/) {
         const auto& inst = *inst_it;
 
-        if (inst.opcode() != SpvOpPhi) {
+        if (inst.opcode() != spv::Op::OpPhi) {
           return;
         }
 
diff --git a/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp b/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp
index a6c07b4..c0397e1 100644
--- a/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp
+++ b/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp
@@ -35,10 +35,11 @@
              opt::BasicBlock::iterator instruction_iterator,
              const protobufs::InstructionDescriptor& instruction_descriptor)
           -> void {
-        assert(instruction_iterator->opcode() ==
-                   instruction_descriptor.target_instruction_opcode() &&
-               "The opcode of the instruction we might insert before must be "
-               "the same as the opcode in the descriptor for the instruction");
+        assert(
+            instruction_iterator->opcode() ==
+                spv::Op(instruction_descriptor.target_instruction_opcode()) &&
+            "The opcode of the instruction we might insert before must be "
+            "the same as the opcode in the descriptor for the instruction");
 
         // Randomly decide whether to try pushing an id through a variable.
         if (!GetFuzzerContext()->ChoosePercentage(
@@ -55,16 +56,16 @@
         // It must be valid to insert OpStore and OpLoad instructions
         // before the instruction to insert before.
         if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(
-                SpvOpStore, instruction_iterator) ||
+                spv::Op::OpStore, instruction_iterator) ||
             !fuzzerutil::CanInsertOpcodeBeforeInstruction(
-                SpvOpLoad, instruction_iterator)) {
+                spv::Op::OpLoad, instruction_iterator)) {
           return;
         }
 
         // Randomly decides whether a global or local variable will be added.
         auto variable_storage_class = GetFuzzerContext()->ChooseEven()
-                                          ? SpvStorageClassPrivate
-                                          : SpvStorageClassFunction;
+                                          ? spv::StorageClass::Private
+                                          : spv::StorageClass::Function;
 
         // Gets the available basic and pointer types.
         auto basic_type_ids_and_pointers =
@@ -127,13 +128,13 @@
             GetIRContext()->get_def_use_mgr()->GetDef(basic_type_id);
         assert(type_inst);
         switch (type_inst->opcode()) {
-          case SpvOpTypeBool:
-          case SpvOpTypeFloat:
-          case SpvOpTypeInt:
-          case SpvOpTypeArray:
-          case SpvOpTypeMatrix:
-          case SpvOpTypeVector:
-          case SpvOpTypeStruct:
+          case spv::Op::OpTypeBool:
+          case spv::Op::OpTypeFloat:
+          case spv::Op::OpTypeInt:
+          case spv::Op::OpTypeArray:
+          case spv::Op::OpTypeMatrix:
+          case spv::Op::OpTypeVector:
+          case spv::Op::OpTypeStruct:
             break;
           default:
             return;
@@ -150,7 +151,8 @@
                                    value_instructions)]
                 ->result_id(),
             GetFuzzerContext()->GetFreshId(), GetFuzzerContext()->GetFreshId(),
-            variable_storage_class, initializer_id, instruction_descriptor));
+            uint32_t(variable_storage_class), initializer_id,
+            instruction_descriptor));
       });
 }
 
diff --git a/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.cpp b/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.cpp
index 995657c..52c0381 100644
--- a/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.cpp
+++ b/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.cpp
@@ -38,13 +38,13 @@
   // to be executed with the Fragment execution model.  We conservatively only
   // allow OpKill if every entry point in the module has the Fragment execution
   // model.
-  auto fragment_execution_model_guaranteed =
-      std::all_of(GetIRContext()->module()->entry_points().begin(),
-                  GetIRContext()->module()->entry_points().end(),
-                  [](const opt::Instruction& entry_point) -> bool {
-                    return entry_point.GetSingleWordInOperand(0) ==
-                           SpvExecutionModelFragment;
-                  });
+  auto fragment_execution_model_guaranteed = std::all_of(
+      GetIRContext()->module()->entry_points().begin(),
+      GetIRContext()->module()->entry_points().end(),
+      [](const opt::Instruction& entry_point) -> bool {
+        return spv::ExecutionModel(entry_point.GetSingleWordInOperand(0)) ==
+               spv::ExecutionModel::Fragment;
+      });
 
   // Transformations of this type can disable one another.  To avoid ordering
   // bias, we therefore build a set of candidate transformations to apply, and
@@ -71,20 +71,20 @@
       // Whether we can use OpKill depends on the execution model, and which of
       // OpReturn and OpReturnValue we can use depends on the return type of the
       // enclosing function.
-      std::vector<SpvOp> opcodes = {SpvOpUnreachable};
+      std::vector<spv::Op> opcodes = {spv::Op::OpUnreachable};
       if (fragment_execution_model_guaranteed) {
-        opcodes.emplace_back(SpvOpKill);
+        opcodes.emplace_back(spv::Op::OpKill);
       }
       auto function_return_type =
           GetIRContext()->get_type_mgr()->GetType(function.type_id());
       if (function_return_type->AsVoid()) {
-        opcodes.emplace_back(SpvOpReturn);
+        opcodes.emplace_back(spv::Op::OpReturn);
       } else if (fuzzerutil::CanCreateConstant(GetIRContext(),
                                                function.type_id())) {
         // For simplicity we only allow OpReturnValue if the function return
         // type is a type for which we can create a constant.  This allows us a
         // zero of the given type as a default return value.
-        opcodes.emplace_back(SpvOpReturnValue);
+        opcodes.emplace_back(spv::Op::OpReturnValue);
       }
       // Choose one of the available terminator opcodes at random and create a
       // candidate transformation.
@@ -92,7 +92,7 @@
       candidate_transformations.emplace_back(
           TransformationReplaceBranchFromDeadBlockWithExit(
               block.id(), opcode,
-              opcode == SpvOpReturnValue
+              opcode == spv::Op::OpReturnValue
                   ? FindOrCreateZeroConstant(function.type_id(), true)
                   : 0));
     }
diff --git a/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.cpp b/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.cpp
index af1aace..aabc6bc 100644
--- a/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.cpp
+++ b/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.cpp
@@ -41,7 +41,7 @@
     }
 
     // The instruction must be OpCopyMemory.
-    if (instruction->opcode() != SpvOpCopyMemory) {
+    if (instruction->opcode() != spv::Op::OpCopyMemory) {
       return;
     }
 
diff --git a/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.cpp b/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.cpp
index d0992a3..c1892be 100644
--- a/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.cpp
+++ b/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.cpp
@@ -40,7 +40,7 @@
       return;
     }
     // The instruction must be OpCopyObject.
-    if (instruction->opcode() != SpvOpCopyObject) {
+    if (instruction->opcode() != spv::Op::OpCopyObject) {
       return;
     }
     // The opcode of the type_id instruction cannot be a OpTypePointer,
@@ -48,21 +48,22 @@
     if (GetIRContext()
             ->get_def_use_mgr()
             ->GetDef(instruction->type_id())
-            ->opcode() == SpvOpTypePointer) {
+            ->opcode() == spv::Op::OpTypePointer) {
       return;
     }
     // It must be valid to insert OpStore and OpLoad instructions
     // before the instruction OpCopyObject.
-    if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpStore,
+    if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(spv::Op::OpStore,
                                                       instruction) ||
-        !fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad, instruction)) {
+        !fuzzerutil::CanInsertOpcodeBeforeInstruction(spv::Op::OpLoad,
+                                                      instruction)) {
       return;
     }
 
     // Randomly decides whether a global or local variable will be added.
     auto variable_storage_class = GetFuzzerContext()->ChooseEven()
-                                      ? SpvStorageClassPrivate
-                                      : SpvStorageClassFunction;
+                                      ? spv::StorageClass::Private
+                                      : spv::StorageClass::Function;
 
     // Find or create a constant to initialize the variable from. The type of
     // |instruction| must be such that the function FindOrCreateConstant can be
@@ -79,7 +80,7 @@
     // Apply the transformation replacing OpCopyObject with Store and Load.
     ApplyTransformation(TransformationReplaceCopyObjectWithStoreLoad(
         instruction->result_id(), GetFuzzerContext()->GetFreshId(),
-        variable_storage_class, variable_initializer_id));
+        uint32_t(variable_storage_class), variable_initializer_id));
   });
 }
 
diff --git a/source/fuzz/fuzzer_pass_replace_irrelevant_ids.cpp b/source/fuzz/fuzzer_pass_replace_irrelevant_ids.cpp
index 4d55ae8..4c0bd85 100644
--- a/source/fuzz/fuzzer_pass_replace_irrelevant_ids.cpp
+++ b/source/fuzz/fuzzer_pass_replace_irrelevant_ids.cpp
@@ -73,7 +73,7 @@
   // we cannot use these as replacements.
   for (const auto& pair : GetIRContext()->get_def_use_mgr()->id_to_defs()) {
     uint32_t type_id = pair.second->type_id();
-    if (pair.second->opcode() != SpvOpFunction && type_id &&
+    if (pair.second->opcode() != spv::Op::OpFunction && type_id &&
         types_to_ids.count(type_id)) {
       types_to_ids[type_id].push_back(pair.first);
     }
diff --git a/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.cpp b/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.cpp
index 38ac048..8d292ac 100644
--- a/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.cpp
+++ b/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.cpp
@@ -50,9 +50,9 @@
       std::unordered_map<uint32_t, opt::Instruction*> current_op_loads;
       for (auto& instruction : block) {
         // Add a potential OpLoad instruction.
-        if (instruction.opcode() == SpvOpLoad) {
+        if (instruction.opcode() == spv::Op::OpLoad) {
           current_op_loads[instruction.result_id()] = &instruction;
-        } else if (instruction.opcode() == SpvOpStore) {
+        } else if (instruction.opcode() == spv::Op::OpStore) {
           if (current_op_loads.find(instruction.GetSingleWordOperand(1)) !=
               current_op_loads.end()) {
             // We have found the matching OpLoad instruction to the current
@@ -73,7 +73,7 @@
             opt::Instruction* source_id =
                 GetIRContext()->get_def_use_mgr()->GetDef(
                     it->second->GetSingleWordOperand(2));
-            SpvStorageClass storage_class =
+            spv::StorageClass storage_class =
                 fuzzerutil::GetStorageClassFromPointerType(
                     GetIRContext(), source_id->type_id());
             if (!TransformationReplaceLoadStoreWithCopyMemory::
diff --git a/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.cpp b/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.cpp
index ea90a7a..26475ab 100644
--- a/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.cpp
+++ b/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.cpp
@@ -50,7 +50,7 @@
               block->id(), [this, &function, block, &transformations](
                                opt::Instruction* instruction, uint32_t) {
                 // Only consider OpPhi instructions.
-                if (instruction->opcode() != SpvOpPhi) {
+                if (instruction->opcode() != spv::Op::OpPhi) {
                   return;
                 }
 
diff --git a/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.cpp b/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.cpp
index 72ed093..4691e0a 100644
--- a/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.cpp
+++ b/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.cpp
@@ -52,7 +52,7 @@
 
       for (auto& instruction : block) {
         // We only care about OpSelect instructions.
-        if (instruction.opcode() != SpvOpSelect) {
+        if (instruction.opcode() != spv::Op::OpSelect) {
           continue;
         }
 
@@ -69,7 +69,7 @@
                 ->get_def_use_mgr()
                 ->GetDef(fuzzerutil::GetTypeId(
                     GetIRContext(), instruction.GetSingleWordInOperand(0)))
-                ->opcode() != SpvOpTypeBool) {
+                ->opcode() != spv::Op::OpTypeBool) {
           continue;
         }
 
@@ -136,7 +136,7 @@
 
 bool FuzzerPassReplaceOpSelectsWithConditionalBranches::
     InstructionNeedsSplitBefore(opt::Instruction* instruction) {
-  assert(instruction && instruction->opcode() == SpvOpSelect &&
+  assert(instruction && instruction->opcode() == spv::Op::OpSelect &&
          "The instruction must be OpSelect.");
 
   auto block = GetIRContext()->get_instr_block(instruction);
@@ -163,7 +163,7 @@
   auto predecessor = GetIRContext()->get_instr_block(
       GetIRContext()->cfg()->preds(block->id())[0]);
   return predecessor->MergeBlockIdIfAny() ||
-         predecessor->terminator()->opcode() != SpvOpBranch;
+         predecessor->terminator()->opcode() != spv::Op::OpBranch;
 }
 
 }  // namespace fuzz
diff --git a/source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp b/source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp
index 7fb7b0d..d7eddca 100644
--- a/source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp
+++ b/source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp
@@ -72,7 +72,8 @@
     assert(replaced_param && "Unable to find a parameter to replace");
 
     // Make sure type id for the global variable exists in the module.
-    FindOrCreatePointerType(replaced_param->type_id(), SpvStorageClassPrivate);
+    FindOrCreatePointerType(replaced_param->type_id(),
+                            spv::StorageClass::Private);
 
     // Make sure initializer for the global variable exists in the module.
     FindOrCreateZeroConstant(replaced_param->type_id(), false);
diff --git a/source/fuzz/fuzzer_pass_split_blocks.cpp b/source/fuzz/fuzzer_pass_split_blocks.cpp
index 40a4151..5b4afcc 100644
--- a/source/fuzz/fuzzer_pass_split_blocks.cpp
+++ b/source/fuzz/fuzzer_pass_split_blocks.cpp
@@ -65,7 +65,7 @@
 
     // Counts the number of times we have seen each opcode since we reset the
     // base instruction.
-    std::map<SpvOp, uint32_t> skip_count;
+    std::map<spv::Op, uint32_t> skip_count;
 
     // Consider every instruction in the block.  The label is excluded: it is
     // only necessary to consider it as a base in case the first instruction
@@ -78,7 +78,7 @@
         base = inst.result_id();
         skip_count.clear();
       }
-      const SpvOp opcode = inst.opcode();
+      const spv::Op opcode = inst.opcode();
       instruction_descriptors.emplace_back(MakeInstructionDescriptor(
           base, opcode, skip_count.count(opcode) ? skip_count.at(opcode) : 0));
       if (!inst.HasResultId()) {
diff --git a/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.cpp b/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.cpp
index f8bf111..72c8358 100644
--- a/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.cpp
+++ b/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.cpp
@@ -39,7 +39,7 @@
              const protobufs::InstructionDescriptor& instruction_descriptor) {
         const auto& inst = *inst_it;
 
-        if (inst.opcode() != SpvOpBranchConditional) {
+        if (inst.opcode() != spv::Op::OpBranchConditional) {
           return;
         }
 
diff --git a/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp b/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp
index ac2b156..2a1ad6e 100644
--- a/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp
+++ b/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp
@@ -36,8 +36,9 @@
   // probabilistically applied.
   context->module()->ForEachInst([this,
                                   context](opt::Instruction* instruction) {
-    SpvOp opcode = instruction->opcode();
-    if ((opcode == SpvOpAccessChain || opcode == SpvOpInBoundsAccessChain) &&
+    spv::Op opcode = instruction->opcode();
+    if ((opcode == spv::Op::OpAccessChain ||
+         opcode == spv::Op::OpInBoundsAccessChain) &&
         GetFuzzerContext()->ChoosePercentage(
             GetFuzzerContext()->GetChanceOfTogglingAccessChainInstruction())) {
       auto instructionDescriptor =
diff --git a/source/fuzz/fuzzer_pass_wrap_vector_synonym.cpp b/source/fuzz/fuzzer_pass_wrap_vector_synonym.cpp
index 35adcfe..55fbe2c 100644
--- a/source/fuzz/fuzzer_pass_wrap_vector_synonym.cpp
+++ b/source/fuzz/fuzzer_pass_wrap_vector_synonym.cpp
@@ -52,7 +52,7 @@
         // It must be valid to insert an OpCompositeConstruct instruction
         // before |instruction_iterator|.
         if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(
-                SpvOpCompositeConstruct, instruction_iterator)) {
+                spv::Op::OpCompositeConstruct, instruction_iterator)) {
           return;
         }
 
diff --git a/source/fuzz/fuzzer_util.cpp b/source/fuzz/fuzzer_util.cpp
index 1d368a9..e85ff2f 100644
--- a/source/fuzz/fuzzer_util.cpp
+++ b/source/fuzz/fuzzer_util.cpp
@@ -49,7 +49,7 @@
                             const std::vector<uint32_t>& words,
                             uint32_t type_id, bool is_irrelevant) {
   for (const auto& inst : ir_context->types_values()) {
-    if (inst.opcode() == SpvOpConstant && inst.type_id() == type_id &&
+    if (inst.opcode() == spv::Op::OpConstant && inst.type_id() == type_id &&
         inst.GetInOperand(0).words == words &&
         transformation_context.GetFactManager()->IdIsIrrelevant(
             inst.result_id()) == is_irrelevant) {
@@ -112,7 +112,7 @@
     // No instruction defining this id was found.
     return nullptr;
   }
-  if (inst->opcode() != SpvOpLabel) {
+  if (inst->opcode() != spv::Op::OpLabel) {
     // The instruction defining the id is not a label, so it cannot be a block
     // id.
     return nullptr;
@@ -138,7 +138,7 @@
   // makes sense here because we need to increment |phi_index| for each OpPhi
   // instruction.
   for (auto& inst : *bb_to) {
-    if (inst.opcode() != SpvOpPhi) {
+    if (inst.opcode() != spv::Op::OpPhi) {
       // The OpPhi instructions all occur at the start of the block; if we find
       // a non-OpPhi then we have seen them all.
       break;
@@ -189,24 +189,24 @@
   const auto* bb_from = MaybeFindBlock(ir_context, bb_from_id);
   assert(bb_from && "|bb_from_id| is invalid");
   assert(MaybeFindBlock(ir_context, bb_to_id) && "|bb_to_id| is invalid");
-  assert(bb_from->terminator()->opcode() == SpvOpBranch &&
+  assert(bb_from->terminator()->opcode() == spv::Op::OpBranch &&
          "Precondition on terminator of bb_from is not satisfied");
 
   // Get the id of the boolean constant to be used as the condition.
   auto condition_inst = ir_context->get_def_use_mgr()->GetDef(bool_id);
   assert(condition_inst &&
-         (condition_inst->opcode() == SpvOpConstantTrue ||
-          condition_inst->opcode() == SpvOpConstantFalse) &&
+         (condition_inst->opcode() == spv::Op::OpConstantTrue ||
+          condition_inst->opcode() == spv::Op::OpConstantFalse) &&
          "|bool_id| is invalid");
 
-  auto condition_value = condition_inst->opcode() == SpvOpConstantTrue;
+  auto condition_value = condition_inst->opcode() == spv::Op::OpConstantTrue;
   auto successor_id = bb_from->terminator()->GetSingleWordInOperand(0);
 
   // Add the dead branch, by turning OpBranch into OpBranchConditional, and
   // ordering the targets depending on whether the given boolean corresponds to
   // true or false.
   return opt::Instruction(
-      ir_context, SpvOpBranchConditional, 0, 0,
+      ir_context, spv::Op::OpBranchConditional, 0, 0,
       {{SPV_OPERAND_TYPE_ID, {bool_id}},
        {SPV_OPERAND_TYPE_ID, {condition_value ? successor_id : bb_to_id}},
        {SPV_OPERAND_TYPE_ID, {condition_value ? bb_to_id : successor_id}}});
@@ -228,7 +228,7 @@
   if (!from_to_edge_already_exists) {
     uint32_t phi_index = 0;
     for (auto& inst : *bb_to) {
-      if (inst.opcode() != SpvOpPhi) {
+      if (inst.opcode() != spv::Op::OpPhi) {
         break;
       }
       assert(phi_index < static_cast<uint32_t>(phi_ids.size()) &&
@@ -285,28 +285,30 @@
 }
 
 bool CanInsertOpcodeBeforeInstruction(
-    SpvOp opcode, const opt::BasicBlock::iterator& instruction_in_block) {
+    spv::Op opcode, const opt::BasicBlock::iterator& instruction_in_block) {
   if (instruction_in_block->PreviousNode() &&
-      (instruction_in_block->PreviousNode()->opcode() == SpvOpLoopMerge ||
-       instruction_in_block->PreviousNode()->opcode() == SpvOpSelectionMerge)) {
+      (instruction_in_block->PreviousNode()->opcode() == spv::Op::OpLoopMerge ||
+       instruction_in_block->PreviousNode()->opcode() ==
+           spv::Op::OpSelectionMerge)) {
     // We cannot insert directly after a merge instruction.
     return false;
   }
-  if (opcode != SpvOpVariable &&
-      instruction_in_block->opcode() == SpvOpVariable) {
+  if (opcode != spv::Op::OpVariable &&
+      instruction_in_block->opcode() == spv::Op::OpVariable) {
     // We cannot insert a non-OpVariable instruction directly before a
     // variable; variables in a function must be contiguous in the entry block.
     return false;
   }
   // We cannot insert a non-OpPhi instruction directly before an OpPhi, because
   // OpPhi instructions need to be contiguous at the start of a block.
-  return opcode == SpvOpPhi || instruction_in_block->opcode() != SpvOpPhi;
+  return opcode == spv::Op::OpPhi ||
+         instruction_in_block->opcode() != spv::Op::OpPhi;
 }
 
 bool CanMakeSynonymOf(opt::IRContext* ir_context,
                       const TransformationContext& transformation_context,
                       const opt::Instruction& inst) {
-  if (inst.opcode() == SpvOpSampledImage) {
+  if (inst.opcode() == spv::Op::OpSampledImage) {
     // The SPIR-V data rules say that only very specific instructions may
     // may consume the result id of an OpSampledImage, and this excludes the
     // instructions that are used for making synonyms.
@@ -326,15 +328,15 @@
     return false;
   }
   auto type_inst = ir_context->get_def_use_mgr()->GetDef(inst.type_id());
-  if (type_inst->opcode() == SpvOpTypeVoid) {
+  if (type_inst->opcode() == spv::Op::OpTypeVoid) {
     // We only make synonyms of instructions that define objects, and an object
     // cannot have void type.
     return false;
   }
-  if (type_inst->opcode() == SpvOpTypePointer) {
+  if (type_inst->opcode() == spv::Op::OpTypePointer) {
     switch (inst.opcode()) {
-      case SpvOpConstantNull:
-      case SpvOpUndef:
+      case spv::Op::OpConstantNull:
+      case spv::Op::OpUndef:
         // We disallow making synonyms of null or undefined pointers.  This is
         // to provide the property that if the original shader exhibited no bad
         // pointer accesses, the transformed shader will not either.
@@ -373,22 +375,22 @@
       context->get_def_use_mgr()->GetDef(base_object_type_id);
   assert(should_be_composite_type && "The type should exist.");
   switch (should_be_composite_type->opcode()) {
-    case SpvOpTypeArray: {
+    case spv::Op::OpTypeArray: {
       auto array_length = GetArraySize(*should_be_composite_type, context);
       if (array_length == 0 || index >= array_length) {
         return 0;
       }
       return should_be_composite_type->GetSingleWordInOperand(0);
     }
-    case SpvOpTypeMatrix:
-    case SpvOpTypeVector: {
+    case spv::Op::OpTypeMatrix:
+    case spv::Op::OpTypeVector: {
       auto count = should_be_composite_type->GetSingleWordInOperand(1);
       if (index >= count) {
         return 0;
       }
       return should_be_composite_type->GetSingleWordInOperand(0);
     }
-    case SpvOpTypeStruct: {
+    case spv::Op::OpTypeStruct: {
       if (index >= GetNumberOfStructMembers(*should_be_composite_type)) {
         return 0;
       }
@@ -415,7 +417,7 @@
 
 uint32_t GetNumberOfStructMembers(
     const opt::Instruction& struct_type_instruction) {
-  assert(struct_type_instruction.opcode() == SpvOpTypeStruct &&
+  assert(struct_type_instruction.opcode() == spv::Op::OpTypeStruct &&
          "An OpTypeStruct instruction is required here.");
   return struct_type_instruction.NumInOperands();
 }
@@ -436,15 +438,15 @@
 uint32_t GetBoundForCompositeIndex(const opt::Instruction& composite_type_inst,
                                    opt::IRContext* ir_context) {
   switch (composite_type_inst.opcode()) {
-    case SpvOpTypeArray:
+    case spv::Op::OpTypeArray:
       return fuzzerutil::GetArraySize(composite_type_inst, ir_context);
-    case SpvOpTypeMatrix:
-    case SpvOpTypeVector:
+    case spv::Op::OpTypeMatrix:
+    case spv::Op::OpTypeVector:
       return composite_type_inst.GetSingleWordInOperand(1);
-    case SpvOpTypeStruct: {
+    case spv::Op::OpTypeStruct: {
       return fuzzerutil::GetNumberOfStructMembers(composite_type_inst);
     }
-    case SpvOpTypeRuntimeArray:
+    case spv::Op::OpTypeRuntimeArray:
       assert(false &&
              "GetBoundForCompositeIndex should not be invoked with an "
              "OpTypeRuntimeArray, which does not have a static bound.");
@@ -455,27 +457,27 @@
   }
 }
 
-SpvMemorySemanticsMask GetMemorySemanticsForStorageClass(
-    SpvStorageClass storage_class) {
+spv::MemorySemanticsMask GetMemorySemanticsForStorageClass(
+    spv::StorageClass storage_class) {
   switch (storage_class) {
-    case SpvStorageClassWorkgroup:
-      return SpvMemorySemanticsWorkgroupMemoryMask;
+    case spv::StorageClass::Workgroup:
+      return spv::MemorySemanticsMask::WorkgroupMemory;
 
-    case SpvStorageClassStorageBuffer:
-    case SpvStorageClassPhysicalStorageBuffer:
-      return SpvMemorySemanticsUniformMemoryMask;
+    case spv::StorageClass::StorageBuffer:
+    case spv::StorageClass::PhysicalStorageBuffer:
+      return spv::MemorySemanticsMask::UniformMemory;
 
-    case SpvStorageClassCrossWorkgroup:
-      return SpvMemorySemanticsCrossWorkgroupMemoryMask;
+    case spv::StorageClass::CrossWorkgroup:
+      return spv::MemorySemanticsMask::CrossWorkgroupMemory;
 
-    case SpvStorageClassAtomicCounter:
-      return SpvMemorySemanticsAtomicCounterMemoryMask;
+    case spv::StorageClass::AtomicCounter:
+      return spv::MemorySemanticsMask::AtomicCounterMemory;
 
-    case SpvStorageClassImage:
-      return SpvMemorySemanticsImageMemoryMask;
+    case spv::StorageClass::Image:
+      return spv::MemorySemanticsMask::ImageMemory;
 
     default:
-      return SpvMemorySemanticsMaskNone;
+      return spv::MemorySemanticsMask::MaskNone;
   }
 }
 
@@ -562,8 +564,8 @@
       [&result](const opt::Instruction* use_instruction,
                 uint32_t /*unused*/) -> bool {
         switch (use_instruction->opcode()) {
-          case SpvOpLoopMerge:
-          case SpvOpSelectionMerge:
+          case spv::Op::OpLoopMerge:
+          case spv::Op::OpSelectionMerge:
             result = true;
             return false;
           default:
@@ -581,7 +583,7 @@
       [ir_context, &result](opt::Instruction* use_instruction,
                             uint32_t use_index) -> bool {
         switch (use_instruction->opcode()) {
-          case SpvOpLoopMerge:
+          case spv::Op::OpLoopMerge:
             // The merge block operand is the first operand in OpLoopMerge.
             if (use_index == 0) {
               result = ir_context->get_instr_block(use_instruction)->id();
@@ -599,7 +601,7 @@
                           const std::vector<uint32_t>& type_ids) {
   // Look through the existing types for a match.
   for (auto& type_or_value : ir_context->types_values()) {
-    if (type_or_value.opcode() != SpvOpTypeFunction) {
+    if (type_or_value.opcode() != spv::Op::OpTypeFunction) {
       // We are only interested in function types.
       continue;
     }
@@ -641,8 +643,8 @@
 
 bool FunctionContainsOpKillOrUnreachable(const opt::Function& function) {
   for (auto& block : function) {
-    if (block.terminator()->opcode() == SpvOpKill ||
-        block.terminator()->opcode() == SpvOpUnreachable) {
+    if (block.terminator()->opcode() == spv::Op::OpKill ||
+        block.terminator()->opcode() == spv::Op::OpUnreachable) {
       return true;
     }
   }
@@ -669,7 +671,7 @@
       context->get_instr_block(use_instruction)->GetParent();
   // If the id a function parameter, it needs to be associated with the
   // function containing the use.
-  if (defining_instruction->opcode() == SpvOpFunctionParameter) {
+  if (defining_instruction->opcode() == spv::Op::OpFunctionParameter) {
     return InstructionIsFunctionParameter(defining_instruction,
                                           enclosing_function);
   }
@@ -687,7 +689,7 @@
     return false;
   }
   auto dominator_analysis = context->GetDominatorAnalysis(enclosing_function);
-  if (use_instruction->opcode() == SpvOpPhi) {
+  if (use_instruction->opcode() == spv::Op::OpPhi) {
     // In the case where the use is an operand to OpPhi, it is actually the
     // *parent* block associated with the operand that must be dominated by
     // the synonym.
@@ -710,7 +712,7 @@
       context->get_instr_block(instruction)->GetParent();
   // If the id a function parameter, it needs to be associated with the
   // function containing the instruction.
-  if (id_definition->opcode() == SpvOpFunctionParameter) {
+  if (id_definition->opcode() == spv::Op::OpFunctionParameter) {
     return InstructionIsFunctionParameter(id_definition,
                                           function_enclosing_instruction);
   }
@@ -732,7 +734,7 @@
     // the instruction.
     return true;
   }
-  if (id_definition->opcode() == SpvOpVariable &&
+  if (id_definition->opcode() == spv::Op::OpVariable &&
       function_enclosing_instruction ==
           context->get_instr_block(id)->GetParent()) {
     assert(!context->IsReachable(*context->get_instr_block(instruction)) &&
@@ -747,7 +749,7 @@
 
 bool InstructionIsFunctionParameter(opt::Instruction* instruction,
                                     opt::Function* function) {
-  if (instruction->opcode() != SpvOpFunctionParameter) {
+  if (instruction->opcode() != spv::Op::OpFunctionParameter) {
     return false;
   }
   bool found_parameter = false;
@@ -767,7 +769,8 @@
 }
 
 uint32_t GetPointeeTypeIdFromPointerType(opt::Instruction* pointer_type_inst) {
-  assert(pointer_type_inst && pointer_type_inst->opcode() == SpvOpTypePointer &&
+  assert(pointer_type_inst &&
+         pointer_type_inst->opcode() == spv::Op::OpTypePointer &&
          "Precondition: |pointer_type_inst| must be OpTypePointer.");
   return pointer_type_inst->GetSingleWordInOperand(1);
 }
@@ -778,26 +781,28 @@
       context->get_def_use_mgr()->GetDef(pointer_type_id));
 }
 
-SpvStorageClass GetStorageClassFromPointerType(
+spv::StorageClass GetStorageClassFromPointerType(
     opt::Instruction* pointer_type_inst) {
-  assert(pointer_type_inst && pointer_type_inst->opcode() == SpvOpTypePointer &&
+  assert(pointer_type_inst &&
+         pointer_type_inst->opcode() == spv::Op::OpTypePointer &&
          "Precondition: |pointer_type_inst| must be OpTypePointer.");
-  return static_cast<SpvStorageClass>(
+  return static_cast<spv::StorageClass>(
       pointer_type_inst->GetSingleWordInOperand(0));
 }
 
-SpvStorageClass GetStorageClassFromPointerType(opt::IRContext* context,
-                                               uint32_t pointer_type_id) {
+spv::StorageClass GetStorageClassFromPointerType(opt::IRContext* context,
+                                                 uint32_t pointer_type_id) {
   return GetStorageClassFromPointerType(
       context->get_def_use_mgr()->GetDef(pointer_type_id));
 }
 
 uint32_t MaybeGetPointerType(opt::IRContext* context, uint32_t pointee_type_id,
-                             SpvStorageClass storage_class) {
+                             spv::StorageClass storage_class) {
   for (auto& inst : context->types_values()) {
     switch (inst.opcode()) {
-      case SpvOpTypePointer:
-        if (inst.GetSingleWordInOperand(0) == storage_class &&
+      case spv::Op::OpTypePointer:
+        if (spv::StorageClass(inst.GetSingleWordInOperand(0)) ==
+                storage_class &&
             inst.GetSingleWordInOperand(1) == pointee_type_id) {
           return inst.result_id();
         }
@@ -818,30 +823,30 @@
 bool IsNullConstantSupported(opt::IRContext* ir_context,
                              const opt::Instruction& type_inst) {
   switch (type_inst.opcode()) {
-    case SpvOpTypeArray:
-    case SpvOpTypeBool:
-    case SpvOpTypeDeviceEvent:
-    case SpvOpTypeEvent:
-    case SpvOpTypeFloat:
-    case SpvOpTypeInt:
-    case SpvOpTypeMatrix:
-    case SpvOpTypeQueue:
-    case SpvOpTypeReserveId:
-    case SpvOpTypeVector:
-    case SpvOpTypeStruct:
+    case spv::Op::OpTypeArray:
+    case spv::Op::OpTypeBool:
+    case spv::Op::OpTypeDeviceEvent:
+    case spv::Op::OpTypeEvent:
+    case spv::Op::OpTypeFloat:
+    case spv::Op::OpTypeInt:
+    case spv::Op::OpTypeMatrix:
+    case spv::Op::OpTypeQueue:
+    case spv::Op::OpTypeReserveId:
+    case spv::Op::OpTypeVector:
+    case spv::Op::OpTypeStruct:
       return true;
-    case SpvOpTypePointer:
+    case spv::Op::OpTypePointer:
       // Null pointers are allowed if the VariablePointers capability is
       // enabled, or if the VariablePointersStorageBuffer capability is enabled
       // and the pointer type has StorageBuffer as its storage class.
       if (ir_context->get_feature_mgr()->HasCapability(
-              SpvCapabilityVariablePointers)) {
+              spv::Capability::VariablePointers)) {
         return true;
       }
       if (ir_context->get_feature_mgr()->HasCapability(
-              SpvCapabilityVariablePointersStorageBuffer)) {
-        return type_inst.GetSingleWordInOperand(0) ==
-               SpvStorageClassStorageBuffer;
+              spv::Capability::VariablePointersStorageBuffer)) {
+        return spv::StorageClass(type_inst.GetSingleWordInOperand(0)) ==
+               spv::StorageClass::StorageBuffer;
       }
       return false;
     default:
@@ -885,22 +890,22 @@
 
 opt::Instruction* AddGlobalVariable(opt::IRContext* context, uint32_t result_id,
                                     uint32_t type_id,
-                                    SpvStorageClass storage_class,
+                                    spv::StorageClass storage_class,
                                     uint32_t initializer_id) {
   // Check various preconditions.
   assert(result_id != 0 && "Result id can't be 0");
 
-  assert((storage_class == SpvStorageClassPrivate ||
-          storage_class == SpvStorageClassWorkgroup) &&
+  assert((storage_class == spv::StorageClass::Private ||
+          storage_class == spv::StorageClass::Workgroup) &&
          "Variable's storage class must be either Private or Workgroup");
 
   auto* type_inst = context->get_def_use_mgr()->GetDef(type_id);
   (void)type_inst;  // Variable becomes unused in release mode.
-  assert(type_inst && type_inst->opcode() == SpvOpTypePointer &&
+  assert(type_inst && type_inst->opcode() == spv::Op::OpTypePointer &&
          GetStorageClassFromPointerType(type_inst) == storage_class &&
          "Variable's type is invalid");
 
-  if (storage_class == SpvStorageClassWorkgroup) {
+  if (storage_class == spv::StorageClass::Workgroup) {
     assert(initializer_id == 0);
   }
 
@@ -922,7 +927,7 @@
   }
 
   auto new_instruction = MakeUnique<opt::Instruction>(
-      context, SpvOpVariable, type_id, result_id, std::move(operands));
+      context, spv::Op::OpVariable, type_id, result_id, std::move(operands));
   auto result = new_instruction.get();
   context->module()->AddGlobalValue(std::move(new_instruction));
 
@@ -940,8 +945,9 @@
 
   auto* type_inst = context->get_def_use_mgr()->GetDef(type_id);
   (void)type_inst;  // Variable becomes unused in release mode.
-  assert(type_inst && type_inst->opcode() == SpvOpTypePointer &&
-         GetStorageClassFromPointerType(type_inst) == SpvStorageClassFunction &&
+  assert(type_inst && type_inst->opcode() == spv::Op::OpTypePointer &&
+         GetStorageClassFromPointerType(type_inst) ==
+             spv::StorageClass::Function &&
          "Variable's type is invalid");
 
   const auto* constant_inst =
@@ -956,10 +962,10 @@
   assert(function && "Function id is invalid");
 
   auto new_instruction = MakeUnique<opt::Instruction>(
-      context, SpvOpVariable, type_id, result_id,
-      opt::Instruction::OperandList{
-          {SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}},
-          {SPV_OPERAND_TYPE_ID, {initializer_id}}});
+      context, spv::Op::OpVariable, type_id, result_id,
+      opt::Instruction::OperandList{{SPV_OPERAND_TYPE_STORAGE_CLASS,
+                                     {uint32_t(spv::StorageClass::Function)}},
+                                    {SPV_OPERAND_TYPE_ID, {initializer_id}}});
   auto result = new_instruction.get();
   function->begin()->begin()->InsertBefore(std::move(new_instruction));
 
@@ -1022,7 +1028,7 @@
   std::vector<opt::Instruction*> result;
   ir_context->get_def_use_mgr()->ForEachUser(
       function_id, [&result, function_id](opt::Instruction* inst) {
-        if (inst->opcode() == SpvOpFunctionCall &&
+        if (inst->opcode() == spv::Op::OpFunctionCall &&
             inst->GetSingleWordInOperand(0) == function_id) {
           result.push_back(inst);
         }
@@ -1137,7 +1143,7 @@
   }
 
   ir_context->AddType(MakeUnique<opt::Instruction>(
-      ir_context, SpvOpTypeFunction, 0, result_id, std::move(operands)));
+      ir_context, spv::Op::OpTypeFunction, 0, result_id, std::move(operands)));
 
   UpdateModuleIdBound(ir_context, result_id);
 }
@@ -1186,7 +1192,7 @@
 uint32_t MaybeGetStructType(opt::IRContext* ir_context,
                             const std::vector<uint32_t>& component_type_ids) {
   for (auto& type_or_value : ir_context->types_values()) {
-    if (type_or_value.opcode() != SpvOpTypeStruct ||
+    if (type_or_value.opcode() != spv::Op::OpTypeStruct ||
         type_or_value.NumInOperands() !=
             static_cast<uint32_t>(component_type_ids.size())) {
       continue;
@@ -1219,11 +1225,11 @@
   assert(type_inst && "|scalar_or_composite_type_id| is invalid");
 
   switch (type_inst->opcode()) {
-    case SpvOpTypeBool:
+    case spv::Op::OpTypeBool:
       return MaybeGetBoolConstant(ir_context, transformation_context, false,
                                   is_irrelevant);
-    case SpvOpTypeFloat:
-    case SpvOpTypeInt: {
+    case spv::Op::OpTypeFloat:
+    case spv::Op::OpTypeInt: {
       const auto width = type_inst->GetSingleWordInOperand(0);
       std::vector<uint32_t> words = {0};
       if (width > 32) {
@@ -1233,7 +1239,7 @@
       return MaybeGetScalarConstant(ir_context, transformation_context, words,
                                     scalar_or_composite_type_id, is_irrelevant);
     }
-    case SpvOpTypeStruct: {
+    case spv::Op::OpTypeStruct: {
       std::vector<uint32_t> component_ids;
       for (uint32_t i = 0; i < type_inst->NumInOperands(); ++i) {
         const auto component_type_id = type_inst->GetSingleWordInOperand(i);
@@ -1260,8 +1266,8 @@
           ir_context, transformation_context, component_ids,
           scalar_or_composite_type_id, is_irrelevant);
     }
-    case SpvOpTypeMatrix:
-    case SpvOpTypeVector: {
+    case spv::Op::OpTypeMatrix:
+    case spv::Op::OpTypeVector: {
       const auto component_type_id = type_inst->GetSingleWordInOperand(0);
 
       auto component_id = MaybeGetZeroConstant(
@@ -1284,7 +1290,7 @@
           std::vector<uint32_t>(component_count, component_id),
           scalar_or_composite_type_id, is_irrelevant);
     }
-    case SpvOpTypeArray: {
+    case spv::Op::OpTypeArray: {
       const auto component_type_id = type_inst->GetSingleWordInOperand(0);
 
       auto component_id = MaybeGetZeroConstant(
@@ -1319,16 +1325,16 @@
   assert(spvOpcodeGeneratesType(type_instr->opcode()) &&
          "A type-generating opcode was expected.");
   switch (type_instr->opcode()) {
-    case SpvOpTypeBool:
-    case SpvOpTypeInt:
-    case SpvOpTypeFloat:
-    case SpvOpTypeMatrix:
-    case SpvOpTypeVector:
+    case spv::Op::OpTypeBool:
+    case spv::Op::OpTypeInt:
+    case spv::Op::OpTypeFloat:
+    case spv::Op::OpTypeMatrix:
+    case spv::Op::OpTypeVector:
       return true;
-    case SpvOpTypeArray:
+    case spv::Op::OpTypeArray:
       return CanCreateConstant(ir_context,
                                type_instr->GetSingleWordInOperand(0));
-    case SpvOpTypeStruct:
+    case spv::Op::OpTypeStruct:
       if (HasBlockOrBufferBlockDecoration(ir_context, type_id)) {
         return false;
       }
@@ -1377,7 +1383,7 @@
   assert(IsCompositeType(type) && "|composite_type_id| is invalid");
 
   for (const auto& inst : ir_context->types_values()) {
-    if (inst.opcode() == SpvOpConstantComposite &&
+    if (inst.opcode() == spv::Op::OpConstantComposite &&
         inst.type_id() == composite_type_id &&
         transformation_context.GetFactManager()->IdIsIrrelevant(
             inst.result_id()) == is_irrelevant &&
@@ -1457,7 +1463,8 @@
     bool is_irrelevant) {
   if (auto type_id = MaybeGetBoolType(ir_context)) {
     for (const auto& inst : ir_context->types_values()) {
-      if (inst.opcode() == (value ? SpvOpConstantTrue : SpvOpConstantFalse) &&
+      if (inst.opcode() ==
+              (value ? spv::Op::OpConstantTrue : spv::Op::OpConstantFalse) &&
           inst.type_id() == type_id &&
           transformation_context.GetFactManager()->IdIsIrrelevant(
               inst.result_id()) == is_irrelevant) {
@@ -1552,13 +1559,13 @@
 
 opt::Instruction* GetLastInsertBeforeInstruction(opt::IRContext* ir_context,
                                                  uint32_t block_id,
-                                                 SpvOp opcode) {
+                                                 spv::Op opcode) {
   // CFG::block uses std::map::at which throws an exception when |block_id| is
   // invalid. The error message is unhelpful, though. Thus, we test that
   // |block_id| is valid here.
   const auto* label_inst = ir_context->get_def_use_mgr()->GetDef(block_id);
   (void)label_inst;  // Make compilers happy in release mode.
-  assert(label_inst && label_inst->opcode() == SpvOpLabel &&
+  assert(label_inst && label_inst->opcode() == spv::Op::OpLabel &&
          "|block_id| is invalid");
 
   auto* block = ir_context->cfg()->block(block_id);
@@ -1624,7 +1631,7 @@
         assert(composite_type_being_accessed->AsStruct());
         auto constant_index_instruction = ir_context->get_def_use_mgr()->GetDef(
             use_instruction->GetSingleWordInOperand(index_in_operand));
-        assert(constant_index_instruction->opcode() == SpvOpConstant);
+        assert(constant_index_instruction->opcode() == spv::Op::OpConstant);
         uint32_t member_index =
             constant_index_instruction->GetSingleWordInOperand(0);
         composite_type_being_accessed =
@@ -1641,7 +1648,7 @@
     }
   }
 
-  if (use_instruction->opcode() == SpvOpFunctionCall &&
+  if (use_instruction->opcode() == spv::Op::OpFunctionCall &&
       use_in_operand_index > 0) {
     // This is a function call argument.  It is not allowed to have pointer
     // type.
@@ -1663,7 +1670,7 @@
     }
   }
 
-  if (use_instruction->opcode() == SpvOpImageTexelPointer &&
+  if (use_instruction->opcode() == spv::Op::OpImageTexelPointer &&
       use_in_operand_index == 2) {
     // The OpImageTexelPointer instruction has a Sample parameter that in some
     // situations must be an id for the value 0.  To guard against disrupting
@@ -1671,38 +1678,38 @@
     return false;
   }
 
-  if (ir_context->get_feature_mgr()->HasCapability(SpvCapabilityShader)) {
+  if (ir_context->get_feature_mgr()->HasCapability(spv::Capability::Shader)) {
     // With the Shader capability, memory scope and memory semantics operands
     // are required to be constants, so they cannot be replaced arbitrarily.
     switch (use_instruction->opcode()) {
-      case SpvOpAtomicLoad:
-      case SpvOpAtomicStore:
-      case SpvOpAtomicExchange:
-      case SpvOpAtomicIIncrement:
-      case SpvOpAtomicIDecrement:
-      case SpvOpAtomicIAdd:
-      case SpvOpAtomicISub:
-      case SpvOpAtomicSMin:
-      case SpvOpAtomicUMin:
-      case SpvOpAtomicSMax:
-      case SpvOpAtomicUMax:
-      case SpvOpAtomicAnd:
-      case SpvOpAtomicOr:
-      case SpvOpAtomicXor:
+      case spv::Op::OpAtomicLoad:
+      case spv::Op::OpAtomicStore:
+      case spv::Op::OpAtomicExchange:
+      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:
         if (use_in_operand_index == 1 || use_in_operand_index == 2) {
           return false;
         }
         break;
-      case SpvOpAtomicCompareExchange:
+      case spv::Op::OpAtomicCompareExchange:
         if (use_in_operand_index == 1 || use_in_operand_index == 2 ||
             use_in_operand_index == 3) {
           return false;
         }
         break;
-      case SpvOpAtomicCompareExchangeWeak:
-      case SpvOpAtomicFlagTestAndSet:
-      case SpvOpAtomicFlagClear:
-      case SpvOpAtomicFAddEXT:
+      case spv::Op::OpAtomicCompareExchangeWeak:
+      case spv::Op::OpAtomicFlagTestAndSet:
+      case spv::Op::OpAtomicFlagClear:
+      case spv::Op::OpAtomicFAddEXT:
         assert(false && "Not allowed with the Shader capability.");
       default:
         break;
@@ -1715,17 +1722,17 @@
 bool MembersHaveBuiltInDecoration(opt::IRContext* ir_context,
                                   uint32_t struct_type_id) {
   const auto* type_inst = ir_context->get_def_use_mgr()->GetDef(struct_type_id);
-  assert(type_inst && type_inst->opcode() == SpvOpTypeStruct &&
+  assert(type_inst && type_inst->opcode() == spv::Op::OpTypeStruct &&
          "|struct_type_id| is not a result id of an OpTypeStruct");
 
   uint32_t builtin_count = 0;
   ir_context->get_def_use_mgr()->ForEachUser(
       type_inst,
       [struct_type_id, &builtin_count](const opt::Instruction* user) {
-        if (user->opcode() == SpvOpMemberDecorate &&
+        if (user->opcode() == spv::Op::OpMemberDecorate &&
             user->GetSingleWordInOperand(0) == struct_type_id &&
-            static_cast<SpvDecoration>(user->GetSingleWordInOperand(2)) ==
-                SpvDecorationBuiltIn) {
+            static_cast<spv::Decoration>(user->GetSingleWordInOperand(2)) ==
+                spv::Decoration::BuiltIn) {
           ++builtin_count;
         }
       });
@@ -1738,9 +1745,11 @@
 }
 
 bool HasBlockOrBufferBlockDecoration(opt::IRContext* ir_context, uint32_t id) {
-  for (auto decoration : {SpvDecorationBlock, SpvDecorationBufferBlock}) {
+  for (auto decoration :
+       {spv::Decoration::Block, spv::Decoration::BufferBlock}) {
     if (!ir_context->get_decoration_mgr()->WhileEachDecoration(
-            id, decoration, [](const opt::Instruction & /*unused*/) -> bool {
+            id, uint32_t(decoration),
+            [](const opt::Instruction & /*unused*/) -> bool {
               return false;
             })) {
       return true;
@@ -1762,7 +1771,7 @@
     if (before_split) {
       // If the instruction comes before the split and its opcode is
       // OpSampledImage, record its result id.
-      if (instruction.opcode() == SpvOpSampledImage) {
+      if (instruction.opcode() == spv::Op::OpSampledImage) {
         sampled_image_result_ids.insert(instruction.result_id());
       }
     } else {
@@ -1784,110 +1793,110 @@
 
 bool InstructionHasNoSideEffects(const opt::Instruction& instruction) {
   switch (instruction.opcode()) {
-    case SpvOpUndef:
-    case SpvOpAccessChain:
-    case SpvOpInBoundsAccessChain:
-    case SpvOpArrayLength:
-    case SpvOpVectorExtractDynamic:
-    case SpvOpVectorInsertDynamic:
-    case SpvOpVectorShuffle:
-    case SpvOpCompositeConstruct:
-    case SpvOpCompositeExtract:
-    case SpvOpCompositeInsert:
-    case SpvOpCopyObject:
-    case SpvOpTranspose:
-    case SpvOpConvertFToU:
-    case SpvOpConvertFToS:
-    case SpvOpConvertSToF:
-    case SpvOpConvertUToF:
-    case SpvOpUConvert:
-    case SpvOpSConvert:
-    case SpvOpFConvert:
-    case SpvOpQuantizeToF16:
-    case SpvOpSatConvertSToU:
-    case SpvOpSatConvertUToS:
-    case SpvOpBitcast:
-    case SpvOpSNegate:
-    case SpvOpFNegate:
-    case SpvOpIAdd:
-    case SpvOpFAdd:
-    case SpvOpISub:
-    case SpvOpFSub:
-    case SpvOpIMul:
-    case SpvOpFMul:
-    case SpvOpUDiv:
-    case SpvOpSDiv:
-    case SpvOpFDiv:
-    case SpvOpUMod:
-    case SpvOpSRem:
-    case SpvOpSMod:
-    case SpvOpFRem:
-    case SpvOpFMod:
-    case SpvOpVectorTimesScalar:
-    case SpvOpMatrixTimesScalar:
-    case SpvOpVectorTimesMatrix:
-    case SpvOpMatrixTimesVector:
-    case SpvOpMatrixTimesMatrix:
-    case SpvOpOuterProduct:
-    case SpvOpDot:
-    case SpvOpIAddCarry:
-    case SpvOpISubBorrow:
-    case SpvOpUMulExtended:
-    case SpvOpSMulExtended:
-    case SpvOpAny:
-    case SpvOpAll:
-    case SpvOpIsNan:
-    case SpvOpIsInf:
-    case SpvOpIsFinite:
-    case SpvOpIsNormal:
-    case SpvOpSignBitSet:
-    case SpvOpLessOrGreater:
-    case SpvOpOrdered:
-    case SpvOpUnordered:
-    case SpvOpLogicalEqual:
-    case SpvOpLogicalNotEqual:
-    case SpvOpLogicalOr:
-    case SpvOpLogicalAnd:
-    case SpvOpLogicalNot:
-    case SpvOpSelect:
-    case SpvOpIEqual:
-    case SpvOpINotEqual:
-    case SpvOpUGreaterThan:
-    case SpvOpSGreaterThan:
-    case SpvOpUGreaterThanEqual:
-    case SpvOpSGreaterThanEqual:
-    case SpvOpULessThan:
-    case SpvOpSLessThan:
-    case SpvOpULessThanEqual:
-    case SpvOpSLessThanEqual:
-    case SpvOpFOrdEqual:
-    case SpvOpFUnordEqual:
-    case SpvOpFOrdNotEqual:
-    case SpvOpFUnordNotEqual:
-    case SpvOpFOrdLessThan:
-    case SpvOpFUnordLessThan:
-    case SpvOpFOrdGreaterThan:
-    case SpvOpFUnordGreaterThan:
-    case SpvOpFOrdLessThanEqual:
-    case SpvOpFUnordLessThanEqual:
-    case SpvOpFOrdGreaterThanEqual:
-    case SpvOpFUnordGreaterThanEqual:
-    case SpvOpShiftRightLogical:
-    case SpvOpShiftRightArithmetic:
-    case SpvOpShiftLeftLogical:
-    case SpvOpBitwiseOr:
-    case SpvOpBitwiseXor:
-    case SpvOpBitwiseAnd:
-    case SpvOpNot:
-    case SpvOpBitFieldInsert:
-    case SpvOpBitFieldSExtract:
-    case SpvOpBitFieldUExtract:
-    case SpvOpBitReverse:
-    case SpvOpBitCount:
-    case SpvOpCopyLogical:
-    case SpvOpPhi:
-    case SpvOpPtrEqual:
-    case SpvOpPtrNotEqual:
+    case spv::Op::OpUndef:
+    case spv::Op::OpAccessChain:
+    case spv::Op::OpInBoundsAccessChain:
+    case spv::Op::OpArrayLength:
+    case spv::Op::OpVectorExtractDynamic:
+    case spv::Op::OpVectorInsertDynamic:
+    case spv::Op::OpVectorShuffle:
+    case spv::Op::OpCompositeConstruct:
+    case spv::Op::OpCompositeExtract:
+    case spv::Op::OpCompositeInsert:
+    case spv::Op::OpCopyObject:
+    case spv::Op::OpTranspose:
+    case spv::Op::OpConvertFToU:
+    case spv::Op::OpConvertFToS:
+    case spv::Op::OpConvertSToF:
+    case spv::Op::OpConvertUToF:
+    case spv::Op::OpUConvert:
+    case spv::Op::OpSConvert:
+    case spv::Op::OpFConvert:
+    case spv::Op::OpQuantizeToF16:
+    case spv::Op::OpSatConvertSToU:
+    case spv::Op::OpSatConvertUToS:
+    case spv::Op::OpBitcast:
+    case spv::Op::OpSNegate:
+    case spv::Op::OpFNegate:
+    case spv::Op::OpIAdd:
+    case spv::Op::OpFAdd:
+    case spv::Op::OpISub:
+    case spv::Op::OpFSub:
+    case spv::Op::OpIMul:
+    case spv::Op::OpFMul:
+    case spv::Op::OpUDiv:
+    case spv::Op::OpSDiv:
+    case spv::Op::OpFDiv:
+    case spv::Op::OpUMod:
+    case spv::Op::OpSRem:
+    case spv::Op::OpSMod:
+    case spv::Op::OpFRem:
+    case spv::Op::OpFMod:
+    case spv::Op::OpVectorTimesScalar:
+    case spv::Op::OpMatrixTimesScalar:
+    case spv::Op::OpVectorTimesMatrix:
+    case spv::Op::OpMatrixTimesVector:
+    case spv::Op::OpMatrixTimesMatrix:
+    case spv::Op::OpOuterProduct:
+    case spv::Op::OpDot:
+    case spv::Op::OpIAddCarry:
+    case spv::Op::OpISubBorrow:
+    case spv::Op::OpUMulExtended:
+    case spv::Op::OpSMulExtended:
+    case spv::Op::OpAny:
+    case spv::Op::OpAll:
+    case spv::Op::OpIsNan:
+    case spv::Op::OpIsInf:
+    case spv::Op::OpIsFinite:
+    case spv::Op::OpIsNormal:
+    case spv::Op::OpSignBitSet:
+    case spv::Op::OpLessOrGreater:
+    case spv::Op::OpOrdered:
+    case spv::Op::OpUnordered:
+    case spv::Op::OpLogicalEqual:
+    case spv::Op::OpLogicalNotEqual:
+    case spv::Op::OpLogicalOr:
+    case spv::Op::OpLogicalAnd:
+    case spv::Op::OpLogicalNot:
+    case spv::Op::OpSelect:
+    case spv::Op::OpIEqual:
+    case spv::Op::OpINotEqual:
+    case spv::Op::OpUGreaterThan:
+    case spv::Op::OpSGreaterThan:
+    case spv::Op::OpUGreaterThanEqual:
+    case spv::Op::OpSGreaterThanEqual:
+    case spv::Op::OpULessThan:
+    case spv::Op::OpSLessThan:
+    case spv::Op::OpULessThanEqual:
+    case spv::Op::OpSLessThanEqual:
+    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::OpShiftRightLogical:
+    case spv::Op::OpShiftRightArithmetic:
+    case spv::Op::OpShiftLeftLogical:
+    case spv::Op::OpBitwiseOr:
+    case spv::Op::OpBitwiseXor:
+    case spv::Op::OpBitwiseAnd:
+    case spv::Op::OpNot:
+    case spv::Op::OpBitFieldInsert:
+    case spv::Op::OpBitFieldSExtract:
+    case spv::Op::OpBitFieldUExtract:
+    case spv::Op::OpBitReverse:
+    case spv::Op::OpBitCount:
+    case spv::Op::OpCopyLogical:
+    case spv::Op::OpPhi:
+    case spv::Op::OpPtrEqual:
+    case spv::Op::OpPtrNotEqual:
       return true;
     default:
       return false;
@@ -1975,7 +1984,7 @@
 
     for (const auto& inst : block) {
       for (uint32_t i = 0; i < inst.NumInOperands();
-           i += inst.opcode() == SpvOpPhi ? 2 : 1) {
+           i += inst.opcode() == spv::Op::OpPhi ? 2 : 1) {
         const auto& operand = inst.GetInOperand(i);
         if (!spvIsInIdType(operand.type)) {
           continue;
@@ -1994,7 +2003,7 @@
           continue;
         }
 
-        auto domination_target_id = inst.opcode() == SpvOpPhi
+        auto domination_target_id = inst.opcode() == spv::Op::OpPhi
                                         ? inst.GetSingleWordInOperand(i + 1)
                                         : block.id();
 
@@ -2021,68 +2030,68 @@
 // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3582): Add all
 //  opcodes that are agnostic to signedness of operands to function.
 //  This is not exhaustive yet.
-bool IsAgnosticToSignednessOfOperand(SpvOp opcode,
+bool IsAgnosticToSignednessOfOperand(spv::Op opcode,
                                      uint32_t use_in_operand_index) {
   switch (opcode) {
-    case SpvOpSNegate:
-    case SpvOpNot:
-    case SpvOpIAdd:
-    case SpvOpISub:
-    case SpvOpIMul:
-    case SpvOpSDiv:
-    case SpvOpSRem:
-    case SpvOpSMod:
-    case SpvOpShiftRightLogical:
-    case SpvOpShiftRightArithmetic:
-    case SpvOpShiftLeftLogical:
-    case SpvOpBitwiseOr:
-    case SpvOpBitwiseXor:
-    case SpvOpBitwiseAnd:
-    case SpvOpIEqual:
-    case SpvOpINotEqual:
-    case SpvOpULessThan:
-    case SpvOpSLessThan:
-    case SpvOpUGreaterThan:
-    case SpvOpSGreaterThan:
-    case SpvOpULessThanEqual:
-    case SpvOpSLessThanEqual:
-    case SpvOpUGreaterThanEqual:
-    case SpvOpSGreaterThanEqual:
+    case spv::Op::OpSNegate:
+    case spv::Op::OpNot:
+    case spv::Op::OpIAdd:
+    case spv::Op::OpISub:
+    case spv::Op::OpIMul:
+    case spv::Op::OpSDiv:
+    case spv::Op::OpSRem:
+    case spv::Op::OpSMod:
+    case spv::Op::OpShiftRightLogical:
+    case spv::Op::OpShiftRightArithmetic:
+    case spv::Op::OpShiftLeftLogical:
+    case spv::Op::OpBitwiseOr:
+    case spv::Op::OpBitwiseXor:
+    case spv::Op::OpBitwiseAnd:
+    case spv::Op::OpIEqual:
+    case spv::Op::OpINotEqual:
+    case spv::Op::OpULessThan:
+    case spv::Op::OpSLessThan:
+    case spv::Op::OpUGreaterThan:
+    case spv::Op::OpSGreaterThan:
+    case spv::Op::OpULessThanEqual:
+    case spv::Op::OpSLessThanEqual:
+    case spv::Op::OpUGreaterThanEqual:
+    case spv::Op::OpSGreaterThanEqual:
       return true;
 
-    case SpvOpAtomicStore:
-    case SpvOpAtomicExchange:
-    case SpvOpAtomicIAdd:
-    case SpvOpAtomicISub:
-    case SpvOpAtomicSMin:
-    case SpvOpAtomicUMin:
-    case SpvOpAtomicSMax:
-    case SpvOpAtomicUMax:
-    case SpvOpAtomicAnd:
-    case SpvOpAtomicOr:
-    case SpvOpAtomicXor:
-    case SpvOpAtomicFAddEXT:  // Capability AtomicFloat32AddEXT,
+    case spv::Op::OpAtomicStore:
+    case spv::Op::OpAtomicExchange:
+    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:
+    case spv::Op::OpAtomicFAddEXT:  // Capability AtomicFloat32AddEXT,
       // AtomicFloat64AddEXT.
       assert(use_in_operand_index != 0 &&
              "Signedness check should not occur on a pointer operand.");
       return use_in_operand_index == 1 || use_in_operand_index == 2;
 
-    case SpvOpAtomicCompareExchange:
-    case SpvOpAtomicCompareExchangeWeak:  // Capability Kernel.
+    case spv::Op::OpAtomicCompareExchange:
+    case spv::Op::OpAtomicCompareExchangeWeak:  // Capability Kernel.
       assert(use_in_operand_index != 0 &&
              "Signedness check should not occur on a pointer operand.");
       return use_in_operand_index >= 1 && use_in_operand_index <= 3;
 
-    case SpvOpAtomicLoad:
-    case SpvOpAtomicIIncrement:
-    case SpvOpAtomicIDecrement:
-    case SpvOpAtomicFlagTestAndSet:  // Capability Kernel.
-    case SpvOpAtomicFlagClear:       // Capability Kernel.
+    case spv::Op::OpAtomicLoad:
+    case spv::Op::OpAtomicIIncrement:
+    case spv::Op::OpAtomicIDecrement:
+    case spv::Op::OpAtomicFlagTestAndSet:  // Capability Kernel.
+    case spv::Op::OpAtomicFlagClear:       // Capability Kernel.
       assert(use_in_operand_index != 0 &&
              "Signedness check should not occur on a pointer operand.");
       return use_in_operand_index >= 1;
 
-    case SpvOpAccessChain:
+    case spv::Op::OpAccessChain:
       // The signedness of indices does not matter.
       return use_in_operand_index > 0;
 
@@ -2093,7 +2102,7 @@
   }
 }
 
-bool TypesAreCompatible(opt::IRContext* ir_context, SpvOp opcode,
+bool TypesAreCompatible(opt::IRContext* ir_context, spv::Op opcode,
                         uint32_t use_in_operand_index, uint32_t type_id_1,
                         uint32_t type_id_2) {
   assert(ir_context->get_type_mgr()->GetType(type_id_1) &&
diff --git a/source/fuzz/fuzzer_util.h b/source/fuzz/fuzzer_util.h
index 54aa14a..374e32e 100644
--- a/source/fuzz/fuzzer_util.h
+++ b/source/fuzz/fuzzer_util.h
@@ -111,7 +111,7 @@
 // Determines whether it is OK to insert an instruction with opcode |opcode|
 // before |instruction_in_block|.
 bool CanInsertOpcodeBeforeInstruction(
-    SpvOp opcode, const opt::BasicBlock::iterator& instruction_in_block);
+    spv::Op opcode, const opt::BasicBlock::iterator& instruction_in_block);
 
 // Determines whether it is OK to make a synonym of |inst|.
 // |transformation_context| is used to verify that the result id of |inst|
@@ -170,8 +170,8 @@
                                    opt::IRContext* ir_context);
 
 // Returns memory semantics mask for specific storage class.
-SpvMemorySemanticsMask GetMemorySemanticsForStorageClass(
-    SpvStorageClass storage_class);
+spv::MemorySemanticsMask GetMemorySemanticsForStorageClass(
+    spv::StorageClass storage_class);
 
 // Returns true if and only if |context| is valid, according to the validator
 // instantiated with |validator_options|.  |consumer| is used for error
@@ -258,18 +258,18 @@
 
 // Given |pointer_type_inst|, which must be an OpTypePointer instruction,
 // returns the associated storage class.
-SpvStorageClass GetStorageClassFromPointerType(
+spv::StorageClass GetStorageClassFromPointerType(
     opt::Instruction* pointer_type_inst);
 
 // Given |pointer_type_id|, which must be the id of a pointer type, returns the
 // associated storage class.
-SpvStorageClass GetStorageClassFromPointerType(opt::IRContext* context,
-                                               uint32_t pointer_type_id);
+spv::StorageClass GetStorageClassFromPointerType(opt::IRContext* context,
+                                                 uint32_t pointer_type_id);
 
 // Returns the id of a pointer with pointee type |pointee_type_id| and storage
 // class |storage_class|, if it exists, and 0 otherwise.
 uint32_t MaybeGetPointerType(opt::IRContext* context, uint32_t pointee_type_id,
-                             SpvStorageClass storage_class);
+                             spv::StorageClass storage_class);
 
 // Given an instruction |inst| and an operand absolute index |absolute_index|,
 // returns the index of the operand restricted to the input operands.
@@ -309,7 +309,7 @@
 // Returns a pointer to the new global variable instruction.
 opt::Instruction* AddGlobalVariable(opt::IRContext* context, uint32_t result_id,
                                     uint32_t type_id,
-                                    SpvStorageClass storage_class,
+                                    spv::StorageClass storage_class,
                                     uint32_t initializer_id);
 
 // Adds an instruction to the start of |function_id|, of the form:
@@ -541,7 +541,7 @@
 // opcode |opcode| can be inserted, or nullptr if there is no such instruction.
 opt::Instruction* GetLastInsertBeforeInstruction(opt::IRContext* ir_context,
                                                  uint32_t block_id,
-                                                 SpvOp opcode);
+                                                 spv::Op opcode);
 
 // Checks whether various conditions hold related to the acceptability of
 // replacing the id use at |use_in_operand_index| of |use_instruction| with a
@@ -608,14 +608,14 @@
 // behaviour depending on the signedness of the operand at
 // |use_in_operand_index|.
 // Assumes that the operand must be the id of an integer scalar or vector.
-bool IsAgnosticToSignednessOfOperand(SpvOp opcode,
+bool IsAgnosticToSignednessOfOperand(spv::Op opcode,
                                      uint32_t use_in_operand_index);
 
 // Returns true if |type_id_1| and |type_id_2| represent compatible types
 // given the context of the instruction with |opcode| (i.e. we can replace
 // an operand of |opcode| of the first type with an id of the second type
 // and vice-versa).
-bool TypesAreCompatible(opt::IRContext* ir_context, SpvOp opcode,
+bool TypesAreCompatible(opt::IRContext* ir_context, spv::Op opcode,
                         uint32_t use_in_operand_index, uint32_t type_id_1,
                         uint32_t type_id_2);
 
diff --git a/source/fuzz/instruction_descriptor.cpp b/source/fuzz/instruction_descriptor.cpp
index fb1ff76..120d8f8 100644
--- a/source/fuzz/instruction_descriptor.cpp
+++ b/source/fuzz/instruction_descriptor.cpp
@@ -40,8 +40,9 @@
              "The skipped instruction count should only be incremented "
              "after the instruction base has been found.");
     }
-    if (found_base && instruction.opcode() ==
-                          instruction_descriptor.target_instruction_opcode()) {
+    if (found_base &&
+        instruction.opcode() ==
+            spv::Op(instruction_descriptor.target_instruction_opcode())) {
       if (num_ignored == instruction_descriptor.num_opcodes_to_ignore()) {
         return &instruction;
       }
@@ -52,11 +53,11 @@
 }
 
 protobufs::InstructionDescriptor MakeInstructionDescriptor(
-    uint32_t base_instruction_result_id, SpvOp target_instruction_opcode,
+    uint32_t base_instruction_result_id, spv::Op target_instruction_opcode,
     uint32_t num_opcodes_to_ignore) {
   protobufs::InstructionDescriptor result;
   result.set_base_instruction_result_id(base_instruction_result_id);
-  result.set_target_instruction_opcode(target_instruction_opcode);
+  result.set_target_instruction_opcode(uint32_t(target_instruction_opcode));
   result.set_num_opcodes_to_ignore(num_opcodes_to_ignore);
   return result;
 }
@@ -64,7 +65,7 @@
 protobufs::InstructionDescriptor MakeInstructionDescriptor(
     const opt::BasicBlock& block,
     const opt::BasicBlock::const_iterator& inst_it) {
-  const SpvOp opcode =
+  const spv::Op opcode =
       inst_it->opcode();    // The opcode of the instruction being described.
   uint32_t skip_count = 0;  // The number of these opcodes we have skipped when
   // searching backwards.
diff --git a/source/fuzz/instruction_descriptor.h b/source/fuzz/instruction_descriptor.h
index 2ccd15a..063cad4 100644
--- a/source/fuzz/instruction_descriptor.h
+++ b/source/fuzz/instruction_descriptor.h
@@ -32,7 +32,7 @@
 // components.  See the protobuf definition for details of what these
 // components mean.
 protobufs::InstructionDescriptor MakeInstructionDescriptor(
-    uint32_t base_instruction_result_id, SpvOp target_instruction_opcode,
+    uint32_t base_instruction_result_id, spv::Op target_instruction_opcode,
     uint32_t num_opcodes_to_ignore);
 
 // Returns an instruction descriptor that describing the instruction at
diff --git a/source/fuzz/instruction_message.cpp b/source/fuzz/instruction_message.cpp
index 9503932..7e8ac71 100644
--- a/source/fuzz/instruction_message.cpp
+++ b/source/fuzz/instruction_message.cpp
@@ -20,10 +20,10 @@
 namespace fuzz {
 
 protobufs::Instruction MakeInstructionMessage(
-    SpvOp opcode, uint32_t result_type_id, uint32_t result_id,
+    spv::Op opcode, uint32_t result_type_id, uint32_t result_id,
     const opt::Instruction::OperandList& input_operands) {
   protobufs::Instruction result;
-  result.set_opcode(opcode);
+  result.set_opcode(uint32_t(opcode));
   result.set_result_type_id(result_type_id);
   result.set_result_id(result_id);
   for (auto& operand : input_operands) {
@@ -71,7 +71,7 @@
   }
   // Create and return the instruction.
   return MakeUnique<opt::Instruction>(
-      ir_context, static_cast<SpvOp>(instruction_message.opcode()),
+      ir_context, static_cast<spv::Op>(instruction_message.opcode()),
       instruction_message.result_type_id(), instruction_message.result_id(),
       in_operands);
 }
diff --git a/source/fuzz/instruction_message.h b/source/fuzz/instruction_message.h
index fcbb4c7..e1312f4 100644
--- a/source/fuzz/instruction_message.h
+++ b/source/fuzz/instruction_message.h
@@ -26,7 +26,7 @@
 
 // Creates an Instruction protobuf message from its component parts.
 protobufs::Instruction MakeInstructionMessage(
-    SpvOp opcode, uint32_t result_type_id, uint32_t result_id,
+    spv::Op opcode, uint32_t result_type_id, uint32_t result_id,
     const opt::Instruction::OperandList& input_operands);
 
 // Creates an Instruction protobuf message from a parsed instruction.
diff --git a/source/fuzz/protobufs/spirvfuzz_protobufs.h b/source/fuzz/protobufs/spirvfuzz_protobufs.h
index 46c2188..44aecfd 100644
--- a/source/fuzz/protobufs/spirvfuzz_protobufs.h
+++ b/source/fuzz/protobufs/spirvfuzz_protobufs.h
@@ -21,6 +21,10 @@
 // of these header files without having to compromise on freedom from warnings
 // in the rest of the project.
 
+#ifndef GOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE
+#define GOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE 1
+#endif
+
 #if defined(__clang__)
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wunknown-warning-option"  // Must come first
@@ -28,6 +32,8 @@
 #pragma clang diagnostic ignored "-Wshadow"
 #pragma clang diagnostic ignored "-Wsuggest-destructor-override"
 #pragma clang diagnostic ignored "-Wunused-parameter"
+#pragma clang diagnostic ignored "-Wc++98-compat-extra-semi"
+#pragma clang diagnostic ignored "-Wshorten-64-to-32"
 #elif defined(__GNUC__)
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wconversion"
diff --git a/source/fuzz/transformation_access_chain.cpp b/source/fuzz/transformation_access_chain.cpp
index 3fe9e65..1e7f87b 100644
--- a/source/fuzz/transformation_access_chain.cpp
+++ b/source/fuzz/transformation_access_chain.cpp
@@ -63,7 +63,7 @@
   }
   // The type must indeed be a pointer.
   auto pointer_type = ir_context->get_def_use_mgr()->GetDef(pointer->type_id());
-  if (pointer_type->opcode() != SpvOpTypePointer) {
+  if (pointer_type->opcode() != spv::Op::OpTypePointer) {
     return false;
   }
 
@@ -75,7 +75,7 @@
     return false;
   }
   if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(
-          SpvOpAccessChain, instruction_to_insert_before)) {
+          spv::Op::OpAccessChain, instruction_to_insert_before)) {
     return false;
   }
 
@@ -83,8 +83,8 @@
   // we do not want to allow accessing such pointers.  This might be acceptable
   // in dead blocks, but we conservatively avoid it.
   switch (pointer->opcode()) {
-    case SpvOpConstantNull:
-    case SpvOpUndef:
+    case spv::Op::OpConstantNull:
+    case spv::Op::OpUndef:
       assert(
           false &&
           "Access chains should not be created from null/undefined pointers");
@@ -117,7 +117,7 @@
 
     // Check whether the object is a struct.
     if (ir_context->get_def_use_mgr()->GetDef(subobject_type_id)->opcode() ==
-        SpvOpTypeStruct) {
+        spv::Op::OpTypeStruct) {
       // It is a struct: we need to retrieve the integer value.
 
       bool successful;
@@ -202,7 +202,7 @@
   // associated with pointers to isomorphic structs being regarded as the same.
   return fuzzerutil::MaybeGetPointerType(
              ir_context, subobject_type_id,
-             static_cast<SpvStorageClass>(
+             static_cast<spv::StorageClass>(
                  pointer_type->GetSingleWordInOperand(0))) != 0;
 }
 
@@ -243,7 +243,7 @@
 
     // Check whether the object is a struct.
     if (ir_context->get_def_use_mgr()->GetDef(subobject_type_id)->opcode() ==
-        SpvOpTypeStruct) {
+        spv::Op::OpTypeStruct) {
       // It is a struct: we need to retrieve the integer value.
 
       index_value =
@@ -290,7 +290,8 @@
       //   %fresh_ids.first = OpULessThanEqual %bool %int_id %bound_minus_one.
       fuzzerutil::UpdateModuleIdBound(ir_context, fresh_ids.first());
       auto comparison_instruction = MakeUnique<opt::Instruction>(
-          ir_context, SpvOpULessThanEqual, bool_type_id, fresh_ids.first(),
+          ir_context, spv::Op::OpULessThanEqual, bool_type_id,
+          fresh_ids.first(),
           opt::Instruction::OperandList(
               {{SPV_OPERAND_TYPE_ID, {index_instruction->result_id()}},
                {SPV_OPERAND_TYPE_ID, {bound_minus_one_id}}}));
@@ -306,7 +307,7 @@
       //                           %bound_minus_one
       fuzzerutil::UpdateModuleIdBound(ir_context, fresh_ids.second());
       auto select_instruction = MakeUnique<opt::Instruction>(
-          ir_context, SpvOpSelect, int_type_inst->result_id(),
+          ir_context, spv::Op::OpSelect, int_type_inst->result_id(),
           fresh_ids.second(),
           opt::Instruction::OperandList(
               {{SPV_OPERAND_TYPE_ID, {fresh_ids.first()}},
@@ -334,13 +335,14 @@
   // of the original pointer.
   uint32_t result_type = fuzzerutil::MaybeGetPointerType(
       ir_context, subobject_type_id,
-      static_cast<SpvStorageClass>(pointer_type->GetSingleWordInOperand(0)));
+      static_cast<spv::StorageClass>(pointer_type->GetSingleWordInOperand(0)));
 
   // Add the access chain instruction to the module, and update the module's
   // id bound.
   fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
-  auto access_chain_instruction = MakeUnique<opt::Instruction>(
-      ir_context, SpvOpAccessChain, result_type, message_.fresh_id(), operands);
+  auto access_chain_instruction =
+      MakeUnique<opt::Instruction>(ir_context, spv::Op::OpAccessChain,
+                                   result_type, message_.fresh_id(), operands);
   auto access_chain_instruction_ptr = access_chain_instruction.get();
   instruction_to_insert_before->InsertBefore(
       std::move(access_chain_instruction));
@@ -367,7 +369,7 @@
     opt::IRContext* ir_context, uint32_t index_id,
     uint32_t object_type_id) const {
   assert(ir_context->get_def_use_mgr()->GetDef(object_type_id)->opcode() ==
-             SpvOpTypeStruct &&
+             spv::Op::OpTypeStruct &&
          "Precondition: the type must be a struct type.");
   if (!ValidIndexToComposite(ir_context, index_id, object_type_id)) {
     return {false, 0};
@@ -408,14 +410,14 @@
   // The index type must be 32-bit integer.
   auto index_type =
       ir_context->get_def_use_mgr()->GetDef(index_instruction->type_id());
-  if (index_type->opcode() != SpvOpTypeInt ||
+  if (index_type->opcode() != spv::Op::OpTypeInt ||
       index_type->GetSingleWordInOperand(0) != 32) {
     return false;
   }
 
   // If the object being traversed is a struct, the id must correspond to an
   // in-bound constant.
-  if (object_type_def->opcode() == SpvOpTypeStruct) {
+  if (object_type_def->opcode() == spv::Op::OpTypeStruct) {
     if (!spvOpcodeIsConstant(index_instruction->opcode())) {
       return false;
     }
diff --git a/source/fuzz/transformation_add_bit_instruction_synonym.cpp b/source/fuzz/transformation_add_bit_instruction_synonym.cpp
index 636c0a3..4b26c2b 100644
--- a/source/fuzz/transformation_add_bit_instruction_synonym.cpp
+++ b/source/fuzz/transformation_add_bit_instruction_synonym.cpp
@@ -87,10 +87,10 @@
   // synonym fact.  The helper function should take care of invalidating
   // analyses before adding facts.
   switch (bit_instruction->opcode()) {
-    case SpvOpBitwiseOr:
-    case SpvOpBitwiseXor:
-    case SpvOpBitwiseAnd:
-    case SpvOpNot:
+    case spv::Op::OpBitwiseOr:
+    case spv::Op::OpBitwiseXor:
+    case spv::Op::OpBitwiseAnd:
+    case spv::Op::OpNot:
       AddOpBitwiseOrOpNotSynonym(ir_context, transformation_context,
                                  bit_instruction);
       break;
@@ -106,10 +106,10 @@
   //  Right now we only support certain operations. When this issue is addressed
   //  the following conditional can use the function |spvOpcodeIsBit|.
   // |instruction| must be defined and must be a supported bit instruction.
-  if (!instruction || (instruction->opcode() != SpvOpBitwiseOr &&
-                       instruction->opcode() != SpvOpBitwiseXor &&
-                       instruction->opcode() != SpvOpBitwiseAnd &&
-                       instruction->opcode() != SpvOpNot)) {
+  if (!instruction || (instruction->opcode() != spv::Op::OpBitwiseOr &&
+                       instruction->opcode() != spv::Op::OpBitwiseXor &&
+                       instruction->opcode() != spv::Op::OpBitwiseAnd &&
+                       instruction->opcode() != spv::Op::OpNot)) {
     return false;
   }
 
@@ -119,7 +119,7 @@
     return false;
   }
 
-  if (instruction->opcode() == SpvOpNot) {
+  if (instruction->opcode() == spv::Op::OpNot) {
     auto operand = instruction->GetInOperand(0).words[0];
     auto operand_inst = ir_context->get_def_use_mgr()->GetDef(operand);
     auto operand_type =
@@ -171,10 +171,10 @@
   // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3557):
   //  Right now, only certain operations are supported.
   switch (bit_instruction->opcode()) {
-    case SpvOpBitwiseOr:
-    case SpvOpBitwiseXor:
-    case SpvOpBitwiseAnd:
-    case SpvOpNot:
+    case spv::Op::OpBitwiseOr:
+    case spv::Op::OpBitwiseXor:
+    case spv::Op::OpBitwiseAnd:
+    case spv::Op::OpNot:
       return (2 + bit_instruction->NumInOperands()) *
                  ir_context->get_type_mgr()
                      ->GetType(bit_instruction->type_id())
@@ -220,7 +220,7 @@
     for (auto operand = bit_instruction->begin() + 2;
          operand != bit_instruction->end(); operand++) {
       auto bit_extract =
-          opt::Instruction(ir_context, SpvOpBitFieldUExtract,
+          opt::Instruction(ir_context, spv::Op::OpBitFieldUExtract,
                            bit_instruction->type_id(), *fresh_id++,
                            {{SPV_OPERAND_TYPE_ID, operand->words},
                             {SPV_OPERAND_TYPE_ID, {offset}},
@@ -246,12 +246,13 @@
   // first two bits of the result.
   uint32_t offset = fuzzerutil::MaybeGetIntegerConstant(
       ir_context, *transformation_context, {1}, 32, false, false);
-  auto bit_insert = opt::Instruction(
-      ir_context, SpvOpBitFieldInsert, bit_instruction->type_id(), *fresh_id++,
-      {{SPV_OPERAND_TYPE_ID, {extracted_bit_instructions[0]}},
-       {SPV_OPERAND_TYPE_ID, {extracted_bit_instructions[1]}},
-       {SPV_OPERAND_TYPE_ID, {offset}},
-       {SPV_OPERAND_TYPE_ID, {count}}});
+  auto bit_insert =
+      opt::Instruction(ir_context, spv::Op::OpBitFieldInsert,
+                       bit_instruction->type_id(), *fresh_id++,
+                       {{SPV_OPERAND_TYPE_ID, {extracted_bit_instructions[0]}},
+                        {SPV_OPERAND_TYPE_ID, {extracted_bit_instructions[1]}},
+                        {SPV_OPERAND_TYPE_ID, {offset}},
+                        {SPV_OPERAND_TYPE_ID, {count}}});
   bit_instruction->InsertBefore(MakeUnique<opt::Instruction>(bit_insert));
   fuzzerutil::UpdateModuleIdBound(ir_context, bit_insert.result_id());
 
@@ -260,7 +261,7 @@
     offset = fuzzerutil::MaybeGetIntegerConstant(
         ir_context, *transformation_context, {i}, 32, false, false);
     bit_insert = opt::Instruction(
-        ir_context, SpvOpBitFieldInsert, bit_instruction->type_id(),
+        ir_context, spv::Op::OpBitFieldInsert, bit_instruction->type_id(),
         *fresh_id++,
         {{SPV_OPERAND_TYPE_ID, {bit_insert.result_id()}},
          {SPV_OPERAND_TYPE_ID, {extracted_bit_instructions[i]}},
diff --git a/source/fuzz/transformation_add_constant_boolean.cpp b/source/fuzz/transformation_add_constant_boolean.cpp
index 3935432..89c2e57 100644
--- a/source/fuzz/transformation_add_constant_boolean.cpp
+++ b/source/fuzz/transformation_add_constant_boolean.cpp
@@ -43,7 +43,8 @@
   // Add the boolean constant to the module, ensuring the module's id bound is
   // high enough.
   auto new_instruction = MakeUnique<opt::Instruction>(
-      ir_context, message_.is_true() ? SpvOpConstantTrue : SpvOpConstantFalse,
+      ir_context,
+      message_.is_true() ? spv::Op::OpConstantTrue : spv::Op::OpConstantFalse,
       fuzzerutil::MaybeGetBoolType(ir_context), message_.fresh_id(),
       opt::Instruction::OperandList());
   auto new_instruction_ptr = new_instruction.get();
diff --git a/source/fuzz/transformation_add_constant_composite.cpp b/source/fuzz/transformation_add_constant_composite.cpp
index 89007ab..b8ee8be 100644
--- a/source/fuzz/transformation_add_constant_composite.cpp
+++ b/source/fuzz/transformation_add_constant_composite.cpp
@@ -53,7 +53,7 @@
   // struct - whether its decorations are OK.
   std::vector<uint32_t> constituent_type_ids;
   switch (composite_type_instruction->opcode()) {
-    case SpvOpTypeArray:
+    case spv::Op::OpTypeArray:
       for (uint32_t index = 0;
            index <
            fuzzerutil::GetArraySize(*composite_type_instruction, ir_context);
@@ -62,8 +62,8 @@
             composite_type_instruction->GetSingleWordInOperand(0));
       }
       break;
-    case SpvOpTypeMatrix:
-    case SpvOpTypeVector:
+    case spv::Op::OpTypeMatrix:
+    case spv::Op::OpTypeVector:
       for (uint32_t index = 0;
            index < composite_type_instruction->GetSingleWordInOperand(1);
            index++) {
@@ -71,7 +71,7 @@
             composite_type_instruction->GetSingleWordInOperand(0));
       }
       break;
-    case SpvOpTypeStruct:
+    case spv::Op::OpTypeStruct:
       // We do not create constants of structs decorated with Block nor
       // BufferBlock.  The SPIR-V spec does not explicitly disallow this, but it
       // seems like a strange thing to do, so we disallow it to avoid triggering
@@ -120,7 +120,7 @@
     in_operands.push_back({SPV_OPERAND_TYPE_ID, {constituent_id}});
   }
   auto new_instruction = MakeUnique<opt::Instruction>(
-      ir_context, SpvOpConstantComposite, message_.type_id(),
+      ir_context, spv::Op::OpConstantComposite, message_.type_id(),
       message_.fresh_id(), in_operands);
   auto new_instruction_ptr = new_instruction.get();
   ir_context->module()->AddGlobalValue(std::move(new_instruction));
diff --git a/source/fuzz/transformation_add_constant_null.cpp b/source/fuzz/transformation_add_constant_null.cpp
index c0f7367..7b83bae 100644
--- a/source/fuzz/transformation_add_constant_null.cpp
+++ b/source/fuzz/transformation_add_constant_null.cpp
@@ -48,8 +48,8 @@
 void TransformationAddConstantNull::Apply(
     opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
   auto new_instruction = MakeUnique<opt::Instruction>(
-      ir_context, SpvOpConstantNull, message_.type_id(), message_.fresh_id(),
-      opt::Instruction::OperandList());
+      ir_context, spv::Op::OpConstantNull, message_.type_id(),
+      message_.fresh_id(), opt::Instruction::OperandList());
   auto new_instruction_ptr = new_instruction.get();
   ir_context->module()->AddGlobalValue(std::move(new_instruction));
   fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
diff --git a/source/fuzz/transformation_add_constant_scalar.cpp b/source/fuzz/transformation_add_constant_scalar.cpp
index a2d95fb..45989d4 100644
--- a/source/fuzz/transformation_add_constant_scalar.cpp
+++ b/source/fuzz/transformation_add_constant_scalar.cpp
@@ -65,7 +65,7 @@
     opt::IRContext* ir_context,
     TransformationContext* transformation_context) const {
   auto new_instruction = MakeUnique<opt::Instruction>(
-      ir_context, SpvOpConstant, message_.type_id(), message_.fresh_id(),
+      ir_context, spv::Op::OpConstant, message_.type_id(), message_.fresh_id(),
       opt::Instruction::OperandList(
           {{SPV_OPERAND_TYPE_LITERAL_INTEGER,
             std::vector<uint32_t>(message_.word().begin(),
diff --git a/source/fuzz/transformation_add_copy_memory.cpp b/source/fuzz/transformation_add_copy_memory.cpp
index 5eb4bdc..9d1f325 100644
--- a/source/fuzz/transformation_add_copy_memory.cpp
+++ b/source/fuzz/transformation_add_copy_memory.cpp
@@ -27,12 +27,12 @@
 
 TransformationAddCopyMemory::TransformationAddCopyMemory(
     const protobufs::InstructionDescriptor& instruction_descriptor,
-    uint32_t fresh_id, uint32_t source_id, SpvStorageClass storage_class,
+    uint32_t fresh_id, uint32_t source_id, spv::StorageClass storage_class,
     uint32_t initializer_id) {
   *message_.mutable_instruction_descriptor() = instruction_descriptor;
   message_.set_fresh_id(fresh_id);
   message_.set_source_id(source_id);
-  message_.set_storage_class(storage_class);
+  message_.set_storage_class(uint32_t(storage_class));
   message_.set_initializer_id(initializer_id);
 }
 
@@ -53,7 +53,8 @@
   // Check that we can insert OpCopyMemory before |instruction_descriptor|.
   auto iter = fuzzerutil::GetIteratorForInstruction(
       ir_context->get_instr_block(inst), inst);
-  if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCopyMemory, iter)) {
+  if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(spv::Op::OpCopyMemory,
+                                                    iter)) {
     return false;
   }
 
@@ -65,8 +66,10 @@
   }
 
   // |storage_class| is either Function or Private.
-  if (message_.storage_class() != SpvStorageClassFunction &&
-      message_.storage_class() != SpvStorageClassPrivate) {
+  if (spv::StorageClass(message_.storage_class()) !=
+          spv::StorageClass::Function &&
+      spv::StorageClass(message_.storage_class()) !=
+          spv::StorageClass::Private) {
     return false;
   }
 
@@ -76,7 +79,7 @@
   // OpTypePointer with |message_.storage_class| exists.
   if (!fuzzerutil::MaybeGetPointerType(
           ir_context, pointee_type_id,
-          static_cast<SpvStorageClass>(message_.storage_class()))) {
+          static_cast<spv::StorageClass>(message_.storage_class()))) {
     return false;
   }
 
@@ -103,20 +106,20 @@
       ir_context->get_instr_block(insert_before_inst);
 
   // Add global or local variable to copy memory into.
-  auto storage_class = static_cast<SpvStorageClass>(message_.storage_class());
+  auto storage_class = static_cast<spv::StorageClass>(message_.storage_class());
   auto type_id = fuzzerutil::MaybeGetPointerType(
       ir_context,
       fuzzerutil::GetPointeeTypeIdFromPointerType(
           ir_context, fuzzerutil::GetTypeId(ir_context, message_.source_id())),
       storage_class);
 
-  if (storage_class == SpvStorageClassPrivate) {
+  if (storage_class == spv::StorageClass::Private) {
     opt::Instruction* new_global =
         fuzzerutil::AddGlobalVariable(ir_context, message_.fresh_id(), type_id,
                                       storage_class, message_.initializer_id());
     ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_global);
   } else {
-    assert(storage_class == SpvStorageClassFunction &&
+    assert(storage_class == spv::StorageClass::Function &&
            "Storage class can be either Private or Function");
     opt::Function* enclosing_function = enclosing_block->GetParent();
     opt::Instruction* new_local = fuzzerutil::AddLocalVariable(
@@ -130,7 +133,7 @@
       enclosing_block, insert_before_inst);
 
   auto new_instruction = MakeUnique<opt::Instruction>(
-      ir_context, SpvOpCopyMemory, 0, 0,
+      ir_context, spv::Op::OpCopyMemory, 0, 0,
       opt::Instruction::OperandList{
           {SPV_OPERAND_TYPE_ID, {message_.fresh_id()}},
           {SPV_OPERAND_TYPE_ID, {message_.source_id()}}});
@@ -160,7 +163,8 @@
 bool TransformationAddCopyMemory::IsInstructionSupported(
     opt::IRContext* ir_context, opt::Instruction* inst) {
   if (!inst->result_id() || !inst->type_id() ||
-      inst->opcode() == SpvOpConstantNull || inst->opcode() == SpvOpUndef) {
+      inst->opcode() == spv::Op::OpConstantNull ||
+      inst->opcode() == spv::Op::OpUndef) {
     return false;
   }
 
diff --git a/source/fuzz/transformation_add_copy_memory.h b/source/fuzz/transformation_add_copy_memory.h
index b25652f..053b629 100644
--- a/source/fuzz/transformation_add_copy_memory.h
+++ b/source/fuzz/transformation_add_copy_memory.h
@@ -30,7 +30,7 @@
 
   TransformationAddCopyMemory(
       const protobufs::InstructionDescriptor& instruction_descriptor,
-      uint32_t fresh_id, uint32_t source_id, SpvStorageClass storage_class,
+      uint32_t fresh_id, uint32_t source_id, spv::StorageClass storage_class,
       uint32_t initializer_id);
 
   // - |instruction_descriptor| must point to a valid instruction in the module.
diff --git a/source/fuzz/transformation_add_dead_block.cpp b/source/fuzz/transformation_add_dead_block.cpp
index df700ce..930adc1 100644
--- a/source/fuzz/transformation_add_dead_block.cpp
+++ b/source/fuzz/transformation_add_dead_block.cpp
@@ -61,7 +61,7 @@
   }
 
   // It must end with OpBranch.
-  if (existing_block->terminator()->opcode() != SpvOpBranch) {
+  if (existing_block->terminator()->opcode() != spv::Op::OpBranch) {
     return false;
   }
 
@@ -122,27 +122,27 @@
   auto enclosing_function = existing_block->GetParent();
   std::unique_ptr<opt::BasicBlock> new_block =
       MakeUnique<opt::BasicBlock>(MakeUnique<opt::Instruction>(
-          ir_context, SpvOpLabel, 0, message_.fresh_id(),
+          ir_context, spv::Op::OpLabel, 0, message_.fresh_id(),
           opt::Instruction::OperandList()));
   new_block->AddInstruction(MakeUnique<opt::Instruction>(
-      ir_context, SpvOpBranch, 0, 0,
+      ir_context, spv::Op::OpBranch, 0, 0,
       opt::Instruction::OperandList(
           {{SPV_OPERAND_TYPE_ID, {successor_block_id}}})));
 
   // Turn the original block into a selection merge, with its original successor
   // as the merge block.
   existing_block->terminator()->InsertBefore(MakeUnique<opt::Instruction>(
-      ir_context, SpvOpSelectionMerge, 0, 0,
+      ir_context, spv::Op::OpSelectionMerge, 0, 0,
       opt::Instruction::OperandList(
           {{SPV_OPERAND_TYPE_ID, {successor_block_id}},
            {SPV_OPERAND_TYPE_SELECTION_CONTROL,
-            {SpvSelectionControlMaskNone}}})));
+            {uint32_t(spv::SelectionControlMask::MaskNone)}}})));
 
   // Change the original block's terminator to be a conditional branch on the
   // given boolean, with the original successor and the new successor as branch
   // targets, and such that at runtime control will always transfer to the
   // original successor.
-  existing_block->terminator()->SetOpcode(SpvOpBranchConditional);
+  existing_block->terminator()->SetOpcode(spv::Op::OpBranchConditional);
   existing_block->terminator()->SetInOperands(
       {{SPV_OPERAND_TYPE_ID, {bool_id}},
        {SPV_OPERAND_TYPE_ID,
diff --git a/source/fuzz/transformation_add_dead_break.cpp b/source/fuzz/transformation_add_dead_break.cpp
index 32080ca..07ed4dc 100644
--- a/source/fuzz/transformation_add_dead_break.cpp
+++ b/source/fuzz/transformation_add_dead_break.cpp
@@ -142,7 +142,7 @@
   }
 
   // Check that |message_.from_block| ends with an unconditional branch.
-  if (bb_from->terminator()->opcode() != SpvOpBranch) {
+  if (bb_from->terminator()->opcode() != spv::Op::OpBranch) {
     // The block associated with the id does not end with an unconditional
     // branch.
     return false;
diff --git a/source/fuzz/transformation_add_dead_continue.cpp b/source/fuzz/transformation_add_dead_continue.cpp
index f2b9ab3..c534801 100644
--- a/source/fuzz/transformation_add_dead_continue.cpp
+++ b/source/fuzz/transformation_add_dead_continue.cpp
@@ -55,7 +55,7 @@
   }
 
   // Check that |message_.from_block| ends with an unconditional branch.
-  if (bb_from->terminator()->opcode() != SpvOpBranch) {
+  if (bb_from->terminator()->opcode() != spv::Op::OpBranch) {
     // The block associated with the id does not end with an unconditional
     // branch.
     return false;
diff --git a/source/fuzz/transformation_add_early_terminator_wrapper.cpp b/source/fuzz/transformation_add_early_terminator_wrapper.cpp
index 547398a..28e0186 100644
--- a/source/fuzz/transformation_add_early_terminator_wrapper.cpp
+++ b/source/fuzz/transformation_add_early_terminator_wrapper.cpp
@@ -28,17 +28,17 @@
 TransformationAddEarlyTerminatorWrapper::
     TransformationAddEarlyTerminatorWrapper(uint32_t function_fresh_id,
                                             uint32_t label_fresh_id,
-                                            SpvOp opcode) {
+                                            spv::Op opcode) {
   message_.set_function_fresh_id(function_fresh_id);
   message_.set_label_fresh_id(label_fresh_id);
-  message_.set_opcode(opcode);
+  message_.set_opcode(uint32_t(opcode));
 }
 
 bool TransformationAddEarlyTerminatorWrapper::IsApplicable(
     opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
-  assert((message_.opcode() == SpvOpKill ||
-          message_.opcode() == SpvOpUnreachable ||
-          message_.opcode() == SpvOpTerminateInvocation) &&
+  assert((spv::Op(message_.opcode()) == spv::Op::OpKill ||
+          spv::Op(message_.opcode()) == spv::Op::OpUnreachable ||
+          spv::Op(message_.opcode()) == spv::Op::OpTerminateInvocation) &&
          "Invalid opcode.");
 
   if (!fuzzerutil::IsFreshId(ir_context, message_.function_fresh_id())) {
@@ -66,26 +66,29 @@
   // %label_fresh_id = OpLabel
   //                   OpKill|Unreachable|TerminateInvocation
   auto basic_block = MakeUnique<opt::BasicBlock>(MakeUnique<opt::Instruction>(
-      ir_context, SpvOpLabel, 0, message_.label_fresh_id(),
+      ir_context, spv::Op::OpLabel, 0, message_.label_fresh_id(),
       opt::Instruction::OperandList()));
   basic_block->AddInstruction(MakeUnique<opt::Instruction>(
-      ir_context, static_cast<SpvOp>(message_.opcode()), 0, 0,
+      ir_context, static_cast<spv::Op>(message_.opcode()), 0, 0,
       opt::Instruction::OperandList()));
 
   // Create a zero-argument void function.
   auto void_type_id = fuzzerutil::MaybeGetVoidType(ir_context);
   auto function = MakeUnique<opt::Function>(MakeUnique<opt::Instruction>(
-      ir_context, SpvOpFunction, void_type_id, message_.function_fresh_id(),
+      ir_context, spv::Op::OpFunction, void_type_id,
+      message_.function_fresh_id(),
       opt::Instruction::OperandList(
-          {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}},
+          {{SPV_OPERAND_TYPE_FUNCTION_CONTROL,
+            {uint32_t(spv::FunctionControlMask::MaskNone)}},
            {SPV_OPERAND_TYPE_TYPE_ID,
             {fuzzerutil::FindFunctionType(ir_context, {void_type_id})}}})));
 
   // Add the basic block to the function as the sole block, and add the function
   // to the module.
   function->AddBasicBlock(std::move(basic_block));
-  function->SetFunctionEnd(MakeUnique<opt::Instruction>(
-      ir_context, SpvOpFunctionEnd, 0, 0, opt::Instruction::OperandList()));
+  function->SetFunctionEnd(
+      MakeUnique<opt::Instruction>(ir_context, spv::Op::OpFunctionEnd, 0, 0,
+                                   opt::Instruction::OperandList()));
   ir_context->module()->AddFunction(std::move(function));
 
   ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
diff --git a/source/fuzz/transformation_add_early_terminator_wrapper.h b/source/fuzz/transformation_add_early_terminator_wrapper.h
index 97cc527..5d0201d 100644
--- a/source/fuzz/transformation_add_early_terminator_wrapper.h
+++ b/source/fuzz/transformation_add_early_terminator_wrapper.h
@@ -30,7 +30,7 @@
 
   TransformationAddEarlyTerminatorWrapper(uint32_t function_fresh_id,
                                           uint32_t label_fresh_id,
-                                          SpvOp opcode);
+                                          spv::Op opcode);
 
   // - |message_.function_fresh_id| and |message_.label_fresh_id| must be fresh
   //   and distinct.
diff --git a/source/fuzz/transformation_add_function.cpp b/source/fuzz/transformation_add_function.cpp
index 06cd657..1f61ede 100644
--- a/source/fuzz/transformation_add_function.cpp
+++ b/source/fuzz/transformation_add_function.cpp
@@ -178,7 +178,7 @@
   }
   ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
 
-  assert(message_.instruction(0).opcode() == SpvOpFunction &&
+  assert(spv::Op(message_.instruction(0).opcode()) == spv::Op::OpFunction &&
          "The first instruction of an 'add function' transformation must be "
          "OpFunction.");
 
@@ -189,7 +189,7 @@
   } else {
     // Inform the fact manager that all blocks in the function are dead.
     for (auto& inst : message_.instruction()) {
-      if (inst.opcode() == SpvOpLabel) {
+      if (spv::Op(inst.opcode()) == spv::Op::OpLabel) {
         transformation_context->GetFactManager()->AddFactBlockIsDead(
             inst.result_id());
       }
@@ -202,16 +202,16 @@
   // parameters to other functions knowing that it is OK if they get
   // over-written.
   for (auto& instruction : message_.instruction()) {
-    switch (instruction.opcode()) {
-      case SpvOpFunctionParameter:
+    switch (spv::Op(instruction.opcode())) {
+      case spv::Op::OpFunctionParameter:
         if (ir_context->get_def_use_mgr()
                 ->GetDef(instruction.result_type_id())
-                ->opcode() == SpvOpTypePointer) {
+                ->opcode() == spv::Op::OpTypePointer) {
           transformation_context->GetFactManager()
               ->AddFactValueOfPointeeIsIrrelevant(instruction.result_id());
         }
         break;
-      case SpvOpVariable:
+      case spv::Op::OpVariable:
         transformation_context->GetFactManager()
             ->AddFactValueOfPointeeIsIrrelevant(instruction.result_id());
         break;
@@ -239,7 +239,7 @@
 
   // A function must start with OpFunction.
   auto function_begin = message_.instruction(0);
-  if (function_begin.opcode() != SpvOpFunction) {
+  if (spv::Op(function_begin.opcode()) != spv::Op::OpFunction) {
     return false;
   }
 
@@ -256,8 +256,8 @@
   // Iterate through all function parameter instructions, adding parameters to
   // the new function.
   while (instruction_index < num_instructions &&
-         message_.instruction(instruction_index).opcode() ==
-             SpvOpFunctionParameter) {
+         spv::Op(message_.instruction(instruction_index).opcode()) ==
+             spv::Op::OpFunctionParameter) {
     new_function->AddParameter(InstructionFromMessage(
         ir_context, message_.instruction(instruction_index)));
     instruction_index++;
@@ -265,16 +265,19 @@
 
   // After the parameters, there needs to be a label.
   if (instruction_index == num_instructions ||
-      message_.instruction(instruction_index).opcode() != SpvOpLabel) {
+      spv::Op(message_.instruction(instruction_index).opcode()) !=
+          spv::Op::OpLabel) {
     return false;
   }
 
   // Iterate through the instructions block by block until the end of the
   // function is reached.
   while (instruction_index < num_instructions &&
-         message_.instruction(instruction_index).opcode() != SpvOpFunctionEnd) {
+         spv::Op(message_.instruction(instruction_index).opcode()) !=
+             spv::Op::OpFunctionEnd) {
     // Invariant: we should always be at a label instruction at this point.
-    assert(message_.instruction(instruction_index).opcode() == SpvOpLabel);
+    assert(spv::Op(message_.instruction(instruction_index).opcode()) ==
+           spv::Op::OpLabel);
 
     // Make a basic block using the label instruction.
     std::unique_ptr<opt::BasicBlock> block =
@@ -285,9 +288,10 @@
     // of the function, adding each such instruction to the block.
     instruction_index++;
     while (instruction_index < num_instructions &&
-           message_.instruction(instruction_index).opcode() !=
-               SpvOpFunctionEnd &&
-           message_.instruction(instruction_index).opcode() != SpvOpLabel) {
+           spv::Op(message_.instruction(instruction_index).opcode()) !=
+               spv::Op::OpFunctionEnd &&
+           spv::Op(message_.instruction(instruction_index).opcode()) !=
+               spv::Op::OpLabel) {
       block->AddInstruction(InstructionFromMessage(
           ir_context, message_.instruction(instruction_index)));
       instruction_index++;
@@ -298,7 +302,8 @@
   // Having considered all the blocks, we should be at the last instruction and
   // it needs to be OpFunctionEnd.
   if (instruction_index != num_instructions - 1 ||
-      message_.instruction(instruction_index).opcode() != SpvOpFunctionEnd) {
+      spv::Op(message_.instruction(instruction_index).opcode()) !=
+          spv::Op::OpFunctionEnd) {
     return false;
   }
   // Set the function's final instruction, add the function to the module and
@@ -339,20 +344,20 @@
   for (auto& block : *added_function) {
     for (auto& inst : block) {
       switch (inst.opcode()) {
-        case SpvOpKill:
-        case SpvOpUnreachable:
+        case spv::Op::OpKill:
+        case spv::Op::OpUnreachable:
           if (!TryToTurnKillOrUnreachableIntoReturn(ir_context, added_function,
                                                     &inst)) {
             return false;
           }
           break;
-        case SpvOpAccessChain:
-        case SpvOpInBoundsAccessChain:
+        case spv::Op::OpAccessChain:
+        case spv::Op::OpInBoundsAccessChain:
           if (!TryToClampAccessChainIndices(ir_context, &inst)) {
             return false;
           }
           break;
-        case SpvOpFunctionCall:
+        case spv::Op::OpFunctionCall:
           // A livesafe function my only call other livesafe functions.
           if (!transformation_context.GetFactManager()->FunctionIsLivesafe(
                   inst.GetSingleWordInOperand(0))) {
@@ -404,7 +409,7 @@
   auto loop_limit_constant_id_instr =
       ir_context->get_def_use_mgr()->GetDef(message_.loop_limit_constant_id());
   if (!loop_limit_constant_id_instr ||
-      loop_limit_constant_id_instr->opcode() != SpvOpConstant) {
+      loop_limit_constant_id_instr->opcode() != spv::Op::OpConstant) {
     // The loop limit constant id instruction must exist and have an
     // appropriate opcode.
     return false;
@@ -412,7 +417,7 @@
 
   auto loop_limit_type = ir_context->get_def_use_mgr()->GetDef(
       loop_limit_constant_id_instr->type_id());
-  if (loop_limit_type->opcode() != SpvOpTypeInt ||
+  if (loop_limit_type->opcode() != spv::Op::OpTypeInt ||
       loop_limit_type->GetSingleWordInOperand(0) != 32) {
     // The type of the loop limit constant must be 32-bit integer.  It
     // doesn't actually matter whether the integer is signed or not.
@@ -457,7 +462,7 @@
 
   // Look for pointer-to-unsigned int type.
   opt::analysis::Pointer pointer_to_unsigned_int_type(
-      registered_unsigned_int_type, SpvStorageClassFunction);
+      registered_unsigned_int_type, spv::StorageClass::Function);
   uint32_t pointer_to_unsigned_int_type_id =
       ir_context->get_type_mgr()->GetId(&pointer_to_unsigned_int_type);
   if (!pointer_to_unsigned_int_type_id) {
@@ -477,13 +482,13 @@
 
   // Declare the loop limiter variable at the start of the function's entry
   // block, via an instruction of the form:
-  //   %loop_limiter_var = SpvOpVariable %ptr_to_uint Function %zero
+  //   %loop_limiter_var = spv::Op::OpVariable %ptr_to_uint Function %zero
   added_function->begin()->begin()->InsertBefore(MakeUnique<opt::Instruction>(
-      ir_context, SpvOpVariable, pointer_to_unsigned_int_type_id,
+      ir_context, spv::Op::OpVariable, pointer_to_unsigned_int_type_id,
       message_.loop_limiter_variable_id(),
-      opt::Instruction::OperandList(
-          {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}},
-           {SPV_OPERAND_TYPE_ID, {zero_id}}})));
+      opt::Instruction::OperandList({{SPV_OPERAND_TYPE_STORAGE_CLASS,
+                                      {uint32_t(spv::StorageClass::Function)}},
+                                     {SPV_OPERAND_TYPE_ID, {zero_id}}})));
   // Update the module's id bound since we have added the loop limiter
   // variable id.
   fuzzerutil::UpdateModuleIdBound(ir_context,
@@ -589,10 +594,11 @@
     auto back_edge_block = ir_context->cfg()->block(back_edge_block_id);
     auto back_edge_block_terminator = back_edge_block->terminator();
     bool compare_using_greater_than_equal;
-    if (back_edge_block_terminator->opcode() == SpvOpBranch) {
+    if (back_edge_block_terminator->opcode() == spv::Op::OpBranch) {
       compare_using_greater_than_equal = true;
     } else {
-      assert(back_edge_block_terminator->opcode() == SpvOpBranchConditional);
+      assert(back_edge_block_terminator->opcode() ==
+             spv::Op::OpBranchConditional);
       assert(((back_edge_block_terminator->GetSingleWordInOperand(1) ==
                    loop_header->id() &&
                back_edge_block_terminator->GetSingleWordInOperand(2) ==
@@ -613,7 +619,7 @@
     // Add a load from the loop limiter variable, of the form:
     //   %t1 = OpLoad %uint32 %loop_limiter
     new_instructions.push_back(MakeUnique<opt::Instruction>(
-        ir_context, SpvOpLoad, unsigned_int_type_id,
+        ir_context, spv::Op::OpLoad, unsigned_int_type_id,
         loop_limiter_info.load_id(),
         opt::Instruction::OperandList(
             {{SPV_OPERAND_TYPE_ID, {message_.loop_limiter_variable_id()}}})));
@@ -621,7 +627,7 @@
     // Increment the loaded value:
     //   %t2 = OpIAdd %uint32 %t1 %one
     new_instructions.push_back(MakeUnique<opt::Instruction>(
-        ir_context, SpvOpIAdd, unsigned_int_type_id,
+        ir_context, spv::Op::OpIAdd, unsigned_int_type_id,
         loop_limiter_info.increment_id(),
         opt::Instruction::OperandList(
             {{SPV_OPERAND_TYPE_ID, {loop_limiter_info.load_id()}},
@@ -630,7 +636,7 @@
     // Store the incremented value back to the loop limiter variable:
     //   OpStore %loop_limiter %t2
     new_instructions.push_back(MakeUnique<opt::Instruction>(
-        ir_context, SpvOpStore, 0, 0,
+        ir_context, spv::Op::OpStore, 0, 0,
         opt::Instruction::OperandList(
             {{SPV_OPERAND_TYPE_ID, {message_.loop_limiter_variable_id()}},
              {SPV_OPERAND_TYPE_ID, {loop_limiter_info.increment_id()}}})));
@@ -641,17 +647,18 @@
     //   %t3 = OpULessThan %bool %t1 %loop_limit
     new_instructions.push_back(MakeUnique<opt::Instruction>(
         ir_context,
-        compare_using_greater_than_equal ? SpvOpUGreaterThanEqual
-                                         : SpvOpULessThan,
+        compare_using_greater_than_equal ? spv::Op::OpUGreaterThanEqual
+                                         : spv::Op::OpULessThan,
         bool_type_id, loop_limiter_info.compare_id(),
         opt::Instruction::OperandList(
             {{SPV_OPERAND_TYPE_ID, {loop_limiter_info.load_id()}},
              {SPV_OPERAND_TYPE_ID, {message_.loop_limit_constant_id()}}})));
 
-    if (back_edge_block_terminator->opcode() == SpvOpBranchConditional) {
+    if (back_edge_block_terminator->opcode() == spv::Op::OpBranchConditional) {
       new_instructions.push_back(MakeUnique<opt::Instruction>(
           ir_context,
-          compare_using_greater_than_equal ? SpvOpLogicalOr : SpvOpLogicalAnd,
+          compare_using_greater_than_equal ? spv::Op::OpLogicalOr
+                                           : spv::Op::OpLogicalAnd,
           bool_type_id, loop_limiter_info.logical_op_id(),
           opt::Instruction::OperandList(
               {{SPV_OPERAND_TYPE_ID,
@@ -669,11 +676,11 @@
       back_edge_block_terminator->InsertBefore(std::move(new_instructions));
     }
 
-    if (back_edge_block_terminator->opcode() == SpvOpBranchConditional) {
+    if (back_edge_block_terminator->opcode() == spv::Op::OpBranchConditional) {
       back_edge_block_terminator->SetInOperand(
           0, {loop_limiter_info.logical_op_id()});
     } else {
-      assert(back_edge_block_terminator->opcode() == SpvOpBranch &&
+      assert(back_edge_block_terminator->opcode() == spv::Op::OpBranch &&
              "Back-edge terminator must be OpBranch or OpBranchConditional");
 
       // Check that, if the merge block starts with OpPhi instructions, suitable
@@ -689,7 +696,7 @@
       // Augment OpPhi instructions at the loop merge with the given ids.
       uint32_t phi_index = 0;
       for (auto& inst : *merge_block) {
-        if (inst.opcode() != SpvOpPhi) {
+        if (inst.opcode() != spv::Op::OpPhi) {
           break;
         }
         assert(phi_index <
@@ -702,7 +709,7 @@
       }
 
       // Add the new edge, by changing OpBranch to OpBranchConditional.
-      back_edge_block_terminator->SetOpcode(SpvOpBranchConditional);
+      back_edge_block_terminator->SetOpcode(spv::Op::OpBranchConditional);
       back_edge_block_terminator->SetInOperands(opt::Instruction::OperandList(
           {{SPV_OPERAND_TYPE_ID, {loop_limiter_info.compare_id()}},
            {SPV_OPERAND_TYPE_ID, {loop_header->MergeBlockId()}},
@@ -724,18 +731,18 @@
 bool TransformationAddFunction::TryToTurnKillOrUnreachableIntoReturn(
     opt::IRContext* ir_context, opt::Function* added_function,
     opt::Instruction* kill_or_unreachable_inst) const {
-  assert((kill_or_unreachable_inst->opcode() == SpvOpKill ||
-          kill_or_unreachable_inst->opcode() == SpvOpUnreachable) &&
+  assert((kill_or_unreachable_inst->opcode() == spv::Op::OpKill ||
+          kill_or_unreachable_inst->opcode() == spv::Op::OpUnreachable) &&
          "Precondition: instruction must be OpKill or OpUnreachable.");
 
   // Get the function's return type.
   auto function_return_type_inst =
       ir_context->get_def_use_mgr()->GetDef(added_function->type_id());
 
-  if (function_return_type_inst->opcode() == SpvOpTypeVoid) {
+  if (function_return_type_inst->opcode() == spv::Op::OpTypeVoid) {
     // The function has void return type, so change this instruction to
     // OpReturn.
-    kill_or_unreachable_inst->SetOpcode(SpvOpReturn);
+    kill_or_unreachable_inst->SetOpcode(spv::Op::OpReturn);
   } else {
     // The function has non-void return type, so change this instruction
     // to OpReturnValue, using the value id provided with the
@@ -749,7 +756,7 @@
             ->type_id() != function_return_type_inst->result_id()) {
       return false;
     }
-    kill_or_unreachable_inst->SetOpcode(SpvOpReturnValue);
+    kill_or_unreachable_inst->SetOpcode(spv::Op::OpReturnValue);
     kill_or_unreachable_inst->SetInOperands(
         {{SPV_OPERAND_TYPE_ID, {message_.kill_unreachable_return_value_id()}}});
   }
@@ -758,8 +765,8 @@
 
 bool TransformationAddFunction::TryToClampAccessChainIndices(
     opt::IRContext* ir_context, opt::Instruction* access_chain_inst) const {
-  assert((access_chain_inst->opcode() == SpvOpAccessChain ||
-          access_chain_inst->opcode() == SpvOpInBoundsAccessChain) &&
+  assert((access_chain_inst->opcode() == spv::Op::OpAccessChain ||
+          access_chain_inst->opcode() == spv::Op::OpInBoundsAccessChain) &&
          "Precondition: instruction must be OpAccessChain or "
          "OpInBoundsAccessChain.");
 
@@ -793,7 +800,7 @@
   assert(base_object && "The base object must exist.");
   auto pointer_type =
       ir_context->get_def_use_mgr()->GetDef(base_object->type_id());
-  assert(pointer_type && pointer_type->opcode() == SpvOpTypePointer &&
+  assert(pointer_type && pointer_type->opcode() == spv::Op::OpTypePointer &&
          "The base object must have pointer type.");
   auto should_be_composite_type = ir_context->get_def_use_mgr()->GetDef(
       pointer_type->GetSingleWordInOperand(1));
@@ -824,18 +831,18 @@
     auto index_inst = ir_context->get_def_use_mgr()->GetDef(index_id);
     auto index_type_inst =
         ir_context->get_def_use_mgr()->GetDef(index_inst->type_id());
-    assert(index_type_inst->opcode() == SpvOpTypeInt);
+    assert(index_type_inst->opcode() == spv::Op::OpTypeInt);
     assert(index_type_inst->GetSingleWordInOperand(0) == 32);
     opt::analysis::Integer* index_int_type =
         ir_context->get_type_mgr()
             ->GetType(index_type_inst->result_id())
             ->AsInteger();
 
-    if (index_inst->opcode() != SpvOpConstant ||
+    if (index_inst->opcode() != spv::Op::OpConstant ||
         index_inst->GetSingleWordInOperand(0) >= bound) {
       // The index is either non-constant or an out-of-bounds constant, so we
       // need to clamp it.
-      assert(should_be_composite_type->opcode() != SpvOpTypeStruct &&
+      assert(should_be_composite_type->opcode() != spv::Op::OpTypeStruct &&
              "Access chain indices into structures are required to be "
              "constants.");
       opt::analysis::IntConstant bound_minus_one(index_int_type, {bound - 1});
@@ -866,7 +873,7 @@
       // Compare the index with the bound via an instruction of the form:
       //   %t1 = OpULessThanEqual %bool %index %bound_minus_one
       new_instructions.push_back(MakeUnique<opt::Instruction>(
-          ir_context, SpvOpULessThanEqual, bool_type_id, compare_id,
+          ir_context, spv::Op::OpULessThanEqual, bool_type_id, compare_id,
           opt::Instruction::OperandList(
               {{SPV_OPERAND_TYPE_ID, {index_inst->result_id()}},
                {SPV_OPERAND_TYPE_ID, {bound_minus_one_id}}})));
@@ -874,7 +881,8 @@
       // Select the index if in-bounds, otherwise one less than the bound:
       //   %t2 = OpSelect %int_type %t1 %index %bound_minus_one
       new_instructions.push_back(MakeUnique<opt::Instruction>(
-          ir_context, SpvOpSelect, index_type_inst->result_id(), select_id,
+          ir_context, spv::Op::OpSelect, index_type_inst->result_id(),
+          select_id,
           opt::Instruction::OperandList(
               {{SPV_OPERAND_TYPE_ID, {compare_id}},
                {SPV_OPERAND_TYPE_ID, {index_inst->result_id()}},
@@ -899,20 +907,20 @@
     uint32_t index_id) {
   uint32_t sub_object_type_id;
   switch (composite_type_inst.opcode()) {
-    case SpvOpTypeArray:
-    case SpvOpTypeRuntimeArray:
+    case spv::Op::OpTypeArray:
+    case spv::Op::OpTypeRuntimeArray:
       sub_object_type_id = composite_type_inst.GetSingleWordInOperand(0);
       break;
-    case SpvOpTypeMatrix:
-    case SpvOpTypeVector:
+    case spv::Op::OpTypeMatrix:
+    case spv::Op::OpTypeVector:
       sub_object_type_id = composite_type_inst.GetSingleWordInOperand(0);
       break;
-    case SpvOpTypeStruct: {
+    case spv::Op::OpTypeStruct: {
       auto index_inst = ir_context->get_def_use_mgr()->GetDef(index_id);
-      assert(index_inst->opcode() == SpvOpConstant);
+      assert(index_inst->opcode() == spv::Op::OpConstant);
       assert(ir_context->get_def_use_mgr()
                  ->GetDef(index_inst->type_id())
-                 ->opcode() == SpvOpTypeInt);
+                 ->opcode() == spv::Op::OpTypeInt);
       assert(ir_context->get_def_use_mgr()
                  ->GetDef(index_inst->type_id())
                  ->GetSingleWordInOperand(0) == 32);
diff --git a/source/fuzz/transformation_add_global_undef.cpp b/source/fuzz/transformation_add_global_undef.cpp
index ec0574a..5aca19d 100644
--- a/source/fuzz/transformation_add_global_undef.cpp
+++ b/source/fuzz/transformation_add_global_undef.cpp
@@ -39,14 +39,14 @@
   auto type = ir_context->get_def_use_mgr()->GetDef(message_.type_id());
   // The type must exist, and must not be a function or pointer type.
   return type != nullptr && opt::IsTypeInst(type->opcode()) &&
-         type->opcode() != SpvOpTypeFunction &&
-         type->opcode() != SpvOpTypePointer;
+         type->opcode() != spv::Op::OpTypeFunction &&
+         type->opcode() != spv::Op::OpTypePointer;
 }
 
 void TransformationAddGlobalUndef::Apply(
     opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
   auto new_instruction = MakeUnique<opt::Instruction>(
-      ir_context, SpvOpUndef, message_.type_id(), message_.fresh_id(),
+      ir_context, spv::Op::OpUndef, message_.type_id(), message_.fresh_id(),
       opt::Instruction::OperandList());
   auto new_instruction_ptr = new_instruction.get();
   ir_context->module()->AddGlobalValue(std::move(new_instruction));
diff --git a/source/fuzz/transformation_add_global_variable.cpp b/source/fuzz/transformation_add_global_variable.cpp
index 814d01b..6e82d59 100644
--- a/source/fuzz/transformation_add_global_variable.cpp
+++ b/source/fuzz/transformation_add_global_variable.cpp
@@ -24,11 +24,11 @@
     : message_(std::move(message)) {}
 
 TransformationAddGlobalVariable::TransformationAddGlobalVariable(
-    uint32_t fresh_id, uint32_t type_id, SpvStorageClass storage_class,
+    uint32_t fresh_id, uint32_t type_id, spv::StorageClass storage_class,
     uint32_t initializer_id, bool value_is_irrelevant) {
   message_.set_fresh_id(fresh_id);
   message_.set_type_id(type_id);
-  message_.set_storage_class(storage_class);
+  message_.set_storage_class(uint32_t(storage_class));
   message_.set_initializer_id(initializer_id);
   message_.set_value_is_irrelevant(value_is_irrelevant);
 }
@@ -41,10 +41,10 @@
   }
 
   // The storage class must be Private or Workgroup.
-  auto storage_class = static_cast<SpvStorageClass>(message_.storage_class());
+  auto storage_class = static_cast<spv::StorageClass>(message_.storage_class());
   switch (storage_class) {
-    case SpvStorageClassPrivate:
-    case SpvStorageClassWorkgroup:
+    case spv::StorageClass::Private:
+    case spv::StorageClass::Workgroup:
       break;
     default:
       assert(false && "Unsupported storage class.");
@@ -66,7 +66,7 @@
   }
   if (message_.initializer_id()) {
     // An initializer is not allowed if the storage class is Workgroup.
-    if (storage_class == SpvStorageClassWorkgroup) {
+    if (storage_class == spv::StorageClass::Workgroup) {
       assert(false &&
              "By construction this transformation should not have an "
              "initializer when Workgroup storage class is used.");
@@ -95,7 +95,7 @@
     TransformationContext* transformation_context) const {
   opt::Instruction* new_instruction = fuzzerutil::AddGlobalVariable(
       ir_context, message_.fresh_id(), message_.type_id(),
-      static_cast<SpvStorageClass>(message_.storage_class()),
+      static_cast<spv::StorageClass>(message_.storage_class()),
       message_.initializer_id());
 
   // Inform the def-use manager about the new instruction.
diff --git a/source/fuzz/transformation_add_global_variable.h b/source/fuzz/transformation_add_global_variable.h
index d74d48a..0178219 100644
--- a/source/fuzz/transformation_add_global_variable.h
+++ b/source/fuzz/transformation_add_global_variable.h
@@ -29,7 +29,7 @@
       protobufs::TransformationAddGlobalVariable message);
 
   TransformationAddGlobalVariable(uint32_t fresh_id, uint32_t type_id,
-                                  SpvStorageClass storage_class,
+                                  spv::StorageClass storage_class,
                                   uint32_t initializer_id,
                                   bool value_is_irrelevant);
 
diff --git a/source/fuzz/transformation_add_image_sample_unused_components.cpp b/source/fuzz/transformation_add_image_sample_unused_components.cpp
index 1ead82b..018fed4 100644
--- a/source/fuzz/transformation_add_image_sample_unused_components.cpp
+++ b/source/fuzz/transformation_add_image_sample_unused_components.cpp
@@ -73,7 +73,7 @@
   // It must be an OpCompositeConstruct instruction such that it can be checked
   // that the original components are present.
   if (coordinate_with_unused_components_instruction->opcode() !=
-      SpvOpCompositeConstruct) {
+      spv::Op::OpCompositeConstruct) {
     return false;
   }
 
diff --git a/source/fuzz/transformation_add_local_variable.cpp b/source/fuzz/transformation_add_local_variable.cpp
index 21768d2..9ee210c 100644
--- a/source/fuzz/transformation_add_local_variable.cpp
+++ b/source/fuzz/transformation_add_local_variable.cpp
@@ -43,8 +43,10 @@
   // function storage class.
   auto type_instruction =
       ir_context->get_def_use_mgr()->GetDef(message_.type_id());
-  if (!type_instruction || type_instruction->opcode() != SpvOpTypePointer ||
-      type_instruction->GetSingleWordInOperand(0) != SpvStorageClassFunction) {
+  if (!type_instruction ||
+      type_instruction->opcode() != spv::Op::OpTypePointer ||
+      spv::StorageClass(type_instruction->GetSingleWordInOperand(0)) !=
+          spv::StorageClass::Function) {
     return false;
   }
   // The initializer must...
diff --git a/source/fuzz/transformation_add_loop_preheader.cpp b/source/fuzz/transformation_add_loop_preheader.cpp
index 71ab18d..4b66b69 100644
--- a/source/fuzz/transformation_add_loop_preheader.cpp
+++ b/source/fuzz/transformation_add_loop_preheader.cpp
@@ -120,8 +120,8 @@
         // If |use_inst| is not a branch or merge instruction, it should not be
         // changed.
         if (!use_inst->IsBranch() &&
-            use_inst->opcode() != SpvOpSelectionMerge &&
-            use_inst->opcode() != SpvOpLoopMerge) {
+            use_inst->opcode() != spv::Op::OpSelectionMerge &&
+            use_inst->opcode() != spv::Op::OpLoopMerge) {
           return;
         }
 
@@ -134,7 +134,7 @@
   // Make a new block for the preheader.
   std::unique_ptr<opt::BasicBlock> preheader = MakeUnique<opt::BasicBlock>(
       std::unique_ptr<opt::Instruction>(new opt::Instruction(
-          ir_context, SpvOpLabel, 0, message_.fresh_id(), {})));
+          ir_context, spv::Op::OpLabel, 0, message_.fresh_id(), {})));
 
   uint32_t phi_ids_used = 0;
 
@@ -183,7 +183,7 @@
       fuzzerutil::UpdateModuleIdBound(ir_context, fresh_phi_id);
 
       preheader->AddInstruction(std::unique_ptr<opt::Instruction>(
-          new opt::Instruction(ir_context, SpvOpPhi, phi_inst->type_id(),
+          new opt::Instruction(ir_context, spv::Op::OpPhi, phi_inst->type_id(),
                                fresh_phi_id, preheader_in_operands)));
 
       // Update the OpPhi instruction in the header so that it refers to the
@@ -202,7 +202,7 @@
   // Add an unconditional branch from the preheader to the header.
   preheader->AddInstruction(
       std::unique_ptr<opt::Instruction>(new opt::Instruction(
-          ir_context, SpvOpBranch, 0, 0,
+          ir_context, spv::Op::OpBranch, 0, 0,
           std::initializer_list<opt::Operand>{opt::Operand(
               spv_operand_type_t::SPV_OPERAND_TYPE_ID, {loop_header->id()})})));
 
diff --git a/source/fuzz/transformation_add_loop_to_create_int_constant_synonym.cpp b/source/fuzz/transformation_add_loop_to_create_int_constant_synonym.cpp
index 657fafa..00030e7 100644
--- a/source/fuzz/transformation_add_loop_to_create_int_constant_synonym.cpp
+++ b/source/fuzz/transformation_add_loop_to_create_int_constant_synonym.cpp
@@ -262,13 +262,13 @@
   // Create the loop header block.
   std::unique_ptr<opt::BasicBlock> loop_block =
       MakeUnique<opt::BasicBlock>(MakeUnique<opt::Instruction>(
-          ir_context, SpvOpLabel, 0, message_.loop_id(),
+          ir_context, spv::Op::OpLabel, 0, message_.loop_id(),
           opt::Instruction::OperandList{}));
 
   // Add OpPhi instructions to retrieve the current value of the counter and of
   // the temporary variable that will be decreased at each operation.
   loop_block->AddInstruction(MakeUnique<opt::Instruction>(
-      ir_context, SpvOpPhi, const_0_def->type_id(), message_.ctr_id(),
+      ir_context, spv::Op::OpPhi, const_0_def->type_id(), message_.ctr_id(),
       opt::Instruction::OperandList{
           {SPV_OPERAND_TYPE_ID, {const_0_id}},
           {SPV_OPERAND_TYPE_ID, {pred_id}},
@@ -276,7 +276,8 @@
           {SPV_OPERAND_TYPE_ID, {last_loop_block_id}}}));
 
   loop_block->AddInstruction(MakeUnique<opt::Instruction>(
-      ir_context, SpvOpPhi, initial_val_def->type_id(), message_.temp_id(),
+      ir_context, spv::Op::OpPhi, initial_val_def->type_id(),
+      message_.temp_id(),
       opt::Instruction::OperandList{
           {SPV_OPERAND_TYPE_ID, {message_.initial_val_id()}},
           {SPV_OPERAND_TYPE_ID, {pred_id}},
@@ -291,7 +292,7 @@
   // Add an instruction to subtract the step value from the temporary value.
   // The value of this id will converge to the constant in the last iteration.
   other_instructions.push_back(MakeUnique<opt::Instruction>(
-      ir_context, SpvOpISub, initial_val_def->type_id(),
+      ir_context, spv::Op::OpISub, initial_val_def->type_id(),
       message_.eventual_syn_id(),
       opt::Instruction::OperandList{
           {SPV_OPERAND_TYPE_ID, {message_.temp_id()}},
@@ -299,15 +300,15 @@
 
   // Add an instruction to increment the counter.
   other_instructions.push_back(MakeUnique<opt::Instruction>(
-      ir_context, SpvOpIAdd, const_0_def->type_id(),
+      ir_context, spv::Op::OpIAdd, const_0_def->type_id(),
       message_.incremented_ctr_id(),
       opt::Instruction::OperandList{{SPV_OPERAND_TYPE_ID, {message_.ctr_id()}},
                                     {SPV_OPERAND_TYPE_ID, {const_1_id}}}));
 
   // Add an instruction to decide whether the condition holds.
   other_instructions.push_back(MakeUnique<opt::Instruction>(
-      ir_context, SpvOpSLessThan, fuzzerutil::MaybeGetBoolType(ir_context),
-      message_.cond_id(),
+      ir_context, spv::Op::OpSLessThan,
+      fuzzerutil::MaybeGetBoolType(ir_context), message_.cond_id(),
       opt::Instruction::OperandList{
           {SPV_OPERAND_TYPE_ID, {message_.incremented_ctr_id()}},
           {SPV_OPERAND_TYPE_ID, {message_.num_iterations_id()}}}));
@@ -316,18 +317,19 @@
   // the existing block, the continue block is the last block in the loop
   // (either the loop itself or the additional block).
   std::unique_ptr<opt::Instruction> merge_inst = MakeUnique<opt::Instruction>(
-      ir_context, SpvOpLoopMerge, 0, 0,
+      ir_context, spv::Op::OpLoopMerge, 0, 0,
       opt::Instruction::OperandList{
           {SPV_OPERAND_TYPE_ID, {message_.block_after_loop_id()}},
           {SPV_OPERAND_TYPE_ID, {last_loop_block_id}},
-          {SPV_OPERAND_TYPE_LOOP_CONTROL, {SpvLoopControlMaskNone}}});
+          {SPV_OPERAND_TYPE_LOOP_CONTROL,
+           {uint32_t(spv::LoopControlMask::MaskNone)}}});
 
   // Define a conditional branch instruction, branching to the loop header if
   // the condition holds, and to the existing block otherwise. This instruction
   // will be added to the last block in the loop.
   std::unique_ptr<opt::Instruction> conditional_branch =
       MakeUnique<opt::Instruction>(
-          ir_context, SpvOpBranchConditional, 0, 0,
+          ir_context, spv::Op::OpBranchConditional, 0, 0,
           opt::Instruction::OperandList{
               {SPV_OPERAND_TYPE_ID, {message_.cond_id()}},
               {SPV_OPERAND_TYPE_ID, {message_.loop_id()}},
@@ -340,7 +342,7 @@
 
     std::unique_ptr<opt::BasicBlock> additional_block =
         MakeUnique<opt::BasicBlock>(MakeUnique<opt::Instruction>(
-            ir_context, SpvOpLabel, 0, message_.additional_block_id(),
+            ir_context, spv::Op::OpLabel, 0, message_.additional_block_id(),
             opt::Instruction::OperandList{}));
 
     for (auto& instruction : other_instructions) {
@@ -354,7 +356,7 @@
 
     // Add an unconditional branch from the header to the additional block.
     loop_block->AddInstruction(MakeUnique<opt::Instruction>(
-        ir_context, SpvOpBranch, 0, 0,
+        ir_context, spv::Op::OpBranch, 0, 0,
         opt::Instruction::OperandList{
             {SPV_OPERAND_TYPE_ID, {message_.additional_block_id()}}}));
 
@@ -384,14 +386,14 @@
   ir_context->get_def_use_mgr()->ForEachUse(
       message_.block_after_loop_id(),
       [this](opt::Instruction* instruction, uint32_t operand_index) {
-        assert(instruction->opcode() != SpvOpLoopMerge &&
-               instruction->opcode() != SpvOpSelectionMerge &&
+        assert(instruction->opcode() != spv::Op::OpLoopMerge &&
+               instruction->opcode() != spv::Op::OpSelectionMerge &&
                "The block should not be referenced by OpLoopMerge or "
                "OpSelectionMerge, by construction.");
         // Replace all uses of the label inside branch instructions.
-        if (instruction->opcode() == SpvOpBranch ||
-            instruction->opcode() == SpvOpBranchConditional ||
-            instruction->opcode() == SpvOpSwitch) {
+        if (instruction->opcode() == spv::Op::OpBranch ||
+            instruction->opcode() == spv::Op::OpBranchConditional ||
+            instruction->opcode() == spv::Op::OpSwitch) {
           instruction->SetOperand(operand_index, {message_.loop_id()});
         }
       });
@@ -410,7 +412,7 @@
   // |message_.initial_value_id|, since this is the value that is decremented in
   // the loop.
   block_after_loop->begin()->InsertBefore(MakeUnique<opt::Instruction>(
-      ir_context, SpvOpPhi, initial_val_def->type_id(), message_.syn_id(),
+      ir_context, spv::Op::OpPhi, initial_val_def->type_id(), message_.syn_id(),
       opt::Instruction::OperandList{
           {SPV_OPERAND_TYPE_ID, {message_.eventual_syn_id()}},
           {SPV_OPERAND_TYPE_ID, {last_loop_block_id}}}));
diff --git a/source/fuzz/transformation_add_no_contraction_decoration.cpp b/source/fuzz/transformation_add_no_contraction_decoration.cpp
index 992a216..07a31e5 100644
--- a/source/fuzz/transformation_add_no_contraction_decoration.cpp
+++ b/source/fuzz/transformation_add_no_contraction_decoration.cpp
@@ -43,8 +43,8 @@
 void TransformationAddNoContractionDecoration::Apply(
     opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
   // Add a NoContraction decoration targeting |message_.result_id|.
-  ir_context->get_decoration_mgr()->AddDecoration(message_.result_id(),
-                                                  SpvDecorationNoContraction);
+  ir_context->get_decoration_mgr()->AddDecoration(
+      message_.result_id(), uint32_t(spv::Decoration::NoContraction));
 }
 
 protobufs::Transformation TransformationAddNoContractionDecoration::ToMessage()
@@ -54,50 +54,50 @@
   return result;
 }
 
-bool TransformationAddNoContractionDecoration::IsArithmetic(uint32_t opcode) {
+bool TransformationAddNoContractionDecoration::IsArithmetic(spv::Op opcode) {
   switch (opcode) {
-    case SpvOpSNegate:
-    case SpvOpFNegate:
-    case SpvOpIAdd:
-    case SpvOpFAdd:
-    case SpvOpISub:
-    case SpvOpFSub:
-    case SpvOpIMul:
-    case SpvOpFMul:
-    case SpvOpUDiv:
-    case SpvOpSDiv:
-    case SpvOpFDiv:
-    case SpvOpUMod:
-    case SpvOpSRem:
-    case SpvOpSMod:
-    case SpvOpFRem:
-    case SpvOpFMod:
-    case SpvOpVectorTimesScalar:
-    case SpvOpMatrixTimesScalar:
-    case SpvOpVectorTimesMatrix:
-    case SpvOpMatrixTimesVector:
-    case SpvOpMatrixTimesMatrix:
-    case SpvOpOuterProduct:
-    case SpvOpDot:
-    case SpvOpIAddCarry:
-    case SpvOpISubBorrow:
-    case SpvOpUMulExtended:
-    case SpvOpSMulExtended:
-    case SpvOpAny:
-    case SpvOpAll:
-    case SpvOpIsNan:
-    case SpvOpIsInf:
-    case SpvOpIsFinite:
-    case SpvOpIsNormal:
-    case SpvOpSignBitSet:
-    case SpvOpLessOrGreater:
-    case SpvOpOrdered:
-    case SpvOpUnordered:
-    case SpvOpLogicalEqual:
-    case SpvOpLogicalNotEqual:
-    case SpvOpLogicalOr:
-    case SpvOpLogicalAnd:
-    case SpvOpLogicalNot:
+    case spv::Op::OpSNegate:
+    case spv::Op::OpFNegate:
+    case spv::Op::OpIAdd:
+    case spv::Op::OpFAdd:
+    case spv::Op::OpISub:
+    case spv::Op::OpFSub:
+    case spv::Op::OpIMul:
+    case spv::Op::OpFMul:
+    case spv::Op::OpUDiv:
+    case spv::Op::OpSDiv:
+    case spv::Op::OpFDiv:
+    case spv::Op::OpUMod:
+    case spv::Op::OpSRem:
+    case spv::Op::OpSMod:
+    case spv::Op::OpFRem:
+    case spv::Op::OpFMod:
+    case spv::Op::OpVectorTimesScalar:
+    case spv::Op::OpMatrixTimesScalar:
+    case spv::Op::OpVectorTimesMatrix:
+    case spv::Op::OpMatrixTimesVector:
+    case spv::Op::OpMatrixTimesMatrix:
+    case spv::Op::OpOuterProduct:
+    case spv::Op::OpDot:
+    case spv::Op::OpIAddCarry:
+    case spv::Op::OpISubBorrow:
+    case spv::Op::OpUMulExtended:
+    case spv::Op::OpSMulExtended:
+    case spv::Op::OpAny:
+    case spv::Op::OpAll:
+    case spv::Op::OpIsNan:
+    case spv::Op::OpIsInf:
+    case spv::Op::OpIsFinite:
+    case spv::Op::OpIsNormal:
+    case spv::Op::OpSignBitSet:
+    case spv::Op::OpLessOrGreater:
+    case spv::Op::OpOrdered:
+    case spv::Op::OpUnordered:
+    case spv::Op::OpLogicalEqual:
+    case spv::Op::OpLogicalNotEqual:
+    case spv::Op::OpLogicalOr:
+    case spv::Op::OpLogicalAnd:
+    case spv::Op::OpLogicalNot:
       return true;
     default:
       return false;
diff --git a/source/fuzz/transformation_add_no_contraction_decoration.h b/source/fuzz/transformation_add_no_contraction_decoration.h
index 2f78d42..4235dc1 100644
--- a/source/fuzz/transformation_add_no_contraction_decoration.h
+++ b/source/fuzz/transformation_add_no_contraction_decoration.h
@@ -50,7 +50,7 @@
 
   // Returns true if and only if |opcode| is the opcode of an arithmetic
   // instruction, as defined by the SPIR-V specification.
-  static bool IsArithmetic(uint32_t opcode);
+  static bool IsArithmetic(spv::Op opcode);
 
  private:
   protobufs::TransformationAddNoContractionDecoration message_;
diff --git a/source/fuzz/transformation_add_opphi_synonym.cpp b/source/fuzz/transformation_add_opphi_synonym.cpp
index 3c4698a..31c56b6 100644
--- a/source/fuzz/transformation_add_opphi_synonym.cpp
+++ b/source/fuzz/transformation_add_opphi_synonym.cpp
@@ -142,8 +142,8 @@
   // Add a new OpPhi instructions at the beginning of the block.
   ir_context->get_instr_block(message_.block_id())
       ->begin()
-      .InsertBefore(MakeUnique<opt::Instruction>(ir_context, SpvOpPhi, type_id,
-                                                 message_.fresh_id(),
+      .InsertBefore(MakeUnique<opt::Instruction>(ir_context, spv::Op::OpPhi,
+                                                 type_id, message_.fresh_id(),
                                                  std::move(operand_list)));
 
   // Update the module id bound.
@@ -186,9 +186,9 @@
   if (type->AsPointer()) {
     auto storage_class = type->AsPointer()->storage_class();
     return ir_context->get_feature_mgr()->HasCapability(
-               SpvCapabilityVariablePointers) &&
-           (storage_class == SpvStorageClassWorkgroup ||
-            storage_class == SpvStorageClassStorageBuffer);
+               spv::Capability::VariablePointers) &&
+           (storage_class == spv::StorageClass::Workgroup ||
+            storage_class == spv::StorageClass::StorageBuffer);
   }
 
   // We do not allow other types.
diff --git a/source/fuzz/transformation_add_parameter.cpp b/source/fuzz/transformation_add_parameter.cpp
index 48de3e8..8bd2ed7 100644
--- a/source/fuzz/transformation_add_parameter.cpp
+++ b/source/fuzz/transformation_add_parameter.cpp
@@ -112,7 +112,7 @@
 
   // Add new parameters to the function.
   function->AddParameter(MakeUnique<opt::Instruction>(
-      ir_context, SpvOpFunctionParameter, new_parameter_type_id,
+      ir_context, spv::Op::OpFunctionParameter, new_parameter_type_id,
       message_.parameter_fresh_id(), opt::Instruction::OperandList()));
 
   fuzzerutil::UpdateModuleIdBound(ir_context, message_.parameter_fresh_id());
@@ -178,16 +178,16 @@
   //  Think about other type instructions we can add here.
   opt::Instruction* type_inst = ir_context->get_def_use_mgr()->GetDef(type_id);
   switch (type_inst->opcode()) {
-    case SpvOpTypeBool:
-    case SpvOpTypeInt:
-    case SpvOpTypeFloat:
-    case SpvOpTypeMatrix:
-    case SpvOpTypeVector:
+    case spv::Op::OpTypeBool:
+    case spv::Op::OpTypeInt:
+    case spv::Op::OpTypeFloat:
+    case spv::Op::OpTypeMatrix:
+    case spv::Op::OpTypeVector:
       return true;
-    case SpvOpTypeArray:
+    case spv::Op::OpTypeArray:
       return IsParameterTypeSupported(ir_context,
                                       type_inst->GetSingleWordInOperand(0));
-    case SpvOpTypeStruct:
+    case spv::Op::OpTypeStruct:
       if (fuzzerutil::HasBlockOrBufferBlockDecoration(ir_context, type_id)) {
         return false;
       }
@@ -198,13 +198,13 @@
         }
       }
       return true;
-    case SpvOpTypePointer: {
-      SpvStorageClass storage_class =
-          static_cast<SpvStorageClass>(type_inst->GetSingleWordInOperand(0));
+    case spv::Op::OpTypePointer: {
+      spv::StorageClass storage_class =
+          static_cast<spv::StorageClass>(type_inst->GetSingleWordInOperand(0));
       switch (storage_class) {
-        case SpvStorageClassPrivate:
-        case SpvStorageClassFunction:
-        case SpvStorageClassWorkgroup: {
+        case spv::StorageClass::Private:
+        case spv::StorageClass::Function:
+        case spv::StorageClass::Workgroup: {
           return IsParameterTypeSupported(ir_context,
                                           type_inst->GetSingleWordInOperand(1));
         }
diff --git a/source/fuzz/transformation_add_relaxed_decoration.cpp b/source/fuzz/transformation_add_relaxed_decoration.cpp
index b66a1a8..6cd4ecb 100644
--- a/source/fuzz/transformation_add_relaxed_decoration.cpp
+++ b/source/fuzz/transformation_add_relaxed_decoration.cpp
@@ -54,7 +54,7 @@
     opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
   // Add a RelaxedPrecision decoration targeting |message_.result_id|.
   ir_context->get_decoration_mgr()->AddDecoration(
-      message_.result_id(), SpvDecorationRelaxedPrecision);
+      message_.result_id(), uint32_t(spv::Decoration::RelaxedPrecision));
 }
 
 protobufs::Transformation TransformationAddRelaxedDecoration::ToMessage()
@@ -64,77 +64,77 @@
   return result;
 }
 
-bool TransformationAddRelaxedDecoration::IsNumeric(uint32_t opcode) {
+bool TransformationAddRelaxedDecoration::IsNumeric(spv::Op opcode) {
   switch (opcode) {
-    case SpvOpConvertFToU:
-    case SpvOpConvertFToS:
-    case SpvOpConvertSToF:
-    case SpvOpConvertUToF:
-    case SpvOpUConvert:
-    case SpvOpSConvert:
-    case SpvOpFConvert:
-    case SpvOpConvertPtrToU:
-    case SpvOpSatConvertSToU:
-    case SpvOpSatConvertUToS:
-    case SpvOpVectorExtractDynamic:
-    case SpvOpVectorInsertDynamic:
-    case SpvOpVectorShuffle:
-    case SpvOpTranspose:
-    case SpvOpSNegate:
-    case SpvOpFNegate:
-    case SpvOpIAdd:
-    case SpvOpFAdd:
-    case SpvOpISub:
-    case SpvOpFSub:
-    case SpvOpIMul:
-    case SpvOpFMul:
-    case SpvOpUDiv:
-    case SpvOpSDiv:
-    case SpvOpFDiv:
-    case SpvOpUMod:
-    case SpvOpSRem:
-    case SpvOpSMod:
-    case SpvOpFRem:
-    case SpvOpFMod:
-    case SpvOpVectorTimesScalar:
-    case SpvOpMatrixTimesScalar:
-    case SpvOpVectorTimesMatrix:
-    case SpvOpMatrixTimesVector:
-    case SpvOpMatrixTimesMatrix:
-    case SpvOpOuterProduct:
-    case SpvOpDot:
-    case SpvOpIAddCarry:
-    case SpvOpISubBorrow:
-    case SpvOpUMulExtended:
-    case SpvOpSMulExtended:
-    case SpvOpShiftRightLogical:
-    case SpvOpShiftRightArithmetic:
-    case SpvOpShiftLeftLogical:
-    case SpvOpBitwiseOr:
-    case SpvOpBitwiseXor:
-    case SpvOpBitwiseAnd:
-    case SpvOpNot:
-    case SpvOpBitFieldInsert:
-    case SpvOpBitFieldSExtract:
-    case SpvOpBitFieldUExtract:
-    case SpvOpBitReverse:
-    case SpvOpBitCount:
-    case SpvOpAtomicLoad:
-    case SpvOpAtomicStore:
-    case SpvOpAtomicExchange:
-    case SpvOpAtomicCompareExchange:
-    case SpvOpAtomicCompareExchangeWeak:
-    case SpvOpAtomicIIncrement:
-    case SpvOpAtomicIDecrement:
-    case SpvOpAtomicIAdd:
-    case SpvOpAtomicISub:
-    case SpvOpAtomicSMin:
-    case SpvOpAtomicUMin:
-    case SpvOpAtomicSMax:
-    case SpvOpAtomicUMax:
-    case SpvOpAtomicAnd:
-    case SpvOpAtomicOr:
-    case SpvOpAtomicXor:
+    case spv::Op::OpConvertFToU:
+    case spv::Op::OpConvertFToS:
+    case spv::Op::OpConvertSToF:
+    case spv::Op::OpConvertUToF:
+    case spv::Op::OpUConvert:
+    case spv::Op::OpSConvert:
+    case spv::Op::OpFConvert:
+    case spv::Op::OpConvertPtrToU:
+    case spv::Op::OpSatConvertSToU:
+    case spv::Op::OpSatConvertUToS:
+    case spv::Op::OpVectorExtractDynamic:
+    case spv::Op::OpVectorInsertDynamic:
+    case spv::Op::OpVectorShuffle:
+    case spv::Op::OpTranspose:
+    case spv::Op::OpSNegate:
+    case spv::Op::OpFNegate:
+    case spv::Op::OpIAdd:
+    case spv::Op::OpFAdd:
+    case spv::Op::OpISub:
+    case spv::Op::OpFSub:
+    case spv::Op::OpIMul:
+    case spv::Op::OpFMul:
+    case spv::Op::OpUDiv:
+    case spv::Op::OpSDiv:
+    case spv::Op::OpFDiv:
+    case spv::Op::OpUMod:
+    case spv::Op::OpSRem:
+    case spv::Op::OpSMod:
+    case spv::Op::OpFRem:
+    case spv::Op::OpFMod:
+    case spv::Op::OpVectorTimesScalar:
+    case spv::Op::OpMatrixTimesScalar:
+    case spv::Op::OpVectorTimesMatrix:
+    case spv::Op::OpMatrixTimesVector:
+    case spv::Op::OpMatrixTimesMatrix:
+    case spv::Op::OpOuterProduct:
+    case spv::Op::OpDot:
+    case spv::Op::OpIAddCarry:
+    case spv::Op::OpISubBorrow:
+    case spv::Op::OpUMulExtended:
+    case spv::Op::OpSMulExtended:
+    case spv::Op::OpShiftRightLogical:
+    case spv::Op::OpShiftRightArithmetic:
+    case spv::Op::OpShiftLeftLogical:
+    case spv::Op::OpBitwiseOr:
+    case spv::Op::OpBitwiseXor:
+    case spv::Op::OpBitwiseAnd:
+    case spv::Op::OpNot:
+    case spv::Op::OpBitFieldInsert:
+    case spv::Op::OpBitFieldSExtract:
+    case spv::Op::OpBitFieldUExtract:
+    case spv::Op::OpBitReverse:
+    case spv::Op::OpBitCount:
+    case spv::Op::OpAtomicLoad:
+    case spv::Op::OpAtomicStore:
+    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:
       return true;
     default:
       return false;
@@ -147,4 +147,4 @@
 }
 
 }  // namespace fuzz
-}  // namespace spvtools
\ No newline at end of file
+}  // namespace spvtools
diff --git a/source/fuzz/transformation_add_relaxed_decoration.h b/source/fuzz/transformation_add_relaxed_decoration.h
index c016349..e13594e 100644
--- a/source/fuzz/transformation_add_relaxed_decoration.h
+++ b/source/fuzz/transformation_add_relaxed_decoration.h
@@ -52,7 +52,7 @@
   // Returns true if and only if |opcode| is the opcode of an instruction
   // that operates on 32-bit integers and 32-bit floats
   // as defined by the SPIR-V specification.
-  static bool IsNumeric(uint32_t opcode);
+  static bool IsNumeric(spv::Op opcode);
 
  private:
   protobufs::TransformationAddRelaxedDecoration message_;
diff --git a/source/fuzz/transformation_add_spec_constant_op.cpp b/source/fuzz/transformation_add_spec_constant_op.cpp
index 19c5e85..685f0a4 100644
--- a/source/fuzz/transformation_add_spec_constant_op.cpp
+++ b/source/fuzz/transformation_add_spec_constant_op.cpp
@@ -26,11 +26,11 @@
     : message_(std::move(message)) {}
 
 TransformationAddSpecConstantOp::TransformationAddSpecConstantOp(
-    uint32_t fresh_id, uint32_t type_id, SpvOp opcode,
+    uint32_t fresh_id, uint32_t type_id, spv::Op opcode,
     const opt::Instruction::OperandList& operands) {
   message_.set_fresh_id(fresh_id);
   message_.set_type_id(type_id);
-  message_.set_opcode(opcode);
+  message_.set_opcode(uint32_t(opcode));
   for (const auto& operand : operands) {
     auto* op = message_.add_operand();
     op->set_operand_type(operand.type);
@@ -70,8 +70,8 @@
   }
 
   ir_context->AddGlobalValue(MakeUnique<opt::Instruction>(
-      ir_context, SpvOpSpecConstantOp, message_.type_id(), message_.fresh_id(),
-      std::move(operands)));
+      ir_context, spv::Op::OpSpecConstantOp, message_.type_id(),
+      message_.fresh_id(), std::move(operands)));
 
   fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
 }
diff --git a/source/fuzz/transformation_add_spec_constant_op.h b/source/fuzz/transformation_add_spec_constant_op.h
index 29851fd..665f66a 100644
--- a/source/fuzz/transformation_add_spec_constant_op.h
+++ b/source/fuzz/transformation_add_spec_constant_op.h
@@ -29,7 +29,7 @@
       protobufs::TransformationAddSpecConstantOp message);
 
   TransformationAddSpecConstantOp(
-      uint32_t fresh_id, uint32_t type_id, SpvOp opcode,
+      uint32_t fresh_id, uint32_t type_id, spv::Op opcode,
       const opt::Instruction::OperandList& operands);
 
   // - |fresh_id| is a fresh result id in the module.
diff --git a/source/fuzz/transformation_add_synonym.cpp b/source/fuzz/transformation_add_synonym.cpp
index 69269e5..00df9cf 100644
--- a/source/fuzz/transformation_add_synonym.cpp
+++ b/source/fuzz/transformation_add_synonym.cpp
@@ -82,7 +82,7 @@
   // Check that we can insert |message._synonymous_instruction| before
   // |message_.insert_before| instruction. We use OpIAdd to represent some
   // instruction that can produce a synonym.
-  if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpIAdd,
+  if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(spv::Op::OpIAdd,
                                                     insert_before_inst)) {
     return false;
   }
@@ -147,7 +147,8 @@
   // Instruction must have a result id, type id. We skip OpUndef and
   // OpConstantNull.
   if (!inst || !inst->result_id() || !inst->type_id() ||
-      inst->opcode() == SpvOpUndef || inst->opcode() == SpvOpConstantNull) {
+      inst->opcode() == spv::Op::OpUndef ||
+      inst->opcode() == spv::Op::OpConstantNull) {
     return false;
   }
 
@@ -208,7 +209,7 @@
   auto synonym_type_id =
       fuzzerutil::GetTypeId(ir_context, message_.result_id());
   assert(synonym_type_id && "Synonym has invalid type id");
-  auto opcode = SpvOpNop;
+  auto opcode = spv::Op::OpNop;
   const auto* synonym_type =
       ir_context->get_type_mgr()->GetType(synonym_type_id);
   assert(synonym_type && "Synonym has invalid type");
@@ -219,30 +220,30 @@
 
   switch (message_.synonym_type()) {
     case protobufs::TransformationAddSynonym::SUB_ZERO:
-      opcode = is_integral ? SpvOpISub : SpvOpFSub;
+      opcode = is_integral ? spv::Op::OpISub : spv::Op::OpFSub;
       break;
     case protobufs::TransformationAddSynonym::MUL_ONE:
-      opcode = is_integral ? SpvOpIMul : SpvOpFMul;
+      opcode = is_integral ? spv::Op::OpIMul : spv::Op::OpFMul;
       break;
     case protobufs::TransformationAddSynonym::ADD_ZERO:
-      opcode = is_integral ? SpvOpIAdd : SpvOpFAdd;
+      opcode = is_integral ? spv::Op::OpIAdd : spv::Op::OpFAdd;
       break;
     case protobufs::TransformationAddSynonym::LOGICAL_OR:
-      opcode = SpvOpLogicalOr;
+      opcode = spv::Op::OpLogicalOr;
       break;
     case protobufs::TransformationAddSynonym::LOGICAL_AND:
-      opcode = SpvOpLogicalAnd;
+      opcode = spv::Op::OpLogicalAnd;
       break;
     case protobufs::TransformationAddSynonym::BITWISE_OR:
-      opcode = SpvOpBitwiseOr;
+      opcode = spv::Op::OpBitwiseOr;
       break;
     case protobufs::TransformationAddSynonym::BITWISE_XOR:
-      opcode = SpvOpBitwiseXor;
+      opcode = spv::Op::OpBitwiseXor;
       break;
 
     case protobufs::TransformationAddSynonym::COPY_OBJECT:
       return MakeUnique<opt::Instruction>(
-          ir_context, SpvOpCopyObject, synonym_type_id,
+          ir_context, spv::Op::OpCopyObject, synonym_type_id,
           message_.synonym_fresh_id(),
           opt::Instruction::OperandList{
               {SPV_OPERAND_TYPE_ID, {message_.result_id()}}});
diff --git a/source/fuzz/transformation_add_type_array.cpp b/source/fuzz/transformation_add_type_array.cpp
index 45bc8df..d00d0e4 100644
--- a/source/fuzz/transformation_add_type_array.cpp
+++ b/source/fuzz/transformation_add_type_array.cpp
@@ -72,7 +72,7 @@
   in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.element_type_id()}});
   in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.size_id()}});
   auto type_instruction = MakeUnique<opt::Instruction>(
-      ir_context, SpvOpTypeArray, 0, message_.fresh_id(), in_operands);
+      ir_context, spv::Op::OpTypeArray, 0, message_.fresh_id(), in_operands);
   auto type_instruction_ptr = type_instruction.get();
   ir_context->module()->AddType(std::move(type_instruction));
 
diff --git a/source/fuzz/transformation_add_type_boolean.cpp b/source/fuzz/transformation_add_type_boolean.cpp
index 30ff43e..47fc744 100644
--- a/source/fuzz/transformation_add_type_boolean.cpp
+++ b/source/fuzz/transformation_add_type_boolean.cpp
@@ -43,7 +43,7 @@
     opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
   opt::Instruction::OperandList empty_operands;
   auto type_instruction = MakeUnique<opt::Instruction>(
-      ir_context, SpvOpTypeBool, 0, message_.fresh_id(), empty_operands);
+      ir_context, spv::Op::OpTypeBool, 0, message_.fresh_id(), empty_operands);
   auto type_instruction_ptr = type_instruction.get();
   ir_context->module()->AddType(std::move(type_instruction));
 
diff --git a/source/fuzz/transformation_add_type_float.cpp b/source/fuzz/transformation_add_type_float.cpp
index 1b88b25..1943ffa 100644
--- a/source/fuzz/transformation_add_type_float.cpp
+++ b/source/fuzz/transformation_add_type_float.cpp
@@ -40,7 +40,8 @@
   switch (message_.width()) {
     case 16:
       // The Float16 capability must be present.
-      if (!ir_context->get_feature_mgr()->HasCapability(SpvCapabilityFloat16)) {
+      if (!ir_context->get_feature_mgr()->HasCapability(
+              spv::Capability::Float16)) {
         return false;
       }
       break;
@@ -49,7 +50,8 @@
       break;
     case 64:
       // The Float64 capability must be present.
-      if (!ir_context->get_feature_mgr()->HasCapability(SpvCapabilityFloat64)) {
+      if (!ir_context->get_feature_mgr()->HasCapability(
+              spv::Capability::Float64)) {
         return false;
       }
       break;
@@ -66,7 +68,7 @@
 void TransformationAddTypeFloat::Apply(
     opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
   auto type_instruction = MakeUnique<opt::Instruction>(
-      ir_context, SpvOpTypeFloat, 0, message_.fresh_id(),
+      ir_context, spv::Op::OpTypeFloat, 0, message_.fresh_id(),
       opt::Instruction::OperandList{
           {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.width()}}});
   auto type_instruction_ptr = type_instruction.get();
diff --git a/source/fuzz/transformation_add_type_int.cpp b/source/fuzz/transformation_add_type_int.cpp
index d4ef981..35663f9 100644
--- a/source/fuzz/transformation_add_type_int.cpp
+++ b/source/fuzz/transformation_add_type_int.cpp
@@ -42,13 +42,15 @@
   switch (message_.width()) {
     case 8:
       // The Int8 capability must be present.
-      if (!ir_context->get_feature_mgr()->HasCapability(SpvCapabilityInt8)) {
+      if (!ir_context->get_feature_mgr()->HasCapability(
+              spv::Capability::Int8)) {
         return false;
       }
       break;
     case 16:
       // The Int16 capability must be present.
-      if (!ir_context->get_feature_mgr()->HasCapability(SpvCapabilityInt16)) {
+      if (!ir_context->get_feature_mgr()->HasCapability(
+              spv::Capability::Int16)) {
         return false;
       }
       break;
@@ -57,7 +59,8 @@
       break;
     case 64:
       // The Int64 capability must be present.
-      if (!ir_context->get_feature_mgr()->HasCapability(SpvCapabilityInt64)) {
+      if (!ir_context->get_feature_mgr()->HasCapability(
+              spv::Capability::Int64)) {
         return false;
       }
       break;
@@ -75,7 +78,7 @@
 void TransformationAddTypeInt::Apply(opt::IRContext* ir_context,
                                      TransformationContext* /*unused*/) const {
   auto type_instruction = MakeUnique<opt::Instruction>(
-      ir_context, SpvOpTypeInt, 0, message_.fresh_id(),
+      ir_context, spv::Op::OpTypeInt, 0, message_.fresh_id(),
       opt::Instruction::OperandList{
           {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.width()}},
           {SPV_OPERAND_TYPE_LITERAL_INTEGER,
diff --git a/source/fuzz/transformation_add_type_matrix.cpp b/source/fuzz/transformation_add_type_matrix.cpp
index b574b01..e3f1786 100644
--- a/source/fuzz/transformation_add_type_matrix.cpp
+++ b/source/fuzz/transformation_add_type_matrix.cpp
@@ -53,7 +53,7 @@
   in_operands.push_back(
       {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.column_count()}});
   auto type_instruction = MakeUnique<opt::Instruction>(
-      ir_context, SpvOpTypeMatrix, 0, message_.fresh_id(), in_operands);
+      ir_context, spv::Op::OpTypeMatrix, 0, message_.fresh_id(), in_operands);
   auto type_instruction_ptr = type_instruction.get();
   ir_context->module()->AddType(std::move(type_instruction));
 
diff --git a/source/fuzz/transformation_add_type_pointer.cpp b/source/fuzz/transformation_add_type_pointer.cpp
index c6c3945..c112615 100644
--- a/source/fuzz/transformation_add_type_pointer.cpp
+++ b/source/fuzz/transformation_add_type_pointer.cpp
@@ -24,9 +24,9 @@
     : message_(std::move(message)) {}
 
 TransformationAddTypePointer::TransformationAddTypePointer(
-    uint32_t fresh_id, SpvStorageClass storage_class, uint32_t base_type_id) {
+    uint32_t fresh_id, spv::StorageClass storage_class, uint32_t base_type_id) {
   message_.set_fresh_id(fresh_id);
-  message_.set_storage_class(storage_class);
+  message_.set_storage_class(uint32_t(storage_class));
   message_.set_base_type_id(base_type_id);
 }
 
@@ -48,7 +48,7 @@
       {SPV_OPERAND_TYPE_STORAGE_CLASS, {message_.storage_class()}},
       {SPV_OPERAND_TYPE_ID, {message_.base_type_id()}}};
   auto type_instruction = MakeUnique<opt::Instruction>(
-      ir_context, SpvOpTypePointer, 0, message_.fresh_id(), in_operands);
+      ir_context, spv::Op::OpTypePointer, 0, message_.fresh_id(), in_operands);
   auto type_instruction_ptr = type_instruction.get();
   ir_context->module()->AddType(std::move(type_instruction));
 
diff --git a/source/fuzz/transformation_add_type_pointer.h b/source/fuzz/transformation_add_type_pointer.h
index 8468c14..e4ef9d8 100644
--- a/source/fuzz/transformation_add_type_pointer.h
+++ b/source/fuzz/transformation_add_type_pointer.h
@@ -28,7 +28,8 @@
   explicit TransformationAddTypePointer(
       protobufs::TransformationAddTypePointer message);
 
-  TransformationAddTypePointer(uint32_t fresh_id, SpvStorageClass storage_class,
+  TransformationAddTypePointer(uint32_t fresh_id,
+                               spv::StorageClass storage_class,
                                uint32_t base_type_id);
 
   // - |message_.fresh_id| must not be used by the module
diff --git a/source/fuzz/transformation_add_type_struct.cpp b/source/fuzz/transformation_add_type_struct.cpp
index d7f0711..95fbbba 100644
--- a/source/fuzz/transformation_add_type_struct.cpp
+++ b/source/fuzz/transformation_add_type_struct.cpp
@@ -79,8 +79,9 @@
     operands.push_back({SPV_OPERAND_TYPE_ID, {type_id}});
   }
 
-  auto type_instruction = MakeUnique<opt::Instruction>(
-      ir_context, SpvOpTypeStruct, 0, message_.fresh_id(), std::move(operands));
+  auto type_instruction =
+      MakeUnique<opt::Instruction>(ir_context, spv::Op::OpTypeStruct, 0,
+                                   message_.fresh_id(), std::move(operands));
   auto type_instruction_ptr = type_instruction.get();
   ir_context->AddType(std::move(type_instruction));
 
diff --git a/source/fuzz/transformation_add_type_vector.cpp b/source/fuzz/transformation_add_type_vector.cpp
index 4da0ff0..a7b0fa7 100644
--- a/source/fuzz/transformation_add_type_vector.cpp
+++ b/source/fuzz/transformation_add_type_vector.cpp
@@ -57,7 +57,7 @@
          "Precondition: component count must be in range [2, 4].");
 
   auto type_instruction = MakeUnique<opt::Instruction>(
-      ir_context, SpvOpTypeVector, 0, message_.fresh_id(),
+      ir_context, spv::Op::OpTypeVector, 0, message_.fresh_id(),
       opt::Instruction::OperandList{
           {SPV_OPERAND_TYPE_ID, {message_.component_type_id()}},
           {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.component_count()}}});
diff --git a/source/fuzz/transformation_adjust_branch_weights.cpp b/source/fuzz/transformation_adjust_branch_weights.cpp
index 21fef25..2651938 100644
--- a/source/fuzz/transformation_adjust_branch_weights.cpp
+++ b/source/fuzz/transformation_adjust_branch_weights.cpp
@@ -47,7 +47,7 @@
     return false;
   }
 
-  SpvOp opcode = static_cast<SpvOp>(
+  spv::Op opcode = static_cast<spv::Op>(
       message_.instruction_descriptor().target_instruction_opcode());
 
   assert(instruction->opcode() == opcode &&
@@ -55,7 +55,7 @@
          "descriptor.");
 
   // Must be an OpBranchConditional instruction.
-  if (opcode != SpvOpBranchConditional) {
+  if (opcode != spv::Op::OpBranchConditional) {
     return false;
   }
 
diff --git a/source/fuzz/transformation_composite_construct.cpp b/source/fuzz/transformation_composite_construct.cpp
index 2d8e599..075b33d 100644
--- a/source/fuzz/transformation_composite_construct.cpp
+++ b/source/fuzz/transformation_composite_construct.cpp
@@ -121,7 +121,7 @@
 
   // Insert an OpCompositeConstruct instruction.
   auto new_instruction = MakeUnique<opt::Instruction>(
-      ir_context, SpvOpCompositeConstruct, message_.composite_type_id(),
+      ir_context, spv::Op::OpCompositeConstruct, message_.composite_type_id(),
       message_.fresh_id(), in_operands);
   auto new_instruction_ptr = new_instruction.get();
   insert_before.InsertBefore(std::move(new_instruction));
diff --git a/source/fuzz/transformation_composite_extract.cpp b/source/fuzz/transformation_composite_extract.cpp
index 0fbd4e1..7118432 100644
--- a/source/fuzz/transformation_composite_extract.cpp
+++ b/source/fuzz/transformation_composite_extract.cpp
@@ -67,7 +67,7 @@
   }
 
   if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(
-          SpvOpCompositeExtract, instruction_to_insert_before)) {
+          spv::Op::OpCompositeExtract, instruction_to_insert_before)) {
     return false;
   }
 
@@ -93,7 +93,7 @@
       FindInstruction(message_.instruction_to_insert_before(), ir_context);
   opt::Instruction* new_instruction =
       insert_before->InsertBefore(MakeUnique<opt::Instruction>(
-          ir_context, SpvOpCompositeExtract, extracted_type,
+          ir_context, spv::Op::OpCompositeExtract, extracted_type,
           message_.fresh_id(), extract_operands));
   ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction);
   ir_context->set_instr_block(new_instruction,
diff --git a/source/fuzz/transformation_composite_insert.cpp b/source/fuzz/transformation_composite_insert.cpp
index 60fa562..2f69c95 100644
--- a/source/fuzz/transformation_composite_insert.cpp
+++ b/source/fuzz/transformation_composite_insert.cpp
@@ -102,7 +102,7 @@
   // It must be possible to insert an OpCompositeInsert before this
   // instruction.
   return fuzzerutil::CanInsertOpcodeBeforeInstruction(
-      SpvOpCompositeInsert, instruction_to_insert_before);
+      spv::Op::OpCompositeInsert, instruction_to_insert_before);
 }
 
 void TransformationCompositeInsert::Apply(
@@ -126,8 +126,8 @@
   auto insert_before =
       FindInstruction(message_.instruction_to_insert_before(), ir_context);
   auto new_instruction = MakeUnique<opt::Instruction>(
-      ir_context, SpvOpCompositeInsert, composite_type_id, message_.fresh_id(),
-      std::move(in_operands));
+      ir_context, spv::Op::OpCompositeInsert, composite_type_id,
+      message_.fresh_id(), std::move(in_operands));
   auto new_instruction_ptr = new_instruction.get();
   insert_before->InsertBefore(std::move(new_instruction));
 
diff --git a/source/fuzz/transformation_duplicate_region_with_selection.cpp b/source/fuzz/transformation_duplicate_region_with_selection.cpp
index db88610..9176bf7 100644
--- a/source/fuzz/transformation_duplicate_region_with_selection.cpp
+++ b/source/fuzz/transformation_duplicate_region_with_selection.cpp
@@ -77,7 +77,7 @@
   // The entry and exit block ids must refer to blocks.
   for (auto block_id : {message_.entry_block_id(), message_.exit_block_id()}) {
     auto block_label = ir_context->get_def_use_mgr()->GetDef(block_id);
-    if (!block_label || block_label->opcode() != SpvOpLabel) {
+    if (!block_label || block_label->opcode() != spv::Op::OpLabel) {
       return false;
     }
   }
@@ -297,7 +297,7 @@
   // in the same function.
   std::unique_ptr<opt::BasicBlock> new_entry_block =
       MakeUnique<opt::BasicBlock>(MakeUnique<opt::Instruction>(
-          ir_context, SpvOpLabel, 0, message_.new_entry_fresh_id(),
+          ir_context, spv::Op::OpLabel, 0, message_.new_entry_fresh_id(),
           opt::Instruction::OperandList()));
   auto entry_block = ir_context->cfg()->block(message_.entry_block_id());
   auto enclosing_function = entry_block->GetParent();
@@ -310,7 +310,7 @@
   // Construct the merge block.
   std::unique_ptr<opt::BasicBlock> merge_block =
       MakeUnique<opt::BasicBlock>(MakeUnique<opt::Instruction>(
-          ir_context, SpvOpLabel, 0, message_.merge_label_fresh_id(),
+          ir_context, spv::Op::OpLabel, 0, message_.merge_label_fresh_id(),
           opt::Instruction::OperandList()));
 
   // Get the maps from the protobuf.
@@ -361,7 +361,7 @@
   exit_block->ForEachSuccessorLabel([this, ir_context](uint32_t label_id) {
     auto block = ir_context->cfg()->block(label_id);
     for (auto& instr : *block) {
-      if (instr.opcode() == SpvOpPhi) {
+      if (instr.opcode() == spv::Op::OpPhi) {
         instr.ForEachId([this](uint32_t* id) {
           if (*id == message_.exit_block_id()) {
             *id = message_.merge_label_fresh_id();
@@ -390,7 +390,7 @@
   // occurrence of |entry_block_pred_id| to the id of |new_entry|, because we
   // will insert |new_entry| before |entry_block|.
   for (auto& instr : *entry_block) {
-    if (instr.opcode() == SpvOpPhi) {
+    if (instr.opcode() == spv::Op::OpPhi) {
       instr.ForEachId([this, entry_block_pred_id](uint32_t* id) {
         if (*id == entry_block_pred_id) {
           *id = message_.new_entry_fresh_id();
@@ -421,7 +421,7 @@
 
     std::unique_ptr<opt::BasicBlock> duplicated_block =
         MakeUnique<opt::BasicBlock>(MakeUnique<opt::Instruction>(
-            ir_context, SpvOpLabel, 0,
+            ir_context, spv::Op::OpLabel, 0,
             original_label_to_duplicate_label.at(block->id()),
             opt::Instruction::OperandList()));
 
@@ -430,12 +430,12 @@
       // handled separately.
       if (block == exit_block && instr.IsBlockTerminator()) {
         switch (instr.opcode()) {
-          case SpvOpBranch:
-          case SpvOpBranchConditional:
-          case SpvOpReturn:
-          case SpvOpReturnValue:
-          case SpvOpUnreachable:
-          case SpvOpKill:
+          case spv::Op::OpBranch:
+          case spv::Op::OpBranchConditional:
+          case spv::Op::OpReturn:
+          case spv::Op::OpReturnValue:
+          case spv::Op::OpUnreachable:
+          case spv::Op::OpKill:
             continue;
           default:
             assert(false &&
@@ -497,7 +497,7 @@
         // the end of the region, as long as the result id is valid for use
         // with OpPhi.
         merge_block->AddInstruction(MakeUnique<opt::Instruction>(
-            ir_context, SpvOpPhi, instr.type_id(),
+            ir_context, spv::Op::OpPhi, instr.type_id(),
             original_id_to_phi_id.at(instr.result_id()),
             opt::Instruction::OperandList({
                 {SPV_OPERAND_TYPE_ID, {instr.result_id()}},
@@ -537,14 +537,14 @@
   // false, the execution proceeds in the first block of the
   // duplicated region.
   new_entry_block->AddInstruction(MakeUnique<opt::Instruction>(
-      ir_context, SpvOpSelectionMerge, 0, 0,
+      ir_context, spv::Op::OpSelectionMerge, 0, 0,
       opt::Instruction::OperandList(
           {{SPV_OPERAND_TYPE_ID, {message_.merge_label_fresh_id()}},
            {SPV_OPERAND_TYPE_SELECTION_CONTROL,
-            {SpvSelectionControlMaskNone}}})));
+            {uint32_t(spv::SelectionControlMask::MaskNone)}}})));
 
   new_entry_block->AddInstruction(MakeUnique<opt::Instruction>(
-      ir_context, SpvOpBranchConditional, 0, 0,
+      ir_context, spv::Op::OpBranchConditional, 0, 0,
       opt::Instruction::OperandList(
           {{SPV_OPERAND_TYPE_ID, {message_.condition_id()}},
            {SPV_OPERAND_TYPE_ID, {message_.entry_block_id()}},
@@ -563,7 +563,7 @@
   // |exit_block| and at the end of |duplicated_exit_block|, so that
   // the execution proceeds in the |merge_block|.
   opt::Instruction merge_branch_instr = opt::Instruction(
-      ir_context, SpvOpBranch, 0, 0,
+      ir_context, spv::Op::OpBranch, 0, 0,
       opt::Instruction::OperandList(
           {{SPV_OPERAND_TYPE_ID, {message_.merge_label_fresh_id()}}}));
   exit_block->AddInstruction(MakeUnique<opt::Instruction>(merge_branch_instr));
@@ -584,14 +584,14 @@
           return;
         }
         switch (user->opcode()) {
-          case SpvOpSwitch:
-          case SpvOpBranch:
-          case SpvOpBranchConditional:
-          case SpvOpLoopMerge:
-          case SpvOpSelectionMerge: {
+          case spv::Op::OpSwitch:
+          case spv::Op::OpBranch:
+          case spv::Op::OpBranchConditional:
+          case spv::Op::OpLoopMerge:
+          case spv::Op::OpSelectionMerge: {
             user->SetOperand(operand_index, {message_.new_entry_fresh_id()});
           } break;
-          case SpvOpName:
+          case spv::Op::OpName:
             break;
           default:
             assert(false &&
@@ -605,8 +605,8 @@
 
   opt::Instruction* merge_block_terminator = merge_block->terminator();
   switch (merge_block_terminator->opcode()) {
-    case SpvOpReturnValue:
-    case SpvOpBranchConditional: {
+    case spv::Op::OpReturnValue:
+    case spv::Op::OpBranchConditional: {
       uint32_t operand = merge_block_terminator->GetSingleWordInOperand(0);
       if (original_id_to_phi_id.count(operand)) {
         merge_block_terminator->SetInOperand(
@@ -699,19 +699,19 @@
       ir_context->get_def_use_mgr()->GetDef(instr.type_id());
 
   // It is invalid to apply OpPhi to void-typed values.
-  if (instr_type->opcode() == SpvOpTypeVoid) {
+  if (instr_type->opcode() == spv::Op::OpTypeVoid) {
     return false;
   }
 
   // Using pointers with OpPhi requires capability VariablePointers.
-  if (instr_type->opcode() == SpvOpTypePointer &&
+  if (instr_type->opcode() == spv::Op::OpTypePointer &&
       !ir_context->get_feature_mgr()->HasCapability(
-          SpvCapabilityVariablePointers)) {
+          spv::Capability::VariablePointers)) {
     return false;
   }
 
   // OpTypeSampledImage cannot be the result type of an OpPhi instruction.
-  if (instr_type->opcode() == SpvOpTypeSampledImage) {
+  if (instr_type->opcode() == spv::Op::OpTypeSampledImage) {
     return false;
   }
   return true;
diff --git a/source/fuzz/transformation_equation_instruction.cpp b/source/fuzz/transformation_equation_instruction.cpp
index 1e5dae9..72487a8 100644
--- a/source/fuzz/transformation_equation_instruction.cpp
+++ b/source/fuzz/transformation_equation_instruction.cpp
@@ -25,10 +25,11 @@
     : message_(std::move(message)) {}
 
 TransformationEquationInstruction::TransformationEquationInstruction(
-    uint32_t fresh_id, SpvOp opcode, const std::vector<uint32_t>& in_operand_id,
+    uint32_t fresh_id, spv::Op opcode,
+    const std::vector<uint32_t>& in_operand_id,
     const protobufs::InstructionDescriptor& instruction_to_insert_before) {
   message_.set_fresh_id(fresh_id);
-  message_.set_opcode(opcode);
+  message_.set_opcode(uint32_t(opcode));
   for (auto id : in_operand_id) {
     message_.add_in_operand_id(id);
   }
@@ -57,7 +58,7 @@
     if (!inst) {
       return false;
     }
-    if (inst->opcode() == SpvOpUndef) {
+    if (inst->opcode() == spv::Op::OpUndef) {
       return false;
     }
     if (transformation_context.GetFactManager()->IdIsIrrelevant(id)) {
@@ -88,7 +89,7 @@
       FindInstruction(message_.instruction_to_insert_before(), ir_context);
   opt::Instruction* new_instruction =
       insert_before->InsertBefore(MakeUnique<opt::Instruction>(
-          ir_context, static_cast<SpvOp>(message_.opcode()),
+          ir_context, static_cast<spv::Op>(message_.opcode()),
           MaybeGetResultTypeId(ir_context), message_.fresh_id(),
           std::move(in_operands)));
 
@@ -101,7 +102,7 @@
   if (!transformation_context->GetFactManager()->IdIsIrrelevant(
           message_.fresh_id())) {
     transformation_context->GetFactManager()->AddFactIdEquation(
-        message_.fresh_id(), static_cast<SpvOp>(message_.opcode()), rhs_id);
+        message_.fresh_id(), static_cast<spv::Op>(message_.opcode()), rhs_id);
   }
 }
 
@@ -113,10 +114,10 @@
 
 uint32_t TransformationEquationInstruction::MaybeGetResultTypeId(
     opt::IRContext* ir_context) const {
-  auto opcode = static_cast<SpvOp>(message_.opcode());
+  auto opcode = static_cast<spv::Op>(message_.opcode());
   switch (opcode) {
-    case SpvOpConvertUToF:
-    case SpvOpConvertSToF: {
+    case spv::Op::OpConvertUToF:
+    case spv::Op::OpConvertSToF: {
       if (message_.in_operand_id_size() != 1) {
         return 0;
       }
@@ -148,7 +149,7 @@
                                              type->AsInteger()->width());
       }
     }
-    case SpvOpBitcast: {
+    case spv::Op::OpBitcast: {
       if (message_.in_operand_id_size() != 1) {
         return 0;
       }
@@ -210,8 +211,8 @@
         return 0;
       }
     }
-    case SpvOpIAdd:
-    case SpvOpISub: {
+    case spv::Op::OpIAdd:
+    case spv::Op::OpISub: {
       if (message_.in_operand_id_size() != 2) {
         return 0;
       }
@@ -249,7 +250,7 @@
              "A type must have been found for the first operand.");
       return first_operand_type_id;
     }
-    case SpvOpLogicalNot: {
+    case spv::Op::OpLogicalNot: {
       if (message_.in_operand_id().size() != 1) {
         return 0;
       }
@@ -267,7 +268,7 @@
       }
       return operand_inst->type_id();
     }
-    case SpvOpSNegate: {
+    case spv::Op::OpSNegate: {
       if (message_.in_operand_id().size() != 1) {
         return 0;
       }
diff --git a/source/fuzz/transformation_equation_instruction.h b/source/fuzz/transformation_equation_instruction.h
index ae32a1a..40118d9 100644
--- a/source/fuzz/transformation_equation_instruction.h
+++ b/source/fuzz/transformation_equation_instruction.h
@@ -31,7 +31,7 @@
       protobufs::TransformationEquationInstruction message);
 
   TransformationEquationInstruction(
-      uint32_t fresh_id, SpvOp opcode,
+      uint32_t fresh_id, spv::Op opcode,
       const std::vector<uint32_t>& in_operand_id,
       const protobufs::InstructionDescriptor& instruction_to_insert_before);
 
diff --git a/source/fuzz/transformation_expand_vector_reduction.cpp b/source/fuzz/transformation_expand_vector_reduction.cpp
index bafcf92..4c13ec1 100644
--- a/source/fuzz/transformation_expand_vector_reduction.cpp
+++ b/source/fuzz/transformation_expand_vector_reduction.cpp
@@ -44,7 +44,8 @@
   }
 
   // |instruction| must be OpAny or OpAll.
-  if (instruction->opcode() != SpvOpAny && instruction->opcode() != SpvOpAll) {
+  if (instruction->opcode() != spv::Op::OpAny &&
+      instruction->opcode() != spv::Op::OpAll) {
     return false;
   }
 
@@ -92,10 +93,11 @@
 
   for (uint32_t i = 0; i < vector_component_count; i++) {
     // Extracts the i-th |vector| component.
-    auto vector_component = opt::Instruction(
-        ir_context, SpvOpCompositeExtract, instruction->type_id(), *fresh_id++,
-        {{SPV_OPERAND_TYPE_ID, {vector->result_id()}},
-         {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}});
+    auto vector_component =
+        opt::Instruction(ir_context, spv::Op::OpCompositeExtract,
+                         instruction->type_id(), *fresh_id++,
+                         {{SPV_OPERAND_TYPE_ID, {vector->result_id()}},
+                          {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}});
     instruction->InsertBefore(MakeUnique<opt::Instruction>(vector_component));
     fuzzerutil::UpdateModuleIdBound(ir_context, vector_component.result_id());
     vector_components.push_back(vector_component.result_id());
@@ -104,7 +106,8 @@
   // The first two |vector| components are used in the first logical operation.
   auto logical_instruction = opt::Instruction(
       ir_context,
-      instruction->opcode() == SpvOpAny ? SpvOpLogicalOr : SpvOpLogicalAnd,
+      instruction->opcode() == spv::Op::OpAny ? spv::Op::OpLogicalOr
+                                              : spv::Op::OpLogicalAnd,
       instruction->type_id(), *fresh_id++,
       {{SPV_OPERAND_TYPE_ID, {vector_components[0]}},
        {SPV_OPERAND_TYPE_ID, {vector_components[1]}}});
diff --git a/source/fuzz/transformation_flatten_conditional_branch.cpp b/source/fuzz/transformation_flatten_conditional_branch.cpp
index 127e762..f8d1c33 100644
--- a/source/fuzz/transformation_flatten_conditional_branch.cpp
+++ b/source/fuzz/transformation_flatten_conditional_branch.cpp
@@ -48,12 +48,12 @@
 
   // The block must have been found and it must be a selection header.
   if (!header_block || !header_block->GetMergeInst() ||
-      header_block->GetMergeInst()->opcode() != SpvOpSelectionMerge) {
+      header_block->GetMergeInst()->opcode() != spv::Op::OpSelectionMerge) {
     return false;
   }
 
   // The header block must end with an OpBranchConditional instruction.
-  if (header_block->terminator()->opcode() != SpvOpBranchConditional) {
+  if (header_block->terminator()->opcode() != spv::Op::OpBranchConditional) {
     return false;
   }
 
@@ -164,14 +164,14 @@
                opt::Instruction* phi_result_type =
                    ir_context->get_def_use_mgr()->GetDef(inst->type_id());
                switch (phi_result_type->opcode()) {
-                 case SpvOpTypeBool:
-                 case SpvOpTypeInt:
-                 case SpvOpTypeFloat:
-                 case SpvOpTypePointer:
+                 case spv::Op::OpTypeBool:
+                 case spv::Op::OpTypeInt:
+                 case spv::Op::OpTypeFloat:
+                 case spv::Op::OpTypePointer:
                    // Fine: OpSelect can work directly on scalar and pointer
                    // types.
                    return true;
-                 case SpvOpTypeVector: {
+                 case spv::Op::OpTypeVector: {
                    // In its restricted form, OpSelect can only select between
                    // vectors if the condition of the select is a boolean
                    // boolean vector.  We thus require the appropriate boolean
@@ -288,8 +288,8 @@
 
       current_block->ForEachInst(
           [&problematic_instructions](opt::Instruction* instruction) {
-            if (instruction->opcode() != SpvOpLabel &&
-                instruction->opcode() != SpvOpBranch &&
+            if (instruction->opcode() != spv::Op::OpLabel &&
+                instruction->opcode() != spv::Op::OpBranch &&
                 !fuzzerutil::InstructionHasNoSideEffects(*instruction)) {
               problematic_instructions.push_back(instruction);
             }
@@ -381,7 +381,7 @@
   // Add a new, unconditional, branch instruction from the current header to
   // |after_header|.
   header_block->AddInstruction(MakeUnique<opt::Instruction>(
-      ir_context, SpvOpBranch, 0, 0,
+      ir_context, spv::Op::OpBranch, 0, 0,
       opt::Instruction::OperandList{{SPV_OPERAND_TYPE_ID, {after_header}}}));
 
   // If the first branch to be laid out exists, change the branch instruction so
@@ -437,8 +437,8 @@
         std::set<opt::Instruction*>* instructions_that_need_ids) {
   uint32_t merge_block_id = header->MergeBlockIdIfAny();
   assert(merge_block_id &&
-         header->GetMergeInst()->opcode() == SpvOpSelectionMerge &&
-         header->terminator()->opcode() == SpvOpBranchConditional &&
+         header->GetMergeInst()->opcode() == spv::Op::OpSelectionMerge &&
+         header->terminator()->opcode() == spv::Op::OpBranchConditional &&
          "|header| must be the header of a conditional.");
 
   // |header| must be reachable.
@@ -508,7 +508,7 @@
     }
 
     // The terminator instruction for the block must be OpBranch.
-    if (block->terminator()->opcode() != SpvOpBranch) {
+    if (block->terminator()->opcode() != spv::Op::OpBranch) {
       return false;
     }
 
@@ -524,7 +524,7 @@
         [ir_context, instructions_that_need_ids,
          &synonym_base_objects](opt::Instruction* instruction) {
           // We can ignore OpLabel instructions.
-          if (instruction->opcode() == SpvOpLabel) {
+          if (instruction->opcode() == spv::Op::OpLabel) {
             return true;
           }
 
@@ -539,7 +539,7 @@
 
           // If the instruction is a branch, it must be an unconditional branch.
           if (instruction->IsBranch()) {
-            return instruction->opcode() == SpvOpBranch;
+            return instruction->opcode() == spv::Op::OpBranch;
           }
 
           // We cannot go ahead if we encounter an instruction that cannot be
@@ -644,7 +644,7 @@
 
   // Add an unconditional branch from |execute_block| to |merge_block|.
   execute_block->AddInstruction(MakeUnique<opt::Instruction>(
-      ir_context, SpvOpBranch, 0, 0,
+      ir_context, spv::Op::OpBranch, 0, 0,
       opt::Instruction::OperandList{
           {SPV_OPERAND_TYPE_ID, {merge_block->id()}}}));
 
@@ -668,10 +668,10 @@
     }
 
     // Create a new block using |fresh_ids.alternative_block_id| for its label.
-    auto alternative_block_temp =
-        MakeUnique<opt::BasicBlock>(MakeUnique<opt::Instruction>(
-            ir_context, SpvOpLabel, 0, wrapper_info.alternative_block_id(),
-            opt::Instruction::OperandList{}));
+    auto alternative_block_temp = MakeUnique<opt::BasicBlock>(
+        MakeUnique<opt::Instruction>(ir_context, spv::Op::OpLabel, 0,
+                                     wrapper_info.alternative_block_id(),
+                                     opt::Instruction::OperandList{}));
 
     // Keep the original result id of the instruction in a variable.
     uint32_t original_result_id = instruction->result_id();
@@ -685,14 +685,14 @@
       // If there is an available id to copy from, the placeholder instruction
       // will be %placeholder_result_id = OpCopyObject %type %value_to_copy_id
       alternative_block_temp->AddInstruction(MakeUnique<opt::Instruction>(
-          ir_context, SpvOpCopyObject, instruction->type_id(),
+          ir_context, spv::Op::OpCopyObject, instruction->type_id(),
           wrapper_info.placeholder_result_id(),
           opt::Instruction::OperandList{
               {SPV_OPERAND_TYPE_ID, {wrapper_info.value_to_copy_id()}}}));
     } else {
       // If there is no such id, use an OpUndef instruction.
       alternative_block_temp->AddInstruction(MakeUnique<opt::Instruction>(
-          ir_context, SpvOpUndef, instruction->type_id(),
+          ir_context, spv::Op::OpUndef, instruction->type_id(),
           wrapper_info.placeholder_result_id(),
           opt::Instruction::OperandList{}));
     }
@@ -702,7 +702,7 @@
 
     // Add an unconditional branch from the new block to the merge block.
     alternative_block_temp->AddInstruction(MakeUnique<opt::Instruction>(
-        ir_context, SpvOpBranch, 0, 0,
+        ir_context, spv::Op::OpBranch, 0, 0,
         opt::Instruction::OperandList{
             {SPV_OPERAND_TYPE_ID, {merge_block->id()}}}));
 
@@ -714,7 +714,7 @@
     // merge block, which will either take the value of the result of the
     // instruction or the placeholder value defined in the alternative block.
     merge_block->begin().InsertBefore(MakeUnique<opt::Instruction>(
-        ir_context, SpvOpPhi, instruction->type_id(), original_result_id,
+        ir_context, spv::Op::OpPhi, instruction->type_id(), original_result_id,
         opt::Instruction::OperandList{
             {SPV_OPERAND_TYPE_ID, {instruction->result_id()}},
             {SPV_OPERAND_TYPE_ID, {execute_block->id()}},
@@ -738,16 +738,17 @@
 
   // Add an OpSelectionMerge instruction to the block.
   block->AddInstruction(MakeUnique<opt::Instruction>(
-      ir_context, SpvOpSelectionMerge, 0, 0,
-      opt::Instruction::OperandList{{SPV_OPERAND_TYPE_ID, {merge_block->id()}},
-                                    {SPV_OPERAND_TYPE_SELECTION_CONTROL,
-                                     {SpvSelectionControlMaskNone}}}));
+      ir_context, spv::Op::OpSelectionMerge, 0, 0,
+      opt::Instruction::OperandList{
+          {SPV_OPERAND_TYPE_ID, {merge_block->id()}},
+          {SPV_OPERAND_TYPE_SELECTION_CONTROL,
+           {uint32_t(spv::SelectionControlMask::MaskNone)}}}));
 
   // Add an OpBranchConditional, to the block, using |condition_id| as the
   // condition and branching to |if_block_id| if the condition is true and to
   // |else_block_id| if the condition is false.
   block->AddInstruction(MakeUnique<opt::Instruction>(
-      ir_context, SpvOpBranchConditional, 0, 0,
+      ir_context, spv::Op::OpBranchConditional, 0, 0,
       opt::Instruction::OperandList{{SPV_OPERAND_TYPE_ID, {condition_id}},
                                     {SPV_OPERAND_TYPE_ID, {if_block_id}},
                                     {SPV_OPERAND_TYPE_ID, {else_block_id}}}));
@@ -764,26 +765,26 @@
 
   // We cannot handle barrier instructions, while we should be able to handle
   // all other instructions by enclosing them inside a conditional.
-  if (instruction.opcode() == SpvOpControlBarrier ||
-      instruction.opcode() == SpvOpMemoryBarrier ||
-      instruction.opcode() == SpvOpNamedBarrierInitialize ||
-      instruction.opcode() == SpvOpMemoryNamedBarrier ||
-      instruction.opcode() == SpvOpTypeNamedBarrier) {
+  if (instruction.opcode() == spv::Op::OpControlBarrier ||
+      instruction.opcode() == spv::Op::OpMemoryBarrier ||
+      instruction.opcode() == spv::Op::OpNamedBarrierInitialize ||
+      instruction.opcode() == spv::Op::OpMemoryNamedBarrier ||
+      instruction.opcode() == spv::Op::OpTypeNamedBarrier) {
     return false;
   }
 
   // We cannot handle OpSampledImage instructions, as they need to be in the
   // same block as their use.
-  if (instruction.opcode() == SpvOpSampledImage) {
+  if (instruction.opcode() == spv::Op::OpSampledImage) {
     return false;
   }
 
   // We cannot handle a sampled image load, because we re-work loads using
   // conditional branches and OpPhi instructions, and the result type of OpPhi
   // cannot be OpTypeSampledImage.
-  if (instruction.opcode() == SpvOpLoad &&
+  if (instruction.opcode() == spv::Op::OpLoad &&
       ir_context->get_def_use_mgr()->GetDef(instruction.type_id())->opcode() ==
-          SpvOpTypeSampledImage) {
+          spv::Op::OpTypeSampledImage) {
     return false;
   }
 
@@ -863,7 +864,7 @@
     in_operands.emplace_back(branch_condition_operand);
   }
   block->begin()->InsertBefore(MakeUnique<opt::Instruction>(
-      ir_context, SpvOpCompositeConstruct,
+      ir_context, spv::Op::OpCompositeConstruct,
       fuzzerutil::MaybeGetVectorType(
           ir_context, fuzzerutil::MaybeGetBoolType(ir_context), dimension),
       fresh_id, in_operands));
@@ -906,7 +907,7 @@
         opt::Operand selector_operand = branch_condition_operand;
         opt::Instruction* type_inst =
             ir_context->get_def_use_mgr()->GetDef(phi_inst->type_id());
-        if (type_inst->opcode() == SpvOpTypeVector) {
+        if (type_inst->opcode() == spv::Op::OpTypeVector) {
           uint32_t dimension = type_inst->GetSingleWordInOperand(1);
           switch (dimension) {
             case 2:
@@ -1012,7 +1013,7 @@
           operands.emplace_back(phi_inst->GetInOperand(2));
           operands.emplace_back(phi_inst->GetInOperand(0));
         }
-        phi_inst->SetOpcode(SpvOpSelect);
+        phi_inst->SetOpcode(spv::Op::OpSelect);
         phi_inst->SetInOperands(std::move(operands));
       });
 
diff --git a/source/fuzz/transformation_function_call.cpp b/source/fuzz/transformation_function_call.cpp
index 0f88ce5..e96a230 100644
--- a/source/fuzz/transformation_function_call.cpp
+++ b/source/fuzz/transformation_function_call.cpp
@@ -49,7 +49,7 @@
   // The function must exist
   auto callee_inst =
       ir_context->get_def_use_mgr()->GetDef(message_.callee_id());
-  if (!callee_inst || callee_inst->opcode() != SpvOpFunction) {
+  if (!callee_inst || callee_inst->opcode() != spv::Op::OpFunction) {
     return false;
   }
 
@@ -60,7 +60,7 @@
 
   auto callee_type_inst = ir_context->get_def_use_mgr()->GetDef(
       callee_inst->GetSingleWordInOperand(1));
-  assert(callee_type_inst->opcode() == SpvOpTypeFunction &&
+  assert(callee_type_inst->opcode() == spv::Op::OpTypeFunction &&
          "Bad function type.");
 
   // The number of expected function arguments must match the number of given
@@ -78,7 +78,7 @@
   if (!insert_before) {
     return false;
   }
-  if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpFunctionCall,
+  if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(spv::Op::OpFunctionCall,
                                                     insert_before)) {
     return false;
   }
@@ -116,10 +116,10 @@
     }
     opt::Instruction* arg_type_inst =
         ir_context->get_def_use_mgr()->GetDef(arg_inst->type_id());
-    if (arg_type_inst->opcode() == SpvOpTypePointer) {
+    if (arg_type_inst->opcode() == spv::Op::OpTypePointer) {
       switch (arg_inst->opcode()) {
-        case SpvOpFunctionParameter:
-        case SpvOpVariable:
+        case spv::Op::OpFunctionParameter:
+        case spv::Op::OpVariable:
           // These are OK
           break;
         default:
@@ -173,7 +173,7 @@
   // Insert the function call before the instruction specified in the message.
   FindInstruction(message_.instruction_to_insert_before(), ir_context)
       ->InsertBefore(MakeUnique<opt::Instruction>(
-          ir_context, SpvOpFunctionCall, return_type, message_.fresh_id(),
+          ir_context, spv::Op::OpFunctionCall, return_type, message_.fresh_id(),
           operands));
   // Invalidate all analyses since we have changed the module.
   ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
diff --git a/source/fuzz/transformation_inline_function.cpp b/source/fuzz/transformation_inline_function.cpp
index a48b817..69e88fd 100644
--- a/source/fuzz/transformation_inline_function.cpp
+++ b/source/fuzz/transformation_inline_function.cpp
@@ -62,7 +62,8 @@
       ir_context->get_instr_block(function_call_instruction);
   if (function_call_instruction !=
           &*--function_call_instruction_block->tail() ||
-      function_call_instruction_block->terminator()->opcode() != SpvOpBranch) {
+      function_call_instruction_block->terminator()->opcode() !=
+          spv::Op::OpBranch) {
     return false;
   }
 
@@ -143,7 +144,7 @@
   for (auto& entry_block_instruction : *called_function->entry()) {
     opt::Instruction* inlined_instruction;
 
-    if (entry_block_instruction.opcode() == SpvOpVariable) {
+    if (entry_block_instruction.opcode() == spv::Op::OpVariable) {
       // All OpVariable instructions in a function must be in the first block
       // in the function.
       inlined_instruction = caller_function->begin()->begin()->InsertBefore(
@@ -206,7 +207,7 @@
         block_containing_function_call->id(),
         [ir_context, new_return_block_id, successor_block](
             opt::Instruction* use_instruction, uint32_t operand_index) {
-          if (use_instruction->opcode() == SpvOpPhi &&
+          if (use_instruction->opcode() == spv::Op::OpPhi &&
               ir_context->get_instr_block(use_instruction) == successor_block) {
             use_instruction->SetOperand(operand_index, {new_return_block_id});
           }
@@ -234,7 +235,7 @@
   // |function_call_instruction| must be defined and must be an OpFunctionCall
   // instruction.
   if (!function_call_instruction ||
-      function_call_instruction->opcode() != SpvOpFunctionCall) {
+      function_call_instruction->opcode() != spv::Op::OpFunctionCall) {
     return false;
   }
 
@@ -331,13 +332,14 @@
             ->terminator()
             ->GetSingleWordInOperand(0);
     switch (instruction_to_be_inlined->opcode()) {
-      case SpvOpReturn:
+      case spv::Op::OpReturn:
         instruction_to_be_inlined->AddOperand(
             {SPV_OPERAND_TYPE_ID, {successor_block_id}});
         break;
-      case SpvOpReturnValue: {
+      case spv::Op::OpReturnValue: {
         instruction_to_be_inlined->InsertBefore(MakeUnique<opt::Instruction>(
-            ir_context, SpvOpCopyObject, function_call_instruction->type_id(),
+            ir_context, spv::Op::OpCopyObject,
+            function_call_instruction->type_id(),
             function_call_instruction->result_id(),
             opt::Instruction::OperandList(
                 {{SPV_OPERAND_TYPE_ID,
@@ -348,7 +350,7 @@
       default:
         break;
     }
-    instruction_to_be_inlined->SetOpcode(SpvOpBranch);
+    instruction_to_be_inlined->SetOpcode(spv::Op::OpBranch);
   }
 }
 
diff --git a/source/fuzz/transformation_invert_comparison_operator.cpp b/source/fuzz/transformation_invert_comparison_operator.cpp
index ed7358f..49801e3 100644
--- a/source/fuzz/transformation_invert_comparison_operator.cpp
+++ b/source/fuzz/transformation_invert_comparison_operator.cpp
@@ -47,7 +47,8 @@
   auto iter = fuzzerutil::GetIteratorForInstruction(block, inst);
   ++iter;
   assert(iter != block->end() && "Instruction can't be the last in the block");
-  assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLogicalNot, iter) &&
+  assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(spv::Op::OpLogicalNot,
+                                                      iter) &&
          "Can't insert negation after comparison operator");
 
   // |message_.fresh_id| must be fresh.
@@ -65,7 +66,7 @@
   ++iter;
 
   iter.InsertBefore(MakeUnique<opt::Instruction>(
-      ir_context, SpvOpLogicalNot, inst->type_id(), inst->result_id(),
+      ir_context, spv::Op::OpLogicalNot, inst->type_id(), inst->result_id(),
       opt::Instruction::OperandList{
           {SPV_OPERAND_TYPE_ID, {message_.fresh_id()}}}));
 
@@ -82,88 +83,88 @@
 }
 
 bool TransformationInvertComparisonOperator::IsInversionSupported(
-    SpvOp opcode) {
+    spv::Op opcode) {
   switch (opcode) {
-    case SpvOpSGreaterThan:
-    case SpvOpSGreaterThanEqual:
-    case SpvOpSLessThan:
-    case SpvOpSLessThanEqual:
-    case SpvOpUGreaterThan:
-    case SpvOpUGreaterThanEqual:
-    case SpvOpULessThan:
-    case SpvOpULessThanEqual:
-    case SpvOpIEqual:
-    case SpvOpINotEqual:
-    case SpvOpFOrdEqual:
-    case SpvOpFUnordEqual:
-    case SpvOpFOrdNotEqual:
-    case SpvOpFUnordNotEqual:
-    case SpvOpFOrdLessThan:
-    case SpvOpFUnordLessThan:
-    case SpvOpFOrdLessThanEqual:
-    case SpvOpFUnordLessThanEqual:
-    case SpvOpFOrdGreaterThan:
-    case SpvOpFUnordGreaterThan:
-    case SpvOpFOrdGreaterThanEqual:
-    case SpvOpFUnordGreaterThanEqual:
+    case spv::Op::OpSGreaterThan:
+    case spv::Op::OpSGreaterThanEqual:
+    case spv::Op::OpSLessThan:
+    case spv::Op::OpSLessThanEqual:
+    case spv::Op::OpUGreaterThan:
+    case spv::Op::OpUGreaterThanEqual:
+    case spv::Op::OpULessThan:
+    case spv::Op::OpULessThanEqual:
+    case spv::Op::OpIEqual:
+    case spv::Op::OpINotEqual:
+    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::OpFOrdLessThanEqual:
+    case spv::Op::OpFUnordLessThanEqual:
+    case spv::Op::OpFOrdGreaterThan:
+    case spv::Op::OpFUnordGreaterThan:
+    case spv::Op::OpFOrdGreaterThanEqual:
+    case spv::Op::OpFUnordGreaterThanEqual:
       return true;
     default:
       return false;
   }
 }
 
-SpvOp TransformationInvertComparisonOperator::InvertOpcode(SpvOp opcode) {
+spv::Op TransformationInvertComparisonOperator::InvertOpcode(spv::Op opcode) {
   assert(IsInversionSupported(opcode) && "Inversion must be supported");
 
   switch (opcode) {
-    case SpvOpSGreaterThan:
-      return SpvOpSLessThanEqual;
-    case SpvOpSGreaterThanEqual:
-      return SpvOpSLessThan;
-    case SpvOpSLessThan:
-      return SpvOpSGreaterThanEqual;
-    case SpvOpSLessThanEqual:
-      return SpvOpSGreaterThan;
-    case SpvOpUGreaterThan:
-      return SpvOpULessThanEqual;
-    case SpvOpUGreaterThanEqual:
-      return SpvOpULessThan;
-    case SpvOpULessThan:
-      return SpvOpUGreaterThanEqual;
-    case SpvOpULessThanEqual:
-      return SpvOpUGreaterThan;
-    case SpvOpIEqual:
-      return SpvOpINotEqual;
-    case SpvOpINotEqual:
-      return SpvOpIEqual;
-    case SpvOpFOrdEqual:
-      return SpvOpFUnordNotEqual;
-    case SpvOpFUnordEqual:
-      return SpvOpFOrdNotEqual;
-    case SpvOpFOrdNotEqual:
-      return SpvOpFUnordEqual;
-    case SpvOpFUnordNotEqual:
-      return SpvOpFOrdEqual;
-    case SpvOpFOrdLessThan:
-      return SpvOpFUnordGreaterThanEqual;
-    case SpvOpFUnordLessThan:
-      return SpvOpFOrdGreaterThanEqual;
-    case SpvOpFOrdLessThanEqual:
-      return SpvOpFUnordGreaterThan;
-    case SpvOpFUnordLessThanEqual:
-      return SpvOpFOrdGreaterThan;
-    case SpvOpFOrdGreaterThan:
-      return SpvOpFUnordLessThanEqual;
-    case SpvOpFUnordGreaterThan:
-      return SpvOpFOrdLessThanEqual;
-    case SpvOpFOrdGreaterThanEqual:
-      return SpvOpFUnordLessThan;
-    case SpvOpFUnordGreaterThanEqual:
-      return SpvOpFOrdLessThan;
+    case spv::Op::OpSGreaterThan:
+      return spv::Op::OpSLessThanEqual;
+    case spv::Op::OpSGreaterThanEqual:
+      return spv::Op::OpSLessThan;
+    case spv::Op::OpSLessThan:
+      return spv::Op::OpSGreaterThanEqual;
+    case spv::Op::OpSLessThanEqual:
+      return spv::Op::OpSGreaterThan;
+    case spv::Op::OpUGreaterThan:
+      return spv::Op::OpULessThanEqual;
+    case spv::Op::OpUGreaterThanEqual:
+      return spv::Op::OpULessThan;
+    case spv::Op::OpULessThan:
+      return spv::Op::OpUGreaterThanEqual;
+    case spv::Op::OpULessThanEqual:
+      return spv::Op::OpUGreaterThan;
+    case spv::Op::OpIEqual:
+      return spv::Op::OpINotEqual;
+    case spv::Op::OpINotEqual:
+      return spv::Op::OpIEqual;
+    case spv::Op::OpFOrdEqual:
+      return spv::Op::OpFUnordNotEqual;
+    case spv::Op::OpFUnordEqual:
+      return spv::Op::OpFOrdNotEqual;
+    case spv::Op::OpFOrdNotEqual:
+      return spv::Op::OpFUnordEqual;
+    case spv::Op::OpFUnordNotEqual:
+      return spv::Op::OpFOrdEqual;
+    case spv::Op::OpFOrdLessThan:
+      return spv::Op::OpFUnordGreaterThanEqual;
+    case spv::Op::OpFUnordLessThan:
+      return spv::Op::OpFOrdGreaterThanEqual;
+    case spv::Op::OpFOrdLessThanEqual:
+      return spv::Op::OpFUnordGreaterThan;
+    case spv::Op::OpFUnordLessThanEqual:
+      return spv::Op::OpFOrdGreaterThan;
+    case spv::Op::OpFOrdGreaterThan:
+      return spv::Op::OpFUnordLessThanEqual;
+    case spv::Op::OpFUnordGreaterThan:
+      return spv::Op::OpFOrdLessThanEqual;
+    case spv::Op::OpFOrdGreaterThanEqual:
+      return spv::Op::OpFUnordLessThan;
+    case spv::Op::OpFUnordGreaterThanEqual:
+      return spv::Op::OpFOrdLessThan;
     default:
       // The program will fail in the debug mode because of the assertion
       // at the beginning of the function.
-      return SpvOpNop;
+      return spv::Op::OpNop;
   }
 }
 
diff --git a/source/fuzz/transformation_invert_comparison_operator.h b/source/fuzz/transformation_invert_comparison_operator.h
index f00f62b..39c2fe0 100644
--- a/source/fuzz/transformation_invert_comparison_operator.h
+++ b/source/fuzz/transformation_invert_comparison_operator.h
@@ -50,11 +50,11 @@
   protobufs::Transformation ToMessage() const override;
 
   // Returns true if |opcode| is supported by this transformation.
-  static bool IsInversionSupported(SpvOp opcode);
+  static bool IsInversionSupported(spv::Op opcode);
 
  private:
   // Returns an inverted |opcode| (e.g. < becomes >=, == becomes != etc.)
-  static SpvOp InvertOpcode(SpvOp opcode);
+  static spv::Op InvertOpcode(spv::Op opcode);
 
   protobufs::TransformationInvertComparisonOperator message_;
 };
diff --git a/source/fuzz/transformation_load.cpp b/source/fuzz/transformation_load.cpp
index bf48d99..1cfde77 100644
--- a/source/fuzz/transformation_load.cpp
+++ b/source/fuzz/transformation_load.cpp
@@ -52,15 +52,15 @@
   // The type must indeed be a pointer type.
   auto pointer_type = ir_context->get_def_use_mgr()->GetDef(pointer->type_id());
   assert(pointer_type && "Type id must be defined.");
-  if (pointer_type->opcode() != SpvOpTypePointer) {
+  if (pointer_type->opcode() != spv::Op::OpTypePointer) {
     return false;
   }
   // We do not want to allow loading from null or undefined pointers, as it is
   // not clear how punishing the consequences of doing so are from a semantics
   // point of view.
   switch (pointer->opcode()) {
-    case SpvOpConstantNull:
-    case SpvOpUndef:
+    case spv::Op::OpConstantNull:
+    case spv::Op::OpUndef:
       return false;
     default:
       break;
@@ -74,13 +74,13 @@
     return false;
   }
   // ... and it must be legitimate to insert a load before it.
-  if (!message_.is_atomic() &&
-      !fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad, insert_before)) {
+  if (!message_.is_atomic() && !fuzzerutil::CanInsertOpcodeBeforeInstruction(
+                                   spv::Op::OpLoad, insert_before)) {
     return false;
   }
 
   if (message_.is_atomic() && !fuzzerutil::CanInsertOpcodeBeforeInstruction(
-                                  SpvOpAtomicLoad, insert_before)) {
+                                  spv::Op::OpAtomicLoad, insert_before)) {
     return false;
   }
 
@@ -99,10 +99,10 @@
     }
     // The memory scope and memory semantics instructions must have the
     // 'OpConstant' opcode.
-    if (memory_scope_instruction->opcode() != SpvOpConstant) {
+    if (memory_scope_instruction->opcode() != spv::Op::OpConstant) {
       return false;
     }
-    if (memory_semantics_instruction->opcode() != SpvOpConstant) {
+    if (memory_semantics_instruction->opcode() != spv::Op::OpConstant) {
       return false;
     }
     // The memory scope and memory semantics need to be available before
@@ -119,12 +119,12 @@
     // operand type with signedness does not matters.
     if (ir_context->get_def_use_mgr()
             ->GetDef(memory_scope_instruction->type_id())
-            ->opcode() != SpvOpTypeInt) {
+            ->opcode() != spv::Op::OpTypeInt) {
       return false;
     }
     if (ir_context->get_def_use_mgr()
             ->GetDef(memory_semantics_instruction->type_id())
-            ->opcode() != SpvOpTypeInt) {
+            ->opcode() != spv::Op::OpTypeInt) {
       return false;
     }
 
@@ -146,20 +146,20 @@
       return false;
     }
 
-    // The memory scope constant value must be that of SpvScopeInvocation.
+    // The memory scope constant value must be that of spv::Scope::Invocation.
     auto memory_scope_const_value =
-        memory_scope_instruction->GetSingleWordInOperand(0);
-    if (memory_scope_const_value != SpvScopeInvocation) {
+        spv::Scope(memory_scope_instruction->GetSingleWordInOperand(0));
+    if (memory_scope_const_value != spv::Scope::Invocation) {
       return false;
     }
 
     // The memory semantics constant value must match the storage class of the
     // pointer being loaded from.
-    auto memory_semantics_const_value = static_cast<SpvMemorySemanticsMask>(
+    auto memory_semantics_const_value = static_cast<spv::MemorySemanticsMask>(
         memory_semantics_instruction->GetSingleWordInOperand(0));
     if (memory_semantics_const_value !=
         fuzzerutil::GetMemorySemanticsForStorageClass(
-            static_cast<SpvStorageClass>(
+            static_cast<spv::StorageClass>(
                 pointer_type->GetSingleWordInOperand(0)))) {
       return false;
     }
@@ -180,7 +180,7 @@
     auto insert_before =
         FindInstruction(message_.instruction_to_insert_before(), ir_context);
     auto new_instruction = MakeUnique<opt::Instruction>(
-        ir_context, SpvOpAtomicLoad, result_type, message_.fresh_id(),
+        ir_context, spv::Op::OpAtomicLoad, result_type, message_.fresh_id(),
         opt::Instruction::OperandList(
             {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}},
              {SPV_OPERAND_TYPE_SCOPE_ID, {message_.memory_scope_id()}},
@@ -201,7 +201,7 @@
     auto insert_before =
         FindInstruction(message_.instruction_to_insert_before(), ir_context);
     auto new_instruction = MakeUnique<opt::Instruction>(
-        ir_context, SpvOpLoad, result_type, message_.fresh_id(),
+        ir_context, spv::Op::OpLoad, result_type, message_.fresh_id(),
         opt::Instruction::OperandList(
             {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}}));
     auto new_instruction_ptr = new_instruction.get();
diff --git a/source/fuzz/transformation_load.h b/source/fuzz/transformation_load.h
index 57b4a53..6622918 100644
--- a/source/fuzz/transformation_load.h
+++ b/source/fuzz/transformation_load.h
@@ -37,7 +37,7 @@
   // - |message_.is_atomic| must be true if want to work with OpAtomicLoad
   // - If |is_atomic| is true then |message_memory_scope_id| must be the id of
   //   an OpConstant 32 bit integer instruction with the value
-  //   SpvScopeInvocation.
+  //   spv::Scope::Invocation.
   // - If |is_atomic| is true then |message_.memory_semantics_id| must be the id
   //   of an OpConstant 32 bit integer instruction with the values
   //   SpvMemorySemanticsWorkgroupMemoryMask or
diff --git a/source/fuzz/transformation_make_vector_operation_dynamic.cpp b/source/fuzz/transformation_make_vector_operation_dynamic.cpp
index bd0664c..ecc8dca 100644
--- a/source/fuzz/transformation_make_vector_operation_dynamic.cpp
+++ b/source/fuzz/transformation_make_vector_operation_dynamic.cpp
@@ -62,19 +62,19 @@
   // The OpVectorInsertDynamic instruction has the vector and component operands
   // in reverse order in relation to the OpCompositeInsert corresponding
   // operands.
-  if (instruction->opcode() == SpvOpCompositeInsert) {
+  if (instruction->opcode() == spv::Op::OpCompositeInsert) {
     std::swap(instruction->GetInOperand(0), instruction->GetInOperand(1));
   }
 
   // Sets the literal operand to the equivalent constant.
   instruction->SetInOperand(
-      instruction->opcode() == SpvOpCompositeExtract ? 1 : 2,
+      instruction->opcode() == spv::Op::OpCompositeExtract ? 1 : 2,
       {message_.constant_index_id()});
 
   // Sets the |instruction| opcode to the corresponding vector dynamic opcode.
-  instruction->SetOpcode(instruction->opcode() == SpvOpCompositeExtract
-                             ? SpvOpVectorExtractDynamic
-                             : SpvOpVectorInsertDynamic);
+  instruction->SetOpcode(instruction->opcode() == spv::Op::OpCompositeExtract
+                             ? spv::Op::OpVectorExtractDynamic
+                             : spv::Op::OpVectorInsertDynamic);
 }
 
 protobufs::Transformation TransformationMakeVectorOperationDynamic::ToMessage()
@@ -88,15 +88,15 @@
     opt::IRContext* ir_context, opt::Instruction* instruction) {
   // |instruction| must be defined and must be an OpCompositeExtract/Insert
   // instruction.
-  if (!instruction || (instruction->opcode() != SpvOpCompositeExtract &&
-                       instruction->opcode() != SpvOpCompositeInsert)) {
+  if (!instruction || (instruction->opcode() != spv::Op::OpCompositeExtract &&
+                       instruction->opcode() != spv::Op::OpCompositeInsert)) {
     return false;
   }
 
   // The composite must be a vector.
   auto composite_instruction =
       ir_context->get_def_use_mgr()->GetDef(instruction->GetSingleWordInOperand(
-          instruction->opcode() == SpvOpCompositeExtract ? 0 : 1));
+          instruction->opcode() == spv::Op::OpCompositeExtract ? 0 : 1));
   if (!ir_context->get_type_mgr()
            ->GetType(composite_instruction->type_id())
            ->AsVector()) {
diff --git a/source/fuzz/transformation_merge_function_returns.cpp b/source/fuzz/transformation_merge_function_returns.cpp
index 022e1b6..b35e358 100644
--- a/source/fuzz/transformation_merge_function_returns.cpp
+++ b/source/fuzz/transformation_merge_function_returns.cpp
@@ -50,7 +50,7 @@
   }
 
   // The entry block must end in an unconditional branch.
-  if (function->entry()->terminator()->opcode() != SpvOpBranch) {
+  if (function->entry()->terminator()->opcode() != spv::Op::OpBranch) {
     return false;
   }
 
@@ -134,9 +134,9 @@
     bool all_instructions_allowed =
         ir_context->get_instr_block(merge_block)
             ->WhileEachInst([](opt::Instruction* inst) {
-              return inst->opcode() == SpvOpLabel ||
-                     inst->opcode() == SpvOpPhi ||
-                     inst->opcode() == SpvOpBranch;
+              return inst->opcode() == spv::Op::OpLabel ||
+                     inst->opcode() == spv::Op::OpPhi ||
+                     inst->opcode() == spv::Op::OpBranch;
             });
     if (!all_instructions_allowed) {
       return false;
@@ -286,7 +286,7 @@
     }
 
     // Replace the return instruction with an unconditional branch.
-    ret_block->terminator()->SetOpcode(SpvOpBranch);
+    ret_block->terminator()->SetOpcode(spv::Op::OpBranch);
     ret_block->terminator()->SetInOperands(
         {{SPV_OPERAND_TYPE_ID, {merge_block_id}}});
   }
@@ -410,7 +410,7 @@
 
       // Insert the instruction.
       merge_block->begin()->InsertBefore(MakeUnique<opt::Instruction>(
-          ir_context, SpvOpPhi, function->type_id(), maybe_return_val_id,
+          ir_context, spv::Op::OpPhi, function->type_id(), maybe_return_val_id,
           std::move(operand_list)));
 
       fuzzerutil::UpdateModuleIdBound(ir_context, maybe_return_val_id);
@@ -448,14 +448,14 @@
 
       // Insert the instruction.
       merge_block->begin()->InsertBefore(MakeUnique<opt::Instruction>(
-          ir_context, SpvOpPhi, bool_type, is_returning_id,
+          ir_context, spv::Op::OpPhi, bool_type, is_returning_id,
           std::move(operand_list)));
 
       fuzzerutil::UpdateModuleIdBound(ir_context, is_returning_id);
     }
 
     // Change the branching instruction of the block.
-    assert(merge_block->terminator()->opcode() == SpvOpBranch &&
+    assert(merge_block->terminator()->opcode() == spv::Op::OpBranch &&
            "Each block should branch unconditionally to the next.");
 
     // Add a new entry to the map corresponding to the merge block of the
@@ -483,14 +483,14 @@
 
     // The block should branch to |enclosing_merge| if |is_returning_id| is
     // true, to |original_succ| otherwise.
-    merge_block->terminator()->SetOpcode(SpvOpBranchConditional);
+    merge_block->terminator()->SetOpcode(spv::Op::OpBranchConditional);
     merge_block->terminator()->SetInOperands(
         {{SPV_OPERAND_TYPE_ID, {is_returning_id}},
          {SPV_OPERAND_TYPE_ID, {enclosing_merge}},
          {SPV_OPERAND_TYPE_ID, {original_succ}}});
   }
 
-  assert(function->entry()->terminator()->opcode() == SpvOpBranch &&
+  assert(function->entry()->terminator()->opcode() == spv::Op::OpBranch &&
          "The entry block should branch unconditionally to another block.");
   uint32_t block_after_entry =
       function->entry()->terminator()->GetSingleWordInOperand(0);
@@ -498,7 +498,7 @@
   // Create the header for the new outer loop.
   auto outer_loop_header =
       MakeUnique<opt::BasicBlock>(MakeUnique<opt::Instruction>(
-          ir_context, SpvOpLabel, 0, message_.outer_header_id(),
+          ir_context, spv::Op::OpLabel, 0, message_.outer_header_id(),
           opt::Instruction::OperandList()));
 
   fuzzerutil::UpdateModuleIdBound(ir_context, message_.outer_header_id());
@@ -506,15 +506,16 @@
   // Add the instruction:
   //   OpLoopMerge %outer_return_id %unreachable_continue_id None
   outer_loop_header->AddInstruction(MakeUnique<opt::Instruction>(
-      ir_context, SpvOpLoopMerge, 0, 0,
+      ir_context, spv::Op::OpLoopMerge, 0, 0,
       opt::Instruction::OperandList{
           {SPV_OPERAND_TYPE_ID, {message_.outer_return_id()}},
           {SPV_OPERAND_TYPE_ID, {message_.unreachable_continue_id()}},
-          {SPV_OPERAND_TYPE_LOOP_CONTROL, {SpvLoopControlMaskNone}}}));
+          {SPV_OPERAND_TYPE_LOOP_CONTROL,
+           {uint32_t(spv::LoopControlMask::MaskNone)}}}));
 
   // Add unconditional branch to %block_after_entry.
   outer_loop_header->AddInstruction(MakeUnique<opt::Instruction>(
-      ir_context, SpvOpBranch, 0, 0,
+      ir_context, spv::Op::OpBranch, 0, 0,
       opt::Instruction::OperandList{
           {SPV_OPERAND_TYPE_ID, {block_after_entry}}}));
 
@@ -531,7 +532,7 @@
   ir_context->get_def_use_mgr()->ForEachUse(
       function->entry()->id(),
       [this](opt::Instruction* use_instruction, uint32_t use_operand_index) {
-        if (use_instruction->opcode() == SpvOpPhi) {
+        if (use_instruction->opcode() == spv::Op::OpPhi) {
           use_instruction->SetOperand(use_operand_index,
                                       {message_.outer_header_id()});
         }
@@ -540,7 +541,7 @@
   // Create the merge block for the loop (and return block for the function).
   auto outer_return_block =
       MakeUnique<opt::BasicBlock>(MakeUnique<opt::Instruction>(
-          ir_context, SpvOpLabel, 0, message_.outer_return_id(),
+          ir_context, spv::Op::OpLabel, 0, message_.outer_return_id(),
           opt::Instruction::OperandList()));
 
   fuzzerutil::UpdateModuleIdBound(ir_context, message_.outer_return_id());
@@ -561,20 +562,20 @@
 
     // Insert the OpPhi instruction.
     outer_return_block->AddInstruction(MakeUnique<opt::Instruction>(
-        ir_context, SpvOpPhi, function->type_id(), message_.return_val_id(),
-        std::move(operand_list)));
+        ir_context, spv::Op::OpPhi, function->type_id(),
+        message_.return_val_id(), std::move(operand_list)));
 
     fuzzerutil::UpdateModuleIdBound(ir_context, message_.return_val_id());
 
     // Insert the OpReturnValue instruction.
     outer_return_block->AddInstruction(MakeUnique<opt::Instruction>(
-        ir_context, SpvOpReturnValue, 0, 0,
+        ir_context, spv::Op::OpReturnValue, 0, 0,
         opt::Instruction::OperandList{
             {SPV_OPERAND_TYPE_ID, {message_.return_val_id()}}}));
   } else {
     // Insert an OpReturn instruction (the function is void).
     outer_return_block->AddInstruction(MakeUnique<opt::Instruction>(
-        ir_context, SpvOpReturn, 0, 0, opt::Instruction::OperandList{}));
+        ir_context, spv::Op::OpReturn, 0, 0, opt::Instruction::OperandList{}));
   }
 
   // Insert the new return block at the end of the function.
@@ -583,7 +584,7 @@
   // Create the unreachable continue block associated with the enclosing loop.
   auto unreachable_continue_block =
       MakeUnique<opt::BasicBlock>(MakeUnique<opt::Instruction>(
-          ir_context, SpvOpLabel, 0, message_.unreachable_continue_id(),
+          ir_context, spv::Op::OpLabel, 0, message_.unreachable_continue_id(),
           opt::Instruction::OperandList()));
 
   fuzzerutil::UpdateModuleIdBound(ir_context,
@@ -591,7 +592,7 @@
 
   // Insert an branch back to the loop header, to create a back edge.
   unreachable_continue_block->AddInstruction(MakeUnique<opt::Instruction>(
-      ir_context, SpvOpBranch, 0, 0,
+      ir_context, spv::Op::OpBranch, 0, 0,
       opt::Instruction::OperandList{
           {SPV_OPERAND_TYPE_ID, {message_.outer_header_id()}}}));
 
@@ -751,7 +752,7 @@
                   // The usage is OK if it is inside an OpPhi instruction in the
                   // merge block.
                   return block_use == merge_block &&
-                         inst_use->opcode() == SpvOpPhi;
+                         inst_use->opcode() == spv::Op::OpPhi;
                 });
           });
 
diff --git a/source/fuzz/transformation_move_instruction_down.cpp b/source/fuzz/transformation_move_instruction_down.cpp
index c8139e7..4d5d9f0 100644
--- a/source/fuzz/transformation_move_instruction_down.cpp
+++ b/source/fuzz/transformation_move_instruction_down.cpp
@@ -26,7 +26,7 @@
 
 std::string GetExtensionSet(opt::IRContext* ir_context,
                             const opt::Instruction& op_ext_inst) {
-  assert(op_ext_inst.opcode() == SpvOpExtInst && "Wrong opcode");
+  assert(op_ext_inst.opcode() == spv::Op::OpExtInst && "Wrong opcode");
 
   const auto* ext_inst_import = ir_context->get_def_use_mgr()->GetDef(
       op_ext_inst.GetSingleWordInOperand(0));
@@ -142,112 +142,112 @@
 bool TransformationMoveInstructionDown::IsSimpleInstruction(
     opt::IRContext* ir_context, const opt::Instruction& inst) {
   switch (inst.opcode()) {
-    case SpvOpNop:
-    case SpvOpUndef:
-    case SpvOpAccessChain:
-    case SpvOpInBoundsAccessChain:
+    case spv::Op::OpNop:
+    case spv::Op::OpUndef:
+    case spv::Op::OpAccessChain:
+    case spv::Op::OpInBoundsAccessChain:
       // OpAccessChain and OpInBoundsAccessChain are considered simple
       // instructions since they result in a pointer to the object in memory,
       // not the object itself.
-    case SpvOpVectorExtractDynamic:
-    case SpvOpVectorInsertDynamic:
-    case SpvOpVectorShuffle:
-    case SpvOpCompositeConstruct:
-    case SpvOpCompositeExtract:
-    case SpvOpCompositeInsert:
-    case SpvOpCopyObject:
-    case SpvOpTranspose:
-    case SpvOpConvertFToU:
-    case SpvOpConvertFToS:
-    case SpvOpConvertSToF:
-    case SpvOpConvertUToF:
-    case SpvOpUConvert:
-    case SpvOpSConvert:
-    case SpvOpFConvert:
-    case SpvOpQuantizeToF16:
-    case SpvOpSatConvertSToU:
-    case SpvOpSatConvertUToS:
-    case SpvOpBitcast:
-    case SpvOpSNegate:
-    case SpvOpFNegate:
-    case SpvOpIAdd:
-    case SpvOpFAdd:
-    case SpvOpISub:
-    case SpvOpFSub:
-    case SpvOpIMul:
-    case SpvOpFMul:
-    case SpvOpUDiv:
-    case SpvOpSDiv:
-    case SpvOpFDiv:
-    case SpvOpUMod:
-    case SpvOpSRem:
-    case SpvOpSMod:
-    case SpvOpFRem:
-    case SpvOpFMod:
-    case SpvOpVectorTimesScalar:
-    case SpvOpMatrixTimesScalar:
-    case SpvOpVectorTimesMatrix:
-    case SpvOpMatrixTimesVector:
-    case SpvOpMatrixTimesMatrix:
-    case SpvOpOuterProduct:
-    case SpvOpDot:
-    case SpvOpIAddCarry:
-    case SpvOpISubBorrow:
-    case SpvOpUMulExtended:
-    case SpvOpSMulExtended:
-    case SpvOpAny:
-    case SpvOpAll:
-    case SpvOpIsNan:
-    case SpvOpIsInf:
-    case SpvOpIsFinite:
-    case SpvOpIsNormal:
-    case SpvOpSignBitSet:
-    case SpvOpLessOrGreater:
-    case SpvOpOrdered:
-    case SpvOpUnordered:
-    case SpvOpLogicalEqual:
-    case SpvOpLogicalNotEqual:
-    case SpvOpLogicalOr:
-    case SpvOpLogicalAnd:
-    case SpvOpLogicalNot:
-    case SpvOpSelect:
-    case SpvOpIEqual:
-    case SpvOpINotEqual:
-    case SpvOpUGreaterThan:
-    case SpvOpSGreaterThan:
-    case SpvOpUGreaterThanEqual:
-    case SpvOpSGreaterThanEqual:
-    case SpvOpULessThan:
-    case SpvOpSLessThan:
-    case SpvOpULessThanEqual:
-    case SpvOpSLessThanEqual:
-    case SpvOpFOrdEqual:
-    case SpvOpFUnordEqual:
-    case SpvOpFOrdNotEqual:
-    case SpvOpFUnordNotEqual:
-    case SpvOpFOrdLessThan:
-    case SpvOpFUnordLessThan:
-    case SpvOpFOrdGreaterThan:
-    case SpvOpFUnordGreaterThan:
-    case SpvOpFOrdLessThanEqual:
-    case SpvOpFUnordLessThanEqual:
-    case SpvOpFOrdGreaterThanEqual:
-    case SpvOpFUnordGreaterThanEqual:
-    case SpvOpShiftRightLogical:
-    case SpvOpShiftRightArithmetic:
-    case SpvOpShiftLeftLogical:
-    case SpvOpBitwiseOr:
-    case SpvOpBitwiseXor:
-    case SpvOpBitwiseAnd:
-    case SpvOpNot:
-    case SpvOpBitFieldInsert:
-    case SpvOpBitFieldSExtract:
-    case SpvOpBitFieldUExtract:
-    case SpvOpBitReverse:
-    case SpvOpBitCount:
-    case SpvOpCopyLogical:
+    case spv::Op::OpVectorExtractDynamic:
+    case spv::Op::OpVectorInsertDynamic:
+    case spv::Op::OpVectorShuffle:
+    case spv::Op::OpCompositeConstruct:
+    case spv::Op::OpCompositeExtract:
+    case spv::Op::OpCompositeInsert:
+    case spv::Op::OpCopyObject:
+    case spv::Op::OpTranspose:
+    case spv::Op::OpConvertFToU:
+    case spv::Op::OpConvertFToS:
+    case spv::Op::OpConvertSToF:
+    case spv::Op::OpConvertUToF:
+    case spv::Op::OpUConvert:
+    case spv::Op::OpSConvert:
+    case spv::Op::OpFConvert:
+    case spv::Op::OpQuantizeToF16:
+    case spv::Op::OpSatConvertSToU:
+    case spv::Op::OpSatConvertUToS:
+    case spv::Op::OpBitcast:
+    case spv::Op::OpSNegate:
+    case spv::Op::OpFNegate:
+    case spv::Op::OpIAdd:
+    case spv::Op::OpFAdd:
+    case spv::Op::OpISub:
+    case spv::Op::OpFSub:
+    case spv::Op::OpIMul:
+    case spv::Op::OpFMul:
+    case spv::Op::OpUDiv:
+    case spv::Op::OpSDiv:
+    case spv::Op::OpFDiv:
+    case spv::Op::OpUMod:
+    case spv::Op::OpSRem:
+    case spv::Op::OpSMod:
+    case spv::Op::OpFRem:
+    case spv::Op::OpFMod:
+    case spv::Op::OpVectorTimesScalar:
+    case spv::Op::OpMatrixTimesScalar:
+    case spv::Op::OpVectorTimesMatrix:
+    case spv::Op::OpMatrixTimesVector:
+    case spv::Op::OpMatrixTimesMatrix:
+    case spv::Op::OpOuterProduct:
+    case spv::Op::OpDot:
+    case spv::Op::OpIAddCarry:
+    case spv::Op::OpISubBorrow:
+    case spv::Op::OpUMulExtended:
+    case spv::Op::OpSMulExtended:
+    case spv::Op::OpAny:
+    case spv::Op::OpAll:
+    case spv::Op::OpIsNan:
+    case spv::Op::OpIsInf:
+    case spv::Op::OpIsFinite:
+    case spv::Op::OpIsNormal:
+    case spv::Op::OpSignBitSet:
+    case spv::Op::OpLessOrGreater:
+    case spv::Op::OpOrdered:
+    case spv::Op::OpUnordered:
+    case spv::Op::OpLogicalEqual:
+    case spv::Op::OpLogicalNotEqual:
+    case spv::Op::OpLogicalOr:
+    case spv::Op::OpLogicalAnd:
+    case spv::Op::OpLogicalNot:
+    case spv::Op::OpSelect:
+    case spv::Op::OpIEqual:
+    case spv::Op::OpINotEqual:
+    case spv::Op::OpUGreaterThan:
+    case spv::Op::OpSGreaterThan:
+    case spv::Op::OpUGreaterThanEqual:
+    case spv::Op::OpSGreaterThanEqual:
+    case spv::Op::OpULessThan:
+    case spv::Op::OpSLessThan:
+    case spv::Op::OpULessThanEqual:
+    case spv::Op::OpSLessThanEqual:
+    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::OpShiftRightLogical:
+    case spv::Op::OpShiftRightArithmetic:
+    case spv::Op::OpShiftLeftLogical:
+    case spv::Op::OpBitwiseOr:
+    case spv::Op::OpBitwiseXor:
+    case spv::Op::OpBitwiseAnd:
+    case spv::Op::OpNot:
+    case spv::Op::OpBitFieldInsert:
+    case spv::Op::OpBitFieldSExtract:
+    case spv::Op::OpBitFieldUExtract:
+    case spv::Op::OpBitReverse:
+    case spv::Op::OpBitCount:
+    case spv::Op::OpCopyLogical:
       return true;
-    case SpvOpExtInst: {
+    case spv::Op::OpExtInst: {
       const auto* ext_inst_import =
           ir_context->get_def_use_mgr()->GetDef(inst.GetSingleWordInOperand(0));
 
@@ -346,53 +346,53 @@
     opt::IRContext* ir_context, const opt::Instruction& inst) {
   switch (inst.opcode()) {
       // Some simple instructions.
-    case SpvOpLoad:
-    case SpvOpCopyMemory:
+    case spv::Op::OpLoad:
+    case spv::Op::OpCopyMemory:
       // Image instructions.
-    case SpvOpImageSampleImplicitLod:
-    case SpvOpImageSampleExplicitLod:
-    case SpvOpImageSampleDrefImplicitLod:
-    case SpvOpImageSampleDrefExplicitLod:
-    case SpvOpImageSampleProjImplicitLod:
-    case SpvOpImageSampleProjExplicitLod:
-    case SpvOpImageSampleProjDrefImplicitLod:
-    case SpvOpImageSampleProjDrefExplicitLod:
-    case SpvOpImageFetch:
-    case SpvOpImageGather:
-    case SpvOpImageDrefGather:
-    case SpvOpImageRead:
-    case SpvOpImageSparseSampleImplicitLod:
-    case SpvOpImageSparseSampleExplicitLod:
-    case SpvOpImageSparseSampleDrefImplicitLod:
-    case SpvOpImageSparseSampleDrefExplicitLod:
-    case SpvOpImageSparseSampleProjImplicitLod:
-    case SpvOpImageSparseSampleProjExplicitLod:
-    case SpvOpImageSparseSampleProjDrefImplicitLod:
-    case SpvOpImageSparseSampleProjDrefExplicitLod:
-    case SpvOpImageSparseFetch:
-    case SpvOpImageSparseGather:
-    case SpvOpImageSparseDrefGather:
-    case SpvOpImageSparseRead:
+    case spv::Op::OpImageSampleImplicitLod:
+    case spv::Op::OpImageSampleExplicitLod:
+    case spv::Op::OpImageSampleDrefImplicitLod:
+    case spv::Op::OpImageSampleDrefExplicitLod:
+    case spv::Op::OpImageSampleProjImplicitLod:
+    case spv::Op::OpImageSampleProjExplicitLod:
+    case spv::Op::OpImageSampleProjDrefImplicitLod:
+    case spv::Op::OpImageSampleProjDrefExplicitLod:
+    case spv::Op::OpImageFetch:
+    case spv::Op::OpImageGather:
+    case spv::Op::OpImageDrefGather:
+    case spv::Op::OpImageRead:
+    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::OpImageSparseRead:
       // Atomic instructions.
-    case SpvOpAtomicLoad:
-    case SpvOpAtomicExchange:
-    case SpvOpAtomicCompareExchange:
-    case SpvOpAtomicCompareExchangeWeak:
-    case SpvOpAtomicIIncrement:
-    case SpvOpAtomicIDecrement:
-    case SpvOpAtomicIAdd:
-    case SpvOpAtomicISub:
-    case SpvOpAtomicSMin:
-    case SpvOpAtomicUMin:
-    case SpvOpAtomicSMax:
-    case SpvOpAtomicUMax:
-    case SpvOpAtomicAnd:
-    case SpvOpAtomicOr:
-    case SpvOpAtomicXor:
-    case SpvOpAtomicFlagTestAndSet:
+    case spv::Op::OpAtomicLoad:
+    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:
+    case spv::Op::OpAtomicFlagTestAndSet:
       return true;
       // Extensions.
-    case SpvOpExtInst: {
+    case spv::Op::OpExtInst: {
       if (GetExtensionSet(ir_context, inst) != kExtensionSetName) {
         return false;
       }
@@ -419,53 +419,53 @@
 
   switch (inst.opcode()) {
       // Simple instructions.
-    case SpvOpLoad:
+    case spv::Op::OpLoad:
       // Image instructions.
-    case SpvOpImageSampleImplicitLod:
-    case SpvOpImageSampleExplicitLod:
-    case SpvOpImageSampleDrefImplicitLod:
-    case SpvOpImageSampleDrefExplicitLod:
-    case SpvOpImageSampleProjImplicitLod:
-    case SpvOpImageSampleProjExplicitLod:
-    case SpvOpImageSampleProjDrefImplicitLod:
-    case SpvOpImageSampleProjDrefExplicitLod:
-    case SpvOpImageFetch:
-    case SpvOpImageGather:
-    case SpvOpImageDrefGather:
-    case SpvOpImageRead:
-    case SpvOpImageSparseSampleImplicitLod:
-    case SpvOpImageSparseSampleExplicitLod:
-    case SpvOpImageSparseSampleDrefImplicitLod:
-    case SpvOpImageSparseSampleDrefExplicitLod:
-    case SpvOpImageSparseSampleProjImplicitLod:
-    case SpvOpImageSparseSampleProjExplicitLod:
-    case SpvOpImageSparseSampleProjDrefImplicitLod:
-    case SpvOpImageSparseSampleProjDrefExplicitLod:
-    case SpvOpImageSparseFetch:
-    case SpvOpImageSparseGather:
-    case SpvOpImageSparseDrefGather:
-    case SpvOpImageSparseRead:
+    case spv::Op::OpImageSampleImplicitLod:
+    case spv::Op::OpImageSampleExplicitLod:
+    case spv::Op::OpImageSampleDrefImplicitLod:
+    case spv::Op::OpImageSampleDrefExplicitLod:
+    case spv::Op::OpImageSampleProjImplicitLod:
+    case spv::Op::OpImageSampleProjExplicitLod:
+    case spv::Op::OpImageSampleProjDrefImplicitLod:
+    case spv::Op::OpImageSampleProjDrefExplicitLod:
+    case spv::Op::OpImageFetch:
+    case spv::Op::OpImageGather:
+    case spv::Op::OpImageDrefGather:
+    case spv::Op::OpImageRead:
+    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::OpImageSparseRead:
       // Atomic instructions.
-    case SpvOpAtomicLoad:
-    case SpvOpAtomicExchange:
-    case SpvOpAtomicCompareExchange:
-    case SpvOpAtomicCompareExchangeWeak:
-    case SpvOpAtomicIIncrement:
-    case SpvOpAtomicIDecrement:
-    case SpvOpAtomicIAdd:
-    case SpvOpAtomicISub:
-    case SpvOpAtomicSMin:
-    case SpvOpAtomicUMin:
-    case SpvOpAtomicSMax:
-    case SpvOpAtomicUMax:
-    case SpvOpAtomicAnd:
-    case SpvOpAtomicOr:
-    case SpvOpAtomicXor:
-    case SpvOpAtomicFlagTestAndSet:
+    case spv::Op::OpAtomicLoad:
+    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:
+    case spv::Op::OpAtomicFlagTestAndSet:
       return inst.GetSingleWordInOperand(0);
-    case SpvOpCopyMemory:
+    case spv::Op::OpCopyMemory:
       return inst.GetSingleWordInOperand(1);
-    case SpvOpExtInst: {
+    case spv::Op::OpExtInst: {
       assert(GetExtensionSet(ir_context, inst) == kExtensionSetName &&
              "Extension set is not supported");
 
@@ -493,31 +493,31 @@
     opt::IRContext* ir_context, const opt::Instruction& inst) {
   switch (inst.opcode()) {
       // Simple Instructions.
-    case SpvOpStore:
-    case SpvOpCopyMemory:
+    case spv::Op::OpStore:
+    case spv::Op::OpCopyMemory:
       // Image instructions.
-    case SpvOpImageWrite:
+    case spv::Op::OpImageWrite:
       // Atomic instructions.
-    case SpvOpAtomicStore:
-    case SpvOpAtomicExchange:
-    case SpvOpAtomicCompareExchange:
-    case SpvOpAtomicCompareExchangeWeak:
-    case SpvOpAtomicIIncrement:
-    case SpvOpAtomicIDecrement:
-    case SpvOpAtomicIAdd:
-    case SpvOpAtomicISub:
-    case SpvOpAtomicSMin:
-    case SpvOpAtomicUMin:
-    case SpvOpAtomicSMax:
-    case SpvOpAtomicUMax:
-    case SpvOpAtomicAnd:
-    case SpvOpAtomicOr:
-    case SpvOpAtomicXor:
-    case SpvOpAtomicFlagTestAndSet:
-    case SpvOpAtomicFlagClear:
+    case spv::Op::OpAtomicStore:
+    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:
+    case spv::Op::OpAtomicFlagTestAndSet:
+    case spv::Op::OpAtomicFlagClear:
       return true;
       // Extensions.
-    case SpvOpExtInst: {
+    case spv::Op::OpExtInst: {
       if (GetExtensionSet(ir_context, inst) != kExtensionSetName) {
         return false;
       }
@@ -537,28 +537,28 @@
          "|inst| is not a memory write instruction");
 
   switch (inst.opcode()) {
-    case SpvOpStore:
-    case SpvOpCopyMemory:
-    case SpvOpImageWrite:
-    case SpvOpAtomicStore:
-    case SpvOpAtomicExchange:
-    case SpvOpAtomicCompareExchange:
-    case SpvOpAtomicCompareExchangeWeak:
-    case SpvOpAtomicIIncrement:
-    case SpvOpAtomicIDecrement:
-    case SpvOpAtomicIAdd:
-    case SpvOpAtomicISub:
-    case SpvOpAtomicSMin:
-    case SpvOpAtomicUMin:
-    case SpvOpAtomicSMax:
-    case SpvOpAtomicUMax:
-    case SpvOpAtomicAnd:
-    case SpvOpAtomicOr:
-    case SpvOpAtomicXor:
-    case SpvOpAtomicFlagTestAndSet:
-    case SpvOpAtomicFlagClear:
+    case spv::Op::OpStore:
+    case spv::Op::OpCopyMemory:
+    case spv::Op::OpImageWrite:
+    case spv::Op::OpAtomicStore:
+    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:
+    case spv::Op::OpAtomicFlagTestAndSet:
+    case spv::Op::OpAtomicFlagClear:
       return inst.GetSingleWordInOperand(0);
-    case SpvOpExtInst: {
+    case spv::Op::OpExtInst: {
       assert(GetExtensionSet(ir_context, inst) == kExtensionSetName &&
              "Extension set is not supported");
 
@@ -590,9 +590,9 @@
 bool TransformationMoveInstructionDown::IsBarrierInstruction(
     const opt::Instruction& inst) {
   switch (inst.opcode()) {
-    case SpvOpMemoryBarrier:
-    case SpvOpControlBarrier:
-    case SpvOpMemoryNamedBarrier:
+    case spv::Op::OpMemoryBarrier:
+    case spv::Op::OpControlBarrier:
+    case spv::Op::OpMemoryNamedBarrier:
       return true;
     default:
       return false;
diff --git a/source/fuzz/transformation_mutate_pointer.cpp b/source/fuzz/transformation_mutate_pointer.cpp
index 516a0d6..a1620cc 100644
--- a/source/fuzz/transformation_mutate_pointer.cpp
+++ b/source/fuzz/transformation_mutate_pointer.cpp
@@ -51,7 +51,7 @@
   // Check that it is possible to insert OpLoad and OpStore before
   // |insert_before_inst|. We are only using OpLoad here since the result does
   // not depend on the opcode.
-  if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad,
+  if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(spv::Op::OpLoad,
                                                     insert_before_inst)) {
     return false;
   }
@@ -100,7 +100,7 @@
 
   // Back up the original value.
   auto backup_instruction = MakeUnique<opt::Instruction>(
-      ir_context, SpvOpLoad, pointee_type_id, message_.fresh_id(),
+      ir_context, spv::Op::OpLoad, pointee_type_id, message_.fresh_id(),
       opt::Instruction::OperandList{
           {SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}});
   auto backup_instruction_ptr = backup_instruction.get();
@@ -110,7 +110,7 @@
 
   // Insert a new value.
   auto new_value_instruction = MakeUnique<opt::Instruction>(
-      ir_context, SpvOpStore, 0, 0,
+      ir_context, spv::Op::OpStore, 0, 0,
       opt::Instruction::OperandList{
           {SPV_OPERAND_TYPE_ID, {message_.pointer_id()}},
           {SPV_OPERAND_TYPE_ID,
@@ -123,7 +123,7 @@
 
   // Restore the original value.
   auto restore_instruction = MakeUnique<opt::Instruction>(
-      ir_context, SpvOpStore, 0, 0,
+      ir_context, spv::Op::OpStore, 0, 0,
       opt::Instruction::OperandList{
           {SPV_OPERAND_TYPE_ID, {message_.pointer_id()}},
           {SPV_OPERAND_TYPE_ID, {message_.fresh_id()}}});
@@ -145,8 +145,9 @@
     opt::IRContext* ir_context, const opt::Instruction& inst) {
   // |inst| must have both result id and type id and it may not cause undefined
   // behaviour.
-  if (!inst.result_id() || !inst.type_id() || inst.opcode() == SpvOpUndef ||
-      inst.opcode() == SpvOpConstantNull) {
+  if (!inst.result_id() || !inst.type_id() ||
+      inst.opcode() == spv::Op::OpUndef ||
+      inst.opcode() == spv::Op::OpConstantNull) {
     return false;
   }
 
@@ -155,15 +156,16 @@
   assert(type_inst != nullptr && "|inst| has invalid type id");
 
   // |inst| must be a pointer.
-  if (type_inst->opcode() != SpvOpTypePointer) {
+  if (type_inst->opcode() != spv::Op::OpTypePointer) {
     return false;
   }
 
   // |inst| must have a supported storage class.
-  switch (static_cast<SpvStorageClass>(type_inst->GetSingleWordInOperand(0))) {
-    case SpvStorageClassFunction:
-    case SpvStorageClassPrivate:
-    case SpvStorageClassWorkgroup:
+  switch (
+      static_cast<spv::StorageClass>(type_inst->GetSingleWordInOperand(0))) {
+    case spv::StorageClass::Function:
+    case spv::StorageClass::Private:
+    case spv::StorageClass::Workgroup:
       break;
     default:
       return false;
diff --git a/source/fuzz/transformation_outline_function.cpp b/source/fuzz/transformation_outline_function.cpp
index 3140fa6..4ab68d0 100644
--- a/source/fuzz/transformation_outline_function.cpp
+++ b/source/fuzz/transformation_outline_function.cpp
@@ -107,7 +107,7 @@
   // The entry and exit block ids must indeed refer to blocks.
   for (auto block_id : {message_.entry_block(), message_.exit_block()}) {
     auto block_label = ir_context->get_def_use_mgr()->GetDef(block_id);
-    if (!block_label || block_label->opcode() != SpvOpLabel) {
+    if (!block_label || block_label->opcode() != spv::Op::OpLabel) {
       return false;
     }
   }
@@ -118,7 +118,7 @@
   // The entry block cannot start with OpVariable - this would mean that
   // outlining would remove a variable from the function containing the region
   // being outlined.
-  if (entry_block->begin()->opcode() == SpvOpVariable) {
+  if (entry_block->begin()->opcode() == spv::Op::OpVariable) {
     return false;
   }
 
@@ -136,7 +136,7 @@
   // The entry block cannot start with OpPhi.  This is to keep the
   // transformation logic simple.  (Another transformation to split the OpPhis
   // from a block could be applied to avoid this scenario.)
-  if (entry_block->begin()->opcode() == SpvOpPhi) {
+  if (entry_block->begin()->opcode() == spv::Op::OpPhi) {
     return false;
   }
 
@@ -257,10 +257,10 @@
     auto input_id_inst = ir_context->get_def_use_mgr()->GetDef(id);
     if (ir_context->get_def_use_mgr()
             ->GetDef(input_id_inst->type_id())
-            ->opcode() == SpvOpTypePointer) {
+            ->opcode() == spv::Op::OpTypePointer) {
       switch (input_id_inst->opcode()) {
-        case SpvOpFunctionParameter:
-        case SpvOpVariable:
+        case spv::Op::OpFunctionParameter:
+        case spv::Op::OpVariable:
           // These are OK.
           break;
         default:
@@ -286,7 +286,7 @@
         // function)
         || ir_context->get_def_use_mgr()
                    ->GetDef(fuzzerutil::GetTypeId(ir_context, id))
-                   ->opcode() == SpvOpTypePointer) {
+                   ->opcode() == spv::Op::OpTypePointer) {
       return false;
     }
   }
@@ -608,7 +608,7 @@
         auto output_id_type =
             ir_context->get_def_use_mgr()->GetDef(output_id)->type_id();
         if (ir_context->get_def_use_mgr()->GetDef(output_id_type)->opcode() ==
-            SpvOpTypeVoid) {
+            spv::Op::OpTypeVoid) {
           // We cannot add a void field to a struct.  We instead use OpUndef to
           // handle void output ids.
           continue;
@@ -617,7 +617,7 @@
       }
       // Add a new struct type to the module.
       ir_context->module()->AddType(MakeUnique<opt::Instruction>(
-          ir_context, SpvOpTypeStruct, 0,
+          ir_context, spv::Op::OpTypeStruct, 0,
           message_.new_function_struct_return_type_id(),
           std::move(struct_member_types)));
       // The return type for the function is the newly-created struct.
@@ -638,7 +638,7 @@
     // Add a new function type to the module, and record that this is the type
     // id for the new function.
     ir_context->module()->AddType(MakeUnique<opt::Instruction>(
-        ir_context, SpvOpTypeFunction, 0, message_.new_function_type_id(),
+        ir_context, spv::Op::OpTypeFunction, 0, message_.new_function_type_id(),
         function_type_operands));
     function_type_id = message_.new_function_type_id();
   }
@@ -647,10 +647,11 @@
   // and the return type and function type prepared above.
   std::unique_ptr<opt::Function> outlined_function =
       MakeUnique<opt::Function>(MakeUnique<opt::Instruction>(
-          ir_context, SpvOpFunction, return_type_id, message_.new_function_id(),
+          ir_context, spv::Op::OpFunction, return_type_id,
+          message_.new_function_id(),
           opt::Instruction::OperandList(
               {{spv_operand_type_t ::SPV_OPERAND_TYPE_LITERAL_INTEGER,
-                {SpvFunctionControlMaskNone}},
+                {uint32_t(spv::FunctionControlMask::MaskNone)}},
                {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
                 {function_type_id}}})));
 
@@ -659,7 +660,7 @@
   for (auto id : region_input_ids) {
     uint32_t fresh_id = input_id_to_fresh_id_map.at(id);
     outlined_function->AddParameter(MakeUnique<opt::Instruction>(
-        ir_context, SpvOpFunctionParameter,
+        ir_context, spv::Op::OpFunctionParameter,
         ir_context->get_def_use_mgr()->GetDef(id)->type_id(), fresh_id,
         opt::Instruction::OperandList()));
 
@@ -788,7 +789,8 @@
   // |message_.new_function_region_entry_block| as its id.
   std::unique_ptr<opt::BasicBlock> outlined_region_entry_block =
       MakeUnique<opt::BasicBlock>(MakeUnique<opt::Instruction>(
-          ir_context, SpvOpLabel, 0, message_.new_function_region_entry_block(),
+          ir_context, spv::Op::OpLabel, 0,
+          message_.new_function_region_entry_block(),
           opt::Instruction::OperandList()));
 
   if (&original_region_entry_block == &original_region_exit_block) {
@@ -854,8 +856,8 @@
   // the cloned exit block.
   for (auto inst_it = outlined_region_exit_block->begin();
        inst_it != outlined_region_exit_block->end();) {
-    if (inst_it->opcode() == SpvOpLoopMerge ||
-        inst_it->opcode() == SpvOpSelectionMerge) {
+    if (inst_it->opcode() == spv::Op::OpLoopMerge ||
+        inst_it->opcode() == spv::Op::OpSelectionMerge) {
       inst_it = inst_it.Erase();
     } else if (inst_it->IsBlockTerminator()) {
       inst_it = inst_it.Erase();
@@ -870,7 +872,7 @@
     // The case where there are no region output ids is simple: we just add
     // OpReturn.
     outlined_region_exit_block->AddInstruction(MakeUnique<opt::Instruction>(
-        ir_context, SpvOpReturn, 0, 0, opt::Instruction::OperandList()));
+        ir_context, spv::Op::OpReturn, 0, 0, opt::Instruction::OperandList()));
   } else {
     // In the case where there are output ids, we add an OpCompositeConstruct
     // instruction to pack all the non-void output values into a struct, and
@@ -879,23 +881,24 @@
     for (uint32_t id : region_output_ids) {
       if (ir_context->get_def_use_mgr()
               ->GetDef(output_id_to_type_id.at(id))
-              ->opcode() != SpvOpTypeVoid) {
+              ->opcode() != spv::Op::OpTypeVoid) {
         struct_member_operands.push_back(
             {SPV_OPERAND_TYPE_ID, {output_id_to_fresh_id_map.at(id)}});
       }
     }
     outlined_region_exit_block->AddInstruction(MakeUnique<opt::Instruction>(
-        ir_context, SpvOpCompositeConstruct,
+        ir_context, spv::Op::OpCompositeConstruct,
         message_.new_function_struct_return_type_id(),
         message_.new_callee_result_id(), struct_member_operands));
     outlined_region_exit_block->AddInstruction(MakeUnique<opt::Instruction>(
-        ir_context, SpvOpReturnValue, 0, 0,
+        ir_context, spv::Op::OpReturnValue, 0, 0,
         opt::Instruction::OperandList(
             {{SPV_OPERAND_TYPE_ID, {message_.new_callee_result_id()}}})));
   }
 
-  outlined_function->SetFunctionEnd(MakeUnique<opt::Instruction>(
-      ir_context, SpvOpFunctionEnd, 0, 0, opt::Instruction::OperandList()));
+  outlined_function->SetFunctionEnd(
+      MakeUnique<opt::Instruction>(ir_context, spv::Op::OpFunctionEnd, 0, 0,
+                                   opt::Instruction::OperandList()));
 }
 
 void TransformationOutlineFunction::ShrinkOriginalRegion(
@@ -963,7 +966,7 @@
   }
 
   original_region_entry_block->AddInstruction(MakeUnique<opt::Instruction>(
-      ir_context, SpvOpFunctionCall, return_type_id,
+      ir_context, spv::Op::OpFunctionCall, return_type_id,
       message_.new_caller_result_id(), function_call_operands));
 
   // If there are output ids, the function call will return a struct.  For each
@@ -975,15 +978,15 @@
   for (uint32_t output_id : region_output_ids) {
     uint32_t output_type_id = output_id_to_type_id.at(output_id);
     if (ir_context->get_def_use_mgr()->GetDef(output_type_id)->opcode() ==
-        SpvOpTypeVoid) {
+        spv::Op::OpTypeVoid) {
       original_region_entry_block->AddInstruction(MakeUnique<opt::Instruction>(
-          ir_context, SpvOpUndef, output_type_id, output_id,
+          ir_context, spv::Op::OpUndef, output_type_id, output_id,
           opt::Instruction::OperandList()));
       // struct_member_index is not incremented since there was no struct member
       // associated with this void-typed output id.
     } else {
       original_region_entry_block->AddInstruction(MakeUnique<opt::Instruction>(
-          ir_context, SpvOpCompositeExtract, output_type_id, output_id,
+          ir_context, spv::Op::OpCompositeExtract, output_type_id, output_id,
           opt::Instruction::OperandList(
               {{SPV_OPERAND_TYPE_ID, {message_.new_caller_result_id()}},
                {SPV_OPERAND_TYPE_LITERAL_INTEGER, {struct_member_index}}})));
diff --git a/source/fuzz/transformation_permute_function_parameters.cpp b/source/fuzz/transformation_permute_function_parameters.cpp
index 5663d72..b666cdf 100644
--- a/source/fuzz/transformation_permute_function_parameters.cpp
+++ b/source/fuzz/transformation_permute_function_parameters.cpp
@@ -43,7 +43,7 @@
   // Check that function exists
   const auto* function =
       fuzzerutil::FindFunction(ir_context, message_.function_id());
-  if (!function || function->DefInst().opcode() != SpvOpFunction ||
+  if (!function || function->DefInst().opcode() != spv::Op::OpFunction ||
       fuzzerutil::FunctionIsEntryPoint(ir_context, function->result_id())) {
     return false;
   }
diff --git a/source/fuzz/transformation_permute_phi_operands.cpp b/source/fuzz/transformation_permute_phi_operands.cpp
index 7ee7a82..f2f4057 100644
--- a/source/fuzz/transformation_permute_phi_operands.cpp
+++ b/source/fuzz/transformation_permute_phi_operands.cpp
@@ -39,7 +39,7 @@
   // Check that |message_.result_id| is valid.
   const auto* inst =
       ir_context->get_def_use_mgr()->GetDef(message_.result_id());
-  if (!inst || inst->opcode() != SpvOpPhi) {
+  if (!inst || inst->opcode() != spv::Op::OpPhi) {
     return false;
   }
 
diff --git a/source/fuzz/transformation_propagate_instruction_down.cpp b/source/fuzz/transformation_propagate_instruction_down.cpp
index c3b7c4d..4b98784 100644
--- a/source/fuzz/transformation_propagate_instruction_down.cpp
+++ b/source/fuzz/transformation_propagate_instruction_down.cpp
@@ -144,7 +144,7 @@
         ->block(merge_block_id)
         ->begin()
         ->InsertBefore(MakeUnique<opt::Instruction>(
-            ir_context, SpvOpPhi, inst_to_propagate->type_id(),
+            ir_context, spv::Op::OpPhi, inst_to_propagate->type_id(),
             message_.phi_fresh_id(), std::move(in_operands)));
 
     fuzzerutil::UpdateModuleIdBound(ir_context, message_.phi_fresh_id());
@@ -234,115 +234,115 @@
   return result;
 }
 
-bool TransformationPropagateInstructionDown::IsOpcodeSupported(SpvOp opcode) {
+bool TransformationPropagateInstructionDown::IsOpcodeSupported(spv::Op opcode) {
   // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3605):
   //  We only support "simple" instructions that don't work with memory.
   //  We should extend this so that we support the ones that modify the memory
   //  too.
   switch (opcode) {
-    case SpvOpUndef:
-    case SpvOpAccessChain:
-    case SpvOpInBoundsAccessChain:
-    case SpvOpArrayLength:
-    case SpvOpVectorExtractDynamic:
-    case SpvOpVectorInsertDynamic:
-    case SpvOpVectorShuffle:
-    case SpvOpCompositeConstruct:
-    case SpvOpCompositeExtract:
-    case SpvOpCompositeInsert:
-    case SpvOpCopyObject:
-    case SpvOpTranspose:
-    case SpvOpConvertFToU:
-    case SpvOpConvertFToS:
-    case SpvOpConvertSToF:
-    case SpvOpConvertUToF:
-    case SpvOpUConvert:
-    case SpvOpSConvert:
-    case SpvOpFConvert:
-    case SpvOpQuantizeToF16:
-    case SpvOpSatConvertSToU:
-    case SpvOpSatConvertUToS:
-    case SpvOpBitcast:
-    case SpvOpSNegate:
-    case SpvOpFNegate:
-    case SpvOpIAdd:
-    case SpvOpFAdd:
-    case SpvOpISub:
-    case SpvOpFSub:
-    case SpvOpIMul:
-    case SpvOpFMul:
-    case SpvOpUDiv:
-    case SpvOpSDiv:
-    case SpvOpFDiv:
-    case SpvOpUMod:
-    case SpvOpSRem:
-    case SpvOpSMod:
-    case SpvOpFRem:
-    case SpvOpFMod:
-    case SpvOpVectorTimesScalar:
-    case SpvOpMatrixTimesScalar:
-    case SpvOpVectorTimesMatrix:
-    case SpvOpMatrixTimesVector:
-    case SpvOpMatrixTimesMatrix:
-    case SpvOpOuterProduct:
-    case SpvOpDot:
-    case SpvOpIAddCarry:
-    case SpvOpISubBorrow:
-    case SpvOpUMulExtended:
-    case SpvOpSMulExtended:
-    case SpvOpAny:
-    case SpvOpAll:
-    case SpvOpIsNan:
-    case SpvOpIsInf:
-    case SpvOpIsFinite:
-    case SpvOpIsNormal:
-    case SpvOpSignBitSet:
-    case SpvOpLessOrGreater:
-    case SpvOpOrdered:
-    case SpvOpUnordered:
-    case SpvOpLogicalEqual:
-    case SpvOpLogicalNotEqual:
-    case SpvOpLogicalOr:
-    case SpvOpLogicalAnd:
-    case SpvOpLogicalNot:
-    case SpvOpSelect:
-    case SpvOpIEqual:
-    case SpvOpINotEqual:
-    case SpvOpUGreaterThan:
-    case SpvOpSGreaterThan:
-    case SpvOpUGreaterThanEqual:
-    case SpvOpSGreaterThanEqual:
-    case SpvOpULessThan:
-    case SpvOpSLessThan:
-    case SpvOpULessThanEqual:
-    case SpvOpSLessThanEqual:
-    case SpvOpFOrdEqual:
-    case SpvOpFUnordEqual:
-    case SpvOpFOrdNotEqual:
-    case SpvOpFUnordNotEqual:
-    case SpvOpFOrdLessThan:
-    case SpvOpFUnordLessThan:
-    case SpvOpFOrdGreaterThan:
-    case SpvOpFUnordGreaterThan:
-    case SpvOpFOrdLessThanEqual:
-    case SpvOpFUnordLessThanEqual:
-    case SpvOpFOrdGreaterThanEqual:
-    case SpvOpFUnordGreaterThanEqual:
-    case SpvOpShiftRightLogical:
-    case SpvOpShiftRightArithmetic:
-    case SpvOpShiftLeftLogical:
-    case SpvOpBitwiseOr:
-    case SpvOpBitwiseXor:
-    case SpvOpBitwiseAnd:
-    case SpvOpNot:
-    case SpvOpBitFieldInsert:
-    case SpvOpBitFieldSExtract:
-    case SpvOpBitFieldUExtract:
-    case SpvOpBitReverse:
-    case SpvOpBitCount:
-    case SpvOpCopyLogical:
-    case SpvOpPtrEqual:
-    case SpvOpPtrNotEqual:
+    case spv::Op::OpUndef:
+    case spv::Op::OpAccessChain:
+    case spv::Op::OpInBoundsAccessChain:
+    case spv::Op::OpArrayLength:
+    case spv::Op::OpVectorExtractDynamic:
+    case spv::Op::OpVectorInsertDynamic:
+    case spv::Op::OpVectorShuffle:
+    case spv::Op::OpCompositeConstruct:
+    case spv::Op::OpCompositeExtract:
+    case spv::Op::OpCompositeInsert:
+    case spv::Op::OpCopyObject:
+    case spv::Op::OpTranspose:
+    case spv::Op::OpConvertFToU:
+    case spv::Op::OpConvertFToS:
+    case spv::Op::OpConvertSToF:
+    case spv::Op::OpConvertUToF:
+    case spv::Op::OpUConvert:
+    case spv::Op::OpSConvert:
+    case spv::Op::OpFConvert:
+    case spv::Op::OpQuantizeToF16:
+    case spv::Op::OpSatConvertSToU:
+    case spv::Op::OpSatConvertUToS:
+    case spv::Op::OpBitcast:
+    case spv::Op::OpSNegate:
+    case spv::Op::OpFNegate:
+    case spv::Op::OpIAdd:
+    case spv::Op::OpFAdd:
+    case spv::Op::OpISub:
+    case spv::Op::OpFSub:
+    case spv::Op::OpIMul:
+    case spv::Op::OpFMul:
+    case spv::Op::OpUDiv:
+    case spv::Op::OpSDiv:
+    case spv::Op::OpFDiv:
+    case spv::Op::OpUMod:
+    case spv::Op::OpSRem:
+    case spv::Op::OpSMod:
+    case spv::Op::OpFRem:
+    case spv::Op::OpFMod:
+    case spv::Op::OpVectorTimesScalar:
+    case spv::Op::OpMatrixTimesScalar:
+    case spv::Op::OpVectorTimesMatrix:
+    case spv::Op::OpMatrixTimesVector:
+    case spv::Op::OpMatrixTimesMatrix:
+    case spv::Op::OpOuterProduct:
+    case spv::Op::OpDot:
+    case spv::Op::OpIAddCarry:
+    case spv::Op::OpISubBorrow:
+    case spv::Op::OpUMulExtended:
+    case spv::Op::OpSMulExtended:
+    case spv::Op::OpAny:
+    case spv::Op::OpAll:
+    case spv::Op::OpIsNan:
+    case spv::Op::OpIsInf:
+    case spv::Op::OpIsFinite:
+    case spv::Op::OpIsNormal:
+    case spv::Op::OpSignBitSet:
+    case spv::Op::OpLessOrGreater:
+    case spv::Op::OpOrdered:
+    case spv::Op::OpUnordered:
+    case spv::Op::OpLogicalEqual:
+    case spv::Op::OpLogicalNotEqual:
+    case spv::Op::OpLogicalOr:
+    case spv::Op::OpLogicalAnd:
+    case spv::Op::OpLogicalNot:
+    case spv::Op::OpSelect:
+    case spv::Op::OpIEqual:
+    case spv::Op::OpINotEqual:
+    case spv::Op::OpUGreaterThan:
+    case spv::Op::OpSGreaterThan:
+    case spv::Op::OpUGreaterThanEqual:
+    case spv::Op::OpSGreaterThanEqual:
+    case spv::Op::OpULessThan:
+    case spv::Op::OpSLessThan:
+    case spv::Op::OpULessThanEqual:
+    case spv::Op::OpSLessThanEqual:
+    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::OpShiftRightLogical:
+    case spv::Op::OpShiftRightArithmetic:
+    case spv::Op::OpShiftLeftLogical:
+    case spv::Op::OpBitwiseOr:
+    case spv::Op::OpBitwiseXor:
+    case spv::Op::OpBitwiseAnd:
+    case spv::Op::OpNot:
+    case spv::Op::OpBitFieldInsert:
+    case spv::Op::OpBitFieldSExtract:
+    case spv::Op::OpBitFieldUExtract:
+    case spv::Op::OpBitReverse:
+    case spv::Op::OpBitCount:
+    case spv::Op::OpCopyLogical:
+    case spv::Op::OpPtrEqual:
+    case spv::Op::OpPtrNotEqual:
       return true;
     default:
       return false;
@@ -408,7 +408,7 @@
   // use |inst|.
   for (auto successor_id : successor_ids) {
     for (const auto& maybe_phi_inst : *ir_context->cfg()->block(successor_id)) {
-      if (maybe_phi_inst.opcode() != SpvOpPhi) {
+      if (maybe_phi_inst.opcode() != spv::Op::OpPhi) {
         // OpPhis can be intermixed with OpLine and OpNoLine.
         continue;
       }
@@ -446,7 +446,7 @@
         // |phi_block_id| dominates |user|'s block (or its predecessor if the
         // user is an OpPhi). We can't use fuzzerutil::IdIsAvailableAtUse since
         // the id in question hasn't yet been created in the module.
-        auto block_id_to_dominate = user->opcode() == SpvOpPhi
+        auto block_id_to_dominate = user->opcode() == spv::Op::OpPhi
                                         ? user->GetSingleWordOperand(index + 1)
                                         : user_block->id();
 
@@ -465,7 +465,7 @@
 
 opt::Instruction*
 TransformationPropagateInstructionDown::GetFirstInsertBeforeInstruction(
-    opt::IRContext* ir_context, uint32_t block_id, SpvOp opcode) {
+    opt::IRContext* ir_context, uint32_t block_id, spv::Op opcode) {
   auto* block = ir_context->cfg()->block(block_id);
 
   auto it = block->begin();
@@ -572,7 +572,7 @@
   // OpPhi instructions cannot have operands of pointer types.
   if (propagate_type->AsPointer() &&
       !ir_context->get_feature_mgr()->HasCapability(
-          SpvCapabilityVariablePointersStorageBuffer)) {
+          spv::Capability::VariablePointersStorageBuffer)) {
     return 0;
   }
 
diff --git a/source/fuzz/transformation_propagate_instruction_down.h b/source/fuzz/transformation_propagate_instruction_down.h
index 560d7dc..9133928 100644
--- a/source/fuzz/transformation_propagate_instruction_down.h
+++ b/source/fuzz/transformation_propagate_instruction_down.h
@@ -120,12 +120,12 @@
                                                      uint32_t block_id);
 
   // Returns true if |opcode| is supported by this transformation.
-  static bool IsOpcodeSupported(SpvOp opcode);
+  static bool IsOpcodeSupported(spv::Op opcode);
 
   // Returns the first instruction in the |block| that allows us to insert
   // |opcode| above itself. Returns nullptr is no such instruction exists.
   static opt::Instruction* GetFirstInsertBeforeInstruction(
-      opt::IRContext* ir_context, uint32_t block_id, SpvOp opcode);
+      opt::IRContext* ir_context, uint32_t block_id, spv::Op opcode);
 
   // Returns a result id of a basic block, where an OpPhi instruction can be
   // inserted. Returns nullptr if it's not possible to create an OpPhi. The
diff --git a/source/fuzz/transformation_propagate_instruction_up.cpp b/source/fuzz/transformation_propagate_instruction_up.cpp
index bf0e663..f9674fa 100644
--- a/source/fuzz/transformation_propagate_instruction_up.cpp
+++ b/source/fuzz/transformation_propagate_instruction_up.cpp
@@ -23,7 +23,7 @@
 
 uint32_t GetResultIdFromLabelId(const opt::Instruction& phi_inst,
                                 uint32_t label_id) {
-  assert(phi_inst.opcode() == SpvOpPhi && "|phi_inst| is not an OpPhi");
+  assert(phi_inst.opcode() == spv::Op::OpPhi && "|phi_inst| is not an OpPhi");
 
   for (uint32_t i = 1; i < phi_inst.NumInOperands(); i += 2) {
     if (phi_inst.GetSingleWordInOperand(i) == label_id) {
@@ -66,7 +66,7 @@
     assert(dependency && "Operand has invalid id");
 
     if (ir_context->get_instr_block(dependency) == inst_block &&
-        dependency->opcode() != SpvOpPhi) {
+        dependency->opcode() != spv::Op::OpPhi) {
       // |dependency| is "valid" if it's an OpPhi from the same basic block or
       // an instruction from a different basic block.
       return false;
@@ -173,7 +173,7 @@
         continue;
       }
 
-      assert(dependency_inst->opcode() == SpvOpPhi &&
+      assert(dependency_inst->opcode() == spv::Op::OpPhi &&
              "Propagated instruction can depend only on OpPhis from the same "
              "basic block or instructions from different basic blocks");
 
@@ -191,7 +191,7 @@
 
   // Insert an OpPhi instruction into the basic block of |inst|.
   ir_context->get_instr_block(inst)->begin()->InsertBefore(
-      MakeUnique<opt::Instruction>(ir_context, SpvOpPhi, inst->type_id(),
+      MakeUnique<opt::Instruction>(ir_context, spv::Op::OpPhi, inst->type_id(),
                                    inst->result_id(),
                                    std::move(op_phi_operands)));
 
@@ -210,115 +210,115 @@
   return result;
 }
 
-bool TransformationPropagateInstructionUp::IsOpcodeSupported(SpvOp opcode) {
+bool TransformationPropagateInstructionUp::IsOpcodeSupported(spv::Op opcode) {
   // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3605):
   //  We only support "simple" instructions that don't work with memory.
   //  We should extend this so that we support the ones that modify the memory
   //  too.
   switch (opcode) {
-    case SpvOpUndef:
-    case SpvOpAccessChain:
-    case SpvOpInBoundsAccessChain:
-    case SpvOpArrayLength:
-    case SpvOpVectorExtractDynamic:
-    case SpvOpVectorInsertDynamic:
-    case SpvOpVectorShuffle:
-    case SpvOpCompositeConstruct:
-    case SpvOpCompositeExtract:
-    case SpvOpCompositeInsert:
-    case SpvOpCopyObject:
-    case SpvOpTranspose:
-    case SpvOpConvertFToU:
-    case SpvOpConvertFToS:
-    case SpvOpConvertSToF:
-    case SpvOpConvertUToF:
-    case SpvOpUConvert:
-    case SpvOpSConvert:
-    case SpvOpFConvert:
-    case SpvOpQuantizeToF16:
-    case SpvOpSatConvertSToU:
-    case SpvOpSatConvertUToS:
-    case SpvOpBitcast:
-    case SpvOpSNegate:
-    case SpvOpFNegate:
-    case SpvOpIAdd:
-    case SpvOpFAdd:
-    case SpvOpISub:
-    case SpvOpFSub:
-    case SpvOpIMul:
-    case SpvOpFMul:
-    case SpvOpUDiv:
-    case SpvOpSDiv:
-    case SpvOpFDiv:
-    case SpvOpUMod:
-    case SpvOpSRem:
-    case SpvOpSMod:
-    case SpvOpFRem:
-    case SpvOpFMod:
-    case SpvOpVectorTimesScalar:
-    case SpvOpMatrixTimesScalar:
-    case SpvOpVectorTimesMatrix:
-    case SpvOpMatrixTimesVector:
-    case SpvOpMatrixTimesMatrix:
-    case SpvOpOuterProduct:
-    case SpvOpDot:
-    case SpvOpIAddCarry:
-    case SpvOpISubBorrow:
-    case SpvOpUMulExtended:
-    case SpvOpSMulExtended:
-    case SpvOpAny:
-    case SpvOpAll:
-    case SpvOpIsNan:
-    case SpvOpIsInf:
-    case SpvOpIsFinite:
-    case SpvOpIsNormal:
-    case SpvOpSignBitSet:
-    case SpvOpLessOrGreater:
-    case SpvOpOrdered:
-    case SpvOpUnordered:
-    case SpvOpLogicalEqual:
-    case SpvOpLogicalNotEqual:
-    case SpvOpLogicalOr:
-    case SpvOpLogicalAnd:
-    case SpvOpLogicalNot:
-    case SpvOpSelect:
-    case SpvOpIEqual:
-    case SpvOpINotEqual:
-    case SpvOpUGreaterThan:
-    case SpvOpSGreaterThan:
-    case SpvOpUGreaterThanEqual:
-    case SpvOpSGreaterThanEqual:
-    case SpvOpULessThan:
-    case SpvOpSLessThan:
-    case SpvOpULessThanEqual:
-    case SpvOpSLessThanEqual:
-    case SpvOpFOrdEqual:
-    case SpvOpFUnordEqual:
-    case SpvOpFOrdNotEqual:
-    case SpvOpFUnordNotEqual:
-    case SpvOpFOrdLessThan:
-    case SpvOpFUnordLessThan:
-    case SpvOpFOrdGreaterThan:
-    case SpvOpFUnordGreaterThan:
-    case SpvOpFOrdLessThanEqual:
-    case SpvOpFUnordLessThanEqual:
-    case SpvOpFOrdGreaterThanEqual:
-    case SpvOpFUnordGreaterThanEqual:
-    case SpvOpShiftRightLogical:
-    case SpvOpShiftRightArithmetic:
-    case SpvOpShiftLeftLogical:
-    case SpvOpBitwiseOr:
-    case SpvOpBitwiseXor:
-    case SpvOpBitwiseAnd:
-    case SpvOpNot:
-    case SpvOpBitFieldInsert:
-    case SpvOpBitFieldSExtract:
-    case SpvOpBitFieldUExtract:
-    case SpvOpBitReverse:
-    case SpvOpBitCount:
-    case SpvOpCopyLogical:
-    case SpvOpPtrEqual:
-    case SpvOpPtrNotEqual:
+    case spv::Op::OpUndef:
+    case spv::Op::OpAccessChain:
+    case spv::Op::OpInBoundsAccessChain:
+    case spv::Op::OpArrayLength:
+    case spv::Op::OpVectorExtractDynamic:
+    case spv::Op::OpVectorInsertDynamic:
+    case spv::Op::OpVectorShuffle:
+    case spv::Op::OpCompositeConstruct:
+    case spv::Op::OpCompositeExtract:
+    case spv::Op::OpCompositeInsert:
+    case spv::Op::OpCopyObject:
+    case spv::Op::OpTranspose:
+    case spv::Op::OpConvertFToU:
+    case spv::Op::OpConvertFToS:
+    case spv::Op::OpConvertSToF:
+    case spv::Op::OpConvertUToF:
+    case spv::Op::OpUConvert:
+    case spv::Op::OpSConvert:
+    case spv::Op::OpFConvert:
+    case spv::Op::OpQuantizeToF16:
+    case spv::Op::OpSatConvertSToU:
+    case spv::Op::OpSatConvertUToS:
+    case spv::Op::OpBitcast:
+    case spv::Op::OpSNegate:
+    case spv::Op::OpFNegate:
+    case spv::Op::OpIAdd:
+    case spv::Op::OpFAdd:
+    case spv::Op::OpISub:
+    case spv::Op::OpFSub:
+    case spv::Op::OpIMul:
+    case spv::Op::OpFMul:
+    case spv::Op::OpUDiv:
+    case spv::Op::OpSDiv:
+    case spv::Op::OpFDiv:
+    case spv::Op::OpUMod:
+    case spv::Op::OpSRem:
+    case spv::Op::OpSMod:
+    case spv::Op::OpFRem:
+    case spv::Op::OpFMod:
+    case spv::Op::OpVectorTimesScalar:
+    case spv::Op::OpMatrixTimesScalar:
+    case spv::Op::OpVectorTimesMatrix:
+    case spv::Op::OpMatrixTimesVector:
+    case spv::Op::OpMatrixTimesMatrix:
+    case spv::Op::OpOuterProduct:
+    case spv::Op::OpDot:
+    case spv::Op::OpIAddCarry:
+    case spv::Op::OpISubBorrow:
+    case spv::Op::OpUMulExtended:
+    case spv::Op::OpSMulExtended:
+    case spv::Op::OpAny:
+    case spv::Op::OpAll:
+    case spv::Op::OpIsNan:
+    case spv::Op::OpIsInf:
+    case spv::Op::OpIsFinite:
+    case spv::Op::OpIsNormal:
+    case spv::Op::OpSignBitSet:
+    case spv::Op::OpLessOrGreater:
+    case spv::Op::OpOrdered:
+    case spv::Op::OpUnordered:
+    case spv::Op::OpLogicalEqual:
+    case spv::Op::OpLogicalNotEqual:
+    case spv::Op::OpLogicalOr:
+    case spv::Op::OpLogicalAnd:
+    case spv::Op::OpLogicalNot:
+    case spv::Op::OpSelect:
+    case spv::Op::OpIEqual:
+    case spv::Op::OpINotEqual:
+    case spv::Op::OpUGreaterThan:
+    case spv::Op::OpSGreaterThan:
+    case spv::Op::OpUGreaterThanEqual:
+    case spv::Op::OpSGreaterThanEqual:
+    case spv::Op::OpULessThan:
+    case spv::Op::OpSLessThan:
+    case spv::Op::OpULessThanEqual:
+    case spv::Op::OpSLessThanEqual:
+    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::OpShiftRightLogical:
+    case spv::Op::OpShiftRightArithmetic:
+    case spv::Op::OpShiftLeftLogical:
+    case spv::Op::OpBitwiseOr:
+    case spv::Op::OpBitwiseXor:
+    case spv::Op::OpBitwiseAnd:
+    case spv::Op::OpNot:
+    case spv::Op::OpBitFieldInsert:
+    case spv::Op::OpBitFieldSExtract:
+    case spv::Op::OpBitFieldUExtract:
+    case spv::Op::OpBitReverse:
+    case spv::Op::OpBitCount:
+    case spv::Op::OpCopyLogical:
+    case spv::Op::OpPtrEqual:
+    case spv::Op::OpPtrNotEqual:
       return true;
     default:
       return false;
@@ -338,7 +338,7 @@
     // - it must be supported by this transformation
     // - it may depend only on instructions from different basic blocks or on
     //   OpPhi instructions from the same basic block.
-    if (inst.opcode() == SpvOpPhi || !IsOpcodeSupported(inst.opcode()) ||
+    if (inst.opcode() == spv::Op::OpPhi || !IsOpcodeSupported(inst.opcode()) ||
         !inst.type_id() || !inst.result_id()) {
       continue;
     }
@@ -353,7 +353,7 @@
     }
 
     if (!ir_context->get_feature_mgr()->HasCapability(
-            SpvCapabilityVariablePointersStorageBuffer) &&
+            spv::Capability::VariablePointersStorageBuffer) &&
         ContainsPointers(*inst_type)) {
       // OpPhi supports pointer operands only with VariablePointers or
       // VariablePointersStorageBuffer capabilities.
@@ -377,7 +377,7 @@
     opt::IRContext* ir_context, uint32_t block_id) {
   // Check that |block_id| is valid.
   const auto* label_inst = ir_context->get_def_use_mgr()->GetDef(block_id);
-  if (!label_inst || label_inst->opcode() != SpvOpLabel) {
+  if (!label_inst || label_inst->opcode() != spv::Op::OpLabel) {
     return false;
   }
 
diff --git a/source/fuzz/transformation_propagate_instruction_up.h b/source/fuzz/transformation_propagate_instruction_up.h
index 0ca051b..93aa365 100644
--- a/source/fuzz/transformation_propagate_instruction_up.h
+++ b/source/fuzz/transformation_propagate_instruction_up.h
@@ -80,7 +80,7 @@
                                                      uint32_t block_id);
 
   // Returns true if |opcode| is supported by this transformation.
-  static bool IsOpcodeSupported(SpvOp opcode);
+  static bool IsOpcodeSupported(spv::Op opcode);
 
   protobufs::TransformationPropagateInstructionUp message_;
 };
diff --git a/source/fuzz/transformation_push_id_through_variable.cpp b/source/fuzz/transformation_push_id_through_variable.cpp
index 55a57a1..2372d70 100644
--- a/source/fuzz/transformation_push_id_through_variable.cpp
+++ b/source/fuzz/transformation_push_id_through_variable.cpp
@@ -53,9 +53,9 @@
 
   // It must be valid to insert the OpStore and OpLoad instruction before it.
   if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(
-          SpvOpStore, instruction_to_insert_before) ||
+          spv::Op::OpStore, instruction_to_insert_before) ||
       !fuzzerutil::CanInsertOpcodeBeforeInstruction(
-          SpvOpLoad, instruction_to_insert_before)) {
+          spv::Op::OpLoad, instruction_to_insert_before)) {
     return false;
   }
 
@@ -75,14 +75,16 @@
   // A pointer type instruction pointing to the value type must be defined.
   auto pointer_type_id = fuzzerutil::MaybeGetPointerType(
       ir_context, value_instruction->type_id(),
-      static_cast<SpvStorageClass>(message_.variable_storage_class()));
+      static_cast<spv::StorageClass>(message_.variable_storage_class()));
   if (!pointer_type_id) {
     return false;
   }
 
   // |message_.variable_storage_class| must be private or function.
-  assert((message_.variable_storage_class() == SpvStorageClassPrivate ||
-          message_.variable_storage_class() == SpvStorageClassFunction) &&
+  assert((message_.variable_storage_class() ==
+              (uint32_t)spv::StorageClass::Private ||
+          message_.variable_storage_class() ==
+              (uint32_t)spv::StorageClass::Function) &&
          "The variable storage class must be private or function.");
 
   // Check that initializer is valid.
@@ -111,14 +113,15 @@
   // A pointer type instruction pointing to the value type must be defined.
   auto pointer_type_id = fuzzerutil::MaybeGetPointerType(
       ir_context, value_instruction->type_id(),
-      static_cast<SpvStorageClass>(message_.variable_storage_class()));
+      static_cast<spv::StorageClass>(message_.variable_storage_class()));
   assert(pointer_type_id && "The required pointer type must be available.");
 
   // Adds whether a global or local variable.
-  if (message_.variable_storage_class() == SpvStorageClassPrivate) {
+  if (spv::StorageClass(message_.variable_storage_class()) ==
+      spv::StorageClass::Private) {
     opt::Instruction* global_variable = fuzzerutil::AddGlobalVariable(
         ir_context, message_.variable_id(), pointer_type_id,
-        SpvStorageClassPrivate, message_.initializer_id());
+        spv::StorageClass::Private, message_.initializer_id());
     ir_context->get_def_use_mgr()->AnalyzeInstDefUse(global_variable);
   } else {
     opt::Function* function =
@@ -138,13 +141,13 @@
   fuzzerutil::UpdateModuleIdBound(ir_context, message_.value_synonym_id());
   opt::Instruction* load_instruction =
       insert_before->InsertBefore(MakeUnique<opt::Instruction>(
-          ir_context, SpvOpLoad, value_instruction->type_id(),
+          ir_context, spv::Op::OpLoad, value_instruction->type_id(),
           message_.value_synonym_id(),
           opt::Instruction::OperandList(
               {{SPV_OPERAND_TYPE_ID, {message_.variable_id()}}})));
   opt::Instruction* store_instruction =
       load_instruction->InsertBefore(MakeUnique<opt::Instruction>(
-          ir_context, SpvOpStore, 0, 0,
+          ir_context, spv::Op::OpStore, 0, 0,
           opt::Instruction::OperandList(
               {{SPV_OPERAND_TYPE_ID, {message_.variable_id()}},
                {SPV_OPERAND_TYPE_ID, {message_.value_id()}}})));
diff --git a/source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.cpp b/source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.cpp
index e1977a6..03e9737 100644
--- a/source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.cpp
+++ b/source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.cpp
@@ -97,20 +97,20 @@
 
   // Determine the opcode of the new instruction that computes the result into a
   // struct.
-  SpvOp new_instruction_opcode;
+  spv::Op new_instruction_opcode;
 
   switch (original_instruction->opcode()) {
-    case SpvOpIAdd:
-      new_instruction_opcode = SpvOpIAddCarry;
+    case spv::Op::OpIAdd:
+      new_instruction_opcode = spv::Op::OpIAddCarry;
       break;
-    case SpvOpISub:
-      new_instruction_opcode = SpvOpISubBorrow;
+    case spv::Op::OpISub:
+      new_instruction_opcode = spv::Op::OpISubBorrow;
       break;
-    case SpvOpIMul:
+    case spv::Op::OpIMul:
       if (!operand_is_signed) {
-        new_instruction_opcode = SpvOpUMulExtended;
+        new_instruction_opcode = spv::Op::OpUMulExtended;
       } else {
-        new_instruction_opcode = SpvOpSMulExtended;
+        new_instruction_opcode = spv::Op::OpSMulExtended;
       }
       break;
     default:
@@ -148,7 +148,7 @@
   // takes the first component of the struct which represents low-order bits of
   // the operation. This is the original result.
   original_instruction->InsertBefore(MakeUnique<opt::Instruction>(
-      ir_context, SpvOpCompositeExtract, original_instruction->type_id(),
+      ir_context, spv::Op::OpCompositeExtract, original_instruction->type_id(),
       message_.result_id(),
       opt::Instruction::OperandList(
           {{SPV_OPERAND_TYPE_ID, {message_.struct_fresh_id()}},
@@ -168,9 +168,9 @@
 
   // Only instructions OpIAdd, OpISub, OpIMul are supported.
   switch (instruction_opcode) {
-    case SpvOpIAdd:
-    case SpvOpISub:
-    case SpvOpIMul:
+    case spv::Op::OpIAdd:
+    case spv::Op::OpISub:
+    case spv::Op::OpIMul:
       break;
     default:
       return false;
@@ -201,8 +201,8 @@
   auto type = ir_context->get_type_mgr()->GetType(instruction.type_id());
 
   switch (instruction_opcode) {
-    case SpvOpIAdd:
-    case SpvOpISub: {
+    case spv::Op::OpIAdd:
+    case spv::Op::OpISub: {
       // In case of OpIAdd and OpISub if the operand is a vector, the component
       // type must be unsigned. Otherwise (if the operand is an int), the
       // operand must be unsigned.
diff --git a/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.cpp b/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.cpp
index 2429351..efd1555 100644
--- a/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.cpp
+++ b/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.cpp
@@ -28,7 +28,8 @@
 // operator |binop|, returns true if it is certain that 'lhs binop rhs'
 // evaluates to |required_value|.
 template <typename T>
-bool float_binop_evaluates_to(T lhs, T rhs, SpvOp binop, bool required_value) {
+bool float_binop_evaluates_to(T lhs, T rhs, spv::Op binop,
+                              bool required_value) {
   // Infinity and NaN values are conservatively treated as out of scope.
   if (!std::isfinite(lhs) || !std::isfinite(rhs)) {
     return false;
@@ -37,20 +38,20 @@
   // The following captures the binary operators that spirv-fuzz can actually
   // generate when turning a boolean constant into a binary expression.
   switch (binop) {
-    case SpvOpFOrdGreaterThanEqual:
-    case SpvOpFUnordGreaterThanEqual:
+    case spv::Op::OpFOrdGreaterThanEqual:
+    case spv::Op::OpFUnordGreaterThanEqual:
       binop_result = (lhs >= rhs);
       break;
-    case SpvOpFOrdGreaterThan:
-    case SpvOpFUnordGreaterThan:
+    case spv::Op::OpFOrdGreaterThan:
+    case spv::Op::OpFUnordGreaterThan:
       binop_result = (lhs > rhs);
       break;
-    case SpvOpFOrdLessThanEqual:
-    case SpvOpFUnordLessThanEqual:
+    case spv::Op::OpFOrdLessThanEqual:
+    case spv::Op::OpFUnordLessThanEqual:
       binop_result = (lhs <= rhs);
       break;
-    case SpvOpFOrdLessThan:
-    case SpvOpFUnordLessThan:
+    case spv::Op::OpFOrdLessThan:
+    case spv::Op::OpFUnordLessThan:
       binop_result = (lhs < rhs);
       break;
     default:
@@ -61,20 +62,20 @@
 
 // Analogous to 'float_binop_evaluates_to', but for signed int values.
 template <typename T>
-bool signed_int_binop_evaluates_to(T lhs, T rhs, SpvOp binop,
+bool signed_int_binop_evaluates_to(T lhs, T rhs, spv::Op binop,
                                    bool required_value) {
   bool binop_result;
   switch (binop) {
-    case SpvOpSGreaterThanEqual:
+    case spv::Op::OpSGreaterThanEqual:
       binop_result = (lhs >= rhs);
       break;
-    case SpvOpSGreaterThan:
+    case spv::Op::OpSGreaterThan:
       binop_result = (lhs > rhs);
       break;
-    case SpvOpSLessThanEqual:
+    case spv::Op::OpSLessThanEqual:
       binop_result = (lhs <= rhs);
       break;
-    case SpvOpSLessThan:
+    case spv::Op::OpSLessThan:
       binop_result = (lhs < rhs);
       break;
     default:
@@ -85,20 +86,20 @@
 
 // Analogous to 'float_binop_evaluates_to', but for unsigned int values.
 template <typename T>
-bool unsigned_int_binop_evaluates_to(T lhs, T rhs, SpvOp binop,
+bool unsigned_int_binop_evaluates_to(T lhs, T rhs, spv::Op binop,
                                      bool required_value) {
   bool binop_result;
   switch (binop) {
-    case SpvOpUGreaterThanEqual:
+    case spv::Op::OpUGreaterThanEqual:
       binop_result = (lhs >= rhs);
       break;
-    case SpvOpUGreaterThan:
+    case spv::Op::OpUGreaterThan:
       binop_result = (lhs > rhs);
       break;
-    case SpvOpULessThanEqual:
+    case spv::Op::OpULessThanEqual:
       binop_result = (lhs <= rhs);
       break;
-    case SpvOpULessThan:
+    case spv::Op::OpULessThan:
       binop_result = (lhs < rhs);
       break;
     default:
@@ -118,12 +119,12 @@
 TransformationReplaceBooleanConstantWithConstantBinary::
     TransformationReplaceBooleanConstantWithConstantBinary(
         const protobufs::IdUseDescriptor& id_use_descriptor, uint32_t lhs_id,
-        uint32_t rhs_id, SpvOp comparison_opcode,
+        uint32_t rhs_id, spv::Op comparison_opcode,
         uint32_t fresh_id_for_binary_operation) {
   *message_.mutable_id_use_descriptor() = id_use_descriptor;
   message_.set_lhs_id(lhs_id);
   message_.set_rhs_id(rhs_id);
-  message_.set_opcode(comparison_opcode);
+  message_.set_opcode(uint32_t(comparison_opcode));
   message_.set_fresh_id_for_binary_operation(fresh_id_for_binary_operation);
 }
 
@@ -141,8 +142,8 @@
   if (!boolean_constant) {
     return false;
   }
-  if (!(boolean_constant->opcode() == SpvOpConstantFalse ||
-        boolean_constant->opcode() == SpvOpConstantTrue)) {
+  if (!(boolean_constant->opcode() == spv::Op::OpConstantFalse ||
+        boolean_constant->opcode() == spv::Op::OpConstantTrue)) {
     return false;
   }
 
@@ -152,7 +153,7 @@
   if (!lhs_constant_inst) {
     return false;
   }
-  if (lhs_constant_inst->opcode() != SpvOpConstant) {
+  if (lhs_constant_inst->opcode() != spv::Op::OpConstant) {
     return false;
   }
 
@@ -162,7 +163,7 @@
   if (!rhs_constant_inst) {
     return false;
   }
-  if (rhs_constant_inst->opcode() != SpvOpConstant) {
+  if (rhs_constant_inst->opcode() != spv::Op::OpConstant) {
     return false;
   }
 
@@ -176,9 +177,10 @@
       ir_context->get_constant_mgr()->FindDeclaredConstant(message_.lhs_id());
   auto rhs_constant =
       ir_context->get_constant_mgr()->FindDeclaredConstant(message_.rhs_id());
-  bool expected_result = (boolean_constant->opcode() == SpvOpConstantTrue);
+  bool expected_result =
+      (boolean_constant->opcode() == spv::Op::OpConstantTrue);
 
-  const auto binary_opcode = static_cast<SpvOp>(message_.opcode());
+  const auto binary_opcode = static_cast<spv::Op>(message_.opcode());
 
   // We consider the floating point, signed and unsigned integer cases
   // separately.  In each case the logic is very similar.
@@ -247,7 +249,7 @@
   // a binary operator before an OpVariable, but in any case (b) the
   // constant we would be replacing is the initializer constant of the
   // OpVariable, and this cannot be the result of a binary operation.
-  if (instruction->opcode() == SpvOpVariable) {
+  if (instruction->opcode() == spv::Op::OpVariable) {
     return false;
   }
 
@@ -268,7 +270,7 @@
       {SPV_OPERAND_TYPE_ID, {message_.lhs_id()}},
       {SPV_OPERAND_TYPE_ID, {message_.rhs_id()}}};
   auto binary_instruction = MakeUnique<opt::Instruction>(
-      ir_context, static_cast<SpvOp>(message_.opcode()),
+      ir_context, static_cast<spv::Op>(message_.opcode()),
       ir_context->get_type_mgr()->GetId(&bool_type),
       message_.fresh_id_for_binary_operation(), operands);
   opt::Instruction* result = binary_instruction.get();
@@ -279,7 +281,7 @@
   // If |instruction_before_which_to_insert| is an OpPhi instruction,
   // then |binary_instruction| will be inserted into the parent block associated
   // with the OpPhi variable operand.
-  if (instruction_containing_constant_use->opcode() == SpvOpPhi) {
+  if (instruction_containing_constant_use->opcode() == spv::Op::OpPhi) {
     instruction_before_which_to_insert =
         ir_context->cfg()
             ->block(instruction_containing_constant_use->GetSingleWordInOperand(
@@ -293,8 +295,9 @@
   {
     opt::Instruction* previous_node =
         instruction_before_which_to_insert->PreviousNode();
-    if (previous_node && (previous_node->opcode() == SpvOpLoopMerge ||
-                          previous_node->opcode() == SpvOpSelectionMerge)) {
+    if (previous_node &&
+        (previous_node->opcode() == spv::Op::OpLoopMerge ||
+         previous_node->opcode() == spv::Op::OpSelectionMerge)) {
       instruction_before_which_to_insert = previous_node;
     }
   }
diff --git a/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h b/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h
index 97c66bf..4d48bf2 100644
--- a/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h
+++ b/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h
@@ -32,7 +32,7 @@
 
   TransformationReplaceBooleanConstantWithConstantBinary(
       const protobufs::IdUseDescriptor& id_use_descriptor, uint32_t lhs_id,
-      uint32_t rhs_id, SpvOp comparison_opcode,
+      uint32_t rhs_id, spv::Op comparison_opcode,
       uint32_t fresh_id_for_binary_operation);
 
   // - |message_.fresh_id_for_binary_operation| must not already be used by the
diff --git a/source/fuzz/transformation_replace_branch_from_dead_block_with_exit.cpp b/source/fuzz/transformation_replace_branch_from_dead_block_with_exit.cpp
index 9ea7cb6..4150bb1 100644
--- a/source/fuzz/transformation_replace_branch_from_dead_block_with_exit.cpp
+++ b/source/fuzz/transformation_replace_branch_from_dead_block_with_exit.cpp
@@ -26,10 +26,10 @@
 
 TransformationReplaceBranchFromDeadBlockWithExit::
     TransformationReplaceBranchFromDeadBlockWithExit(uint32_t block_id,
-                                                     SpvOp opcode,
+                                                     spv::Op opcode,
                                                      uint32_t return_value_id) {
   message_.set_block_id(block_id);
-  message_.set_opcode(opcode);
+  message_.set_opcode(uint32_t(opcode));
   message_.set_return_value_id(return_value_id);
 }
 
@@ -45,11 +45,11 @@
     return false;
   }
   auto function_return_type_id = block->GetParent()->type_id();
-  switch (message_.opcode()) {
-    case SpvOpKill:
+  switch (spv::Op(message_.opcode())) {
+    case spv::Op::OpKill:
       for (auto& entry_point : ir_context->module()->entry_points()) {
-        if (entry_point.GetSingleWordInOperand(0) !=
-            SpvExecutionModelFragment) {
+        if (spv::ExecutionModel(entry_point.GetSingleWordInOperand(0)) !=
+            spv::ExecutionModel::Fragment) {
           // OpKill is only allowed in a fragment shader.  This is a
           // conservative check: if the module contains a non-fragment entry
           // point then adding an OpKill might lead to OpKill being used in a
@@ -58,15 +58,15 @@
         }
       }
       break;
-    case SpvOpReturn:
+    case spv::Op::OpReturn:
       if (ir_context->get_def_use_mgr()
               ->GetDef(function_return_type_id)
-              ->opcode() != SpvOpTypeVoid) {
+              ->opcode() != spv::Op::OpTypeVoid) {
         // OpReturn is only allowed in a function with void return type.
         return false;
       }
       break;
-    case SpvOpReturnValue: {
+    case spv::Op::OpReturnValue: {
       // If the terminator is to be changed to OpReturnValue, with
       // |message_.return_value_id| being the value that will be returned, then
       // |message_.return_value_id| must have a compatible type and be available
@@ -83,7 +83,7 @@
       break;
     }
     default:
-      assert(message_.opcode() == SpvOpUnreachable &&
+      assert(spv::Op(message_.opcode()) == spv::Op::OpUnreachable &&
              "Invalid early exit opcode.");
       break;
   }
@@ -95,7 +95,7 @@
   // If the successor block has OpPhi instructions then arguments related to
   // |message_.block_id| need to be removed from these instruction.
   auto block = ir_context->get_instr_block(message_.block_id());
-  assert(block->terminator()->opcode() == SpvOpBranch &&
+  assert(block->terminator()->opcode() == spv::Op::OpBranch &&
          "Precondition: the block must end with OpBranch.");
   auto successor = ir_context->get_instr_block(
       block->terminator()->GetSingleWordInOperand(0));
@@ -114,12 +114,12 @@
 
   // Rewrite the terminator of |message_.block_id|.
   opt::Instruction::OperandList new_terminator_in_operands;
-  if (message_.opcode() == SpvOpReturnValue) {
+  if (spv::Op(message_.opcode()) == spv::Op::OpReturnValue) {
     new_terminator_in_operands.push_back(
         {SPV_OPERAND_TYPE_ID, {message_.return_value_id()}});
   }
   auto terminator = block->terminator();
-  terminator->SetOpcode(static_cast<SpvOp>(message_.opcode()));
+  terminator->SetOpcode(static_cast<spv::Op>(message_.opcode()));
   terminator->SetInOperands(std::move(new_terminator_in_operands));
   ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
 }
@@ -145,7 +145,7 @@
     return false;
   }
   // The block's terminator must be OpBranch.
-  if (block.terminator()->opcode() != SpvOpBranch) {
+  if (block.terminator()->opcode() != spv::Op::OpBranch) {
     return false;
   }
   if (ir_context->GetStructuredCFGAnalysis()->IsInContinueConstruct(
@@ -164,7 +164,7 @@
   // Make sure that domination rules are satisfied when we remove the branch
   // from the |block| to its |successor|.
   return fuzzerutil::NewTerminatorPreservesDominationRules(
-      ir_context, block.id(), {ir_context, SpvOpUnreachable});
+      ir_context, block.id(), {ir_context, spv::Op::OpUnreachable});
 }
 
 }  // namespace fuzz
diff --git a/source/fuzz/transformation_replace_branch_from_dead_block_with_exit.h b/source/fuzz/transformation_replace_branch_from_dead_block_with_exit.h
index 89667fc..9a5d42e 100644
--- a/source/fuzz/transformation_replace_branch_from_dead_block_with_exit.h
+++ b/source/fuzz/transformation_replace_branch_from_dead_block_with_exit.h
@@ -30,7 +30,7 @@
       protobufs::TransformationReplaceBranchFromDeadBlockWithExit message);
 
   TransformationReplaceBranchFromDeadBlockWithExit(uint32_t block_id,
-                                                   SpvOp opcode,
+                                                   spv::Op opcode,
                                                    uint32_t return_value_id);
 
   // - |message_.block_id| must be the id of a dead block that is not part of
diff --git a/source/fuzz/transformation_replace_constant_with_uniform.cpp b/source/fuzz/transformation_replace_constant_with_uniform.cpp
index c6698c0..7174e6a 100644
--- a/source/fuzz/transformation_replace_constant_with_uniform.cpp
+++ b/source/fuzz/transformation_replace_constant_with_uniform.cpp
@@ -67,15 +67,15 @@
   // The type id for the access chain is a uniform pointer with base type
   // matching the given constant id type.
   auto type_and_pointer_type =
-      ir_context->get_type_mgr()->GetTypeAndPointerType(constant_type_id,
-                                                        SpvStorageClassUniform);
+      ir_context->get_type_mgr()->GetTypeAndPointerType(
+          constant_type_id, spv::StorageClass::Uniform);
   assert(type_and_pointer_type.first != nullptr);
   assert(type_and_pointer_type.second != nullptr);
   auto pointer_to_uniform_constant_type_id =
       ir_context->get_type_mgr()->GetId(type_and_pointer_type.second.get());
 
   return MakeUnique<opt::Instruction>(
-      ir_context, SpvOpAccessChain, pointer_to_uniform_constant_type_id,
+      ir_context, spv::Op::OpAccessChain, pointer_to_uniform_constant_type_id,
       message_.fresh_id_for_access_chain(), operands_for_access_chain);
 }
 
@@ -84,9 +84,9 @@
     spvtools::opt::IRContext* ir_context, uint32_t constant_type_id) const {
   opt::Instruction::OperandList operands_for_load = {
       {SPV_OPERAND_TYPE_ID, {message_.fresh_id_for_access_chain()}}};
-  return MakeUnique<opt::Instruction>(ir_context, SpvOpLoad, constant_type_id,
-                                      message_.fresh_id_for_load(),
-                                      operands_for_load);
+  return MakeUnique<opt::Instruction>(
+      ir_context, spv::Op::OpLoad, constant_type_id,
+      message_.fresh_id_for_load(), operands_for_load);
 }
 
 opt::Instruction*
@@ -99,7 +99,7 @@
   }
 
   // The use might be in an OpPhi instruction.
-  if (result->opcode() == SpvOpPhi) {
+  if (result->opcode() == spv::Op::OpPhi) {
     // OpPhi instructions must be the first instructions in a block. Thus, we
     // can't insert above the OpPhi instruction. Given the predecessor block
     // that corresponds to the id use, get the last instruction in that block
@@ -108,18 +108,19 @@
         ir_context,
         result->GetSingleWordInOperand(
             message_.id_use_descriptor().in_operand_index() + 1),
-        SpvOpLoad);
+        spv::Op::OpLoad);
   }
 
   // The only operand that we could've replaced in the OpBranchConditional is
   // the condition id. But that operand has a boolean type and uniform variables
   // can't store booleans (see the spec on OpTypeBool). Thus, |result| can't be
   // an OpBranchConditional.
-  assert(result->opcode() != SpvOpBranchConditional &&
+  assert(result->opcode() != spv::Op::OpBranchConditional &&
          "OpBranchConditional has no operands to replace");
 
-  assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad, result) &&
-         "We should be able to insert OpLoad and OpAccessChain at this point");
+  assert(
+      fuzzerutil::CanInsertOpcodeBeforeInstruction(spv::Op::OpLoad, result) &&
+      "We should be able to insert OpLoad and OpAccessChain at this point");
   return result;
 }
 
@@ -191,15 +192,15 @@
 
   // The use must not be a variable initializer; these are required to be
   // constants, so it would be illegal to replace one with a uniform access.
-  if (instruction_using_constant->opcode() == SpvOpVariable) {
+  if (instruction_using_constant->opcode() == spv::Op::OpVariable) {
     return false;
   }
 
   // The module needs to have a uniform pointer type suitable for indexing into
   // the uniform variable, i.e. matching the type of the constant we wish to
   // replace with a uniform.
-  opt::analysis::Pointer pointer_to_type_of_constant(declared_constant->type(),
-                                                     SpvStorageClassUniform);
+  opt::analysis::Pointer pointer_to_type_of_constant(
+      declared_constant->type(), spv::StorageClass::Uniform);
   if (!ir_context->get_type_mgr()->GetId(&pointer_to_type_of_constant)) {
     return false;
   }
diff --git a/source/fuzz/transformation_replace_copy_memory_with_load_store.cpp b/source/fuzz/transformation_replace_copy_memory_with_load_store.cpp
index de9d1fd..8f14145 100644
--- a/source/fuzz/transformation_replace_copy_memory_with_load_store.cpp
+++ b/source/fuzz/transformation_replace_copy_memory_with_load_store.cpp
@@ -45,7 +45,7 @@
   auto copy_memory_instruction = FindInstruction(
       message_.copy_memory_instruction_descriptor(), ir_context);
   if (!copy_memory_instruction ||
-      copy_memory_instruction->opcode() != SpvOpCopyMemory) {
+      copy_memory_instruction->opcode() != spv::Op::OpCopyMemory) {
     return false;
   }
   return true;
@@ -57,7 +57,7 @@
       message_.copy_memory_instruction_descriptor(), ir_context);
   // |copy_memory_instruction| must be defined.
   assert(copy_memory_instruction &&
-         copy_memory_instruction->opcode() == SpvOpCopyMemory &&
+         copy_memory_instruction->opcode() == spv::Op::OpCopyMemory &&
          "The required OpCopyMemory instruction must be defined.");
 
   // Integrity check: Both operands must be pointers.
@@ -78,8 +78,8 @@
   (void)target_type_opcode;
   (void)source_type_opcode;
 
-  assert(target_type_opcode == SpvOpTypePointer &&
-         source_type_opcode == SpvOpTypePointer &&
+  assert(target_type_opcode == spv::Op::OpTypePointer &&
+         source_type_opcode == spv::Op::OpTypePointer &&
          "Operands must be of type OpTypePointer");
 
   // Integrity check: |source| and |target| must point to the same type.
@@ -100,12 +100,12 @@
   fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
   FindInstruction(message_.copy_memory_instruction_descriptor(), ir_context)
       ->InsertBefore(MakeUnique<opt::Instruction>(
-          ir_context, SpvOpStore, 0, 0,
+          ir_context, spv::Op::OpStore, 0, 0,
           opt::Instruction::OperandList(
               {{SPV_OPERAND_TYPE_ID, {target->result_id()}},
                {SPV_OPERAND_TYPE_ID, {message_.fresh_id()}}})))
       ->InsertBefore(MakeUnique<opt::Instruction>(
-          ir_context, SpvOpLoad, target_pointee_type, message_.fresh_id(),
+          ir_context, spv::Op::OpLoad, target_pointee_type, message_.fresh_id(),
           opt::Instruction::OperandList(
               {{SPV_OPERAND_TYPE_ID, {source->result_id()}}})));
 
diff --git a/source/fuzz/transformation_replace_copy_object_with_store_load.cpp b/source/fuzz/transformation_replace_copy_object_with_store_load.cpp
index e0643bf..f08a734 100644
--- a/source/fuzz/transformation_replace_copy_object_with_store_load.cpp
+++ b/source/fuzz/transformation_replace_copy_object_with_store_load.cpp
@@ -46,7 +46,7 @@
 
   // This must be a defined OpCopyObject instruction.
   if (!copy_object_instruction ||
-      copy_object_instruction->opcode() != SpvOpCopyObject) {
+      copy_object_instruction->opcode() != spv::Op::OpCopyObject) {
     return false;
   }
 
@@ -54,14 +54,14 @@
   // because we cannot define a pointer to pointer.
   if (ir_context->get_def_use_mgr()
           ->GetDef(copy_object_instruction->type_id())
-          ->opcode() == SpvOpTypePointer) {
+          ->opcode() == spv::Op::OpTypePointer) {
     return false;
   }
 
   // A pointer type instruction pointing to the value type must be defined.
   auto pointer_type_id = fuzzerutil::MaybeGetPointerType(
       ir_context, copy_object_instruction->type_id(),
-      static_cast<SpvStorageClass>(message_.variable_storage_class()));
+      static_cast<spv::StorageClass>(message_.variable_storage_class()));
   if (!pointer_type_id) {
     return false;
   }
@@ -74,8 +74,10 @@
     return false;
   }
   // |message_.variable_storage_class| must be Private or Function.
-  return message_.variable_storage_class() == SpvStorageClassPrivate ||
-         message_.variable_storage_class() == SpvStorageClassFunction;
+  return spv::StorageClass(message_.variable_storage_class()) ==
+             spv::StorageClass::Private ||
+         spv::StorageClass(message_.variable_storage_class()) ==
+             spv::StorageClass::Function;
 }
 
 void TransformationReplaceCopyObjectWithStoreLoad::Apply(
@@ -85,7 +87,7 @@
       ir_context->get_def_use_mgr()->GetDef(message_.copy_object_result_id());
   // |copy_object_instruction| must be defined.
   assert(copy_object_instruction &&
-         copy_object_instruction->opcode() == SpvOpCopyObject &&
+         copy_object_instruction->opcode() == spv::Op::OpCopyObject &&
          "The required OpCopyObject instruction must be defined.");
 
   opt::BasicBlock* enclosing_block =
@@ -96,14 +98,15 @@
   // A pointer type instruction pointing to the value type must be defined.
   auto pointer_type_id = fuzzerutil::MaybeGetPointerType(
       ir_context, copy_object_instruction->type_id(),
-      static_cast<SpvStorageClass>(message_.variable_storage_class()));
+      static_cast<spv::StorageClass>(message_.variable_storage_class()));
   assert(pointer_type_id && "The required pointer type must be available.");
 
   // Adds a global or local variable (according to the storage class).
-  if (message_.variable_storage_class() == SpvStorageClassPrivate) {
+  if (spv::StorageClass(message_.variable_storage_class()) ==
+      spv::StorageClass::Private) {
     opt::Instruction* new_global = fuzzerutil::AddGlobalVariable(
         ir_context, message_.fresh_variable_id(), pointer_type_id,
-        SpvStorageClassPrivate, message_.variable_initializer_id());
+        spv::StorageClass::Private, message_.variable_initializer_id());
     ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_global);
   } else {
     opt::Function* function =
@@ -120,13 +123,13 @@
   fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_variable_id());
   opt::Instruction* load_instruction =
       copy_object_instruction->InsertBefore(MakeUnique<opt::Instruction>(
-          ir_context, SpvOpLoad, copy_object_instruction->type_id(),
+          ir_context, spv::Op::OpLoad, copy_object_instruction->type_id(),
           message_.copy_object_result_id(),
           opt::Instruction::OperandList(
               {{SPV_OPERAND_TYPE_ID, {message_.fresh_variable_id()}}})));
   opt::Instruction* store_instruction =
       load_instruction->InsertBefore(MakeUnique<opt::Instruction>(
-          ir_context, SpvOpStore, 0, 0,
+          ir_context, spv::Op::OpStore, 0, 0,
           opt::Instruction::OperandList(
               {{SPV_OPERAND_TYPE_ID, {message_.fresh_variable_id()}},
                {SPV_OPERAND_TYPE_ID, {src_operand}}})));
diff --git a/source/fuzz/transformation_replace_irrelevant_id.cpp b/source/fuzz/transformation_replace_irrelevant_id.cpp
index a71f96a..7f64063 100644
--- a/source/fuzz/transformation_replace_irrelevant_id.cpp
+++ b/source/fuzz/transformation_replace_irrelevant_id.cpp
@@ -65,7 +65,7 @@
   }
 
   // The replacement id must not be the result of an OpFunction instruction.
-  if (replacement_id_def->opcode() == SpvOpFunction) {
+  if (replacement_id_def->opcode() == spv::Op::OpFunction) {
     return false;
   }
 
@@ -130,7 +130,7 @@
     AttemptsToReplaceVariableInitializerWithNonConstant(
         const opt::Instruction& use_instruction,
         const opt::Instruction& replacement_for_use) {
-  return use_instruction.opcode() == SpvOpVariable &&
+  return use_instruction.opcode() == spv::Op::OpVariable &&
          !spvOpcodeIsConstant(replacement_for_use.opcode());
 }
 
diff --git a/source/fuzz/transformation_replace_linear_algebra_instruction.cpp b/source/fuzz/transformation_replace_linear_algebra_instruction.cpp
index 2430cca..fb7b294 100644
--- a/source/fuzz/transformation_replace_linear_algebra_instruction.cpp
+++ b/source/fuzz/transformation_replace_linear_algebra_instruction.cpp
@@ -68,28 +68,28 @@
       FindInstruction(message_.instruction_descriptor(), ir_context);
 
   switch (linear_algebra_instruction->opcode()) {
-    case SpvOpTranspose:
+    case spv::Op::OpTranspose:
       ReplaceOpTranspose(ir_context, linear_algebra_instruction);
       break;
-    case SpvOpVectorTimesScalar:
+    case spv::Op::OpVectorTimesScalar:
       ReplaceOpVectorTimesScalar(ir_context, linear_algebra_instruction);
       break;
-    case SpvOpMatrixTimesScalar:
+    case spv::Op::OpMatrixTimesScalar:
       ReplaceOpMatrixTimesScalar(ir_context, linear_algebra_instruction);
       break;
-    case SpvOpVectorTimesMatrix:
+    case spv::Op::OpVectorTimesMatrix:
       ReplaceOpVectorTimesMatrix(ir_context, linear_algebra_instruction);
       break;
-    case SpvOpMatrixTimesVector:
+    case spv::Op::OpMatrixTimesVector:
       ReplaceOpMatrixTimesVector(ir_context, linear_algebra_instruction);
       break;
-    case SpvOpMatrixTimesMatrix:
+    case spv::Op::OpMatrixTimesMatrix:
       ReplaceOpMatrixTimesMatrix(ir_context, linear_algebra_instruction);
       break;
-    case SpvOpOuterProduct:
+    case spv::Op::OpOuterProduct:
       ReplaceOpOuterProduct(ir_context, linear_algebra_instruction);
       break;
-    case SpvOpDot:
+    case spv::Op::OpDot:
       ReplaceOpDot(ir_context, linear_algebra_instruction);
       break;
     default:
@@ -112,7 +112,7 @@
   // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3354):
   // Right now we only support certain operations.
   switch (instruction->opcode()) {
-    case SpvOpTranspose: {
+    case spv::Op::OpTranspose: {
       // For each matrix row, |2 * matrix_column_count| OpCompositeExtract and 1
       // OpCompositeConstruct will be inserted.
       auto matrix_instruction = ir_context->get_def_use_mgr()->GetDef(
@@ -130,7 +130,7 @@
                                       ->element_count();
       return matrix_row_count * (2 * matrix_column_count + 1);
     }
-    case SpvOpVectorTimesScalar:
+    case spv::Op::OpVectorTimesScalar:
       // For each vector component, 1 OpCompositeExtract and 1 OpFMul will be
       // inserted.
       return 2 *
@@ -140,7 +140,7 @@
                                ->type_id())
                  ->AsVector()
                  ->element_count();
-    case SpvOpMatrixTimesScalar: {
+    case spv::Op::OpMatrixTimesScalar: {
       // For each matrix column, |1 + column.size| OpCompositeExtract,
       // |column.size| OpFMul and 1 OpCompositeConstruct instructions will be
       // inserted.
@@ -154,7 +154,7 @@
                       ->AsVector()
                       ->element_count());
     }
-    case SpvOpVectorTimesMatrix: {
+    case spv::Op::OpVectorTimesMatrix: {
       // For each vector component, 1 OpCompositeExtract instruction will be
       // inserted. For each matrix column, |1 + vector_component_count|
       // OpCompositeExtract, |vector_component_count| OpFMul and
@@ -175,7 +175,7 @@
               ->element_count();
       return vector_component_count * (3 * matrix_column_count + 1);
     }
-    case SpvOpMatrixTimesVector: {
+    case spv::Op::OpMatrixTimesVector: {
       // For each matrix column, |1 + matrix_row_count| OpCompositeExtract
       // will be inserted. For each matrix row, |matrix_column_count| OpFMul and
       // |matrix_column_count - 1| OpFAdd instructions will be inserted. For
@@ -197,7 +197,7 @@
       return 3 * matrix_column_count * matrix_row_count +
              2 * matrix_column_count - matrix_row_count;
     }
-    case SpvOpMatrixTimesMatrix: {
+    case spv::Op::OpMatrixTimesMatrix: {
       // For each matrix 2 column, 1 OpCompositeExtract, 1 OpCompositeConstruct,
       // |3 * matrix_1_row_count * matrix_1_column_count| OpCompositeExtract,
       // |matrix_1_row_count * matrix_1_column_count| OpFMul,
@@ -228,7 +228,7 @@
       return matrix_2_column_count *
              (2 + matrix_1_row_count * (5 * matrix_1_column_count - 1));
     }
-    case SpvOpOuterProduct: {
+    case spv::Op::OpOuterProduct: {
       // For each |vector_2| component, |vector_1_component_count + 1|
       // OpCompositeExtract, |vector_1_component_count| OpFMul and 1
       // OpCompositeConstruct instructions will be inserted.
@@ -248,7 +248,7 @@
               ->element_count();
       return 2 * vector_2_component_count * (vector_1_component_count + 1);
     }
-    case SpvOpDot:
+    case spv::Op::OpDot:
       // For each pair of vector components, 2 OpCompositeExtract and 1 OpFMul
       // will be inserted. The first two OpFMul instructions will result the
       // first OpFAdd instruction to be inserted. For each remaining OpFMul, 1
@@ -299,7 +299,7 @@
       // Extracts the matrix column.
       uint32_t matrix_column_id = message_.fresh_ids(fresh_id_index++);
       linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
-          ir_context, SpvOpCompositeExtract,
+          ir_context, spv::Op::OpCompositeExtract,
           ir_context->get_type_mgr()->GetId(matrix_column_type),
           matrix_column_id,
           opt::Instruction::OperandList(
@@ -309,7 +309,7 @@
       // Extracts the matrix column component.
       column_component_ids[j] = message_.fresh_ids(fresh_id_index++);
       linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
-          ir_context, SpvOpCompositeExtract,
+          ir_context, spv::Op::OpCompositeExtract,
           ir_context->get_type_mgr()->GetId(matrix_column_component_type),
           column_component_ids[j],
           opt::Instruction::OperandList(
@@ -324,14 +324,14 @@
     }
     result_column_ids[i] = message_.fresh_ids(fresh_id_index++);
     linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
-        ir_context, SpvOpCompositeConstruct,
+        ir_context, spv::Op::OpCompositeConstruct,
         ir_context->get_type_mgr()->GetId(resulting_matrix_column_type),
         result_column_ids[i], opt::Instruction::OperandList(in_operands)));
   }
 
   // The OpTranspose instruction is changed to an OpCompositeConstruct
   // instruction.
-  linear_algebra_instruction->SetOpcode(SpvOpCompositeConstruct);
+  linear_algebra_instruction->SetOpcode(spv::Op::OpCompositeConstruct);
   linear_algebra_instruction->SetInOperand(0, {result_column_ids[0]});
   for (uint32_t i = 1; i < result_column_ids.size(); i++) {
     linear_algebra_instruction->AddOperand(
@@ -363,7 +363,8 @@
     uint32_t vector_extract_id = message_.fresh_ids(fresh_id_index++);
     fuzzerutil::UpdateModuleIdBound(ir_context, vector_extract_id);
     linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
-        ir_context, SpvOpCompositeExtract, scalar->type_id(), vector_extract_id,
+        ir_context, spv::Op::OpCompositeExtract, scalar->type_id(),
+        vector_extract_id,
         opt::Instruction::OperandList(
             {{SPV_OPERAND_TYPE_ID, {vector->result_id()}},
              {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}})));
@@ -373,7 +374,7 @@
     float_multiplication_ids[i] = float_multiplication_id;
     fuzzerutil::UpdateModuleIdBound(ir_context, float_multiplication_id);
     linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
-        ir_context, SpvOpFMul, scalar->type_id(), float_multiplication_id,
+        ir_context, spv::Op::OpFMul, scalar->type_id(), float_multiplication_id,
         opt::Instruction::OperandList(
             {{SPV_OPERAND_TYPE_ID, {vector_extract_id}},
              {SPV_OPERAND_TYPE_ID, {scalar->result_id()}}})));
@@ -381,7 +382,7 @@
 
   // The OpVectorTimesScalar instruction is changed to an OpCompositeConstruct
   // instruction.
-  linear_algebra_instruction->SetOpcode(SpvOpCompositeConstruct);
+  linear_algebra_instruction->SetOpcode(spv::Op::OpCompositeConstruct);
   linear_algebra_instruction->SetInOperand(0, {float_multiplication_ids[0]});
   linear_algebra_instruction->SetInOperand(1, {float_multiplication_ids[1]});
   for (uint32_t i = 2; i < float_multiplication_ids.size(); i++) {
@@ -418,7 +419,7 @@
     uint32_t matrix_extract_id = message_.fresh_ids(fresh_id_index++);
     fuzzerutil::UpdateModuleIdBound(ir_context, matrix_extract_id);
     linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
-        ir_context, SpvOpCompositeExtract,
+        ir_context, spv::Op::OpCompositeExtract,
         ir_context->get_type_mgr()->GetId(matrix_column_type),
         matrix_extract_id,
         opt::Instruction::OperandList(
@@ -432,8 +433,8 @@
       uint32_t column_extract_id = message_.fresh_ids(fresh_id_index++);
       fuzzerutil::UpdateModuleIdBound(ir_context, column_extract_id);
       linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
-          ir_context, SpvOpCompositeExtract, scalar_instruction->type_id(),
-          column_extract_id,
+          ir_context, spv::Op::OpCompositeExtract,
+          scalar_instruction->type_id(), column_extract_id,
           opt::Instruction::OperandList(
               {{SPV_OPERAND_TYPE_ID, {matrix_extract_id}},
                {SPV_OPERAND_TYPE_LITERAL_INTEGER, {j}}})));
@@ -442,7 +443,7 @@
       float_multiplication_ids[j] = message_.fresh_ids(fresh_id_index++);
       fuzzerutil::UpdateModuleIdBound(ir_context, float_multiplication_ids[j]);
       linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
-          ir_context, SpvOpFMul, scalar_instruction->type_id(),
+          ir_context, spv::Op::OpFMul, scalar_instruction->type_id(),
           float_multiplication_ids[j],
           opt::Instruction::OperandList(
               {{SPV_OPERAND_TYPE_ID, {column_extract_id}},
@@ -458,14 +459,14 @@
     composite_construct_ids[i] = message_.fresh_ids(fresh_id_index++);
     fuzzerutil::UpdateModuleIdBound(ir_context, composite_construct_ids[i]);
     linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
-        ir_context, SpvOpCompositeConstruct,
+        ir_context, spv::Op::OpCompositeConstruct,
         ir_context->get_type_mgr()->GetId(matrix_column_type),
         composite_construct_ids[i], composite_construct_in_operands));
   }
 
   // The OpMatrixTimesScalar instruction is changed to an OpCompositeConstruct
   // instruction.
-  linear_algebra_instruction->SetOpcode(SpvOpCompositeConstruct);
+  linear_algebra_instruction->SetOpcode(spv::Op::OpCompositeConstruct);
   linear_algebra_instruction->SetInOperand(0, {composite_construct_ids[0]});
   linear_algebra_instruction->SetInOperand(1, {composite_construct_ids[1]});
   for (uint32_t i = 2; i < composite_construct_ids.size(); i++) {
@@ -495,7 +496,7 @@
   for (uint32_t i = 0; i < vector_component_count; i++) {
     vector_component_ids[i] = message_.fresh_ids(fresh_id_index++);
     linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
-        ir_context, SpvOpCompositeExtract,
+        ir_context, spv::Op::OpCompositeExtract,
         ir_context->get_type_mgr()->GetId(vector_component_type),
         vector_component_ids[i],
         opt::Instruction::OperandList(
@@ -520,7 +521,7 @@
     // Extracts matrix column.
     uint32_t matrix_extract_id = message_.fresh_ids(fresh_id_index++);
     linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
-        ir_context, SpvOpCompositeExtract,
+        ir_context, spv::Op::OpCompositeExtract,
         ir_context->get_type_mgr()->GetId(matrix_column_type),
         matrix_extract_id,
         opt::Instruction::OperandList(
@@ -532,7 +533,7 @@
       // Extracts column component.
       uint32_t column_extract_id = message_.fresh_ids(fresh_id_index++);
       linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
-          ir_context, SpvOpCompositeExtract,
+          ir_context, spv::Op::OpCompositeExtract,
           ir_context->get_type_mgr()->GetId(vector_component_type),
           column_extract_id,
           opt::Instruction::OperandList(
@@ -542,7 +543,7 @@
       // Multiplies corresponding vector and column components.
       float_multiplication_ids[j] = message_.fresh_ids(fresh_id_index++);
       linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
-          ir_context, SpvOpFMul,
+          ir_context, spv::Op::OpFMul,
           ir_context->get_type_mgr()->GetId(vector_component_type),
           float_multiplication_ids[j],
           opt::Instruction::OperandList(
@@ -555,7 +556,7 @@
     uint32_t float_add_id = message_.fresh_ids(fresh_id_index++);
     float_add_ids.push_back(float_add_id);
     linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
-        ir_context, SpvOpFAdd,
+        ir_context, spv::Op::OpFAdd,
         ir_context->get_type_mgr()->GetId(vector_component_type), float_add_id,
         opt::Instruction::OperandList(
             {{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[0]}},
@@ -564,7 +565,7 @@
       float_add_id = message_.fresh_ids(fresh_id_index++);
       float_add_ids.push_back(float_add_id);
       linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
-          ir_context, SpvOpFAdd,
+          ir_context, spv::Op::OpFAdd,
           ir_context->get_type_mgr()->GetId(vector_component_type),
           float_add_id,
           opt::Instruction::OperandList(
@@ -577,7 +578,7 @@
 
   // The OpVectorTimesMatrix instruction is changed to an OpCompositeConstruct
   // instruction.
-  linear_algebra_instruction->SetOpcode(SpvOpCompositeConstruct);
+  linear_algebra_instruction->SetOpcode(spv::Op::OpCompositeConstruct);
   linear_algebra_instruction->SetInOperand(0, {result_component_ids[0]});
   linear_algebra_instruction->SetInOperand(1, {result_component_ids[1]});
   for (uint32_t i = 2; i < result_component_ids.size(); i++) {
@@ -611,7 +612,7 @@
   for (uint32_t i = 0; i < matrix_column_count; i++) {
     matrix_column_ids[i] = message_.fresh_ids(fresh_id_index++);
     linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
-        ir_context, SpvOpCompositeExtract,
+        ir_context, spv::Op::OpCompositeExtract,
         ir_context->get_type_mgr()->GetId(matrix_column_type),
         matrix_column_ids[i],
         opt::Instruction::OperandList(
@@ -632,7 +633,7 @@
   for (uint32_t i = 0; i < matrix_column_count; i++) {
     vector_component_ids[i] = message_.fresh_ids(fresh_id_index++);
     linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
-        ir_context, SpvOpCompositeExtract,
+        ir_context, spv::Op::OpCompositeExtract,
         ir_context->get_type_mgr()->GetId(vector_component_type),
         vector_component_ids[i],
         opt::Instruction::OperandList(
@@ -647,7 +648,7 @@
       // Extracts column component.
       uint32_t column_extract_id = message_.fresh_ids(fresh_id_index++);
       linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
-          ir_context, SpvOpCompositeExtract,
+          ir_context, spv::Op::OpCompositeExtract,
           ir_context->get_type_mgr()->GetId(vector_component_type),
           column_extract_id,
           opt::Instruction::OperandList(
@@ -657,7 +658,7 @@
       // Multiplies corresponding vector and column components.
       float_multiplication_ids[j] = message_.fresh_ids(fresh_id_index++);
       linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
-          ir_context, SpvOpFMul,
+          ir_context, spv::Op::OpFMul,
           ir_context->get_type_mgr()->GetId(vector_component_type),
           float_multiplication_ids[j],
           opt::Instruction::OperandList(
@@ -670,7 +671,7 @@
     uint32_t float_add_id = message_.fresh_ids(fresh_id_index++);
     float_add_ids.push_back(float_add_id);
     linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
-        ir_context, SpvOpFAdd,
+        ir_context, spv::Op::OpFAdd,
         ir_context->get_type_mgr()->GetId(vector_component_type), float_add_id,
         opt::Instruction::OperandList(
             {{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[0]}},
@@ -679,7 +680,7 @@
       float_add_id = message_.fresh_ids(fresh_id_index++);
       float_add_ids.push_back(float_add_id);
       linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
-          ir_context, SpvOpFAdd,
+          ir_context, spv::Op::OpFAdd,
           ir_context->get_type_mgr()->GetId(vector_component_type),
           float_add_id,
           opt::Instruction::OperandList(
@@ -692,7 +693,7 @@
 
   // The OpMatrixTimesVector instruction is changed to an OpCompositeConstruct
   // instruction.
-  linear_algebra_instruction->SetOpcode(SpvOpCompositeConstruct);
+  linear_algebra_instruction->SetOpcode(spv::Op::OpCompositeConstruct);
   linear_algebra_instruction->SetInOperand(0, {result_component_ids[0]});
   linear_algebra_instruction->SetInOperand(1, {result_component_ids[1]});
   for (uint32_t i = 2; i < result_component_ids.size(); i++) {
@@ -743,7 +744,7 @@
     // Extracts matrix 2 column.
     uint32_t matrix_2_column_id = message_.fresh_ids(fresh_id_index++);
     linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
-        ir_context, SpvOpCompositeExtract,
+        ir_context, spv::Op::OpCompositeExtract,
         ir_context->get_type_mgr()->GetId(matrix_2_column_type),
         matrix_2_column_id,
         opt::Instruction::OperandList(
@@ -757,7 +758,7 @@
         // Extracts matrix 1 column.
         uint32_t matrix_1_column_id = message_.fresh_ids(fresh_id_index++);
         linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
-            ir_context, SpvOpCompositeExtract,
+            ir_context, spv::Op::OpCompositeExtract,
             ir_context->get_type_mgr()->GetId(matrix_1_column_type),
             matrix_1_column_id,
             opt::Instruction::OperandList(
@@ -768,7 +769,7 @@
         uint32_t matrix_1_column_component_id =
             message_.fresh_ids(fresh_id_index++);
         linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
-            ir_context, SpvOpCompositeExtract,
+            ir_context, spv::Op::OpCompositeExtract,
             ir_context->get_type_mgr()->GetId(matrix_1_column_component_type),
             matrix_1_column_component_id,
             opt::Instruction::OperandList(
@@ -779,7 +780,7 @@
         uint32_t matrix_2_column_component_id =
             message_.fresh_ids(fresh_id_index++);
         linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
-            ir_context, SpvOpCompositeExtract,
+            ir_context, spv::Op::OpCompositeExtract,
             ir_context->get_type_mgr()->GetId(matrix_1_column_component_type),
             matrix_2_column_component_id,
             opt::Instruction::OperandList(
@@ -789,7 +790,7 @@
         // Multiplies corresponding matrix 1 and matrix 2 column components.
         float_multiplication_ids[k] = message_.fresh_ids(fresh_id_index++);
         linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
-            ir_context, SpvOpFMul,
+            ir_context, spv::Op::OpFMul,
             ir_context->get_type_mgr()->GetId(matrix_1_column_component_type),
             float_multiplication_ids[k],
             opt::Instruction::OperandList(
@@ -802,7 +803,7 @@
       uint32_t float_add_id = message_.fresh_ids(fresh_id_index++);
       float_add_ids.push_back(float_add_id);
       linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
-          ir_context, SpvOpFAdd,
+          ir_context, spv::Op::OpFAdd,
           ir_context->get_type_mgr()->GetId(matrix_1_column_component_type),
           float_add_id,
           opt::Instruction::OperandList(
@@ -812,7 +813,7 @@
         float_add_id = message_.fresh_ids(fresh_id_index++);
         float_add_ids.push_back(float_add_id);
         linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
-            ir_context, SpvOpFAdd,
+            ir_context, spv::Op::OpFAdd,
             ir_context->get_type_mgr()->GetId(matrix_1_column_component_type),
             float_add_id,
             opt::Instruction::OperandList(
@@ -830,14 +831,14 @@
     }
     result_column_ids[i] = message_.fresh_ids(fresh_id_index++);
     linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
-        ir_context, SpvOpCompositeConstruct,
+        ir_context, spv::Op::OpCompositeConstruct,
         ir_context->get_type_mgr()->GetId(matrix_1_column_type),
         result_column_ids[i], opt::Instruction::OperandList(in_operands)));
   }
 
   // The OpMatrixTimesMatrix instruction is changed to an OpCompositeConstruct
   // instruction.
-  linear_algebra_instruction->SetOpcode(SpvOpCompositeConstruct);
+  linear_algebra_instruction->SetOpcode(spv::Op::OpCompositeConstruct);
   linear_algebra_instruction->SetInOperand(0, {result_column_ids[0]});
   linear_algebra_instruction->SetInOperand(1, {result_column_ids[1]});
   for (uint32_t i = 2; i < result_column_ids.size(); i++) {
@@ -880,7 +881,7 @@
     // Extracts |vector_2| component.
     uint32_t vector_2_component_id = message_.fresh_ids(fresh_id_index++);
     linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
-        ir_context, SpvOpCompositeExtract,
+        ir_context, spv::Op::OpCompositeExtract,
         ir_context->get_type_mgr()->GetId(vector_1_component_type),
         vector_2_component_id,
         opt::Instruction::OperandList(
@@ -892,7 +893,7 @@
       // Extracts |vector_1| component.
       uint32_t vector_1_component_id = message_.fresh_ids(fresh_id_index++);
       linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
-          ir_context, SpvOpCompositeExtract,
+          ir_context, spv::Op::OpCompositeExtract,
           ir_context->get_type_mgr()->GetId(vector_1_component_type),
           vector_1_component_id,
           opt::Instruction::OperandList(
@@ -902,7 +903,7 @@
       // Multiplies |vector_1| and |vector_2| components.
       column_component_ids[j] = message_.fresh_ids(fresh_id_index++);
       linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
-          ir_context, SpvOpFMul,
+          ir_context, spv::Op::OpFMul,
           ir_context->get_type_mgr()->GetId(vector_1_component_type),
           column_component_ids[j],
           opt::Instruction::OperandList(
@@ -917,13 +918,13 @@
     }
     result_column_ids[i] = message_.fresh_ids(fresh_id_index++);
     linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
-        ir_context, SpvOpCompositeConstruct, vector_1_instruction->type_id(),
-        result_column_ids[i], in_operands));
+        ir_context, spv::Op::OpCompositeConstruct,
+        vector_1_instruction->type_id(), result_column_ids[i], in_operands));
   }
 
   // The OpOuterProduct instruction is changed to an OpCompositeConstruct
   // instruction.
-  linear_algebra_instruction->SetOpcode(SpvOpCompositeConstruct);
+  linear_algebra_instruction->SetOpcode(spv::Op::OpCompositeConstruct);
   linear_algebra_instruction->SetInOperand(0, {result_column_ids[0]});
   linear_algebra_instruction->SetInOperand(1, {result_column_ids[1]});
   for (uint32_t i = 2; i < result_column_ids.size(); i++) {
@@ -956,7 +957,7 @@
     uint32_t vector_1_extract_id = message_.fresh_ids(fresh_id_index++);
     fuzzerutil::UpdateModuleIdBound(ir_context, vector_1_extract_id);
     linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
-        ir_context, SpvOpCompositeExtract,
+        ir_context, spv::Op::OpCompositeExtract,
         linear_algebra_instruction->type_id(), vector_1_extract_id,
         opt::Instruction::OperandList(
             {{SPV_OPERAND_TYPE_ID, {vector_1->result_id()}},
@@ -966,7 +967,7 @@
     uint32_t vector_2_extract_id = message_.fresh_ids(fresh_id_index++);
     fuzzerutil::UpdateModuleIdBound(ir_context, vector_2_extract_id);
     linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
-        ir_context, SpvOpCompositeExtract,
+        ir_context, spv::Op::OpCompositeExtract,
         linear_algebra_instruction->type_id(), vector_2_extract_id,
         opt::Instruction::OperandList(
             {{SPV_OPERAND_TYPE_ID, {vector_2->result_id()}},
@@ -976,7 +977,7 @@
     float_multiplication_ids[i] = message_.fresh_ids(fresh_id_index++);
     fuzzerutil::UpdateModuleIdBound(ir_context, float_multiplication_ids[i]);
     linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
-        ir_context, SpvOpFMul, linear_algebra_instruction->type_id(),
+        ir_context, spv::Op::OpFMul, linear_algebra_instruction->type_id(),
         float_multiplication_ids[i],
         opt::Instruction::OperandList(
             {{SPV_OPERAND_TYPE_ID, {vector_1_extract_id}},
@@ -986,7 +987,7 @@
   // If the vector has 2 components, then there will be 2 float multiplication
   // instructions.
   if (vectors_component_count == 2) {
-    linear_algebra_instruction->SetOpcode(SpvOpFAdd);
+    linear_algebra_instruction->SetOpcode(spv::Op::OpFAdd);
     linear_algebra_instruction->SetInOperand(0, {float_multiplication_ids[0]});
     linear_algebra_instruction->SetInOperand(1, {float_multiplication_ids[1]});
   } else {
@@ -997,7 +998,7 @@
     float_add_ids.push_back(float_add_id);
     fuzzerutil::UpdateModuleIdBound(ir_context, float_add_id);
     linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
-        ir_context, SpvOpFAdd, linear_algebra_instruction->type_id(),
+        ir_context, spv::Op::OpFAdd, linear_algebra_instruction->type_id(),
         float_add_id,
         opt::Instruction::OperandList(
             {{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[0]}},
@@ -1010,7 +1011,7 @@
       fuzzerutil::UpdateModuleIdBound(ir_context, float_add_id);
       float_add_ids.push_back(float_add_id);
       linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
-          ir_context, SpvOpFAdd, linear_algebra_instruction->type_id(),
+          ir_context, spv::Op::OpFAdd, linear_algebra_instruction->type_id(),
           float_add_id,
           opt::Instruction::OperandList(
               {{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[i]}},
@@ -1019,7 +1020,7 @@
 
     // The last OpFAdd instruction is got by changing some of the OpDot
     // instruction attributes.
-    linear_algebra_instruction->SetOpcode(SpvOpFAdd);
+    linear_algebra_instruction->SetOpcode(spv::Op::OpFAdd);
     linear_algebra_instruction->SetInOperand(
         0, {float_multiplication_ids[float_multiplication_ids.size() - 1]});
     linear_algebra_instruction->SetInOperand(
diff --git a/source/fuzz/transformation_replace_load_store_with_copy_memory.cpp b/source/fuzz/transformation_replace_load_store_with_copy_memory.cpp
index e75337f..36610f2 100644
--- a/source/fuzz/transformation_replace_load_store_with_copy_memory.cpp
+++ b/source/fuzz/transformation_replace_load_store_with_copy_memory.cpp
@@ -48,14 +48,14 @@
   // The OpLoad instruction must be defined.
   auto load_instruction =
       FindInstruction(message_.load_instruction_descriptor(), ir_context);
-  if (!load_instruction || load_instruction->opcode() != SpvOpLoad) {
+  if (!load_instruction || load_instruction->opcode() != spv::Op::OpLoad) {
     return false;
   }
 
   // The OpStore instruction must be defined.
   auto store_instruction =
       FindInstruction(message_.store_instruction_descriptor(), ir_context);
-  if (!store_instruction || store_instruction->opcode() != SpvOpStore) {
+  if (!store_instruction || store_instruction->opcode() != spv::Op::OpStore) {
     return false;
   }
 
@@ -69,7 +69,7 @@
   // Get storage class of the variable pointed by the source operand in OpLoad.
   opt::Instruction* source_id = ir_context->get_def_use_mgr()->GetDef(
       load_instruction->GetSingleWordOperand(2));
-  SpvStorageClass storage_class = fuzzerutil::GetStorageClassFromPointerType(
+  spv::StorageClass storage_class = fuzzerutil::GetStorageClassFromPointerType(
       ir_context, source_id->type_id());
 
   // Iterate over all instructions between |load_instruction| and
@@ -99,11 +99,11 @@
   // OpLoad and OpStore instructions must be defined.
   auto load_instruction =
       FindInstruction(message_.load_instruction_descriptor(), ir_context);
-  assert(load_instruction && load_instruction->opcode() == SpvOpLoad &&
+  assert(load_instruction && load_instruction->opcode() == spv::Op::OpLoad &&
          "The required OpLoad instruction must be defined.");
   auto store_instruction =
       FindInstruction(message_.store_instruction_descriptor(), ir_context);
-  assert(store_instruction && store_instruction->opcode() == SpvOpStore &&
+  assert(store_instruction && store_instruction->opcode() == spv::Op::OpStore &&
          "The required OpStore instruction must be defined.");
 
   // Intermediate values of the OpLoad and the OpStore must match.
@@ -121,7 +121,7 @@
 
   // Insert the OpCopyMemory instruction before the OpStore instruction.
   store_instruction->InsertBefore(MakeUnique<opt::Instruction>(
-      ir_context, SpvOpCopyMemory, 0, 0,
+      ir_context, spv::Op::OpCopyMemory, 0, 0,
       opt::Instruction::OperandList(
           {{SPV_OPERAND_TYPE_ID, {target_variable_id}},
            {SPV_OPERAND_TYPE_ID, {source_variable_id}}})));
@@ -133,14 +133,14 @@
 }
 
 bool TransformationReplaceLoadStoreWithCopyMemory::IsMemoryWritingOpCode(
-    SpvOp op_code) {
+    spv::Op op_code) {
   if (spvOpcodeIsAtomicOp(op_code)) {
-    return op_code != SpvOpAtomicLoad;
+    return op_code != spv::Op::OpAtomicLoad;
   }
   switch (op_code) {
-    case SpvOpStore:
-    case SpvOpCopyMemory:
-    case SpvOpCopyMemorySized:
+    case spv::Op::OpStore:
+    case spv::Op::OpCopyMemory:
+    case spv::Op::OpCopyMemorySized:
       return true;
     default:
       return false;
@@ -148,10 +148,10 @@
 }
 
 bool TransformationReplaceLoadStoreWithCopyMemory::IsMemoryBarrierOpCode(
-    SpvOp op_code) {
+    spv::Op op_code) {
   switch (op_code) {
-    case SpvOpMemoryBarrier:
-    case SpvOpMemoryNamedBarrier:
+    case spv::Op::OpMemoryBarrier:
+    case spv::Op::OpMemoryNamedBarrier:
       return true;
     default:
       return false;
@@ -159,13 +159,13 @@
 }
 
 bool TransformationReplaceLoadStoreWithCopyMemory::
-    IsStorageClassSafeAcrossMemoryBarriers(SpvStorageClass storage_class) {
+    IsStorageClassSafeAcrossMemoryBarriers(spv::StorageClass storage_class) {
   switch (storage_class) {
-    case SpvStorageClassUniformConstant:
-    case SpvStorageClassInput:
-    case SpvStorageClassUniform:
-    case SpvStorageClassPrivate:
-    case SpvStorageClassFunction:
+    case spv::StorageClass::UniformConstant:
+    case spv::StorageClass::Input:
+    case spv::StorageClass::Uniform:
+    case spv::StorageClass::Private:
+    case spv::StorageClass::Function:
       return true;
     default:
       return false;
diff --git a/source/fuzz/transformation_replace_load_store_with_copy_memory.h b/source/fuzz/transformation_replace_load_store_with_copy_memory.h
index bb4d27e..43c754e 100644
--- a/source/fuzz/transformation_replace_load_store_with_copy_memory.h
+++ b/source/fuzz/transformation_replace_load_store_with_copy_memory.h
@@ -52,17 +52,17 @@
 
   // Checks if the instruction that has an |op_code| might write to
   // the source operand of the OpLoad instruction.
-  static bool IsMemoryWritingOpCode(SpvOp op_code);
+  static bool IsMemoryWritingOpCode(spv::Op op_code);
 
   // Checks if the instruction that has an |op_code| is a memory barrier that
   // could interfere with the source operand of the OpLoad instruction
-  static bool IsMemoryBarrierOpCode(SpvOp op_code);
+  static bool IsMemoryBarrierOpCode(spv::Op op_code);
 
   // Checks if the |storage_class| of the source operand of the OpLoad
   // instruction implies that this variable cannot change (due to other threads)
   // across memory barriers.
   static bool IsStorageClassSafeAcrossMemoryBarriers(
-      SpvStorageClass storage_class);
+      spv::StorageClass storage_class);
 
   std::unordered_set<uint32_t> GetFreshIds() const override;
 
diff --git a/source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.cpp b/source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.cpp
index 84ca1ab..eeda68d 100644
--- a/source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.cpp
+++ b/source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.cpp
@@ -38,7 +38,7 @@
     const TransformationContext& transformation_context) const {
   // |opphi_id| must be the id of an OpPhi instruction.
   auto opphi_def = ir_context->get_def_use_mgr()->GetDef(message_.opphi_id());
-  if (!opphi_def || opphi_def->opcode() != SpvOpPhi) {
+  if (!opphi_def || opphi_def->opcode() != spv::Op::OpPhi) {
     return false;
   }
 
diff --git a/source/fuzz/transformation_replace_opselect_with_conditional_branch.cpp b/source/fuzz/transformation_replace_opselect_with_conditional_branch.cpp
index c0e6e44..b2010a9 100644
--- a/source/fuzz/transformation_replace_opselect_with_conditional_branch.cpp
+++ b/source/fuzz/transformation_replace_opselect_with_conditional_branch.cpp
@@ -50,7 +50,7 @@
       ir_context->get_def_use_mgr()->GetDef(message_.select_id());
 
   // The instruction must exist and it must be an OpSelect instruction.
-  if (!instruction || instruction->opcode() != SpvOpSelect) {
+  if (!instruction || instruction->opcode() != spv::Op::OpSelect) {
     return false;
   }
 
@@ -90,7 +90,7 @@
   // The predecessor must not be the header of a construct and it must end with
   // OpBranch.
   if (predecessor->GetMergeInst() != nullptr ||
-      predecessor->terminator()->opcode() != SpvOpBranch) {
+      predecessor->terminator()->opcode() != spv::Op::OpBranch) {
     return false;
   }
 
@@ -115,13 +115,14 @@
       fuzzerutil::UpdateModuleIdBound(ir_context, id);
 
       // Create the new block.
-      auto new_block = MakeUnique<opt::BasicBlock>(MakeUnique<opt::Instruction>(
-          ir_context, SpvOpLabel, 0, id, opt::Instruction::OperandList{}));
+      auto new_block = MakeUnique<opt::BasicBlock>(
+          MakeUnique<opt::Instruction>(ir_context, spv::Op::OpLabel, 0, id,
+                                       opt::Instruction::OperandList{}));
 
       // Add an unconditional branch from the new block to the instruction
       // block.
       new_block->AddInstruction(MakeUnique<opt::Instruction>(
-          ir_context, SpvOpBranch, 0, 0,
+          ir_context, spv::Op::OpBranch, 0, 0,
           opt::Instruction::OperandList{{SPV_OPERAND_TYPE_ID, {block->id()}}}));
 
       // Insert the new block right after the predecessor of the instruction
@@ -136,10 +137,11 @@
   // Add an OpSelectionMerge instruction to the predecessor block, where the
   // merge block is the instruction block.
   predecessor->AddInstruction(MakeUnique<opt::Instruction>(
-      ir_context, SpvOpSelectionMerge, 0, 0,
-      opt::Instruction::OperandList{{SPV_OPERAND_TYPE_ID, {block->id()}},
-                                    {SPV_OPERAND_TYPE_SELECTION_CONTROL,
-                                     {SpvSelectionControlMaskNone}}}));
+      ir_context, spv::Op::OpSelectionMerge, 0, 0,
+      opt::Instruction::OperandList{
+          {SPV_OPERAND_TYPE_ID, {block->id()}},
+          {SPV_OPERAND_TYPE_SELECTION_CONTROL,
+           {uint32_t(spv::SelectionControlMask::MaskNone)}}}));
 
   // |if_block| will be the true block, if it has been created, the instruction
   // block otherwise.
@@ -158,7 +160,7 @@
   // Add a conditional branching instruction to the predecessor, branching to
   // |if_block| if the condition is true and to |if_false| otherwise.
   predecessor->AddInstruction(MakeUnique<opt::Instruction>(
-      ir_context, SpvOpBranchConditional, 0, 0,
+      ir_context, spv::Op::OpBranchConditional, 0, 0,
       opt::Instruction::OperandList{
           {SPV_OPERAND_TYPE_ID, {instruction->GetSingleWordInOperand(0)}},
           {SPV_OPERAND_TYPE_ID, {if_block}},
@@ -177,7 +179,7 @@
   // Replace the OpSelect instruction in the merge block with an OpPhi.
   // This:          OpSelect %type %cond %if %else
   // will become:   OpPhi %type %if %if_pred %else %else_pred
-  instruction->SetOpcode(SpvOpPhi);
+  instruction->SetOpcode(spv::Op::OpPhi);
   std::vector<opt::Operand> operands;
 
   operands.emplace_back(instruction->GetInOperand(1));
diff --git a/source/fuzz/transformation_replace_parameter_with_global.cpp b/source/fuzz/transformation_replace_parameter_with_global.cpp
index caf6716..e80dfe9 100644
--- a/source/fuzz/transformation_replace_parameter_with_global.cpp
+++ b/source/fuzz/transformation_replace_parameter_with_global.cpp
@@ -41,7 +41,7 @@
   // Check that |parameter_id| is valid.
   const auto* param_inst =
       ir_context->get_def_use_mgr()->GetDef(message_.parameter_id());
-  if (!param_inst || param_inst->opcode() != SpvOpFunctionParameter) {
+  if (!param_inst || param_inst->opcode() != spv::Op::OpFunctionParameter) {
     return false;
   }
 
@@ -69,7 +69,7 @@
 
   // Check that pointer type for the global variable exists in the module.
   if (!fuzzerutil::MaybeGetPointerType(ir_context, param_inst->type_id(),
-                                       SpvStorageClassPrivate)) {
+                                       spv::StorageClass::Private)) {
     return false;
   }
 
@@ -91,8 +91,8 @@
   fuzzerutil::AddGlobalVariable(
       ir_context, message_.global_variable_fresh_id(),
       fuzzerutil::MaybeGetPointerType(ir_context, param_inst->type_id(),
-                                      SpvStorageClassPrivate),
-      SpvStorageClassPrivate,
+                                      spv::StorageClass::Private),
+      spv::StorageClass::Private,
       fuzzerutil::MaybeGetZeroConstant(ir_context, *transformation_context,
                                        param_inst->type_id(), false));
 
@@ -103,16 +103,17 @@
   // Insert an OpLoad instruction right after OpVariable instructions.
   auto it = function->begin()->begin();
   while (it != function->begin()->end() &&
-         !fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad, it)) {
+         !fuzzerutil::CanInsertOpcodeBeforeInstruction(spv::Op::OpLoad, it)) {
     ++it;
   }
 
-  assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad, it) &&
+  assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(spv::Op::OpLoad, it) &&
          "Can't insert OpLoad or OpCopyMemory into the first basic block of "
          "the function");
 
   it.InsertBefore(MakeUnique<opt::Instruction>(
-      ir_context, SpvOpLoad, param_inst->type_id(), param_inst->result_id(),
+      ir_context, spv::Op::OpLoad, param_inst->type_id(),
+      param_inst->result_id(),
       opt::Instruction::OperandList{
           {SPV_OPERAND_TYPE_ID, {message_.global_variable_fresh_id()}}}));
 
@@ -132,13 +133,14 @@
 
   // Update all relevant OpFunctionCall instructions.
   for (auto* inst : fuzzerutil::GetCallers(ir_context, function->result_id())) {
-    assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpStore, inst) &&
-           "Can't insert OpStore right before the function call");
+    assert(
+        fuzzerutil::CanInsertOpcodeBeforeInstruction(spv::Op::OpStore, inst) &&
+        "Can't insert OpStore right before the function call");
 
     // Insert an OpStore before the OpFunctionCall. +1 since the first
     // operand of OpFunctionCall is an id of the function.
     inst->InsertBefore(MakeUnique<opt::Instruction>(
-        ir_context, SpvOpStore, 0, 0,
+        ir_context, spv::Op::OpStore, 0, 0,
         opt::Instruction::OperandList{
             {SPV_OPERAND_TYPE_ID, {message_.global_variable_fresh_id()}},
             {SPV_OPERAND_TYPE_ID,
diff --git a/source/fuzz/transformation_replace_params_with_struct.cpp b/source/fuzz/transformation_replace_params_with_struct.cpp
index 13eeccb..c8af14a 100644
--- a/source/fuzz/transformation_replace_params_with_struct.cpp
+++ b/source/fuzz/transformation_replace_params_with_struct.cpp
@@ -142,7 +142,7 @@
 
   // Add new parameter to the function.
   function->AddParameter(MakeUnique<opt::Instruction>(
-      ir_context, SpvOpFunctionParameter, struct_type_id,
+      ir_context, spv::Op::OpFunctionParameter, struct_type_id,
       message_.fresh_parameter_id(), opt::Instruction::OperandList()));
 
   fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_parameter_id());
@@ -182,8 +182,8 @@
     auto fresh_composite_id =
         caller_id_to_fresh_composite_id.at(inst->result_id());
     inst->InsertBefore(MakeUnique<opt::Instruction>(
-        ir_context, SpvOpCompositeConstruct, struct_type_id, fresh_composite_id,
-        std::move(composite_components)));
+        ir_context, spv::Op::OpCompositeConstruct, struct_type_id,
+        fresh_composite_id, std::move(composite_components)));
 
     // Add a new operand to the OpFunctionCall instruction.
     inst->AddOperand({SPV_OPERAND_TYPE_ID, {fresh_composite_id}});
@@ -200,19 +200,19 @@
     // Skip all OpVariable instructions.
     auto iter = function->begin()->begin();
     while (iter != function->begin()->end() &&
-           !fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCompositeExtract,
-                                                         iter)) {
+           !fuzzerutil::CanInsertOpcodeBeforeInstruction(
+               spv::Op::OpCompositeExtract, iter)) {
       ++iter;
     }
 
-    assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCompositeExtract,
-                                                        iter) &&
+    assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(
+               spv::Op::OpCompositeExtract, iter) &&
            "Can't extract parameter's value from the structure");
 
     // Insert OpCompositeExtract instructions to unpack parameters' values from
     // the struct type.
     iter.InsertBefore(MakeUnique<opt::Instruction>(
-        ir_context, SpvOpCompositeExtract, param_inst->type_id(),
+        ir_context, spv::Op::OpCompositeExtract, param_inst->type_id(),
         param_inst->result_id(),
         opt::Instruction::OperandList{
             {SPV_OPERAND_TYPE_ID, {message_.fresh_parameter_id()}},
diff --git a/source/fuzz/transformation_set_function_control.cpp b/source/fuzz/transformation_set_function_control.cpp
index 02a8c9f..ef48171 100644
--- a/source/fuzz/transformation_set_function_control.cpp
+++ b/source/fuzz/transformation_set_function_control.cpp
@@ -40,9 +40,9 @@
 
   // Check (via an assertion) that function control mask doesn't have any bad
   // bits set.
-  uint32_t acceptable_function_control_bits =
-      SpvFunctionControlInlineMask | SpvFunctionControlDontInlineMask |
-      SpvFunctionControlPureMask | SpvFunctionControlConstMask;
+  uint32_t acceptable_function_control_bits = uint32_t(
+      spv::FunctionControlMask::Inline | spv::FunctionControlMask::DontInline |
+      spv::FunctionControlMask::Pure | spv::FunctionControlMask::Const);
   // The following is to keep release-mode compilers happy as this variable is
   // only used in an assertion.
   (void)(acceptable_function_control_bits);
@@ -51,17 +51,19 @@
 
   // Check (via an assertion) that function control mask does not have both
   // Inline and DontInline bits set.
-  assert(!((message_.function_control() & SpvFunctionControlInlineMask) &&
-           (message_.function_control() & SpvFunctionControlDontInlineMask)) &&
+  assert(!((message_.function_control() &
+            (uint32_t)spv::FunctionControlMask::Inline) &&
+           (message_.function_control() &
+            (uint32_t)spv::FunctionControlMask::DontInline)) &&
          "It is not OK to set both the 'Inline' and 'DontInline' bits of a "
          "function control mask");
 
   // Check that Const and Pure are only present if they were present on the
   // original function
   for (auto mask_bit :
-       {SpvFunctionControlPureMask, SpvFunctionControlConstMask}) {
-    if ((message_.function_control() & mask_bit) &&
-        !(existing_function_control_mask & mask_bit)) {
+       {spv::FunctionControlMask::Pure, spv::FunctionControlMask::Const}) {
+    if ((message_.function_control() & uint32_t(mask_bit)) &&
+        !(existing_function_control_mask & uint32_t(mask_bit))) {
       return false;
     }
   }
diff --git a/source/fuzz/transformation_set_loop_control.cpp b/source/fuzz/transformation_set_loop_control.cpp
index 1449960..dc9ee7c 100644
--- a/source/fuzz/transformation_set_loop_control.cpp
+++ b/source/fuzz/transformation_set_loop_control.cpp
@@ -38,18 +38,20 @@
     return false;
   }
   auto merge_inst = block->GetMergeInst();
-  if (!merge_inst || merge_inst->opcode() != SpvOpLoopMerge) {
+  if (!merge_inst || merge_inst->opcode() != spv::Op::OpLoopMerge) {
     return false;
   }
 
   // We assert that the transformation does not try to set any meaningless bits
   // of the loop control mask.
-  uint32_t all_loop_control_mask_bits_set =
-      SpvLoopControlUnrollMask | SpvLoopControlDontUnrollMask |
-      SpvLoopControlDependencyInfiniteMask |
-      SpvLoopControlDependencyLengthMask | SpvLoopControlMinIterationsMask |
-      SpvLoopControlMaxIterationsMask | SpvLoopControlIterationMultipleMask |
-      SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask;
+  uint32_t all_loop_control_mask_bits_set = uint32_t(
+      spv::LoopControlMask::Unroll | spv::LoopControlMask::DontUnroll |
+      spv::LoopControlMask::DependencyInfinite |
+      spv::LoopControlMask::DependencyLength |
+      spv::LoopControlMask::MinIterations |
+      spv::LoopControlMask::MaxIterations |
+      spv::LoopControlMask::IterationMultiple |
+      spv::LoopControlMask::PeelCount | spv::LoopControlMask::PartialCount);
 
   // The variable is only used in an assertion; the following keeps release-mode
   // compilers happy.
@@ -65,10 +67,11 @@
 
   // Check that there is no attempt to set one of the loop controls that
   // requires guarantees to hold.
-  for (SpvLoopControlMask mask :
-       {SpvLoopControlDependencyInfiniteMask,
-        SpvLoopControlDependencyLengthMask, SpvLoopControlMinIterationsMask,
-        SpvLoopControlMaxIterationsMask, SpvLoopControlIterationMultipleMask}) {
+  for (spv::LoopControlMask mask : {spv::LoopControlMask::DependencyInfinite,
+                                    spv::LoopControlMask::DependencyLength,
+                                    spv::LoopControlMask::MinIterations,
+                                    spv::LoopControlMask::MaxIterations,
+                                    spv::LoopControlMask::IterationMultiple}) {
     // We have a problem if this loop control bit was not set in the original
     // loop control mask but is set by the transformation.
     if (LoopControlBitIsAddedByTransformation(mask,
@@ -78,33 +81,36 @@
   }
 
   // Check that PeelCount and PartialCount are supported if used.
-  if ((message_.loop_control() & SpvLoopControlPeelCountMask) &&
+  if ((message_.loop_control() & uint32_t(spv::LoopControlMask::PeelCount)) &&
       !PeelCountIsSupported(ir_context)) {
     return false;
   }
 
-  if ((message_.loop_control() & SpvLoopControlPartialCountMask) &&
+  if ((message_.loop_control() &
+       uint32_t(spv::LoopControlMask::PartialCount)) &&
       !PartialCountIsSupported(ir_context)) {
     return false;
   }
 
   if (message_.peel_count() > 0 &&
-      !(message_.loop_control() & SpvLoopControlPeelCountMask)) {
+      !(message_.loop_control() & uint32_t(spv::LoopControlMask::PeelCount))) {
     // Peel count provided, but peel count mask bit not set.
     return false;
   }
 
   if (message_.partial_count() > 0 &&
-      !(message_.loop_control() & SpvLoopControlPartialCountMask)) {
+      !(message_.loop_control() &
+        uint32_t(spv::LoopControlMask::PartialCount))) {
     // Partial count provided, but partial count mask bit not set.
     return false;
   }
 
   // We must not set both 'don't unroll' and one of 'peel count' or 'partial
   // count'.
-  return !((message_.loop_control() & SpvLoopControlDontUnrollMask) &&
-           (message_.loop_control() &
-            (SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask)));
+  return !(
+      (message_.loop_control() & uint32_t(spv::LoopControlMask::DontUnroll)) &&
+      (message_.loop_control() & uint32_t(spv::LoopControlMask::PeelCount |
+                                          spv::LoopControlMask::PartialCount)));
 }
 
 void TransformationSetLoopControl::Apply(
@@ -133,13 +139,14 @@
 
   uint32_t literal_index = 0;  // Indexes into the literals from the original
   // instruction.
-  for (SpvLoopControlMask mask :
-       {SpvLoopControlDependencyLengthMask, SpvLoopControlMinIterationsMask,
-        SpvLoopControlMaxIterationsMask, SpvLoopControlIterationMultipleMask}) {
+  for (spv::LoopControlMask mask : {spv::LoopControlMask::DependencyLength,
+                                    spv::LoopControlMask::MinIterations,
+                                    spv::LoopControlMask::MaxIterations,
+                                    spv::LoopControlMask::IterationMultiple}) {
     // Check whether the bit was set in the original loop control mask.
-    if (existing_loop_control_mask & mask) {
+    if (existing_loop_control_mask & uint32_t(mask)) {
       // Check whether the bit is set in the new loop control mask.
-      if (message_.loop_control() & mask) {
+      if (message_.loop_control() & uint32_t(mask)) {
         // Add the associated literal to our sequence of replacement operands.
         new_operands.push_back(
             {SPV_OPERAND_TYPE_LITERAL_INTEGER,
@@ -154,13 +161,13 @@
 
   // If PeelCount is set in the new mask, |message_.peel_count| provides the
   // associated peel count.
-  if (message_.loop_control() & SpvLoopControlPeelCountMask) {
+  if (message_.loop_control() & uint32_t(spv::LoopControlMask::PeelCount)) {
     new_operands.push_back(
         {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.peel_count()}});
   }
 
   // Similar, but for PartialCount.
-  if (message_.loop_control() & SpvLoopControlPartialCountMask) {
+  if (message_.loop_control() & uint32_t(spv::LoopControlMask::PartialCount)) {
     new_operands.push_back(
         {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.partial_count()}});
   }
@@ -177,10 +184,11 @@
 }
 
 bool TransformationSetLoopControl::LoopControlBitIsAddedByTransformation(
-    SpvLoopControlMask loop_control_single_bit_mask,
+    spv::LoopControlMask loop_control_single_bit_mask,
     uint32_t existing_loop_control_mask) const {
-  return !(loop_control_single_bit_mask & existing_loop_control_mask) &&
-         (loop_control_single_bit_mask & message_.loop_control());
+  return !(uint32_t(loop_control_single_bit_mask) &
+           existing_loop_control_mask) &&
+         (uint32_t(loop_control_single_bit_mask) & message_.loop_control());
 }
 
 bool TransformationSetLoopControl::PartialCountIsSupported(
diff --git a/source/fuzz/transformation_set_loop_control.h b/source/fuzz/transformation_set_loop_control.h
index bc17c8a..d57447b 100644
--- a/source/fuzz/transformation_set_loop_control.h
+++ b/source/fuzz/transformation_set_loop_control.h
@@ -71,7 +71,7 @@
   // Returns true if and only if |loop_single_bit_mask| is *not* set in
   // |existing_loop_control| but *is* set in |message_.loop_control|.
   bool LoopControlBitIsAddedByTransformation(
-      SpvLoopControlMask loop_control_single_bit_mask,
+      spv::LoopControlMask loop_control_single_bit_mask,
       uint32_t existing_loop_control_mask) const;
 
   protobufs::TransformationSetLoopControl message_;
diff --git a/source/fuzz/transformation_set_memory_operands_mask.cpp b/source/fuzz/transformation_set_memory_operands_mask.cpp
index 5a986ad..16d591a 100644
--- a/source/fuzz/transformation_set_memory_operands_mask.cpp
+++ b/source/fuzz/transformation_set_memory_operands_mask.cpp
@@ -48,10 +48,13 @@
     // which they were applied during fuzzing, hence why these are assertions
     // rather than applicability checks.
     assert(message_.memory_operands_mask_index() == 1);
-    assert(message_.memory_access_instruction().target_instruction_opcode() ==
-               SpvOpCopyMemory ||
-           message_.memory_access_instruction().target_instruction_opcode() ==
-               SpvOpCopyMemorySized);
+    assert(
+        spv::Op(
+            message_.memory_access_instruction().target_instruction_opcode()) ==
+            spv::Op::OpCopyMemory ||
+        spv::Op(
+            message_.memory_access_instruction().target_instruction_opcode()) ==
+            spv::Op::OpCopyMemorySized);
     assert(MultipleMemoryOperandMasksAreSupported(ir_context) &&
            "Multiple memory operand masks are not supported for this SPIR-V "
            "version.");
@@ -73,12 +76,12 @@
   uint32_t original_mask =
       original_mask_in_operand_index < instruction->NumInOperands()
           ? instruction->GetSingleWordInOperand(original_mask_in_operand_index)
-          : static_cast<uint32_t>(SpvMemoryAccessMaskNone);
+          : static_cast<uint32_t>(spv::MemoryAccessMask::MaskNone);
   uint32_t new_mask = message_.memory_operands_mask();
 
   // Volatile must not be removed
-  if ((original_mask & SpvMemoryAccessVolatileMask) &&
-      !(new_mask & SpvMemoryAccessVolatileMask)) {
+  if ((original_mask & uint32_t(spv::MemoryAccessMask::Volatile)) &&
+      !(new_mask & uint32_t(spv::MemoryAccessMask::Volatile))) {
     return false;
   }
 
@@ -87,10 +90,10 @@
   // their Volatile and Nontemporal flags to the same value (this works
   // because valid manipulation of Volatile is checked above, and the manner
   // in which Nontemporal is manipulated does not matter).
-  return (original_mask | SpvMemoryAccessVolatileMask |
-          SpvMemoryAccessNontemporalMask) ==
-         (new_mask | SpvMemoryAccessVolatileMask |
-          SpvMemoryAccessNontemporalMask);
+  return (original_mask | uint32_t(spv::MemoryAccessMask::Volatile) |
+          uint32_t(spv::MemoryAccessMask::Nontemporal)) ==
+         (new_mask | uint32_t(spv::MemoryAccessMask::Volatile) |
+          uint32_t(spv::MemoryAccessMask::Nontemporal));
 }
 
 void TransformationSetMemoryOperandsMask::Apply(
@@ -106,8 +109,8 @@
     if (message_.memory_operands_mask_index() == 1 &&
         GetInOperandIndexForMask(*instruction, 0) >=
             instruction->NumInOperands()) {
-      instruction->AddOperand(
-          {SPV_OPERAND_TYPE_MEMORY_ACCESS, {SpvMemoryAccessMaskNone}});
+      instruction->AddOperand({SPV_OPERAND_TYPE_MEMORY_ACCESS,
+                               {uint32_t(spv::MemoryAccessMask::MaskNone)}});
     }
 
     instruction->AddOperand(
@@ -129,10 +132,10 @@
 bool TransformationSetMemoryOperandsMask::IsMemoryAccess(
     const opt::Instruction& instruction) {
   switch (instruction.opcode()) {
-    case SpvOpLoad:
-    case SpvOpStore:
-    case SpvOpCopyMemory:
-    case SpvOpCopyMemorySized:
+    case spv::Op::OpLoad:
+    case spv::Op::OpStore:
+    case spv::Op::OpCopyMemory:
+    case spv::Op::OpCopyMemorySized:
       return true;
     default:
       return false;
@@ -145,16 +148,16 @@
   // for the instruction.
   uint32_t first_mask_in_operand_index = 0;
   switch (instruction.opcode()) {
-    case SpvOpLoad:
+    case spv::Op::OpLoad:
       first_mask_in_operand_index = kOpLoadMemoryOperandsMaskIndex;
       break;
-    case SpvOpStore:
+    case spv::Op::OpStore:
       first_mask_in_operand_index = kOpStoreMemoryOperandsMaskIndex;
       break;
-    case SpvOpCopyMemory:
+    case spv::Op::OpCopyMemory:
       first_mask_in_operand_index = kOpCopyMemoryFirstMemoryOperandsMaskIndex;
       break;
-    case SpvOpCopyMemorySized:
+    case spv::Op::OpCopyMemorySized:
       first_mask_in_operand_index =
           kOpCopyMemorySizedFirstMemoryOperandsMaskIndex;
       break;
@@ -192,12 +195,12 @@
   // Consider each bit that might have an associated extra input operand, and
   // count how many there are expected to be.
   uint32_t first_mask_extra_operand_count = 0;
-  for (auto mask_bit :
-       {SpvMemoryAccessAlignedMask, SpvMemoryAccessMakePointerAvailableMask,
-        SpvMemoryAccessMakePointerAvailableKHRMask,
-        SpvMemoryAccessMakePointerVisibleMask,
-        SpvMemoryAccessMakePointerVisibleKHRMask}) {
-    if (first_mask & mask_bit) {
+  for (auto mask_bit : {spv::MemoryAccessMask::Aligned,
+                        spv::MemoryAccessMask::MakePointerAvailable,
+                        spv::MemoryAccessMask::MakePointerAvailableKHR,
+                        spv::MemoryAccessMask::MakePointerVisible,
+                        spv::MemoryAccessMask::MakePointerVisibleKHR}) {
+    if (first_mask & uint32_t(mask_bit)) {
       first_mask_extra_operand_count++;
     }
   }
diff --git a/source/fuzz/transformation_set_selection_control.cpp b/source/fuzz/transformation_set_selection_control.cpp
index 6dddbdf..c6f68d9 100644
--- a/source/fuzz/transformation_set_selection_control.cpp
+++ b/source/fuzz/transformation_set_selection_control.cpp
@@ -29,14 +29,17 @@
 
 bool TransformationSetSelectionControl::IsApplicable(
     opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
-  assert((message_.selection_control() == SpvSelectionControlMaskNone ||
-          message_.selection_control() == SpvSelectionControlFlattenMask ||
-          message_.selection_control() == SpvSelectionControlDontFlattenMask) &&
+  assert((spv::SelectionControlMask(message_.selection_control()) ==
+              spv::SelectionControlMask::MaskNone ||
+          spv::SelectionControlMask(message_.selection_control()) ==
+              spv::SelectionControlMask::Flatten ||
+          spv::SelectionControlMask(message_.selection_control()) ==
+              spv::SelectionControlMask::DontFlatten) &&
          "Selection control should never be set to something other than "
          "'None', 'Flatten' or 'DontFlatten'");
   if (auto block = ir_context->get_instr_block(message_.block_id())) {
     if (auto merge_inst = block->GetMergeInst()) {
-      return merge_inst->opcode() == SpvOpSelectionMerge;
+      return merge_inst->opcode() == spv::Op::OpSelectionMerge;
     }
   }
   // Either the block did not exit, or did not end with OpSelectionMerge.
diff --git a/source/fuzz/transformation_split_block.cpp b/source/fuzz/transformation_split_block.cpp
index e15dffa..959bedc 100644
--- a/source/fuzz/transformation_split_block.cpp
+++ b/source/fuzz/transformation_split_block.cpp
@@ -64,19 +64,19 @@
          " block split point exists.");
 
   if (split_before->PreviousNode() &&
-      split_before->PreviousNode()->opcode() == SpvOpSelectionMerge) {
+      split_before->PreviousNode()->opcode() == spv::Op::OpSelectionMerge) {
     // We cannot split directly after a selection merge: this would separate
     // the merge from its associated branch or switch operation.
     return false;
   }
-  if (split_before->opcode() == SpvOpVariable) {
+  if (split_before->opcode() == spv::Op::OpVariable) {
     // We cannot split directly after a variable; variables in a function
     // must be contiguous in the entry block.
     return false;
   }
   // We cannot split before an OpPhi unless the OpPhi has exactly one
   // associated incoming edge.
-  if (split_before->opcode() == SpvOpPhi &&
+  if (split_before->opcode() == spv::Op::OpPhi &&
       split_before->NumInOperands() != 2) {
     return false;
   }
@@ -110,7 +110,7 @@
   // The split does not automatically add a branch between the two parts of
   // the original block, so we add one.
   auto branch_instruction = MakeUnique<opt::Instruction>(
-      ir_context, SpvOpBranch, 0, 0,
+      ir_context, spv::Op::OpBranch, 0, 0,
       std::initializer_list<opt::Operand>{opt::Operand(
           spv_operand_type_t::SPV_OPERAND_TYPE_ID, {message_.fresh_id()})});
   auto branch_instruction_ptr = branch_instruction.get();
diff --git a/source/fuzz/transformation_store.cpp b/source/fuzz/transformation_store.cpp
index c00cd34..6ba1c55 100644
--- a/source/fuzz/transformation_store.cpp
+++ b/source/fuzz/transformation_store.cpp
@@ -48,7 +48,7 @@
   // The pointer type must indeed be a pointer.
   auto pointer_type = ir_context->get_def_use_mgr()->GetDef(pointer->type_id());
   assert(pointer_type && "Type id must be defined.");
-  if (pointer_type->opcode() != SpvOpTypePointer) {
+  if (pointer_type->opcode() != spv::Op::OpTypePointer) {
     return false;
   }
 
@@ -59,8 +59,8 @@
 
   // We do not want to allow storing to null or undefined pointers.
   switch (pointer->opcode()) {
-    case SpvOpConstantNull:
-    case SpvOpUndef:
+    case spv::Op::OpConstantNull:
+    case spv::Op::OpUndef:
       return false;
     default:
       break;
@@ -75,11 +75,11 @@
   }
   // ... and it must be legitimate to insert a store before it.
   if (!message_.is_atomic() && !fuzzerutil::CanInsertOpcodeBeforeInstruction(
-                                   SpvOpStore, insert_before)) {
+                                   spv::Op::OpStore, insert_before)) {
     return false;
   }
   if (message_.is_atomic() && !fuzzerutil::CanInsertOpcodeBeforeInstruction(
-                                  SpvOpAtomicStore, insert_before)) {
+                                  spv::Op::OpAtomicStore, insert_before)) {
     return false;
   }
 
@@ -125,10 +125,10 @@
     }
     // The memory scope and memory semantics instructions must have the
     // 'OpConstant' opcode.
-    if (memory_scope_instruction->opcode() != SpvOpConstant) {
+    if (memory_scope_instruction->opcode() != spv::Op::OpConstant) {
       return false;
     }
-    if (memory_semantics_instruction->opcode() != SpvOpConstant) {
+    if (memory_semantics_instruction->opcode() != spv::Op::OpConstant) {
       return false;
     }
     // The memory scope and memory semantics need to be available before
@@ -145,12 +145,12 @@
     // operand type with signedness does not matters.
     if (ir_context->get_def_use_mgr()
             ->GetDef(memory_scope_instruction->type_id())
-            ->opcode() != SpvOpTypeInt) {
+            ->opcode() != spv::Op::OpTypeInt) {
       return false;
     }
     if (ir_context->get_def_use_mgr()
             ->GetDef(memory_semantics_instruction->type_id())
-            ->opcode() != SpvOpTypeInt) {
+            ->opcode() != spv::Op::OpTypeInt) {
       return false;
     }
 
@@ -172,20 +172,20 @@
       return false;
     }
 
-    // The memory scope constant value must be that of SpvScopeInvocation.
+    // The memory scope constant value must be that of spv::Scope::Invocation.
     auto memory_scope_const_value =
         memory_scope_instruction->GetSingleWordInOperand(0);
-    if (memory_scope_const_value != SpvScopeInvocation) {
+    if (spv::Scope(memory_scope_const_value) != spv::Scope::Invocation) {
       return false;
     }
 
     // The memory semantics constant value must match the storage class of the
     // pointer being loaded from.
-    auto memory_semantics_const_value = static_cast<SpvMemorySemanticsMask>(
+    auto memory_semantics_const_value = static_cast<spv::MemorySemanticsMask>(
         memory_semantics_instruction->GetSingleWordInOperand(0));
     if (memory_semantics_const_value !=
         fuzzerutil::GetMemorySemanticsForStorageClass(
-            static_cast<SpvStorageClass>(
+            static_cast<spv::StorageClass>(
                 pointer_type->GetSingleWordInOperand(0)))) {
       return false;
     }
@@ -203,7 +203,7 @@
     auto insert_before =
         FindInstruction(message_.instruction_to_insert_before(), ir_context);
     auto new_instruction = MakeUnique<opt::Instruction>(
-        ir_context, SpvOpAtomicStore, 0, 0,
+        ir_context, spv::Op::OpAtomicStore, 0, 0,
         opt::Instruction::OperandList(
             {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}},
              {SPV_OPERAND_TYPE_SCOPE_ID, {message_.memory_scope_id()}},
@@ -223,7 +223,7 @@
     auto insert_before =
         FindInstruction(message_.instruction_to_insert_before(), ir_context);
     auto new_instruction = MakeUnique<opt::Instruction>(
-        ir_context, SpvOpStore, 0, 0,
+        ir_context, spv::Op::OpStore, 0, 0,
         opt::Instruction::OperandList(
             {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}},
              {SPV_OPERAND_TYPE_ID, {message_.value_id()}}}));
diff --git a/source/fuzz/transformation_store.h b/source/fuzz/transformation_store.h
index 638713b..5b55ce6 100644
--- a/source/fuzz/transformation_store.h
+++ b/source/fuzz/transformation_store.h
@@ -38,7 +38,7 @@
   // - |message_.is_atomic| must be true if want to work with OpAtomicStore.
   // - If |is_atomic| is true then |message_memory_scope_id| must be the id of
   //   an OpConstant 32 bit integer instruction with the value
-  //   SpvScopeInvocation.
+  //   spv::Scope::Invocation.
   // - If |is_atomic| is true then |message_.memory_semantics_id| must be the id
   //   of an OpConstant 32 bit integer instruction with the values
   //   SpvMemorySemanticsWorkgroupMemoryMask or
diff --git a/source/fuzz/transformation_swap_commutable_operands.cpp b/source/fuzz/transformation_swap_commutable_operands.cpp
index a02e95a..1512144 100644
--- a/source/fuzz/transformation_swap_commutable_operands.cpp
+++ b/source/fuzz/transformation_swap_commutable_operands.cpp
@@ -35,9 +35,9 @@
       FindInstruction(message_.instruction_descriptor(), ir_context);
   if (instruction == nullptr) return false;
 
-  SpvOp opcode = static_cast<SpvOp>(
+  spv::Op opcode = static_cast<spv::Op>(
       message_.instruction_descriptor().target_instruction_opcode());
-  assert(instruction->opcode() == opcode &&
+  assert(spv::Op(instruction->opcode()) == opcode &&
          "The located instruction must have the same opcode as in the "
          "descriptor.");
   return spvOpcodeIsCommutativeBinaryOperator(opcode);
diff --git a/source/fuzz/transformation_swap_conditional_branch_operands.cpp b/source/fuzz/transformation_swap_conditional_branch_operands.cpp
index 340836d..520a6a8 100644
--- a/source/fuzz/transformation_swap_conditional_branch_operands.cpp
+++ b/source/fuzz/transformation_swap_conditional_branch_operands.cpp
@@ -38,7 +38,7 @@
   const auto* inst =
       FindInstruction(message_.instruction_descriptor(), ir_context);
   return fuzzerutil::IsFreshId(ir_context, message_.fresh_id()) && inst &&
-         inst->opcode() == SpvOpBranchConditional;
+         inst->opcode() == spv::Op::OpBranchConditional;
 }
 
 void TransformationSwapConditionalBranchOperands::Apply(
@@ -53,13 +53,15 @@
   // Compute the last instruction in the |block| that allows us to insert
   // OpLogicalNot above it.
   auto iter = fuzzerutil::GetIteratorForInstruction(block, branch_inst);
-  if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLogicalNot, iter)) {
+  if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(spv::Op::OpLogicalNot,
+                                                    iter)) {
     // There might be a merge instruction before OpBranchConditional.
     --iter;
   }
 
-  assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLogicalNot, iter) &&
-         "We should now be able to insert SpvOpLogicalNot before |iter|");
+  assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(spv::Op::OpLogicalNot,
+                                                      iter) &&
+         "We should now be able to insert spv::Op::OpLogicalNot before |iter|");
 
   // Get the instruction whose result is used as a condition for
   // OpBranchConditional.
@@ -70,7 +72,7 @@
   // We are swapping the labels in OpBranchConditional. This means that we must
   // invert the guard as well. We are using OpLogicalNot for that purpose here.
   auto new_instruction = MakeUnique<opt::Instruction>(
-      ir_context, SpvOpLogicalNot, condition_inst->type_id(),
+      ir_context, spv::Op::OpLogicalNot, condition_inst->type_id(),
       message_.fresh_id(),
       opt::Instruction::OperandList{
           {SPV_OPERAND_TYPE_ID, {condition_inst->result_id()}}});
diff --git a/source/fuzz/transformation_swap_function_variables.cpp b/source/fuzz/transformation_swap_function_variables.cpp
index aec32fe..14ba7bb 100644
--- a/source/fuzz/transformation_swap_function_variables.cpp
+++ b/source/fuzz/transformation_swap_function_variables.cpp
@@ -43,8 +43,8 @@
     return false;
   }
   // Both instructions must be variables.
-  if (instruction1->opcode() != SpvOpVariable ||
-      instruction2->opcode() != SpvOpVariable) {
+  if (instruction1->opcode() != spv::Op::OpVariable ||
+      instruction2->opcode() != spv::Op::OpVariable) {
     return false;
   }
 
diff --git a/source/fuzz/transformation_toggle_access_chain_instruction.cpp b/source/fuzz/transformation_toggle_access_chain_instruction.cpp
index 34523fe..ae6ab9a 100644
--- a/source/fuzz/transformation_toggle_access_chain_instruction.cpp
+++ b/source/fuzz/transformation_toggle_access_chain_instruction.cpp
@@ -39,14 +39,15 @@
     return false;
   }
 
-  SpvOp opcode = static_cast<SpvOp>(
+  spv::Op opcode = static_cast<spv::Op>(
       message_.instruction_descriptor().target_instruction_opcode());
 
   assert(instruction->opcode() == opcode &&
          "The located instruction must have the same opcode as in the "
          "descriptor.");
 
-  if (opcode == SpvOpAccessChain || opcode == SpvOpInBoundsAccessChain) {
+  if (opcode == spv::Op::OpAccessChain ||
+      opcode == spv::Op::OpInBoundsAccessChain) {
     return true;
   }
 
@@ -57,15 +58,15 @@
     opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
   auto instruction =
       FindInstruction(message_.instruction_descriptor(), ir_context);
-  SpvOp opcode = instruction->opcode();
+  spv::Op opcode = instruction->opcode();
 
-  if (opcode == SpvOpAccessChain) {
-    instruction->SetOpcode(SpvOpInBoundsAccessChain);
+  if (opcode == spv::Op::OpAccessChain) {
+    instruction->SetOpcode(spv::Op::OpInBoundsAccessChain);
   } else {
-    assert(opcode == SpvOpInBoundsAccessChain &&
+    assert(opcode == spv::Op::OpInBoundsAccessChain &&
            "The located instruction must be an OpInBoundsAccessChain "
            "instruction.");
-    instruction->SetOpcode(SpvOpAccessChain);
+    instruction->SetOpcode(spv::Op::OpAccessChain);
   }
 }
 
diff --git a/source/fuzz/transformation_vector_shuffle.cpp b/source/fuzz/transformation_vector_shuffle.cpp
index 742a2c8..8ba557c 100644
--- a/source/fuzz/transformation_vector_shuffle.cpp
+++ b/source/fuzz/transformation_vector_shuffle.cpp
@@ -108,7 +108,7 @@
   // It must be legitimate to insert an OpVectorShuffle before the identified
   // instruction.
   return fuzzerutil::CanInsertOpcodeBeforeInstruction(
-      SpvOpVectorShuffle, instruction_to_insert_before);
+      spv::Op::OpVectorShuffle, instruction_to_insert_before);
 }
 
 void TransformationVectorShuffle::Apply(
@@ -134,8 +134,8 @@
       FindInstruction(message_.instruction_to_insert_before(), ir_context);
   opt::Instruction* new_instruction =
       insert_before->InsertBefore(MakeUnique<opt::Instruction>(
-          ir_context, SpvOpVectorShuffle, result_type_id, message_.fresh_id(),
-          shuffle_operands));
+          ir_context, spv::Op::OpVectorShuffle, result_type_id,
+          message_.fresh_id(), shuffle_operands));
   fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
   // Inform the def-use manager about the new instruction and record its basic
   // block.
diff --git a/source/fuzz/transformation_wrap_early_terminator_in_function.cpp b/source/fuzz/transformation_wrap_early_terminator_in_function.cpp
index 468d809..f59d78a 100644
--- a/source/fuzz/transformation_wrap_early_terminator_in_function.cpp
+++ b/source/fuzz/transformation_wrap_early_terminator_in_function.cpp
@@ -52,9 +52,9 @@
     return false;
   }
   switch (early_terminator->opcode()) {
-    case SpvOpKill:
-    case SpvOpUnreachable:
-    case SpvOpTerminateInvocation:
+    case spv::Op::OpKill:
+    case spv::Op::OpUnreachable:
+    case spv::Op::OpTerminateInvocation:
       break;
     default:
       return false;
@@ -119,7 +119,7 @@
       MaybeGetWrapperFunction(ir_context, early_terminator->opcode());
 
   iterator->InsertBefore(MakeUnique<opt::Instruction>(
-      ir_context, SpvOpFunctionCall, wrapper_function->type_id(),
+      ir_context, spv::Op::OpFunctionCall, wrapper_function->type_id(),
       message_.fresh_id(),
       opt::Instruction::OperandList(
           {{SPV_OPERAND_TYPE_ID, {wrapper_function->result_id()}}})));
@@ -130,9 +130,9 @@
            ->AsVoid()) {
     new_in_operands.push_back(
         {SPV_OPERAND_TYPE_ID, {message_.returned_value_id()}});
-    early_terminator->SetOpcode(SpvOpReturnValue);
+    early_terminator->SetOpcode(spv::Op::OpReturnValue);
   } else {
-    early_terminator->SetOpcode(SpvOpReturn);
+    early_terminator->SetOpcode(spv::Op::OpReturn);
   }
   early_terminator->SetInOperands(std::move(new_in_operands));
 
@@ -153,10 +153,10 @@
 
 opt::Function*
 TransformationWrapEarlyTerminatorInFunction::MaybeGetWrapperFunction(
-    opt::IRContext* ir_context, SpvOp early_terminator_opcode) {
-  assert((early_terminator_opcode == SpvOpKill ||
-          early_terminator_opcode == SpvOpUnreachable ||
-          early_terminator_opcode == SpvOpTerminateInvocation) &&
+    opt::IRContext* ir_context, spv::Op early_terminator_opcode) {
+  assert((early_terminator_opcode == spv::Op::OpKill ||
+          early_terminator_opcode == spv::Op::OpUnreachable ||
+          early_terminator_opcode == spv::Op::OpTerminateInvocation) &&
          "Invalid opcode.");
   auto void_type_id = fuzzerutil::MaybeGetVoidType(ir_context);
   if (!void_type_id) {
diff --git a/source/fuzz/transformation_wrap_early_terminator_in_function.h b/source/fuzz/transformation_wrap_early_terminator_in_function.h
index d6e5551..d824783 100644
--- a/source/fuzz/transformation_wrap_early_terminator_in_function.h
+++ b/source/fuzz/transformation_wrap_early_terminator_in_function.h
@@ -60,8 +60,8 @@
 
   protobufs::Transformation ToMessage() const override;
 
-  static opt::Function* MaybeGetWrapperFunction(opt::IRContext* ir_context,
-                                                SpvOp early_terminator_opcode);
+  static opt::Function* MaybeGetWrapperFunction(
+      opt::IRContext* ir_context, spv::Op early_terminator_opcode);
 
  private:
   protobufs::TransformationWrapEarlyTerminatorInFunction message_;
diff --git a/source/fuzz/transformation_wrap_region_in_selection.cpp b/source/fuzz/transformation_wrap_region_in_selection.cpp
index 01c98cc..a63d1ac 100644
--- a/source/fuzz/transformation_wrap_region_in_selection.cpp
+++ b/source/fuzz/transformation_wrap_region_in_selection.cpp
@@ -53,14 +53,14 @@
     TransformationContext* transformation_context) const {
   auto* new_header_block =
       ir_context->cfg()->block(message_.region_entry_block_id());
-  assert(new_header_block->terminator()->opcode() == SpvOpBranch &&
+  assert(new_header_block->terminator()->opcode() == spv::Op::OpBranch &&
          "This condition should have been checked in the IsApplicable");
 
   const auto successor_id =
       new_header_block->terminator()->GetSingleWordInOperand(0);
 
   // Change |entry_block|'s terminator to |OpBranchConditional|.
-  new_header_block->terminator()->SetOpcode(SpvOpBranchConditional);
+  new_header_block->terminator()->SetOpcode(spv::Op::OpBranchConditional);
   new_header_block->terminator()->SetInOperands(
       {{SPV_OPERAND_TYPE_ID,
         {fuzzerutil::MaybeGetBoolConstant(ir_context, *transformation_context,
@@ -70,11 +70,11 @@
 
   // Insert OpSelectionMerge before the terminator.
   new_header_block->terminator()->InsertBefore(MakeUnique<opt::Instruction>(
-      ir_context, SpvOpSelectionMerge, 0, 0,
+      ir_context, spv::Op::OpSelectionMerge, 0, 0,
       opt::Instruction::OperandList{
           {SPV_OPERAND_TYPE_ID, {message_.region_exit_block_id()}},
           {SPV_OPERAND_TYPE_SELECTION_CONTROL,
-           {SpvSelectionControlMaskNone}}}));
+           {uint32_t(spv::SelectionControlMask::MaskNone)}}}));
 
   // We've change the module so we must invalidate analyses.
   ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
@@ -130,7 +130,7 @@
   }
 
   // |header_block_candidate| must have an OpBranch terminator.
-  if (header_block_candidate->terminator()->opcode() != SpvOpBranch) {
+  if (header_block_candidate->terminator()->opcode() != spv::Op::OpBranch) {
     return false;
   }
 
diff --git a/source/fuzz/transformation_wrap_vector_synonym.cpp b/source/fuzz/transformation_wrap_vector_synonym.cpp
index 490bcd7..3b1543d 100644
--- a/source/fuzz/transformation_wrap_vector_synonym.cpp
+++ b/source/fuzz/transformation_wrap_vector_synonym.cpp
@@ -83,7 +83,7 @@
   }
 
   auto vec1_type = ir_context->get_def_use_mgr()->GetDef(vec1_type_id);
-  if (vec1_type->opcode() != SpvOpTypeVector) {
+  if (vec1_type->opcode() != spv::Op::OpTypeVector) {
     return false;
   }
 
@@ -178,18 +178,18 @@
   auto type_instruction =
       ir_context->get_def_use_mgr()->GetDef(instruction.type_id());
 
-  if ((type_instruction->opcode() != SpvOpTypeInt &&
-       type_instruction->opcode() != SpvOpTypeFloat)) {
+  if ((type_instruction->opcode() != spv::Op::OpTypeInt &&
+       type_instruction->opcode() != spv::Op::OpTypeFloat)) {
     return false;
   }
 
   switch (instruction.opcode()) {
-    case SpvOpIAdd:
-    case SpvOpISub:
-    case SpvOpIMul:
-    case SpvOpFAdd:
-    case SpvOpFSub:
-    case SpvOpFMul:
+    case spv::Op::OpIAdd:
+    case spv::Op::OpISub:
+    case spv::Op::OpIMul:
+    case spv::Op::OpFAdd:
+    case spv::Op::OpFSub:
+    case spv::Op::OpFMul:
       return true;
     default:
       return false;
diff --git a/source/fuzz/uniform_buffer_element_descriptor.cpp b/source/fuzz/uniform_buffer_element_descriptor.cpp
index 90fd85e..55e84de 100644
--- a/source/fuzz/uniform_buffer_element_descriptor.cpp
+++ b/source/fuzz/uniform_buffer_element_descriptor.cpp
@@ -49,10 +49,11 @@
 
   for (auto& inst : context->types_values()) {
     // Consider all global variables with uniform storage class.
-    if (inst.opcode() != SpvOpVariable) {
+    if (inst.opcode() != spv::Op::OpVariable) {
       continue;
     }
-    if (inst.GetSingleWordInOperand(0) != SpvStorageClassUniform) {
+    if (spv::StorageClass(inst.GetSingleWordInOperand(0)) !=
+        spv::StorageClass::Uniform) {
       continue;
     }
 
@@ -60,7 +61,7 @@
     // matching that in |uniform_buffer_element|.
     bool descriptor_set_matches = false;
     context->get_decoration_mgr()->ForEachDecoration(
-        inst.result_id(), SpvDecorationDescriptorSet,
+        inst.result_id(), uint32_t(spv::Decoration::DescriptorSet),
         [&descriptor_set_matches, &uniform_buffer_element_descriptor](
             const opt::Instruction& decoration_inst) {
           const uint32_t kDescriptorSetOperandIndex = 2;
@@ -79,7 +80,7 @@
     // in |uniform_buffer_element|.
     bool binding_matches = false;
     context->get_decoration_mgr()->ForEachDecoration(
-        inst.result_id(), SpvDecorationBinding,
+        inst.result_id(), uint32_t(spv::Decoration::Binding),
         [&binding_matches, &uniform_buffer_element_descriptor](
             const opt::Instruction& decoration_inst) {
           const uint32_t kBindingOperandIndex = 2;
diff --git a/source/instruction.h b/source/instruction.h
index 9e7dccd..2acbb57 100644
--- a/source/instruction.h
+++ b/source/instruction.h
@@ -26,7 +26,7 @@
   // Normally, both opcode and extInstType contain valid data.
   // However, when the assembler parses !<number> as the first word in
   // an instruction and opcode and extInstType are invalid.
-  SpvOp opcode;
+  spv::Op opcode;
   spv_ext_inst_type_t extInstType;
 
   // The Id of the result type, if this instruction has one.  Zero otherwise.
diff --git a/source/latest_version_spirv_header.h b/source/latest_version_spirv_header.h
index e4f28e4..f6ab5c8 100644
--- a/source/latest_version_spirv_header.h
+++ b/source/latest_version_spirv_header.h
@@ -15,6 +15,6 @@
 #ifndef SOURCE_LATEST_VERSION_SPIRV_HEADER_H_
 #define SOURCE_LATEST_VERSION_SPIRV_HEADER_H_
 
-#include "spirv/unified1/spirv.h"
+#include "spirv/unified1/spirv.hpp11"
 
 #endif  // SOURCE_LATEST_VERSION_SPIRV_HEADER_H_
diff --git a/source/libspirv.cpp b/source/libspirv.cpp
index be76caa..83e8629 100644
--- a/source/libspirv.cpp
+++ b/source/libspirv.cpp
@@ -108,6 +108,40 @@
   return status == SPV_SUCCESS;
 }
 
+struct CxxParserContext {
+  const HeaderParser& header_parser;
+  const InstructionParser& instruction_parser;
+};
+
+bool SpirvTools::Parse(const std::vector<uint32_t>& binary,
+                       const HeaderParser& header_parser,
+                       const InstructionParser& instruction_parser,
+                       spv_diagnostic* diagnostic) {
+  CxxParserContext parser_context = {header_parser, instruction_parser};
+
+  spv_parsed_header_fn_t header_fn_wrapper =
+      [](void* user_data, spv_endianness_t endianness, uint32_t magic,
+         uint32_t version, uint32_t generator, uint32_t id_bound,
+         uint32_t reserved) {
+        CxxParserContext* ctx = reinterpret_cast<CxxParserContext*>(user_data);
+        spv_parsed_header_t header = {magic, version, generator, id_bound,
+                                      reserved};
+
+        return ctx->header_parser(endianness, header);
+      };
+
+  spv_parsed_instruction_fn_t instruction_fn_wrapper =
+      [](void* user_data, const spv_parsed_instruction_t* instruction) {
+        CxxParserContext* ctx = reinterpret_cast<CxxParserContext*>(user_data);
+        return ctx->instruction_parser(*instruction);
+      };
+
+  spv_result_t status = spvBinaryParse(
+      impl_->context, &parser_context, binary.data(), binary.size(),
+      header_fn_wrapper, instruction_fn_wrapper, diagnostic);
+  return status == SPV_SUCCESS;
+}
+
 bool SpirvTools::Validate(const std::vector<uint32_t>& binary) const {
   return Validate(binary.data(), binary.size());
 }
diff --git a/source/link/CMakeLists.txt b/source/link/CMakeLists.txt
index a452a10..a35b9a5 100644
--- a/source/link/CMakeLists.txt
+++ b/source/link/CMakeLists.txt
@@ -31,10 +31,7 @@
 spvtools_check_symbol_exports(SPIRV-Tools-link)
 
 if(ENABLE_SPIRV_TOOLS_INSTALL)
-  install(TARGETS SPIRV-Tools-link EXPORT SPIRV-Tools-linkTargets
-    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
-    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
-    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
+  install(TARGETS SPIRV-Tools-link EXPORT SPIRV-Tools-linkTargets)
   export(EXPORT SPIRV-Tools-linkTargets FILE SPIRV-Tools-linkTargets.cmake)
 
   spvtools_config_package_dir(SPIRV-Tools-link PACKAGE_DIR)
diff --git a/source/link/linker.cpp b/source/link/linker.cpp
index 3b388cc..58930e4 100644
--- a/source/link/linker.cpp
+++ b/source/link/linker.cpp
@@ -57,12 +57,12 @@
 
 // Stores various information about an imported or exported symbol.
 struct LinkageSymbolInfo {
-  SpvId id;          // ID of the symbol
-  SpvId type_id;     // ID of the type of the symbol
+  spv::Id id;        // ID of the symbol
+  spv::Id type_id;   // ID of the type of the symbol
   std::string name;  // unique name defining the symbol and used for matching
                      // imports and exports together
-  std::vector<SpvId> parameter_ids;  // ID of the parameters of the symbol, if
-                                     // it is a function
+  std::vector<spv::Id> parameter_ids;  // ID of the parameters of the symbol, if
+                                       // it is a function
 };
 struct LinkageEntry {
   LinkageSymbolInfo imported_symbol;
@@ -91,7 +91,8 @@
 // should be non-null. |max_id_bound| should be strictly greater than 0.
 spv_result_t GenerateHeader(const MessageConsumer& consumer,
                             const std::vector<opt::Module*>& modules,
-                            uint32_t max_id_bound, opt::ModuleHeader* header);
+                            uint32_t max_id_bound, opt::ModuleHeader* header,
+                            const LinkerOptions& options);
 
 // Merge all the modules from |in_modules| into a single module owned by
 // |linked_context|.
@@ -202,7 +203,8 @@
 
 spv_result_t GenerateHeader(const MessageConsumer& consumer,
                             const std::vector<opt::Module*>& modules,
-                            uint32_t max_id_bound, opt::ModuleHeader* header) {
+                            uint32_t max_id_bound, opt::ModuleHeader* header,
+                            const LinkerOptions& options) {
   spv_position_t position = {};
 
   if (modules.empty())
@@ -212,10 +214,12 @@
     return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_DATA)
            << "|max_id_bound| of GenerateHeader should not be null.";
 
-  const uint32_t linked_version = modules.front()->version();
+  uint32_t linked_version = modules.front()->version();
   for (std::size_t i = 1; i < modules.size(); ++i) {
     const uint32_t module_version = modules[i]->version();
-    if (module_version != linked_version)
+    if (options.GetUseHighestVersion()) {
+      linked_version = std::max(linked_version, module_version);
+    } else if (module_version != linked_version) {
       return DiagnosticStream({0, 0, 1}, consumer, "", SPV_ERROR_INTERNAL)
              << "Conflicting SPIR-V versions: "
              << SPV_SPIRV_VERSION_MAJOR_PART(linked_version) << "."
@@ -224,9 +228,10 @@
              << SPV_SPIRV_VERSION_MAJOR_PART(module_version) << "."
              << SPV_SPIRV_VERSION_MINOR_PART(module_version)
              << " (input module " << (i + 1) << ").";
+    }
   }
 
-  header->magic_number = SpvMagicNumber;
+  header->magic_number = spv::MagicNumber;
   header->version = linked_version;
   header->generator = SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS_LINKER, 0);
   header->bound = max_id_bound;
@@ -367,7 +372,7 @@
     std::vector<uint32_t> processed_words =
         spvtools::utils::MakeVector(processed_string);
     linked_module->AddDebug3Inst(std::unique_ptr<Instruction>(
-        new Instruction(linked_context, SpvOpModuleProcessed, 0u, 0u,
+        new Instruction(linked_context, spv::Op::OpModuleProcessed, 0u, 0u,
                         {{SPV_OPERAND_TYPE_LITERAL_STRING, processed_words}})));
   }
 
@@ -377,7 +382,7 @@
           std::unique_ptr<Instruction>(inst.Clone(linked_context)));
 
   // TODO(pierremoreau): Since the modules have not been validate, should we
-  //                     expect SpvStorageClassFunction variables outside
+  //                     expect spv::StorageClass::Function variables outside
   //                     functions?
   for (const auto& module : input_modules) {
     for (const auto& inst : module->types_values()) {
@@ -414,16 +419,18 @@
 
   // Figure out the imports and exports
   for (const auto& decoration : linked_context.annotations()) {
-    if (decoration.opcode() != SpvOpDecorate ||
-        decoration.GetSingleWordInOperand(1u) != SpvDecorationLinkageAttributes)
+    if (decoration.opcode() != spv::Op::OpDecorate ||
+        spv::Decoration(decoration.GetSingleWordInOperand(1u)) !=
+            spv::Decoration::LinkageAttributes)
       continue;
 
-    const SpvId id = decoration.GetSingleWordInOperand(0u);
+    const spv::Id id = decoration.GetSingleWordInOperand(0u);
     // Ignore if the targeted symbol is a built-in
     bool is_built_in = false;
     for (const auto& id_decoration :
          decoration_manager.GetDecorationsFor(id, false)) {
-      if (id_decoration->GetSingleWordInOperand(1u) == SpvDecorationBuiltIn) {
+      if (spv::Decoration(id_decoration->GetSingleWordInOperand(1u)) ==
+          spv::Decoration::BuiltIn) {
         is_built_in = true;
         break;
       }
@@ -447,9 +454,9 @@
       return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_BINARY)
              << "ID " << id << " is never defined:\n";
 
-    if (def_inst->opcode() == SpvOpVariable) {
+    if (def_inst->opcode() == spv::Op::OpVariable) {
       symbol_info.type_id = def_inst->type_id();
-    } else if (def_inst->opcode() == SpvOpFunction) {
+    } else if (def_inst->opcode() == spv::Op::OpFunction) {
       symbol_info.type_id = def_inst->GetSingleWordInOperand(1u);
 
       // range-based for loop calls begin()/end(), but never cbegin()/cend(),
@@ -467,9 +474,9 @@
              << " LinkageAttributes; " << id << " is neither of them.\n";
     }
 
-    if (type == SpvLinkageTypeImport)
+    if (spv::LinkageType(type) == spv::LinkageType::Import)
       imports.push_back(symbol_info);
-    else if (type == SpvLinkageTypeExport)
+    else if (spv::LinkageType(type) == spv::LinkageType::Export)
       exports[symbol_info.name].push_back(symbol_info);
   }
 
@@ -585,7 +592,7 @@
   // TODO(pierremoreau): This will not work if the decoration is applied
   //                     through a group, but the linker does not support that
   //                     either.
-  std::unordered_set<SpvId> imports;
+  std::unordered_set<spv::Id> imports;
   if (options.GetAllowPartialLinkage()) {
     imports.reserve(linkings_to_do.size());
     for (const auto& linking_entry : linkings_to_do)
@@ -601,9 +608,11 @@
     // * if we do not allow partial linkage, remove all import annotations;
     // * otherwise, remove the annotation only if there was a corresponding
     //   export.
-    if (inst->opcode() == SpvOpDecorate &&
-        inst->GetSingleWordOperand(1u) == SpvDecorationLinkageAttributes &&
-        inst->GetSingleWordOperand(3u) == SpvLinkageTypeImport &&
+    if (inst->opcode() == spv::Op::OpDecorate &&
+        spv::Decoration(inst->GetSingleWordOperand(1u)) ==
+            spv::Decoration::LinkageAttributes &&
+        spv::LinkageType(inst->GetSingleWordOperand(3u)) ==
+            spv::LinkageType::Import &&
         (!options.GetAllowPartialLinkage() ||
          imports.find(inst->GetSingleWordOperand(0u)) != imports.end())) {
       linked_context->KillInst(&*inst);
@@ -616,9 +625,11 @@
     for (auto inst = next; inst != linked_context->annotation_end();
          inst = next) {
       ++next;
-      if (inst->opcode() == SpvOpDecorate &&
-          inst->GetSingleWordOperand(1u) == SpvDecorationLinkageAttributes &&
-          inst->GetSingleWordOperand(3u) == SpvLinkageTypeExport) {
+      if (inst->opcode() == spv::Op::OpDecorate &&
+          spv::Decoration(inst->GetSingleWordOperand(1u)) ==
+              spv::Decoration::LinkageAttributes &&
+          spv::LinkageType(inst->GetSingleWordOperand(3u)) ==
+              spv::LinkageType::Export) {
         linked_context->KillInst(&*inst);
       }
     }
@@ -628,10 +639,11 @@
   // not allowed
   if (!options.GetCreateLibrary() && !options.GetAllowPartialLinkage()) {
     for (auto& inst : linked_context->capabilities())
-      if (inst.GetSingleWordInOperand(0u) == SpvCapabilityLinkage) {
+      if (spv::Capability(inst.GetSingleWordInOperand(0u)) ==
+          spv::Capability::Linkage) {
         linked_context->KillInst(&inst);
         // The RemoveDuplicatesPass did remove duplicated capabilities, so we
-        // now there aren’t more SpvCapabilityLinkage further down.
+        // now there aren’t more spv::Capability::Linkage further down.
         break;
       }
   }
@@ -671,7 +683,7 @@
 
   size_t num_global_values = 0u;
   for (const auto& inst : linked_context.module()->types_values()) {
-    num_global_values += inst.opcode() == SpvOpVariable;
+    num_global_values += inst.opcode() == spv::Op::OpVariable;
   }
   if (num_global_values >= SPV_LIMIT_GLOBAL_VARIABLES_MAX)
     DiagnosticStream(position, consumer, "", SPV_WARNING)
@@ -746,7 +758,7 @@
 
   // Phase 2: Generate the header
   opt::ModuleHeader header;
-  res = GenerateHeader(consumer, modules, max_id_bound, &header);
+  res = GenerateHeader(consumer, modules, max_id_bound, &header, options);
   if (res != SPV_SUCCESS) return res;
   IRContext linked_context(c_context->target_env, consumer);
   linked_context.module()->SetHeader(header);
diff --git a/source/lint/CMakeLists.txt b/source/lint/CMakeLists.txt
index 1feae3f..4704beb 100644
--- a/source/lint/CMakeLists.txt
+++ b/source/lint/CMakeLists.txt
@@ -46,10 +46,7 @@
 spvtools_check_symbol_exports(SPIRV-Tools-lint)
 
 if(ENABLE_SPIRV_TOOLS_INSTALL)
-  install(TARGETS SPIRV-Tools-lint EXPORT SPIRV-Tools-lintTargets
-    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
-    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
-    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
+  install(TARGETS SPIRV-Tools-lint EXPORT SPIRV-Tools-lintTargets)
   export(EXPORT SPIRV-Tools-lintTargets FILE SPIRV-Tools-lintTargets.cmake)
 
   spvtools_config_package_dir(SPIRV-Tools-lint PACKAGE_DIR)
diff --git a/source/lint/divergence_analysis.cpp b/source/lint/divergence_analysis.cpp
index b5a72b4..fe32e1a 100644
--- a/source/lint/divergence_analysis.cpp
+++ b/source/lint/divergence_analysis.cpp
@@ -19,7 +19,6 @@
 #include "source/opt/dataflow.h"
 #include "source/opt/function.h"
 #include "source/opt/instruction.h"
-#include "spirv/unified1/spirv.h"
 
 namespace spvtools {
 namespace lint {
@@ -32,7 +31,7 @@
   uint32_t block_id;
   if (inst->IsBlockTerminator()) {
     block_id = context().get_instr_block(inst)->id();
-  } else if (inst->opcode() == SpvOpLabel) {
+  } else if (inst->opcode() == spv::Op::OpLabel) {
     block_id = inst->result_id();
     opt::BasicBlock* bb = context().cfg()->block(block_id);
     // Only enqueue phi instructions, as other uses don't affect divergence.
@@ -54,7 +53,7 @@
 
 opt::DataFlowAnalysis::VisitResult DivergenceAnalysis::Visit(
     opt::Instruction* inst) {
-  if (inst->opcode() == SpvOpLabel) {
+  if (inst->opcode() == spv::Op::OpLabel) {
     return VisitBlock(inst->result_id());
   } else {
     return VisitInstruction(inst);
@@ -128,12 +127,12 @@
   // Device/QueueFamily could satisfy fully uniform.
   uint32_t id = inst->result_id();
   // Handle divergence roots.
-  if (inst->opcode() == SpvOpFunctionParameter) {
+  if (inst->opcode() == spv::Op::OpFunctionParameter) {
     divergence_source_[id] = 0;
     return divergence_[id] = DivergenceLevel::kDivergent;
   } else if (inst->IsLoad()) {
     spvtools::opt::Instruction* var = inst->GetBaseAddress();
-    if (var->opcode() != SpvOpVariable) {
+    if (var->opcode() != spv::Op::OpVariable) {
       // Assume divergent.
       divergence_source_[id] = 0;
       return DivergenceLevel::kDivergent;
@@ -166,29 +165,30 @@
   uint32_t def_id = var->result_id();
   DivergenceLevel ret;
   switch (type->storage_class()) {
-    case SpvStorageClassFunction:
-    case SpvStorageClassGeneric:
-    case SpvStorageClassAtomicCounter:
-    case SpvStorageClassStorageBuffer:
-    case SpvStorageClassPhysicalStorageBuffer:
-    case SpvStorageClassOutput:
-    case SpvStorageClassWorkgroup:
-    case SpvStorageClassImage:  // Image atomics probably aren't uniform.
-    case SpvStorageClassPrivate:
+    case spv::StorageClass::Function:
+    case spv::StorageClass::Generic:
+    case spv::StorageClass::AtomicCounter:
+    case spv::StorageClass::StorageBuffer:
+    case spv::StorageClass::PhysicalStorageBuffer:
+    case spv::StorageClass::Output:
+    case spv::StorageClass::Workgroup:
+    case spv::StorageClass::Image:  // Image atomics probably aren't uniform.
+    case spv::StorageClass::Private:
       ret = DivergenceLevel::kDivergent;
       break;
-    case SpvStorageClassInput:
+    case spv::StorageClass::Input:
       ret = DivergenceLevel::kDivergent;
       // If this variable has a Flat decoration, it is partially uniform.
       // TODO(kuhar): Track access chain indices and also consider Flat members
       // of a structure.
       context().get_decoration_mgr()->WhileEachDecoration(
-          def_id, SpvDecorationFlat, [&ret](const opt::Instruction&) {
+          def_id, static_cast<uint32_t>(spv::Decoration::Flat),
+          [&ret](const opt::Instruction&) {
             ret = DivergenceLevel::kPartiallyUniform;
             return false;
           });
       break;
-    case SpvStorageClassUniformConstant:
+    case spv::StorageClass::UniformConstant:
       // May be a storage image which is also written to; mark those as
       // divergent.
       if (!var->IsVulkanStorageImage() || var->IsReadOnlyPointer()) {
@@ -197,9 +197,10 @@
         ret = DivergenceLevel::kDivergent;
       }
       break;
-    case SpvStorageClassUniform:
-    case SpvStorageClassPushConstant:
-    case SpvStorageClassCrossWorkgroup:  // Not for shaders; default uniform.
+    case spv::StorageClass::Uniform:
+    case spv::StorageClass::PushConstant:
+    case spv::StorageClass::CrossWorkgroup:  // Not for shaders; default
+                                             // uniform.
     default:
       ret = DivergenceLevel::kUniform;
       break;
@@ -216,7 +217,7 @@
       function->entry().get(), [this](const opt::BasicBlock* bb) {
         uint32_t id = bb->id();
         if (bb->terminator() == nullptr ||
-            bb->terminator()->opcode() != SpvOpBranch) {
+            bb->terminator()->opcode() != spv::Op::OpBranch) {
           follow_unconditional_branches_[id] = id;
         } else {
           uint32_t target_id = bb->terminator()->GetSingleWordInOperand(0);
diff --git a/source/lint/lint_divergent_derivatives.cpp b/source/lint/lint_divergent_derivatives.cpp
index 512847b..82d5ac6 100644
--- a/source/lint/lint_divergent_derivatives.cpp
+++ b/source/lint/lint_divergent_derivatives.cpp
@@ -27,7 +27,6 @@
 #include "source/opt/instruction.h"
 #include "source/opt/ir_context.h"
 #include "spirv-tools/libspirv.h"
-#include "spirv/unified1/spirv.h"
 
 namespace spvtools {
 namespace lint {
@@ -43,7 +42,7 @@
     ss << id;
   } else {
     opt::Instruction* inst_name = names.begin()->second;
-    if (inst_name->opcode() == SpvOpName) {
+    if (inst_name->opcode() == spv::Op::OpName) {
       ss << names.begin()->second->GetInOperand(0).AsString();
       ss << "[" << id << "]";
     } else {
@@ -54,26 +53,26 @@
 }
 
 bool InstructionHasDerivative(const opt::Instruction& inst) {
-  static const SpvOp derivative_opcodes[] = {
+  static const spv::Op derivative_opcodes[] = {
       // Implicit derivatives.
-      SpvOpImageSampleImplicitLod,
-      SpvOpImageSampleDrefImplicitLod,
-      SpvOpImageSampleProjImplicitLod,
-      SpvOpImageSampleProjDrefImplicitLod,
-      SpvOpImageSparseSampleImplicitLod,
-      SpvOpImageSparseSampleDrefImplicitLod,
-      SpvOpImageSparseSampleProjImplicitLod,
-      SpvOpImageSparseSampleProjDrefImplicitLod,
+      spv::Op::OpImageSampleImplicitLod,
+      spv::Op::OpImageSampleDrefImplicitLod,
+      spv::Op::OpImageSampleProjImplicitLod,
+      spv::Op::OpImageSampleProjDrefImplicitLod,
+      spv::Op::OpImageSparseSampleImplicitLod,
+      spv::Op::OpImageSparseSampleDrefImplicitLod,
+      spv::Op::OpImageSparseSampleProjImplicitLod,
+      spv::Op::OpImageSparseSampleProjDrefImplicitLod,
       // Explicit derivatives.
-      SpvOpDPdx,
-      SpvOpDPdy,
-      SpvOpFwidth,
-      SpvOpDPdxFine,
-      SpvOpDPdyFine,
-      SpvOpFwidthFine,
-      SpvOpDPdxCoarse,
-      SpvOpDPdyCoarse,
-      SpvOpFwidthCoarse,
+      spv::Op::OpDPdx,
+      spv::Op::OpDPdy,
+      spv::Op::OpFwidth,
+      spv::Op::OpDPdxFine,
+      spv::Op::OpDPdyFine,
+      spv::Op::OpFwidthFine,
+      spv::Op::OpDPdxCoarse,
+      spv::Op::OpDPdyCoarse,
+      spv::Op::OpFwidthCoarse,
   };
   return std::find(std::begin(derivative_opcodes), std::end(derivative_opcodes),
                    inst.opcode()) != std::end(derivative_opcodes);
@@ -97,13 +96,14 @@
   opt::analysis::DefUseManager* def_use = context->get_def_use_mgr();
   opt::CFG* cfg = context->cfg();
   while (id != 0) {
-    bool is_block = def_use->GetDef(id)->opcode() == SpvOpLabel;
+    bool is_block = def_use->GetDef(id)->opcode() == spv::Op::OpLabel;
     if (is_block) {
       Warn(context, nullptr)
           << "block " << GetFriendlyName(context, id) << " is divergent";
       uint32_t source = div.GetDivergenceSource(id);
       // Skip intermediate blocks.
-      while (source != 0 && def_use->GetDef(source)->opcode() == SpvOpLabel) {
+      while (source != 0 &&
+             def_use->GetDef(source)->opcode() == spv::Op::OpLabel) {
         id = source;
         source = div.GetDivergenceSource(id);
       }
@@ -122,7 +122,7 @@
       opt::Instruction* source_def =
           source == 0 ? nullptr : def_use->GetDef(source);
       // First print data -> data dependencies.
-      while (source != 0 && source_def->opcode() != SpvOpLabel) {
+      while (source != 0 && source_def->opcode() != spv::Op::OpLabel) {
         Warn(context, def_use->GetDef(id))
             << "because " << GetFriendlyName(context, id) << " uses value "
             << GetFriendlyName(context, source)
diff --git a/source/lint/linter.cpp b/source/lint/linter.cpp
index e4ed04e..7480676 100644
--- a/source/lint/linter.cpp
+++ b/source/lint/linter.cpp
@@ -19,7 +19,6 @@
 #include "source/opt/ir_context.h"
 #include "spirv-tools/libspirv.h"
 #include "spirv-tools/libspirv.hpp"
-#include "spirv/unified1/spirv.h"
 
 namespace spvtools {
 
diff --git a/source/name_mapper.cpp b/source/name_mapper.cpp
index 3b31d33..b2d0f44 100644
--- a/source/name_mapper.cpp
+++ b/source/name_mapper.cpp
@@ -100,18 +100,18 @@
 void FriendlyNameMapper::SaveBuiltInName(uint32_t target_id,
                                          uint32_t built_in) {
 #define GLCASE(name)                  \
-  case SpvBuiltIn##name:              \
+  case spv::BuiltIn::name:            \
     SaveName(target_id, "gl_" #name); \
     return;
 #define GLCASE2(name, suggested)           \
-  case SpvBuiltIn##name:                   \
+  case spv::BuiltIn::name:                 \
     SaveName(target_id, "gl_" #suggested); \
     return;
 #define CASE(name)              \
-  case SpvBuiltIn##name:        \
+  case spv::BuiltIn::name:      \
     SaveName(target_id, #name); \
     return;
-  switch (built_in) {
+  switch (spv::BuiltIn(built_in)) {
     GLCASE(Position)
     GLCASE(PointSize)
     GLCASE(ClipDistance)
@@ -170,28 +170,28 @@
 spv_result_t FriendlyNameMapper::ParseInstruction(
     const spv_parsed_instruction_t& inst) {
   const auto result_id = inst.result_id;
-  switch (inst.opcode) {
-    case SpvOpName:
+  switch (spv::Op(inst.opcode)) {
+    case spv::Op::OpName:
       SaveName(inst.words[1], spvDecodeLiteralStringOperand(inst, 1));
       break;
-    case SpvOpDecorate:
+    case spv::Op::OpDecorate:
       // Decorations come after OpName.  So OpName will take precedence over
       // decorations.
       //
       // In theory, we should also handle OpGroupDecorate.  But that's unlikely
       // to occur.
-      if (inst.words[2] == SpvDecorationBuiltIn) {
+      if (spv::Decoration(inst.words[2]) == spv::Decoration::BuiltIn) {
         assert(inst.num_words > 3);
         SaveBuiltInName(inst.words[1], inst.words[3]);
       }
       break;
-    case SpvOpTypeVoid:
+    case spv::Op::OpTypeVoid:
       SaveName(result_id, "void");
       break;
-    case SpvOpTypeBool:
+    case spv::Op::OpTypeBool:
       SaveName(result_id, "bool");
       break;
-    case SpvOpTypeInt: {
+    case spv::Op::OpTypeInt: {
       std::string signedness;
       std::string root;
       const auto bit_width = inst.words[2];
@@ -216,7 +216,7 @@
       if (0 == inst.words[3]) signedness = "u";
       SaveName(result_id, signedness + root);
     } break;
-    case SpvOpTypeFloat: {
+    case spv::Op::OpTypeFloat: {
       const auto bit_width = inst.words[2];
       switch (bit_width) {
         case 16:
@@ -233,68 +233,68 @@
           break;
       }
     } break;
-    case SpvOpTypeVector:
+    case spv::Op::OpTypeVector:
       SaveName(result_id, std::string("v") + to_string(inst.words[3]) +
                               NameForId(inst.words[2]));
       break;
-    case SpvOpTypeMatrix:
+    case spv::Op::OpTypeMatrix:
       SaveName(result_id, std::string("mat") + to_string(inst.words[3]) +
                               NameForId(inst.words[2]));
       break;
-    case SpvOpTypeArray:
+    case spv::Op::OpTypeArray:
       SaveName(result_id, std::string("_arr_") + NameForId(inst.words[2]) +
                               "_" + NameForId(inst.words[3]));
       break;
-    case SpvOpTypeRuntimeArray:
+    case spv::Op::OpTypeRuntimeArray:
       SaveName(result_id,
                std::string("_runtimearr_") + NameForId(inst.words[2]));
       break;
-    case SpvOpTypePointer:
+    case spv::Op::OpTypePointer:
       SaveName(result_id, std::string("_ptr_") +
                               NameForEnumOperand(SPV_OPERAND_TYPE_STORAGE_CLASS,
                                                  inst.words[2]) +
                               "_" + NameForId(inst.words[3]));
       break;
-    case SpvOpTypePipe:
+    case spv::Op::OpTypePipe:
       SaveName(result_id,
                std::string("Pipe") +
                    NameForEnumOperand(SPV_OPERAND_TYPE_ACCESS_QUALIFIER,
                                       inst.words[2]));
       break;
-    case SpvOpTypeEvent:
+    case spv::Op::OpTypeEvent:
       SaveName(result_id, "Event");
       break;
-    case SpvOpTypeDeviceEvent:
+    case spv::Op::OpTypeDeviceEvent:
       SaveName(result_id, "DeviceEvent");
       break;
-    case SpvOpTypeReserveId:
+    case spv::Op::OpTypeReserveId:
       SaveName(result_id, "ReserveId");
       break;
-    case SpvOpTypeQueue:
+    case spv::Op::OpTypeQueue:
       SaveName(result_id, "Queue");
       break;
-    case SpvOpTypeOpaque:
+    case spv::Op::OpTypeOpaque:
       SaveName(result_id, std::string("Opaque_") +
                               Sanitize(spvDecodeLiteralStringOperand(inst, 1)));
       break;
-    case SpvOpTypePipeStorage:
+    case spv::Op::OpTypePipeStorage:
       SaveName(result_id, "PipeStorage");
       break;
-    case SpvOpTypeNamedBarrier:
+    case spv::Op::OpTypeNamedBarrier:
       SaveName(result_id, "NamedBarrier");
       break;
-    case SpvOpTypeStruct:
+    case spv::Op::OpTypeStruct:
       // Structs are mapped rather simplisitically. Just indicate that they
       // are a struct and then give the raw Id number.
       SaveName(result_id, std::string("_struct_") + to_string(result_id));
       break;
-    case SpvOpConstantTrue:
+    case spv::Op::OpConstantTrue:
       SaveName(result_id, "true");
       break;
-    case SpvOpConstantFalse:
+    case spv::Op::OpConstantFalse:
       SaveName(result_id, "false");
       break;
-    case SpvOpConstant: {
+    case spv::Op::OpConstant: {
       std::ostringstream value;
       EmitNumericLiteral(&value, inst, inst.operands[2]);
       auto value_str = value.str();
diff --git a/source/opcode.cpp b/source/opcode.cpp
index 3f92729..ffbb2e8 100644
--- a/source/opcode.cpp
+++ b/source/opcode.cpp
@@ -64,7 +64,7 @@
   return "Unknown";
 }
 
-uint32_t spvOpcodeMake(uint16_t wordCount, SpvOp opcode) {
+uint32_t spvOpcodeMake(uint16_t wordCount, spv::Op opcode) {
   return ((uint32_t)opcode) | (((uint32_t)wordCount) << 16);
 }
 
@@ -125,7 +125,7 @@
 
 spv_result_t spvOpcodeTableValueLookup(spv_target_env env,
                                        const spv_opcode_table table,
-                                       const SpvOp opcode,
+                                       const spv::Op opcode,
                                        spv_opcode_desc* pEntry) {
   if (!table) return SPV_ERROR_INVALID_TABLE;
   if (!pEntry) return SPV_ERROR_INVALID_POINTER;
@@ -166,7 +166,7 @@
   return SPV_ERROR_INVALID_LOOKUP;
 }
 
-void spvInstructionCopy(const uint32_t* words, const SpvOp opcode,
+void spvInstructionCopy(const uint32_t* words, const spv::Op opcode,
                         const uint16_t wordCount, const spv_endianness_t endian,
                         spv_instruction_t* pInst) {
   pInst->opcode = opcode;
@@ -177,7 +177,7 @@
       uint16_t thisWordCount;
       uint16_t thisOpcode;
       spvOpcodeSplit(pInst->words[wordIndex], &thisWordCount, &thisOpcode);
-      assert(opcode == static_cast<SpvOp>(thisOpcode) &&
+      assert(opcode == static_cast<spv::Op>(thisOpcode) &&
              wordCount == thisWordCount && "Endianness failed!");
     }
   }
@@ -186,7 +186,7 @@
 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<SpvOp>(opcode),
+  spv_opcode_desc_t needle = {"",    static_cast<spv::Op>(opcode),
                               0,     nullptr,
                               0,     {},
                               false, false,
@@ -196,7 +196,7 @@
     return lhs.opcode < rhs.opcode;
   };
   auto it = std::lower_bound(beg, end, needle, comp);
-  if (it != end && it->opcode == opcode) {
+  if (it != end && it->opcode == spv::Op(opcode)) {
     return it->name;
   }
 
@@ -204,140 +204,148 @@
   return "unknown";
 }
 
-int32_t spvOpcodeIsScalarType(const SpvOp opcode) {
+const char* spvOpcodeString(const spv::Op opcode) {
+  return spvOpcodeString(static_cast<uint32_t>(opcode));
+}
+
+int32_t spvOpcodeIsScalarType(const spv::Op opcode) {
   switch (opcode) {
-    case SpvOpTypeInt:
-    case SpvOpTypeFloat:
-    case SpvOpTypeBool:
+    case spv::Op::OpTypeInt:
+    case spv::Op::OpTypeFloat:
+    case spv::Op::OpTypeBool:
       return true;
     default:
       return false;
   }
 }
 
-int32_t spvOpcodeIsSpecConstant(const SpvOp opcode) {
+int32_t spvOpcodeIsSpecConstant(const spv::Op opcode) {
   switch (opcode) {
-    case SpvOpSpecConstantTrue:
-    case SpvOpSpecConstantFalse:
-    case SpvOpSpecConstant:
-    case SpvOpSpecConstantComposite:
-    case SpvOpSpecConstantOp:
+    case spv::Op::OpSpecConstantTrue:
+    case spv::Op::OpSpecConstantFalse:
+    case spv::Op::OpSpecConstant:
+    case spv::Op::OpSpecConstantComposite:
+    case spv::Op::OpSpecConstantOp:
       return true;
     default:
       return false;
   }
 }
 
-int32_t spvOpcodeIsConstant(const SpvOp opcode) {
+int32_t spvOpcodeIsConstant(const spv::Op opcode) {
   switch (opcode) {
-    case SpvOpConstantTrue:
-    case SpvOpConstantFalse:
-    case SpvOpConstant:
-    case SpvOpConstantComposite:
-    case SpvOpConstantSampler:
-    case SpvOpConstantNull:
-    case SpvOpSpecConstantTrue:
-    case SpvOpSpecConstantFalse:
-    case SpvOpSpecConstant:
-    case SpvOpSpecConstantComposite:
-    case SpvOpSpecConstantOp:
+    case spv::Op::OpConstantTrue:
+    case spv::Op::OpConstantFalse:
+    case spv::Op::OpConstant:
+    case spv::Op::OpConstantComposite:
+    case spv::Op::OpConstantSampler:
+    case spv::Op::OpConstantNull:
+    case spv::Op::OpConstantFunctionPointerINTEL:
+    case spv::Op::OpSpecConstantTrue:
+    case spv::Op::OpSpecConstantFalse:
+    case spv::Op::OpSpecConstant:
+    case spv::Op::OpSpecConstantComposite:
+    case spv::Op::OpSpecConstantOp:
       return true;
     default:
       return false;
   }
 }
 
-bool spvOpcodeIsConstantOrUndef(const SpvOp opcode) {
-  return opcode == SpvOpUndef || spvOpcodeIsConstant(opcode);
+bool spvOpcodeIsConstantOrUndef(const spv::Op opcode) {
+  return opcode == spv::Op::OpUndef || spvOpcodeIsConstant(opcode);
 }
 
-bool spvOpcodeIsScalarSpecConstant(const SpvOp opcode) {
+bool spvOpcodeIsScalarSpecConstant(const spv::Op opcode) {
   switch (opcode) {
-    case SpvOpSpecConstantTrue:
-    case SpvOpSpecConstantFalse:
-    case SpvOpSpecConstant:
+    case spv::Op::OpSpecConstantTrue:
+    case spv::Op::OpSpecConstantFalse:
+    case spv::Op::OpSpecConstant:
       return true;
     default:
       return false;
   }
 }
 
-int32_t spvOpcodeIsComposite(const SpvOp opcode) {
+int32_t spvOpcodeIsComposite(const spv::Op opcode) {
   switch (opcode) {
-    case SpvOpTypeVector:
-    case SpvOpTypeMatrix:
-    case SpvOpTypeArray:
-    case SpvOpTypeStruct:
-    case SpvOpTypeCooperativeMatrixNV:
+    case spv::Op::OpTypeVector:
+    case spv::Op::OpTypeMatrix:
+    case spv::Op::OpTypeArray:
+    case spv::Op::OpTypeStruct:
+    case spv::Op::OpTypeCooperativeMatrixNV:
+    case spv::Op::OpTypeCooperativeMatrixKHR:
       return true;
     default:
       return false;
   }
 }
 
-bool spvOpcodeReturnsLogicalVariablePointer(const SpvOp opcode) {
+bool spvOpcodeReturnsLogicalVariablePointer(const spv::Op opcode) {
   switch (opcode) {
-    case SpvOpVariable:
-    case SpvOpAccessChain:
-    case SpvOpInBoundsAccessChain:
-    case SpvOpFunctionParameter:
-    case SpvOpImageTexelPointer:
-    case SpvOpCopyObject:
-    case SpvOpSelect:
-    case SpvOpPhi:
-    case SpvOpFunctionCall:
-    case SpvOpPtrAccessChain:
-    case SpvOpLoad:
-    case SpvOpConstantNull:
+    case spv::Op::OpVariable:
+    case spv::Op::OpAccessChain:
+    case spv::Op::OpInBoundsAccessChain:
+    case spv::Op::OpFunctionParameter:
+    case spv::Op::OpImageTexelPointer:
+    case spv::Op::OpCopyObject:
+    case spv::Op::OpSelect:
+    case spv::Op::OpPhi:
+    case spv::Op::OpFunctionCall:
+    case spv::Op::OpPtrAccessChain:
+    case spv::Op::OpLoad:
+    case spv::Op::OpConstantNull:
       return true;
     default:
       return false;
   }
 }
 
-int32_t spvOpcodeReturnsLogicalPointer(const SpvOp opcode) {
+int32_t spvOpcodeReturnsLogicalPointer(const spv::Op opcode) {
   switch (opcode) {
-    case SpvOpVariable:
-    case SpvOpAccessChain:
-    case SpvOpInBoundsAccessChain:
-    case SpvOpFunctionParameter:
-    case SpvOpImageTexelPointer:
-    case SpvOpCopyObject:
+    case spv::Op::OpVariable:
+    case spv::Op::OpAccessChain:
+    case spv::Op::OpInBoundsAccessChain:
+    case spv::Op::OpFunctionParameter:
+    case spv::Op::OpImageTexelPointer:
+    case spv::Op::OpCopyObject:
       return true;
     default:
       return false;
   }
 }
 
-int32_t spvOpcodeGeneratesType(SpvOp op) {
+int32_t spvOpcodeGeneratesType(spv::Op op) {
   switch (op) {
-    case SpvOpTypeVoid:
-    case SpvOpTypeBool:
-    case SpvOpTypeInt:
-    case SpvOpTypeFloat:
-    case SpvOpTypeVector:
-    case SpvOpTypeMatrix:
-    case SpvOpTypeImage:
-    case SpvOpTypeSampler:
-    case SpvOpTypeSampledImage:
-    case SpvOpTypeArray:
-    case SpvOpTypeRuntimeArray:
-    case SpvOpTypeStruct:
-    case SpvOpTypeOpaque:
-    case SpvOpTypePointer:
-    case SpvOpTypeFunction:
-    case SpvOpTypeEvent:
-    case SpvOpTypeDeviceEvent:
-    case SpvOpTypeReserveId:
-    case SpvOpTypeQueue:
-    case SpvOpTypePipe:
-    case SpvOpTypePipeStorage:
-    case SpvOpTypeNamedBarrier:
-    case SpvOpTypeAccelerationStructureNV:
-    case SpvOpTypeCooperativeMatrixNV:
-    // case SpvOpTypeAccelerationStructureKHR: covered by
-    // SpvOpTypeAccelerationStructureNV
-    case SpvOpTypeRayQueryKHR:
+    case spv::Op::OpTypeVoid:
+    case spv::Op::OpTypeBool:
+    case spv::Op::OpTypeInt:
+    case spv::Op::OpTypeFloat:
+    case spv::Op::OpTypeVector:
+    case spv::Op::OpTypeMatrix:
+    case spv::Op::OpTypeImage:
+    case spv::Op::OpTypeSampler:
+    case spv::Op::OpTypeSampledImage:
+    case spv::Op::OpTypeArray:
+    case spv::Op::OpTypeRuntimeArray:
+    case spv::Op::OpTypeStruct:
+    case spv::Op::OpTypeOpaque:
+    case spv::Op::OpTypePointer:
+    case spv::Op::OpTypeFunction:
+    case spv::Op::OpTypeEvent:
+    case spv::Op::OpTypeDeviceEvent:
+    case spv::Op::OpTypeReserveId:
+    case spv::Op::OpTypeQueue:
+    case spv::Op::OpTypePipe:
+    case spv::Op::OpTypePipeStorage:
+    case spv::Op::OpTypeNamedBarrier:
+    case spv::Op::OpTypeAccelerationStructureNV:
+    case spv::Op::OpTypeCooperativeMatrixNV:
+    case spv::Op::OpTypeCooperativeMatrixKHR:
+    // case spv::Op::OpTypeAccelerationStructureKHR: covered by
+    // spv::Op::OpTypeAccelerationStructureNV
+    case spv::Op::OpTypeRayQueryKHR:
+    case spv::Op::OpTypeHitObjectNV:
       return true;
     default:
       // In particular, OpTypeForwardPointer does not generate a type,
@@ -348,15 +356,15 @@
   return 0;
 }
 
-bool spvOpcodeIsDecoration(const SpvOp opcode) {
+bool spvOpcodeIsDecoration(const spv::Op opcode) {
   switch (opcode) {
-    case SpvOpDecorate:
-    case SpvOpDecorateId:
-    case SpvOpMemberDecorate:
-    case SpvOpGroupDecorate:
-    case SpvOpGroupMemberDecorate:
-    case SpvOpDecorateStringGOOGLE:
-    case SpvOpMemberDecorateStringGOOGLE:
+    case spv::Op::OpDecorate:
+    case spv::Op::OpDecorateId:
+    case spv::Op::OpMemberDecorate:
+    case spv::Op::OpGroupDecorate:
+    case spv::Op::OpGroupMemberDecorate:
+    case spv::Op::OpDecorateStringGOOGLE:
+    case spv::Op::OpMemberDecorateStringGOOGLE:
       return true;
     default:
       break;
@@ -364,402 +372,403 @@
   return false;
 }
 
-bool spvOpcodeIsLoad(const SpvOp opcode) {
+bool spvOpcodeIsLoad(const spv::Op opcode) {
   switch (opcode) {
-    case SpvOpLoad:
-    case SpvOpImageSampleExplicitLod:
-    case SpvOpImageSampleImplicitLod:
-    case SpvOpImageSampleDrefImplicitLod:
-    case SpvOpImageSampleDrefExplicitLod:
-    case SpvOpImageSampleProjImplicitLod:
-    case SpvOpImageSampleProjExplicitLod:
-    case SpvOpImageSampleProjDrefImplicitLod:
-    case SpvOpImageSampleProjDrefExplicitLod:
-    case SpvOpImageFetch:
-    case SpvOpImageGather:
-    case SpvOpImageDrefGather:
-    case SpvOpImageRead:
-    case SpvOpImageSparseSampleImplicitLod:
-    case SpvOpImageSparseSampleExplicitLod:
-    case SpvOpImageSparseSampleDrefExplicitLod:
-    case SpvOpImageSparseSampleDrefImplicitLod:
-    case SpvOpImageSparseFetch:
-    case SpvOpImageSparseGather:
-    case SpvOpImageSparseDrefGather:
-    case SpvOpImageSparseRead:
+    case spv::Op::OpLoad:
+    case spv::Op::OpImageSampleExplicitLod:
+    case spv::Op::OpImageSampleImplicitLod:
+    case spv::Op::OpImageSampleDrefImplicitLod:
+    case spv::Op::OpImageSampleDrefExplicitLod:
+    case spv::Op::OpImageSampleProjImplicitLod:
+    case spv::Op::OpImageSampleProjExplicitLod:
+    case spv::Op::OpImageSampleProjDrefImplicitLod:
+    case spv::Op::OpImageSampleProjDrefExplicitLod:
+    case spv::Op::OpImageFetch:
+    case spv::Op::OpImageGather:
+    case spv::Op::OpImageDrefGather:
+    case spv::Op::OpImageRead:
+    case spv::Op::OpImageSparseSampleImplicitLod:
+    case spv::Op::OpImageSparseSampleExplicitLod:
+    case spv::Op::OpImageSparseSampleDrefExplicitLod:
+    case spv::Op::OpImageSparseSampleDrefImplicitLod:
+    case spv::Op::OpImageSparseFetch:
+    case spv::Op::OpImageSparseGather:
+    case spv::Op::OpImageSparseDrefGather:
+    case spv::Op::OpImageSparseRead:
       return true;
     default:
       return false;
   }
 }
 
-bool spvOpcodeIsBranch(SpvOp opcode) {
+bool spvOpcodeIsBranch(spv::Op opcode) {
   switch (opcode) {
-    case SpvOpBranch:
-    case SpvOpBranchConditional:
-    case SpvOpSwitch:
+    case spv::Op::OpBranch:
+    case spv::Op::OpBranchConditional:
+    case spv::Op::OpSwitch:
       return true;
     default:
       return false;
   }
 }
 
-bool spvOpcodeIsAtomicWithLoad(const SpvOp opcode) {
+bool spvOpcodeIsAtomicWithLoad(const spv::Op opcode) {
   switch (opcode) {
-    case SpvOpAtomicLoad:
-    case SpvOpAtomicExchange:
-    case SpvOpAtomicCompareExchange:
-    case SpvOpAtomicCompareExchangeWeak:
-    case SpvOpAtomicIIncrement:
-    case SpvOpAtomicIDecrement:
-    case SpvOpAtomicIAdd:
-    case SpvOpAtomicFAddEXT:
-    case SpvOpAtomicISub:
-    case SpvOpAtomicSMin:
-    case SpvOpAtomicUMin:
-    case SpvOpAtomicFMinEXT:
-    case SpvOpAtomicSMax:
-    case SpvOpAtomicUMax:
-    case SpvOpAtomicFMaxEXT:
-    case SpvOpAtomicAnd:
-    case SpvOpAtomicOr:
-    case SpvOpAtomicXor:
-    case SpvOpAtomicFlagTestAndSet:
+    case spv::Op::OpAtomicLoad:
+    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::OpAtomicFAddEXT:
+    case spv::Op::OpAtomicISub:
+    case spv::Op::OpAtomicSMin:
+    case spv::Op::OpAtomicUMin:
+    case spv::Op::OpAtomicFMinEXT:
+    case spv::Op::OpAtomicSMax:
+    case spv::Op::OpAtomicUMax:
+    case spv::Op::OpAtomicFMaxEXT:
+    case spv::Op::OpAtomicAnd:
+    case spv::Op::OpAtomicOr:
+    case spv::Op::OpAtomicXor:
+    case spv::Op::OpAtomicFlagTestAndSet:
       return true;
     default:
       return false;
   }
 }
 
-bool spvOpcodeIsAtomicOp(const SpvOp opcode) {
-  return (spvOpcodeIsAtomicWithLoad(opcode) || opcode == SpvOpAtomicStore ||
-          opcode == SpvOpAtomicFlagClear);
+bool spvOpcodeIsAtomicOp(const spv::Op opcode) {
+  return (spvOpcodeIsAtomicWithLoad(opcode) ||
+          opcode == spv::Op::OpAtomicStore ||
+          opcode == spv::Op::OpAtomicFlagClear);
 }
 
-bool spvOpcodeIsReturn(SpvOp opcode) {
+bool spvOpcodeIsReturn(spv::Op opcode) {
   switch (opcode) {
-    case SpvOpReturn:
-    case SpvOpReturnValue:
+    case spv::Op::OpReturn:
+    case spv::Op::OpReturnValue:
       return true;
     default:
       return false;
   }
 }
 
-bool spvOpcodeIsAbort(SpvOp opcode) {
+bool spvOpcodeIsAbort(spv::Op opcode) {
   switch (opcode) {
-    case SpvOpKill:
-    case SpvOpUnreachable:
-    case SpvOpTerminateInvocation:
-    case SpvOpTerminateRayKHR:
-    case SpvOpIgnoreIntersectionKHR:
-    case SpvOpEmitMeshTasksEXT:
+    case spv::Op::OpKill:
+    case spv::Op::OpUnreachable:
+    case spv::Op::OpTerminateInvocation:
+    case spv::Op::OpTerminateRayKHR:
+    case spv::Op::OpIgnoreIntersectionKHR:
+    case spv::Op::OpEmitMeshTasksEXT:
       return true;
     default:
       return false;
   }
 }
 
-bool spvOpcodeIsReturnOrAbort(SpvOp opcode) {
+bool spvOpcodeIsReturnOrAbort(spv::Op opcode) {
   return spvOpcodeIsReturn(opcode) || spvOpcodeIsAbort(opcode);
 }
 
-bool spvOpcodeIsBlockTerminator(SpvOp opcode) {
+bool spvOpcodeIsBlockTerminator(spv::Op opcode) {
   return spvOpcodeIsBranch(opcode) || spvOpcodeIsReturnOrAbort(opcode);
 }
 
-bool spvOpcodeIsBaseOpaqueType(SpvOp opcode) {
+bool spvOpcodeIsBaseOpaqueType(spv::Op opcode) {
   switch (opcode) {
-    case SpvOpTypeImage:
-    case SpvOpTypeSampler:
-    case SpvOpTypeSampledImage:
-    case SpvOpTypeOpaque:
-    case SpvOpTypeEvent:
-    case SpvOpTypeDeviceEvent:
-    case SpvOpTypeReserveId:
-    case SpvOpTypeQueue:
-    case SpvOpTypePipe:
-    case SpvOpTypeForwardPointer:
-    case SpvOpTypePipeStorage:
-    case SpvOpTypeNamedBarrier:
+    case spv::Op::OpTypeImage:
+    case spv::Op::OpTypeSampler:
+    case spv::Op::OpTypeSampledImage:
+    case spv::Op::OpTypeOpaque:
+    case spv::Op::OpTypeEvent:
+    case spv::Op::OpTypeDeviceEvent:
+    case spv::Op::OpTypeReserveId:
+    case spv::Op::OpTypeQueue:
+    case spv::Op::OpTypePipe:
+    case spv::Op::OpTypeForwardPointer:
+    case spv::Op::OpTypePipeStorage:
+    case spv::Op::OpTypeNamedBarrier:
       return true;
     default:
       return false;
   }
 }
 
-bool spvOpcodeIsNonUniformGroupOperation(SpvOp opcode) {
+bool spvOpcodeIsNonUniformGroupOperation(spv::Op opcode) {
   switch (opcode) {
-    case SpvOpGroupNonUniformElect:
-    case SpvOpGroupNonUniformAll:
-    case SpvOpGroupNonUniformAny:
-    case SpvOpGroupNonUniformAllEqual:
-    case SpvOpGroupNonUniformBroadcast:
-    case SpvOpGroupNonUniformBroadcastFirst:
-    case SpvOpGroupNonUniformBallot:
-    case SpvOpGroupNonUniformInverseBallot:
-    case SpvOpGroupNonUniformBallotBitExtract:
-    case SpvOpGroupNonUniformBallotBitCount:
-    case SpvOpGroupNonUniformBallotFindLSB:
-    case SpvOpGroupNonUniformBallotFindMSB:
-    case SpvOpGroupNonUniformShuffle:
-    case SpvOpGroupNonUniformShuffleXor:
-    case SpvOpGroupNonUniformShuffleUp:
-    case SpvOpGroupNonUniformShuffleDown:
-    case SpvOpGroupNonUniformIAdd:
-    case SpvOpGroupNonUniformFAdd:
-    case SpvOpGroupNonUniformIMul:
-    case SpvOpGroupNonUniformFMul:
-    case SpvOpGroupNonUniformSMin:
-    case SpvOpGroupNonUniformUMin:
-    case SpvOpGroupNonUniformFMin:
-    case SpvOpGroupNonUniformSMax:
-    case SpvOpGroupNonUniformUMax:
-    case SpvOpGroupNonUniformFMax:
-    case SpvOpGroupNonUniformBitwiseAnd:
-    case SpvOpGroupNonUniformBitwiseOr:
-    case SpvOpGroupNonUniformBitwiseXor:
-    case SpvOpGroupNonUniformLogicalAnd:
-    case SpvOpGroupNonUniformLogicalOr:
-    case SpvOpGroupNonUniformLogicalXor:
-    case SpvOpGroupNonUniformQuadBroadcast:
-    case SpvOpGroupNonUniformQuadSwap:
-    case SpvOpGroupNonUniformRotateKHR:
+    case spv::Op::OpGroupNonUniformElect:
+    case spv::Op::OpGroupNonUniformAll:
+    case spv::Op::OpGroupNonUniformAny:
+    case spv::Op::OpGroupNonUniformAllEqual:
+    case spv::Op::OpGroupNonUniformBroadcast:
+    case spv::Op::OpGroupNonUniformBroadcastFirst:
+    case spv::Op::OpGroupNonUniformBallot:
+    case spv::Op::OpGroupNonUniformInverseBallot:
+    case spv::Op::OpGroupNonUniformBallotBitExtract:
+    case spv::Op::OpGroupNonUniformBallotBitCount:
+    case spv::Op::OpGroupNonUniformBallotFindLSB:
+    case spv::Op::OpGroupNonUniformBallotFindMSB:
+    case spv::Op::OpGroupNonUniformShuffle:
+    case spv::Op::OpGroupNonUniformShuffleXor:
+    case spv::Op::OpGroupNonUniformShuffleUp:
+    case spv::Op::OpGroupNonUniformShuffleDown:
+    case spv::Op::OpGroupNonUniformIAdd:
+    case spv::Op::OpGroupNonUniformFAdd:
+    case spv::Op::OpGroupNonUniformIMul:
+    case spv::Op::OpGroupNonUniformFMul:
+    case spv::Op::OpGroupNonUniformSMin:
+    case spv::Op::OpGroupNonUniformUMin:
+    case spv::Op::OpGroupNonUniformFMin:
+    case spv::Op::OpGroupNonUniformSMax:
+    case spv::Op::OpGroupNonUniformUMax:
+    case spv::Op::OpGroupNonUniformFMax:
+    case spv::Op::OpGroupNonUniformBitwiseAnd:
+    case spv::Op::OpGroupNonUniformBitwiseOr:
+    case spv::Op::OpGroupNonUniformBitwiseXor:
+    case spv::Op::OpGroupNonUniformLogicalAnd:
+    case spv::Op::OpGroupNonUniformLogicalOr:
+    case spv::Op::OpGroupNonUniformLogicalXor:
+    case spv::Op::OpGroupNonUniformQuadBroadcast:
+    case spv::Op::OpGroupNonUniformQuadSwap:
+    case spv::Op::OpGroupNonUniformRotateKHR:
       return true;
     default:
       return false;
   }
 }
 
-bool spvOpcodeIsScalarizable(SpvOp opcode) {
+bool spvOpcodeIsScalarizable(spv::Op opcode) {
   switch (opcode) {
-    case SpvOpPhi:
-    case SpvOpCopyObject:
-    case SpvOpConvertFToU:
-    case SpvOpConvertFToS:
-    case SpvOpConvertSToF:
-    case SpvOpConvertUToF:
-    case SpvOpUConvert:
-    case SpvOpSConvert:
-    case SpvOpFConvert:
-    case SpvOpQuantizeToF16:
-    case SpvOpVectorInsertDynamic:
-    case SpvOpSNegate:
-    case SpvOpFNegate:
-    case SpvOpIAdd:
-    case SpvOpFAdd:
-    case SpvOpISub:
-    case SpvOpFSub:
-    case SpvOpIMul:
-    case SpvOpFMul:
-    case SpvOpUDiv:
-    case SpvOpSDiv:
-    case SpvOpFDiv:
-    case SpvOpUMod:
-    case SpvOpSRem:
-    case SpvOpSMod:
-    case SpvOpFRem:
-    case SpvOpFMod:
-    case SpvOpVectorTimesScalar:
-    case SpvOpIAddCarry:
-    case SpvOpISubBorrow:
-    case SpvOpUMulExtended:
-    case SpvOpSMulExtended:
-    case SpvOpShiftRightLogical:
-    case SpvOpShiftRightArithmetic:
-    case SpvOpShiftLeftLogical:
-    case SpvOpBitwiseOr:
-    case SpvOpBitwiseAnd:
-    case SpvOpNot:
-    case SpvOpBitFieldInsert:
-    case SpvOpBitFieldSExtract:
-    case SpvOpBitFieldUExtract:
-    case SpvOpBitReverse:
-    case SpvOpBitCount:
-    case SpvOpIsNan:
-    case SpvOpIsInf:
-    case SpvOpIsFinite:
-    case SpvOpIsNormal:
-    case SpvOpSignBitSet:
-    case SpvOpLessOrGreater:
-    case SpvOpOrdered:
-    case SpvOpUnordered:
-    case SpvOpLogicalEqual:
-    case SpvOpLogicalNotEqual:
-    case SpvOpLogicalOr:
-    case SpvOpLogicalAnd:
-    case SpvOpLogicalNot:
-    case SpvOpSelect:
-    case SpvOpIEqual:
-    case SpvOpINotEqual:
-    case SpvOpUGreaterThan:
-    case SpvOpSGreaterThan:
-    case SpvOpUGreaterThanEqual:
-    case SpvOpSGreaterThanEqual:
-    case SpvOpULessThan:
-    case SpvOpSLessThan:
-    case SpvOpULessThanEqual:
-    case SpvOpSLessThanEqual:
-    case SpvOpFOrdEqual:
-    case SpvOpFUnordEqual:
-    case SpvOpFOrdNotEqual:
-    case SpvOpFUnordNotEqual:
-    case SpvOpFOrdLessThan:
-    case SpvOpFUnordLessThan:
-    case SpvOpFOrdGreaterThan:
-    case SpvOpFUnordGreaterThan:
-    case SpvOpFOrdLessThanEqual:
-    case SpvOpFUnordLessThanEqual:
-    case SpvOpFOrdGreaterThanEqual:
-    case SpvOpFUnordGreaterThanEqual:
+    case spv::Op::OpPhi:
+    case spv::Op::OpCopyObject:
+    case spv::Op::OpConvertFToU:
+    case spv::Op::OpConvertFToS:
+    case spv::Op::OpConvertSToF:
+    case spv::Op::OpConvertUToF:
+    case spv::Op::OpUConvert:
+    case spv::Op::OpSConvert:
+    case spv::Op::OpFConvert:
+    case spv::Op::OpQuantizeToF16:
+    case spv::Op::OpVectorInsertDynamic:
+    case spv::Op::OpSNegate:
+    case spv::Op::OpFNegate:
+    case spv::Op::OpIAdd:
+    case spv::Op::OpFAdd:
+    case spv::Op::OpISub:
+    case spv::Op::OpFSub:
+    case spv::Op::OpIMul:
+    case spv::Op::OpFMul:
+    case spv::Op::OpUDiv:
+    case spv::Op::OpSDiv:
+    case spv::Op::OpFDiv:
+    case spv::Op::OpUMod:
+    case spv::Op::OpSRem:
+    case spv::Op::OpSMod:
+    case spv::Op::OpFRem:
+    case spv::Op::OpFMod:
+    case spv::Op::OpVectorTimesScalar:
+    case spv::Op::OpIAddCarry:
+    case spv::Op::OpISubBorrow:
+    case spv::Op::OpUMulExtended:
+    case spv::Op::OpSMulExtended:
+    case spv::Op::OpShiftRightLogical:
+    case spv::Op::OpShiftRightArithmetic:
+    case spv::Op::OpShiftLeftLogical:
+    case spv::Op::OpBitwiseOr:
+    case spv::Op::OpBitwiseAnd:
+    case spv::Op::OpNot:
+    case spv::Op::OpBitFieldInsert:
+    case spv::Op::OpBitFieldSExtract:
+    case spv::Op::OpBitFieldUExtract:
+    case spv::Op::OpBitReverse:
+    case spv::Op::OpBitCount:
+    case spv::Op::OpIsNan:
+    case spv::Op::OpIsInf:
+    case spv::Op::OpIsFinite:
+    case spv::Op::OpIsNormal:
+    case spv::Op::OpSignBitSet:
+    case spv::Op::OpLessOrGreater:
+    case spv::Op::OpOrdered:
+    case spv::Op::OpUnordered:
+    case spv::Op::OpLogicalEqual:
+    case spv::Op::OpLogicalNotEqual:
+    case spv::Op::OpLogicalOr:
+    case spv::Op::OpLogicalAnd:
+    case spv::Op::OpLogicalNot:
+    case spv::Op::OpSelect:
+    case spv::Op::OpIEqual:
+    case spv::Op::OpINotEqual:
+    case spv::Op::OpUGreaterThan:
+    case spv::Op::OpSGreaterThan:
+    case spv::Op::OpUGreaterThanEqual:
+    case spv::Op::OpSGreaterThanEqual:
+    case spv::Op::OpULessThan:
+    case spv::Op::OpSLessThan:
+    case spv::Op::OpULessThanEqual:
+    case spv::Op::OpSLessThanEqual:
+    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:
       return true;
     default:
       return false;
   }
 }
 
-bool spvOpcodeIsDebug(SpvOp opcode) {
+bool spvOpcodeIsDebug(spv::Op opcode) {
   switch (opcode) {
-    case SpvOpName:
-    case SpvOpMemberName:
-    case SpvOpSource:
-    case SpvOpSourceContinued:
-    case SpvOpSourceExtension:
-    case SpvOpString:
-    case SpvOpLine:
-    case SpvOpNoLine:
-    case SpvOpModuleProcessed:
+    case spv::Op::OpName:
+    case spv::Op::OpMemberName:
+    case spv::Op::OpSource:
+    case spv::Op::OpSourceContinued:
+    case spv::Op::OpSourceExtension:
+    case spv::Op::OpString:
+    case spv::Op::OpLine:
+    case spv::Op::OpNoLine:
+    case spv::Op::OpModuleProcessed:
       return true;
     default:
       return false;
   }
 }
 
-bool spvOpcodeIsCommutativeBinaryOperator(SpvOp opcode) {
+bool spvOpcodeIsCommutativeBinaryOperator(spv::Op opcode) {
   switch (opcode) {
-    case SpvOpPtrEqual:
-    case SpvOpPtrNotEqual:
-    case SpvOpIAdd:
-    case SpvOpFAdd:
-    case SpvOpIMul:
-    case SpvOpFMul:
-    case SpvOpDot:
-    case SpvOpIAddCarry:
-    case SpvOpUMulExtended:
-    case SpvOpSMulExtended:
-    case SpvOpBitwiseOr:
-    case SpvOpBitwiseXor:
-    case SpvOpBitwiseAnd:
-    case SpvOpOrdered:
-    case SpvOpUnordered:
-    case SpvOpLogicalEqual:
-    case SpvOpLogicalNotEqual:
-    case SpvOpLogicalOr:
-    case SpvOpLogicalAnd:
-    case SpvOpIEqual:
-    case SpvOpINotEqual:
-    case SpvOpFOrdEqual:
-    case SpvOpFUnordEqual:
-    case SpvOpFOrdNotEqual:
-    case SpvOpFUnordNotEqual:
+    case spv::Op::OpPtrEqual:
+    case spv::Op::OpPtrNotEqual:
+    case spv::Op::OpIAdd:
+    case spv::Op::OpFAdd:
+    case spv::Op::OpIMul:
+    case spv::Op::OpFMul:
+    case spv::Op::OpDot:
+    case spv::Op::OpIAddCarry:
+    case spv::Op::OpUMulExtended:
+    case spv::Op::OpSMulExtended:
+    case spv::Op::OpBitwiseOr:
+    case spv::Op::OpBitwiseXor:
+    case spv::Op::OpBitwiseAnd:
+    case spv::Op::OpOrdered:
+    case spv::Op::OpUnordered:
+    case spv::Op::OpLogicalEqual:
+    case spv::Op::OpLogicalNotEqual:
+    case spv::Op::OpLogicalOr:
+    case spv::Op::OpLogicalAnd:
+    case spv::Op::OpIEqual:
+    case spv::Op::OpINotEqual:
+    case spv::Op::OpFOrdEqual:
+    case spv::Op::OpFUnordEqual:
+    case spv::Op::OpFOrdNotEqual:
+    case spv::Op::OpFUnordNotEqual:
       return true;
     default:
       return false;
   }
 }
 
-bool spvOpcodeIsLinearAlgebra(SpvOp opcode) {
+bool spvOpcodeIsLinearAlgebra(spv::Op opcode) {
   switch (opcode) {
-    case SpvOpTranspose:
-    case SpvOpVectorTimesScalar:
-    case SpvOpMatrixTimesScalar:
-    case SpvOpVectorTimesMatrix:
-    case SpvOpMatrixTimesVector:
-    case SpvOpMatrixTimesMatrix:
-    case SpvOpOuterProduct:
-    case SpvOpDot:
+    case spv::Op::OpTranspose:
+    case spv::Op::OpVectorTimesScalar:
+    case spv::Op::OpMatrixTimesScalar:
+    case spv::Op::OpVectorTimesMatrix:
+    case spv::Op::OpMatrixTimesVector:
+    case spv::Op::OpMatrixTimesMatrix:
+    case spv::Op::OpOuterProduct:
+    case spv::Op::OpDot:
       return true;
     default:
       return false;
   }
 }
 
-bool spvOpcodeIsImageSample(const SpvOp opcode) {
+bool spvOpcodeIsImageSample(const spv::Op opcode) {
   switch (opcode) {
-    case SpvOpImageSampleImplicitLod:
-    case SpvOpImageSampleExplicitLod:
-    case SpvOpImageSampleDrefImplicitLod:
-    case SpvOpImageSampleDrefExplicitLod:
-    case SpvOpImageSampleProjImplicitLod:
-    case SpvOpImageSampleProjExplicitLod:
-    case SpvOpImageSampleProjDrefImplicitLod:
-    case SpvOpImageSampleProjDrefExplicitLod:
-    case SpvOpImageSparseSampleImplicitLod:
-    case SpvOpImageSparseSampleExplicitLod:
-    case SpvOpImageSparseSampleDrefImplicitLod:
-    case SpvOpImageSparseSampleDrefExplicitLod:
+    case spv::Op::OpImageSampleImplicitLod:
+    case spv::Op::OpImageSampleExplicitLod:
+    case spv::Op::OpImageSampleDrefImplicitLod:
+    case spv::Op::OpImageSampleDrefExplicitLod:
+    case spv::Op::OpImageSampleProjImplicitLod:
+    case spv::Op::OpImageSampleProjExplicitLod:
+    case spv::Op::OpImageSampleProjDrefImplicitLod:
+    case spv::Op::OpImageSampleProjDrefExplicitLod:
+    case spv::Op::OpImageSparseSampleImplicitLod:
+    case spv::Op::OpImageSparseSampleExplicitLod:
+    case spv::Op::OpImageSparseSampleDrefImplicitLod:
+    case spv::Op::OpImageSparseSampleDrefExplicitLod:
       return true;
     default:
       return false;
   }
 }
 
-std::vector<uint32_t> spvOpcodeMemorySemanticsOperandIndices(SpvOp opcode) {
+std::vector<uint32_t> spvOpcodeMemorySemanticsOperandIndices(spv::Op opcode) {
   switch (opcode) {
-    case SpvOpMemoryBarrier:
+    case spv::Op::OpMemoryBarrier:
       return {1};
-    case SpvOpAtomicStore:
-    case SpvOpControlBarrier:
-    case SpvOpAtomicFlagClear:
-    case SpvOpMemoryNamedBarrier:
+    case spv::Op::OpAtomicStore:
+    case spv::Op::OpControlBarrier:
+    case spv::Op::OpAtomicFlagClear:
+    case spv::Op::OpMemoryNamedBarrier:
       return {2};
-    case SpvOpAtomicLoad:
-    case SpvOpAtomicExchange:
-    case SpvOpAtomicIIncrement:
-    case SpvOpAtomicIDecrement:
-    case SpvOpAtomicIAdd:
-    case SpvOpAtomicFAddEXT:
-    case SpvOpAtomicISub:
-    case SpvOpAtomicSMin:
-    case SpvOpAtomicUMin:
-    case SpvOpAtomicSMax:
-    case SpvOpAtomicUMax:
-    case SpvOpAtomicAnd:
-    case SpvOpAtomicOr:
-    case SpvOpAtomicXor:
-    case SpvOpAtomicFlagTestAndSet:
+    case spv::Op::OpAtomicLoad:
+    case spv::Op::OpAtomicExchange:
+    case spv::Op::OpAtomicIIncrement:
+    case spv::Op::OpAtomicIDecrement:
+    case spv::Op::OpAtomicIAdd:
+    case spv::Op::OpAtomicFAddEXT:
+    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:
+    case spv::Op::OpAtomicFlagTestAndSet:
       return {4};
-    case SpvOpAtomicCompareExchange:
-    case SpvOpAtomicCompareExchangeWeak:
+    case spv::Op::OpAtomicCompareExchange:
+    case spv::Op::OpAtomicCompareExchangeWeak:
       return {4, 5};
     default:
       return {};
   }
 }
 
-bool spvOpcodeIsAccessChain(SpvOp opcode) {
+bool spvOpcodeIsAccessChain(spv::Op opcode) {
   switch (opcode) {
-    case SpvOpAccessChain:
-    case SpvOpInBoundsAccessChain:
-    case SpvOpPtrAccessChain:
-    case SpvOpInBoundsPtrAccessChain:
+    case spv::Op::OpAccessChain:
+    case spv::Op::OpInBoundsAccessChain:
+    case spv::Op::OpPtrAccessChain:
+    case spv::Op::OpInBoundsPtrAccessChain:
       return true;
     default:
       return false;
   }
 }
 
-bool spvOpcodeIsBit(SpvOp opcode) {
+bool spvOpcodeIsBit(spv::Op opcode) {
   switch (opcode) {
-    case SpvOpShiftRightLogical:
-    case SpvOpShiftRightArithmetic:
-    case SpvOpShiftLeftLogical:
-    case SpvOpBitwiseOr:
-    case SpvOpBitwiseXor:
-    case SpvOpBitwiseAnd:
-    case SpvOpNot:
-    case SpvOpBitReverse:
-    case SpvOpBitCount:
+    case spv::Op::OpShiftRightLogical:
+    case spv::Op::OpShiftRightArithmetic:
+    case spv::Op::OpShiftLeftLogical:
+    case spv::Op::OpBitwiseOr:
+    case spv::Op::OpBitwiseXor:
+    case spv::Op::OpBitwiseAnd:
+    case spv::Op::OpNot:
+    case spv::Op::OpBitReverse:
+    case spv::Op::OpBitCount:
       return true;
     default:
       return false;
diff --git a/source/opcode.h b/source/opcode.h
index 77a0bed..217aeb2 100644
--- a/source/opcode.h
+++ b/source/opcode.h
@@ -29,7 +29,7 @@
 const char* spvGeneratorStr(uint32_t generator);
 
 // Combines word_count and opcode enumerant in single word.
-uint32_t spvOpcodeMake(uint16_t word_count, SpvOp opcode);
+uint32_t spvOpcodeMake(uint16_t word_count, spv::Op opcode);
 
 // Splits word into into two constituent parts: word_count and opcode.
 void spvOpcodeSplit(const uint32_t word, uint16_t* word_count,
@@ -45,115 +45,118 @@
 // 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 SpvOp opcode,
+                                       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.
 // Writes copied instruction into *inst.
-void spvInstructionCopy(const uint32_t* words, const SpvOp opcode,
+void spvInstructionCopy(const uint32_t* words, const spv::Op opcode,
                         const uint16_t word_count,
                         const spv_endianness_t endian, spv_instruction_t* inst);
 
 // Determine if the given opcode is a scalar type. Returns zero if false,
 // non-zero otherwise.
-int32_t spvOpcodeIsScalarType(const SpvOp opcode);
+int32_t spvOpcodeIsScalarType(const spv::Op opcode);
 
 // Determines if the given opcode is a specialization constant. Returns zero if
 // false, non-zero otherwise.
-int32_t spvOpcodeIsSpecConstant(const SpvOp opcode);
+int32_t spvOpcodeIsSpecConstant(const spv::Op opcode);
 
 // Determines if the given opcode is a constant. Returns zero if false, non-zero
 // otherwise.
-int32_t spvOpcodeIsConstant(const SpvOp opcode);
+int32_t spvOpcodeIsConstant(const spv::Op opcode);
 
 // Returns true if the given opcode is a constant or undef.
-bool spvOpcodeIsConstantOrUndef(const SpvOp opcode);
+bool spvOpcodeIsConstantOrUndef(const spv::Op opcode);
 
 // Returns true if the given opcode is a scalar specialization constant.
-bool spvOpcodeIsScalarSpecConstant(const SpvOp opcode);
+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 SpvOp opcode);
+int32_t spvOpcodeIsComposite(const spv::Op opcode);
 
 // Determines if the given opcode results in a pointer when using the logical
 // addressing model. Returns zero if false, non-zero otherwise.
-int32_t spvOpcodeReturnsLogicalPointer(const SpvOp opcode);
+int32_t spvOpcodeReturnsLogicalPointer(const spv::Op opcode);
 
 // Returns whether the given opcode could result in a pointer or a variable
 // pointer when using the logical addressing model.
-bool spvOpcodeReturnsLogicalVariablePointer(const SpvOp opcode);
+bool spvOpcodeReturnsLogicalVariablePointer(const spv::Op opcode);
 
 // Determines if the given opcode generates a type. Returns zero if false,
 // non-zero otherwise.
-int32_t spvOpcodeGeneratesType(SpvOp opcode);
+int32_t spvOpcodeGeneratesType(spv::Op opcode);
 
 // Returns true if the opcode adds a decoration to an id.
-bool spvOpcodeIsDecoration(const SpvOp opcode);
+bool spvOpcodeIsDecoration(const spv::Op opcode);
 
 // Returns true if the opcode is a load from memory into a result id.  This
 // function only considers core instructions.
-bool spvOpcodeIsLoad(const SpvOp opcode);
+bool spvOpcodeIsLoad(const spv::Op opcode);
 
 // Returns true if the opcode is an atomic operation that uses the original
 // value.
-bool spvOpcodeIsAtomicWithLoad(const SpvOp opcode);
+bool spvOpcodeIsAtomicWithLoad(const spv::Op opcode);
 
 // Returns true if the opcode is an atomic operation.
-bool spvOpcodeIsAtomicOp(const SpvOp opcode);
+bool spvOpcodeIsAtomicOp(const spv::Op opcode);
 
 // Returns true if the given opcode is a branch instruction.
-bool spvOpcodeIsBranch(SpvOp opcode);
+bool spvOpcodeIsBranch(spv::Op opcode);
 
 // Returns true if the given opcode is a return instruction.
-bool spvOpcodeIsReturn(SpvOp opcode);
+bool spvOpcodeIsReturn(spv::Op opcode);
 
 // Returns true if the given opcode aborts execution.  To abort means that after
 // executing that instruction, no other instructions will be executed regardless
 // of the context in which the instruction appears.  Note that `OpUnreachable`
 // is considered an abort even if its behaviour is undefined.
-bool spvOpcodeIsAbort(SpvOp opcode);
+bool spvOpcodeIsAbort(spv::Op opcode);
 
 // Returns true if the given opcode is a return instruction or it aborts
 // execution.
-bool spvOpcodeIsReturnOrAbort(SpvOp opcode);
+bool spvOpcodeIsReturnOrAbort(spv::Op opcode);
 
 // Returns true if the given opcode is a basic block terminator.
-bool spvOpcodeIsBlockTerminator(SpvOp opcode);
+bool spvOpcodeIsBlockTerminator(spv::Op opcode);
 
 // Returns true if the given opcode always defines an opaque type.
-bool spvOpcodeIsBaseOpaqueType(SpvOp opcode);
+bool spvOpcodeIsBaseOpaqueType(spv::Op opcode);
 
 // Returns true if the given opcode is a non-uniform group operation.
-bool spvOpcodeIsNonUniformGroupOperation(SpvOp opcode);
+bool spvOpcodeIsNonUniformGroupOperation(spv::Op opcode);
 
 // Returns true if the opcode with vector inputs could be divided into a series
 // of independent scalar operations that would give the same result.
-bool spvOpcodeIsScalarizable(SpvOp opcode);
+bool spvOpcodeIsScalarizable(spv::Op opcode);
 
 // Returns true if the given opcode is a debug instruction.
-bool spvOpcodeIsDebug(SpvOp opcode);
+bool spvOpcodeIsDebug(spv::Op opcode);
 
 // Returns true for opcodes that are binary operators,
 // where the order of the operands is irrelevant.
-bool spvOpcodeIsCommutativeBinaryOperator(SpvOp opcode);
+bool spvOpcodeIsCommutativeBinaryOperator(spv::Op opcode);
 
 // Returns true for opcodes that represent linear algebra instructions.
-bool spvOpcodeIsLinearAlgebra(SpvOp opcode);
+bool spvOpcodeIsLinearAlgebra(spv::Op opcode);
 
 // Returns true for opcodes that represent image sample instructions.
-bool spvOpcodeIsImageSample(SpvOp opcode);
+bool spvOpcodeIsImageSample(spv::Op opcode);
 
 // Returns a vector containing the indices of the memory semantics <id>
 // operands for |opcode|.
-std::vector<uint32_t> spvOpcodeMemorySemanticsOperandIndices(SpvOp opcode);
+std::vector<uint32_t> spvOpcodeMemorySemanticsOperandIndices(spv::Op opcode);
 
 // Returns true for opcodes that represent access chain instructions.
-bool spvOpcodeIsAccessChain(SpvOp opcode);
+bool spvOpcodeIsAccessChain(spv::Op opcode);
 
 // Returns true for opcodes that represent bit instructions.
-bool spvOpcodeIsBit(SpvOp opcode);
+bool spvOpcodeIsBit(spv::Op opcode);
+
+// Gets the name of an instruction, without the "Op" prefix.
+const char* spvOpcodeString(const spv::Op opcode);
 
 #endif  // SOURCE_OPCODE_H_
diff --git a/source/operand.cpp b/source/operand.cpp
index 0c255a3..6577f8f 100644
--- a/source/operand.cpp
+++ b/source/operand.cpp
@@ -26,7 +26,6 @@
 #include "source/macro.h"
 #include "source/opcode.h"
 #include "source/spirv_constant.h"
-#include "source/spirv_target_env.h"
 
 // For now, assume unified1 contains up to SPIR-V 1.3 and no later
 // SPIR-V version.
@@ -48,7 +47,7 @@
   return SPV_SUCCESS;
 }
 
-spv_result_t spvOperandTableNameLookup(spv_target_env env,
+spv_result_t spvOperandTableNameLookup(spv_target_env,
                                        const spv_operand_table table,
                                        const spv_operand_type_t type,
                                        const char* name,
@@ -57,31 +56,18 @@
   if (!table) return SPV_ERROR_INVALID_TABLE;
   if (!name || !pEntry) return SPV_ERROR_INVALID_POINTER;
 
-  const auto version = spvVersionForTargetEnv(env);
   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
-      // 1. The target environment satisfies the minimal requirement of the
-      //    operand; or
-      // 2. There is at least one extension enabling this operand; or
-      // 3. There is at least one capability enabling this operand.
-      //
-      // Note that the second rule assumes the extension enabling this operand
-      // is indeed requested in the SPIR-V code; checking that should be
-      // validator's work.
+      // it is in the grammar.  It might not be *valid* to use,
+      // but that should be checked by the validator, not by parsing.
       if (nameLength == strlen(entry.name) &&
           !strncmp(entry.name, name, nameLength)) {
-        if ((version >= entry.minVersion && version <= entry.lastVersion) ||
-            entry.numExtensions > 0u || entry.numCapabilities > 0u) {
-          *pEntry = &entry;
-          return SPV_SUCCESS;
-        } else {
-          // if there is no extension/capability then the version is wrong
-          return SPV_ERROR_WRONG_VERSION;
-        }
+        *pEntry = &entry;
+        return SPV_SUCCESS;
       }
     }
   }
@@ -89,7 +75,7 @@
   return SPV_ERROR_INVALID_LOOKUP;
 }
 
-spv_result_t spvOperandTableValueLookup(spv_target_env env,
+spv_result_t spvOperandTableValueLookup(spv_target_env,
                                         const spv_operand_table table,
                                         const spv_operand_type_t type,
                                         const uint32_t value,
@@ -110,33 +96,15 @@
     const auto beg = group.entries;
     const auto end = group.entries + group.count;
 
-    // We need to loop here because there can exist multiple symbols for the
-    // same operand value, and they can be introduced in different target
-    // environments, which means they can have different minimal version
-    // requirements. For example, SubgroupEqMaskKHR can exist in any SPIR-V
-    // version as long as the SPV_KHR_shader_ballot extension is there; but
-    // starting from SPIR-V 1.3, SubgroupEqMask, which has the same numeric
-    // value as SubgroupEqMaskKHR, is available in core SPIR-V without extension
-    // 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->value == value; ++it) {
-      // We consider the current operand as available as long as
-      // 1. The target environment satisfies the minimal requirement of the
-      //    operand; or
-      // 2. There is at least one extension enabling this operand; or
-      // 3. There is at least one capability enabling this operand.
-      //
-      // Note that the second rule assumes the extension enabling this operand
-      // is indeed requested in the SPIR-V code; checking that should be
-      // validator's work.
-      if ((version >= it->minVersion && version <= it->lastVersion) ||
-          it->numExtensions > 0u || it->numCapabilities > 0u) {
-        *pEntry = it;
-        return SPV_SUCCESS;
-      }
+    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;
     }
   }
 
@@ -155,6 +123,7 @@
     case SPV_OPERAND_TYPE_LITERAL_INTEGER:
     case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER:
     case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_NUMBER:
+    case SPV_OPERAND_TYPE_LITERAL_FLOAT:
       return "literal number";
     case SPV_OPERAND_TYPE_OPTIONAL_TYPED_LITERAL_INTEGER:
       return "possibly multi-word literal integer";
@@ -236,6 +205,21 @@
     case SPV_OPERAND_TYPE_PACKED_VECTOR_FORMAT:
     case SPV_OPERAND_TYPE_OPTIONAL_PACKED_VECTOR_FORMAT:
       return "packed vector format";
+    case SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_OPERANDS:
+    case SPV_OPERAND_TYPE_OPTIONAL_COOPERATIVE_MATRIX_OPERANDS:
+      return "cooperative matrix operands";
+    case SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_LAYOUT:
+      return "cooperative matrix layout";
+    case SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_USE:
+      return "cooperative matrix use";
+    case SPV_OPERAND_TYPE_INITIALIZATION_MODE_QUALIFIER:
+      return "initialization mode qualifier";
+    case SPV_OPERAND_TYPE_HOST_ACCESS_QUALIFIER:
+      return "host access qualifier";
+    case SPV_OPERAND_TYPE_LOAD_CACHE_CONTROL:
+      return "load cache control";
+    case SPV_OPERAND_TYPE_STORE_CACHE_CONTROL:
+      return "store cache control";
     case SPV_OPERAND_TYPE_IMAGE:
     case SPV_OPERAND_TYPE_OPTIONAL_IMAGE:
       return "image";
@@ -325,6 +309,7 @@
   }
   switch (type) {
     case SPV_OPERAND_TYPE_LITERAL_INTEGER:
+    case SPV_OPERAND_TYPE_LITERAL_FLOAT:
     case SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER:
     case SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER:
     case SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER:
@@ -369,6 +354,12 @@
     case SPV_OPERAND_TYPE_QUANTIZATION_MODES:
     case SPV_OPERAND_TYPE_OVERFLOW_MODES:
     case SPV_OPERAND_TYPE_PACKED_VECTOR_FORMAT:
+    case SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_LAYOUT:
+    case SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_USE:
+    case SPV_OPERAND_TYPE_INITIALIZATION_MODE_QUALIFIER:
+    case SPV_OPERAND_TYPE_HOST_ACCESS_QUALIFIER:
+    case SPV_OPERAND_TYPE_LOAD_CACHE_CONTROL:
+    case SPV_OPERAND_TYPE_STORE_CACHE_CONTROL:
       return true;
     default:
       break;
@@ -387,6 +378,7 @@
     case SPV_OPERAND_TYPE_FRAGMENT_SHADING_RATE:
     case SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS:
     case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS:
+    case SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_OPERANDS:
       return true;
     default:
       break;
@@ -405,6 +397,7 @@
     case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_STRING:
     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_CIV:
       return true;
     default:
@@ -512,7 +505,7 @@
 }
 
 std::function<bool(unsigned)> spvOperandCanBeForwardDeclaredFunction(
-    SpvOp opcode) {
+    spv::Op opcode) {
   std::function<bool(unsigned index)> out;
   if (spvOpcodeGeneratesType(opcode)) {
     // All types can use forward pointers.
@@ -520,57 +513,57 @@
     return out;
   }
   switch (opcode) {
-    case SpvOpExecutionMode:
-    case SpvOpExecutionModeId:
-    case SpvOpEntryPoint:
-    case SpvOpName:
-    case SpvOpMemberName:
-    case SpvOpSelectionMerge:
-    case SpvOpDecorate:
-    case SpvOpMemberDecorate:
-    case SpvOpDecorateId:
-    case SpvOpDecorateStringGOOGLE:
-    case SpvOpMemberDecorateStringGOOGLE:
-    case SpvOpBranch:
-    case SpvOpLoopMerge:
+    case spv::Op::OpExecutionMode:
+    case spv::Op::OpExecutionModeId:
+    case spv::Op::OpEntryPoint:
+    case spv::Op::OpName:
+    case spv::Op::OpMemberName:
+    case spv::Op::OpSelectionMerge:
+    case spv::Op::OpDecorate:
+    case spv::Op::OpMemberDecorate:
+    case spv::Op::OpDecorateId:
+    case spv::Op::OpDecorateStringGOOGLE:
+    case spv::Op::OpMemberDecorateStringGOOGLE:
+    case spv::Op::OpBranch:
+    case spv::Op::OpLoopMerge:
       out = [](unsigned) { return true; };
       break;
-    case SpvOpGroupDecorate:
-    case SpvOpGroupMemberDecorate:
-    case SpvOpBranchConditional:
-    case SpvOpSwitch:
+    case spv::Op::OpGroupDecorate:
+    case spv::Op::OpGroupMemberDecorate:
+    case spv::Op::OpBranchConditional:
+    case spv::Op::OpSwitch:
       out = [](unsigned index) { return index != 0; };
       break;
 
-    case SpvOpFunctionCall:
+    case spv::Op::OpFunctionCall:
       // The Function parameter.
       out = [](unsigned index) { return index == 2; };
       break;
 
-    case SpvOpPhi:
+    case spv::Op::OpPhi:
       out = [](unsigned index) { return index > 1; };
       break;
 
-    case SpvOpEnqueueKernel:
+    case spv::Op::OpEnqueueKernel:
       // The Invoke parameter.
       out = [](unsigned index) { return index == 8; };
       break;
 
-    case SpvOpGetKernelNDrangeSubGroupCount:
-    case SpvOpGetKernelNDrangeMaxSubGroupSize:
+    case spv::Op::OpGetKernelNDrangeSubGroupCount:
+    case spv::Op::OpGetKernelNDrangeMaxSubGroupSize:
       // The Invoke parameter.
       out = [](unsigned index) { return index == 3; };
       break;
 
-    case SpvOpGetKernelWorkGroupSize:
-    case SpvOpGetKernelPreferredWorkGroupSizeMultiple:
+    case spv::Op::OpGetKernelWorkGroupSize:
+    case spv::Op::OpGetKernelPreferredWorkGroupSizeMultiple:
       // The Invoke parameter.
       out = [](unsigned index) { return index == 2; };
       break;
-    case SpvOpTypeForwardPointer:
+    case spv::Op::OpTypeForwardPointer:
       out = [](unsigned index) { return index == 0; };
       break;
-    case SpvOpTypeArray:
+    case spv::Op::OpTypeArray:
       out = [](unsigned index) { return index == 1; };
       break;
     default:
diff --git a/source/operand.h b/source/operand.h
index 7c73c6f..a3010d9 100644
--- a/source/operand.h
+++ b/source/operand.h
@@ -139,7 +139,7 @@
 // of the operand can be forward declared. This function will
 // used in the SSA validation stage of the pipeline
 std::function<bool(unsigned)> spvOperandCanBeForwardDeclaredFunction(
-    SpvOp opcode);
+    spv::Op opcode);
 
 // Takes the instruction key of a debug info extension instruction
 // and returns a function object that will return true if the index
diff --git a/source/opt/CMakeLists.txt b/source/opt/CMakeLists.txt
index 75fe4c0..6ebbfbf 100644
--- a/source/opt/CMakeLists.txt
+++ b/source/opt/CMakeLists.txt
@@ -15,6 +15,7 @@
   fix_func_call_arguments.h
   aggressive_dead_code_elim_pass.h
   amd_ext_to_khr.h
+  analyze_live_input_pass.h
   basic_block.h
   block_merge_pass.h
   block_merge_util.h
@@ -46,8 +47,9 @@
   eliminate_dead_constant_pass.h
   eliminate_dead_functions_pass.h
   eliminate_dead_functions_util.h
-  eliminate_dead_input_components_pass.h
+  eliminate_dead_io_components_pass.h
   eliminate_dead_members_pass.h
+  eliminate_dead_output_stores_pass.h
   empty_pass.h
   feature_manager.h
   fix_storage_class.h
@@ -69,11 +71,13 @@
   instruction_list.h
   instrument_pass.h
   interface_var_sroa.h
+  invocation_interlock_placement_pass.h
   interp_fixup_pass.h
   ir_builder.h
   ir_context.h
   ir_loader.h
   licm_pass.h
+  liveness.h
   local_access_chain_convert_pass.h
   local_redundancy_elimination.h
   local_single_block_elim_pass.h
@@ -118,7 +122,9 @@
   strip_debug_info_pass.h
   strip_nonsemantic_info_pass.h
   struct_cfg_analysis.h
+  switch_descriptorset_pass.h
   tree_iterator.h
+  trim_capabilities_pass.h
   type_manager.h
   types.h
   unify_const_pass.h
@@ -131,6 +137,7 @@
   fix_func_call_arguments.cpp
   aggressive_dead_code_elim_pass.cpp
   amd_ext_to_khr.cpp
+  analyze_live_input_pass.cpp
   basic_block.cpp
   block_merge_pass.cpp
   block_merge_util.cpp
@@ -162,8 +169,9 @@
   eliminate_dead_constant_pass.cpp
   eliminate_dead_functions_pass.cpp
   eliminate_dead_functions_util.cpp
-  eliminate_dead_input_components_pass.cpp
+  eliminate_dead_io_components_pass.cpp
   eliminate_dead_members_pass.cpp
+  eliminate_dead_output_stores_pass.cpp
   feature_manager.cpp
   fix_storage_class.cpp
   flatten_decoration_pass.cpp
@@ -184,10 +192,12 @@
   instruction_list.cpp
   instrument_pass.cpp
   interface_var_sroa.cpp
+  invocation_interlock_placement_pass.cpp
   interp_fixup_pass.cpp
   ir_context.cpp
   ir_loader.cpp
   licm_pass.cpp
+  liveness.cpp
   local_access_chain_convert_pass.cpp
   local_redundancy_elimination.cpp
   local_single_block_elim_pass.cpp
@@ -230,6 +240,8 @@
   strip_debug_info_pass.cpp
   strip_nonsemantic_info_pass.cpp
   struct_cfg_analysis.cpp
+  switch_descriptorset_pass.cpp
+  trim_capabilities_pass.cpp
   type_manager.cpp
   types.cpp
   unify_const_pass.cpp
@@ -265,10 +277,7 @@
 spvtools_check_symbol_exports(SPIRV-Tools-opt)
 
 if(ENABLE_SPIRV_TOOLS_INSTALL)
-  install(TARGETS SPIRV-Tools-opt EXPORT SPIRV-Tools-optTargets
-    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
-    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
-    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
+  install(TARGETS SPIRV-Tools-opt 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 7fa5c8a..55feca8 100644
--- a/source/opt/aggressive_dead_code_elim_pass.cpp
+++ b/source/opt/aggressive_dead_code_elim_pass.cpp
@@ -21,61 +21,58 @@
 #include <stack>
 
 #include "source/cfa.h"
-#include "source/latest_version_glsl_std_450_header.h"
 #include "source/opt/eliminate_dead_functions_util.h"
 #include "source/opt/ir_builder.h"
-#include "source/opt/iterator.h"
 #include "source/opt/reflect.h"
 #include "source/spirv_constant.h"
 #include "source/util/string_utils.h"
 
 namespace spvtools {
 namespace opt {
-
 namespace {
 
-const uint32_t kTypePointerStorageClassInIdx = 0;
-const uint32_t kEntryPointFunctionIdInIdx = 1;
-const uint32_t kSelectionMergeMergeBlockIdInIdx = 0;
-const uint32_t kLoopMergeContinueBlockIdInIdx = 1;
-const uint32_t kCopyMemoryTargetAddrInIdx = 0;
-const uint32_t kCopyMemorySourceAddrInIdx = 1;
-const uint32_t kLoadSourceAddrInIdx = 0;
-const uint32_t kDebugDeclareOperandVariableIndex = 5;
-const uint32_t kGlobalVariableVariableIndex = 12;
+constexpr uint32_t kTypePointerStorageClassInIdx = 0;
+constexpr uint32_t kEntryPointFunctionIdInIdx = 1;
+constexpr uint32_t kSelectionMergeMergeBlockIdInIdx = 0;
+constexpr uint32_t kLoopMergeContinueBlockIdInIdx = 1;
+constexpr uint32_t kCopyMemoryTargetAddrInIdx = 0;
+constexpr uint32_t kCopyMemorySourceAddrInIdx = 1;
+constexpr uint32_t kLoadSourceAddrInIdx = 0;
+constexpr uint32_t kDebugDeclareOperandVariableIndex = 5;
+constexpr uint32_t kGlobalVariableVariableIndex = 12;
 
 // Sorting functor to present annotation instructions in an easy-to-process
 // order. The functor orders by opcode first and falls back on unique id
 // ordering if both instructions have the same opcode.
 //
 // Desired priority:
-// SpvOpGroupDecorate
-// SpvOpGroupMemberDecorate
-// SpvOpDecorate
-// SpvOpMemberDecorate
-// SpvOpDecorateId
-// SpvOpDecorateStringGOOGLE
-// SpvOpDecorationGroup
+// spv::Op::OpGroupDecorate
+// spv::Op::OpGroupMemberDecorate
+// spv::Op::OpDecorate
+// spv::Op::OpMemberDecorate
+// spv::Op::OpDecorateId
+// spv::Op::OpDecorateStringGOOGLE
+// spv::Op::OpDecorationGroup
 struct DecorationLess {
   bool operator()(const Instruction* lhs, const Instruction* rhs) const {
     assert(lhs && rhs);
-    SpvOp lhsOp = lhs->opcode();
-    SpvOp rhsOp = rhs->opcode();
+    spv::Op lhsOp = lhs->opcode();
+    spv::Op rhsOp = rhs->opcode();
     if (lhsOp != rhsOp) {
 #define PRIORITY_CASE(opcode)                          \
   if (lhsOp == opcode && rhsOp != opcode) return true; \
   if (rhsOp == opcode && lhsOp != opcode) return false;
       // OpGroupDecorate and OpGroupMember decorate are highest priority to
       // eliminate dead targets early and simplify subsequent checks.
-      PRIORITY_CASE(SpvOpGroupDecorate)
-      PRIORITY_CASE(SpvOpGroupMemberDecorate)
-      PRIORITY_CASE(SpvOpDecorate)
-      PRIORITY_CASE(SpvOpMemberDecorate)
-      PRIORITY_CASE(SpvOpDecorateId)
-      PRIORITY_CASE(SpvOpDecorateStringGOOGLE)
+      PRIORITY_CASE(spv::Op::OpGroupDecorate)
+      PRIORITY_CASE(spv::Op::OpGroupMemberDecorate)
+      PRIORITY_CASE(spv::Op::OpDecorate)
+      PRIORITY_CASE(spv::Op::OpMemberDecorate)
+      PRIORITY_CASE(spv::Op::OpDecorateId)
+      PRIORITY_CASE(spv::Op::OpDecorateStringGOOGLE)
       // OpDecorationGroup is lowest priority to ensure use/def chains remain
       // usable for instructions that target this group.
-      PRIORITY_CASE(SpvOpDecorationGroup)
+      PRIORITY_CASE(spv::Op::OpDecorationGroup)
 #undef PRIORITY_CASE
     }
 
@@ -86,25 +83,26 @@
 
 }  // namespace
 
-bool AggressiveDCEPass::IsVarOfStorage(uint32_t varId, uint32_t storageClass) {
+bool AggressiveDCEPass::IsVarOfStorage(uint32_t varId,
+                                       spv::StorageClass storageClass) {
   if (varId == 0) return false;
   const Instruction* varInst = get_def_use_mgr()->GetDef(varId);
-  const SpvOp op = varInst->opcode();
-  if (op != SpvOpVariable) return false;
+  const spv::Op op = varInst->opcode();
+  if (op != spv::Op::OpVariable) return false;
   const uint32_t varTypeId = varInst->type_id();
   const Instruction* varTypeInst = get_def_use_mgr()->GetDef(varTypeId);
-  if (varTypeInst->opcode() != SpvOpTypePointer) return false;
-  return varTypeInst->GetSingleWordInOperand(kTypePointerStorageClassInIdx) ==
-         storageClass;
+  if (varTypeInst->opcode() != spv::Op::OpTypePointer) return false;
+  return spv::StorageClass(varTypeInst->GetSingleWordInOperand(
+             kTypePointerStorageClassInIdx)) == storageClass;
 }
 
 bool AggressiveDCEPass::IsLocalVar(uint32_t varId, Function* func) {
-  if (IsVarOfStorage(varId, SpvStorageClassFunction)) {
+  if (IsVarOfStorage(varId, spv::StorageClass::Function)) {
     return true;
   }
 
-  if (!IsVarOfStorage(varId, SpvStorageClassPrivate) &&
-      !IsVarOfStorage(varId, SpvStorageClassWorkgroup)) {
+  if (!IsVarOfStorage(varId, spv::StorageClass::Private) &&
+      !IsVarOfStorage(varId, spv::StorageClass::Workgroup)) {
     return false;
   }
 
@@ -122,21 +120,21 @@
     if (blk && blk->GetParent() != func) return;
 
     switch (user->opcode()) {
-      case SpvOpAccessChain:
-      case SpvOpInBoundsAccessChain:
-      case SpvOpCopyObject:
+      case spv::Op::OpAccessChain:
+      case spv::Op::OpInBoundsAccessChain:
+      case spv::Op::OpCopyObject:
         this->AddStores(func, user->result_id());
         break;
-      case SpvOpLoad:
+      case spv::Op::OpLoad:
         break;
-      case SpvOpCopyMemory:
-      case SpvOpCopyMemorySized:
+      case spv::Op::OpCopyMemory:
+      case spv::Op::OpCopyMemorySized:
         if (user->GetSingleWordInOperand(kCopyMemoryTargetAddrInIdx) == ptrId) {
           AddToWorklist(user);
         }
         break;
       // If default, assume it stores e.g. frexp, modf, function call
-      case SpvOpStore:
+      case spv::Op::OpStore:
       default:
         AddToWorklist(user);
         break;
@@ -154,11 +152,12 @@
   // Only allow NonSemantic.Shader.DebugInfo.100, 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() == SpvOpExtInstImport &&
+    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") {
+        (extension_name != "NonSemantic.Shader.DebugInfo.100") &&
+        (extension_name != "NonSemantic.DebugPrintf")) {
       return false;
     }
   }
@@ -172,11 +171,11 @@
     // This must be a decoration group. We go through annotations in a specific
     // order. So if this is not used by any group or group member decorates, it
     // is dead.
-    assert(tInst->opcode() == SpvOpDecorationGroup);
+    assert(tInst->opcode() == spv::Op::OpDecorationGroup);
     bool dead = true;
     get_def_use_mgr()->ForEachUser(tInst, [&dead](Instruction* user) {
-      if (user->opcode() == SpvOpGroupDecorate ||
-          user->opcode() == SpvOpGroupMemberDecorate)
+      if (user->opcode() == spv::Op::OpGroupDecorate ||
+          user->opcode() == spv::Op::OpGroupMemberDecorate)
         dead = false;
     });
     return dead;
@@ -197,7 +196,7 @@
 
 void AggressiveDCEPass::AddBranch(uint32_t labelId, BasicBlock* bp) {
   std::unique_ptr<Instruction> newBranch(
-      new Instruction(context(), SpvOpBranch, 0, 0,
+      new Instruction(context(), spv::Op::OpBranch, 0, 0,
                       {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {labelId}}}));
   context()->AnalyzeDefUse(&*newBranch);
   context()->set_instr_block(&*newBranch, bp);
@@ -206,8 +205,8 @@
 
 void AggressiveDCEPass::AddBreaksAndContinuesToWorklist(
     Instruction* mergeInst) {
-  assert(mergeInst->opcode() == SpvOpSelectionMerge ||
-         mergeInst->opcode() == SpvOpLoopMerge);
+  assert(mergeInst->opcode() == spv::Op::OpSelectionMerge ||
+         mergeInst->opcode() == spv::Op::OpLoopMerge);
 
   BasicBlock* header = context()->get_instr_block(mergeInst);
   const uint32_t mergeId = mergeInst->GetSingleWordInOperand(0);
@@ -223,7 +222,7 @@
     }
   });
 
-  if (mergeInst->opcode() != SpvOpLoopMerge) {
+  if (mergeInst->opcode() != spv::Op::OpLoopMerge) {
     return;
   }
 
@@ -231,26 +230,27 @@
   const uint32_t contId =
       mergeInst->GetSingleWordInOperand(kLoopMergeContinueBlockIdInIdx);
   get_def_use_mgr()->ForEachUser(contId, [&contId, this](Instruction* user) {
-    SpvOp op = user->opcode();
-    if (op == SpvOpBranchConditional || op == SpvOpSwitch) {
+    spv::Op op = user->opcode();
+    if (op == spv::Op::OpBranchConditional || op == spv::Op::OpSwitch) {
       // A conditional branch or switch can only be a continue if it does not
       // have a merge instruction or its merge block is not the continue block.
       Instruction* hdrMerge = GetMergeInstruction(user);
-      if (hdrMerge != nullptr && hdrMerge->opcode() == SpvOpSelectionMerge) {
+      if (hdrMerge != nullptr &&
+          hdrMerge->opcode() == spv::Op::OpSelectionMerge) {
         uint32_t hdrMergeId =
             hdrMerge->GetSingleWordInOperand(kSelectionMergeMergeBlockIdInIdx);
         if (hdrMergeId == contId) return;
         // Need to mark merge instruction too
         AddToWorklist(hdrMerge);
       }
-    } else if (op == SpvOpBranch) {
+    } else if (op == spv::Op::OpBranch) {
       // An unconditional branch can only be a continue if it is not
       // branching to its own merge block.
       BasicBlock* blk = context()->get_instr_block(user);
       Instruction* hdrBranch = GetHeaderBranch(blk);
       if (hdrBranch == nullptr) return;
       Instruction* hdrMerge = GetMergeInstruction(hdrBranch);
-      if (hdrMerge->opcode() == SpvOpLoopMerge) return;
+      if (hdrMerge->opcode() == spv::Op::OpLoopMerge) return;
       uint32_t hdrMergeId =
           hdrMerge->GetSingleWordInOperand(kSelectionMergeMergeBlockIdInIdx);
       if (contId == hdrMergeId) return;
@@ -277,11 +277,11 @@
     uint32_t merge_block_id = 0;
     (*bi)->ForEachInst([this, &modified, &merge_block_id](Instruction* inst) {
       if (IsLive(inst)) return;
-      if (inst->opcode() == SpvOpLabel) return;
+      if (inst->opcode() == spv::Op::OpLabel) return;
       // If dead instruction is selection merge, remember merge block
       // for new branch at end of block
-      if (inst->opcode() == SpvOpSelectionMerge ||
-          inst->opcode() == SpvOpLoopMerge)
+      if (inst->opcode() == spv::Op::OpSelectionMerge ||
+          inst->opcode() == spv::Op::OpLoopMerge)
         merge_block_id = inst->GetSingleWordInOperand(0);
       to_kill_.push_back(inst);
       modified = true;
@@ -295,19 +295,19 @@
       }
 
       auto merge_terminator = (*bi)->terminator();
-      if (merge_terminator->opcode() == SpvOpUnreachable) {
+      if (merge_terminator->opcode() == spv::Op::OpUnreachable) {
         // The merge was unreachable. This is undefined behaviour so just
         // return (or return an undef). Then mark the new return as live.
         auto func_ret_type_inst = get_def_use_mgr()->GetDef(func->type_id());
-        if (func_ret_type_inst->opcode() == SpvOpTypeVoid) {
-          merge_terminator->SetOpcode(SpvOpReturn);
+        if (func_ret_type_inst->opcode() == spv::Op::OpTypeVoid) {
+          merge_terminator->SetOpcode(spv::Op::OpReturn);
         } else {
           // Find an undef for the return value and make sure it gets kept by
           // the pass.
           auto undef_id = Type2Undef(func->type_id());
           auto undef = get_def_use_mgr()->GetDef(undef_id);
           live_insts_.Set(undef->unique_id());
-          merge_terminator->SetOpcode(SpvOpReturnValue);
+          merge_terminator->SetOpcode(spv::Op::OpReturnValue);
           merge_terminator->SetInOperands({{SPV_OPERAND_TYPE_ID, {undef_id}}});
           get_def_use_mgr()->AnalyzeInstUse(merge_terminator);
         }
@@ -369,11 +369,11 @@
     // We only care about OpDecorateId instructions because the are the only
     // decorations that will reference an id that will have to be kept live
     // because of that use.
-    if (dec->opcode() != SpvOpDecorateId) {
+    if (dec->opcode() != spv::Op::OpDecorateId) {
       continue;
     }
-    if (dec->GetSingleWordInOperand(1) ==
-        SpvDecorationHlslCounterBufferGOOGLE) {
+    if (spv::Decoration(dec->GetSingleWordInOperand(1)) ==
+        spv::Decoration::HlslCounterBufferGOOGLE) {
       // These decorations should not force the use id to be live.  It will be
       // removed if either the target or the in operand are dead.
       continue;
@@ -391,7 +391,7 @@
 }
 
 std::vector<uint32_t> AggressiveDCEPass::GetLoadedVariables(Instruction* inst) {
-  if (inst->opcode() == SpvOpFunctionCall) {
+  if (inst->opcode() == spv::Op::OpFunctionCall) {
     return GetLoadedVariablesFromFunctionCall(inst);
   }
   uint32_t var_id = GetLoadedVariableFromNonFunctionCalls(inst);
@@ -409,11 +409,11 @@
   }
 
   switch (inst->opcode()) {
-    case SpvOpLoad:
-    case SpvOpImageTexelPointer:
+    case spv::Op::OpLoad:
+    case spv::Op::OpImageTexelPointer:
       return GetVariableId(inst->GetSingleWordInOperand(kLoadSourceAddrInIdx));
-    case SpvOpCopyMemory:
-    case SpvOpCopyMemorySized:
+    case spv::Op::OpCopyMemory:
+    case spv::Op::OpCopyMemorySized:
       return GetVariableId(
           inst->GetSingleWordInOperand(kCopyMemorySourceAddrInIdx));
     default:
@@ -436,8 +436,11 @@
 
 std::vector<uint32_t> AggressiveDCEPass::GetLoadedVariablesFromFunctionCall(
     const Instruction* inst) {
-  assert(inst->opcode() == SpvOpFunctionCall);
+  assert(inst->opcode() == spv::Op::OpFunctionCall);
   std::vector<uint32_t> live_variables;
+  // NOTE: we should only be checking function call parameters here, not the
+  // function itself, however, `IsPtr` will trivially return false for
+  // OpFunction
   inst->ForEachInId([this, &live_variables](const uint32_t* operand_id) {
     if (!IsPtr(*operand_id)) return;
     uint32_t var_id = GetVariableId(*operand_id);
@@ -481,7 +484,7 @@
   // the loop, so the loop construct must be live.  We exclude the label because
   // it does not matter how many times it is executed.  This could be extended
   // to more instructions, but we will need it for now.
-  if (inst->opcode() != SpvOpLabel)
+  if (inst->opcode() != spv::Op::OpLabel)
     MarkLoopConstructAsLiveIfLoopHeader(basic_block);
 
   Instruction* next_branch_inst = GetBranchForNextHeader(basic_block);
@@ -491,8 +494,8 @@
     AddToWorklist(mergeInst);
   }
 
-  if (inst->opcode() == SpvOpLoopMerge ||
-      inst->opcode() == SpvOpSelectionMerge) {
+  if (inst->opcode() == spv::Op::OpLoopMerge ||
+      inst->opcode() == spv::Op::OpSelectionMerge) {
     AddBreaksAndContinuesToWorklist(inst);
   }
 }
@@ -529,27 +532,27 @@
   // cleaned up.
   for (auto& bi : structured_order) {
     for (auto ii = bi->begin(); ii != bi->end(); ++ii) {
-      SpvOp op = ii->opcode();
+      spv::Op op = ii->opcode();
       if (ii->IsBranch()) {
         continue;
       }
       switch (op) {
-        case SpvOpStore: {
+        case spv::Op::OpStore: {
           uint32_t var_id = 0;
           (void)GetPtr(&*ii, &var_id);
           if (!IsLocalVar(var_id, func)) AddToWorklist(&*ii);
         } break;
-        case SpvOpCopyMemory:
-        case SpvOpCopyMemorySized: {
+        case spv::Op::OpCopyMemory:
+        case spv::Op::OpCopyMemorySized: {
           uint32_t var_id = 0;
           uint32_t target_addr_id =
               ii->GetSingleWordInOperand(kCopyMemoryTargetAddrInIdx);
           (void)GetPtr(target_addr_id, &var_id);
           if (!IsLocalVar(var_id, func)) AddToWorklist(&*ii);
         } break;
-        case SpvOpLoopMerge:
-        case SpvOpSelectionMerge:
-        case SpvOpUnreachable:
+        case spv::Op::OpLoopMerge:
+        case spv::Op::OpSelectionMerge:
+        case spv::Op::OpUnreachable:
           break;
         default: {
           // Function calls, atomics, function params, function returns, etc.
@@ -578,8 +581,10 @@
         auto* var = get_def_use_mgr()->GetDef(entry.GetSingleWordInOperand(i));
         auto storage_class = var->GetSingleWordInOperand(0u);
         // Vulkan support outputs without an associated input, but not inputs
-        // without an associated output.
-        if (storage_class == SpvStorageClassOutput) {
+        // without an associated output. Don't remove outputs unless explicitly
+        // allowed.
+        if (!remove_outputs_ &&
+            spv::StorageClass(storage_class) == spv::StorageClass::Output) {
           AddToWorklist(var);
         }
       }
@@ -588,24 +593,29 @@
     }
   }
   for (auto& anno : get_module()->annotations()) {
-    if (anno.opcode() == SpvOpDecorate) {
+    if (anno.opcode() == spv::Op::OpDecorate) {
       // Keep workgroup size.
-      if (anno.GetSingleWordInOperand(1u) == SpvDecorationBuiltIn &&
-          anno.GetSingleWordInOperand(2u) == SpvBuiltInWorkgroupSize) {
+      if (spv::Decoration(anno.GetSingleWordInOperand(1u)) ==
+              spv::Decoration::BuiltIn &&
+          spv::BuiltIn(anno.GetSingleWordInOperand(2u)) ==
+              spv::BuiltIn::WorkgroupSize) {
         AddToWorklist(&anno);
       }
 
       if (context()->preserve_bindings()) {
         // Keep all bindings.
-        if ((anno.GetSingleWordInOperand(1u) == SpvDecorationDescriptorSet) ||
-            (anno.GetSingleWordInOperand(1u) == SpvDecorationBinding)) {
+        if ((spv::Decoration(anno.GetSingleWordInOperand(1u)) ==
+             spv::Decoration::DescriptorSet) ||
+            (spv::Decoration(anno.GetSingleWordInOperand(1u)) ==
+             spv::Decoration::Binding)) {
           AddToWorklist(&anno);
         }
       }
 
       if (context()->preserve_spec_constants()) {
         // Keep all specialization constant instructions
-        if (anno.GetSingleWordInOperand(1u) == SpvDecorationSpecId) {
+        if (spv::Decoration(anno.GetSingleWordInOperand(1u)) ==
+            spv::Decoration::SpecId) {
           AddToWorklist(&anno);
         }
       }
@@ -624,7 +634,7 @@
     debug_global_seen = true;
     dbg.ForEachInId([this](const uint32_t* iid) {
       Instruction* in_inst = get_def_use_mgr()->GetDef(*iid);
-      if (in_inst->opcode() == SpvOpVariable) return;
+      if (in_inst->opcode() == spv::Op::OpVariable) return;
       AddToWorklist(in_inst);
     });
   }
@@ -647,19 +657,19 @@
 Pass::Status AggressiveDCEPass::ProcessImpl() {
   // Current functionality assumes shader capability
   // TODO(greg-lunarg): Handle additional capabilities
-  if (!context()->get_feature_mgr()->HasCapability(SpvCapabilityShader))
+  if (!context()->get_feature_mgr()->HasCapability(spv::Capability::Shader))
     return Status::SuccessWithoutChange;
 
   // Current functionality assumes relaxed logical addressing (see
   // instruction.h)
   // TODO(greg-lunarg): Handle non-logical addressing
-  if (context()->get_feature_mgr()->HasCapability(SpvCapabilityAddresses))
+  if (context()->get_feature_mgr()->HasCapability(spv::Capability::Addresses))
     return Status::SuccessWithoutChange;
 
   // The variable pointer extension is no longer needed to use the capability,
   // so we have to look for the capability.
   if (context()->get_feature_mgr()->HasCapability(
-          SpvCapabilityVariablePointersStorageBuffer))
+          spv::Capability::VariablePointersStorageBuffer))
     return Status::SuccessWithoutChange;
 
   // If any extensions in the module are not explicitly supported,
@@ -743,7 +753,7 @@
   bool modified = false;
   Instruction* instruction = &*get_module()->debug2_begin();
   while (instruction) {
-    if (instruction->opcode() != SpvOpName) {
+    if (instruction->opcode() != spv::Op::OpName) {
       instruction = instruction->NextNode();
       continue;
     }
@@ -764,22 +774,22 @@
   std::sort(annotations.begin(), annotations.end(), DecorationLess());
   for (auto annotation : annotations) {
     switch (annotation->opcode()) {
-      case SpvOpDecorate:
-      case SpvOpMemberDecorate:
-      case SpvOpDecorateStringGOOGLE:
-      case SpvOpMemberDecorateStringGOOGLE:
+      case spv::Op::OpDecorate:
+      case spv::Op::OpMemberDecorate:
+      case spv::Op::OpDecorateStringGOOGLE:
+      case spv::Op::OpMemberDecorateStringGOOGLE:
         if (IsTargetDead(annotation)) {
           context()->KillInst(annotation);
           modified = true;
         }
         break;
-      case SpvOpDecorateId:
+      case spv::Op::OpDecorateId:
         if (IsTargetDead(annotation)) {
           context()->KillInst(annotation);
           modified = true;
         } else {
-          if (annotation->GetSingleWordInOperand(1) ==
-              SpvDecorationHlslCounterBufferGOOGLE) {
+          if (spv::Decoration(annotation->GetSingleWordInOperand(1)) ==
+              spv::Decoration::HlslCounterBufferGOOGLE) {
             // HlslCounterBuffer will reference an id other than the target.
             // If that id is dead, then the decoration can be removed as well.
             uint32_t counter_buffer_id = annotation->GetSingleWordInOperand(2);
@@ -792,7 +802,7 @@
           }
         }
         break;
-      case SpvOpGroupDecorate: {
+      case spv::Op::OpGroupDecorate: {
         // Go through the targets of this group decorate. Remove each dead
         // target. If all targets are dead, remove this decoration.
         bool dead = true;
@@ -818,7 +828,7 @@
         }
         break;
       }
-      case SpvOpGroupMemberDecorate: {
+      case spv::Op::OpGroupMemberDecorate: {
         // Go through the targets of this group member decorate. Remove each
         // dead target (and member index). If all targets are dead, remove this
         // decoration.
@@ -846,7 +856,7 @@
         }
         break;
       }
-      case SpvOpDecorationGroup:
+      case spv::Op::OpDecorationGroup:
         // By the time we hit decoration groups we've checked everything that
         // can target them. So if they have no uses they must be dead.
         if (get_def_use_mgr()->NumUsers(annotation) == 0) {
@@ -887,7 +897,7 @@
       // this live as it does not have a result id. This is a little too
       // conservative since it is not known if the structure type that needed
       // it is still live. TODO(greg-lunarg): Only save if needed.
-      if (val.opcode() == SpvOpTypeForwardPointer) {
+      if (val.opcode() == spv::Op::OpTypeForwardPointer) {
         uint32_t ptr_ty_id = val.GetSingleWordInOperand(0);
         Instruction* ptr_ty_inst = get_def_use_mgr()->GetDef(ptr_ty_id);
         if (IsLive(ptr_ty_inst)) continue;
@@ -978,6 +988,7 @@
       "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_vulkan_memory_model",
@@ -987,6 +998,9 @@
       "SPV_KHR_non_semantic_info",
       "SPV_KHR_uniform_group_instructions",
       "SPV_KHR_fragment_shader_barycentric",
+      "SPV_NV_bindless_texture",
+      "SPV_EXT_shader_atomic_float_add",
+      "SPV_EXT_fragment_shader_interlock",
   });
 }
 
@@ -1083,8 +1097,9 @@
 }
 
 bool AggressiveDCEPass::HasCall(Function* func) {
-  return !func->WhileEachInst(
-      [](Instruction* inst) { return inst->opcode() != SpvOpFunctionCall; });
+  return !func->WhileEachInst([](Instruction* inst) {
+    return inst->opcode() != spv::Op::OpFunctionCall;
+  });
 }
 
 void AggressiveDCEPass::MarkFirstBlockAsLive(Function* func) {
diff --git a/source/opt/aggressive_dead_code_elim_pass.h b/source/opt/aggressive_dead_code_elim_pass.h
index c1291dc..fbe08ad 100644
--- a/source/opt/aggressive_dead_code_elim_pass.h
+++ b/source/opt/aggressive_dead_code_elim_pass.h
@@ -44,8 +44,10 @@
   using GetBlocksFunction =
       std::function<std::vector<BasicBlock*>*(const BasicBlock*)>;
 
-  AggressiveDCEPass(bool preserve_interface = false)
-      : preserve_interface_(preserve_interface) {}
+  AggressiveDCEPass(bool preserve_interface = false,
+                    bool remove_outputs = false)
+      : preserve_interface_(preserve_interface),
+        remove_outputs_(remove_outputs) {}
 
   const char* name() const override { return "eliminate-dead-code-aggressive"; }
   Status Process() override;
@@ -63,9 +65,14 @@
   // is not allowed.
   bool preserve_interface_;
 
+  // Output variables can be removed from the interface if this is true.
+  // This is safe if the caller knows that the corresponding input variable
+  // in the following shader has been removed. It is false by default.
+  bool remove_outputs_;
+
   // Return true if |varId| is a variable of |storageClass|. |varId| must either
   // be 0 or the result of an instruction.
-  bool IsVarOfStorage(uint32_t varId, uint32_t storageClass);
+  bool IsVarOfStorage(uint32_t varId, spv::StorageClass storageClass);
 
   // Return true if the instance of the variable |varId| can only be access in
   // |func|.  For example, a function scope variable, or a private variable
diff --git a/source/opt/amd_ext_to_khr.cpp b/source/opt/amd_ext_to_khr.cpp
index dd9bafd..a314567 100644
--- a/source/opt/amd_ext_to_khr.cpp
+++ b/source/opt/amd_ext_to_khr.cpp
@@ -24,7 +24,6 @@
 
 namespace spvtools {
 namespace opt {
-
 namespace {
 
 enum AmdShaderBallotExtOpcodes {
@@ -136,19 +135,19 @@
 // Returns a folding rule that will replace the opcode with |opcode| and add
 // the capabilities required.  The folding rule assumes it is folding an
 // OpGroup*NonUniformAMD instruction from the SPV_AMD_shader_ballot extension.
-template <SpvOp new_opcode>
+template <spv::Op new_opcode>
 bool ReplaceGroupNonuniformOperationOpCode(
     IRContext* ctx, Instruction* inst,
     const std::vector<const analysis::Constant*>&) {
   switch (new_opcode) {
-    case SpvOpGroupNonUniformIAdd:
-    case SpvOpGroupNonUniformFAdd:
-    case SpvOpGroupNonUniformUMin:
-    case SpvOpGroupNonUniformSMin:
-    case SpvOpGroupNonUniformFMin:
-    case SpvOpGroupNonUniformUMax:
-    case SpvOpGroupNonUniformSMax:
-    case SpvOpGroupNonUniformFMax:
+    case spv::Op::OpGroupNonUniformIAdd:
+    case spv::Op::OpGroupNonUniformFAdd:
+    case spv::Op::OpGroupNonUniformUMin:
+    case spv::Op::OpGroupNonUniformSMin:
+    case spv::Op::OpGroupNonUniformFMin:
+    case spv::Op::OpGroupNonUniformUMax:
+    case spv::Op::OpGroupNonUniformSMax:
+    case spv::Op::OpGroupNonUniformFMax:
       break;
     default:
       assert(
@@ -157,21 +156,21 @@
   }
 
   switch (inst->opcode()) {
-    case SpvOpGroupIAddNonUniformAMD:
-    case SpvOpGroupFAddNonUniformAMD:
-    case SpvOpGroupUMinNonUniformAMD:
-    case SpvOpGroupSMinNonUniformAMD:
-    case SpvOpGroupFMinNonUniformAMD:
-    case SpvOpGroupUMaxNonUniformAMD:
-    case SpvOpGroupSMaxNonUniformAMD:
-    case SpvOpGroupFMaxNonUniformAMD:
+    case spv::Op::OpGroupIAddNonUniformAMD:
+    case spv::Op::OpGroupFAddNonUniformAMD:
+    case spv::Op::OpGroupUMinNonUniformAMD:
+    case spv::Op::OpGroupSMinNonUniformAMD:
+    case spv::Op::OpGroupFMinNonUniformAMD:
+    case spv::Op::OpGroupUMaxNonUniformAMD:
+    case spv::Op::OpGroupSMaxNonUniformAMD:
+    case spv::Op::OpGroupFMaxNonUniformAMD:
       break;
     default:
       assert(false &&
              "Should be replacing a group non uniform arithmetic operation.");
   }
 
-  ctx->AddCapability(SpvCapabilityGroupNonUniformArithmetic);
+  ctx->AddCapability(spv::Capability::GroupNonUniformArithmetic);
   inst->SetOpcode(new_opcode);
   return true;
 }
@@ -215,8 +214,8 @@
   analysis::ConstantManager* const_mgr = ctx->get_constant_mgr();
 
   ctx->AddExtension("SPV_KHR_shader_ballot");
-  ctx->AddCapability(SpvCapabilityGroupNonUniformBallot);
-  ctx->AddCapability(SpvCapabilityGroupNonUniformShuffle);
+  ctx->AddCapability(spv::Capability::GroupNonUniformBallot);
+  ctx->AddCapability(spv::Capability::GroupNonUniformShuffle);
 
   InstructionBuilder ir_builder(
       ctx, inst,
@@ -226,8 +225,8 @@
   uint32_t offset_id = inst->GetSingleWordInOperand(3);
 
   // Get the subgroup invocation id.
-  uint32_t var_id =
-      ctx->GetBuiltinInputVarId(SpvBuiltInSubgroupLocalInvocationId);
+  uint32_t var_id = ctx->GetBuiltinInputVarId(
+      uint32_t(spv::BuiltIn::SubgroupLocalInvocationId));
   assert(var_id != 0 && "Could not get SubgroupLocalInvocationId variable.");
   Instruction* var_inst = ctx->get_def_use_mgr()->GetDef(var_id);
   Instruction* var_ptr_type =
@@ -239,35 +238,38 @@
   uint32_t quad_mask = ir_builder.GetUintConstantId(3);
 
   // This gives the offset in the group of 4 of this invocation.
-  Instruction* quad_idx = ir_builder.AddBinaryOp(uint_type_id, SpvOpBitwiseAnd,
-                                                 id->result_id(), quad_mask);
+  Instruction* quad_idx = ir_builder.AddBinaryOp(
+      uint_type_id, spv::Op::OpBitwiseAnd, id->result_id(), quad_mask);
 
   // Get the invocation id of the first invocation in the group of 4.
-  Instruction* quad_ldr = ir_builder.AddBinaryOp(
-      uint_type_id, SpvOpBitwiseXor, id->result_id(), quad_idx->result_id());
+  Instruction* quad_ldr =
+      ir_builder.AddBinaryOp(uint_type_id, spv::Op::OpBitwiseXor,
+                             id->result_id(), quad_idx->result_id());
 
   // Get the offset of the target invocation from the offset vector.
   Instruction* my_offset =
-      ir_builder.AddBinaryOp(uint_type_id, SpvOpVectorExtractDynamic, offset_id,
-                             quad_idx->result_id());
+      ir_builder.AddBinaryOp(uint_type_id, spv::Op::OpVectorExtractDynamic,
+                             offset_id, quad_idx->result_id());
 
   // Determine the index of the invocation to read from.
-  Instruction* target_inv = ir_builder.AddBinaryOp(
-      uint_type_id, SpvOpIAdd, quad_ldr->result_id(), my_offset->result_id());
+  Instruction* target_inv =
+      ir_builder.AddBinaryOp(uint_type_id, spv::Op::OpIAdd,
+                             quad_ldr->result_id(), my_offset->result_id());
 
   // Do the group operations
   uint32_t uint_max_id = ir_builder.GetUintConstantId(0xFFFFFFFF);
-  uint32_t subgroup_scope = ir_builder.GetUintConstantId(SpvScopeSubgroup);
+  uint32_t subgroup_scope =
+      ir_builder.GetUintConstantId(uint32_t(spv::Scope::Subgroup));
   const auto* ballot_value_const = const_mgr->GetConstant(
       type_mgr->GetUIntVectorType(4),
       {uint_max_id, uint_max_id, uint_max_id, uint_max_id});
   Instruction* ballot_value =
       const_mgr->GetDefiningInstruction(ballot_value_const);
   Instruction* is_active = ir_builder.AddNaryOp(
-      type_mgr->GetBoolTypeId(), SpvOpGroupNonUniformBallotBitExtract,
+      type_mgr->GetBoolTypeId(), spv::Op::OpGroupNonUniformBallotBitExtract,
       {subgroup_scope, ballot_value->result_id(), target_inv->result_id()});
   Instruction* shuffle =
-      ir_builder.AddNaryOp(inst->type_id(), SpvOpGroupNonUniformShuffle,
+      ir_builder.AddNaryOp(inst->type_id(), spv::Op::OpGroupNonUniformShuffle,
                            {subgroup_scope, data_id, target_inv->result_id()});
 
   // Create the null constant to use in the select.
@@ -276,7 +278,7 @@
   Instruction* null_inst = const_mgr->GetDefiningInstruction(null);
 
   // Build the select.
-  inst->SetOpcode(SpvOpSelect);
+  inst->SetOpcode(spv::Op::OpSelect);
   Instruction::OperandList new_operands;
   new_operands.push_back({SPV_OPERAND_TYPE_ID, {is_active->result_id()}});
   new_operands.push_back({SPV_OPERAND_TYPE_ID, {shuffle->result_id()}});
@@ -327,8 +329,8 @@
   analysis::DefUseManager* def_use_mgr = ctx->get_def_use_mgr();
   analysis::ConstantManager* const_mgr = ctx->get_constant_mgr();
 
-  ctx->AddCapability(SpvCapabilityGroupNonUniformBallot);
-  ctx->AddCapability(SpvCapabilityGroupNonUniformShuffle);
+  ctx->AddCapability(spv::Capability::GroupNonUniformBallot);
+  ctx->AddCapability(spv::Capability::GroupNonUniformShuffle);
 
   InstructionBuilder ir_builder(
       ctx, inst,
@@ -338,7 +340,7 @@
   uint32_t data_id = inst->GetSingleWordInOperand(2);
 
   Instruction* mask_inst = def_use_mgr->GetDef(inst->GetSingleWordInOperand(3));
-  assert(mask_inst->opcode() == SpvOpConstantComposite &&
+  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.");
@@ -348,8 +350,8 @@
   uint32_t uint_z = mask_inst->GetSingleWordInOperand(2);
 
   // Get the subgroup invocation id.
-  uint32_t var_id =
-      ctx->GetBuiltinInputVarId(SpvBuiltInSubgroupLocalInvocationId);
+  uint32_t var_id = ctx->GetBuiltinInputVarId(
+      uint32_t(spv::BuiltIn::SubgroupLocalInvocationId));
   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);
@@ -361,28 +363,30 @@
 
   // Do the bitwise operations.
   uint32_t mask_extended = ir_builder.GetUintConstantId(0xFFFFFFE0);
-  Instruction* and_mask = ir_builder.AddBinaryOp(uint_type_id, SpvOpBitwiseOr,
-                                                 uint_x, mask_extended);
-  Instruction* and_result = ir_builder.AddBinaryOp(
-      uint_type_id, SpvOpBitwiseAnd, id->result_id(), and_mask->result_id());
+  Instruction* and_mask = ir_builder.AddBinaryOp(
+      uint_type_id, spv::Op::OpBitwiseOr, uint_x, mask_extended);
+  Instruction* and_result =
+      ir_builder.AddBinaryOp(uint_type_id, spv::Op::OpBitwiseAnd,
+                             id->result_id(), and_mask->result_id());
   Instruction* or_result = ir_builder.AddBinaryOp(
-      uint_type_id, SpvOpBitwiseOr, and_result->result_id(), uint_y);
+      uint_type_id, spv::Op::OpBitwiseOr, and_result->result_id(), uint_y);
   Instruction* target_inv = ir_builder.AddBinaryOp(
-      uint_type_id, SpvOpBitwiseXor, or_result->result_id(), uint_z);
+      uint_type_id, spv::Op::OpBitwiseXor, or_result->result_id(), uint_z);
 
   // Do the group operations
   uint32_t uint_max_id = ir_builder.GetUintConstantId(0xFFFFFFFF);
-  uint32_t subgroup_scope = ir_builder.GetUintConstantId(SpvScopeSubgroup);
+  uint32_t subgroup_scope =
+      ir_builder.GetUintConstantId(uint32_t(spv::Scope::Subgroup));
   const auto* ballot_value_const = const_mgr->GetConstant(
       type_mgr->GetUIntVectorType(4),
       {uint_max_id, uint_max_id, uint_max_id, uint_max_id});
   Instruction* ballot_value =
       const_mgr->GetDefiningInstruction(ballot_value_const);
   Instruction* is_active = ir_builder.AddNaryOp(
-      type_mgr->GetBoolTypeId(), SpvOpGroupNonUniformBallotBitExtract,
+      type_mgr->GetBoolTypeId(), spv::Op::OpGroupNonUniformBallotBitExtract,
       {subgroup_scope, ballot_value->result_id(), target_inv->result_id()});
   Instruction* shuffle =
-      ir_builder.AddNaryOp(inst->type_id(), SpvOpGroupNonUniformShuffle,
+      ir_builder.AddNaryOp(inst->type_id(), spv::Op::OpGroupNonUniformShuffle,
                            {subgroup_scope, data_id, target_inv->result_id()});
 
   // Create the null constant to use in the select.
@@ -391,7 +395,7 @@
   Instruction* null_inst = const_mgr->GetDefiningInstruction(null);
 
   // Build the select.
-  inst->SetOpcode(SpvOpSelect);
+  inst->SetOpcode(spv::Op::OpSelect);
   Instruction::OperandList new_operands;
   new_operands.push_back({SPV_OPERAND_TYPE_ID, {is_active->result_id()}});
   new_operands.push_back({SPV_OPERAND_TYPE_ID, {shuffle->result_id()}});
@@ -420,9 +424,9 @@
 // Also adding the capabilities and builtins that are needed.
 bool ReplaceWriteInvocation(IRContext* ctx, Instruction* inst,
                             const std::vector<const analysis::Constant*>&) {
-  uint32_t var_id =
-      ctx->GetBuiltinInputVarId(SpvBuiltInSubgroupLocalInvocationId);
-  ctx->AddCapability(SpvCapabilitySubgroupBallotKHR);
+  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.");
   Instruction* var_inst = ctx->get_def_use_mgr()->GetDef(var_id);
@@ -437,11 +441,11 @@
   analysis::Bool bool_type;
   uint32_t bool_type_id = ctx->get_type_mgr()->GetTypeInstruction(&bool_type);
   Instruction* cmp =
-      ir_builder.AddBinaryOp(bool_type_id, SpvOpIEqual, t->result_id(),
+      ir_builder.AddBinaryOp(bool_type_id, spv::Op::OpIEqual, t->result_id(),
                              inst->GetSingleWordInOperand(4));
 
   // Build a select.
-  inst->SetOpcode(SpvOpSelect);
+  inst->SetOpcode(spv::Op::OpSelect);
   Instruction::OperandList new_operands;
   new_operands.push_back({SPV_OPERAND_TYPE_ID, {cmp->result_id()}});
   new_operands.push_back(inst->GetInOperand(3));
@@ -479,14 +483,15 @@
   analysis::TypeManager* type_mgr = context->get_type_mgr();
   analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
 
-  uint32_t var_id = context->GetBuiltinInputVarId(SpvBuiltInSubgroupLtMask);
+  uint32_t var_id =
+      context->GetBuiltinInputVarId(uint32_t(spv::BuiltIn::SubgroupLtMask));
   assert(var_id != 0 && "Could not get SubgroupLtMask variable.");
-  context->AddCapability(SpvCapabilityGroupNonUniformBallot);
+  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());
   Instruction* var_type =
       def_use_mgr->GetDef(var_ptr_type->GetSingleWordInOperand(1));
-  assert(var_type->opcode() == SpvOpTypeVector &&
+  assert(var_type->opcode() == spv::Op::OpTypeVector &&
          "Variable is suppose to be a vector of 4 ints");
 
   // Get the type for the shuffle.
@@ -509,11 +514,12 @@
   Instruction* shuffle = ir_builder.AddVectorShuffle(
       shuffle_type_id, load->result_id(), load->result_id(), {0, 1});
   Instruction* bitcast = ir_builder.AddUnaryOp(
-      mask_inst->type_id(), SpvOpBitcast, shuffle->result_id());
-  Instruction* t = ir_builder.AddBinaryOp(mask_inst->type_id(), SpvOpBitwiseAnd,
-                                          bitcast->result_id(), mask_id);
+      mask_inst->type_id(), spv::Op::OpBitcast, shuffle->result_id());
+  Instruction* t =
+      ir_builder.AddBinaryOp(mask_inst->type_id(), spv::Op::OpBitwiseAnd,
+                             bitcast->result_id(), mask_id);
 
-  inst->SetOpcode(SpvOpBitCount);
+  inst->SetOpcode(spv::Op::OpBitCount);
   inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {t->result_id()}}});
   context->UpdateDefUse(inst);
   return true;
@@ -599,11 +605,11 @@
 
   // Negate the input values.
   Instruction* nx =
-      ir_builder.AddUnaryOp(float_type_id, SpvOpFNegate, x->result_id());
+      ir_builder.AddUnaryOp(float_type_id, spv::Op::OpFNegate, x->result_id());
   Instruction* ny =
-      ir_builder.AddUnaryOp(float_type_id, SpvOpFNegate, y->result_id());
+      ir_builder.AddUnaryOp(float_type_id, spv::Op::OpFNegate, y->result_id());
   Instruction* nz =
-      ir_builder.AddUnaryOp(float_type_id, SpvOpFNegate, z->result_id());
+      ir_builder.AddUnaryOp(float_type_id, spv::Op::OpFNegate, z->result_id());
 
   // Get the abolsute values of the inputs.
   Instruction* ax = ir_builder.AddNaryExtendedInstruction(
@@ -614,12 +620,12 @@
       float_type_id, glsl405_ext_inst_id, GLSLstd450FAbs, {z->result_id()});
 
   // Find which values are negative.  Used in later computations.
-  Instruction* is_z_neg = ir_builder.AddBinaryOp(bool_id, SpvOpFOrdLessThan,
-                                                 z->result_id(), f0_const_id);
-  Instruction* is_y_neg = ir_builder.AddBinaryOp(bool_id, SpvOpFOrdLessThan,
-                                                 y->result_id(), f0_const_id);
-  Instruction* is_x_neg = ir_builder.AddBinaryOp(bool_id, SpvOpFOrdLessThan,
-                                                 x->result_id(), f0_const_id);
+  Instruction* is_z_neg = ir_builder.AddBinaryOp(
+      bool_id, spv::Op::OpFOrdLessThan, z->result_id(), f0_const_id);
+  Instruction* is_y_neg = ir_builder.AddBinaryOp(
+      bool_id, spv::Op::OpFOrdLessThan, y->result_id(), f0_const_id);
+  Instruction* is_x_neg = ir_builder.AddBinaryOp(
+      bool_id, spv::Op::OpFOrdLessThan, x->result_id(), f0_const_id);
 
   // Compute cubema
   Instruction* amax_x_y = ir_builder.AddNaryExtendedInstruction(
@@ -628,19 +634,21 @@
   Instruction* amax = ir_builder.AddNaryExtendedInstruction(
       float_type_id, glsl405_ext_inst_id, GLSLstd450FMax,
       {az->result_id(), amax_x_y->result_id()});
-  Instruction* cubema = ir_builder.AddBinaryOp(float_type_id, SpvOpFMul,
+  Instruction* cubema = ir_builder.AddBinaryOp(float_type_id, spv::Op::OpFMul,
                                                f2_const_id, amax->result_id());
 
   // Do the comparisons needed for computing cubesc and cubetc.
   Instruction* is_z_max =
-      ir_builder.AddBinaryOp(bool_id, SpvOpFOrdGreaterThanEqual,
+      ir_builder.AddBinaryOp(bool_id, spv::Op::OpFOrdGreaterThanEqual,
                              az->result_id(), amax_x_y->result_id());
-  Instruction* not_is_z_max =
-      ir_builder.AddUnaryOp(bool_id, SpvOpLogicalNot, is_z_max->result_id());
-  Instruction* y_gr_x = ir_builder.AddBinaryOp(
-      bool_id, SpvOpFOrdGreaterThanEqual, ay->result_id(), ax->result_id());
-  Instruction* is_y_max = ir_builder.AddBinaryOp(
-      bool_id, SpvOpLogicalAnd, not_is_z_max->result_id(), y_gr_x->result_id());
+  Instruction* not_is_z_max = ir_builder.AddUnaryOp(
+      bool_id, spv::Op::OpLogicalNot, is_z_max->result_id());
+  Instruction* y_gr_x =
+      ir_builder.AddBinaryOp(bool_id, spv::Op::OpFOrdGreaterThanEqual,
+                             ay->result_id(), ax->result_id());
+  Instruction* is_y_max =
+      ir_builder.AddBinaryOp(bool_id, spv::Op::OpLogicalAnd,
+                             not_is_z_max->result_id(), y_gr_x->result_id());
 
   // Select the correct value for cubesc.
   Instruction* cubesc_case_1 = ir_builder.AddSelect(
@@ -667,10 +675,10 @@
   Instruction* denom = ir_builder.AddCompositeConstruct(
       v2_float_type_id, {cubema->result_id(), cubema->result_id()});
   Instruction* div = ir_builder.AddBinaryOp(
-      v2_float_type_id, SpvOpFDiv, cube->result_id(), denom->result_id());
+      v2_float_type_id, spv::Op::OpFDiv, cube->result_id(), denom->result_id());
 
   // Get the final result by adding 0.5 to |div|.
-  inst->SetOpcode(SpvOpFAdd);
+  inst->SetOpcode(spv::Op::OpFAdd);
   Instruction::OperandList new_operands;
   new_operands.push_back({SPV_OPERAND_TYPE_ID, {div->result_id()}});
   new_operands.push_back({SPV_OPERAND_TYPE_ID, {vec_const_id}});
@@ -752,22 +760,23 @@
       float_type_id, glsl405_ext_inst_id, GLSLstd450FAbs, {z->result_id()});
 
   // Find which values are negative.  Used in later computations.
-  Instruction* is_z_neg = ir_builder.AddBinaryOp(bool_id, SpvOpFOrdLessThan,
-                                                 z->result_id(), f0_const_id);
-  Instruction* is_y_neg = ir_builder.AddBinaryOp(bool_id, SpvOpFOrdLessThan,
-                                                 y->result_id(), f0_const_id);
-  Instruction* is_x_neg = ir_builder.AddBinaryOp(bool_id, SpvOpFOrdLessThan,
-                                                 x->result_id(), f0_const_id);
+  Instruction* is_z_neg = ir_builder.AddBinaryOp(
+      bool_id, spv::Op::OpFOrdLessThan, z->result_id(), f0_const_id);
+  Instruction* is_y_neg = ir_builder.AddBinaryOp(
+      bool_id, spv::Op::OpFOrdLessThan, y->result_id(), f0_const_id);
+  Instruction* is_x_neg = ir_builder.AddBinaryOp(
+      bool_id, spv::Op::OpFOrdLessThan, x->result_id(), f0_const_id);
 
   // 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()});
   Instruction* is_z_max =
-      ir_builder.AddBinaryOp(bool_id, SpvOpFOrdGreaterThanEqual,
+      ir_builder.AddBinaryOp(bool_id, spv::Op::OpFOrdGreaterThanEqual,
                              az->result_id(), amax_x_y->result_id());
-  Instruction* y_gr_x = ir_builder.AddBinaryOp(
-      bool_id, SpvOpFOrdGreaterThanEqual, ay->result_id(), ax->result_id());
+  Instruction* y_gr_x =
+      ir_builder.AddBinaryOp(bool_id, spv::Op::OpFOrdGreaterThanEqual,
+                             ay->result_id(), ax->result_id());
 
   // Get the value for each case.
   Instruction* case_z = ir_builder.AddSelect(
@@ -783,7 +792,7 @@
                            case_y->result_id(), case_x->result_id());
 
   // Get the final result by adding 0.5 to |div|.
-  inst->SetOpcode(SpvOpSelect);
+  inst->SetOpcode(spv::Op::OpSelect);
   Instruction::OperandList new_operands;
   new_operands.push_back({SPV_OPERAND_TYPE_ID, {is_z_max->result_id()}});
   new_operands.push_back({SPV_OPERAND_TYPE_ID, {case_z->result_id()}});
@@ -813,11 +822,12 @@
       ctx, inst,
       IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
   ctx->AddExtension("SPV_KHR_shader_clock");
-  ctx->AddCapability(SpvCapabilityShaderClockKHR);
+  ctx->AddCapability(spv::Capability::ShaderClockKHR);
 
-  inst->SetOpcode(SpvOpReadClockKHR);
+  inst->SetOpcode(spv::Op::OpReadClockKHR);
   Instruction::OperandList args;
-  uint32_t subgroup_scope_id = ir_builder.GetUintConstantId(SpvScopeSubgroup);
+  uint32_t subgroup_scope_id =
+      ir_builder.GetUintConstantId(uint32_t(spv::Scope::Subgroup));
   args.push_back({SPV_OPERAND_TYPE_ID, {subgroup_scope_id}});
   inst->SetInOperands(std::move(args));
   ctx->UpdateDefUse(inst);
@@ -831,22 +841,22 @@
 
  protected:
   virtual void AddFoldingRules() override {
-    rules_[SpvOpGroupIAddNonUniformAMD].push_back(
-        ReplaceGroupNonuniformOperationOpCode<SpvOpGroupNonUniformIAdd>);
-    rules_[SpvOpGroupFAddNonUniformAMD].push_back(
-        ReplaceGroupNonuniformOperationOpCode<SpvOpGroupNonUniformFAdd>);
-    rules_[SpvOpGroupUMinNonUniformAMD].push_back(
-        ReplaceGroupNonuniformOperationOpCode<SpvOpGroupNonUniformUMin>);
-    rules_[SpvOpGroupSMinNonUniformAMD].push_back(
-        ReplaceGroupNonuniformOperationOpCode<SpvOpGroupNonUniformSMin>);
-    rules_[SpvOpGroupFMinNonUniformAMD].push_back(
-        ReplaceGroupNonuniformOperationOpCode<SpvOpGroupNonUniformFMin>);
-    rules_[SpvOpGroupUMaxNonUniformAMD].push_back(
-        ReplaceGroupNonuniformOperationOpCode<SpvOpGroupNonUniformUMax>);
-    rules_[SpvOpGroupSMaxNonUniformAMD].push_back(
-        ReplaceGroupNonuniformOperationOpCode<SpvOpGroupNonUniformSMax>);
-    rules_[SpvOpGroupFMaxNonUniformAMD].push_back(
-        ReplaceGroupNonuniformOperationOpCode<SpvOpGroupNonUniformFMax>);
+    rules_[spv::Op::OpGroupIAddNonUniformAMD].push_back(
+        ReplaceGroupNonuniformOperationOpCode<spv::Op::OpGroupNonUniformIAdd>);
+    rules_[spv::Op::OpGroupFAddNonUniformAMD].push_back(
+        ReplaceGroupNonuniformOperationOpCode<spv::Op::OpGroupNonUniformFAdd>);
+    rules_[spv::Op::OpGroupUMinNonUniformAMD].push_back(
+        ReplaceGroupNonuniformOperationOpCode<spv::Op::OpGroupNonUniformUMin>);
+    rules_[spv::Op::OpGroupSMinNonUniformAMD].push_back(
+        ReplaceGroupNonuniformOperationOpCode<spv::Op::OpGroupNonUniformSMin>);
+    rules_[spv::Op::OpGroupFMinNonUniformAMD].push_back(
+        ReplaceGroupNonuniformOperationOpCode<spv::Op::OpGroupNonUniformFMin>);
+    rules_[spv::Op::OpGroupUMaxNonUniformAMD].push_back(
+        ReplaceGroupNonuniformOperationOpCode<spv::Op::OpGroupNonUniformUMax>);
+    rules_[spv::Op::OpGroupSMaxNonUniformAMD].push_back(
+        ReplaceGroupNonuniformOperationOpCode<spv::Op::OpGroupNonUniformSMax>);
+    rules_[spv::Op::OpGroupFMaxNonUniformAMD].push_back(
+        ReplaceGroupNonuniformOperationOpCode<spv::Op::OpGroupNonUniformFMax>);
 
     uint32_t extension_id =
         context()->module()->GetExtInstImportId("SPV_AMD_shader_ballot");
@@ -934,7 +944,7 @@
 
   std::vector<Instruction*> to_be_killed;
   for (Instruction& inst : context()->module()->extensions()) {
-    if (inst.opcode() == SpvOpExtension) {
+    if (inst.opcode() == spv::Op::OpExtension) {
       if (ext_to_remove.count(inst.GetInOperand(0).AsString()) != 0) {
         to_be_killed.push_back(&inst);
       }
@@ -942,7 +952,7 @@
   }
 
   for (Instruction& inst : context()->ext_inst_imports()) {
-    if (inst.opcode() == SpvOpExtInstImport) {
+    if (inst.opcode() == spv::Op::OpExtInstImport) {
       if (ext_to_remove.count(inst.GetInOperand(0).AsString()) != 0) {
         to_be_killed.push_back(&inst);
       }
diff --git a/source/opt/analyze_live_input_pass.cpp b/source/opt/analyze_live_input_pass.cpp
new file mode 100644
index 0000000..529e684
--- /dev/null
+++ b/source/opt/analyze_live_input_pass.cpp
@@ -0,0 +1,45 @@
+// Copyright (c) 2022 The Khronos Group Inc.
+// Copyright (c) 2022 LunarG Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/opt/analyze_live_input_pass.h"
+
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace opt {
+
+Pass::Status AnalyzeLiveInputPass::Process() {
+  // Current functionality assumes shader capability
+  if (!context()->get_feature_mgr()->HasCapability(spv::Capability::Shader))
+    return Status::SuccessWithoutChange;
+  Pass::Status status = DoLiveInputAnalysis();
+  return status;
+}
+
+Pass::Status AnalyzeLiveInputPass::DoLiveInputAnalysis() {
+  // Current functionality only supports frag, tesc, tese or geom shaders.
+  // Report failure for any other stage.
+  auto stage = context()->GetStage();
+  if (stage != spv::ExecutionModel::Fragment &&
+      stage != spv::ExecutionModel::TessellationControl &&
+      stage != spv::ExecutionModel::TessellationEvaluation &&
+      stage != spv::ExecutionModel::Geometry)
+    return Status::Failure;
+  context()->get_liveness_mgr()->GetLiveness(live_locs_, live_builtins_);
+  return Status::SuccessWithoutChange;
+}
+
+}  // namespace opt
+}  // namespace spvtools
diff --git a/source/opt/analyze_live_input_pass.h b/source/opt/analyze_live_input_pass.h
new file mode 100644
index 0000000..ab292ef
--- /dev/null
+++ b/source/opt/analyze_live_input_pass.h
@@ -0,0 +1,57 @@
+// Copyright (c) 2022 The Khronos Group Inc.
+// Copyright (c) 2022 LunarG Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_OPT_ANALYZE_LIVE_INPUT_H_
+#define SOURCE_OPT_ANALYZE_LIVE_INPUT_H_
+
+#include <unordered_set>
+
+#include "source/opt/pass.h"
+
+namespace spvtools {
+namespace opt {
+
+// See optimizer.hpp for documentation.
+class AnalyzeLiveInputPass : public Pass {
+ public:
+  explicit AnalyzeLiveInputPass(std::unordered_set<uint32_t>* live_locs,
+                                std::unordered_set<uint32_t>* live_builtins)
+      : live_locs_(live_locs), live_builtins_(live_builtins) {}
+
+  const char* name() const override { return "analyze-live-input"; }
+  Status Process() override;
+
+  // Return the mask of preserved Analyses.
+  IRContext::Analysis GetPreservedAnalyses() override {
+    return IRContext::kAnalysisDefUse |
+           IRContext::kAnalysisInstrToBlockMapping |
+           IRContext::kAnalysisCombinators | IRContext::kAnalysisCFG |
+           IRContext::kAnalysisDominatorAnalysis |
+           IRContext::kAnalysisLoopAnalysis | IRContext::kAnalysisNameMap |
+           IRContext::kAnalysisConstants | IRContext::kAnalysisTypes;
+  }
+
+ private:
+  // Do live input analysis
+  Status DoLiveInputAnalysis();
+
+  std::unordered_set<uint32_t>* live_locs_;
+  std::unordered_set<uint32_t>* live_builtins_;
+};
+
+}  // namespace opt
+}  // namespace spvtools
+
+#endif  // SOURCE_OPT_ANALYZE_LIVE_INPUT_H_
diff --git a/source/opt/basic_block.cpp b/source/opt/basic_block.cpp
index e82a744..a9fc8e2 100644
--- a/source/opt/basic_block.cpp
+++ b/source/opt/basic_block.cpp
@@ -16,20 +16,16 @@
 
 #include <ostream>
 
-#include "source/opt/function.h"
 #include "source/opt/ir_context.h"
-#include "source/opt/module.h"
 #include "source/opt/reflect.h"
 #include "source/util/make_unique.h"
 
 namespace spvtools {
 namespace opt {
 namespace {
-
-const uint32_t kLoopMergeContinueBlockIdInIdx = 1;
-const uint32_t kLoopMergeMergeBlockIdInIdx = 0;
-const uint32_t kSelectionMergeMergeBlockIdInIdx = 0;
-
+constexpr uint32_t kLoopMergeContinueBlockIdInIdx = 1;
+constexpr uint32_t kLoopMergeMergeBlockIdInIdx = 0;
+constexpr uint32_t kSelectionMergeMergeBlockIdInIdx = 0;
 }  // namespace
 
 BasicBlock* BasicBlock::Clone(IRContext* context) const {
@@ -58,7 +54,7 @@
   if (iter != cbegin()) {
     --iter;
     const auto opcode = iter->opcode();
-    if (opcode == SpvOpLoopMerge || opcode == SpvOpSelectionMerge) {
+    if (opcode == spv::Op::OpLoopMerge || opcode == spv::Op::OpSelectionMerge) {
       result = &*iter;
     }
   }
@@ -73,7 +69,7 @@
   if (iter != begin()) {
     --iter;
     const auto opcode = iter->opcode();
-    if (opcode == SpvOpLoopMerge || opcode == SpvOpSelectionMerge) {
+    if (opcode == spv::Op::OpLoopMerge || opcode == spv::Op::OpSelectionMerge) {
       result = &*iter;
     }
   }
@@ -82,7 +78,7 @@
 
 const Instruction* BasicBlock::GetLoopMergeInst() const {
   if (auto* merge = GetMergeInst()) {
-    if (merge->opcode() == SpvOpLoopMerge) {
+    if (merge->opcode() == spv::Op::OpLoopMerge) {
       return merge;
     }
   }
@@ -91,7 +87,7 @@
 
 Instruction* BasicBlock::GetLoopMergeInst() {
   if (auto* merge = GetMergeInst()) {
-    if (merge->opcode() == SpvOpLoopMerge) {
+    if (merge->opcode() == spv::Op::OpLoopMerge) {
       return merge;
     }
   }
@@ -100,7 +96,7 @@
 
 void BasicBlock::KillAllInsts(bool killLabel) {
   ForEachInst([killLabel](Instruction* ip) {
-    if (killLabel || ip->opcode() != SpvOpLabel) {
+    if (killLabel || ip->opcode() != spv::Op::OpLabel) {
       ip->context()->KillInst(ip);
     }
   });
@@ -118,10 +114,10 @@
     const std::function<bool(const uint32_t)>& f) const {
   const auto br = &insts_.back();
   switch (br->opcode()) {
-    case SpvOpBranch:
+    case spv::Op::OpBranch:
       return f(br->GetOperand(0).words[0]);
-    case SpvOpBranchConditional:
-    case SpvOpSwitch: {
+    case spv::Op::OpBranchConditional:
+    case spv::Op::OpSwitch: {
       bool is_first = true;
       return br->WhileEachInId([&is_first, &f](const uint32_t* idp) {
         if (!is_first) return f(*idp);
@@ -138,13 +134,13 @@
     const std::function<void(uint32_t*)>& f) {
   auto br = &insts_.back();
   switch (br->opcode()) {
-    case SpvOpBranch: {
+    case spv::Op::OpBranch: {
       uint32_t tmp_id = br->GetOperand(0).words[0];
       f(&tmp_id);
       if (tmp_id != br->GetOperand(0).words[0]) br->SetOperand(0, {tmp_id});
     } break;
-    case SpvOpBranchConditional:
-    case SpvOpSwitch: {
+    case spv::Op::OpBranchConditional:
+    case spv::Op::OpSwitch: {
       bool is_first = true;
       br->ForEachInId([&is_first, &f](uint32_t* idp) {
         if (!is_first) f(idp);
@@ -171,7 +167,8 @@
   --ii;
   if (ii == insts_.begin()) return;
   --ii;
-  if (ii->opcode() == SpvOpSelectionMerge || ii->opcode() == SpvOpLoopMerge) {
+  if (ii->opcode() == spv::Op::OpSelectionMerge ||
+      ii->opcode() == spv::Op::OpLoopMerge) {
     ii->ForEachInId([&f](const uint32_t* idp) { f(*idp); });
   }
 }
@@ -182,9 +179,9 @@
   uint32_t mbid = 0;
   if (merge_ii != cbegin()) {
     --merge_ii;
-    if (merge_ii->opcode() == SpvOpLoopMerge) {
+    if (merge_ii->opcode() == spv::Op::OpLoopMerge) {
       mbid = merge_ii->GetSingleWordInOperand(kLoopMergeMergeBlockIdInIdx);
-    } else if (merge_ii->opcode() == SpvOpSelectionMerge) {
+    } else if (merge_ii->opcode() == spv::Op::OpSelectionMerge) {
       mbid = merge_ii->GetSingleWordInOperand(kSelectionMergeMergeBlockIdInIdx);
     }
   }
@@ -204,7 +201,7 @@
   uint32_t cbid = 0;
   if (merge_ii != cbegin()) {
     --merge_ii;
-    if (merge_ii->opcode() == SpvOpLoopMerge) {
+    if (merge_ii->opcode() == spv::Op::OpLoopMerge) {
       cbid = merge_ii->GetSingleWordInOperand(kLoopMergeContinueBlockIdInIdx);
     }
   }
@@ -241,9 +238,9 @@
                                         iterator iter) {
   assert(!insts_.empty());
 
-  std::unique_ptr<BasicBlock> new_block_temp =
-      MakeUnique<BasicBlock>(MakeUnique<Instruction>(
-          context, SpvOpLabel, 0, label_id, std::initializer_list<Operand>{}));
+  std::unique_ptr<BasicBlock> new_block_temp = MakeUnique<BasicBlock>(
+      MakeUnique<Instruction>(context, spv::Op::OpLabel, 0, label_id,
+                              std::initializer_list<Operand>{}));
   BasicBlock* new_block = new_block_temp.get();
   function_->InsertBasicBlockAfter(std::move(new_block_temp), this);
 
diff --git a/source/opt/basic_block.h b/source/opt/basic_block.h
index dd3b2e2..24d5fce 100644
--- a/source/opt/basic_block.h
+++ b/source/opt/basic_block.h
@@ -319,7 +319,7 @@
   Instruction* inst = &insts_.front();
   while (inst != nullptr) {
     Instruction* next_instruction = inst->NextNode();
-    if (inst->opcode() != SpvOpPhi) break;
+    if (inst->opcode() != spv::Op::OpPhi) break;
     if (!inst->WhileEachInst(f, run_on_debug_line_insts)) return false;
     inst = next_instruction;
   }
diff --git a/source/opt/block_merge_pass.cpp b/source/opt/block_merge_pass.cpp
index ef7f31f..d6c33e5 100644
--- a/source/opt/block_merge_pass.cpp
+++ b/source/opt/block_merge_pass.cpp
@@ -16,11 +16,8 @@
 
 #include "source/opt/block_merge_pass.h"
 
-#include <vector>
-
 #include "source/opt/block_merge_util.h"
 #include "source/opt/ir_context.h"
-#include "source/opt/iterator.h"
 
 namespace spvtools {
 namespace opt {
diff --git a/source/opt/block_merge_util.cpp b/source/opt/block_merge_util.cpp
index 8ae8020..fe23e36 100644
--- a/source/opt/block_merge_util.cpp
+++ b/source/opt/block_merge_util.cpp
@@ -20,7 +20,6 @@
 namespace spvtools {
 namespace opt {
 namespace blockmergeutil {
-
 namespace {
 
 // Returns true if |block| contains a merge instruction.
@@ -34,14 +33,15 @@
 
 // Returns true if |id| is the merge target of a merge instruction.
 bool IsMerge(IRContext* context, uint32_t id) {
-  return !context->get_def_use_mgr()->WhileEachUse(id, [](Instruction* user,
-                                                          uint32_t index) {
-    SpvOp op = user->opcode();
-    if ((op == SpvOpLoopMerge || op == SpvOpSelectionMerge) && index == 0u) {
-      return false;
-    }
-    return true;
-  });
+  return !context->get_def_use_mgr()->WhileEachUse(
+      id, [](Instruction* user, uint32_t index) {
+        spv::Op op = user->opcode();
+        if ((op == spv::Op::OpLoopMerge || op == spv::Op::OpSelectionMerge) &&
+            index == 0u) {
+          return false;
+        }
+        return true;
+      });
 }
 
 // Returns true if |block| is the merge target of a merge instruction.
@@ -53,8 +53,8 @@
 bool IsContinue(IRContext* context, uint32_t id) {
   return !context->get_def_use_mgr()->WhileEachUse(
       id, [](Instruction* user, uint32_t index) {
-        SpvOp op = user->opcode();
-        if (op == SpvOpLoopMerge && index == 1u) {
+        spv::Op op = user->opcode();
+        if (op == spv::Op::OpLoopMerge && index == 1u) {
           return false;
         }
         return true;
@@ -82,7 +82,7 @@
   auto ii = block->end();
   --ii;
   Instruction* br = &*ii;
-  if (br->opcode() != SpvOpBranch) {
+  if (br->opcode() != spv::Op::OpBranch) {
     return false;
   }
 
@@ -119,12 +119,33 @@
     // The merge must be a loop merge because a selection merge cannot be
     // followed by an unconditional branch.
     BasicBlock* succ_block = context->get_instr_block(lab_id);
-    SpvOp succ_term_op = succ_block->terminator()->opcode();
-    assert(merge_inst->opcode() == SpvOpLoopMerge);
-    if (succ_term_op != SpvOpBranch && succ_term_op != SpvOpBranchConditional) {
+    spv::Op succ_term_op = succ_block->terminator()->opcode();
+    assert(merge_inst->opcode() == spv::Op::OpLoopMerge);
+    if (succ_term_op != spv::Op::OpBranch &&
+        succ_term_op != spv::Op::OpBranchConditional) {
       return false;
     }
   }
+
+  if (succ_is_merge || IsContinue(context, lab_id)) {
+    auto* struct_cfg = context->GetStructuredCFGAnalysis();
+    auto switch_block_id = struct_cfg->ContainingSwitch(block->id());
+    if (switch_block_id) {
+      auto switch_merge_id = struct_cfg->SwitchMergeBlock(switch_block_id);
+      const auto* switch_inst =
+          &*block->GetParent()->FindBlock(switch_block_id)->tail();
+      for (uint32_t i = 1; i < switch_inst->NumInOperands(); i += 2) {
+        auto target_id = switch_inst->GetSingleWordInOperand(i);
+        if (target_id == block->id() && target_id != switch_merge_id) {
+          // Case constructs must be structurally dominated by the OpSwitch.
+          // Since the successor is the merge/continue for another construct,
+          // merging the blocks would break that requirement.
+          return false;
+        }
+      }
+    }
+  }
+
   return true;
 }
 
@@ -150,6 +171,11 @@
   // sbi must follow bi in func's ordering.
   assert(sbi != func->end());
 
+  if (sbi->tail()->opcode() == spv::Op::OpSwitch &&
+      sbi->MergeBlockIdIfAny() != 0) {
+    context->InvalidateAnalyses(IRContext::Analysis::kAnalysisStructuredCFG);
+  }
+
   // Update the inst-to-block mapping for the instructions in sbi.
   for (auto& inst : *sbi) {
     context->set_instr_block(&inst, &*bi);
diff --git a/source/opt/ccp_pass.cpp b/source/opt/ccp_pass.cpp
index 5f85502..46bfc90 100644
--- a/source/opt/ccp_pass.cpp
+++ b/source/opt/ccp_pass.cpp
@@ -24,19 +24,15 @@
 
 #include "source/opt/fold.h"
 #include "source/opt/function.h"
-#include "source/opt/module.h"
 #include "source/opt/propagator.h"
 
 namespace spvtools {
 namespace opt {
-
 namespace {
-
 // This SSA id is never defined nor referenced in the IR.  It is a special ID
 // which represents varying values.  When an ID is found to have a varying
 // value, its entry in the |values_| table maps to kVaryingSSAId.
-const uint32_t kVaryingSSAId = std::numeric_limits<uint32_t>::max();
-
+constexpr uint32_t kVaryingSSAId = std::numeric_limits<uint32_t>::max();
 }  // namespace
 
 bool CCPPass::IsVaryingValue(uint32_t id) const { return id == kVaryingSSAId; }
@@ -136,7 +132,7 @@
 
   // If this is a copy operation, and the RHS is a known constant, assign its
   // value to the LHS.
-  if (instr->opcode() == SpvOpCopyObject) {
+  if (instr->opcode() == spv::Op::OpCopyObject) {
     uint32_t rhs_id = instr->GetSingleWordInOperand(0);
     auto it = values_.find(rhs_id);
     if (it != values_.end()) {
@@ -211,10 +207,10 @@
 
   *dest_bb = nullptr;
   uint32_t dest_label = 0;
-  if (instr->opcode() == SpvOpBranch) {
+  if (instr->opcode() == spv::Op::OpBranch) {
     // An unconditional jump always goes to its unique destination.
     dest_label = instr->GetSingleWordInOperand(0);
-  } else if (instr->opcode() == SpvOpBranchConditional) {
+  } else if (instr->opcode() == spv::Op::OpBranchConditional) {
     // For a conditional branch, determine whether the predicate selector has a
     // known value in |values_|.  If it does, set the destination block
     // according to the selector's boolean value.
@@ -243,7 +239,7 @@
     // For an OpSwitch, extract the value taken by the switch selector and check
     // which of the target literals it matches.  The branch associated with that
     // literal is the taken branch.
-    assert(instr->opcode() == SpvOpSwitch);
+    assert(instr->opcode() == spv::Op::OpSwitch);
     if (instr->GetOperand(0).words.size() != 1) {
       // If the selector is wider than 32-bits, return varying. TODO(dnovillo):
       // Add support for wider constants.
@@ -290,7 +286,7 @@
 SSAPropagator::PropStatus CCPPass::VisitInstruction(Instruction* instr,
                                                     BasicBlock** dest_bb) {
   *dest_bb = nullptr;
-  if (instr->opcode() == SpvOpPhi) {
+  if (instr->opcode() == spv::Op::OpPhi) {
     return VisitPhi(instr);
   } else if (instr->IsBranch()) {
     return VisitBranch(instr, dest_bb);
diff --git a/source/opt/cfg.cpp b/source/opt/cfg.cpp
index a0248d5..4c4bb25 100644
--- a/source/opt/cfg.cpp
+++ b/source/opt/cfg.cpp
@@ -29,16 +29,16 @@
 using cbb_ptr = const opt::BasicBlock*;
 
 // Universal Limit of ResultID + 1
-const int kMaxResultId = 0x400000;
+constexpr int kMaxResultId = 0x400000;
 
 }  // namespace
 
 CFG::CFG(Module* module)
     : module_(module),
       pseudo_entry_block_(std::unique_ptr<Instruction>(
-          new Instruction(module->context(), SpvOpLabel, 0, 0, {}))),
+          new Instruction(module->context(), spv::Op::OpLabel, 0, 0, {}))),
       pseudo_exit_block_(std::unique_ptr<Instruction>(new Instruction(
-          module->context(), SpvOpLabel, 0, kMaxResultId, {}))) {
+          module->context(), spv::Op::OpLabel, 0, kMaxResultId, {}))) {
   for (auto& fn : *module) {
     for (auto& blk : fn) {
       RegisterBlock(&blk);
@@ -81,7 +81,7 @@
                                  BasicBlock* end,
                                  std::list<BasicBlock*>* order) {
   assert(module_->context()->get_feature_mgr()->HasCapability(
-             SpvCapabilityShader) &&
+             spv::Capability::Shader) &&
          "This only works on structured control flow");
 
   // Compute structured successors and do DFS.
@@ -228,7 +228,7 @@
   // Create the new header bb basic bb.
   // Leave the phi instructions behind.
   auto iter = bb->begin();
-  while (iter->opcode() == SpvOpPhi) {
+  while (iter->opcode() == spv::Op::OpPhi) {
     ++iter;
   }
 
@@ -304,7 +304,7 @@
       context, bb,
       IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
   bb->AddInstruction(
-      MakeUnique<Instruction>(context, SpvOpBranch, 0, 0,
+      MakeUnique<Instruction>(context, spv::Op::OpBranch, 0, 0,
                               std::initializer_list<Operand>{
                                   {SPV_OPERAND_TYPE_ID, {new_header->id()}}}));
   context->AnalyzeUses(bb->terminator());
diff --git a/source/opt/cfg_cleanup_pass.cpp b/source/opt/cfg_cleanup_pass.cpp
index 6d48637..26fed89 100644
--- a/source/opt/cfg_cleanup_pass.cpp
+++ b/source/opt/cfg_cleanup_pass.cpp
@@ -16,13 +16,9 @@
 // constructs (e.g., unreachable basic blocks, empty control flow structures,
 // etc)
 
-#include <queue>
-#include <unordered_set>
-
 #include "source/opt/cfg_cleanup_pass.h"
 
 #include "source/opt/function.h"
-#include "source/opt/module.h"
 
 namespace spvtools {
 namespace opt {
diff --git a/source/opt/code_sink.cpp b/source/opt/code_sink.cpp
index cd77797..9023179 100644
--- a/source/opt/code_sink.cpp
+++ b/source/opt/code_sink.cpp
@@ -14,11 +14,9 @@
 
 #include "code_sink.h"
 
-#include <set>
 #include <vector>
 
 #include "source/opt/instruction.h"
-#include "source/opt/ir_builder.h"
 #include "source/opt/ir_context.h"
 #include "source/util/bit_vector.h"
 
@@ -50,7 +48,8 @@
 }
 
 bool CodeSinkingPass::SinkInstruction(Instruction* inst) {
-  if (inst->opcode() != SpvOpLoad && inst->opcode() != SpvOpAccessChain) {
+  if (inst->opcode() != spv::Op::OpLoad &&
+      inst->opcode() != spv::Op::OpAccessChain) {
     return false;
   }
 
@@ -60,7 +59,7 @@
 
   if (BasicBlock* target_bb = FindNewBasicBlockFor(inst)) {
     Instruction* pos = &*target_bb->begin();
-    while (pos->opcode() == SpvOpPhi) {
+    while (pos->opcode() == spv::Op::OpPhi) {
       pos = pos->NextNode();
     }
 
@@ -79,7 +78,7 @@
   std::unordered_set<uint32_t> bbs_with_uses;
   get_def_use_mgr()->ForEachUse(
       inst, [&bbs_with_uses, this](Instruction* use, uint32_t idx) {
-        if (use->opcode() != SpvOpPhi) {
+        if (use->opcode() != spv::Op::OpPhi) {
           BasicBlock* use_bb = context()->get_instr_block(use);
           if (use_bb) {
             bbs_with_uses.insert(use_bb->id());
@@ -99,7 +98,7 @@
     // of succ_bb, then |inst| can be moved to succ_bb.  If succ_bb, has move
     // then one predecessor, then moving |inst| into succ_bb could cause it to
     // be executed more often, so the search has to stop.
-    if (bb->terminator()->opcode() == SpvOpBranch) {
+    if (bb->terminator()->opcode() == spv::Op::OpBranch) {
       uint32_t succ_bb_id = bb->terminator()->GetSingleWordInOperand(0);
       if (cfg()->preds(succ_bb_id).size() == 1) {
         bb = context()->get_instr_block(succ_bb_id);
@@ -113,7 +112,8 @@
     // instruction or an OpLoopMerge, then it is a break or continue.  We could
     // figure it out, but not worth doing it now.
     Instruction* merge_inst = bb->GetMergeInst();
-    if (merge_inst == nullptr || merge_inst->opcode() != SpvOpSelectionMerge) {
+    if (merge_inst == nullptr ||
+        merge_inst->opcode() != spv::Op::OpSelectionMerge) {
       break;
     }
 
@@ -173,7 +173,7 @@
   }
 
   Instruction* base_ptr = inst->GetBaseAddress();
-  if (base_ptr->opcode() != SpvOpVariable) {
+  if (base_ptr->opcode() != spv::Op::OpVariable) {
     return true;
   }
 
@@ -185,7 +185,8 @@
     return true;
   }
 
-  if (base_ptr->GetSingleWordInOperand(0) != SpvStorageClassUniform) {
+  if (spv::StorageClass(base_ptr->GetSingleWordInOperand(0)) !=
+      spv::StorageClass::Uniform) {
     return true;
   }
 
@@ -200,41 +201,41 @@
   bool has_sync = false;
   get_module()->ForEachInst([this, &has_sync](Instruction* inst) {
     switch (inst->opcode()) {
-      case SpvOpMemoryBarrier: {
+      case spv::Op::OpMemoryBarrier: {
         uint32_t mem_semantics_id = inst->GetSingleWordInOperand(1);
         if (IsSyncOnUniform(mem_semantics_id)) {
           has_sync = true;
         }
         break;
       }
-      case SpvOpControlBarrier:
-      case SpvOpAtomicLoad:
-      case SpvOpAtomicStore:
-      case SpvOpAtomicExchange:
-      case SpvOpAtomicIIncrement:
-      case SpvOpAtomicIDecrement:
-      case SpvOpAtomicIAdd:
-      case SpvOpAtomicFAddEXT:
-      case SpvOpAtomicISub:
-      case SpvOpAtomicSMin:
-      case SpvOpAtomicUMin:
-      case SpvOpAtomicFMinEXT:
-      case SpvOpAtomicSMax:
-      case SpvOpAtomicUMax:
-      case SpvOpAtomicFMaxEXT:
-      case SpvOpAtomicAnd:
-      case SpvOpAtomicOr:
-      case SpvOpAtomicXor:
-      case SpvOpAtomicFlagTestAndSet:
-      case SpvOpAtomicFlagClear: {
+      case spv::Op::OpControlBarrier:
+      case spv::Op::OpAtomicLoad:
+      case spv::Op::OpAtomicStore:
+      case spv::Op::OpAtomicExchange:
+      case spv::Op::OpAtomicIIncrement:
+      case spv::Op::OpAtomicIDecrement:
+      case spv::Op::OpAtomicIAdd:
+      case spv::Op::OpAtomicFAddEXT:
+      case spv::Op::OpAtomicISub:
+      case spv::Op::OpAtomicSMin:
+      case spv::Op::OpAtomicUMin:
+      case spv::Op::OpAtomicFMinEXT:
+      case spv::Op::OpAtomicSMax:
+      case spv::Op::OpAtomicUMax:
+      case spv::Op::OpAtomicFMaxEXT:
+      case spv::Op::OpAtomicAnd:
+      case spv::Op::OpAtomicOr:
+      case spv::Op::OpAtomicXor:
+      case spv::Op::OpAtomicFlagTestAndSet:
+      case spv::Op::OpAtomicFlagClear: {
         uint32_t mem_semantics_id = inst->GetSingleWordInOperand(2);
         if (IsSyncOnUniform(mem_semantics_id)) {
           has_sync = true;
         }
         break;
       }
-      case SpvOpAtomicCompareExchange:
-      case SpvOpAtomicCompareExchangeWeak:
+      case spv::Op::OpAtomicCompareExchange:
+      case spv::Op::OpAtomicCompareExchangeWeak:
         if (IsSyncOnUniform(inst->GetSingleWordInOperand(2)) ||
             IsSyncOnUniform(inst->GetSingleWordInOperand(3))) {
           has_sync = true;
@@ -259,28 +260,30 @@
 
   // If it does not affect uniform memory, then it is does not apply to uniform
   // memory.
-  if ((mem_semantics_int & SpvMemorySemanticsUniformMemoryMask) == 0) {
+  if ((mem_semantics_int & uint32_t(spv::MemorySemanticsMask::UniformMemory)) ==
+      0) {
     return false;
   }
 
   // Check if there is an acquire or release.  If so not, this it does not add
   // any memory constraints.
-  return (mem_semantics_int & (SpvMemorySemanticsAcquireMask |
-                               SpvMemorySemanticsAcquireReleaseMask |
-                               SpvMemorySemanticsReleaseMask)) != 0;
+  return (mem_semantics_int &
+          uint32_t(spv::MemorySemanticsMask::Acquire |
+                   spv::MemorySemanticsMask::AcquireRelease |
+                   spv::MemorySemanticsMask::Release)) != 0;
 }
 
 bool CodeSinkingPass::HasPossibleStore(Instruction* var_inst) {
-  assert(var_inst->opcode() == SpvOpVariable ||
-         var_inst->opcode() == SpvOpAccessChain ||
-         var_inst->opcode() == SpvOpPtrAccessChain);
+  assert(var_inst->opcode() == spv::Op::OpVariable ||
+         var_inst->opcode() == spv::Op::OpAccessChain ||
+         var_inst->opcode() == spv::Op::OpPtrAccessChain);
 
   return get_def_use_mgr()->WhileEachUser(var_inst, [this](Instruction* use) {
     switch (use->opcode()) {
-      case SpvOpStore:
+      case spv::Op::OpStore:
         return true;
-      case SpvOpAccessChain:
-      case SpvOpPtrAccessChain:
+      case spv::Op::OpAccessChain:
+      case spv::Op::OpPtrAccessChain:
         return HasPossibleStore(use);
       default:
         return false;
diff --git a/source/opt/combine_access_chains.cpp b/source/opt/combine_access_chains.cpp
index 142897a..99ec796 100644
--- a/source/opt/combine_access_chains.cpp
+++ b/source/opt/combine_access_chains.cpp
@@ -44,10 +44,10 @@
       function.entry().get(), [&modified, this](BasicBlock* block) {
         block->ForEachInst([&modified, this](Instruction* inst) {
           switch (inst->opcode()) {
-            case SpvOpAccessChain:
-            case SpvOpInBoundsAccessChain:
-            case SpvOpPtrAccessChain:
-            case SpvOpInBoundsPtrAccessChain:
+            case spv::Op::OpAccessChain:
+            case spv::Op::OpInBoundsAccessChain:
+            case spv::Op::OpPtrAccessChain:
+            case spv::Op::OpInBoundsPtrAccessChain:
               modified |= CombineAccessChain(inst);
               break;
             default:
@@ -76,10 +76,10 @@
 uint32_t CombineAccessChains::GetArrayStride(const Instruction* inst) {
   uint32_t array_stride = 0;
   context()->get_decoration_mgr()->WhileEachDecoration(
-      inst->type_id(), SpvDecorationArrayStride,
+      inst->type_id(), uint32_t(spv::Decoration::ArrayStride),
       [&array_stride](const Instruction& decoration) {
-        assert(decoration.opcode() != SpvOpDecorateId);
-        if (decoration.opcode() == SpvOpDecorate) {
+        assert(decoration.opcode() != spv::Op::OpDecorateId);
+        if (decoration.opcode() == spv::Op::OpDecorate) {
           array_stride = decoration.GetSingleWordInOperand(1);
         } else {
           array_stride = decoration.GetSingleWordInOperand(2);
@@ -200,18 +200,18 @@
 }
 
 bool CombineAccessChains::CombineAccessChain(Instruction* inst) {
-  assert((inst->opcode() == SpvOpPtrAccessChain ||
-          inst->opcode() == SpvOpAccessChain ||
-          inst->opcode() == SpvOpInBoundsAccessChain ||
-          inst->opcode() == SpvOpInBoundsPtrAccessChain) &&
+  assert((inst->opcode() == spv::Op::OpPtrAccessChain ||
+          inst->opcode() == spv::Op::OpAccessChain ||
+          inst->opcode() == spv::Op::OpInBoundsAccessChain ||
+          inst->opcode() == spv::Op::OpInBoundsPtrAccessChain) &&
          "Wrong opcode. Expected an access chain.");
 
   Instruction* ptr_input =
       context()->get_def_use_mgr()->GetDef(inst->GetSingleWordInOperand(0));
-  if (ptr_input->opcode() != SpvOpAccessChain &&
-      ptr_input->opcode() != SpvOpInBoundsAccessChain &&
-      ptr_input->opcode() != SpvOpPtrAccessChain &&
-      ptr_input->opcode() != SpvOpInBoundsPtrAccessChain) {
+  if (ptr_input->opcode() != spv::Op::OpAccessChain &&
+      ptr_input->opcode() != spv::Op::OpInBoundsAccessChain &&
+      ptr_input->opcode() != spv::Op::OpPtrAccessChain &&
+      ptr_input->opcode() != spv::Op::OpInBoundsPtrAccessChain) {
     return false;
   }
 
@@ -246,7 +246,7 @@
   } else if (inst->NumInOperands() == 1) {
     // |inst| is a no-op, change it to a copy. Instruction simplification will
     // clean it up.
-    inst->SetOpcode(SpvOpCopyObject);
+    inst->SetOpcode(spv::Op::OpCopyObject);
   } else {
     std::vector<Operand> new_operands;
     if (!CreateNewInputOperands(ptr_input, inst, &new_operands)) return false;
@@ -259,23 +259,25 @@
   return true;
 }
 
-SpvOp CombineAccessChains::UpdateOpcode(SpvOp base_opcode, SpvOp input_opcode) {
-  auto IsInBounds = [](SpvOp opcode) {
-    return opcode == SpvOpInBoundsPtrAccessChain ||
-           opcode == SpvOpInBoundsAccessChain;
+spv::Op CombineAccessChains::UpdateOpcode(spv::Op base_opcode,
+                                          spv::Op input_opcode) {
+  auto IsInBounds = [](spv::Op opcode) {
+    return opcode == spv::Op::OpInBoundsPtrAccessChain ||
+           opcode == spv::Op::OpInBoundsAccessChain;
   };
 
-  if (input_opcode == SpvOpInBoundsPtrAccessChain) {
-    if (!IsInBounds(base_opcode)) return SpvOpPtrAccessChain;
-  } else if (input_opcode == SpvOpInBoundsAccessChain) {
-    if (!IsInBounds(base_opcode)) return SpvOpAccessChain;
+  if (input_opcode == spv::Op::OpInBoundsPtrAccessChain) {
+    if (!IsInBounds(base_opcode)) return spv::Op::OpPtrAccessChain;
+  } else if (input_opcode == spv::Op::OpInBoundsAccessChain) {
+    if (!IsInBounds(base_opcode)) return spv::Op::OpAccessChain;
   }
 
   return input_opcode;
 }
 
-bool CombineAccessChains::IsPtrAccessChain(SpvOp opcode) {
-  return opcode == SpvOpPtrAccessChain || opcode == SpvOpInBoundsPtrAccessChain;
+bool CombineAccessChains::IsPtrAccessChain(spv::Op opcode) {
+  return opcode == spv::Op::OpPtrAccessChain ||
+         opcode == spv::Op::OpInBoundsPtrAccessChain;
 }
 
 bool CombineAccessChains::Has64BitIndices(Instruction* inst) {
diff --git a/source/opt/combine_access_chains.h b/source/opt/combine_access_chains.h
index 531209e..32ee50d 100644
--- a/source/opt/combine_access_chains.h
+++ b/source/opt/combine_access_chains.h
@@ -68,10 +68,10 @@
                       std::vector<Operand>* new_operands);
 
   // Returns the opcode to use for the combined access chain.
-  SpvOp UpdateOpcode(SpvOp base_opcode, SpvOp input_opcode);
+  spv::Op UpdateOpcode(spv::Op base_opcode, spv::Op input_opcode);
 
   // Returns true if |opcode| is a pointer access chain.
-  bool IsPtrAccessChain(SpvOp opcode);
+  bool IsPtrAccessChain(spv::Op opcode);
 
   // Returns true if |inst| (an access chain) has 64-bit indices.
   bool Has64BitIndices(Instruction* inst);
diff --git a/source/opt/const_folding_rules.cpp b/source/opt/const_folding_rules.cpp
index 0ad755c..e676974 100644
--- a/source/opt/const_folding_rules.cpp
+++ b/source/opt/const_folding_rules.cpp
@@ -19,8 +19,7 @@
 namespace spvtools {
 namespace opt {
 namespace {
-
-const uint32_t kExtractCompositeIdInIdx = 0;
+constexpr uint32_t kExtractCompositeIdInIdx = 0;
 
 // Returns a constants with the value NaN of the given type.  Only works for
 // 32-bit and 64-bit float point types.  Returns |nullptr| if an error occurs.
@@ -89,6 +88,22 @@
   return nullptr;
 }
 
+// Returns a constants with the value |-val| of the given type.
+const analysis::Constant* NegateIntConst(const analysis::Type* result_type,
+                                         const analysis::Constant* val,
+                                         analysis::ConstantManager* const_mgr) {
+  const analysis::Integer* int_type = result_type->AsInteger();
+  assert(int_type != nullptr);
+
+  if (val->AsNullConstant()) {
+    return val;
+  }
+
+  uint64_t new_value = static_cast<uint64_t>(-val->GetSignExtendedValue());
+  return const_mgr->GetIntConst(new_value, int_type->width(),
+                                int_type->IsSigned());
+}
+
 // Folds an OpcompositeExtract where input is a composite constant.
 ConstantFoldingRule FoldExtractWithConstants() {
   return [](IRContext* context, Instruction* inst,
@@ -120,11 +135,102 @@
   };
 }
 
+// Folds an OpcompositeInsert where input is a composite constant.
+ConstantFoldingRule FoldInsertWithConstants() {
+  return [](IRContext* context, Instruction* inst,
+            const std::vector<const analysis::Constant*>& constants)
+             -> const analysis::Constant* {
+    analysis::ConstantManager* const_mgr = context->get_constant_mgr();
+    const analysis::Constant* object = constants[0];
+    const analysis::Constant* composite = constants[1];
+    if (object == nullptr || composite == nullptr) {
+      return nullptr;
+    }
+
+    // If there is more than 1 index, then each additional constant used by the
+    // index will need to be recreated to use the inserted object.
+    std::vector<const analysis::Constant*> chain;
+    std::vector<const analysis::Constant*> components;
+    const analysis::Type* type = nullptr;
+    const uint32_t final_index = (inst->NumInOperands() - 1);
+
+    // Work down hierarchy of all indexes
+    for (uint32_t i = 2; i < inst->NumInOperands(); ++i) {
+      type = composite->type();
+
+      if (composite->AsNullConstant()) {
+        // Make new composite so it can be inserted in the index with the
+        // non-null value
+        if (const auto new_composite =
+                const_mgr->GetNullCompositeConstant(type)) {
+          // Keep track of any indexes along the way to last index
+          if (i != final_index) {
+            chain.push_back(new_composite);
+          }
+          components = new_composite->AsCompositeConstant()->GetComponents();
+        } else {
+          // Unsupported input type (such as structs)
+          return nullptr;
+        }
+      } else {
+        // Keep track of any indexes along the way to last index
+        if (i != final_index) {
+          chain.push_back(composite);
+        }
+        components = composite->AsCompositeConstant()->GetComponents();
+      }
+      const uint32_t index = inst->GetSingleWordInOperand(i);
+      composite = components[index];
+    }
+
+    // Final index in hierarchy is inserted with new object.
+    const uint32_t final_operand = inst->GetSingleWordInOperand(final_index);
+    std::vector<uint32_t> ids;
+    for (size_t i = 0; i < components.size(); i++) {
+      const analysis::Constant* constant =
+          (i == final_operand) ? object : components[i];
+      Instruction* member_inst = const_mgr->GetDefiningInstruction(constant);
+      ids.push_back(member_inst->result_id());
+    }
+    const analysis::Constant* new_constant = const_mgr->GetConstant(type, ids);
+
+    // Work backwards up the chain and replace each index with new constant.
+    for (size_t i = chain.size(); i > 0; i--) {
+      // Need to insert any previous instruction into the module first.
+      // Can't just insert in types_values_begin() because it will move above
+      // where the types are declared.
+      // Can't compare with location of inst because not all new added
+      // instructions are added to types_values_
+      auto iter = context->types_values_end();
+      Module::inst_iterator* pos = &iter;
+      const_mgr->BuildInstructionAndAddToModule(new_constant, pos);
+
+      composite = chain[i - 1];
+      components = composite->AsCompositeConstant()->GetComponents();
+      type = composite->type();
+      ids.clear();
+      for (size_t k = 0; k < components.size(); k++) {
+        const uint32_t index =
+            inst->GetSingleWordInOperand(1 + static_cast<uint32_t>(i));
+        const analysis::Constant* constant =
+            (k == index) ? new_constant : components[k];
+        const uint32_t constant_id =
+            const_mgr->FindDeclaredConstant(constant, 0);
+        ids.push_back(constant_id);
+      }
+      new_constant = const_mgr->GetConstant(type, ids);
+    }
+
+    // If multiple constants were created, only need to return the top index.
+    return new_constant;
+  };
+}
+
 ConstantFoldingRule FoldVectorShuffleWithConstants() {
   return [](IRContext* context, Instruction* inst,
             const std::vector<const analysis::Constant*>& constants)
              -> const analysis::Constant* {
-    assert(inst->opcode() == SpvOpVectorShuffle);
+    assert(inst->opcode() == spv::Op::OpVectorShuffle);
     const analysis::Constant* c1 = constants[0];
     const analysis::Constant* c2 = constants[1];
     if (c1 == nullptr || c2 == nullptr) {
@@ -180,7 +286,7 @@
   return [](IRContext* context, Instruction* inst,
             const std::vector<const analysis::Constant*>& constants)
              -> const analysis::Constant* {
-    assert(inst->opcode() == SpvOpVectorTimesScalar);
+    assert(inst->opcode() == spv::Op::OpVectorTimesScalar);
     analysis::ConstantManager* const_mgr = context->get_constant_mgr();
     analysis::TypeManager* type_mgr = context->get_type_mgr();
 
@@ -251,11 +357,74 @@
   };
 }
 
+// Returns to the constant that results from tranposing |matrix|. The result
+// will have type |result_type|, and |matrix| must exist in |context|. The
+// result constant will also exist in |context|.
+const analysis::Constant* TransposeMatrix(const analysis::Constant* matrix,
+                                          analysis::Matrix* result_type,
+                                          IRContext* context) {
+  analysis::ConstantManager* const_mgr = context->get_constant_mgr();
+  if (matrix->AsNullConstant() != nullptr) {
+    return const_mgr->GetNullCompositeConstant(result_type);
+  }
+
+  const auto& columns = matrix->AsMatrixConstant()->GetComponents();
+  uint32_t number_of_rows = columns[0]->type()->AsVector()->element_count();
+
+  // Collect the ids of the elements in their new positions.
+  std::vector<std::vector<uint32_t>> result_elements(number_of_rows);
+  for (const analysis::Constant* column : columns) {
+    if (column->AsNullConstant()) {
+      column = const_mgr->GetNullCompositeConstant(column->type());
+    }
+    const auto& column_components = column->AsVectorConstant()->GetComponents();
+
+    for (uint32_t row = 0; row < number_of_rows; ++row) {
+      result_elements[row].push_back(
+          const_mgr->GetDefiningInstruction(column_components[row])
+              ->result_id());
+    }
+  }
+
+  // Create the constant for each row in the result, and collect the ids.
+  std::vector<uint32_t> result_columns(number_of_rows);
+  for (uint32_t col = 0; col < number_of_rows; ++col) {
+    auto* element = const_mgr->GetConstant(result_type->element_type(),
+                                           result_elements[col]);
+    result_columns[col] =
+        const_mgr->GetDefiningInstruction(element)->result_id();
+  }
+
+  // Create the matrix constant from the row ids, and return it.
+  return const_mgr->GetConstant(result_type, result_columns);
+}
+
+const analysis::Constant* FoldTranspose(
+    IRContext* context, Instruction* inst,
+    const std::vector<const analysis::Constant*>& constants) {
+  assert(inst->opcode() == spv::Op::OpTranspose);
+
+  analysis::TypeManager* type_mgr = context->get_type_mgr();
+  if (!inst->IsFloatingPointFoldingAllowed()) {
+    if (HasFloatingPoint(type_mgr->GetType(inst->type_id()))) {
+      return nullptr;
+    }
+  }
+
+  const analysis::Constant* matrix = constants[0];
+  if (matrix == nullptr) {
+    return nullptr;
+  }
+
+  auto* result_type = type_mgr->GetType(inst->type_id());
+  return TransposeMatrix(matrix, result_type->AsMatrix(), context);
+}
+
 ConstantFoldingRule FoldVectorTimesMatrix() {
   return [](IRContext* context, Instruction* inst,
             const std::vector<const analysis::Constant*>& constants)
              -> const analysis::Constant* {
-    assert(inst->opcode() == SpvOpVectorTimesMatrix);
+    assert(inst->opcode() == spv::Op::OpVectorTimesMatrix);
     analysis::ConstantManager* const_mgr = context->get_constant_mgr();
     analysis::TypeManager* type_mgr = context->get_type_mgr();
 
@@ -286,13 +455,7 @@
     assert(c1->type()->AsVector()->element_type() == element_type &&
            c2->type()->AsMatrix()->element_type() == vector_type);
 
-    // Get a float vector that is the result of vector-times-matrix.
-    std::vector<const analysis::Constant*> c1_components =
-        c1->GetVectorComponents(const_mgr);
-    std::vector<const analysis::Constant*> c2_components =
-        c2->AsMatrixConstant()->GetComponents();
     uint32_t resultVectorSize = result_type->AsVector()->element_count();
-
     std::vector<uint32_t> ids;
 
     if ((c1 && c1->IsZero()) || (c2 && c2->IsZero())) {
@@ -305,15 +468,23 @@
       return const_mgr->GetConstant(vector_type, ids);
     }
 
+    // Get a float vector that is the result of vector-times-matrix.
+    std::vector<const analysis::Constant*> c1_components =
+        c1->GetVectorComponents(const_mgr);
+    std::vector<const analysis::Constant*> c2_components =
+        c2->AsMatrixConstant()->GetComponents();
+
     if (float_type->width() == 32) {
       for (uint32_t i = 0; i < resultVectorSize; ++i) {
         float result_scalar = 0.0f;
-        const analysis::VectorConstant* c2_vec =
-            c2_components[i]->AsVectorConstant();
-        for (uint32_t j = 0; j < c2_vec->GetComponents().size(); ++j) {
-          float c1_scalar = c1_components[j]->GetFloat();
-          float c2_scalar = c2_vec->GetComponents()[j]->GetFloat();
-          result_scalar += c1_scalar * c2_scalar;
+        if (!c2_components[i]->AsNullConstant()) {
+          const analysis::VectorConstant* c2_vec =
+              c2_components[i]->AsVectorConstant();
+          for (uint32_t j = 0; j < c2_vec->GetComponents().size(); ++j) {
+            float c1_scalar = c1_components[j]->GetFloat();
+            float c2_scalar = c2_vec->GetComponents()[j]->GetFloat();
+            result_scalar += c1_scalar * c2_scalar;
+          }
         }
         utils::FloatProxy<float> result(result_scalar);
         std::vector<uint32_t> words = result.GetWords();
@@ -325,12 +496,14 @@
     } else if (float_type->width() == 64) {
       for (uint32_t i = 0; i < c2_components.size(); ++i) {
         double result_scalar = 0.0;
-        const analysis::VectorConstant* c2_vec =
-            c2_components[i]->AsVectorConstant();
-        for (uint32_t j = 0; j < c2_vec->GetComponents().size(); ++j) {
-          double c1_scalar = c1_components[j]->GetDouble();
-          double c2_scalar = c2_vec->GetComponents()[j]->GetDouble();
-          result_scalar += c1_scalar * c2_scalar;
+        if (!c2_components[i]->AsNullConstant()) {
+          const analysis::VectorConstant* c2_vec =
+              c2_components[i]->AsVectorConstant();
+          for (uint32_t j = 0; j < c2_vec->GetComponents().size(); ++j) {
+            double c1_scalar = c1_components[j]->GetDouble();
+            double c2_scalar = c2_vec->GetComponents()[j]->GetDouble();
+            result_scalar += c1_scalar * c2_scalar;
+          }
         }
         utils::FloatProxy<double> result(result_scalar);
         std::vector<uint32_t> words = result.GetWords();
@@ -348,7 +521,7 @@
   return [](IRContext* context, Instruction* inst,
             const std::vector<const analysis::Constant*>& constants)
              -> const analysis::Constant* {
-    assert(inst->opcode() == SpvOpMatrixTimesVector);
+    assert(inst->opcode() == spv::Op::OpMatrixTimesVector);
     analysis::ConstantManager* const_mgr = context->get_constant_mgr();
     analysis::TypeManager* type_mgr = context->get_type_mgr();
 
@@ -378,13 +551,7 @@
     assert(c1->type()->AsMatrix()->element_type() == vector_type);
     assert(c2->type()->AsVector()->element_type() == element_type);
 
-    // Get a float vector that is the result of matrix-times-vector.
-    std::vector<const analysis::Constant*> c1_components =
-        c1->AsMatrixConstant()->GetComponents();
-    std::vector<const analysis::Constant*> c2_components =
-        c2->GetVectorComponents(const_mgr);
     uint32_t resultVectorSize = result_type->AsVector()->element_count();
-
     std::vector<uint32_t> ids;
 
     if ((c1 && c1->IsZero()) || (c2 && c2->IsZero())) {
@@ -397,16 +564,24 @@
       return const_mgr->GetConstant(vector_type, ids);
     }
 
+    // Get a float vector that is the result of matrix-times-vector.
+    std::vector<const analysis::Constant*> c1_components =
+        c1->AsMatrixConstant()->GetComponents();
+    std::vector<const analysis::Constant*> c2_components =
+        c2->GetVectorComponents(const_mgr);
+
     if (float_type->width() == 32) {
       for (uint32_t i = 0; i < resultVectorSize; ++i) {
         float result_scalar = 0.0f;
         for (uint32_t j = 0; j < c1_components.size(); ++j) {
-          float c1_scalar = c1_components[j]
-                                ->AsVectorConstant()
-                                ->GetComponents()[i]
-                                ->GetFloat();
-          float c2_scalar = c2_components[j]->GetFloat();
-          result_scalar += c1_scalar * c2_scalar;
+          if (!c1_components[j]->AsNullConstant()) {
+            float c1_scalar = c1_components[j]
+                                  ->AsVectorConstant()
+                                  ->GetComponents()[i]
+                                  ->GetFloat();
+            float c2_scalar = c2_components[j]->GetFloat();
+            result_scalar += c1_scalar * c2_scalar;
+          }
         }
         utils::FloatProxy<float> result(result_scalar);
         std::vector<uint32_t> words = result.GetWords();
@@ -419,12 +594,14 @@
       for (uint32_t i = 0; i < resultVectorSize; ++i) {
         double result_scalar = 0.0;
         for (uint32_t j = 0; j < c1_components.size(); ++j) {
-          double c1_scalar = c1_components[j]
-                                 ->AsVectorConstant()
-                                 ->GetComponents()[i]
-                                 ->GetDouble();
-          double c2_scalar = c2_components[j]->GetDouble();
-          result_scalar += c1_scalar * c2_scalar;
+          if (!c1_components[j]->AsNullConstant()) {
+            double c1_scalar = c1_components[j]
+                                   ->AsVectorConstant()
+                                   ->GetComponents()[i]
+                                   ->GetDouble();
+            double c2_scalar = c2_components[j]->GetDouble();
+            result_scalar += c1_scalar * c2_scalar;
+          }
         }
         utils::FloatProxy<double> result(result_scalar);
         std::vector<uint32_t> words = result.GetWords();
@@ -458,9 +635,9 @@
       }
 
       uint32_t component_type_id = 0;
-      if (type_inst->opcode() == SpvOpTypeStruct) {
+      if (type_inst->opcode() == spv::Op::OpTypeStruct) {
         component_type_id = type_inst->GetSingleWordInOperand(i);
-      } else if (type_inst->opcode() == SpvOpTypeArray) {
+      } else if (type_inst->opcode() == spv::Op::OpTypeArray) {
         component_type_id = type_inst->GetSingleWordInOperand(0);
       }
 
@@ -489,27 +666,24 @@
     const analysis::Type* result_type, const analysis::Constant* a,
     const analysis::Constant* b, analysis::ConstantManager*)>;
 
-// Returns a |ConstantFoldingRule| that folds unary floating point scalar ops
-// using |scalar_rule| and unary float point vectors ops by applying
+// Returns a |ConstantFoldingRule| that folds unary scalar ops
+// using |scalar_rule| and unary vectors ops by applying
 // |scalar_rule| to the elements of the vector.  The |ConstantFoldingRule|
 // that is returned assumes that |constants| contains 1 entry.  If they are
 // not |nullptr|, then their type is either |Float| or |Integer| or a |Vector|
 // whose element type is |Float| or |Integer|.
-ConstantFoldingRule FoldFPUnaryOp(UnaryScalarFoldingRule scalar_rule) {
+ConstantFoldingRule FoldUnaryOp(UnaryScalarFoldingRule scalar_rule) {
   return [scalar_rule](IRContext* context, Instruction* inst,
                        const std::vector<const analysis::Constant*>& constants)
              -> const analysis::Constant* {
+
     analysis::ConstantManager* const_mgr = context->get_constant_mgr();
     analysis::TypeManager* type_mgr = context->get_type_mgr();
     const analysis::Type* result_type = type_mgr->GetType(inst->type_id());
     const analysis::Vector* vector_type = result_type->AsVector();
 
-    if (!inst->IsFloatingPointFoldingAllowed()) {
-      return nullptr;
-    }
-
     const analysis::Constant* arg =
-        (inst->opcode() == SpvOpExtInst) ? constants[1] : constants[0];
+        (inst->opcode() == spv::Op::OpExtInst) ? constants[1] : constants[0];
 
     if (arg == nullptr) {
       return nullptr;
@@ -542,6 +716,25 @@
   };
 }
 
+// Returns a |ConstantFoldingRule| that folds unary floating point scalar ops
+// using |scalar_rule| and unary float point vectors ops by applying
+// |scalar_rule| to the elements of the vector.  The |ConstantFoldingRule|
+// that is returned assumes that |constants| contains 1 entry.  If they are
+// not |nullptr|, then their type is either |Float| or |Integer| or a |Vector|
+// whose element type is |Float| or |Integer|.
+ConstantFoldingRule FoldFPUnaryOp(UnaryScalarFoldingRule scalar_rule) {
+  auto folding_rule = FoldUnaryOp(scalar_rule);
+  return [folding_rule](IRContext* context, Instruction* inst,
+                        const std::vector<const analysis::Constant*>& constants)
+             -> const analysis::Constant* {
+    if (!inst->IsFloatingPointFoldingAllowed()) {
+      return nullptr;
+    }
+
+    return folding_rule(context, inst, constants);
+  };
+}
+
 // Returns the result of folding the constants in |constants| according the
 // |scalar_rule|.  If |result_type| is a vector, then |scalar_rule| is applied
 // per component.
@@ -599,7 +792,7 @@
     if (!inst->IsFloatingPointFoldingAllowed()) {
       return nullptr;
     }
-    if (inst->opcode() == SpvOpExtInst) {
+    if (inst->opcode() == spv::Op::OpExtInst) {
       return FoldFPBinaryOp(scalar_rule, inst->type_id(),
                             {constants[1], constants[2]}, context);
     }
@@ -774,6 +967,11 @@
     return FoldFPScalarDivideByZero(result_type, numerator, const_mgr);
   }
 
+  uint32_t width = denominator->type()->AsFloat()->width();
+  if (width != 32 && width != 64) {
+    return nullptr;
+  }
+
   const analysis::FloatConstant* denominator_float =
       denominator->AsFloatConstant();
   if (denominator_float && denominator->GetValueAsDouble() == -0.0) {
@@ -944,20 +1142,10 @@
   };
 }
 
-// This function defines a |UnaryScalarFoldingRule| that subtracts the constant
-// from zero.
-UnaryScalarFoldingRule FoldFNegateOp() {
-  return [](const analysis::Type* result_type, const analysis::Constant* a,
-            analysis::ConstantManager* const_mgr) -> const analysis::Constant* {
-    assert(result_type != nullptr && a != nullptr);
-    assert(result_type == a->type());
-    return NegateFPConst(result_type, a, const_mgr);
-  };
-}
+ConstantFoldingRule FoldFNegate() { return FoldFPUnaryOp(NegateFPConst); }
+ConstantFoldingRule FoldSNegate() { return FoldUnaryOp(NegateIntConst); }
 
-ConstantFoldingRule FoldFNegate() { return FoldFPUnaryOp(FoldFNegateOp()); }
-
-ConstantFoldingRule FoldFClampFeedingCompare(uint32_t cmp_opcode) {
+ConstantFoldingRule FoldFClampFeedingCompare(spv::Op cmp_opcode) {
   return [cmp_opcode](IRContext* context, Instruction* inst,
                       const std::vector<const analysis::Constant*>& constants)
              -> const analysis::Constant* {
@@ -985,7 +1173,7 @@
       return nullptr;
     }
 
-    if (operand_inst->opcode() != SpvOpExtInst) {
+    if (operand_inst->opcode() != spv::Op::OpExtInst) {
       return nullptr;
     }
 
@@ -1009,25 +1197,25 @@
     bool result = false;
 
     switch (cmp_opcode) {
-      case SpvOpFOrdLessThan:
-      case SpvOpFUnordLessThan:
-      case SpvOpFOrdGreaterThanEqual:
-      case SpvOpFUnordGreaterThanEqual:
+      case spv::Op::OpFOrdLessThan:
+      case spv::Op::OpFUnordLessThan:
+      case spv::Op::OpFOrdGreaterThanEqual:
+      case spv::Op::OpFUnordGreaterThanEqual:
         if (constants[0]) {
           if (min_const) {
             if (constants[0]->GetValueAsDouble() <
                 min_const->GetValueAsDouble()) {
               found_result = true;
-              result = (cmp_opcode == SpvOpFOrdLessThan ||
-                        cmp_opcode == SpvOpFUnordLessThan);
+              result = (cmp_opcode == spv::Op::OpFOrdLessThan ||
+                        cmp_opcode == spv::Op::OpFUnordLessThan);
             }
           }
           if (max_const) {
             if (constants[0]->GetValueAsDouble() >=
                 max_const->GetValueAsDouble()) {
               found_result = true;
-              result = !(cmp_opcode == SpvOpFOrdLessThan ||
-                         cmp_opcode == SpvOpFUnordLessThan);
+              result = !(cmp_opcode == spv::Op::OpFOrdLessThan ||
+                         cmp_opcode == spv::Op::OpFUnordLessThan);
             }
           }
         }
@@ -1037,8 +1225,8 @@
             if (max_const->GetValueAsDouble() <
                 constants[1]->GetValueAsDouble()) {
               found_result = true;
-              result = (cmp_opcode == SpvOpFOrdLessThan ||
-                        cmp_opcode == SpvOpFUnordLessThan);
+              result = (cmp_opcode == spv::Op::OpFOrdLessThan ||
+                        cmp_opcode == spv::Op::OpFUnordLessThan);
             }
           }
 
@@ -1046,31 +1234,31 @@
             if (min_const->GetValueAsDouble() >=
                 constants[1]->GetValueAsDouble()) {
               found_result = true;
-              result = !(cmp_opcode == SpvOpFOrdLessThan ||
-                         cmp_opcode == SpvOpFUnordLessThan);
+              result = !(cmp_opcode == spv::Op::OpFOrdLessThan ||
+                         cmp_opcode == spv::Op::OpFUnordLessThan);
             }
           }
         }
         break;
-      case SpvOpFOrdGreaterThan:
-      case SpvOpFUnordGreaterThan:
-      case SpvOpFOrdLessThanEqual:
-      case SpvOpFUnordLessThanEqual:
+      case spv::Op::OpFOrdGreaterThan:
+      case spv::Op::OpFUnordGreaterThan:
+      case spv::Op::OpFOrdLessThanEqual:
+      case spv::Op::OpFUnordLessThanEqual:
         if (constants[0]) {
           if (min_const) {
             if (constants[0]->GetValueAsDouble() <=
                 min_const->GetValueAsDouble()) {
               found_result = true;
-              result = (cmp_opcode == SpvOpFOrdLessThanEqual ||
-                        cmp_opcode == SpvOpFUnordLessThanEqual);
+              result = (cmp_opcode == spv::Op::OpFOrdLessThanEqual ||
+                        cmp_opcode == spv::Op::OpFUnordLessThanEqual);
             }
           }
           if (max_const) {
             if (constants[0]->GetValueAsDouble() >
                 max_const->GetValueAsDouble()) {
               found_result = true;
-              result = !(cmp_opcode == SpvOpFOrdLessThanEqual ||
-                         cmp_opcode == SpvOpFUnordLessThanEqual);
+              result = !(cmp_opcode == spv::Op::OpFOrdLessThanEqual ||
+                         cmp_opcode == spv::Op::OpFUnordLessThanEqual);
             }
           }
         }
@@ -1080,8 +1268,8 @@
             if (max_const->GetValueAsDouble() <=
                 constants[1]->GetValueAsDouble()) {
               found_result = true;
-              result = (cmp_opcode == SpvOpFOrdLessThanEqual ||
-                        cmp_opcode == SpvOpFUnordLessThanEqual);
+              result = (cmp_opcode == spv::Op::OpFOrdLessThanEqual ||
+                        cmp_opcode == spv::Op::OpFUnordLessThanEqual);
             }
           }
 
@@ -1089,8 +1277,8 @@
             if (min_const->GetValueAsDouble() >
                 constants[1]->GetValueAsDouble()) {
               found_result = true;
-              result = !(cmp_opcode == SpvOpFOrdLessThanEqual ||
-                         cmp_opcode == SpvOpFUnordLessThanEqual);
+              result = !(cmp_opcode == spv::Op::OpFOrdLessThanEqual ||
+                         cmp_opcode == spv::Op::OpFUnordLessThanEqual);
             }
           }
         }
@@ -1117,7 +1305,7 @@
             const std::vector<const analysis::Constant*>& constants)
              -> const analysis::Constant* {
     analysis::ConstantManager* const_mgr = context->get_constant_mgr();
-    assert(inst->opcode() == SpvOpExtInst &&
+    assert(inst->opcode() == spv::Op::OpExtInst &&
            "Expecting an extended instruction.");
     assert(inst->GetSingleWordInOperand(0) ==
                context->get_feature_mgr()->GetExtInstImportId_GLSLstd450() &&
@@ -1267,7 +1455,7 @@
 const analysis::Constant* FoldClamp1(
     IRContext* context, Instruction* inst,
     const std::vector<const analysis::Constant*>& constants) {
-  assert(inst->opcode() == SpvOpExtInst &&
+  assert(inst->opcode() == spv::Op::OpExtInst &&
          "Expecting an extended instruction.");
   assert(inst->GetSingleWordInOperand(0) ==
              context->get_feature_mgr()->GetExtInstImportId_GLSLstd450() &&
@@ -1293,7 +1481,7 @@
 const analysis::Constant* FoldClamp2(
     IRContext* context, Instruction* inst,
     const std::vector<const analysis::Constant*>& constants) {
-  assert(inst->opcode() == SpvOpExtInst &&
+  assert(inst->opcode() == spv::Op::OpExtInst &&
          "Expecting an extended instruction.");
   assert(inst->GetSingleWordInOperand(0) ==
              context->get_feature_mgr()->GetExtInstImportId_GLSLstd450() &&
@@ -1321,7 +1509,7 @@
 const analysis::Constant* FoldClamp3(
     IRContext* context, Instruction* inst,
     const std::vector<const analysis::Constant*>& constants) {
-  assert(inst->opcode() == SpvOpExtInst &&
+  assert(inst->opcode() == spv::Op::OpExtInst &&
          "Expecting an extended instruction.");
   assert(inst->GetSingleWordInOperand(0) ==
              context->get_feature_mgr()->GetExtInstImportId_GLSLstd450() &&
@@ -1407,68 +1595,72 @@
   // applies to the instruction, the rest of the rules will not be attempted.
   // Take that into consideration.
 
-  rules_[SpvOpCompositeConstruct].push_back(FoldCompositeWithConstants());
+  rules_[spv::Op::OpCompositeConstruct].push_back(FoldCompositeWithConstants());
 
-  rules_[SpvOpCompositeExtract].push_back(FoldExtractWithConstants());
+  rules_[spv::Op::OpCompositeExtract].push_back(FoldExtractWithConstants());
+  rules_[spv::Op::OpCompositeInsert].push_back(FoldInsertWithConstants());
 
-  rules_[SpvOpConvertFToS].push_back(FoldFToI());
-  rules_[SpvOpConvertFToU].push_back(FoldFToI());
-  rules_[SpvOpConvertSToF].push_back(FoldIToF());
-  rules_[SpvOpConvertUToF].push_back(FoldIToF());
+  rules_[spv::Op::OpConvertFToS].push_back(FoldFToI());
+  rules_[spv::Op::OpConvertFToU].push_back(FoldFToI());
+  rules_[spv::Op::OpConvertSToF].push_back(FoldIToF());
+  rules_[spv::Op::OpConvertUToF].push_back(FoldIToF());
 
-  rules_[SpvOpDot].push_back(FoldOpDotWithConstants());
-  rules_[SpvOpFAdd].push_back(FoldFAdd());
-  rules_[SpvOpFDiv].push_back(FoldFDiv());
-  rules_[SpvOpFMul].push_back(FoldFMul());
-  rules_[SpvOpFSub].push_back(FoldFSub());
+  rules_[spv::Op::OpDot].push_back(FoldOpDotWithConstants());
+  rules_[spv::Op::OpFAdd].push_back(FoldFAdd());
+  rules_[spv::Op::OpFDiv].push_back(FoldFDiv());
+  rules_[spv::Op::OpFMul].push_back(FoldFMul());
+  rules_[spv::Op::OpFSub].push_back(FoldFSub());
 
-  rules_[SpvOpFOrdEqual].push_back(FoldFOrdEqual());
+  rules_[spv::Op::OpFOrdEqual].push_back(FoldFOrdEqual());
 
-  rules_[SpvOpFUnordEqual].push_back(FoldFUnordEqual());
+  rules_[spv::Op::OpFUnordEqual].push_back(FoldFUnordEqual());
 
-  rules_[SpvOpFOrdNotEqual].push_back(FoldFOrdNotEqual());
+  rules_[spv::Op::OpFOrdNotEqual].push_back(FoldFOrdNotEqual());
 
-  rules_[SpvOpFUnordNotEqual].push_back(FoldFUnordNotEqual());
+  rules_[spv::Op::OpFUnordNotEqual].push_back(FoldFUnordNotEqual());
 
-  rules_[SpvOpFOrdLessThan].push_back(FoldFOrdLessThan());
-  rules_[SpvOpFOrdLessThan].push_back(
-      FoldFClampFeedingCompare(SpvOpFOrdLessThan));
+  rules_[spv::Op::OpFOrdLessThan].push_back(FoldFOrdLessThan());
+  rules_[spv::Op::OpFOrdLessThan].push_back(
+      FoldFClampFeedingCompare(spv::Op::OpFOrdLessThan));
 
-  rules_[SpvOpFUnordLessThan].push_back(FoldFUnordLessThan());
-  rules_[SpvOpFUnordLessThan].push_back(
-      FoldFClampFeedingCompare(SpvOpFUnordLessThan));
+  rules_[spv::Op::OpFUnordLessThan].push_back(FoldFUnordLessThan());
+  rules_[spv::Op::OpFUnordLessThan].push_back(
+      FoldFClampFeedingCompare(spv::Op::OpFUnordLessThan));
 
-  rules_[SpvOpFOrdGreaterThan].push_back(FoldFOrdGreaterThan());
-  rules_[SpvOpFOrdGreaterThan].push_back(
-      FoldFClampFeedingCompare(SpvOpFOrdGreaterThan));
+  rules_[spv::Op::OpFOrdGreaterThan].push_back(FoldFOrdGreaterThan());
+  rules_[spv::Op::OpFOrdGreaterThan].push_back(
+      FoldFClampFeedingCompare(spv::Op::OpFOrdGreaterThan));
 
-  rules_[SpvOpFUnordGreaterThan].push_back(FoldFUnordGreaterThan());
-  rules_[SpvOpFUnordGreaterThan].push_back(
-      FoldFClampFeedingCompare(SpvOpFUnordGreaterThan));
+  rules_[spv::Op::OpFUnordGreaterThan].push_back(FoldFUnordGreaterThan());
+  rules_[spv::Op::OpFUnordGreaterThan].push_back(
+      FoldFClampFeedingCompare(spv::Op::OpFUnordGreaterThan));
 
-  rules_[SpvOpFOrdLessThanEqual].push_back(FoldFOrdLessThanEqual());
-  rules_[SpvOpFOrdLessThanEqual].push_back(
-      FoldFClampFeedingCompare(SpvOpFOrdLessThanEqual));
+  rules_[spv::Op::OpFOrdLessThanEqual].push_back(FoldFOrdLessThanEqual());
+  rules_[spv::Op::OpFOrdLessThanEqual].push_back(
+      FoldFClampFeedingCompare(spv::Op::OpFOrdLessThanEqual));
 
-  rules_[SpvOpFUnordLessThanEqual].push_back(FoldFUnordLessThanEqual());
-  rules_[SpvOpFUnordLessThanEqual].push_back(
-      FoldFClampFeedingCompare(SpvOpFUnordLessThanEqual));
+  rules_[spv::Op::OpFUnordLessThanEqual].push_back(FoldFUnordLessThanEqual());
+  rules_[spv::Op::OpFUnordLessThanEqual].push_back(
+      FoldFClampFeedingCompare(spv::Op::OpFUnordLessThanEqual));
 
-  rules_[SpvOpFOrdGreaterThanEqual].push_back(FoldFOrdGreaterThanEqual());
-  rules_[SpvOpFOrdGreaterThanEqual].push_back(
-      FoldFClampFeedingCompare(SpvOpFOrdGreaterThanEqual));
+  rules_[spv::Op::OpFOrdGreaterThanEqual].push_back(FoldFOrdGreaterThanEqual());
+  rules_[spv::Op::OpFOrdGreaterThanEqual].push_back(
+      FoldFClampFeedingCompare(spv::Op::OpFOrdGreaterThanEqual));
 
-  rules_[SpvOpFUnordGreaterThanEqual].push_back(FoldFUnordGreaterThanEqual());
-  rules_[SpvOpFUnordGreaterThanEqual].push_back(
-      FoldFClampFeedingCompare(SpvOpFUnordGreaterThanEqual));
+  rules_[spv::Op::OpFUnordGreaterThanEqual].push_back(
+      FoldFUnordGreaterThanEqual());
+  rules_[spv::Op::OpFUnordGreaterThanEqual].push_back(
+      FoldFClampFeedingCompare(spv::Op::OpFUnordGreaterThanEqual));
 
-  rules_[SpvOpVectorShuffle].push_back(FoldVectorShuffleWithConstants());
-  rules_[SpvOpVectorTimesScalar].push_back(FoldVectorTimesScalar());
-  rules_[SpvOpVectorTimesMatrix].push_back(FoldVectorTimesMatrix());
-  rules_[SpvOpMatrixTimesVector].push_back(FoldMatrixTimesVector());
+  rules_[spv::Op::OpVectorShuffle].push_back(FoldVectorShuffleWithConstants());
+  rules_[spv::Op::OpVectorTimesScalar].push_back(FoldVectorTimesScalar());
+  rules_[spv::Op::OpVectorTimesMatrix].push_back(FoldVectorTimesMatrix());
+  rules_[spv::Op::OpMatrixTimesVector].push_back(FoldMatrixTimesVector());
+  rules_[spv::Op::OpTranspose].push_back(FoldTranspose);
 
-  rules_[SpvOpFNegate].push_back(FoldFNegate());
-  rules_[SpvOpQuantizeToF16].push_back(FoldQuantizeToF16());
+  rules_[spv::Op::OpFNegate].push_back(FoldFNegate());
+  rules_[spv::Op::OpSNegate].push_back(FoldSNegate());
+  rules_[spv::Op::OpQuantizeToF16].push_back(FoldQuantizeToF16());
 
   // Add rules for GLSLstd450
   FeatureManager* feature_manager = context_->get_feature_mgr();
diff --git a/source/opt/const_folding_rules.h b/source/opt/const_folding_rules.h
index 41ee2aa..fa34532 100644
--- a/source/opt/const_folding_rules.h
+++ b/source/opt/const_folding_rules.h
@@ -88,7 +88,7 @@
   // Returns true if there is at least 1 folding rule for |inst|.
   const std::vector<ConstantFoldingRule>& GetRulesForInstruction(
       const Instruction* inst) const {
-    if (inst->opcode() != SpvOpExtInst) {
+    if (inst->opcode() != spv::Op::OpExtInst) {
       auto it = rules_.find(inst->opcode());
       if (it != rules_.end()) {
         return it->second.value;
@@ -108,9 +108,15 @@
   virtual void AddFoldingRules();
 
  protected:
+  struct hasher {
+    size_t operator()(const spv::Op& op) const noexcept {
+      return std::hash<uint32_t>()(uint32_t(op));
+    }
+  };
+
   // |rules[opcode]| is the set of rules that can be applied to instructions
   // with |opcode| as the opcode.
-  std::unordered_map<uint32_t, Value> rules_;
+  std::unordered_map<spv::Op, Value, hasher> rules_;
 
   // The folding rules for extended instructions.
   std::map<Key, Value> ext_rules_;
diff --git a/source/opt/constants.cpp b/source/opt/constants.cpp
index bcff08c..a487a45 100644
--- a/source/opt/constants.cpp
+++ b/source/opt/constants.cpp
@@ -14,7 +14,6 @@
 
 #include "source/opt/constants.h"
 
-#include <unordered_map>
 #include <vector>
 
 #include "source/opt/ir_context.h"
@@ -306,16 +305,16 @@
   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.
-    case SpvOp::SpvOpConstantTrue:
+    case spv::Op::OpConstantTrue:
       literal_words_or_ids.push_back(true);
       break;
-    case SpvOp::SpvOpConstantFalse:
+    case spv::Op::OpConstantFalse:
       literal_words_or_ids.push_back(false);
       break;
-    case SpvOp::SpvOpConstantNull:
-    case SpvOp::SpvOpConstant:
-    case SpvOp::SpvOpConstantComposite:
-    case SpvOp::SpvOpSpecConstantComposite:
+    case spv::Op::OpConstantNull:
+    case spv::Op::OpConstant:
+    case spv::Op::OpConstantComposite:
+    case spv::Op::OpSpecConstantComposite:
       break;
     default:
       return nullptr;
@@ -329,22 +328,22 @@
   uint32_t type =
       (type_id == 0) ? context()->get_type_mgr()->GetId(c->type()) : type_id;
   if (c->AsNullConstant()) {
-    return MakeUnique<Instruction>(context(), SpvOp::SpvOpConstantNull, type,
-                                   id, std::initializer_list<Operand>{});
+    return MakeUnique<Instruction>(context(), spv::Op::OpConstantNull, type, id,
+                                   std::initializer_list<Operand>{});
   } else if (const BoolConstant* bc = c->AsBoolConstant()) {
     return MakeUnique<Instruction>(
         context(),
-        bc->value() ? SpvOp::SpvOpConstantTrue : SpvOp::SpvOpConstantFalse,
-        type, id, std::initializer_list<Operand>{});
+        bc->value() ? spv::Op::OpConstantTrue : spv::Op::OpConstantFalse, type,
+        id, std::initializer_list<Operand>{});
   } else if (const IntConstant* ic = c->AsIntConstant()) {
     return MakeUnique<Instruction>(
-        context(), SpvOp::SpvOpConstant, type, id,
+        context(), spv::Op::OpConstant, type, id,
         std::initializer_list<Operand>{
             Operand(spv_operand_type_t::SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER,
                     ic->words())});
   } else if (const FloatConstant* fc = c->AsFloatConstant()) {
     return MakeUnique<Instruction>(
-        context(), SpvOp::SpvOpConstant, type, id,
+        context(), spv::Op::OpConstant, type, id,
         std::initializer_list<Operand>{
             Operand(spv_operand_type_t::SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER,
                     fc->words())});
@@ -362,9 +361,9 @@
   uint32_t component_index = 0;
   for (const Constant* component_const : cc->GetComponents()) {
     uint32_t component_type_id = 0;
-    if (type_inst && type_inst->opcode() == SpvOpTypeStruct) {
+    if (type_inst && type_inst->opcode() == spv::Op::OpTypeStruct) {
       component_type_id = type_inst->GetSingleWordInOperand(component_index);
-    } else if (type_inst && type_inst->opcode() == SpvOpTypeArray) {
+    } else if (type_inst && type_inst->opcode() == spv::Op::OpTypeArray) {
       component_type_id = type_inst->GetSingleWordInOperand(0);
     }
     uint32_t id = FindDeclaredConstant(component_const, component_type_id);
@@ -381,7 +380,7 @@
   }
   uint32_t type =
       (type_id == 0) ? context()->get_type_mgr()->GetId(cc->type()) : type_id;
-  return MakeUnique<Instruction>(context(), SpvOp::SpvOpConstantComposite, type,
+  return MakeUnique<Instruction>(context(), spv::Op::OpConstantComposite, type,
                                  result_id, std::move(operands));
 }
 
@@ -391,6 +390,43 @@
   return cst ? RegisterConstant(std::move(cst)) : nullptr;
 }
 
+const Constant* ConstantManager::GetNullCompositeConstant(const Type* type) {
+  std::vector<uint32_t> literal_words_or_id;
+
+  if (type->AsVector()) {
+    const Type* element_type = type->AsVector()->element_type();
+    const uint32_t null_id = GetNullConstId(element_type);
+    const uint32_t element_count = type->AsVector()->element_count();
+    for (uint32_t i = 0; i < element_count; i++) {
+      literal_words_or_id.push_back(null_id);
+    }
+  } else if (type->AsMatrix()) {
+    const Type* element_type = type->AsMatrix()->element_type();
+    const uint32_t null_id = GetNullConstId(element_type);
+    const uint32_t element_count = type->AsMatrix()->element_count();
+    for (uint32_t i = 0; i < element_count; i++) {
+      literal_words_or_id.push_back(null_id);
+    }
+  } else if (type->AsStruct()) {
+    // TODO (sfricke-lunarg) add proper struct support
+    return nullptr;
+  } else if (type->AsArray()) {
+    const Type* element_type = type->AsArray()->element_type();
+    const uint32_t null_id = GetNullConstId(element_type);
+    assert(type->AsArray()->length_info().words[0] ==
+               analysis::Array::LengthInfo::kConstant &&
+           "unexpected array length");
+    const uint32_t element_count = type->AsArray()->length_info().words[0];
+    for (uint32_t i = 0; i < element_count; i++) {
+      literal_words_or_id.push_back(null_id);
+    }
+  } else {
+    return nullptr;
+  }
+
+  return GetConstant(type, literal_words_or_id);
+}
+
 const Constant* ConstantManager::GetNumericVectorConstantWithWords(
     const Vector* type, const std::vector<uint32_t>& literal_words) {
   const auto* element_type = type->element_type();
@@ -399,6 +435,8 @@
     words_per_element = float_type->width() / 32;
   else if (const auto* int_type = element_type->AsInteger())
     words_per_element = int_type->width() / 32;
+  else if (element_type->AsBool() != nullptr)
+    words_per_element = 1;
 
   if (words_per_element != 1 && words_per_element != 2) return nullptr;
 
@@ -445,18 +483,48 @@
   return c;
 }
 
-uint32_t ConstantManager::GetSIntConst(int32_t val) {
+uint32_t ConstantManager::GetSIntConstId(int32_t val) {
   Type* sint_type = context()->get_type_mgr()->GetSIntType();
   const Constant* c = GetConstant(sint_type, {static_cast<uint32_t>(val)});
   return GetDefiningInstruction(c)->result_id();
 }
 
-uint32_t ConstantManager::GetUIntConst(uint32_t val) {
+const Constant* ConstantManager::GetIntConst(uint64_t val, int32_t bitWidth,
+                                             bool isSigned) {
+  Type* int_type = context()->get_type_mgr()->GetIntType(bitWidth, isSigned);
+
+  if (isSigned) {
+    // Sign extend the value.
+    int32_t num_of_bit_to_ignore = 64 - bitWidth;
+    val = static_cast<int64_t>(val << num_of_bit_to_ignore) >>
+          num_of_bit_to_ignore;
+  } else {
+    // Clear the upper bit that are not used.
+    uint64_t mask = ((1ull << bitWidth) - 1);
+    val &= mask;
+  }
+
+  if (bitWidth <= 32) {
+    return GetConstant(int_type, {static_cast<uint32_t>(val)});
+  }
+
+  // If the value is more than 32-bit, we need to split the operands into two
+  // 32-bit integers.
+  return GetConstant(
+      int_type, {static_cast<uint32_t>(val >> 32), static_cast<uint32_t>(val)});
+}
+
+uint32_t ConstantManager::GetUIntConstId(uint32_t val) {
   Type* uint_type = context()->get_type_mgr()->GetUIntType();
   const Constant* c = GetConstant(uint_type, {val});
   return GetDefiningInstruction(c)->result_id();
 }
 
+uint32_t ConstantManager::GetNullConstId(const Type* type) {
+  const Constant* c = GetConstant(type, {});
+  return GetDefiningInstruction(c)->result_id();
+}
+
 std::vector<const analysis::Constant*> Constant::GetVectorComponents(
     analysis::ConstantManager* const_mgr) const {
   std::vector<const analysis::Constant*> components;
diff --git a/source/opt/constants.h b/source/opt/constants.h
index 588ca3e..ae8dc62 100644
--- a/source/opt/constants.h
+++ b/source/opt/constants.h
@@ -520,6 +520,14 @@
                                                    literal_words_or_ids.end()));
   }
 
+  // Takes a type and creates a OpConstantComposite
+  // This allows a
+  // OpConstantNull %composite_type
+  // to become a
+  // OpConstantComposite %composite_type %null %null ... etc
+  // Assumes type is a Composite already, otherwise returns null
+  const Constant* GetNullCompositeConstant(const Type* type);
+
   // Gets or creates a unique Constant instance of Vector type |type| with
   // numeric elements and a vector of constant defining words |literal_words|.
   // If a Constant instance existed already in the constant pool, it returns a
@@ -649,10 +657,19 @@
   const Constant* GetDoubleConst(double val);
 
   // Returns the id of a 32-bit signed integer constant with value |val|.
-  uint32_t GetSIntConst(int32_t val);
+  uint32_t GetSIntConstId(int32_t val);
+
+  // Returns an integer constant with `bitWidth` and value |val|. If `isSigned`
+  // is true, the constant will be a signed integer. Otherwise it will be
+  // unsigned. Only the `bitWidth` lower order bits of |val| will be used. The
+  // rest will be ignored.
+  const Constant* GetIntConst(uint64_t val, int32_t bitWidth, bool isSigned);
 
   // Returns the id of a 32-bit unsigned integer constant with value |val|.
-  uint32_t GetUIntConst(uint32_t val);
+  uint32_t GetUIntConstId(uint32_t val);
+
+  // Returns the id of a OpConstantNull with type of |type|.
+  uint32_t GetNullConstId(const Type* type);
 
  private:
   // Creates a Constant instance with the given type and a vector of constant
diff --git a/source/opt/control_dependence.cpp b/source/opt/control_dependence.cpp
index f4879e0..3d48139 100644
--- a/source/opt/control_dependence.cpp
+++ b/source/opt/control_dependence.cpp
@@ -16,15 +16,12 @@
 
 #include <cassert>
 #include <tuple>
-#include <utility>
-#include <vector>
 
 #include "source/opt/basic_block.h"
 #include "source/opt/cfg.h"
 #include "source/opt/dominator_analysis.h"
 #include "source/opt/function.h"
 #include "source/opt/instruction.h"
-#include "spirv/unified1/spirv.h"
 
 // Computes the control dependence graph (CDG) using the algorithm in Cytron
 // 1991, "Efficiently Computing Static Single Assignment Form and the Control
@@ -49,8 +46,8 @@
   }
   const BasicBlock* source_bb = cfg.block(source_bb_id());
   const Instruction* branch = source_bb->terminator();
-  assert((branch->opcode() == SpvOpBranchConditional ||
-          branch->opcode() == SpvOpSwitch) &&
+  assert((branch->opcode() == spv::Op::OpBranchConditional ||
+          branch->opcode() == spv::Op::OpSwitch) &&
          "invalid control dependence; last instruction must be conditional "
          "branch or switch");
   return branch->GetSingleWordInOperand(0);
diff --git a/source/opt/convert_to_half_pass.cpp b/source/opt/convert_to_half_pass.cpp
index 4086e31..cb0065d 100644
--- a/source/opt/convert_to_half_pass.cpp
+++ b/source/opt/convert_to_half_pass.cpp
@@ -18,19 +18,16 @@
 
 #include "source/opt/ir_builder.h"
 
-namespace {
-
-// Indices of operands in SPIR-V instructions
-static const int kImageSampleDrefIdInIdx = 2;
-
-}  // anonymous namespace
-
 namespace spvtools {
 namespace opt {
+namespace {
+// Indices of operands in SPIR-V instructions
+constexpr int kImageSampleDrefIdInIdx = 2;
+}  // namespace
 
 bool ConvertToHalfPass::IsArithmetic(Instruction* inst) {
   return target_ops_core_.count(inst->opcode()) != 0 ||
-         (inst->opcode() == SpvOpExtInst &&
+         (inst->opcode() == spv::Op::OpExtInst &&
           inst->GetSingleWordInOperand(0) ==
               context()->get_feature_mgr()->GetExtInstImportId_GLSLstd450() &&
           target_ops_450_.count(inst->GetSingleWordInOperand(1)) != 0);
@@ -42,12 +39,21 @@
   return Pass::IsFloat(ty_id, width);
 }
 
+bool ConvertToHalfPass::IsStruct(Instruction* inst) {
+  uint32_t ty_id = inst->type_id();
+  if (ty_id == 0) return false;
+  Instruction* ty_inst = Pass::GetBaseType(ty_id);
+  return (ty_inst->opcode() == spv::Op::OpTypeStruct);
+}
+
 bool ConvertToHalfPass::IsDecoratedRelaxed(Instruction* inst) {
   uint32_t r_id = inst->result_id();
   for (auto r_inst : get_decoration_mgr()->GetDecorationsFor(r_id, false))
-    if (r_inst->opcode() == SpvOpDecorate &&
-        r_inst->GetSingleWordInOperand(1) == SpvDecorationRelaxedPrecision)
+    if (r_inst->opcode() == spv::Op::OpDecorate &&
+        spv::Decoration(r_inst->GetSingleWordInOperand(1)) ==
+            spv::Decoration::RelaxedPrecision) {
       return true;
+    }
   return false;
 }
 
@@ -57,6 +63,10 @@
 
 void ConvertToHalfPass::AddRelaxed(uint32_t id) { relaxed_ids_set_.insert(id); }
 
+bool ConvertToHalfPass::CanRelaxOpOperands(Instruction* inst) {
+  return image_ops_.count(inst->opcode()) == 0;
+}
+
 analysis::Type* ConvertToHalfPass::FloatScalarType(uint32_t width) {
   analysis::Float float_ty(width);
   return context()->get_type_mgr()->GetRegisteredType(&float_ty);
@@ -82,12 +92,12 @@
 uint32_t ConvertToHalfPass::EquivFloatTypeId(uint32_t ty_id, uint32_t width) {
   analysis::Type* reg_equiv_ty;
   Instruction* ty_inst = get_def_use_mgr()->GetDef(ty_id);
-  if (ty_inst->opcode() == SpvOpTypeMatrix)
+  if (ty_inst->opcode() == spv::Op::OpTypeMatrix)
     reg_equiv_ty = FloatMatrixType(ty_inst->GetSingleWordInOperand(1),
                                    ty_inst->GetSingleWordInOperand(0), width);
-  else if (ty_inst->opcode() == SpvOpTypeVector)
+  else if (ty_inst->opcode() == spv::Op::OpTypeVector)
     reg_equiv_ty = FloatVectorType(ty_inst->GetSingleWordInOperand(1), width);
-  else  // SpvOpTypeFloat
+  else  // spv::Op::OpTypeFloat
     reg_equiv_ty = FloatScalarType(width);
   return context()->get_type_mgr()->GetTypeInstruction(reg_equiv_ty);
 }
@@ -102,18 +112,18 @@
   InstructionBuilder builder(
       context(), inst,
       IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
-  if (val_inst->opcode() == SpvOpUndef)
-    cvt_inst = builder.AddNullaryOp(nty_id, SpvOpUndef);
+  if (val_inst->opcode() == spv::Op::OpUndef)
+    cvt_inst = builder.AddNullaryOp(nty_id, spv::Op::OpUndef);
   else
-    cvt_inst = builder.AddUnaryOp(nty_id, SpvOpFConvert, *val_idp);
+    cvt_inst = builder.AddUnaryOp(nty_id, spv::Op::OpFConvert, *val_idp);
   *val_idp = cvt_inst->result_id();
 }
 
 bool ConvertToHalfPass::MatConvertCleanup(Instruction* inst) {
-  if (inst->opcode() != SpvOpFConvert) return false;
+  if (inst->opcode() != spv::Op::OpFConvert) return false;
   uint32_t mty_id = inst->type_id();
   Instruction* mty_inst = get_def_use_mgr()->GetDef(mty_id);
-  if (mty_inst->opcode() != SpvOpTypeMatrix) return false;
+  if (mty_inst->opcode() != spv::Op::OpTypeMatrix) return false;
   uint32_t vty_id = mty_inst->GetSingleWordInOperand(0);
   uint32_t v_cnt = mty_inst->GetSingleWordInOperand(1);
   Instruction* vty_inst = get_def_use_mgr()->GetDef(vty_id);
@@ -130,18 +140,18 @@
   std::vector<Operand> opnds = {};
   for (uint32_t vidx = 0; vidx < v_cnt; ++vidx) {
     Instruction* ext_inst = builder.AddIdLiteralOp(
-        orig_vty_id, SpvOpCompositeExtract, orig_mat_id, vidx);
+        orig_vty_id, spv::Op::OpCompositeExtract, orig_mat_id, vidx);
     Instruction* cvt_inst =
-        builder.AddUnaryOp(vty_id, SpvOpFConvert, ext_inst->result_id());
+        builder.AddUnaryOp(vty_id, spv::Op::OpFConvert, ext_inst->result_id());
     opnds.push_back({SPV_OPERAND_TYPE_ID, {cvt_inst->result_id()}});
   }
   uint32_t mat_id = TakeNextId();
   std::unique_ptr<Instruction> mat_inst(new Instruction(
-      context(), SpvOpCompositeConstruct, mty_id, mat_id, opnds));
+      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.
-  inst->SetOpcode(SpvOpCopyObject);
+  inst->SetOpcode(spv::Op::OpCopyObject);
   inst->SetResultType(EquivFloatTypeId(mty_id, orig_width));
   get_def_use_mgr()->AnalyzeInstUse(inst);
   return true;
@@ -150,10 +160,11 @@
 bool ConvertToHalfPass::RemoveRelaxedDecoration(uint32_t id) {
   return context()->get_decoration_mgr()->RemoveDecorationsFrom(
       id, [](const Instruction& dec) {
-        if (dec.opcode() == SpvOpDecorate &&
-            dec.GetSingleWordInOperand(1u) == SpvDecorationRelaxedPrecision)
+        if (dec.opcode() == spv::Op::OpDecorate &&
+            spv::Decoration(dec.GetSingleWordInOperand(1u)) ==
+                spv::Decoration::RelaxedPrecision) {
           return true;
-        else
+        } else
           return false;
       });
 }
@@ -196,8 +207,8 @@
         auto insert_before = bp->tail();
         if (insert_before != bp->begin()) {
           --insert_before;
-          if (insert_before->opcode() != SpvOpSelectionMerge &&
-              insert_before->opcode() != SpvOpLoopMerge)
+          if (insert_before->opcode() != spv::Op::OpSelectionMerge &&
+              insert_before->opcode() != spv::Op::OpLoopMerge)
             ++insert_before;
         }
         GenConvert(prev_idp, to_width, &*insert_before);
@@ -229,7 +240,8 @@
   // changed to half.
   uint32_t val_id = inst->GetSingleWordInOperand(0);
   Instruction* val_inst = get_def_use_mgr()->GetDef(val_id);
-  if (inst->type_id() == val_inst->type_id()) inst->SetOpcode(SpvOpCopyObject);
+  if (inst->type_id() == val_inst->type_id())
+    inst->SetOpcode(spv::Op::OpCopyObject);
   return true;  // modified
 }
 
@@ -251,7 +263,7 @@
 bool ConvertToHalfPass::ProcessDefault(Instruction* inst) {
   // If non-relaxed instruction has changed operands, need to convert
   // them back to float32
-  if (inst->opcode() == SpvOpPhi) return ProcessPhi(inst, 16u, 32u);
+  if (inst->opcode() == spv::Op::OpPhi) return ProcessPhi(inst, 16u, 32u);
   bool modified = false;
   inst->ForEachInId([&inst, &modified, this](uint32_t* idp) {
     if (converted_ids_.count(*idp) == 0) return;
@@ -269,9 +281,9 @@
   bool inst_relaxed = IsRelaxed(inst->result_id());
   if (IsArithmetic(inst) && inst_relaxed)
     modified = GenHalfArith(inst);
-  else if (inst->opcode() == SpvOpPhi && inst_relaxed)
+  else if (inst->opcode() == spv::Op::OpPhi && inst_relaxed)
     modified = ProcessPhi(inst, 32u, 16u);
-  else if (inst->opcode() == SpvOpFConvert)
+  else if (inst->opcode() == spv::Op::OpFConvert)
     modified = ProcessConvert(inst);
   else if (image_ops_.count(inst->opcode()) != 0)
     modified = ProcessImageRef(inst);
@@ -293,6 +305,7 @@
   bool relax = true;
   inst->ForEachInId([&relax, this](uint32_t* idp) {
     Instruction* op_inst = get_def_use_mgr()->GetDef(*idp);
+    if (IsStruct(op_inst)) relax = false;
     if (!IsFloat(op_inst, 32)) return;
     if (!IsRelaxed(*idp)) relax = false;
   });
@@ -304,7 +317,8 @@
   relax = true;
   get_def_use_mgr()->ForEachUser(inst, [&relax, this](Instruction* uinst) {
     if (uinst->result_id() == 0 || !IsFloat(uinst, 32) ||
-        (!IsDecoratedRelaxed(uinst) && !IsRelaxed(uinst->result_id()))) {
+        (!IsDecoratedRelaxed(uinst) && !IsRelaxed(uinst->result_id())) ||
+        !CanRelaxOpOperands(uinst)) {
       relax = false;
       return;
     }
@@ -350,7 +364,7 @@
   };
   bool modified = context()->ProcessReachableCallTree(pfn);
   // If modified, make sure module has Float16 capability
-  if (modified) context()->AddCapability(SpvCapabilityFloat16);
+  if (modified) context()->AddCapability(spv::Capability::Float16);
   // Remove all RelaxedPrecision decorations from instructions and globals
   for (auto c_id : relaxed_ids_set_) {
     modified |= RemoveRelaxedDecoration(c_id);
@@ -371,44 +385,44 @@
 
 void ConvertToHalfPass::Initialize() {
   target_ops_core_ = {
-      SpvOpVectorExtractDynamic,
-      SpvOpVectorInsertDynamic,
-      SpvOpVectorShuffle,
-      SpvOpCompositeConstruct,
-      SpvOpCompositeInsert,
-      SpvOpCompositeExtract,
-      SpvOpCopyObject,
-      SpvOpTranspose,
-      SpvOpConvertSToF,
-      SpvOpConvertUToF,
-      // SpvOpFConvert,
-      // SpvOpQuantizeToF16,
-      SpvOpFNegate,
-      SpvOpFAdd,
-      SpvOpFSub,
-      SpvOpFMul,
-      SpvOpFDiv,
-      SpvOpFMod,
-      SpvOpVectorTimesScalar,
-      SpvOpMatrixTimesScalar,
-      SpvOpVectorTimesMatrix,
-      SpvOpMatrixTimesVector,
-      SpvOpMatrixTimesMatrix,
-      SpvOpOuterProduct,
-      SpvOpDot,
-      SpvOpSelect,
-      SpvOpFOrdEqual,
-      SpvOpFUnordEqual,
-      SpvOpFOrdNotEqual,
-      SpvOpFUnordNotEqual,
-      SpvOpFOrdLessThan,
-      SpvOpFUnordLessThan,
-      SpvOpFOrdGreaterThan,
-      SpvOpFUnordGreaterThan,
-      SpvOpFOrdLessThanEqual,
-      SpvOpFUnordLessThanEqual,
-      SpvOpFOrdGreaterThanEqual,
-      SpvOpFUnordGreaterThanEqual,
+      spv::Op::OpVectorExtractDynamic,
+      spv::Op::OpVectorInsertDynamic,
+      spv::Op::OpVectorShuffle,
+      spv::Op::OpCompositeConstruct,
+      spv::Op::OpCompositeInsert,
+      spv::Op::OpCompositeExtract,
+      spv::Op::OpCopyObject,
+      spv::Op::OpTranspose,
+      spv::Op::OpConvertSToF,
+      spv::Op::OpConvertUToF,
+      // spv::Op::OpFConvert,
+      // spv::Op::OpQuantizeToF16,
+      spv::Op::OpFNegate,
+      spv::Op::OpFAdd,
+      spv::Op::OpFSub,
+      spv::Op::OpFMul,
+      spv::Op::OpFDiv,
+      spv::Op::OpFMod,
+      spv::Op::OpVectorTimesScalar,
+      spv::Op::OpMatrixTimesScalar,
+      spv::Op::OpVectorTimesMatrix,
+      spv::Op::OpMatrixTimesVector,
+      spv::Op::OpMatrixTimesMatrix,
+      spv::Op::OpOuterProduct,
+      spv::Op::OpDot,
+      spv::Op::OpSelect,
+      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,
   };
   target_ops_450_ = {
       GLSLstd450Round, GLSLstd450RoundEven, GLSLstd450Trunc, GLSLstd450FAbs,
@@ -427,53 +441,53 @@
       GLSLstd450Ldexp, GLSLstd450Length, GLSLstd450Distance, GLSLstd450Cross,
       GLSLstd450Normalize, GLSLstd450FaceForward, GLSLstd450Reflect,
       GLSLstd450Refract, GLSLstd450NMin, GLSLstd450NMax, GLSLstd450NClamp};
-  image_ops_ = {SpvOpImageSampleImplicitLod,
-                SpvOpImageSampleExplicitLod,
-                SpvOpImageSampleDrefImplicitLod,
-                SpvOpImageSampleDrefExplicitLod,
-                SpvOpImageSampleProjImplicitLod,
-                SpvOpImageSampleProjExplicitLod,
-                SpvOpImageSampleProjDrefImplicitLod,
-                SpvOpImageSampleProjDrefExplicitLod,
-                SpvOpImageFetch,
-                SpvOpImageGather,
-                SpvOpImageDrefGather,
-                SpvOpImageRead,
-                SpvOpImageSparseSampleImplicitLod,
-                SpvOpImageSparseSampleExplicitLod,
-                SpvOpImageSparseSampleDrefImplicitLod,
-                SpvOpImageSparseSampleDrefExplicitLod,
-                SpvOpImageSparseSampleProjImplicitLod,
-                SpvOpImageSparseSampleProjExplicitLod,
-                SpvOpImageSparseSampleProjDrefImplicitLod,
-                SpvOpImageSparseSampleProjDrefExplicitLod,
-                SpvOpImageSparseFetch,
-                SpvOpImageSparseGather,
-                SpvOpImageSparseDrefGather,
-                SpvOpImageSparseTexelsResident,
-                SpvOpImageSparseRead};
+  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::OpImageSparseSampleImplicitLod,
+                spv::Op::OpImageSparseSampleExplicitLod,
+                spv::Op::OpImageSparseSampleDrefImplicitLod,
+                spv::Op::OpImageSparseSampleDrefExplicitLod,
+                spv::Op::OpImageSparseSampleProjImplicitLod,
+                spv::Op::OpImageSparseSampleProjExplicitLod,
+                spv::Op::OpImageSparseSampleProjDrefImplicitLod,
+                spv::Op::OpImageSparseSampleProjDrefExplicitLod,
+                spv::Op::OpImageSparseFetch,
+                spv::Op::OpImageSparseGather,
+                spv::Op::OpImageSparseDrefGather,
+                spv::Op::OpImageSparseTexelsResident,
+                spv::Op::OpImageSparseRead};
   dref_image_ops_ = {
-      SpvOpImageSampleDrefImplicitLod,
-      SpvOpImageSampleDrefExplicitLod,
-      SpvOpImageSampleProjDrefImplicitLod,
-      SpvOpImageSampleProjDrefExplicitLod,
-      SpvOpImageDrefGather,
-      SpvOpImageSparseSampleDrefImplicitLod,
-      SpvOpImageSparseSampleDrefExplicitLod,
-      SpvOpImageSparseSampleProjDrefImplicitLod,
-      SpvOpImageSparseSampleProjDrefExplicitLod,
-      SpvOpImageSparseDrefGather,
+      spv::Op::OpImageSampleDrefImplicitLod,
+      spv::Op::OpImageSampleDrefExplicitLod,
+      spv::Op::OpImageSampleProjDrefImplicitLod,
+      spv::Op::OpImageSampleProjDrefExplicitLod,
+      spv::Op::OpImageDrefGather,
+      spv::Op::OpImageSparseSampleDrefImplicitLod,
+      spv::Op::OpImageSparseSampleDrefExplicitLod,
+      spv::Op::OpImageSparseSampleProjDrefImplicitLod,
+      spv::Op::OpImageSparseSampleProjDrefExplicitLod,
+      spv::Op::OpImageSparseDrefGather,
   };
   closure_ops_ = {
-      SpvOpVectorExtractDynamic,
-      SpvOpVectorInsertDynamic,
-      SpvOpVectorShuffle,
-      SpvOpCompositeConstruct,
-      SpvOpCompositeInsert,
-      SpvOpCompositeExtract,
-      SpvOpCopyObject,
-      SpvOpTranspose,
-      SpvOpPhi,
+      spv::Op::OpVectorExtractDynamic,
+      spv::Op::OpVectorInsertDynamic,
+      spv::Op::OpVectorShuffle,
+      spv::Op::OpCompositeConstruct,
+      spv::Op::OpCompositeInsert,
+      spv::Op::OpCompositeExtract,
+      spv::Op::OpCopyObject,
+      spv::Op::OpTranspose,
+      spv::Op::OpPhi,
   };
   relaxed_ids_set_.clear();
   converted_ids_.clear();
diff --git a/source/opt/convert_to_half_pass.h b/source/opt/convert_to_half_pass.h
index c6e84d1..8e10c4f 100644
--- a/source/opt/convert_to_half_pass.h
+++ b/source/opt/convert_to_half_pass.h
@@ -45,6 +45,7 @@
   // Return true if |inst| returns scalar, vector or matrix type with base
   // float and |width|
   bool IsFloat(Instruction* inst, uint32_t width);
+  bool IsStruct(Instruction* inst);
 
   // Return true if |inst| is decorated with RelaxedPrecision
   bool IsDecoratedRelaxed(Instruction* inst);
@@ -55,6 +56,9 @@
   // Add |id| to the relaxed id set
   void AddRelaxed(uint32_t id);
 
+  // Return true if the instruction's operands can be relaxed
+  bool CanRelaxOpOperands(Instruction* inst);
+
   // Return type id for float with |width|
   analysis::Type* FloatScalarType(uint32_t width);
 
@@ -120,20 +124,26 @@
   // Initialize state for converting to half
   void Initialize();
 
+  struct hasher {
+    size_t operator()(const spv::Op& op) const noexcept {
+      return std::hash<uint32_t>()(uint32_t(op));
+    }
+  };
+
   // Set of core operations to be processed
-  std::unordered_set<uint32_t> target_ops_core_;
+  std::unordered_set<spv::Op, hasher> target_ops_core_;
 
   // Set of 450 extension operations to be processed
   std::unordered_set<uint32_t> target_ops_450_;
 
-  // Set of sample operations
-  std::unordered_set<uint32_t> image_ops_;
+  // Set of all sample operations, including dref and non-dref operations
+  std::unordered_set<spv::Op, hasher> image_ops_;
 
-  // Set of dref sample operations
-  std::unordered_set<uint32_t> dref_image_ops_;
+  // Set of only dref sample operations
+  std::unordered_set<spv::Op, hasher> dref_image_ops_;
 
-  // Set of dref sample operations
-  std::unordered_set<uint32_t> closure_ops_;
+  // Set of operations that can be marked as relaxed
+  std::unordered_set<spv::Op, hasher> closure_ops_;
 
   // Set of ids of all relaxed instructions
   std::unordered_set<uint32_t> relaxed_ids_set_;
diff --git a/source/opt/convert_to_sampled_image_pass.cpp b/source/opt/convert_to_sampled_image_pass.cpp
index e84d357..c82db41 100644
--- a/source/opt/convert_to_sampled_image_pass.cpp
+++ b/source/opt/convert_to_sampled_image_pass.cpp
@@ -16,7 +16,6 @@
 
 #include <cctype>
 #include <cstring>
-#include <tuple>
 
 #include "source/opt/ir_builder.h"
 #include "source/util/make_unique.h"
@@ -70,7 +69,7 @@
 Instruction* GetNonCopyObjectDef(analysis::DefUseManager* def_use_mgr,
                                  uint32_t inst_id) {
   Instruction* inst = def_use_mgr->GetDef(inst_id);
-  while (inst->opcode() == SpvOpCopyObject) {
+  while (inst->opcode() == spv::Op::OpCopyObject) {
     inst_id = inst->GetSingleWordInOperand(0u);
     inst = def_use_mgr->GetDef(inst_id);
   }
@@ -87,8 +86,9 @@
   bool found_binding_to_convert = false;
   for (auto decorate :
        decoration_manager->GetDecorationsFor(inst.result_id(), false)) {
-    uint32_t decoration = decorate->GetSingleWordInOperand(1u);
-    if (decoration == SpvDecorationDescriptorSet) {
+    spv::Decoration decoration =
+        spv::Decoration(decorate->GetSingleWordInOperand(1u));
+    if (decoration == spv::Decoration::DescriptorSet) {
       if (found_descriptor_set_to_convert) {
         assert(false && "A resource has two OpDecorate for the descriptor set");
         return false;
@@ -96,7 +96,7 @@
       descriptor_set_binding->descriptor_set =
           decorate->GetSingleWordInOperand(2u);
       found_descriptor_set_to_convert = true;
-    } else if (decoration == SpvDecorationBinding) {
+    } else if (decoration == spv::Decoration::Binding) {
       if (found_binding_to_convert) {
         assert(false && "A resource has two OpDecorate for the binding");
         return false;
@@ -116,7 +116,7 @@
 
 const analysis::Type* ConvertToSampledImagePass::GetVariableType(
     const Instruction& variable) const {
-  if (variable.opcode() != SpvOpVariable) return nullptr;
+  if (variable.opcode() != spv::Op::OpVariable) return nullptr;
   auto* type = context()->get_type_mgr()->GetType(variable.type_id());
   auto* pointer_type = type->AsPointer();
   if (!pointer_type) return nullptr;
@@ -124,12 +124,12 @@
   return pointer_type->pointee_type();
 }
 
-SpvStorageClass ConvertToSampledImagePass::GetStorageClass(
+spv::StorageClass ConvertToSampledImagePass::GetStorageClass(
     const Instruction& variable) const {
-  assert(variable.opcode() == SpvOpVariable);
+  assert(variable.opcode() == spv::Op::OpVariable);
   auto* type = context()->get_type_mgr()->GetType(variable.type_id());
   auto* pointer_type = type->AsPointer();
-  if (!pointer_type) return SpvStorageClassMax;
+  if (!pointer_type) return spv::StorageClass::Max;
 
   return pointer_type->storage_class();
 }
@@ -205,12 +205,12 @@
 
 void ConvertToSampledImagePass::FindUses(const Instruction* inst,
                                          std::vector<Instruction*>* uses,
-                                         uint32_t user_opcode) const {
+                                         spv::Op user_opcode) const {
   auto* def_use_mgr = context()->get_def_use_mgr();
   def_use_mgr->ForEachUser(inst, [uses, user_opcode, this](Instruction* user) {
     if (user->opcode() == user_opcode) {
       uses->push_back(user);
-    } else if (user->opcode() == SpvOpCopyObject) {
+    } else if (user->opcode() == spv::Op::OpCopyObject) {
       FindUses(user, uses, user_opcode);
     }
   });
@@ -221,21 +221,21 @@
   auto* def_use_mgr = context()->get_def_use_mgr();
   def_use_mgr->ForEachUser(image, [uses, this](Instruction* user) {
     switch (user->opcode()) {
-      case SpvOpImageFetch:
-      case SpvOpImageRead:
-      case SpvOpImageWrite:
-      case SpvOpImageQueryFormat:
-      case SpvOpImageQueryOrder:
-      case SpvOpImageQuerySizeLod:
-      case SpvOpImageQuerySize:
-      case SpvOpImageQueryLevels:
-      case SpvOpImageQuerySamples:
-      case SpvOpImageSparseFetch:
+      case spv::Op::OpImageFetch:
+      case spv::Op::OpImageRead:
+      case spv::Op::OpImageWrite:
+      case spv::Op::OpImageQueryFormat:
+      case spv::Op::OpImageQueryOrder:
+      case spv::Op::OpImageQuerySizeLod:
+      case spv::Op::OpImageQuerySize:
+      case spv::Op::OpImageQueryLevels:
+      case spv::Op::OpImageQuerySamples:
+      case spv::Op::OpImageSparseFetch:
         uses->push_back(user);
       default:
         break;
     }
-    if (user->opcode() == SpvOpCopyObject) {
+    if (user->opcode() == spv::Op::OpCopyObject) {
       FindUsesOfImage(user, uses);
     }
   });
@@ -248,7 +248,7 @@
       IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
   return builder.AddUnaryOp(
       GetImageTypeOfSampledImage(context()->get_type_mgr(), sampled_image),
-      SpvOpImage, sampled_image->result_id());
+      spv::Op::OpImage, sampled_image->result_id());
 }
 
 uint32_t ConvertToSampledImagePass::GetSampledImageTypeForImage(
@@ -284,7 +284,7 @@
   auto* def_use_mgr = context()->get_def_use_mgr();
   uint32_t sampler_id = sampled_image_inst->GetSingleWordInOperand(1u);
   auto* sampler_load = def_use_mgr->GetDef(sampler_id);
-  if (sampler_load->opcode() != SpvOpLoad) return false;
+  if (sampler_load->opcode() != spv::Op::OpLoad) return false;
   auto* sampler = def_use_mgr->GetDef(sampler_load->GetSingleWordInOperand(0u));
   DescriptorSetAndBinding sampler_descriptor_set_binding;
   return GetDescriptorSetBinding(*sampler, &sampler_descriptor_set_binding) &&
@@ -295,7 +295,7 @@
     Instruction* image_load, Instruction* image_extraction,
     const DescriptorSetAndBinding& image_descriptor_set_binding) {
   std::vector<Instruction*> sampled_image_users;
-  FindUses(image_load, &sampled_image_users, SpvOpSampledImage);
+  FindUses(image_load, &sampled_image_users, spv::Op::OpSampledImage);
 
   auto* def_use_mgr = context()->get_def_use_mgr();
   for (auto* sampled_image_inst : sampled_image_users) {
@@ -328,7 +328,7 @@
       context()->get_type_mgr()->GetType(sampled_image_type_id);
   if (sampled_image_type == nullptr) return false;
   auto storage_class = GetStorageClass(*image_variable);
-  if (storage_class == SpvStorageClassMax) return false;
+  if (storage_class == spv::StorageClass::Max) return false;
   analysis::Pointer sampled_image_pointer(sampled_image_type, storage_class);
 
   // Make sure |image_variable| is behind its type i.e., avoid the forward
@@ -343,7 +343,7 @@
     Instruction* image_variable,
     const DescriptorSetAndBinding& descriptor_set_binding) {
   std::vector<Instruction*> image_variable_loads;
-  FindUses(image_variable, &image_variable_loads, SpvOpLoad);
+  FindUses(image_variable, &image_variable_loads, spv::Op::OpLoad);
   if (image_variable_loads.empty()) return Status::SuccessWithoutChange;
 
   const uint32_t sampled_image_type_id =
@@ -364,14 +364,14 @@
 
 bool ConvertToSampledImagePass::DoesSampledImageReferenceImage(
     Instruction* sampled_image_inst, Instruction* image_variable) {
-  if (sampled_image_inst->opcode() != SpvOpSampledImage) return false;
+  if (sampled_image_inst->opcode() != spv::Op::OpSampledImage) return false;
   auto* def_use_mgr = context()->get_def_use_mgr();
   auto* image_load = GetNonCopyObjectDef(
       def_use_mgr, sampled_image_inst->GetSingleWordInOperand(0u));
-  if (image_load->opcode() != SpvOpLoad) return false;
+  if (image_load->opcode() != spv::Op::OpLoad) return false;
   auto* image =
       GetNonCopyObjectDef(def_use_mgr, image_load->GetSingleWordInOperand(0u));
-  return image->opcode() == SpvOpVariable &&
+  return image->opcode() == spv::Op::OpVariable &&
          image->result_id() == image_variable->result_id();
 }
 
@@ -381,10 +381,10 @@
   if (image_to_be_combined_with == nullptr) return Status::Failure;
 
   std::vector<Instruction*> sampler_variable_loads;
-  FindUses(sampler_variable, &sampler_variable_loads, SpvOpLoad);
+  FindUses(sampler_variable, &sampler_variable_loads, spv::Op::OpLoad);
   for (auto* load : sampler_variable_loads) {
     std::vector<Instruction*> sampled_image_users;
-    FindUses(load, &sampled_image_users, SpvOpSampledImage);
+    FindUses(load, &sampled_image_users, spv::Op::OpSampledImage);
     for (auto* sampled_image_inst : sampled_image_users) {
       if (!DoesSampledImageReferenceImage(sampled_image_inst,
                                           image_to_be_combined_with)) {
diff --git a/source/opt/convert_to_sampled_image_pass.h b/source/opt/convert_to_sampled_image_pass.h
index d3938af..a8b1501 100644
--- a/source/opt/convert_to_sampled_image_pass.h
+++ b/source/opt/convert_to_sampled_image_pass.h
@@ -120,13 +120,13 @@
   const analysis::Type* GetVariableType(const Instruction& variable) const;
 
   // Returns the storage class of |variable|.
-  SpvStorageClass GetStorageClass(const Instruction& variable) const;
+  spv::StorageClass GetStorageClass(const Instruction& variable) const;
 
   // Finds |inst|'s users whose opcode is |user_opcode| or users of OpCopyObject
   // instructions of |inst| whose opcode is |user_opcode| and puts them in
   // |uses|.
   void FindUses(const Instruction* inst, std::vector<Instruction*>* uses,
-                uint32_t user_opcode) const;
+                spv::Op user_opcode) const;
 
   // Finds OpImage* instructions using |image| or OpCopyObject instructions that
   // copy |image| and puts them in |uses|.
diff --git a/source/opt/copy_prop_arrays.cpp b/source/opt/copy_prop_arrays.cpp
index 0b23562..66a268f 100644
--- a/source/opt/copy_prop_arrays.cpp
+++ b/source/opt/copy_prop_arrays.cpp
@@ -22,12 +22,12 @@
 namespace opt {
 namespace {
 
-const uint32_t kLoadPointerInOperand = 0;
-const uint32_t kStorePointerInOperand = 0;
-const uint32_t kStoreObjectInOperand = 1;
-const uint32_t kCompositeExtractObjectInOperand = 0;
-const uint32_t kTypePointerStorageClassInIdx = 0;
-const uint32_t kTypePointerPointeeInIdx = 1;
+constexpr uint32_t kLoadPointerInOperand = 0;
+constexpr uint32_t kStorePointerInOperand = 0;
+constexpr uint32_t kStoreObjectInOperand = 1;
+constexpr uint32_t kCompositeExtractObjectInOperand = 0;
+constexpr uint32_t kTypePointerStorageClassInIdx = 0;
+constexpr uint32_t kTypePointerPointeeInIdx = 1;
 
 bool IsDebugDeclareOrValue(Instruction* di) {
   auto dbg_opcode = di->GetCommonDebugOpcode();
@@ -46,8 +46,8 @@
 
     BasicBlock* entry_bb = &*function.begin();
 
-    for (auto var_inst = entry_bb->begin(); var_inst->opcode() == SpvOpVariable;
-         ++var_inst) {
+    for (auto var_inst = entry_bb->begin();
+         var_inst->opcode() == spv::Op::OpVariable; ++var_inst) {
       if (!IsPointerToArrayType(var_inst->type_id())) {
         continue;
       }
@@ -76,7 +76,7 @@
 std::unique_ptr<CopyPropagateArrays::MemoryObject>
 CopyPropagateArrays::FindSourceObjectIfPossible(Instruction* var_inst,
                                                 Instruction* store_inst) {
-  assert(var_inst->opcode() == SpvOpVariable && "Expecting a variable.");
+  assert(var_inst->opcode() == spv::Op::OpVariable && "Expecting a variable.");
 
   // Check that the variable is a composite object where |store_inst|
   // dominates all of its loads.
@@ -114,7 +114,7 @@
   Instruction* store_inst = nullptr;
   get_def_use_mgr()->WhileEachUser(
       var_inst, [&store_inst, var_inst](Instruction* use) {
-        if (use->opcode() == SpvOpStore &&
+        if (use->opcode() == spv::Op::OpStore &&
             use->GetSingleWordInOperand(kStorePointerInOperand) ==
                 var_inst->result_id()) {
           if (store_inst == nullptr) {
@@ -132,7 +132,7 @@
 void CopyPropagateArrays::PropagateObject(Instruction* var_inst,
                                           MemoryObject* source,
                                           Instruction* insertion_point) {
-  assert(var_inst->opcode() == SpvOpVariable &&
+  assert(var_inst->opcode() == spv::Op::OpVariable &&
          "This function propagates variables.");
 
   Instruction* new_access_chain = BuildNewAccessChain(insertion_point, source);
@@ -166,17 +166,17 @@
 
 bool CopyPropagateArrays::HasNoStores(Instruction* ptr_inst) {
   return get_def_use_mgr()->WhileEachUser(ptr_inst, [this](Instruction* use) {
-    if (use->opcode() == SpvOpLoad) {
+    if (use->opcode() == spv::Op::OpLoad) {
       return true;
-    } else if (use->opcode() == SpvOpAccessChain) {
+    } else if (use->opcode() == spv::Op::OpAccessChain) {
       return HasNoStores(use);
-    } else if (use->IsDecoration() || use->opcode() == SpvOpName) {
+    } else if (use->IsDecoration() || use->opcode() == spv::Op::OpName) {
       return true;
-    } else if (use->opcode() == SpvOpStore) {
+    } else if (use->opcode() == spv::Op::OpStore) {
       return false;
-    } else if (use->opcode() == SpvOpImageTexelPointer) {
+    } else if (use->opcode() == spv::Op::OpImageTexelPointer) {
       return true;
-    } else if (use->opcode() == SpvOpEntryPoint) {
+    } else if (use->opcode() == spv::Op::OpEntryPoint) {
       return true;
     }
     // Some other instruction.  Be conservative.
@@ -193,19 +193,19 @@
   return get_def_use_mgr()->WhileEachUser(
       ptr_inst,
       [this, store_inst, dominator_analysis, ptr_inst](Instruction* use) {
-        if (use->opcode() == SpvOpLoad ||
-            use->opcode() == SpvOpImageTexelPointer) {
+        if (use->opcode() == spv::Op::OpLoad ||
+            use->opcode() == spv::Op::OpImageTexelPointer) {
           // TODO: If there are many load in the same BB as |store_inst| the
           // time to do the multiple traverses can add up.  Consider collecting
           // those loads and doing a single traversal.
           return dominator_analysis->Dominates(store_inst, use);
-        } else if (use->opcode() == SpvOpAccessChain) {
+        } else if (use->opcode() == spv::Op::OpAccessChain) {
           return HasValidReferencesOnly(use, store_inst);
-        } else if (use->IsDecoration() || use->opcode() == SpvOpName) {
+        } else if (use->IsDecoration() || use->opcode() == spv::Op::OpName) {
           return true;
-        } else if (use->opcode() == SpvOpStore) {
+        } else if (use->opcode() == spv::Op::OpStore) {
           // If we are storing to part of the object it is not an candidate.
-          return ptr_inst->opcode() == SpvOpVariable &&
+          return ptr_inst->opcode() == spv::Op::OpVariable &&
                  store_inst->GetSingleWordInOperand(kStorePointerInOperand) ==
                      ptr_inst->result_id();
         } else if (IsDebugDeclareOrValue(use)) {
@@ -221,15 +221,15 @@
   Instruction* result_inst = context()->get_def_use_mgr()->GetDef(result);
 
   switch (result_inst->opcode()) {
-    case SpvOpLoad:
+    case spv::Op::OpLoad:
       return BuildMemoryObjectFromLoad(result_inst);
-    case SpvOpCompositeExtract:
+    case spv::Op::OpCompositeExtract:
       return BuildMemoryObjectFromExtract(result_inst);
-    case SpvOpCompositeConstruct:
+    case spv::Op::OpCompositeConstruct:
       return BuildMemoryObjectFromCompositeConstruct(result_inst);
-    case SpvOpCopyObject:
+    case spv::Op::OpCopyObject:
       return GetSourceObjectIfAny(result_inst->GetSingleWordInOperand(0));
-    case SpvOpCompositeInsert:
+    case spv::Op::OpCompositeInsert:
       return BuildMemoryObjectFromInsert(result_inst);
     default:
       return nullptr;
@@ -251,7 +251,7 @@
   //
   // It is built in reverse order because the different |OpAccessChain|
   // instructions are visited in reverse order from which they are applied.
-  while (current_inst->opcode() == SpvOpAccessChain) {
+  while (current_inst->opcode() == spv::Op::OpAccessChain) {
     for (uint32_t i = current_inst->NumInOperands() - 1; i >= 1; --i) {
       uint32_t element_index_id = current_inst->GetSingleWordInOperand(i);
       components_in_reverse.push_back(element_index_id);
@@ -263,7 +263,7 @@
   // instruction followed by a series of |OpAccessChain| instructions, then
   // return |nullptr| because we cannot identify the owner or access chain
   // exactly.
-  if (current_inst->opcode() != SpvOpVariable) {
+  if (current_inst->opcode() != spv::Op::OpVariable) {
     return nullptr;
   }
 
@@ -276,7 +276,7 @@
 
 std::unique_ptr<CopyPropagateArrays::MemoryObject>
 CopyPropagateArrays::BuildMemoryObjectFromExtract(Instruction* extract_inst) {
-  assert(extract_inst->opcode() == SpvOpCompositeExtract &&
+  assert(extract_inst->opcode() == spv::Op::OpCompositeExtract &&
          "Expecting an OpCompositeExtract instruction.");
   std::unique_ptr<MemoryObject> result = GetSourceObjectIfAny(
       extract_inst->GetSingleWordInOperand(kCompositeExtractObjectInOperand));
@@ -297,7 +297,7 @@
 std::unique_ptr<CopyPropagateArrays::MemoryObject>
 CopyPropagateArrays::BuildMemoryObjectFromCompositeConstruct(
     Instruction* conststruct_inst) {
-  assert(conststruct_inst->opcode() == SpvOpCompositeConstruct &&
+  assert(conststruct_inst->opcode() == spv::Op::OpCompositeConstruct &&
          "Expecting an OpCompositeConstruct instruction.");
 
   // If every operand in the instruction are part of the same memory object, and
@@ -352,7 +352,7 @@
 
 std::unique_ptr<CopyPropagateArrays::MemoryObject>
 CopyPropagateArrays::BuildMemoryObjectFromInsert(Instruction* insert_inst) {
-  assert(insert_inst->opcode() == SpvOpCompositeInsert &&
+  assert(insert_inst->opcode() == spv::Op::OpCompositeInsert &&
          "Expecting an OpCompositeInsert instruction.");
 
   analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
@@ -407,7 +407,7 @@
   Instruction* current_insert =
       def_use_mgr->GetDef(insert_inst->GetSingleWordInOperand(1));
   for (uint32_t i = number_of_elements - 1; i > 0; --i) {
-    if (current_insert->opcode() != SpvOpCompositeInsert) {
+    if (current_insert->opcode() != spv::Op::OpCompositeInsert) {
       return nullptr;
     }
 
@@ -500,7 +500,7 @@
     if (IsDebugDeclareOrValue(use)) return true;
 
     switch (use->opcode()) {
-      case SpvOpLoad: {
+      case spv::Op::OpLoad: {
         analysis::Pointer* pointer_type = type->AsPointer();
         uint32_t new_type_id = type_mgr->GetId(pointer_type->pointee_type());
 
@@ -509,7 +509,7 @@
         }
         return true;
       }
-      case SpvOpAccessChain: {
+      case spv::Op::OpAccessChain: {
         analysis::Pointer* pointer_type = type->AsPointer();
         const analysis::Type* pointee_type = pointer_type->pointee_type();
 
@@ -547,7 +547,7 @@
         }
         return true;
       }
-      case SpvOpCompositeExtract: {
+      case spv::Op::OpCompositeExtract: {
         std::vector<uint32_t> access_chain;
         for (uint32_t i = 1; i < use->NumInOperands(); ++i) {
           access_chain.push_back(use->GetSingleWordInOperand(i));
@@ -565,13 +565,13 @@
         }
         return true;
       }
-      case SpvOpStore:
+      case spv::Op::OpStore:
         // If needed, we can create an element-by-element copy to change the
         // type of the value being stored.  This way we can always handled
         // stores.
         return true;
-      case SpvOpImageTexelPointer:
-      case SpvOpName:
+      case spv::Op::OpImageTexelPointer:
+      case spv::Op::OpName:
         return true;
       default:
         return use->IsDecoration();
@@ -598,8 +598,8 @@
     if (use->IsCommonDebugInstr()) {
       switch (use->GetCommonDebugOpcode()) {
         case CommonDebugInfoDebugDeclare: {
-          if (new_ptr_inst->opcode() == SpvOpVariable ||
-              new_ptr_inst->opcode() == SpvOpFunctionParameter) {
+          if (new_ptr_inst->opcode() == spv::Op::OpVariable ||
+              new_ptr_inst->opcode() == spv::Op::OpFunctionParameter) {
             context()->ForgetUses(use);
             use->SetOperand(index, {new_ptr_inst->result_id()});
             context()->AnalyzeUses(use);
@@ -640,7 +640,7 @@
     }
 
     switch (use->opcode()) {
-      case SpvOpLoad: {
+      case spv::Op::OpLoad: {
         // Replace the actual use.
         context()->ForgetUses(use);
         use->SetOperand(index, {new_ptr_inst->result_id()});
@@ -658,7 +658,7 @@
           context()->AnalyzeUses(use);
         }
       } break;
-      case SpvOpAccessChain: {
+      case spv::Op::OpAccessChain: {
         // Update the actual use.
         context()->ForgetUses(use);
         use->SetOperand(index, {new_ptr_inst->result_id()});
@@ -685,7 +685,7 @@
             pointer_type_inst->GetSingleWordInOperand(kTypePointerPointeeInIdx),
             access_chain);
 
-        SpvStorageClass storage_class = static_cast<SpvStorageClass>(
+        spv::StorageClass storage_class = static_cast<spv::StorageClass>(
             pointer_type_inst->GetSingleWordInOperand(
                 kTypePointerStorageClassInIdx));
 
@@ -700,7 +700,7 @@
           context()->AnalyzeUses(use);
         }
       } break;
-      case SpvOpCompositeExtract: {
+      case spv::Op::OpCompositeExtract: {
         // Update the actual use.
         context()->ForgetUses(use);
         use->SetOperand(index, {new_ptr_inst->result_id()});
@@ -721,7 +721,7 @@
           context()->AnalyzeUses(use);
         }
       } break;
-      case SpvOpStore:
+      case spv::Op::OpStore:
         // If the use is the pointer, then it is the single store to that
         // variable.  We do not want to replace it.  Instead, it will become
         // dead after all of the loads are removed, and ADCE will get rid of it.
@@ -744,11 +744,11 @@
           context()->AnalyzeUses(use);
         }
         break;
-      case SpvOpDecorate:
+      case spv::Op::OpDecorate:
       // We treat an OpImageTexelPointer as a load.  The result type should
       // always have the Image storage class, and should not need to be
       // updated.
-      case SpvOpImageTexelPointer:
+      case spv::Op::OpImageTexelPointer:
         // Replace the actual use.
         context()->ForgetUses(use);
         use->SetOperand(index, {new_ptr_inst->result_id()});
@@ -766,13 +766,13 @@
   for (uint32_t element_index : access_chain) {
     Instruction* type_inst = get_def_use_mgr()->GetDef(id);
     switch (type_inst->opcode()) {
-      case SpvOpTypeArray:
-      case SpvOpTypeRuntimeArray:
-      case SpvOpTypeMatrix:
-      case SpvOpTypeVector:
+      case spv::Op::OpTypeArray:
+      case spv::Op::OpTypeRuntimeArray:
+      case spv::Op::OpTypeMatrix:
+      case spv::Op::OpTypeVector:
         id = type_inst->GetSingleWordInOperand(0);
         break;
-      case SpvOpTypeStruct:
+      case spv::Op::OpTypeStruct:
         id = type_inst->GetSingleWordInOperand(element_index);
         break;
       default:
diff --git a/source/opt/copy_prop_arrays.h b/source/opt/copy_prop_arrays.h
index 9e7641f..7486f80 100644
--- a/source/opt/copy_prop_arrays.h
+++ b/source/opt/copy_prop_arrays.h
@@ -134,13 +134,13 @@
           var_pointer_inst->GetSingleWordInOperand(1), GetAccessIds());
 
       uint32_t member_pointer_type_id = type_mgr->FindPointerToType(
-          member_type_id, static_cast<SpvStorageClass>(
+          member_type_id, static_cast<spv::StorageClass>(
                               var_pointer_inst->GetSingleWordInOperand(0)));
       return member_pointer_type_id;
     }
 
     // Returns the storage class of the memory object.
-    SpvStorageClass GetStorageClass() const {
+    spv::StorageClass GetStorageClass() const {
       analysis::TypeManager* type_mgr =
           GetVariable()->context()->get_type_mgr();
       const analysis::Pointer* pointer_type =
diff --git a/source/opt/dataflow.cpp b/source/opt/dataflow.cpp
index c91fad0..63737f1 100644
--- a/source/opt/dataflow.cpp
+++ b/source/opt/dataflow.cpp
@@ -14,7 +14,6 @@
 
 #include "source/opt/dataflow.h"
 
-#include <algorithm>
 #include <cstdint>
 
 namespace spvtools {
@@ -78,7 +77,7 @@
 }
 
 void ForwardDataFlowAnalysis::EnqueueBlockSuccessors(Instruction* inst) {
-  if (inst->opcode() != SpvOpLabel) return;
+  if (inst->opcode() != spv::Op::OpLabel) return;
   context()
       .cfg()
       ->block(inst->result_id())
diff --git a/source/opt/dead_branch_elim_pass.cpp b/source/opt/dead_branch_elim_pass.cpp
index d99b7f7..1526b9e 100644
--- a/source/opt/dead_branch_elim_pass.cpp
+++ b/source/opt/dead_branch_elim_pass.cpp
@@ -23,34 +23,30 @@
 
 #include "source/cfa.h"
 #include "source/opt/ir_context.h"
-#include "source/opt/iterator.h"
 #include "source/opt/struct_cfg_analysis.h"
 #include "source/util/make_unique.h"
 
 namespace spvtools {
 namespace opt {
-
 namespace {
-
-const uint32_t kBranchCondTrueLabIdInIdx = 1;
-const uint32_t kBranchCondFalseLabIdInIdx = 2;
-
-}  // anonymous namespace
+constexpr uint32_t kBranchCondTrueLabIdInIdx = 1;
+constexpr uint32_t kBranchCondFalseLabIdInIdx = 2;
+}  // namespace
 
 bool DeadBranchElimPass::GetConstCondition(uint32_t condId, bool* condVal) {
   bool condIsConst;
   Instruction* cInst = get_def_use_mgr()->GetDef(condId);
   switch (cInst->opcode()) {
-    case SpvOpConstantNull:
-    case SpvOpConstantFalse: {
+    case spv::Op::OpConstantNull:
+    case spv::Op::OpConstantFalse: {
       *condVal = false;
       condIsConst = true;
     } break;
-    case SpvOpConstantTrue: {
+    case spv::Op::OpConstantTrue: {
       *condVal = true;
       condIsConst = true;
     } break;
-    case SpvOpLogicalNot: {
+    case spv::Op::OpLogicalNot: {
       bool negVal;
       condIsConst =
           GetConstCondition(cInst->GetSingleWordInOperand(0), &negVal);
@@ -65,13 +61,13 @@
   Instruction* sInst = get_def_use_mgr()->GetDef(selId);
   uint32_t typeId = sInst->type_id();
   Instruction* typeInst = get_def_use_mgr()->GetDef(typeId);
-  if (!typeInst || (typeInst->opcode() != SpvOpTypeInt)) return false;
+  if (!typeInst || (typeInst->opcode() != spv::Op::OpTypeInt)) return false;
   // TODO(greg-lunarg): Support non-32 bit ints
   if (typeInst->GetSingleWordInOperand(0) != 32) return false;
-  if (sInst->opcode() == SpvOpConstant) {
+  if (sInst->opcode() == spv::Op::OpConstant) {
     *selVal = sInst->GetSingleWordInOperand(0);
     return true;
-  } else if (sInst->opcode() == SpvOpConstantNull) {
+  } else if (sInst->opcode() == spv::Op::OpConstantNull) {
     *selVal = 0;
     return true;
   }
@@ -81,7 +77,7 @@
 void DeadBranchElimPass::AddBranch(uint32_t labelId, BasicBlock* bp) {
   assert(get_def_use_mgr()->GetDef(labelId) != nullptr);
   std::unique_ptr<Instruction> newBranch(
-      new Instruction(context(), SpvOpBranch, 0, 0,
+      new Instruction(context(), spv::Op::OpBranch, 0, 0,
                       {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {labelId}}}));
   context()->AnalyzeDefUse(&*newBranch);
   context()->set_instr_block(&*newBranch, bp);
@@ -115,13 +111,13 @@
     Instruction* terminator = block->terminator();
     uint32_t live_lab_id = 0;
     // Check if the terminator has a single valid successor.
-    if (terminator->opcode() == SpvOpBranchConditional) {
+    if (terminator->opcode() == spv::Op::OpBranchConditional) {
       bool condVal;
       if (GetConstCondition(terminator->GetSingleWordInOperand(0u), &condVal)) {
         live_lab_id = terminator->GetSingleWordInOperand(
             condVal ? kBranchCondTrueLabIdInIdx : kBranchCondFalseLabIdInIdx);
       }
-    } else if (terminator->opcode() == SpvOpSwitch) {
+    } else if (terminator->opcode() == spv::Op::OpSwitch) {
       uint32_t sel_val;
       if (GetConstInteger(terminator->GetSingleWordInOperand(0u), &sel_val)) {
         // Search switch operands for selector value, set live_lab_id to
@@ -194,8 +190,8 @@
                                         uint32_t live_lab_id) {
   Instruction* merge_inst = block->GetMergeInst();
   Instruction* terminator = block->terminator();
-  if (merge_inst && merge_inst->opcode() == SpvOpSelectionMerge) {
-    if (merge_inst->NextNode()->opcode() == SpvOpSwitch &&
+  if (merge_inst && merge_inst->opcode() == spv::Op::OpSelectionMerge) {
+    if (merge_inst->NextNode()->opcode() == spv::Op::OpSwitch &&
         SwitchHasNestedBreak(block->id())) {
       if (terminator->NumInOperands() == 2) {
         // We cannot remove the branch, and it already has a single case, so no
@@ -266,7 +262,7 @@
   for (auto& block : *func) {
     if (live_blocks.count(&block)) {
       for (auto iter = block.begin(); iter != block.end();) {
-        if (iter->opcode() != SpvOpPhi) {
+        if (iter->opcode() != spv::Op::OpPhi) {
           break;
         }
 
@@ -292,7 +288,7 @@
               cont_iter->second == &block && inst->NumInOperands() > 4) {
             if (get_def_use_mgr()
                     ->GetDef(inst->GetSingleWordInOperand(i - 1))
-                    ->opcode() == SpvOpUndef) {
+                    ->opcode() == spv::Op::OpUndef) {
               // Already undef incoming value, no change necessary.
               operands.push_back(inst->GetInOperand(i - 1));
               operands.push_back(inst->GetInOperand(i));
@@ -378,14 +374,14 @@
     if (unreachable_continues.count(&*ebi)) {
       uint32_t cont_id = unreachable_continues.find(&*ebi)->second->id();
       if (ebi->begin() != ebi->tail() ||
-          ebi->terminator()->opcode() != SpvOpBranch ||
+          ebi->terminator()->opcode() != spv::Op::OpBranch ||
           ebi->terminator()->GetSingleWordInOperand(0u) != cont_id) {
         // Make unreachable, but leave the label.
         KillAllInsts(&*ebi, false);
         // Add unconditional branch to header.
         assert(unreachable_continues.count(&*ebi));
         ebi->AddInstruction(MakeUnique<Instruction>(
-            context(), SpvOpBranch, 0, 0,
+            context(), spv::Op::OpBranch, 0, 0,
             std::initializer_list<Operand>{{SPV_OPERAND_TYPE_ID, {cont_id}}}));
         get_def_use_mgr()->AnalyzeInstUse(&*ebi->tail());
         context()->set_instr_block(&*ebi->tail(), &*ebi);
@@ -394,12 +390,12 @@
       ++ebi;
     } else if (unreachable_merges.count(&*ebi)) {
       if (ebi->begin() != ebi->tail() ||
-          ebi->terminator()->opcode() != SpvOpUnreachable) {
+          ebi->terminator()->opcode() != spv::Op::OpUnreachable) {
         // Make unreachable, but leave the label.
         KillAllInsts(&*ebi, false);
         // Add unreachable terminator.
         ebi->AddInstruction(
-            MakeUnique<Instruction>(context(), SpvOpUnreachable, 0, 0,
+            MakeUnique<Instruction>(context(), spv::Op::OpUnreachable, 0, 0,
                                     std::initializer_list<Operand>{}));
         context()->AnalyzeUses(ebi->terminator());
         context()->set_instr_block(ebi->terminator(), &*ebi);
@@ -465,7 +461,7 @@
   };
 
   // Structured order is more intuitive so use it where possible.
-  if (context()->get_feature_mgr()->HasCapability(SpvCapabilityShader)) {
+  if (context()->get_feature_mgr()->HasCapability(spv::Capability::Shader)) {
     context()->ProcessReachableCallTree(reorder_structured);
   } else {
     context()->ProcessReachableCallTree(reorder_dominators);
@@ -477,7 +473,8 @@
   // support required in KillNamesAndDecorates().
   // TODO(greg-lunarg): Add support for OpGroupDecorate
   for (auto& ai : get_module()->annotations())
-    if (ai.opcode() == SpvOpGroupDecorate) return Status::SuccessWithoutChange;
+    if (ai.opcode() == spv::Op::OpGroupDecorate)
+      return Status::SuccessWithoutChange;
   // Process all entry point functions
   ProcessFunction pfn = [this](Function* fp) {
     return EliminateDeadBranches(fp);
@@ -501,7 +498,7 @@
     Instruction* branch = start_block->terminator();
     uint32_t next_block_id = 0;
     switch (branch->opcode()) {
-      case SpvOpBranchConditional:
+      case spv::Op::OpBranchConditional:
         next_block_id = start_block->MergeBlockIdIfAny();
         if (next_block_id == 0) {
           // If a possible target is the |loop_merge_id| or |loop_continue_id|,
@@ -530,7 +527,7 @@
           }
         }
         break;
-      case SpvOpSwitch:
+      case spv::Op::OpSwitch:
         next_block_id = start_block->MergeBlockIdIfAny();
         if (next_block_id == 0) {
           // A switch with no merge instructions can have at most 5 targets:
@@ -578,7 +575,7 @@
           // The fall through is case 3.
         }
         break;
-      case SpvOpBranch:
+      case spv::Op::OpBranch:
         // Need to check if this is the header of a loop nested in the
         // selection construct.
         next_block_id = start_block->MergeBlockIdIfAny();
diff --git a/source/opt/dead_insert_elim_pass.cpp b/source/opt/dead_insert_elim_pass.cpp
index d877f0f..a486903 100644
--- a/source/opt/dead_insert_elim_pass.cpp
+++ b/source/opt/dead_insert_elim_pass.cpp
@@ -23,32 +23,29 @@
 
 namespace spvtools {
 namespace opt {
-
 namespace {
-
-const uint32_t kTypeVectorCountInIdx = 1;
-const uint32_t kTypeMatrixCountInIdx = 1;
-const uint32_t kTypeArrayLengthIdInIdx = 1;
-const uint32_t kTypeIntWidthInIdx = 0;
-const uint32_t kConstantValueInIdx = 0;
-const uint32_t kInsertObjectIdInIdx = 0;
-const uint32_t kInsertCompositeIdInIdx = 1;
-
-}  // anonymous namespace
+constexpr uint32_t kTypeVectorCountInIdx = 1;
+constexpr uint32_t kTypeMatrixCountInIdx = 1;
+constexpr uint32_t kTypeArrayLengthIdInIdx = 1;
+constexpr uint32_t kTypeIntWidthInIdx = 0;
+constexpr uint32_t kConstantValueInIdx = 0;
+constexpr uint32_t kInsertObjectIdInIdx = 0;
+constexpr uint32_t kInsertCompositeIdInIdx = 1;
+}  // namespace
 
 uint32_t DeadInsertElimPass::NumComponents(Instruction* typeInst) {
   switch (typeInst->opcode()) {
-    case SpvOpTypeVector: {
+    case spv::Op::OpTypeVector: {
       return typeInst->GetSingleWordInOperand(kTypeVectorCountInIdx);
     } break;
-    case SpvOpTypeMatrix: {
+    case spv::Op::OpTypeMatrix: {
       return typeInst->GetSingleWordInOperand(kTypeMatrixCountInIdx);
     } break;
-    case SpvOpTypeArray: {
+    case spv::Op::OpTypeArray: {
       uint32_t lenId =
           typeInst->GetSingleWordInOperand(kTypeArrayLengthIdInIdx);
       Instruction* lenInst = get_def_use_mgr()->GetDef(lenId);
-      if (lenInst->opcode() != SpvOpConstant) return 0;
+      if (lenInst->opcode() != spv::Op::OpConstant) return 0;
       uint32_t lenTypeId = lenInst->type_id();
       Instruction* lenTypeInst = get_def_use_mgr()->GetDef(lenTypeId);
       // TODO(greg-lunarg): Support non-32-bit array length
@@ -56,7 +53,7 @@
         return 0;
       return lenInst->GetSingleWordInOperand(kConstantValueInIdx);
     } break;
-    case SpvOpTypeStruct: {
+    case spv::Op::OpTypeStruct: {
       return typeInst->NumInOperands();
     } break;
     default: { return 0; } break;
@@ -68,10 +65,10 @@
     uint32_t extOffset, std::unordered_set<uint32_t>* visited_phis) {
   // Not currently optimizing array inserts.
   Instruction* typeInst = get_def_use_mgr()->GetDef(insertChain->type_id());
-  if (typeInst->opcode() == SpvOpTypeArray) return;
+  if (typeInst->opcode() == spv::Op::OpTypeArray) return;
   // Insert chains are only composed of inserts and phis
-  if (insertChain->opcode() != SpvOpCompositeInsert &&
-      insertChain->opcode() != SpvOpPhi)
+  if (insertChain->opcode() != spv::Op::OpCompositeInsert &&
+      insertChain->opcode() != spv::Op::OpPhi)
     return;
   // If extract indices are empty, mark all subcomponents if type
   // is constant length.
@@ -89,7 +86,7 @@
     }
   }
   Instruction* insInst = insertChain;
-  while (insInst->opcode() == SpvOpCompositeInsert) {
+  while (insInst->opcode() == spv::Op::OpCompositeInsert) {
     // If no extract indices, mark insert and inserted object (which might
     // also be an insert chain) and continue up the chain though the input
     // composite.
@@ -139,7 +136,7 @@
     insInst = get_def_use_mgr()->GetDef(compId);
   }
   // If insert chain ended with phi, do recursive call on each operand
-  if (insInst->opcode() != SpvOpPhi) return;
+  if (insInst->opcode() != spv::Op::OpPhi) return;
   // Mark phi visited to prevent potential infinite loop. If phi is already
   // visited, return to avoid infinite loop.
   if (visited_phis->count(insInst->result_id()) != 0) return;
@@ -179,17 +176,17 @@
   for (auto bi = func->begin(); bi != func->end(); ++bi) {
     for (auto ii = bi->begin(); ii != bi->end(); ++ii) {
       // Only process Inserts and composite Phis
-      SpvOp op = ii->opcode();
+      spv::Op op = ii->opcode();
       Instruction* typeInst = get_def_use_mgr()->GetDef(ii->type_id());
-      if (op != SpvOpCompositeInsert &&
-          (op != SpvOpPhi || !spvOpcodeIsComposite(typeInst->opcode())))
+      if (op != spv::Op::OpCompositeInsert &&
+          (op != spv::Op::OpPhi || !spvOpcodeIsComposite(typeInst->opcode())))
         continue;
       // The marking algorithm can be expensive for large arrays and the
       // efficacy of eliminating dead inserts into arrays is questionable.
       // Skip optimizing array inserts for now. Just mark them live.
       // TODO(greg-lunarg): Eliminate dead array inserts
-      if (op == SpvOpCompositeInsert) {
-        if (typeInst->opcode() == SpvOpTypeArray) {
+      if (op == spv::Op::OpCompositeInsert) {
+        if (typeInst->opcode() == spv::Op::OpTypeArray) {
           liveInserts_.insert(ii->result_id());
           continue;
         }
@@ -198,11 +195,11 @@
       get_def_use_mgr()->ForEachUser(id, [&ii, this](Instruction* user) {
         if (user->IsCommonDebugInstr()) return;
         switch (user->opcode()) {
-          case SpvOpCompositeInsert:
-          case SpvOpPhi:
+          case spv::Op::OpCompositeInsert:
+          case spv::Op::OpPhi:
             // Use by insert or phi does not initiate marking
             break;
-          case SpvOpCompositeExtract: {
+          case spv::Op::OpCompositeExtract: {
             // Capture extract indices
             std::vector<uint32_t> extIndices;
             uint32_t icnt = 0;
@@ -226,7 +223,7 @@
   std::vector<Instruction*> dead_instructions;
   for (auto bi = func->begin(); bi != func->end(); ++bi) {
     for (auto ii = bi->begin(); ii != bi->end(); ++ii) {
-      if (ii->opcode() != SpvOpCompositeInsert) continue;
+      if (ii->opcode() != spv::Op::OpCompositeInsert) continue;
       const uint32_t id = ii->result_id();
       if (liveInserts_.find(id) != liveInserts_.end()) continue;
       const uint32_t replId =
diff --git a/source/opt/dead_variable_elimination.cpp b/source/opt/dead_variable_elimination.cpp
index 2837106..e39132c 100644
--- a/source/opt/dead_variable_elimination.cpp
+++ b/source/opt/dead_variable_elimination.cpp
@@ -33,7 +33,7 @@
 
   // Get the reference count for all of the global OpVariable instructions.
   for (auto& inst : context()->types_values()) {
-    if (inst.opcode() != SpvOp::SpvOpVariable) {
+    if (inst.opcode() != spv::Op::OpVariable) {
       continue;
     }
 
@@ -43,11 +43,11 @@
     // Check the linkage.  If it is exported, it could be reference somewhere
     // else, so we must keep the variable around.
     get_decoration_mgr()->ForEachDecoration(
-        result_id, SpvDecorationLinkageAttributes,
+        result_id, uint32_t(spv::Decoration::LinkageAttributes),
         [&count](const Instruction& linkage_instruction) {
           uint32_t last_operand = linkage_instruction.NumOperands() - 1;
-          if (linkage_instruction.GetSingleWordOperand(last_operand) ==
-              SpvLinkageTypeExport) {
+          if (spv::LinkageType(linkage_instruction.GetSingleWordOperand(
+                  last_operand)) == spv::LinkageType::Export) {
             count = kMustKeep;
           }
         });
@@ -57,7 +57,8 @@
       // at the uses and count the number of real references.
       count = 0;
       get_def_use_mgr()->ForEachUser(result_id, [&count](Instruction* user) {
-        if (!IsAnnotationInst(user->opcode()) && user->opcode() != SpvOpName) {
+        if (!IsAnnotationInst(user->opcode()) &&
+            user->opcode() != spv::Op::OpName) {
           ++count;
         }
       });
@@ -81,7 +82,7 @@
 
 void DeadVariableElimination::DeleteVariable(uint32_t result_id) {
   Instruction* inst = get_def_use_mgr()->GetDef(result_id);
-  assert(inst->opcode() == SpvOpVariable &&
+  assert(inst->opcode() == spv::Op::OpVariable &&
          "Should not be trying to delete anything other than an OpVariable.");
 
   // Look for an initializer that references another variable.  We need to know
@@ -93,7 +94,7 @@
     // TODO: Handle OpSpecConstantOP which might be defined in terms of other
     // variables.  Will probably require a unified dead code pass that does all
     // instruction types.  (Issue 906)
-    if (initializer->opcode() == SpvOpVariable) {
+    if (initializer->opcode() == spv::Op::OpVariable) {
       uint32_t initializer_id = initializer->result_id();
       size_t& count = reference_count_[initializer_id];
       if (count != kMustKeep) {
diff --git a/source/opt/debug_info_manager.cpp b/source/opt/debug_info_manager.cpp
index 3585186..1e614c6 100644
--- a/source/opt/debug_info_manager.cpp
+++ b/source/opt/debug_info_manager.cpp
@@ -22,32 +22,31 @@
 // Constants for OpenCL.DebugInfo.100 & NonSemantic.Shader.DebugInfo.100
 // extension instructions.
 
-static const uint32_t kOpLineOperandLineIndex = 1;
-static const uint32_t kLineOperandIndexDebugFunction = 7;
-static const uint32_t kLineOperandIndexDebugLexicalBlock = 5;
-static const uint32_t kLineOperandIndexDebugLine = 5;
-static const uint32_t kDebugFunctionOperandFunctionIndex = 13;
-static const uint32_t kDebugFunctionDefinitionOperandDebugFunctionIndex = 4;
-static const uint32_t kDebugFunctionDefinitionOperandOpFunctionIndex = 5;
-static const uint32_t kDebugFunctionOperandParentIndex = 9;
-static const uint32_t kDebugTypeCompositeOperandParentIndex = 9;
-static const uint32_t kDebugLexicalBlockOperandParentIndex = 7;
-static const uint32_t kDebugInlinedAtOperandInlinedIndex = 6;
-static const uint32_t kDebugExpressOperandOperationIndex = 4;
-static const uint32_t kDebugDeclareOperandLocalVariableIndex = 4;
-static const uint32_t kDebugDeclareOperandVariableIndex = 5;
-static const uint32_t kDebugValueOperandExpressionIndex = 6;
-static const uint32_t kDebugOperationOperandOperationIndex = 4;
-static const uint32_t kOpVariableOperandStorageClassIndex = 2;
-static const uint32_t kDebugLocalVariableOperandParentIndex = 9;
-static const uint32_t kExtInstInstructionInIdx = 1;
-static const uint32_t kDebugGlobalVariableOperandFlagsIndex = 12;
-static const uint32_t kDebugLocalVariableOperandFlagsIndex = 10;
-
 namespace spvtools {
 namespace opt {
 namespace analysis {
 namespace {
+constexpr uint32_t kOpLineOperandLineIndex = 1;
+constexpr uint32_t kLineOperandIndexDebugFunction = 7;
+constexpr uint32_t kLineOperandIndexDebugLexicalBlock = 5;
+constexpr uint32_t kLineOperandIndexDebugLine = 5;
+constexpr uint32_t kDebugFunctionOperandFunctionIndex = 13;
+constexpr uint32_t kDebugFunctionDefinitionOperandDebugFunctionIndex = 4;
+constexpr uint32_t kDebugFunctionDefinitionOperandOpFunctionIndex = 5;
+constexpr uint32_t kDebugFunctionOperandParentIndex = 9;
+constexpr uint32_t kDebugTypeCompositeOperandParentIndex = 9;
+constexpr uint32_t kDebugLexicalBlockOperandParentIndex = 7;
+constexpr uint32_t kDebugInlinedAtOperandInlinedIndex = 6;
+constexpr uint32_t kDebugExpressOperandOperationIndex = 4;
+constexpr uint32_t kDebugDeclareOperandLocalVariableIndex = 4;
+constexpr uint32_t kDebugDeclareOperandVariableIndex = 5;
+constexpr uint32_t kDebugValueOperandExpressionIndex = 6;
+constexpr uint32_t kDebugOperationOperandOperationIndex = 4;
+constexpr uint32_t kOpVariableOperandStorageClassIndex = 2;
+constexpr uint32_t kDebugLocalVariableOperandParentIndex = 9;
+constexpr uint32_t kExtInstInstructionInIdx = 1;
+constexpr uint32_t kDebugGlobalVariableOperandFlagsIndex = 12;
+constexpr uint32_t kDebugLocalVariableOperandFlagsIndex = 10;
 
 void SetInlinedOperand(Instruction* dbg_inlined_at, uint32_t inlined_operand) {
   assert(dbg_inlined_at);
@@ -156,7 +155,8 @@
 uint32_t AddNewConstInGlobals(IRContext* context, uint32_t const_value) {
   uint32_t id = context->TakeNextId();
   std::unique_ptr<Instruction> new_const(new Instruction(
-      context, SpvOpConstant, context->get_type_mgr()->GetUIntTypeId(), id,
+      context, spv::Op::OpConstant, context->get_type_mgr()->GetUIntTypeId(),
+      id,
       {
           {spv_operand_type_t::SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER,
            {const_value}},
@@ -212,7 +212,7 @@
         break;
     }
   } else {
-    if (line->opcode() == SpvOpLine) {
+    if (line->opcode() == spv::Op::OpLine) {
       line_number = line->GetSingleWordOperand(kOpLineOperandLineIndex);
     } else if (line->GetShader100DebugOpcode() ==
                NonSemanticShaderDebugInfo100DebugLine) {
@@ -230,18 +230,19 @@
     // constants that may be generated here is likely not significant
     // and will likely be cleaned up in later passes.
     if (line_number_type == spv_operand_type_t::SPV_OPERAND_TYPE_ID &&
-        line->opcode() == SpvOpLine) {
+        line->opcode() == spv::Op::OpLine) {
       if (!context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse) ||
           !context()->AreAnalysesValid(IRContext::Analysis::kAnalysisConstants))
         line_number = AddNewConstInGlobals(context(), line_number);
       else
-        line_number = context()->get_constant_mgr()->GetUIntConst(line_number);
+        line_number =
+            context()->get_constant_mgr()->GetUIntConstId(line_number);
     }
   }
 
   uint32_t result_id = context()->TakeNextId();
   std::unique_ptr<Instruction> inlined_at(new Instruction(
-      context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
+      context(), spv::Op::OpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
       result_id,
       {
           {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {setId}},
@@ -334,8 +335,8 @@
 
   if (context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo()) {
     deref_operation = std::unique_ptr<Instruction>(new Instruction(
-        context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
-        result_id,
+        context(), spv::Op::OpExtInst,
+        context()->get_type_mgr()->GetVoidTypeId(), result_id,
         {
             {SPV_OPERAND_TYPE_ID, {GetDbgSetImportId()}},
             {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
@@ -344,11 +345,11 @@
              {static_cast<uint32_t>(OpenCLDebugInfo100Deref)}},
         }));
   } else {
-    uint32_t deref_id = context()->get_constant_mgr()->GetUIntConst(
+    uint32_t deref_id = context()->get_constant_mgr()->GetUIntConstId(
         NonSemanticShaderDebugInfo100Deref);
 
     deref_operation = std::unique_ptr<Instruction>(
-        new Instruction(context(), SpvOpExtInst,
+        new Instruction(context(), spv::Op::OpExtInst,
                         context()->get_type_mgr()->GetVoidTypeId(), result_id,
                         {
                             {SPV_OPERAND_TYPE_ID, {GetDbgSetImportId()}},
@@ -390,7 +391,7 @@
 
   uint32_t result_id = context()->TakeNextId();
   std::unique_ptr<Instruction> dbg_info_none_inst(new Instruction(
-      context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
+      context(), spv::Op::OpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
       result_id,
       {
           {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {GetDbgSetImportId()}},
@@ -414,7 +415,7 @@
 
   uint32_t result_id = context()->TakeNextId();
   std::unique_ptr<Instruction> empty_debug_expr(new Instruction(
-      context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
+      context(), spv::Op::OpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
       result_id,
       {
           {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {GetDbgSetImportId()}},
@@ -527,7 +528,7 @@
   assert(scope != nullptr);
 
   std::vector<uint32_t> scope_ids;
-  if (scope->opcode() == SpvOpPhi) {
+  if (scope->opcode() == spv::Op::OpPhi) {
     scope_ids.push_back(scope->GetDebugScope().GetLexicalScope());
     for (uint32_t i = 0; i < scope->NumInOperands(); i += 2) {
       auto* value = context()->get_def_use_mgr()->GetDef(
@@ -571,8 +572,8 @@
     // Avoid inserting the new DebugValue between OpPhi or OpVariable
     // instructions.
     Instruction* insert_before = insert_pos->NextNode();
-    while (insert_before->opcode() == SpvOpPhi ||
-           insert_before->opcode() == SpvOpVariable) {
+    while (insert_before->opcode() == spv::Op::OpPhi ||
+           insert_before->opcode() == spv::Op::OpVariable) {
       insert_before = insert_before->NextNode();
     }
     modified |= AddDebugValueForDecl(dbg_decl_or_val, value_id, insert_before,
@@ -653,9 +654,10 @@
   }
 
   auto* var = context()->get_def_use_mgr()->GetDef(var_id);
-  if (var->opcode() == SpvOpVariable &&
-      SpvStorageClass(var->GetSingleWordOperand(
-          kOpVariableOperandStorageClassIndex)) == SpvStorageClassFunction) {
+  if (var->opcode() == spv::Op::OpVariable &&
+      spv::StorageClass(
+          var->GetSingleWordOperand(kOpVariableOperandStorageClassIndex)) ==
+          spv::StorageClass::Function) {
     return var_id;
   }
   return 0;
@@ -762,8 +764,8 @@
       CommonDebugInfoDebugGlobalVariable) {
     return;
   }
-  assert(local_var->opcode() == SpvOpVariable ||
-         local_var->opcode() == SpvOpFunctionParameter);
+  assert(local_var->opcode() == spv::Op::OpVariable ||
+         local_var->opcode() == spv::Op::OpFunctionParameter);
 
   // Convert |dbg_global_var| to DebugLocalVariable
   dbg_global_var->SetInOperand(kExtInstInstructionInIdx,
@@ -780,7 +782,7 @@
 
   // Create a DebugDeclare
   std::unique_ptr<Instruction> new_dbg_decl(new Instruction(
-      context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
+      context(), spv::Op::OpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
       context()->TakeNextId(),
       {
           {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {GetDbgSetImportId()}},
@@ -794,7 +796,7 @@
       }));
   // Must insert after all OpVariables in block
   Instruction* insert_before = local_var;
-  while (insert_before->opcode() == SpvOpVariable)
+  while (insert_before->opcode() == spv::Op::OpVariable)
     insert_before = insert_before->NextNode();
   auto* added_dbg_decl = insert_before->InsertBefore(std::move(new_dbg_decl));
   if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
diff --git a/source/opt/decoration_manager.cpp b/source/opt/decoration_manager.cpp
index 2146c35..3e95dbc 100644
--- a/source/opt/decoration_manager.cpp
+++ b/source/opt/decoration_manager.cpp
@@ -22,6 +22,9 @@
 
 #include "source/opt/ir_context.h"
 
+namespace spvtools {
+namespace opt {
+namespace analysis {
 namespace {
 using InstructionVector = std::vector<const spvtools::opt::Instruction*>;
 using DecorationSet = std::set<std::u32string>;
@@ -49,10 +52,6 @@
 }
 }  // namespace
 
-namespace spvtools {
-namespace opt {
-namespace analysis {
-
 bool DecorationManager::RemoveDecorationsFrom(
     uint32_t id, std::function<bool(const Instruction&)> pred) {
   bool was_modified = false;
@@ -76,8 +75,8 @@
   // applying the group.
   std::unordered_set<const Instruction*> indirect_decorations_to_remove;
   for (Instruction* inst : decorations_info.indirect_decorations) {
-    assert(inst->opcode() == SpvOpGroupDecorate ||
-           inst->opcode() == SpvOpGroupMemberDecorate);
+    assert(inst->opcode() == spv::Op::OpGroupDecorate ||
+           inst->opcode() == spv::Op::OpGroupMemberDecorate);
 
     std::vector<Instruction*> group_decorations_to_keep;
     const uint32_t group_id = inst->GetSingleWordInOperand(0u);
@@ -99,7 +98,8 @@
     }
 
     // Otherwise, remove |id| from the targets of |group_id|
-    const uint32_t stride = inst->opcode() == SpvOpGroupDecorate ? 1u : 2u;
+    const uint32_t stride =
+        inst->opcode() == spv::Op::OpGroupDecorate ? 1u : 2u;
     for (uint32_t i = 1u; i < inst->NumInOperands();) {
       if (inst->GetSingleWordInOperand(i) != id) {
         i += stride;
@@ -212,16 +212,16 @@
           }
 
           switch (inst->opcode()) {
-            case SpvOpDecorate:
+            case spv::Op::OpDecorate:
               decorate_set->emplace(std::move(decoration_payload));
               break;
-            case SpvOpMemberDecorate:
+            case spv::Op::OpMemberDecorate:
               member_decorate_set->emplace(std::move(decoration_payload));
               break;
-            case SpvOpDecorateId:
+            case spv::Op::OpDecorateId:
               decorate_id_set->emplace(std::move(decoration_payload));
               break;
-            case SpvOpDecorateStringGOOGLE:
+            case spv::Op::OpDecorateStringGOOGLE:
               decorate_string_set->emplace(std::move(decoration_payload));
               break;
             default:
@@ -278,16 +278,16 @@
           }
 
           switch (inst->opcode()) {
-            case SpvOpDecorate:
+            case spv::Op::OpDecorate:
               decorate_set->emplace(std::move(decoration_payload));
               break;
-            case SpvOpMemberDecorate:
+            case spv::Op::OpMemberDecorate:
               member_decorate_set->emplace(std::move(decoration_payload));
               break;
-            case SpvOpDecorateId:
+            case spv::Op::OpDecorateId:
               decorate_id_set->emplace(std::move(decoration_payload));
               break;
-            case SpvOpDecorateStringGOOGLE:
+            case spv::Op::OpDecorateStringGOOGLE:
               decorate_string_set->emplace(std::move(decoration_payload));
               break;
             default:
@@ -328,10 +328,10 @@
                                               const Instruction* inst2,
                                               bool ignore_target) const {
   switch (inst1->opcode()) {
-    case SpvOpDecorate:
-    case SpvOpMemberDecorate:
-    case SpvOpDecorateId:
-    case SpvOpDecorateStringGOOGLE:
+    case spv::Op::OpDecorate:
+    case spv::Op::OpMemberDecorate:
+    case spv::Op::OpDecorateId:
+    case spv::Op::OpDecorateStringGOOGLE:
       break;
     default:
       return false;
@@ -358,17 +358,18 @@
 
 void DecorationManager::AddDecoration(Instruction* inst) {
   switch (inst->opcode()) {
-    case SpvOpDecorate:
-    case SpvOpDecorateId:
-    case SpvOpDecorateStringGOOGLE:
-    case SpvOpMemberDecorate: {
+    case spv::Op::OpDecorate:
+    case spv::Op::OpDecorateId:
+    case spv::Op::OpDecorateStringGOOGLE:
+    case spv::Op::OpMemberDecorate: {
       const auto target_id = inst->GetSingleWordInOperand(0u);
       id_to_decoration_insts_[target_id].direct_decorations.push_back(inst);
       break;
     }
-    case SpvOpGroupDecorate:
-    case SpvOpGroupMemberDecorate: {
-      const uint32_t start = inst->opcode() == SpvOpGroupDecorate ? 1u : 2u;
+    case spv::Op::OpGroupDecorate:
+    case spv::Op::OpGroupMemberDecorate: {
+      const uint32_t start =
+          inst->opcode() == spv::Op::OpGroupDecorate ? 1u : 2u;
       const uint32_t stride = start;
       for (uint32_t i = start; i < inst->NumInOperands(); i += stride) {
         const auto target_id = inst->GetSingleWordInOperand(i);
@@ -384,7 +385,7 @@
   }
 }
 
-void DecorationManager::AddDecoration(SpvOp opcode,
+void DecorationManager::AddDecoration(spv::Op opcode,
                                       std::vector<Operand> opnds) {
   IRContext* ctx = module_->context();
   std::unique_ptr<Instruction> newDecoOp(
@@ -394,7 +395,7 @@
 
 void DecorationManager::AddDecoration(uint32_t inst_id, uint32_t decoration) {
   AddDecoration(
-      SpvOpDecorate,
+      spv::Op::OpDecorate,
       {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {inst_id}},
        {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {decoration}}});
 }
@@ -402,7 +403,7 @@
 void DecorationManager::AddDecorationVal(uint32_t inst_id, uint32_t decoration,
                                          uint32_t decoration_value) {
   AddDecoration(
-      SpvOpDecorate,
+      spv::Op::OpDecorate,
       {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {inst_id}},
        {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {decoration}},
        {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER,
@@ -413,7 +414,7 @@
                                             uint32_t decoration,
                                             uint32_t decoration_value) {
   AddDecoration(
-      SpvOpMemberDecorate,
+      spv::Op::OpMemberDecorate,
       {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {inst_id}},
        {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {member}},
        {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {decoration}},
@@ -436,9 +437,10 @@
       [include_linkage,
        &decorations](const std::vector<Instruction*>& direct_decorations) {
         for (Instruction* inst : direct_decorations) {
-          const bool is_linkage = inst->opcode() == SpvOpDecorate &&
-                                  inst->GetSingleWordInOperand(1u) ==
-                                      SpvDecorationLinkageAttributes;
+          const bool is_linkage =
+              inst->opcode() == spv::Op::OpDecorate &&
+              spv::Decoration(inst->GetSingleWordInOperand(1u)) ==
+                  spv::Decoration::LinkageAttributes;
           if (include_linkage || !is_linkage) decorations.push_back(inst);
         }
       };
@@ -459,17 +461,17 @@
 
 bool DecorationManager::WhileEachDecoration(
     uint32_t id, uint32_t decoration,
-    std::function<bool(const Instruction&)> f) {
+    std::function<bool(const Instruction&)> f) const {
   for (const Instruction* inst : GetDecorationsFor(id, true)) {
     switch (inst->opcode()) {
-      case SpvOpMemberDecorate:
+      case spv::Op::OpMemberDecorate:
         if (inst->GetSingleWordInOperand(2) == decoration) {
           if (!f(*inst)) return false;
         }
         break;
-      case SpvOpDecorate:
-      case SpvOpDecorateId:
-      case SpvOpDecorateStringGOOGLE:
+      case spv::Op::OpDecorate:
+      case spv::Op::OpDecorateId:
+      case spv::Op::OpDecorateStringGOOGLE:
         if (inst->GetSingleWordInOperand(1) == decoration) {
           if (!f(*inst)) return false;
         }
@@ -483,14 +485,19 @@
 
 void DecorationManager::ForEachDecoration(
     uint32_t id, uint32_t decoration,
-    std::function<void(const Instruction&)> f) {
+    std::function<void(const Instruction&)> f) const {
   WhileEachDecoration(id, decoration, [&f](const Instruction& inst) {
     f(inst);
     return true;
   });
 }
 
-bool DecorationManager::HasDecoration(uint32_t id, uint32_t decoration) {
+bool DecorationManager::HasDecoration(uint32_t id,
+                                      spv::Decoration decoration) const {
+  return HasDecoration(id, static_cast<uint32_t>(decoration));
+}
+
+bool DecorationManager::HasDecoration(uint32_t id, uint32_t decoration) const {
   bool has_decoration = false;
   ForEachDecoration(id, decoration, [&has_decoration](const Instruction&) {
     has_decoration = true;
@@ -523,14 +530,14 @@
       decoration_list->second.indirect_decorations;
   for (Instruction* inst : indirect_decorations) {
     switch (inst->opcode()) {
-      case SpvOpGroupDecorate:
+      case spv::Op::OpGroupDecorate:
         context->ForgetUses(inst);
         // add |to| to list of decorated id's
         inst->AddOperand(
             Operand(spv_operand_type_t::SPV_OPERAND_TYPE_ID, {to}));
         context->AnalyzeUses(inst);
         break;
-      case SpvOpGroupMemberDecorate: {
+      case spv::Op::OpGroupMemberDecorate: {
         context->ForgetUses(inst);
         // for each (id == from), add (to, literal) as operands
         const uint32_t num_operands = inst->NumOperands();
@@ -554,13 +561,13 @@
 
 void DecorationManager::CloneDecorations(
     uint32_t from, uint32_t to,
-    const std::vector<SpvDecoration>& decorations_to_copy) {
+    const std::vector<spv::Decoration>& decorations_to_copy) {
   const auto decoration_list = id_to_decoration_insts_.find(from);
   if (decoration_list == id_to_decoration_insts_.end()) return;
   auto context = module_->context();
   for (Instruction* inst : decoration_list->second.direct_decorations) {
     if (std::find(decorations_to_copy.begin(), decorations_to_copy.end(),
-                  inst->GetSingleWordInOperand(1)) ==
+                  spv::Decoration(inst->GetSingleWordInOperand(1))) ==
         decorations_to_copy.end()) {
       continue;
     }
@@ -579,11 +586,11 @@
       decoration_list->second.indirect_decorations;
   for (Instruction* inst : indirect_decorations) {
     switch (inst->opcode()) {
-      case SpvOpGroupDecorate:
+      case spv::Op::OpGroupDecorate:
         CloneDecorations(inst->GetSingleWordInOperand(0), to,
                          decorations_to_copy);
         break;
-      case SpvOpGroupMemberDecorate: {
+      case spv::Op::OpGroupMemberDecorate: {
         assert(false && "The source id is not suppose to be a type.");
         break;
       }
@@ -599,18 +606,19 @@
   };
 
   switch (inst->opcode()) {
-    case SpvOpDecorate:
-    case SpvOpDecorateId:
-    case SpvOpDecorateStringGOOGLE:
-    case SpvOpMemberDecorate: {
+    case spv::Op::OpDecorate:
+    case spv::Op::OpDecorateId:
+    case spv::Op::OpDecorateStringGOOGLE:
+    case spv::Op::OpMemberDecorate: {
       const auto target_id = inst->GetSingleWordInOperand(0u);
       auto const iter = id_to_decoration_insts_.find(target_id);
       if (iter == id_to_decoration_insts_.end()) return;
       remove_from_container(iter->second.direct_decorations);
     } break;
-    case SpvOpGroupDecorate:
-    case SpvOpGroupMemberDecorate: {
-      const uint32_t stride = inst->opcode() == SpvOpGroupDecorate ? 1u : 2u;
+    case spv::Op::OpGroupDecorate:
+    case spv::Op::OpGroupMemberDecorate: {
+      const uint32_t stride =
+          inst->opcode() == spv::Op::OpGroupDecorate ? 1u : 2u;
       for (uint32_t i = 1u; i < inst->NumInOperands(); i += stride) {
         const auto target_id = inst->GetSingleWordInOperand(i);
         auto const iter = id_to_decoration_insts_.find(target_id);
diff --git a/source/opt/decoration_manager.h b/source/opt/decoration_manager.h
index fe78f2c..2be016a 100644
--- a/source/opt/decoration_manager.h
+++ b/source/opt/decoration_manager.h
@@ -71,14 +71,14 @@
                                               bool include_linkage);
   std::vector<const Instruction*> GetDecorationsFor(uint32_t id,
                                                     bool include_linkage) const;
-  // Returns whether two IDs have the same decorations. Two SpvOpGroupDecorate
-  // instructions that apply the same decorations but to different IDs, still
-  // count as being the same.
+  // Returns whether two IDs have the same decorations. Two
+  // spv::Op::OpGroupDecorate instructions that apply the same decorations but
+  // to different IDs, still count as being the same.
   bool HaveTheSameDecorations(uint32_t id1, uint32_t id2) const;
 
-  // Returns whether two IDs have the same decorations. Two SpvOpGroupDecorate
-  // instructions that apply the same decorations but to different IDs, still
-  // count as being the same.
+  // Returns whether two IDs have the same decorations. Two
+  // spv::Op::OpGroupDecorate instructions that apply the same decorations but
+  // to different IDs, still count as being the same.
   bool HaveSubsetOfDecorations(uint32_t id1, uint32_t id2) const;
 
   // Returns whether the two decorations instructions are the same and are
@@ -92,20 +92,21 @@
 
   // Returns whether a decoration instruction for |id| with decoration
   // |decoration| exists or not.
-  bool HasDecoration(uint32_t id, uint32_t decoration);
+  bool HasDecoration(uint32_t id, uint32_t decoration) const;
+  bool HasDecoration(uint32_t id, spv::Decoration decoration) const;
 
   // |f| is run on each decoration instruction for |id| with decoration
   // |decoration|. Processed are all decorations which target |id| either
   // directly or indirectly by Decoration Groups.
   void ForEachDecoration(uint32_t id, uint32_t decoration,
-                         std::function<void(const Instruction&)> f);
+                         std::function<void(const Instruction&)> f) const;
 
   // |f| is run on each decoration instruction for |id| with decoration
   // |decoration|. Processes all decoration which target |id| either directly or
   // indirectly through decoration groups. If |f| returns false, iteration is
   // terminated and this function returns false.
   bool WhileEachDecoration(uint32_t id, uint32_t decoration,
-                           std::function<bool(const Instruction&)> f);
+                           std::function<bool(const Instruction&)> f) const;
 
   // |f| is run on each decoration instruction for |id| with decoration
   // |decoration|. Processes all decoration which target |id| either directly or
@@ -123,14 +124,15 @@
   // Same as above, but only clone the decoration if the decoration operand is
   // in |decorations_to_copy|.  This function has the extra restriction that
   // |from| and |to| must not be an object, not a type.
-  void CloneDecorations(uint32_t from, uint32_t to,
-                        const std::vector<SpvDecoration>& decorations_to_copy);
+  void CloneDecorations(
+      uint32_t from, uint32_t to,
+      const std::vector<spv::Decoration>& decorations_to_copy);
 
   // Informs the decoration manager of a new decoration that it needs to track.
   void AddDecoration(Instruction* inst);
 
   // Add decoration with |opcode| and operands |opnds|.
-  void AddDecoration(SpvOp opcode, const std::vector<Operand> opnds);
+  void AddDecoration(spv::Op opcode, const std::vector<Operand> opnds);
 
   // Add |decoration| of |inst_id| to module.
   void AddDecoration(uint32_t inst_id, uint32_t decoration);
@@ -140,7 +142,7 @@
                         uint32_t decoration_value);
 
   // Add |decoration, decoration_value| of |inst_id, member| to module.
-  void AddMemberDecoration(uint32_t member, uint32_t inst_id,
+  void AddMemberDecoration(uint32_t inst_id, uint32_t member,
                            uint32_t decoration, uint32_t decoration_value);
 
   friend bool operator==(const DecorationManager&, const DecorationManager&);
@@ -195,9 +197,9 @@
 
   // Mapping from ids to the instructions applying a decoration to those ids.
   // In other words, for each id you get all decoration instructions
-  // referencing that id, be it directly (SpvOpDecorate, SpvOpMemberDecorate
-  // and SpvOpDecorateId), or indirectly (SpvOpGroupDecorate,
-  // SpvOpMemberGroupDecorate).
+  // referencing that id, be it directly (spv::Op::OpDecorate,
+  // spv::Op::OpMemberDecorate and spv::Op::OpDecorateId), or indirectly
+  // (spv::Op::OpGroupDecorate, spv::Op::OpMemberGroupDecorate).
   std::unordered_map<uint32_t, TargetData> id_to_decoration_insts_;
   // The enclosing module.
   Module* module_;
diff --git a/source/opt/desc_sroa.cpp b/source/opt/desc_sroa.cpp
index b130ca8..8da0c86 100644
--- a/source/opt/desc_sroa.cpp
+++ b/source/opt/desc_sroa.cpp
@@ -22,8 +22,9 @@
 namespace {
 
 bool IsDecorationBinding(Instruction* inst) {
-  if (inst->opcode() != SpvOpDecorate) return false;
-  return inst->GetSingleWordInOperand(1u) == SpvDecorationBinding;
+  if (inst->opcode() != spv::Op::OpDecorate) return false;
+  return spv::Decoration(inst->GetSingleWordInOperand(1u)) ==
+         spv::Decoration::Binding;
 }
 
 }  // namespace
@@ -56,7 +57,7 @@
   bool failed = !get_def_use_mgr()->WhileEachUser(
       var->result_id(),
       [this, &access_chain_work_list, &load_work_list](Instruction* use) {
-        if (use->opcode() == SpvOpName) {
+        if (use->opcode() == spv::Op::OpName) {
           return true;
         }
 
@@ -65,11 +66,11 @@
         }
 
         switch (use->opcode()) {
-          case SpvOpAccessChain:
-          case SpvOpInBoundsAccessChain:
+          case spv::Op::OpAccessChain:
+          case spv::Op::OpInBoundsAccessChain:
             access_chain_work_list.push_back(use);
             return true;
-          case SpvOpLoad:
+          case spv::Op::OpLoad:
             load_work_list.push_back(use);
             return true;
           default:
@@ -184,7 +185,7 @@
   // Handle OpMemberDecorate instructions.
   for (auto old_decoration : get_decoration_mgr()->GetDecorationsFor(
            old_var_type->result_id(), true)) {
-    assert(old_decoration->opcode() == SpvOpMemberDecorate);
+    assert(old_decoration->opcode() == spv::Op::OpMemberDecorate);
     if (old_decoration->GetSingleWordInOperand(1u) != index) continue;
     CreateNewDecorationForMemberDecorate(old_decoration, new_var_id);
   }
@@ -212,8 +213,8 @@
 
 void DescriptorScalarReplacement::CreateNewDecorationForNewVariable(
     Instruction* old_decoration, uint32_t new_var_id, uint32_t new_binding) {
-  assert(old_decoration->opcode() == SpvOpDecorate ||
-         old_decoration->opcode() == SpvOpDecorateString);
+  assert(old_decoration->opcode() == spv::Op::OpDecorate ||
+         old_decoration->opcode() == spv::Op::OpDecorateString);
   std::unique_ptr<Instruction> new_decoration(old_decoration->Clone(context()));
   new_decoration->SetInOperand(0, {new_var_id});
 
@@ -231,25 +232,25 @@
   auto new_decorate_operand_end = old_member_decoration->end();
   operands.insert(operands.end(), new_decorate_operand_begin,
                   new_decorate_operand_end);
-  get_decoration_mgr()->AddDecoration(SpvOpDecorate, std::move(operands));
+  get_decoration_mgr()->AddDecoration(spv::Op::OpDecorate, std::move(operands));
 }
 
 uint32_t DescriptorScalarReplacement::CreateReplacementVariable(
     Instruction* var, uint32_t idx) {
   // The storage class for the new variable is the same as the original.
-  SpvStorageClass storage_class =
-      static_cast<SpvStorageClass>(var->GetSingleWordInOperand(0));
+  spv::StorageClass storage_class =
+      static_cast<spv::StorageClass>(var->GetSingleWordInOperand(0));
 
   // The type for the new variable will be a pointer to type of the elements of
   // the array.
   uint32_t ptr_type_id = var->type_id();
   Instruction* ptr_type_inst = get_def_use_mgr()->GetDef(ptr_type_id);
-  assert(ptr_type_inst->opcode() == SpvOpTypePointer &&
+  assert(ptr_type_inst->opcode() == spv::Op::OpTypePointer &&
          "Variable should be a pointer to an array or structure.");
   uint32_t pointee_type_id = ptr_type_inst->GetSingleWordInOperand(1);
   Instruction* pointee_type_inst = get_def_use_mgr()->GetDef(pointee_type_id);
-  const bool is_array = pointee_type_inst->opcode() == SpvOpTypeArray;
-  const bool is_struct = pointee_type_inst->opcode() == SpvOpTypeStruct;
+  const bool is_array = pointee_type_inst->opcode() == spv::Op::OpTypeArray;
+  const bool is_struct = pointee_type_inst->opcode() == spv::Op::OpTypeStruct;
   assert((is_array || is_struct) &&
          "Variable should be a pointer to an array or structure.");
 
@@ -263,7 +264,7 @@
   // Create the variable.
   uint32_t id = TakeNextId();
   std::unique_ptr<Instruction> variable(
-      new Instruction(context(), SpvOpVariable, ptr_element_type_id, id,
+      new Instruction(context(), spv::Op::OpVariable, ptr_element_type_id, id,
                       std::initializer_list<Operand>{
                           {SPV_OPERAND_TYPE_STORAGE_CLASS,
                            {static_cast<uint32_t>(storage_class)}}}));
@@ -293,7 +294,7 @@
     }
 
     std::unique_ptr<Instruction> new_name(new Instruction(
-        context(), SpvOpName, 0, 0,
+        context(), spv::Op::OpName, 0, 0,
         std::initializer_list<Operand>{
             {SPV_OPERAND_TYPE_ID, {id}},
             {SPV_OPERAND_TYPE_LITERAL_STRING, utils::MakeVector(name_str)}}));
@@ -315,14 +316,14 @@
   Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
 
   // If it's a pointer, look at the underlying type.
-  if (type_inst->opcode() == SpvOpTypePointer) {
+  if (type_inst->opcode() == spv::Op::OpTypePointer) {
     type_id = type_inst->GetSingleWordInOperand(1);
     type_inst = get_def_use_mgr()->GetDef(type_id);
   }
 
   // Arrays consume N*M binding numbers where N is the array length, and M is
   // the number of bindings used by each array element.
-  if (type_inst->opcode() == SpvOpTypeArray) {
+  if (type_inst->opcode() == spv::Op::OpTypeArray) {
     uint32_t element_type_id = type_inst->GetSingleWordInOperand(0);
     uint32_t length_id = type_inst->GetSingleWordInOperand(1);
     const analysis::Constant* length_const =
@@ -335,7 +336,7 @@
 
   // The number of bindings consumed by a structure is the sum of the bindings
   // used by its members.
-  if (type_inst->opcode() == SpvOpTypeStruct &&
+  if (type_inst->opcode() == spv::Op::OpTypeStruct &&
       !descsroautil::IsTypeOfStructuredBuffer(context(), type_inst)) {
     uint32_t sum = 0;
     for (uint32_t i = 0; i < type_inst->NumInOperands(); i++)
@@ -353,12 +354,12 @@
   // |value| is the OpLoad instruction that has loaded |var|.
   // The function expects all users of |value| to be OpCompositeExtract
   // instructions. Otherwise the function returns false with an error message.
-  assert(value->opcode() == SpvOpLoad);
+  assert(value->opcode() == spv::Op::OpLoad);
   assert(value->GetSingleWordInOperand(0) == var->result_id());
   std::vector<Instruction*> work_list;
   bool failed = !get_def_use_mgr()->WhileEachUser(
       value->result_id(), [this, &work_list](Instruction* use) {
-        if (use->opcode() != SpvOpCompositeExtract) {
+        if (use->opcode() != spv::Op::OpCompositeExtract) {
           context()->EmitErrorMessage(
               "Variable cannot be replaced: invalid instruction", use);
           return false;
@@ -384,7 +385,7 @@
 
 bool DescriptorScalarReplacement::ReplaceCompositeExtract(
     Instruction* var, Instruction* extract) {
-  assert(extract->opcode() == SpvOpCompositeExtract);
+  assert(extract->opcode() == spv::Op::OpCompositeExtract);
   // We're currently only supporting extractions of one index at a time. If we
   // need to, we can handle cases with multiple indexes in the future.
   if (extract->NumInOperands() != 2) {
@@ -400,7 +401,7 @@
   // OpCompositeExtract.
   uint32_t load_id = TakeNextId();
   std::unique_ptr<Instruction> load(
-      new Instruction(context(), SpvOpLoad, extract->type_id(), load_id,
+      new Instruction(context(), spv::Op::OpLoad, extract->type_id(), load_id,
                       std::initializer_list<Operand>{
                           {SPV_OPERAND_TYPE_ID, {replacement_var}}}));
   Instruction* load_instr = load.get();
diff --git a/source/opt/desc_sroa_util.cpp b/source/opt/desc_sroa_util.cpp
index 1954e2c..dba3de9 100644
--- a/source/opt/desc_sroa_util.cpp
+++ b/source/opt/desc_sroa_util.cpp
@@ -17,12 +17,11 @@
 namespace spvtools {
 namespace opt {
 namespace {
-
-const uint32_t kOpAccessChainInOperandIndexes = 1;
+constexpr uint32_t kOpAccessChainInOperandIndexes = 1;
 
 // Returns the length of array type |type|.
 uint32_t GetLengthOfArrayType(IRContext* context, Instruction* type) {
-  assert(type->opcode() == SpvOpTypeArray && "type must be array");
+  assert(type->opcode() == spv::Op::OpTypeArray && "type must be array");
   uint32_t length_id = type->GetSingleWordInOperand(1);
   const analysis::Constant* length_const =
       context->get_constant_mgr()->FindDeclaredConstant(length_id);
@@ -35,20 +34,20 @@
 namespace descsroautil {
 
 bool IsDescriptorArray(IRContext* context, Instruction* var) {
-  if (var->opcode() != SpvOpVariable) {
+  if (var->opcode() != spv::Op::OpVariable) {
     return false;
   }
 
   uint32_t ptr_type_id = var->type_id();
   Instruction* ptr_type_inst = context->get_def_use_mgr()->GetDef(ptr_type_id);
-  if (ptr_type_inst->opcode() != SpvOpTypePointer) {
+  if (ptr_type_inst->opcode() != spv::Op::OpTypePointer) {
     return false;
   }
 
   uint32_t var_type_id = ptr_type_inst->GetSingleWordInOperand(1);
   Instruction* var_type_inst = context->get_def_use_mgr()->GetDef(var_type_id);
-  if (var_type_inst->opcode() != SpvOpTypeArray &&
-      var_type_inst->opcode() != SpvOpTypeStruct) {
+  if (var_type_inst->opcode() != spv::Op::OpTypeArray &&
+      var_type_inst->opcode() != spv::Op::OpTypeStruct) {
     return false;
   }
 
@@ -59,23 +58,23 @@
   }
 
   if (!context->get_decoration_mgr()->HasDecoration(
-          var->result_id(), SpvDecorationDescriptorSet)) {
+          var->result_id(), uint32_t(spv::Decoration::DescriptorSet))) {
     return false;
   }
 
-  return context->get_decoration_mgr()->HasDecoration(var->result_id(),
-                                                      SpvDecorationBinding);
+  return context->get_decoration_mgr()->HasDecoration(
+      var->result_id(), uint32_t(spv::Decoration::Binding));
 }
 
 bool IsTypeOfStructuredBuffer(IRContext* context, const Instruction* type) {
-  if (type->opcode() != SpvOpTypeStruct) {
+  if (type->opcode() != spv::Op::OpTypeStruct) {
     return false;
   }
 
   // All buffers have offset decorations for members of their structure types.
   // This is how we distinguish it from a structure of descriptors.
-  return context->get_decoration_mgr()->HasDecoration(type->result_id(),
-                                                      SpvDecorationOffset);
+  return context->get_decoration_mgr()->HasDecoration(
+      type->result_id(), uint32_t(spv::Decoration::Offset));
 }
 
 const analysis::Constant* GetAccessChainIndexAsConst(
@@ -99,15 +98,15 @@
                                              Instruction* var) {
   uint32_t ptr_type_id = var->type_id();
   Instruction* ptr_type_inst = context->get_def_use_mgr()->GetDef(ptr_type_id);
-  assert(ptr_type_inst->opcode() == SpvOpTypePointer &&
+  assert(ptr_type_inst->opcode() == spv::Op::OpTypePointer &&
          "Variable should be a pointer to an array or structure.");
   uint32_t pointee_type_id = ptr_type_inst->GetSingleWordInOperand(1);
   Instruction* pointee_type_inst =
       context->get_def_use_mgr()->GetDef(pointee_type_id);
-  if (pointee_type_inst->opcode() == SpvOpTypeArray) {
+  if (pointee_type_inst->opcode() == spv::Op::OpTypeArray) {
     return GetLengthOfArrayType(context, pointee_type_inst);
   }
-  assert(pointee_type_inst->opcode() == SpvOpTypeStruct &&
+  assert(pointee_type_inst->opcode() == spv::Op::OpTypeStruct &&
          "Variable should be a pointer to an array or structure.");
   return pointee_type_inst->NumInOperands();
 }
diff --git a/source/opt/dominator_analysis.cpp b/source/opt/dominator_analysis.cpp
index b692d26..eb6dfc9 100644
--- a/source/opt/dominator_analysis.cpp
+++ b/source/opt/dominator_analysis.cpp
@@ -64,7 +64,7 @@
 
   // We handle OpLabel instructions explicitly since they are not stored in the
   // instruction list.
-  if (current->opcode() == SpvOpLabel) {
+  if (current->opcode() == spv::Op::OpLabel) {
     return true;
   }
 
diff --git a/source/opt/dominator_tree.cpp b/source/opt/dominator_tree.cpp
index 2680be2..3c161a9 100644
--- a/source/opt/dominator_tree.cpp
+++ b/source/opt/dominator_tree.cpp
@@ -55,8 +55,8 @@
 // called on each node traversed BEFORE their children.
 template <typename BBType, typename SuccessorLambda, typename PreLambda,
           typename PostLambda>
-static void DepthFirstSearch(const BBType* bb, SuccessorLambda successors,
-                             PreLambda pre, PostLambda post) {
+void DepthFirstSearch(const BBType* bb, SuccessorLambda successors,
+                      PreLambda pre, PostLambda post) {
   auto no_terminal_blocks = [](const BBType*) { return false; };
   CFA<BBType>::DepthFirstTraversal(bb, successors, pre, post,
                                    no_terminal_blocks);
@@ -73,9 +73,8 @@
 // PostLambda - Lamdba matching the signature of 'void (const BBType*)' will be
 // called on each node traversed after their children.
 template <typename BBType, typename SuccessorLambda, typename PostLambda>
-static void DepthFirstSearchPostOrder(const BBType* bb,
-                                      SuccessorLambda successors,
-                                      PostLambda post) {
+void DepthFirstSearchPostOrder(const BBType* bb, SuccessorLambda successors,
+                               PostLambda post) {
   // Ignore preorder operation.
   auto nop_preorder = [](const BBType*) {};
   DepthFirstSearch(bb, successors, nop_preorder, post);
diff --git a/source/opt/eliminate_dead_constant_pass.cpp b/source/opt/eliminate_dead_constant_pass.cpp
index d368bd1..500fd8a 100644
--- a/source/opt/eliminate_dead_constant_pass.cpp
+++ b/source/opt/eliminate_dead_constant_pass.cpp
@@ -20,7 +20,6 @@
 #include <vector>
 
 #include "source/opt/def_use_manager.h"
-#include "source/opt/ir_context.h"
 #include "source/opt/log.h"
 #include "source/opt/reflect.h"
 
@@ -40,7 +39,7 @@
     context()->get_def_use_mgr()->ForEachUse(
         const_id, [&count](Instruction* user, uint32_t index) {
           (void)index;
-          SpvOp op = user->opcode();
+          spv::Op op = user->opcode();
           if (!(IsAnnotationInst(op) || IsDebug1Inst(op) || IsDebug2Inst(op) ||
                 IsDebug3Inst(op))) {
             ++count;
@@ -59,9 +58,9 @@
     Instruction* inst = *working_list.begin();
     // Back propagate if the instruction contains IDs in its operands.
     switch (inst->opcode()) {
-      case SpvOp::SpvOpConstantComposite:
-      case SpvOp::SpvOpSpecConstantComposite:
-      case SpvOp::SpvOpSpecConstantOp:
+      case spv::Op::OpConstantComposite:
+      case spv::Op::OpSpecConstantComposite:
+      case spv::Op::OpSpecConstantOp:
         for (uint32_t i = 0; i < inst->NumInOperands(); i++) {
           // SpecConstantOp instruction contains 'opcode' as its operand. Need
           // to exclude such operands when decreasing uses.
diff --git a/source/opt/eliminate_dead_functions_util.cpp b/source/opt/eliminate_dead_functions_util.cpp
index 1379120..e95b7f6 100644
--- a/source/opt/eliminate_dead_functions_util.cpp
+++ b/source/opt/eliminate_dead_functions_util.cpp
@@ -28,16 +28,18 @@
       ->ForEachInst(
           [context, first_func, func_iter, &seen_func_end,
            &to_kill](Instruction* inst) {
-            if (inst->opcode() == SpvOpFunctionEnd) {
+            if (inst->opcode() == spv::Op::OpFunctionEnd) {
               seen_func_end = true;
             }
             // Move non-semantic instructions to the previous function or
             // global values if this is the first function.
-            if (seen_func_end && inst->opcode() == SpvOpExtInst) {
+            if (seen_func_end && inst->opcode() == spv::Op::OpExtInst) {
               assert(inst->IsNonSemanticInstruction());
               if (to_kill.find(inst) != to_kill.end()) return;
               std::unique_ptr<Instruction> clone(inst->Clone(context));
-              context->ForgetUses(inst);
+              // Clear uses of "inst" to in case this moves a dependent chain of
+              // instructions.
+              context->get_def_use_mgr()->ClearInst(inst);
               context->AnalyzeDefUse(clone.get());
               if (first_func) {
                 context->AddGlobalValue(std::move(clone));
diff --git a/source/opt/eliminate_dead_input_components_pass.cpp b/source/opt/eliminate_dead_input_components_pass.cpp
deleted file mode 100644
index aa2776b..0000000
--- a/source/opt/eliminate_dead_input_components_pass.cpp
+++ /dev/null
@@ -1,189 +0,0 @@
-// Copyright (c) 2022 The Khronos Group Inc.
-// Copyright (c) 2022 LunarG Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "source/opt/eliminate_dead_input_components_pass.h"
-
-#include <set>
-#include <vector>
-
-#include "source/opt/instruction.h"
-#include "source/opt/ir_builder.h"
-#include "source/opt/ir_context.h"
-#include "source/util/bit_vector.h"
-
-namespace {
-
-const uint32_t kAccessChainBaseInIdx = 0;
-const uint32_t kAccessChainIndex0InIdx = 1;
-const uint32_t kConstantValueInIdx = 0;
-const uint32_t kVariableStorageClassInIdx = 0;
-
-}  // namespace
-
-namespace spvtools {
-namespace opt {
-
-Pass::Status EliminateDeadInputComponentsPass::Process() {
-  // Current functionality assumes shader capability
-  if (!context()->get_feature_mgr()->HasCapability(SpvCapabilityShader))
-    return Status::SuccessWithoutChange;
-  analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
-  analysis::TypeManager* type_mgr = context()->get_type_mgr();
-  bool modified = false;
-  std::vector<std::pair<Instruction*, unsigned>> arrays_to_change;
-  for (auto& var : context()->types_values()) {
-    if (var.opcode() != SpvOpVariable) {
-      continue;
-    }
-    analysis::Type* var_type = type_mgr->GetType(var.type_id());
-    analysis::Pointer* ptr_type = var_type->AsPointer();
-    if (ptr_type == nullptr) {
-      continue;
-    }
-    if (ptr_type->storage_class() != SpvStorageClassInput) {
-      continue;
-    }
-    const analysis::Array* arr_type = ptr_type->pointee_type()->AsArray();
-    if (arr_type != nullptr) {
-      unsigned arr_len_id = arr_type->LengthId();
-      Instruction* arr_len_inst = def_use_mgr->GetDef(arr_len_id);
-      if (arr_len_inst->opcode() != SpvOpConstant) {
-        continue;
-      }
-      // SPIR-V requires array size is >= 1, so this works for signed or
-      // unsigned size
-      unsigned original_max =
-          arr_len_inst->GetSingleWordInOperand(kConstantValueInIdx) - 1;
-      unsigned max_idx = FindMaxIndex(var, original_max);
-      if (max_idx != original_max) {
-        ChangeArrayLength(var, max_idx + 1);
-        modified = true;
-      }
-      continue;
-    }
-    const analysis::Struct* struct_type = ptr_type->pointee_type()->AsStruct();
-    if (struct_type == nullptr) continue;
-    const auto elt_types = struct_type->element_types();
-    unsigned original_max = static_cast<unsigned>(elt_types.size()) - 1;
-    unsigned max_idx = FindMaxIndex(var, original_max);
-    if (max_idx != original_max) {
-      ChangeStructLength(var, max_idx + 1);
-      modified = true;
-    }
-  }
-
-  return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
-}
-
-unsigned EliminateDeadInputComponentsPass::FindMaxIndex(Instruction& var,
-                                                        unsigned original_max) {
-  unsigned max = 0;
-  bool seen_non_const_ac = false;
-  assert(var.opcode() == SpvOpVariable && "must be variable");
-  context()->get_def_use_mgr()->WhileEachUser(
-      var.result_id(), [&max, &seen_non_const_ac, var, this](Instruction* use) {
-        auto use_opcode = use->opcode();
-        if (use_opcode == SpvOpLoad || use_opcode == SpvOpCopyMemory ||
-            use_opcode == SpvOpCopyMemorySized ||
-            use_opcode == SpvOpCopyObject) {
-          seen_non_const_ac = true;
-          return false;
-        }
-        if (use->opcode() != SpvOpAccessChain &&
-            use->opcode() != SpvOpInBoundsAccessChain) {
-          return true;
-        }
-        // OpAccessChain with no indices currently not optimized
-        if (use->NumInOperands() == 1) {
-          seen_non_const_ac = true;
-          return false;
-        }
-        unsigned base_id = use->GetSingleWordInOperand(kAccessChainBaseInIdx);
-        USE_ASSERT(base_id == var.result_id() && "unexpected base");
-        unsigned idx_id = use->GetSingleWordInOperand(kAccessChainIndex0InIdx);
-        Instruction* idx_inst = context()->get_def_use_mgr()->GetDef(idx_id);
-        if (idx_inst->opcode() != SpvOpConstant) {
-          seen_non_const_ac = true;
-          return false;
-        }
-        unsigned value = idx_inst->GetSingleWordInOperand(kConstantValueInIdx);
-        if (value > max) max = value;
-        return true;
-      });
-  return seen_non_const_ac ? original_max : max;
-}
-
-void EliminateDeadInputComponentsPass::ChangeArrayLength(Instruction& arr_var,
-                                                         unsigned length) {
-  analysis::TypeManager* type_mgr = context()->get_type_mgr();
-  analysis::ConstantManager* const_mgr = context()->get_constant_mgr();
-  analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
-  analysis::Pointer* ptr_type =
-      type_mgr->GetType(arr_var.type_id())->AsPointer();
-  const analysis::Array* arr_ty = ptr_type->pointee_type()->AsArray();
-  assert(arr_ty && "expecting array type");
-  uint32_t length_id = const_mgr->GetUIntConst(length);
-  analysis::Array new_arr_ty(arr_ty->element_type(),
-                             arr_ty->GetConstantLengthInfo(length_id, length));
-  analysis::Type* reg_new_arr_ty = type_mgr->GetRegisteredType(&new_arr_ty);
-  analysis::Pointer new_ptr_ty(reg_new_arr_ty, SpvStorageClassInput);
-  analysis::Type* reg_new_ptr_ty = type_mgr->GetRegisteredType(&new_ptr_ty);
-  uint32_t new_ptr_ty_id = type_mgr->GetTypeInstruction(reg_new_ptr_ty);
-  arr_var.SetResultType(new_ptr_ty_id);
-  def_use_mgr->AnalyzeInstUse(&arr_var);
-  // Move arr_var after its new type to preserve order
-  USE_ASSERT(arr_var.GetSingleWordInOperand(kVariableStorageClassInIdx) !=
-                 SpvStorageClassFunction &&
-             "cannot move Function variable");
-  Instruction* new_ptr_ty_inst = def_use_mgr->GetDef(new_ptr_ty_id);
-  arr_var.RemoveFromList();
-  arr_var.InsertAfter(new_ptr_ty_inst);
-}
-
-void EliminateDeadInputComponentsPass::ChangeStructLength(
-    Instruction& struct_var, unsigned length) {
-  analysis::TypeManager* type_mgr = context()->get_type_mgr();
-  analysis::Pointer* ptr_type =
-      type_mgr->GetType(struct_var.type_id())->AsPointer();
-  const analysis::Struct* struct_ty = ptr_type->pointee_type()->AsStruct();
-  assert(struct_ty && "expecting struct type");
-  const auto orig_elt_types = struct_ty->element_types();
-  std::vector<const analysis::Type*> new_elt_types;
-  for (unsigned u = 0; u < length; ++u)
-    new_elt_types.push_back(orig_elt_types[u]);
-  analysis::Struct new_struct_ty(new_elt_types);
-  analysis::Type* reg_new_struct_ty =
-      type_mgr->GetRegisteredType(&new_struct_ty);
-  uint32_t new_struct_ty_id = type_mgr->GetTypeInstruction(reg_new_struct_ty);
-  uint32_t old_struct_ty_id = type_mgr->GetTypeInstruction(struct_ty);
-  analysis::DecorationManager* deco_mgr = context()->get_decoration_mgr();
-  deco_mgr->CloneDecorations(old_struct_ty_id, new_struct_ty_id);
-  analysis::Pointer new_ptr_ty(reg_new_struct_ty, SpvStorageClassInput);
-  analysis::Type* reg_new_ptr_ty = type_mgr->GetRegisteredType(&new_ptr_ty);
-  uint32_t new_ptr_ty_id = type_mgr->GetTypeInstruction(reg_new_ptr_ty);
-  struct_var.SetResultType(new_ptr_ty_id);
-  analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
-  def_use_mgr->AnalyzeInstUse(&struct_var);
-  // Move struct_var after its new type to preserve order
-  USE_ASSERT(struct_var.GetSingleWordInOperand(kVariableStorageClassInIdx) !=
-                 SpvStorageClassFunction &&
-             "cannot move Function variable");
-  Instruction* new_ptr_ty_inst = def_use_mgr->GetDef(new_ptr_ty_id);
-  struct_var.RemoveFromList();
-  struct_var.InsertAfter(new_ptr_ty_inst);
-}
-
-}  // namespace opt
-}  // namespace spvtools
diff --git a/source/opt/eliminate_dead_io_components_pass.cpp b/source/opt/eliminate_dead_io_components_pass.cpp
new file mode 100644
index 0000000..5553a33
--- /dev/null
+++ b/source/opt/eliminate_dead_io_components_pass.cpp
@@ -0,0 +1,256 @@
+// Copyright (c) 2022 The Khronos Group Inc.
+// Copyright (c) 2022 LunarG Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/opt/eliminate_dead_io_components_pass.h"
+
+#include <vector>
+
+#include "source/opt/instruction.h"
+#include "source/opt/ir_context.h"
+#include "source/util/bit_vector.h"
+
+namespace spvtools {
+namespace opt {
+namespace {
+constexpr uint32_t kAccessChainBaseInIdx = 0;
+constexpr uint32_t kAccessChainIndex0InIdx = 1;
+constexpr uint32_t kAccessChainIndex1InIdx = 2;
+constexpr uint32_t kConstantValueInIdx = 0;
+}  // namespace
+
+Pass::Status EliminateDeadIOComponentsPass::Process() {
+  // Only process input and output variables
+  if (elim_sclass_ != spv::StorageClass::Input &&
+      elim_sclass_ != spv::StorageClass::Output) {
+    if (consumer()) {
+      std::string message =
+          "EliminateDeadIOComponentsPass only valid for input and output "
+          "variables.";
+      consumer()(SPV_MSG_ERROR, 0, {0, 0, 0}, message.c_str());
+    }
+    return Status::Failure;
+  }
+  // If safe mode, only process Input variables in vertex shader
+  const auto stage = context()->GetStage();
+  if (safe_mode_ && !(stage == spv::ExecutionModel::Vertex &&
+                      elim_sclass_ == spv::StorageClass::Input))
+    return Status::SuccessWithoutChange;
+  // Current functionality assumes shader capability.
+  if (!context()->get_feature_mgr()->HasCapability(spv::Capability::Shader))
+    return Status::SuccessWithoutChange;
+  // Current functionality assumes vert, frag, tesc, tese or geom shader.
+  // TODO(issue #4988): Add GLCompute.
+  if (stage != spv::ExecutionModel::Vertex &&
+      stage != spv::ExecutionModel::Fragment &&
+      stage != spv::ExecutionModel::TessellationControl &&
+      stage != spv::ExecutionModel::TessellationEvaluation &&
+      stage != spv::ExecutionModel::Geometry)
+    return Status::SuccessWithoutChange;
+  analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
+  analysis::TypeManager* type_mgr = context()->get_type_mgr();
+  bool modified = false;
+  std::vector<Instruction*> vars_to_move;
+  for (auto& var : context()->types_values()) {
+    if (var.opcode() != spv::Op::OpVariable) {
+      continue;
+    }
+    analysis::Type* var_type = type_mgr->GetType(var.type_id());
+    analysis::Pointer* ptr_type = var_type->AsPointer();
+    if (ptr_type == nullptr) {
+      continue;
+    }
+    const auto sclass = ptr_type->storage_class();
+    if (sclass != elim_sclass_) {
+      continue;
+    }
+    // For tesc, or input variables in tese or geom shaders,
+    // there is a outer per-vertex-array that must be ignored
+    // for the purposes of this analysis/optimization. Do the
+    // analysis on the inner type in these cases.
+    bool skip_first_index = false;
+    auto core_type = ptr_type->pointee_type();
+    if (stage == spv::ExecutionModel::TessellationControl ||
+        (sclass == spv::StorageClass::Input &&
+         (stage == spv::ExecutionModel::TessellationEvaluation ||
+          stage == spv::ExecutionModel::Geometry))) {
+      auto arr_type = core_type->AsArray();
+      if (!arr_type) continue;
+      core_type = arr_type->element_type();
+      skip_first_index = true;
+    }
+    const analysis::Array* arr_type = core_type->AsArray();
+    if (arr_type != nullptr) {
+      // Only process array if input of vertex shader, or output of
+      // fragment shader. Otherwise, if one shader has a runtime index and the
+      // other does not, interface incompatibility can occur.
+      if (!((sclass == spv::StorageClass::Input &&
+             stage == spv::ExecutionModel::Vertex) ||
+            (sclass == spv::StorageClass::Output &&
+             stage == spv::ExecutionModel::Fragment)))
+        continue;
+      unsigned arr_len_id = arr_type->LengthId();
+      Instruction* arr_len_inst = def_use_mgr->GetDef(arr_len_id);
+      if (arr_len_inst->opcode() != spv::Op::OpConstant) {
+        continue;
+      }
+      // SPIR-V requires array size is >= 1, so this works for signed or
+      // unsigned size.
+      unsigned original_max =
+          arr_len_inst->GetSingleWordInOperand(kConstantValueInIdx) - 1;
+      unsigned max_idx = FindMaxIndex(var, original_max);
+      if (max_idx != original_max) {
+        ChangeArrayLength(var, max_idx + 1);
+        vars_to_move.push_back(&var);
+        modified = true;
+      }
+      continue;
+    }
+    const analysis::Struct* struct_type = core_type->AsStruct();
+    if (struct_type == nullptr) continue;
+    const auto elt_types = struct_type->element_types();
+    unsigned original_max = static_cast<unsigned>(elt_types.size()) - 1;
+    unsigned max_idx = FindMaxIndex(var, original_max, skip_first_index);
+    if (max_idx != original_max) {
+      ChangeIOVarStructLength(var, max_idx + 1);
+      vars_to_move.push_back(&var);
+      modified = true;
+    }
+  }
+
+  // Move changed vars after their new type instruction to preserve backward
+  // referencing.
+  for (auto var : vars_to_move) {
+    auto type_id = var->type_id();
+    auto type_inst = def_use_mgr->GetDef(type_id);
+    var->RemoveFromList();
+    var->InsertAfter(type_inst);
+  }
+
+  return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
+}
+
+unsigned EliminateDeadIOComponentsPass::FindMaxIndex(
+    const Instruction& var, const unsigned original_max,
+    const bool skip_first_index) {
+  unsigned max = 0;
+  bool seen_non_const_ac = false;
+  assert(var.opcode() == spv::Op::OpVariable && "must be variable");
+  context()->get_def_use_mgr()->WhileEachUser(
+      var.result_id(), [&max, &seen_non_const_ac, var, skip_first_index,
+                        this](Instruction* use) {
+        auto use_opcode = use->opcode();
+        if (use_opcode == spv::Op::OpLoad || use_opcode == spv::Op::OpStore ||
+            use_opcode == spv::Op::OpCopyMemory ||
+            use_opcode == spv::Op::OpCopyMemorySized ||
+            use_opcode == spv::Op::OpCopyObject) {
+          seen_non_const_ac = true;
+          return false;
+        }
+        if (use->opcode() != spv::Op::OpAccessChain &&
+            use->opcode() != spv::Op::OpInBoundsAccessChain) {
+          return true;
+        }
+        // OpAccessChain with no indices currently not optimized
+        if (use->NumInOperands() == 1 ||
+            (skip_first_index && use->NumInOperands() == 2)) {
+          seen_non_const_ac = true;
+          return false;
+        }
+        const unsigned base_id =
+            use->GetSingleWordInOperand(kAccessChainBaseInIdx);
+        USE_ASSERT(base_id == var.result_id() && "unexpected base");
+        const unsigned in_idx = skip_first_index ? kAccessChainIndex1InIdx
+                                                 : kAccessChainIndex0InIdx;
+        const unsigned idx_id = use->GetSingleWordInOperand(in_idx);
+        Instruction* idx_inst = context()->get_def_use_mgr()->GetDef(idx_id);
+        if (idx_inst->opcode() != spv::Op::OpConstant) {
+          seen_non_const_ac = true;
+          return false;
+        }
+        unsigned value = idx_inst->GetSingleWordInOperand(kConstantValueInIdx);
+        if (value > max) max = value;
+        return true;
+      });
+  return seen_non_const_ac ? original_max : max;
+}
+
+void EliminateDeadIOComponentsPass::ChangeArrayLength(Instruction& arr_var,
+                                                      unsigned length) {
+  analysis::TypeManager* type_mgr = context()->get_type_mgr();
+  analysis::ConstantManager* const_mgr = context()->get_constant_mgr();
+  analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
+  analysis::Pointer* ptr_type =
+      type_mgr->GetType(arr_var.type_id())->AsPointer();
+  const analysis::Array* arr_ty = ptr_type->pointee_type()->AsArray();
+  assert(arr_ty && "expecting array type");
+  uint32_t length_id = const_mgr->GetUIntConstId(length);
+  analysis::Array new_arr_ty(arr_ty->element_type(),
+                             arr_ty->GetConstantLengthInfo(length_id, length));
+  analysis::Type* reg_new_arr_ty = type_mgr->GetRegisteredType(&new_arr_ty);
+  analysis::Pointer new_ptr_ty(reg_new_arr_ty, ptr_type->storage_class());
+  analysis::Type* reg_new_ptr_ty = type_mgr->GetRegisteredType(&new_ptr_ty);
+  uint32_t new_ptr_ty_id = type_mgr->GetTypeInstruction(reg_new_ptr_ty);
+  arr_var.SetResultType(new_ptr_ty_id);
+  def_use_mgr->AnalyzeInstUse(&arr_var);
+}
+
+void EliminateDeadIOComponentsPass::ChangeIOVarStructLength(Instruction& io_var,
+                                                            unsigned length) {
+  analysis::TypeManager* type_mgr = context()->get_type_mgr();
+  analysis::Pointer* ptr_type =
+      type_mgr->GetType(io_var.type_id())->AsPointer();
+  auto core_type = ptr_type->pointee_type();
+  // Check for per-vertex-array of struct from tesc, tese and geom and grab
+  // embedded struct type.
+  const auto arr_type = core_type->AsArray();
+  if (arr_type) core_type = arr_type->element_type();
+  const analysis::Struct* struct_ty = core_type->AsStruct();
+  assert(struct_ty && "expecting struct type");
+  const auto orig_elt_types = struct_ty->element_types();
+  std::vector<const analysis::Type*> new_elt_types;
+  for (unsigned u = 0; u < length; ++u)
+    new_elt_types.push_back(orig_elt_types[u]);
+  analysis::Struct new_struct_ty(new_elt_types);
+  uint32_t old_struct_ty_id = type_mgr->GetTypeInstruction(struct_ty);
+  std::vector<Instruction*> decorations =
+      context()->get_decoration_mgr()->GetDecorationsFor(old_struct_ty_id,
+                                                         true);
+  for (auto dec : decorations) {
+    if (dec->opcode() == spv::Op::OpMemberDecorate) {
+      uint32_t midx = dec->GetSingleWordInOperand(1);
+      if (midx >= length) continue;
+    }
+    type_mgr->AttachDecoration(*dec, &new_struct_ty);
+  }
+  // Clone name instructions for new struct type
+  analysis::Type* reg_new_str_ty = type_mgr->GetRegisteredType(&new_struct_ty);
+  uint32_t new_struct_ty_id = type_mgr->GetTypeInstruction(reg_new_str_ty);
+  context()->CloneNames(old_struct_ty_id, new_struct_ty_id, length);
+  // Attach new type to var
+  analysis::Type* reg_new_var_ty = reg_new_str_ty;
+  if (arr_type) {
+    analysis::Array new_arr_ty(reg_new_var_ty, arr_type->length_info());
+    reg_new_var_ty = type_mgr->GetRegisteredType(&new_arr_ty);
+  }
+  analysis::Pointer new_ptr_ty(reg_new_var_ty, elim_sclass_);
+  analysis::Type* reg_new_ptr_ty = type_mgr->GetRegisteredType(&new_ptr_ty);
+  uint32_t new_ptr_ty_id = type_mgr->GetTypeInstruction(reg_new_ptr_ty);
+  io_var.SetResultType(new_ptr_ty_id);
+  analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
+  def_use_mgr->AnalyzeInstUse(&io_var);
+}
+
+}  // namespace opt
+}  // namespace spvtools
diff --git a/source/opt/eliminate_dead_input_components_pass.h b/source/opt/eliminate_dead_io_components_pass.h
similarity index 69%
rename from source/opt/eliminate_dead_input_components_pass.h
rename to source/opt/eliminate_dead_io_components_pass.h
index a3a133c..ef4dfb7 100644
--- a/source/opt/eliminate_dead_input_components_pass.h
+++ b/source/opt/eliminate_dead_io_components_pass.h
@@ -26,14 +26,15 @@
 namespace opt {
 
 // See optimizer.hpp for documentation.
-class EliminateDeadInputComponentsPass : public Pass {
+class EliminateDeadIOComponentsPass : public Pass {
  public:
-  explicit EliminateDeadInputComponentsPass() {}
+  explicit EliminateDeadIOComponentsPass(spv::StorageClass elim_sclass,
+                                         bool safe_mode = true)
+      : elim_sclass_(elim_sclass), safe_mode_(safe_mode) {}
 
   const char* name() const override {
     return "eliminate-dead-input-components";
   }
-
   Status Process() override;
 
   // Return the mask of preserved Analyses.
@@ -50,13 +51,22 @@
   // Find the max constant used to index the variable declared by |var|
   // through OpAccessChain or OpInBoundsAccessChain. If any non-constant
   // indices or non-Op*AccessChain use of |var|, return |original_max|.
-  unsigned FindMaxIndex(Instruction& var, unsigned original_max);
+  unsigned FindMaxIndex(const Instruction& var, const unsigned original_max,
+                        const bool skip_first_index = false);
 
   // Change the length of the array |inst| to |length|
   void ChangeArrayLength(Instruction& inst, unsigned length);
 
-  // Change the length of the struct |struct_var| to |length|
-  void ChangeStructLength(Instruction& struct_var, unsigned length);
+  // Change the length of the struct in |io_var| to |length|. |io_var|
+  // is either the struct or a per-vertex-array of the struct.
+  void ChangeIOVarStructLength(Instruction& io_var, unsigned length);
+
+  // Storage class to be optimized. Must be Input or Output.
+  spv::StorageClass elim_sclass_;
+
+  // Only make changes that will not cause interface incompatibility if done
+  // standalone. Currently this is only Input variables in vertex shaders.
+  bool safe_mode_;
 };
 
 }  // namespace opt
diff --git a/source/opt/eliminate_dead_members_pass.cpp b/source/opt/eliminate_dead_members_pass.cpp
index 52aca52..1c98502 100644
--- a/source/opt/eliminate_dead_members_pass.cpp
+++ b/source/opt/eliminate_dead_members_pass.cpp
@@ -17,17 +17,16 @@
 #include "ir_builder.h"
 #include "source/opt/ir_context.h"
 
+namespace spvtools {
+namespace opt {
 namespace {
-const uint32_t kRemovedMember = 0xFFFFFFFF;
-const uint32_t kSpecConstOpOpcodeIdx = 0;
+constexpr uint32_t kRemovedMember = 0xFFFFFFFF;
+constexpr uint32_t kSpecConstOpOpcodeIdx = 0;
 constexpr uint32_t kArrayElementTypeIdx = 0;
 }  // namespace
 
-namespace spvtools {
-namespace opt {
-
 Pass::Status EliminateDeadMembersPass::Process() {
-  if (!context()->get_feature_mgr()->HasCapability(SpvCapabilityShader))
+  if (!context()->get_feature_mgr()->HasCapability(spv::Capability::Shader))
     return Status::SuccessWithoutChange;
 
   FindLiveMembers();
@@ -41,27 +40,27 @@
   // Until we have implemented the rewriting of OpSpecConsantOp instructions,
   // we have to mark them as fully used just to be safe.
   for (auto& inst : get_module()->types_values()) {
-    if (inst.opcode() == SpvOpSpecConstantOp) {
-      switch (inst.GetSingleWordInOperand(kSpecConstOpOpcodeIdx)) {
-        case SpvOpCompositeExtract:
+    if (inst.opcode() == spv::Op::OpSpecConstantOp) {
+      switch (spv::Op(inst.GetSingleWordInOperand(kSpecConstOpOpcodeIdx))) {
+        case spv::Op::OpCompositeExtract:
           MarkMembersAsLiveForExtract(&inst);
           break;
-        case SpvOpCompositeInsert:
+        case spv::Op::OpCompositeInsert:
           // Nothing specific to do.
           break;
-        case SpvOpAccessChain:
-        case SpvOpInBoundsAccessChain:
-        case SpvOpPtrAccessChain:
-        case SpvOpInBoundsPtrAccessChain:
+        case spv::Op::OpAccessChain:
+        case spv::Op::OpInBoundsAccessChain:
+        case spv::Op::OpPtrAccessChain:
+        case spv::Op::OpInBoundsPtrAccessChain:
           assert(false && "Not implemented yet.");
           break;
         default:
           break;
       }
-    } else if (inst.opcode() == SpvOpVariable) {
-      switch (inst.GetSingleWordInOperand(0)) {
-        case SpvStorageClassInput:
-        case SpvStorageClassOutput:
+    } else if (inst.opcode() == spv::Op::OpVariable) {
+      switch (spv::StorageClass(inst.GetSingleWordInOperand(0))) {
+        case spv::StorageClass::Input:
+        case spv::StorageClass::Output:
           MarkPointeeTypeAsFullUsed(inst.type_id());
           break;
         default:
@@ -86,34 +85,34 @@
 
 void EliminateDeadMembersPass::FindLiveMembers(const Instruction* inst) {
   switch (inst->opcode()) {
-    case SpvOpStore:
+    case spv::Op::OpStore:
       MarkMembersAsLiveForStore(inst);
       break;
-    case SpvOpCopyMemory:
-    case SpvOpCopyMemorySized:
+    case spv::Op::OpCopyMemory:
+    case spv::Op::OpCopyMemorySized:
       MarkMembersAsLiveForCopyMemory(inst);
       break;
-    case SpvOpCompositeExtract:
+    case spv::Op::OpCompositeExtract:
       MarkMembersAsLiveForExtract(inst);
       break;
-    case SpvOpAccessChain:
-    case SpvOpInBoundsAccessChain:
-    case SpvOpPtrAccessChain:
-    case SpvOpInBoundsPtrAccessChain:
+    case spv::Op::OpAccessChain:
+    case spv::Op::OpInBoundsAccessChain:
+    case spv::Op::OpPtrAccessChain:
+    case spv::Op::OpInBoundsPtrAccessChain:
       MarkMembersAsLiveForAccessChain(inst);
       break;
-    case SpvOpReturnValue:
+    case spv::Op::OpReturnValue:
       // This should be an issue only if we are returning from the entry point.
       // However, for now I will keep it more conservative because functions are
       // often inlined leaving only the entry points.
       MarkOperandTypeAsFullyUsed(inst, 0);
       break;
-    case SpvOpArrayLength:
+    case spv::Op::OpArrayLength:
       MarkMembersAsLiveForArrayLength(inst);
       break;
-    case SpvOpLoad:
-    case SpvOpCompositeInsert:
-    case SpvOpCompositeConstruct:
+    case spv::Op::OpLoad:
+    case spv::Op::OpCompositeInsert:
+    case spv::Op::OpCompositeConstruct:
       break;
     default:
       // This path is here for safety.  All instructions that can reference
@@ -131,7 +130,7 @@
   // memory that is read outside of the shader.  Other passes can remove all
   // store to memory that is not visible outside of the shader, so we do not
   // complicate the code for now.
-  assert(inst->opcode() == SpvOpStore);
+  assert(inst->opcode() == spv::Op::OpStore);
   uint32_t object_id = inst->GetSingleWordInOperand(1);
   Instruction* object_inst = context()->get_def_use_mgr()->GetDef(object_id);
   uint32_t object_type_id = object_inst->type_id();
@@ -143,15 +142,15 @@
   assert(type_inst != nullptr);
 
   switch (type_inst->opcode()) {
-    case SpvOpTypeStruct:
+    case spv::Op::OpTypeStruct:
       // Mark every member and its type as fully used.
       for (uint32_t i = 0; i < type_inst->NumInOperands(); ++i) {
         used_members_[type_id].insert(i);
         MarkTypeAsFullyUsed(type_inst->GetSingleWordInOperand(i));
       }
       break;
-    case SpvOpTypeArray:
-    case SpvOpTypeRuntimeArray:
+    case spv::Op::OpTypeArray:
+    case spv::Op::OpTypeRuntimeArray:
       MarkTypeAsFullyUsed(
           type_inst->GetSingleWordInOperand(kArrayElementTypeIdx));
       break;
@@ -162,7 +161,7 @@
 
 void EliminateDeadMembersPass::MarkPointeeTypeAsFullUsed(uint32_t ptr_type_id) {
   Instruction* ptr_type_inst = get_def_use_mgr()->GetDef(ptr_type_id);
-  assert(ptr_type_inst->opcode() == SpvOpTypePointer);
+  assert(ptr_type_inst->opcode() == spv::Op::OpTypePointer);
   MarkTypeAsFullyUsed(ptr_type_inst->GetSingleWordInOperand(1));
 }
 
@@ -178,12 +177,13 @@
 
 void EliminateDeadMembersPass::MarkMembersAsLiveForExtract(
     const Instruction* inst) {
-  assert(inst->opcode() == SpvOpCompositeExtract ||
-         (inst->opcode() == SpvOpSpecConstantOp &&
-          inst->GetSingleWordInOperand(kSpecConstOpOpcodeIdx) ==
-              SpvOpCompositeExtract));
+  assert(inst->opcode() == spv::Op::OpCompositeExtract ||
+         (inst->opcode() == spv::Op::OpSpecConstantOp &&
+          spv::Op(inst->GetSingleWordInOperand(kSpecConstOpOpcodeIdx)) ==
+              spv::Op::OpCompositeExtract));
 
-  uint32_t first_operand = (inst->opcode() == SpvOpSpecConstantOp ? 1 : 0);
+  uint32_t first_operand =
+      (inst->opcode() == spv::Op::OpSpecConstantOp ? 1 : 0);
   uint32_t composite_id = inst->GetSingleWordInOperand(first_operand);
   Instruction* composite_inst = get_def_use_mgr()->GetDef(composite_id);
   uint32_t type_id = composite_inst->type_id();
@@ -192,14 +192,14 @@
     Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
     uint32_t member_idx = inst->GetSingleWordInOperand(i);
     switch (type_inst->opcode()) {
-      case SpvOpTypeStruct:
+      case spv::Op::OpTypeStruct:
         used_members_[type_id].insert(member_idx);
         type_id = type_inst->GetSingleWordInOperand(member_idx);
         break;
-      case SpvOpTypeArray:
-      case SpvOpTypeRuntimeArray:
-      case SpvOpTypeVector:
-      case SpvOpTypeMatrix:
+      case spv::Op::OpTypeArray:
+      case spv::Op::OpTypeRuntimeArray:
+      case spv::Op::OpTypeVector:
+      case spv::Op::OpTypeMatrix:
         type_id = type_inst->GetSingleWordInOperand(0);
         break;
       default:
@@ -210,10 +210,10 @@
 
 void EliminateDeadMembersPass::MarkMembersAsLiveForAccessChain(
     const Instruction* inst) {
-  assert(inst->opcode() == SpvOpAccessChain ||
-         inst->opcode() == SpvOpInBoundsAccessChain ||
-         inst->opcode() == SpvOpPtrAccessChain ||
-         inst->opcode() == SpvOpInBoundsPtrAccessChain);
+  assert(inst->opcode() == spv::Op::OpAccessChain ||
+         inst->opcode() == spv::Op::OpInBoundsAccessChain ||
+         inst->opcode() == spv::Op::OpPtrAccessChain ||
+         inst->opcode() == spv::Op::OpInBoundsPtrAccessChain);
 
   uint32_t pointer_id = inst->GetSingleWordInOperand(0);
   Instruction* pointer_inst = get_def_use_mgr()->GetDef(pointer_id);
@@ -225,14 +225,14 @@
 
   // For a pointer access chain, we need to skip the |element| index.  It is not
   // a reference to the member of a struct, and it does not change the type.
-  uint32_t i = (inst->opcode() == SpvOpAccessChain ||
-                        inst->opcode() == SpvOpInBoundsAccessChain
+  uint32_t i = (inst->opcode() == spv::Op::OpAccessChain ||
+                        inst->opcode() == spv::Op::OpInBoundsAccessChain
                     ? 1
                     : 2);
   for (; i < inst->NumInOperands(); ++i) {
     Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
     switch (type_inst->opcode()) {
-      case SpvOpTypeStruct: {
+      case spv::Op::OpTypeStruct: {
         const analysis::IntConstant* member_idx =
             const_mgr->FindDeclaredConstant(inst->GetSingleWordInOperand(i))
                 ->AsIntConstant();
@@ -242,10 +242,10 @@
         used_members_[type_id].insert(index);
         type_id = type_inst->GetSingleWordInOperand(index);
       } break;
-      case SpvOpTypeArray:
-      case SpvOpTypeRuntimeArray:
-      case SpvOpTypeVector:
-      case SpvOpTypeMatrix:
+      case spv::Op::OpTypeArray:
+      case spv::Op::OpTypeRuntimeArray:
+      case spv::Op::OpTypeVector:
+      case spv::Op::OpTypeMatrix:
         type_id = type_inst->GetSingleWordInOperand(0);
         break;
       default:
@@ -263,7 +263,7 @@
 
 void EliminateDeadMembersPass::MarkMembersAsLiveForArrayLength(
     const Instruction* inst) {
-  assert(inst->opcode() == SpvOpArrayLength);
+  assert(inst->opcode() == spv::Op::OpArrayLength);
   uint32_t object_id = inst->GetSingleWordInOperand(0);
   Instruction* object_inst = get_def_use_mgr()->GetDef(object_id);
   uint32_t pointer_type_id = object_inst->type_id();
@@ -278,7 +278,7 @@
   // First update all of the OpTypeStruct instructions.
   get_module()->ForEachInst([&modified, this](Instruction* inst) {
     switch (inst->opcode()) {
-      case SpvOpTypeStruct:
+      case spv::Op::OpTypeStruct:
         modified |= UpdateOpTypeStruct(inst);
         break;
       default:
@@ -289,47 +289,47 @@
   // Now update all of the instructions that reference the OpTypeStructs.
   get_module()->ForEachInst([&modified, this](Instruction* inst) {
     switch (inst->opcode()) {
-      case SpvOpMemberName:
+      case spv::Op::OpMemberName:
         modified |= UpdateOpMemberNameOrDecorate(inst);
         break;
-      case SpvOpMemberDecorate:
+      case spv::Op::OpMemberDecorate:
         modified |= UpdateOpMemberNameOrDecorate(inst);
         break;
-      case SpvOpGroupMemberDecorate:
+      case spv::Op::OpGroupMemberDecorate:
         modified |= UpdateOpGroupMemberDecorate(inst);
         break;
-      case SpvOpSpecConstantComposite:
-      case SpvOpConstantComposite:
-      case SpvOpCompositeConstruct:
+      case spv::Op::OpSpecConstantComposite:
+      case spv::Op::OpConstantComposite:
+      case spv::Op::OpCompositeConstruct:
         modified |= UpdateConstantComposite(inst);
         break;
-      case SpvOpAccessChain:
-      case SpvOpInBoundsAccessChain:
-      case SpvOpPtrAccessChain:
-      case SpvOpInBoundsPtrAccessChain:
+      case spv::Op::OpAccessChain:
+      case spv::Op::OpInBoundsAccessChain:
+      case spv::Op::OpPtrAccessChain:
+      case spv::Op::OpInBoundsPtrAccessChain:
         modified |= UpdateAccessChain(inst);
         break;
-      case SpvOpCompositeExtract:
+      case spv::Op::OpCompositeExtract:
         modified |= UpdateCompsiteExtract(inst);
         break;
-      case SpvOpCompositeInsert:
+      case spv::Op::OpCompositeInsert:
         modified |= UpdateCompositeInsert(inst);
         break;
-      case SpvOpArrayLength:
+      case spv::Op::OpArrayLength:
         modified |= UpdateOpArrayLength(inst);
         break;
-      case SpvOpSpecConstantOp:
-        switch (inst->GetSingleWordInOperand(kSpecConstOpOpcodeIdx)) {
-          case SpvOpCompositeExtract:
+      case spv::Op::OpSpecConstantOp:
+        switch (spv::Op(inst->GetSingleWordInOperand(kSpecConstOpOpcodeIdx))) {
+          case spv::Op::OpCompositeExtract:
             modified |= UpdateCompsiteExtract(inst);
             break;
-          case SpvOpCompositeInsert:
+          case spv::Op::OpCompositeInsert:
             modified |= UpdateCompositeInsert(inst);
             break;
-          case SpvOpAccessChain:
-          case SpvOpInBoundsAccessChain:
-          case SpvOpPtrAccessChain:
-          case SpvOpInBoundsPtrAccessChain:
+          case spv::Op::OpAccessChain:
+          case spv::Op::OpInBoundsAccessChain:
+          case spv::Op::OpPtrAccessChain:
+          case spv::Op::OpInBoundsPtrAccessChain:
             assert(false && "Not implemented yet.");
             break;
           default:
@@ -344,7 +344,7 @@
 }
 
 bool EliminateDeadMembersPass::UpdateOpTypeStruct(Instruction* inst) {
-  assert(inst->opcode() == SpvOpTypeStruct);
+  assert(inst->opcode() == spv::Op::OpTypeStruct);
 
   const auto& live_members = used_members_[inst->result_id()];
   if (live_members.size() == inst->NumInOperands()) {
@@ -362,8 +362,8 @@
 }
 
 bool EliminateDeadMembersPass::UpdateOpMemberNameOrDecorate(Instruction* inst) {
-  assert(inst->opcode() == SpvOpMemberName ||
-         inst->opcode() == SpvOpMemberDecorate);
+  assert(inst->opcode() == spv::Op::OpMemberName ||
+         inst->opcode() == spv::Op::OpMemberDecorate);
 
   uint32_t type_id = inst->GetSingleWordInOperand(0);
   auto live_members = used_members_.find(type_id);
@@ -388,7 +388,7 @@
 }
 
 bool EliminateDeadMembersPass::UpdateOpGroupMemberDecorate(Instruction* inst) {
-  assert(inst->opcode() == SpvOpGroupMemberDecorate);
+  assert(inst->opcode() == spv::Op::OpGroupMemberDecorate);
 
   bool modified = false;
 
@@ -429,9 +429,9 @@
 }
 
 bool EliminateDeadMembersPass::UpdateConstantComposite(Instruction* inst) {
-  assert(inst->opcode() == SpvOpSpecConstantComposite ||
-         inst->opcode() == SpvOpConstantComposite ||
-         inst->opcode() == SpvOpCompositeConstruct);
+  assert(inst->opcode() == spv::Op::OpSpecConstantComposite ||
+         inst->opcode() == spv::Op::OpConstantComposite ||
+         inst->opcode() == spv::Op::OpCompositeConstruct);
   uint32_t type_id = inst->type_id();
 
   bool modified = false;
@@ -450,10 +450,10 @@
 }
 
 bool EliminateDeadMembersPass::UpdateAccessChain(Instruction* inst) {
-  assert(inst->opcode() == SpvOpAccessChain ||
-         inst->opcode() == SpvOpInBoundsAccessChain ||
-         inst->opcode() == SpvOpPtrAccessChain ||
-         inst->opcode() == SpvOpInBoundsPtrAccessChain);
+  assert(inst->opcode() == spv::Op::OpAccessChain ||
+         inst->opcode() == spv::Op::OpInBoundsAccessChain ||
+         inst->opcode() == spv::Op::OpPtrAccessChain ||
+         inst->opcode() == spv::Op::OpInBoundsPtrAccessChain);
 
   uint32_t pointer_id = inst->GetSingleWordInOperand(0);
   Instruction* pointer_inst = get_def_use_mgr()->GetDef(pointer_id);
@@ -467,8 +467,8 @@
   new_operands.emplace_back(inst->GetInOperand(0));
 
   // For pointer access chains we want to copy the element operand.
-  if (inst->opcode() == SpvOpPtrAccessChain ||
-      inst->opcode() == SpvOpInBoundsPtrAccessChain) {
+  if (inst->opcode() == spv::Op::OpPtrAccessChain ||
+      inst->opcode() == spv::Op::OpInBoundsPtrAccessChain) {
     new_operands.emplace_back(inst->GetInOperand(1));
   }
 
@@ -476,7 +476,7 @@
        i < inst->NumInOperands(); ++i) {
     Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
     switch (type_inst->opcode()) {
-      case SpvOpTypeStruct: {
+      case spv::Op::OpTypeStruct: {
         const analysis::IntConstant* member_idx =
             const_mgr->FindDeclaredConstant(inst->GetSingleWordInOperand(i))
                 ->AsIntConstant();
@@ -501,10 +501,10 @@
         // index.
         type_id = type_inst->GetSingleWordInOperand(new_member_idx);
       } break;
-      case SpvOpTypeArray:
-      case SpvOpTypeRuntimeArray:
-      case SpvOpTypeVector:
-      case SpvOpTypeMatrix:
+      case spv::Op::OpTypeArray:
+      case spv::Op::OpTypeRuntimeArray:
+      case spv::Op::OpTypeVector:
+      case spv::Op::OpTypeMatrix:
         new_operands.emplace_back(inst->GetInOperand(i));
         type_id = type_inst->GetSingleWordInOperand(0);
         break;
@@ -539,13 +539,13 @@
 }
 
 bool EliminateDeadMembersPass::UpdateCompsiteExtract(Instruction* inst) {
-  assert(inst->opcode() == SpvOpCompositeExtract ||
-         (inst->opcode() == SpvOpSpecConstantOp &&
-          inst->GetSingleWordInOperand(kSpecConstOpOpcodeIdx) ==
-              SpvOpCompositeExtract));
+  assert(inst->opcode() == spv::Op::OpCompositeExtract ||
+         (inst->opcode() == spv::Op::OpSpecConstantOp &&
+          spv::Op(inst->GetSingleWordInOperand(kSpecConstOpOpcodeIdx)) ==
+              spv::Op::OpCompositeExtract));
 
   uint32_t first_operand = 0;
-  if (inst->opcode() == SpvOpSpecConstantOp) {
+  if (inst->opcode() == spv::Op::OpSpecConstantOp) {
     first_operand = 1;
   }
   uint32_t object_id = inst->GetSingleWordInOperand(first_operand);
@@ -569,15 +569,15 @@
 
     Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
     switch (type_inst->opcode()) {
-      case SpvOpTypeStruct:
+      case spv::Op::OpTypeStruct:
         // The type will have already been rewritten, so use the new member
         // index.
         type_id = type_inst->GetSingleWordInOperand(new_member_idx);
         break;
-      case SpvOpTypeArray:
-      case SpvOpTypeRuntimeArray:
-      case SpvOpTypeVector:
-      case SpvOpTypeMatrix:
+      case spv::Op::OpTypeArray:
+      case spv::Op::OpTypeRuntimeArray:
+      case spv::Op::OpTypeVector:
+      case spv::Op::OpTypeMatrix:
         type_id = type_inst->GetSingleWordInOperand(0);
         break;
       default:
@@ -594,13 +594,13 @@
 }
 
 bool EliminateDeadMembersPass::UpdateCompositeInsert(Instruction* inst) {
-  assert(inst->opcode() == SpvOpCompositeInsert ||
-         (inst->opcode() == SpvOpSpecConstantOp &&
-          inst->GetSingleWordInOperand(kSpecConstOpOpcodeIdx) ==
-              SpvOpCompositeInsert));
+  assert(inst->opcode() == spv::Op::OpCompositeInsert ||
+         (inst->opcode() == spv::Op::OpSpecConstantOp &&
+          spv::Op(inst->GetSingleWordInOperand(kSpecConstOpOpcodeIdx)) ==
+              spv::Op::OpCompositeInsert));
 
   uint32_t first_operand = 0;
-  if (inst->opcode() == SpvOpSpecConstantOp) {
+  if (inst->opcode() == spv::Op::OpSpecConstantOp) {
     first_operand = 1;
   }
 
@@ -630,15 +630,15 @@
 
     Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
     switch (type_inst->opcode()) {
-      case SpvOpTypeStruct:
+      case spv::Op::OpTypeStruct:
         // The type will have already been rewritten, so use the new member
         // index.
         type_id = type_inst->GetSingleWordInOperand(new_member_idx);
         break;
-      case SpvOpTypeArray:
-      case SpvOpTypeRuntimeArray:
-      case SpvOpTypeVector:
-      case SpvOpTypeMatrix:
+      case spv::Op::OpTypeArray:
+      case spv::Op::OpTypeRuntimeArray:
+      case spv::Op::OpTypeVector:
+      case spv::Op::OpTypeMatrix:
         type_id = type_inst->GetSingleWordInOperand(0);
         break;
       default:
diff --git a/source/opt/eliminate_dead_output_stores_pass.cpp b/source/opt/eliminate_dead_output_stores_pass.cpp
new file mode 100644
index 0000000..99711a1
--- /dev/null
+++ b/source/opt/eliminate_dead_output_stores_pass.cpp
@@ -0,0 +1,237 @@
+// Copyright (c) 2022 The Khronos Group Inc.
+// Copyright (c) 2022 LunarG Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/opt/eliminate_dead_output_stores_pass.h"
+
+#include "source/opt/instruction.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace opt {
+namespace {
+constexpr uint32_t kDecorationLocationInIdx = 2;
+constexpr uint32_t kOpDecorateMemberMemberInIdx = 1;
+constexpr uint32_t kOpDecorateBuiltInLiteralInIdx = 2;
+constexpr uint32_t kOpDecorateMemberBuiltInLiteralInIdx = 3;
+constexpr uint32_t kOpAccessChainIdx0InIdx = 1;
+constexpr uint32_t kOpConstantValueInIdx = 0;
+}  // namespace
+
+Pass::Status EliminateDeadOutputStoresPass::Process() {
+  // Current functionality assumes shader capability
+  if (!context()->get_feature_mgr()->HasCapability(spv::Capability::Shader))
+    return Status::SuccessWithoutChange;
+  Pass::Status status = DoDeadOutputStoreElimination();
+  return status;
+}
+
+void EliminateDeadOutputStoresPass::InitializeElimination() {
+  kill_list_.clear();
+}
+
+bool EliminateDeadOutputStoresPass::IsLiveBuiltin(uint32_t bi) {
+  return live_builtins_->find(bi) != live_builtins_->end();
+}
+
+bool EliminateDeadOutputStoresPass::AnyLocsAreLive(uint32_t start,
+                                                   uint32_t count) {
+  auto finish = start + count;
+  for (uint32_t u = start; u < finish; ++u) {
+    if (live_locs_->find(u) != live_locs_->end()) return true;
+  }
+  return false;
+}
+
+void EliminateDeadOutputStoresPass::KillAllStoresOfRef(Instruction* ref) {
+  analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
+  if (ref->opcode() == spv::Op::OpStore) {
+    kill_list_.push_back(ref);
+    return;
+  }
+  assert((ref->opcode() == spv::Op::OpAccessChain ||
+          ref->opcode() == spv::Op::OpInBoundsAccessChain) &&
+         "unexpected use of output variable");
+  def_use_mgr->ForEachUser(ref, [this](Instruction* user) {
+    if (user->opcode() == spv::Op::OpStore) kill_list_.push_back(user);
+  });
+}
+
+void EliminateDeadOutputStoresPass::KillAllDeadStoresOfLocRef(
+    Instruction* ref, Instruction* var) {
+  analysis::TypeManager* type_mgr = context()->get_type_mgr();
+  analysis::DecorationManager* deco_mgr = context()->get_decoration_mgr();
+  analysis::LivenessManager* live_mgr = context()->get_liveness_mgr();
+  // Find variable location if present.
+  uint32_t start_loc = 0;
+  auto var_id = var->result_id();
+  bool no_loc = deco_mgr->WhileEachDecoration(
+      var_id, uint32_t(spv::Decoration::Location),
+      [&start_loc](const Instruction& deco) {
+        assert(deco.opcode() == spv::Op::OpDecorate && "unexpected decoration");
+        start_loc = deco.GetSingleWordInOperand(kDecorationLocationInIdx);
+        return false;
+      });
+  // Find patch decoration if present
+  bool is_patch = !deco_mgr->WhileEachDecoration(
+      var_id, uint32_t(spv::Decoration::Patch), [](const Instruction& deco) {
+        if (deco.opcode() != spv::Op::OpDecorate)
+          assert(false && "unexpected decoration");
+        return false;
+      });
+  // Compute offset and final type of reference. If no location found
+  // or any stored locations are live, return without removing stores.
+  auto ptr_type = type_mgr->GetType(var->type_id())->AsPointer();
+  assert(ptr_type && "unexpected var type");
+  auto var_type = ptr_type->pointee_type();
+  uint32_t ref_loc = start_loc;
+  auto curr_type = var_type;
+  if (ref->opcode() == spv::Op::OpAccessChain ||
+      ref->opcode() == spv::Op::OpInBoundsAccessChain) {
+    live_mgr->AnalyzeAccessChainLoc(ref, &curr_type, &ref_loc, &no_loc,
+                                    is_patch, /* input */ false);
+  }
+  if (no_loc || AnyLocsAreLive(ref_loc, live_mgr->GetLocSize(curr_type)))
+    return;
+  // Kill all stores based on this reference
+  KillAllStoresOfRef(ref);
+}
+
+void EliminateDeadOutputStoresPass::KillAllDeadStoresOfBuiltinRef(
+    Instruction* ref, Instruction* var) {
+  auto deco_mgr = context()->get_decoration_mgr();
+  auto def_use_mgr = context()->get_def_use_mgr();
+  auto type_mgr = context()->get_type_mgr();
+  auto live_mgr = context()->get_liveness_mgr();
+  // Search for builtin decoration of base variable
+  uint32_t builtin = uint32_t(spv::BuiltIn::Max);
+  auto var_id = var->result_id();
+  (void)deco_mgr->WhileEachDecoration(
+      var_id, uint32_t(spv::Decoration::BuiltIn),
+      [&builtin](const Instruction& deco) {
+        assert(deco.opcode() == spv::Op::OpDecorate && "unexpected decoration");
+        builtin = deco.GetSingleWordInOperand(kOpDecorateBuiltInLiteralInIdx);
+        return false;
+      });
+  // If analyzed builtin and not live, kill stores.
+  if (builtin != uint32_t(spv::BuiltIn::Max)) {
+    if (live_mgr->IsAnalyzedBuiltin(builtin) && !IsLiveBuiltin(builtin))
+      KillAllStoresOfRef(ref);
+    return;
+  }
+  // Search for builtin decoration on indexed member
+  auto ref_op = ref->opcode();
+  if (ref_op != spv::Op::OpAccessChain &&
+      ref_op != spv::Op::OpInBoundsAccessChain) {
+    return;
+  }
+  uint32_t in_idx = kOpAccessChainIdx0InIdx;
+  analysis::Type* var_type = type_mgr->GetType(var->type_id());
+  analysis::Pointer* ptr_type = var_type->AsPointer();
+  auto curr_type = ptr_type->pointee_type();
+  auto arr_type = curr_type->AsArray();
+  if (arr_type) {
+    curr_type = arr_type->element_type();
+    ++in_idx;
+  }
+  auto str_type = curr_type->AsStruct();
+  auto str_type_id = type_mgr->GetId(str_type);
+  auto member_idx_id = ref->GetSingleWordInOperand(in_idx);
+  auto member_idx_inst = def_use_mgr->GetDef(member_idx_id);
+  assert(member_idx_inst->opcode() == spv::Op::OpConstant &&
+         "unexpected non-constant index");
+  auto ac_idx = member_idx_inst->GetSingleWordInOperand(kOpConstantValueInIdx);
+  (void)deco_mgr->WhileEachDecoration(
+      str_type_id, uint32_t(spv::Decoration::BuiltIn),
+      [ac_idx, &builtin](const Instruction& deco) {
+        assert(deco.opcode() == spv::Op::OpMemberDecorate &&
+               "unexpected decoration");
+        auto deco_idx =
+            deco.GetSingleWordInOperand(kOpDecorateMemberMemberInIdx);
+        if (deco_idx == ac_idx) {
+          builtin =
+              deco.GetSingleWordInOperand(kOpDecorateMemberBuiltInLiteralInIdx);
+          return false;
+        }
+        return true;
+      });
+  assert(builtin != uint32_t(spv::BuiltIn::Max) && "builtin not found");
+  // If analyzed builtin and not live, kill stores.
+  if (live_mgr->IsAnalyzedBuiltin(builtin) && !IsLiveBuiltin(builtin))
+    KillAllStoresOfRef(ref);
+}
+
+Pass::Status EliminateDeadOutputStoresPass::DoDeadOutputStoreElimination() {
+  // Current implementation only supports vert, tesc, tese, geom shaders
+  auto stage = context()->GetStage();
+  if (stage != spv::ExecutionModel::Vertex &&
+      stage != spv::ExecutionModel::TessellationControl &&
+      stage != spv::ExecutionModel::TessellationEvaluation &&
+      stage != spv::ExecutionModel::Geometry)
+    return Status::Failure;
+  InitializeElimination();
+  analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
+  analysis::TypeManager* type_mgr = context()->get_type_mgr();
+  analysis::DecorationManager* deco_mgr = context()->get_decoration_mgr();
+  // Process all output variables
+  for (auto& var : context()->types_values()) {
+    if (var.opcode() != spv::Op::OpVariable) {
+      continue;
+    }
+    analysis::Type* var_type = type_mgr->GetType(var.type_id());
+    analysis::Pointer* ptr_type = var_type->AsPointer();
+    if (ptr_type->storage_class() != spv::StorageClass::Output) {
+      continue;
+    }
+    // If builtin decoration on variable, process as builtin.
+    auto var_id = var.result_id();
+    bool is_builtin = false;
+    if (deco_mgr->HasDecoration(var_id, uint32_t(spv::Decoration::BuiltIn))) {
+      is_builtin = true;
+    } else {
+      // If interface block with builtin members, process as builtin.
+      // Strip off outer array type if present.
+      auto curr_type = ptr_type->pointee_type();
+      auto arr_type = curr_type->AsArray();
+      if (arr_type) curr_type = arr_type->element_type();
+      auto str_type = curr_type->AsStruct();
+      if (str_type) {
+        auto str_type_id = type_mgr->GetId(str_type);
+        if (deco_mgr->HasDecoration(str_type_id,
+                                    uint32_t(spv::Decoration::BuiltIn)))
+          is_builtin = true;
+      }
+    }
+    // For each store or access chain using var, if dead builtin or all its
+    // locations are dead, kill store or all access chain's stores
+    def_use_mgr->ForEachUser(
+        var_id, [this, &var, is_builtin](Instruction* user) {
+          auto op = user->opcode();
+          if (op == spv::Op::OpEntryPoint || op == spv::Op::OpName ||
+              op == spv::Op::OpDecorate || user->IsNonSemanticInstruction())
+            return;
+          if (is_builtin)
+            KillAllDeadStoresOfBuiltinRef(user, &var);
+          else
+            KillAllDeadStoresOfLocRef(user, &var);
+        });
+  }
+  for (auto& kinst : kill_list_) context()->KillInst(kinst);
+
+  return kill_list_.empty() ? Status::SuccessWithoutChange
+                            : Status::SuccessWithChange;
+}
+
+}  // namespace opt
+}  // namespace spvtools
diff --git a/source/opt/eliminate_dead_output_stores_pass.h b/source/opt/eliminate_dead_output_stores_pass.h
new file mode 100644
index 0000000..676d4f4
--- /dev/null
+++ b/source/opt/eliminate_dead_output_stores_pass.h
@@ -0,0 +1,81 @@
+// Copyright (c) 2022 The Khronos Group Inc.
+// Copyright (c) 2022 LunarG Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_OPT_ELIMINATE_DEAD_OUTPUT_STORES_H_
+#define SOURCE_OPT_ELIMINATE_DEAD_OUTPUT_STORES_H_
+
+#include <unordered_set>
+
+#include "source/opt/ir_context.h"
+#include "source/opt/module.h"
+#include "source/opt/pass.h"
+
+namespace spvtools {
+namespace opt {
+
+// See optimizer.hpp for documentation.
+class EliminateDeadOutputStoresPass : public Pass {
+ public:
+  explicit EliminateDeadOutputStoresPass(
+      std::unordered_set<uint32_t>* live_locs,
+      std::unordered_set<uint32_t>* live_builtins)
+      : live_locs_(live_locs), live_builtins_(live_builtins) {}
+
+  const char* name() const override { return "eliminate-dead-output-stores"; }
+  Status Process() override;
+
+  // Return the mask of preserved Analyses.
+  IRContext::Analysis GetPreservedAnalyses() override {
+    return IRContext::kAnalysisDefUse |
+           IRContext::kAnalysisInstrToBlockMapping |
+           IRContext::kAnalysisCombinators | IRContext::kAnalysisCFG |
+           IRContext::kAnalysisDominatorAnalysis |
+           IRContext::kAnalysisLoopAnalysis | IRContext::kAnalysisNameMap |
+           IRContext::kAnalysisConstants | IRContext::kAnalysisTypes;
+  }
+
+ private:
+  // Initialize elimination
+  void InitializeElimination();
+
+  // Do dead output store analysis
+  Status DoDeadOutputStoreElimination();
+
+  // Kill all stores resulting from |ref|.
+  void KillAllStoresOfRef(Instruction* ref);
+
+  // Kill all dead stores resulting from |user| of loc-based |var|.
+  void KillAllDeadStoresOfLocRef(Instruction* user, Instruction* var);
+
+  // Kill all dead stores resulting from |user| of builtin |var|.
+  void KillAllDeadStoresOfBuiltinRef(Instruction* user, Instruction* var);
+
+  // Return true if any of |count| locations starting at location |start| are
+  // live.
+  bool AnyLocsAreLive(uint32_t start, uint32_t count);
+
+  // Return true if builtin |bi| is live.
+  bool IsLiveBuiltin(uint32_t bi);
+
+  std::unordered_set<uint32_t>* live_locs_;
+  std::unordered_set<uint32_t>* live_builtins_;
+
+  std::vector<Instruction*> kill_list_;
+};
+
+}  // namespace opt
+}  // namespace spvtools
+
+#endif  // SOURCE_OPT_ELIMINATE_DEAD_OUTPUT_STORES_H_
diff --git a/source/opt/feature_manager.cpp b/source/opt/feature_manager.cpp
index a590271..5188370 100644
--- a/source/opt/feature_manager.cpp
+++ b/source/opt/feature_manager.cpp
@@ -14,8 +14,6 @@
 
 #include "source/opt/feature_manager.h"
 
-#include <queue>
-#include <stack>
 #include <string>
 
 #include "source/enum_string_mapping.h"
@@ -36,42 +34,44 @@
 }
 
 void FeatureManager::AddExtension(Instruction* ext) {
-  assert(ext->opcode() == SpvOpExtension &&
+  assert(ext->opcode() == spv::Op::OpExtension &&
          "Expecting an extension instruction.");
 
   const std::string name = ext->GetInOperand(0u).AsString();
   Extension extension;
   if (GetExtensionFromString(name.c_str(), &extension)) {
-    extensions_.Add(extension);
+    extensions_.insert(extension);
   }
 }
 
 void FeatureManager::RemoveExtension(Extension ext) {
-  if (!extensions_.Contains(ext)) return;
-  extensions_.Remove(ext);
+  if (!extensions_.contains(ext)) return;
+  extensions_.erase(ext);
 }
 
-void FeatureManager::AddCapability(SpvCapability cap) {
-  if (capabilities_.Contains(cap)) return;
+void FeatureManager::AddCapability(spv::Capability cap) {
+  if (capabilities_.contains(cap)) return;
 
-  capabilities_.Add(cap);
+  capabilities_.insert(cap);
 
   spv_operand_desc desc = {};
-  if (SPV_SUCCESS ==
-      grammar_.lookupOperand(SPV_OPERAND_TYPE_CAPABILITY, cap, &desc)) {
-    CapabilitySet(desc->numCapabilities, desc->capabilities)
-        .ForEach([this](SpvCapability c) { AddCapability(c); });
+  if (SPV_SUCCESS == grammar_.lookupOperand(SPV_OPERAND_TYPE_CAPABILITY,
+                                            uint32_t(cap), &desc)) {
+    for (auto capability :
+         CapabilitySet(desc->numCapabilities, desc->capabilities)) {
+      AddCapability(capability);
+    }
   }
 }
 
-void FeatureManager::RemoveCapability(SpvCapability cap) {
-  if (!capabilities_.Contains(cap)) return;
-  capabilities_.Remove(cap);
+void FeatureManager::RemoveCapability(spv::Capability cap) {
+  if (!capabilities_.contains(cap)) return;
+  capabilities_.erase(cap);
 }
 
 void FeatureManager::AddCapabilities(Module* module) {
   for (Instruction& inst : module->capabilities()) {
-    AddCapability(static_cast<SpvCapability>(inst.GetSingleWordInOperand(0)));
+    AddCapability(static_cast<spv::Capability>(inst.GetSingleWordInOperand(0)));
   }
 }
 
diff --git a/source/opt/feature_manager.h b/source/opt/feature_manager.h
index 68c8e9a..d150a2f 100644
--- a/source/opt/feature_manager.h
+++ b/source/opt/feature_manager.h
@@ -25,27 +25,19 @@
 // Tracks features enabled by a module. The IRContext has a FeatureManager.
 class FeatureManager {
  public:
-  explicit FeatureManager(const AssemblyGrammar& grammar) : grammar_(grammar) {}
-
   // Returns true if |ext| is an enabled extension in the module.
-  bool HasExtension(Extension ext) const { return extensions_.Contains(ext); }
-
-  // Removes the given |extension| from the current FeatureManager.
-  void RemoveExtension(Extension extension);
+  bool HasExtension(Extension ext) const { return extensions_.contains(ext); }
 
   // Returns true if |cap| is an enabled capability in the module.
-  bool HasCapability(SpvCapability cap) const {
-    return capabilities_.Contains(cap);
+  bool HasCapability(spv::Capability cap) const {
+    return capabilities_.contains(cap);
   }
 
-  // Removes the given |capability| from the current FeatureManager.
-  void RemoveCapability(SpvCapability capability);
+  // Returns the capabilities the module declares.
+  inline const CapabilitySet& GetCapabilities() const { return capabilities_; }
 
-  // Analyzes |module| and records enabled extensions and capabilities.
-  void Analyze(Module* module);
-
-  CapabilitySet* GetCapabilities() { return &capabilities_; }
-  const CapabilitySet* GetCapabilities() const { return &capabilities_; }
+  // Returns the extensions the module imports.
+  inline const ExtensionSet& GetExtensions() const { return extensions_; }
 
   uint32_t GetExtInstImportId_GLSLstd450() const {
     return extinst_importid_GLSLstd450_;
@@ -64,23 +56,34 @@
     return !(a == b);
   }
 
-  // Adds the given |capability| and all implied capabilities into the current
-  // FeatureManager.
-  void AddCapability(SpvCapability capability);
+ private:
+  explicit FeatureManager(const AssemblyGrammar& grammar) : grammar_(grammar) {}
+
+  // Analyzes |module| and records enabled extensions and capabilities.
+  void Analyze(Module* module);
 
   // Add the extension |ext| to the feature manager.
   void AddExtension(Instruction* ext);
 
-  // Analyzes |module| and records imported external instruction sets.
-  void AddExtInstImportIds(Module* module);
-
- private:
   // Analyzes |module| and records enabled extensions.
   void AddExtensions(Module* module);
 
+  // Removes the given |extension| from the current FeatureManager.
+  void RemoveExtension(Extension extension);
+
+  // Adds the given |capability| and all implied capabilities into the current
+  // FeatureManager.
+  void AddCapability(spv::Capability capability);
+
   // Analyzes |module| and records enabled capabilities.
   void AddCapabilities(Module* module);
 
+  // Removes the given |capability| from the current FeatureManager.
+  void RemoveCapability(spv::Capability capability);
+
+  // Analyzes |module| and records imported external instruction sets.
+  void AddExtInstImportIds(Module* module);
+
   // Auxiliary object for querying SPIR-V grammar facts.
   const AssemblyGrammar& grammar_;
 
@@ -100,6 +103,8 @@
   // Common NonSemanticShader100DebugInfo external instruction import ids,
   // cached for performance.
   uint32_t extinst_importid_Shader100DebugInfo_ = 0;
+
+  friend class IRContext;
 };
 
 }  // namespace opt
diff --git a/source/opt/fix_func_call_arguments.cpp b/source/opt/fix_func_call_arguments.cpp
index d140fb4..f3486be 100644
--- a/source/opt/fix_func_call_arguments.cpp
+++ b/source/opt/fix_func_call_arguments.cpp
@@ -29,7 +29,7 @@
   if (ModuleHasASingleFunction()) return Status::SuccessWithoutChange;
   for (auto& func : *get_module()) {
     func.ForEachInst([this, &modified](Instruction* inst) {
-      if (inst->opcode() == SpvOpFunctionCall) {
+      if (inst->opcode() == spv::Op::OpFunctionCall) {
         modified |= FixFuncCallArguments(inst);
       }
     });
@@ -44,7 +44,7 @@
     Operand& op = func_call_inst->GetInOperand(i);
     if (op.type != SPV_OPERAND_TYPE_ID) continue;
     Instruction* operand_inst = get_def_use_mgr()->GetDef(op.AsId());
-    if (operand_inst->opcode() == SpvOpAccessChain) {
+    if (operand_inst->opcode() == spv::Op::OpAccessChain) {
       uint32_t var_id =
           ReplaceAccessChainFuncCallArguments(func_call_inst, operand_inst);
       func_call_inst->SetInOperand(i, {var_id});
@@ -71,10 +71,11 @@
   Instruction* op_type =
       get_def_use_mgr()->GetDef(op_ptr_type->GetSingleWordInOperand(1));
   uint32_t varType = context()->get_type_mgr()->FindPointerToType(
-      op_type->result_id(), SpvStorageClassFunction);
+      op_type->result_id(), spv::StorageClass::Function);
   // Create new variable
   builder.SetInsertPoint(variable_insertion_point);
-  Instruction* var = builder.AddVariable(varType, SpvStorageClassFunction);
+  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);
 
diff --git a/source/opt/fix_storage_class.cpp b/source/opt/fix_storage_class.cpp
index 04eb132..5597e82 100644
--- a/source/opt/fix_storage_class.cpp
+++ b/source/opt/fix_storage_class.cpp
@@ -26,7 +26,7 @@
   bool modified = false;
 
   get_module()->ForEachInst([this, &modified](Instruction* inst) {
-    if (inst->opcode() == SpvOpVariable) {
+    if (inst->opcode() == spv::Op::OpVariable) {
       std::set<uint32_t> seen;
       std::vector<std::pair<Instruction*, uint32_t>> uses;
       get_def_use_mgr()->ForEachUse(inst,
@@ -37,7 +37,7 @@
       for (auto& use : uses) {
         modified |= PropagateStorageClass(
             use.first,
-            static_cast<SpvStorageClass>(inst->GetSingleWordInOperand(0)),
+            static_cast<spv::StorageClass>(inst->GetSingleWordInOperand(0)),
             &seen);
         assert(seen.empty() && "Seen was not properly reset.");
         modified |=
@@ -50,14 +50,14 @@
 }
 
 bool FixStorageClass::PropagateStorageClass(Instruction* inst,
-                                            SpvStorageClass storage_class,
+                                            spv::StorageClass storage_class,
                                             std::set<uint32_t>* seen) {
   if (!IsPointerResultType(inst)) {
     return false;
   }
 
   if (IsPointerToStorageClass(inst, storage_class)) {
-    if (inst->opcode() == SpvOpPhi) {
+    if (inst->opcode() == spv::Op::OpPhi) {
       if (!seen->insert(inst->result_id()).second) {
         return false;
       }
@@ -71,34 +71,34 @@
       modified |= PropagateStorageClass(use, storage_class, seen);
     }
 
-    if (inst->opcode() == SpvOpPhi) {
+    if (inst->opcode() == spv::Op::OpPhi) {
       seen->erase(inst->result_id());
     }
     return modified;
   }
 
   switch (inst->opcode()) {
-    case SpvOpAccessChain:
-    case SpvOpPtrAccessChain:
-    case SpvOpInBoundsAccessChain:
-    case SpvOpCopyObject:
-    case SpvOpPhi:
-    case SpvOpSelect:
+    case spv::Op::OpAccessChain:
+    case spv::Op::OpPtrAccessChain:
+    case spv::Op::OpInBoundsAccessChain:
+    case spv::Op::OpCopyObject:
+    case spv::Op::OpPhi:
+    case spv::Op::OpSelect:
       FixInstructionStorageClass(inst, storage_class, seen);
       return true;
-    case SpvOpFunctionCall:
+    case spv::Op::OpFunctionCall:
       // We cannot be sure of the actual connection between the storage class
       // of the parameter and the storage class of the result, so we should not
       // do anything.  If the result type needs to be fixed, the function call
       // should be inlined.
       return false;
-    case SpvOpImageTexelPointer:
-    case SpvOpLoad:
-    case SpvOpStore:
-    case SpvOpCopyMemory:
-    case SpvOpCopyMemorySized:
-    case SpvOpVariable:
-    case SpvOpBitcast:
+    case spv::Op::OpImageTexelPointer:
+    case spv::Op::OpLoad:
+    case spv::Op::OpStore:
+    case spv::Op::OpCopyMemory:
+    case spv::Op::OpCopyMemorySized:
+    case spv::Op::OpVariable:
+    case spv::Op::OpBitcast:
       // Nothing to change for these opcode.  The result type is the same
       // regardless of the storage class of the operand.
       return false;
@@ -109,9 +109,9 @@
   }
 }
 
-void FixStorageClass::FixInstructionStorageClass(Instruction* inst,
-                                                 SpvStorageClass storage_class,
-                                                 std::set<uint32_t>* seen) {
+void FixStorageClass::FixInstructionStorageClass(
+    Instruction* inst, spv::StorageClass storage_class,
+    std::set<uint32_t>* seen) {
   assert(IsPointerResultType(inst) &&
          "The result type of the instruction must be a pointer.");
 
@@ -126,10 +126,10 @@
 }
 
 void FixStorageClass::ChangeResultStorageClass(
-    Instruction* inst, SpvStorageClass storage_class) const {
+    Instruction* inst, spv::StorageClass storage_class) const {
   analysis::TypeManager* type_mgr = context()->get_type_mgr();
   Instruction* result_type_inst = get_def_use_mgr()->GetDef(inst->type_id());
-  assert(result_type_inst->opcode() == SpvOpTypePointer);
+  assert(result_type_inst->opcode() == spv::Op::OpTypePointer);
   uint32_t pointee_type_id = result_type_inst->GetSingleWordInOperand(1);
   uint32_t new_result_type_id =
       type_mgr->FindPointerToType(pointee_type_id, storage_class);
@@ -147,7 +147,7 @@
 }
 
 bool FixStorageClass::IsPointerToStorageClass(Instruction* inst,
-                                              SpvStorageClass storage_class) {
+                                              spv::StorageClass storage_class) {
   analysis::TypeManager* type_mgr = context()->get_type_mgr();
   analysis::Type* pType = type_mgr->GetType(inst->type_id());
   const analysis::Pointer* result_type = pType->AsPointer();
@@ -180,39 +180,39 @@
   // particular type, then we want find that type.
   uint32_t new_type_id = 0;
   switch (inst->opcode()) {
-    case SpvOpAccessChain:
-    case SpvOpPtrAccessChain:
-    case SpvOpInBoundsAccessChain:
-    case SpvOpInBoundsPtrAccessChain:
+    case spv::Op::OpAccessChain:
+    case spv::Op::OpPtrAccessChain:
+    case spv::Op::OpInBoundsAccessChain:
+    case spv::Op::OpInBoundsPtrAccessChain:
       if (op_idx == 2) {
         new_type_id = WalkAccessChainType(inst, type_id);
       }
       break;
-    case SpvOpCopyObject:
+    case spv::Op::OpCopyObject:
       new_type_id = type_id;
       break;
-    case SpvOpPhi:
+    case spv::Op::OpPhi:
       if (seen->insert(inst->result_id()).second) {
         new_type_id = type_id;
       }
       break;
-    case SpvOpSelect:
+    case spv::Op::OpSelect:
       if (op_idx > 2) {
         new_type_id = type_id;
       }
       break;
-    case SpvOpFunctionCall:
+    case spv::Op::OpFunctionCall:
       // We cannot be sure of the actual connection between the type
       // of the parameter and the type of the result, so we should not
       // do anything.  If the result type needs to be fixed, the function call
       // should be inlined.
       return false;
-    case SpvOpLoad: {
+    case spv::Op::OpLoad: {
       Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
       new_type_id = type_inst->GetSingleWordInOperand(1);
       break;
     }
-    case SpvOpStore: {
+    case spv::Op::OpStore: {
       uint32_t obj_id = inst->GetSingleWordInOperand(1);
       Instruction* obj_inst = get_def_use_mgr()->GetDef(obj_id);
       uint32_t obj_type_id = obj_inst->type_id();
@@ -237,18 +237,18 @@
         context()->UpdateDefUse(inst);
       }
     } break;
-    case SpvOpCopyMemory:
-    case SpvOpCopyMemorySized:
+    case spv::Op::OpCopyMemory:
+    case spv::Op::OpCopyMemorySized:
       // TODO: May need to expand the copy as we do with the stores.
       break;
-    case SpvOpCompositeConstruct:
-    case SpvOpCompositeExtract:
-    case SpvOpCompositeInsert:
+    case spv::Op::OpCompositeConstruct:
+    case spv::Op::OpCompositeExtract:
+    case spv::Op::OpCompositeInsert:
       // TODO: DXC does not seem to generate code that will require changes to
       // these opcode.  The can be implemented when they come up.
       break;
-    case SpvOpImageTexelPointer:
-    case SpvOpBitcast:
+    case spv::Op::OpImageTexelPointer:
+    case spv::Op::OpBitcast:
       // Nothing to change for these opcode.  The result type is the same
       // regardless of the type of the operand.
       return false;
@@ -278,7 +278,7 @@
       PropagateType(use.first, new_type_id, use.second, seen);
     }
 
-    if (inst->opcode() == SpvOpPhi) {
+    if (inst->opcode() == spv::Op::OpPhi) {
       seen->erase(inst->result_id());
     }
   }
@@ -288,12 +288,12 @@
 uint32_t FixStorageClass::WalkAccessChainType(Instruction* inst, uint32_t id) {
   uint32_t start_idx = 0;
   switch (inst->opcode()) {
-    case SpvOpAccessChain:
-    case SpvOpInBoundsAccessChain:
+    case spv::Op::OpAccessChain:
+    case spv::Op::OpInBoundsAccessChain:
       start_idx = 1;
       break;
-    case SpvOpPtrAccessChain:
-    case SpvOpInBoundsPtrAccessChain:
+    case spv::Op::OpPtrAccessChain:
+    case spv::Op::OpInBoundsPtrAccessChain:
       start_idx = 2;
       break;
     default:
@@ -302,19 +302,19 @@
   }
 
   Instruction* orig_type_inst = get_def_use_mgr()->GetDef(id);
-  assert(orig_type_inst->opcode() == SpvOpTypePointer);
+  assert(orig_type_inst->opcode() == spv::Op::OpTypePointer);
   id = orig_type_inst->GetSingleWordInOperand(1);
 
   for (uint32_t i = start_idx; i < inst->NumInOperands(); ++i) {
     Instruction* type_inst = get_def_use_mgr()->GetDef(id);
     switch (type_inst->opcode()) {
-      case SpvOpTypeArray:
-      case SpvOpTypeRuntimeArray:
-      case SpvOpTypeMatrix:
-      case SpvOpTypeVector:
+      case spv::Op::OpTypeArray:
+      case spv::Op::OpTypeRuntimeArray:
+      case spv::Op::OpTypeMatrix:
+      case spv::Op::OpTypeVector:
         id = type_inst->GetSingleWordInOperand(0);
         break;
-      case SpvOpTypeStruct: {
+      case spv::Op::OpTypeStruct: {
         const analysis::Constant* index_const =
             context()->get_constant_mgr()->FindDeclaredConstant(
                 inst->GetSingleWordInOperand(i));
@@ -330,8 +330,8 @@
   }
 
   return context()->get_type_mgr()->FindPointerToType(
-      id,
-      static_cast<SpvStorageClass>(orig_type_inst->GetSingleWordInOperand(0)));
+      id, static_cast<spv::StorageClass>(
+              orig_type_inst->GetSingleWordInOperand(0)));
 }
 
 // namespace opt
diff --git a/source/opt/fix_storage_class.h b/source/opt/fix_storage_class.h
index e72e864..6c67acd 100644
--- a/source/opt/fix_storage_class.h
+++ b/source/opt/fix_storage_class.h
@@ -48,7 +48,7 @@
   // appropriate, and propagates the change to the users of |inst| as well.
   // Returns true of any changes were made.
   // |seen| is used to track OpPhi instructions that should not be processed.
-  bool PropagateStorageClass(Instruction* inst, SpvStorageClass storage_class,
+  bool PropagateStorageClass(Instruction* inst, spv::StorageClass storage_class,
                              std::set<uint32_t>* seen);
 
   // Changes the storage class of the result of |inst| to |storage_class|.
@@ -58,13 +58,13 @@
   // |seen| is used to track OpPhi instructions that should not be processed by
   // |PropagateStorageClass|
   void FixInstructionStorageClass(Instruction* inst,
-                                  SpvStorageClass storage_class,
+                                  spv::StorageClass storage_class,
                                   std::set<uint32_t>* seen);
 
   // Changes the storage class of the result of |inst| to |storage_class|.  The
   // result type of |inst| must be a pointer.
   void ChangeResultStorageClass(Instruction* inst,
-                                SpvStorageClass storage_class) const;
+                                spv::StorageClass storage_class) const;
 
   // Returns true if the result type of |inst| is a pointer.
   bool IsPointerResultType(Instruction* inst);
@@ -72,7 +72,7 @@
   // Returns true if the result of |inst| is a pointer to storage class
   // |storage_class|.
   bool IsPointerToStorageClass(Instruction* inst,
-                               SpvStorageClass storage_class);
+                               spv::StorageClass storage_class);
 
   // Change |inst| to match that operand |op_idx| now has type |type_id|, and
   // adjust any uses of |inst| accordingly. Returns true if the code changed.
diff --git a/source/opt/flatten_decoration_pass.cpp b/source/opt/flatten_decoration_pass.cpp
index f4de911..c878c09 100644
--- a/source/opt/flatten_decoration_pass.cpp
+++ b/source/opt/flatten_decoration_pass.cpp
@@ -49,16 +49,16 @@
   // Rely on unordered_map::operator[] to create its entries on first access.
   for (const auto& inst : annotations) {
     switch (inst.opcode()) {
-      case SpvOp::SpvOpDecorationGroup:
+      case spv::Op::OpDecorationGroup:
         group_ids.insert(inst.result_id());
         break;
-      case SpvOp::SpvOpGroupDecorate: {
+      case spv::Op::OpGroupDecorate: {
         Words& words = normal_uses[inst.GetSingleWordInOperand(0)];
         for (uint32_t i = 1; i < inst.NumInOperandWords(); i++) {
           words.push_back(inst.GetSingleWordInOperand(i));
         }
       } break;
-      case SpvOp::SpvOpGroupMemberDecorate: {
+      case spv::Op::OpGroupMemberDecorate: {
         Words& words = member_uses[inst.GetSingleWordInOperand(0)];
         for (uint32_t i = 1; i < inst.NumInOperandWords(); i++) {
           words.push_back(inst.GetSingleWordInOperand(i));
@@ -77,12 +77,12 @@
     // Should we replace this instruction?
     bool replace = false;
     switch (inst_iter->opcode()) {
-      case SpvOp::SpvOpDecorationGroup:
-      case SpvOp::SpvOpGroupDecorate:
-      case SpvOp::SpvOpGroupMemberDecorate:
+      case spv::Op::OpDecorationGroup:
+      case spv::Op::OpGroupDecorate:
+      case spv::Op::OpGroupMemberDecorate:
         replace = true;
         break;
-      case SpvOp::SpvOpDecorate: {
+      case spv::Op::OpDecorate: {
         // If this decoration targets a group, then replace it
         // by sets of normal and member decorations.
         const uint32_t group = inst_iter->GetSingleWordOperand(0);
@@ -115,7 +115,7 @@
             operands.insert(operands.end(), decoration_operands_iter,
                             inst_iter->end());
             std::unique_ptr<Instruction> new_inst(new Instruction(
-                context(), SpvOp::SpvOpMemberDecorate, 0, 0, operands));
+                context(), spv::Op::OpMemberDecorate, 0, 0, operands));
             inst_iter = inst_iter.InsertBefore(std::move(new_inst));
             ++inst_iter;
             replace = true;
@@ -146,7 +146,7 @@
   if (!group_ids.empty()) {
     for (auto debug_inst_iter = context()->debug2_begin();
          debug_inst_iter != context()->debug2_end();) {
-      if (debug_inst_iter->opcode() == SpvOp::SpvOpName) {
+      if (debug_inst_iter->opcode() == spv::Op::OpName) {
         const uint32_t target = debug_inst_iter->GetSingleWordOperand(0);
         if (group_ids.count(target)) {
           debug_inst_iter = debug_inst_iter.Erase();
diff --git a/source/opt/fold.cpp b/source/opt/fold.cpp
index 315741a..c2a97b6 100644
--- a/source/opt/fold.cpp
+++ b/source/opt/fold.cpp
@@ -21,7 +21,6 @@
 #include "source/opt/const_folding_rules.h"
 #include "source/opt/def_use_manager.h"
 #include "source/opt/folding_rules.h"
-#include "source/opt/ir_builder.h"
 #include "source/opt/ir_context.h"
 
 namespace spvtools {
@@ -42,23 +41,24 @@
 
 }  // namespace
 
-uint32_t InstructionFolder::UnaryOperate(SpvOp opcode, uint32_t operand) const {
+uint32_t InstructionFolder::UnaryOperate(spv::Op opcode,
+                                         uint32_t operand) const {
   switch (opcode) {
     // Arthimetics
-    case SpvOp::SpvOpSNegate: {
+    case spv::Op::OpSNegate: {
       int32_t s_operand = static_cast<int32_t>(operand);
       if (s_operand == std::numeric_limits<int32_t>::min()) {
         return s_operand;
       }
       return -s_operand;
     }
-    case SpvOp::SpvOpNot:
+    case spv::Op::OpNot:
       return ~operand;
-    case SpvOp::SpvOpLogicalNot:
+    case spv::Op::OpLogicalNot:
       return !static_cast<bool>(operand);
-    case SpvOp::SpvOpUConvert:
+    case spv::Op::OpUConvert:
       return operand;
-    case SpvOp::SpvOpSConvert:
+    case spv::Op::OpSConvert:
       return operand;
     default:
       assert(false &&
@@ -67,31 +67,31 @@
   }
 }
 
-uint32_t InstructionFolder::BinaryOperate(SpvOp opcode, uint32_t a,
+uint32_t InstructionFolder::BinaryOperate(spv::Op opcode, uint32_t a,
                                           uint32_t b) const {
   switch (opcode) {
     // Arthimetics
-    case SpvOp::SpvOpIAdd:
+    case spv::Op::OpIAdd:
       return a + b;
-    case SpvOp::SpvOpISub:
+    case spv::Op::OpISub:
       return a - b;
-    case SpvOp::SpvOpIMul:
+    case spv::Op::OpIMul:
       return a * b;
-    case SpvOp::SpvOpUDiv:
+    case spv::Op::OpUDiv:
       if (b != 0) {
         return a / b;
       } else {
         // Dividing by 0 is undefined, so we will just pick 0.
         return 0;
       }
-    case SpvOp::SpvOpSDiv:
+    case spv::Op::OpSDiv:
       if (b != 0u) {
         return (static_cast<int32_t>(a)) / (static_cast<int32_t>(b));
       } else {
         // Dividing by 0 is undefined, so we will just pick 0.
         return 0;
       }
-    case SpvOp::SpvOpSRem: {
+    case spv::Op::OpSRem: {
       // The sign of non-zero result comes from the first operand: a. This is
       // guaranteed by C++11 rules for integer division operator. The division
       // result is rounded toward zero, so the result of '%' has the sign of
@@ -103,10 +103,10 @@
         return 0;
       }
     }
-    case SpvOp::SpvOpSMod: {
+    case spv::Op::OpSMod: {
       // The sign of non-zero result comes from the second operand: b
       if (b != 0u) {
-        int32_t rem = BinaryOperate(SpvOp::SpvOpSRem, a, b);
+        int32_t rem = BinaryOperate(spv::Op::OpSRem, a, b);
         int32_t b_prim = static_cast<int32_t>(b);
         return (rem + b_prim) % b_prim;
       } else {
@@ -114,7 +114,7 @@
         return 0;
       }
     }
-    case SpvOp::SpvOpUMod:
+    case spv::Op::OpUMod:
       if (b != 0u) {
         return (a % b);
       } else {
@@ -123,7 +123,7 @@
       }
 
     // Shifting
-    case SpvOp::SpvOpShiftRightLogical:
+    case spv::Op::OpShiftRightLogical:
       if (b >= 32) {
         // This is undefined behaviour when |b| > 32.  Choose 0 for consistency.
         // When |b| == 32, doing the shift in C++ in undefined, but the result
@@ -131,7 +131,7 @@
         return 0;
       }
       return a >> b;
-    case SpvOp::SpvOpShiftRightArithmetic:
+    case spv::Op::OpShiftRightArithmetic:
       if (b > 32) {
         // This is undefined behaviour.  Choose 0 for consistency.
         return 0;
@@ -146,7 +146,7 @@
         }
       }
       return (static_cast<int32_t>(a)) >> b;
-    case SpvOp::SpvOpShiftLeftLogical:
+    case spv::Op::OpShiftLeftLogical:
       if (b >= 32) {
         // This is undefined behaviour when |b| > 32.  Choose 0 for consistency.
         // When |b| == 32, doing the shift in C++ in undefined, but the result
@@ -156,43 +156,43 @@
       return a << b;
 
     // Bitwise operations
-    case SpvOp::SpvOpBitwiseOr:
+    case spv::Op::OpBitwiseOr:
       return a | b;
-    case SpvOp::SpvOpBitwiseAnd:
+    case spv::Op::OpBitwiseAnd:
       return a & b;
-    case SpvOp::SpvOpBitwiseXor:
+    case spv::Op::OpBitwiseXor:
       return a ^ b;
 
     // Logical
-    case SpvOp::SpvOpLogicalEqual:
+    case spv::Op::OpLogicalEqual:
       return (static_cast<bool>(a)) == (static_cast<bool>(b));
-    case SpvOp::SpvOpLogicalNotEqual:
+    case spv::Op::OpLogicalNotEqual:
       return (static_cast<bool>(a)) != (static_cast<bool>(b));
-    case SpvOp::SpvOpLogicalOr:
+    case spv::Op::OpLogicalOr:
       return (static_cast<bool>(a)) || (static_cast<bool>(b));
-    case SpvOp::SpvOpLogicalAnd:
+    case spv::Op::OpLogicalAnd:
       return (static_cast<bool>(a)) && (static_cast<bool>(b));
 
     // Comparison
-    case SpvOp::SpvOpIEqual:
+    case spv::Op::OpIEqual:
       return a == b;
-    case SpvOp::SpvOpINotEqual:
+    case spv::Op::OpINotEqual:
       return a != b;
-    case SpvOp::SpvOpULessThan:
+    case spv::Op::OpULessThan:
       return a < b;
-    case SpvOp::SpvOpSLessThan:
+    case spv::Op::OpSLessThan:
       return (static_cast<int32_t>(a)) < (static_cast<int32_t>(b));
-    case SpvOp::SpvOpUGreaterThan:
+    case spv::Op::OpUGreaterThan:
       return a > b;
-    case SpvOp::SpvOpSGreaterThan:
+    case spv::Op::OpSGreaterThan:
       return (static_cast<int32_t>(a)) > (static_cast<int32_t>(b));
-    case SpvOp::SpvOpULessThanEqual:
+    case spv::Op::OpULessThanEqual:
       return a <= b;
-    case SpvOp::SpvOpSLessThanEqual:
+    case spv::Op::OpSLessThanEqual:
       return (static_cast<int32_t>(a)) <= (static_cast<int32_t>(b));
-    case SpvOp::SpvOpUGreaterThanEqual:
+    case spv::Op::OpUGreaterThanEqual:
       return a >= b;
-    case SpvOp::SpvOpSGreaterThanEqual:
+    case spv::Op::OpSGreaterThanEqual:
       return (static_cast<int32_t>(a)) >= (static_cast<int32_t>(b));
     default:
       assert(false &&
@@ -201,10 +201,10 @@
   }
 }
 
-uint32_t InstructionFolder::TernaryOperate(SpvOp opcode, uint32_t a, uint32_t b,
-                                           uint32_t c) const {
+uint32_t InstructionFolder::TernaryOperate(spv::Op opcode, uint32_t a,
+                                           uint32_t b, uint32_t c) const {
   switch (opcode) {
-    case SpvOp::SpvOpSelect:
+    case spv::Op::OpSelect:
       return (static_cast<bool>(a)) ? b : c;
     default:
       assert(false &&
@@ -214,7 +214,7 @@
 }
 
 uint32_t InstructionFolder::OperateWords(
-    SpvOp opcode, const std::vector<uint32_t>& operand_words) const {
+    spv::Op opcode, const std::vector<uint32_t>& operand_words) const {
   switch (operand_words.size()) {
     case 1:
       return UnaryOperate(opcode, operand_words.front());
@@ -233,7 +233,7 @@
   auto identity_map = [](uint32_t id) { return id; };
   Instruction* folded_inst = FoldInstructionToConstant(inst, identity_map);
   if (folded_inst != nullptr) {
-    inst->SetOpcode(SpvOpCopyObject);
+    inst->SetOpcode(spv::Op::OpCopyObject);
     inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {folded_inst->result_id()}}});
     return true;
   }
@@ -256,7 +256,7 @@
 // result in 32 bit word. Scalar constants with longer than 32-bit width are
 // not accepted in this function.
 uint32_t InstructionFolder::FoldScalars(
-    SpvOp opcode,
+    spv::Op opcode,
     const std::vector<const analysis::Constant*>& operands) const {
   assert(IsFoldableOpcode(opcode) &&
          "Unhandled instruction opcode in FoldScalars");
@@ -282,7 +282,7 @@
 bool InstructionFolder::FoldBinaryIntegerOpToConstant(
     Instruction* inst, const std::function<uint32_t(uint32_t)>& id_map,
     uint32_t* result) const {
-  SpvOp opcode = inst->opcode();
+  spv::Op opcode = inst->opcode();
   analysis::ConstantManager* const_manger = context_->get_constant_mgr();
 
   uint32_t ids[2];
@@ -300,7 +300,7 @@
 
   switch (opcode) {
     // Arthimetics
-    case SpvOp::SpvOpIMul:
+    case spv::Op::OpIMul:
       for (uint32_t i = 0; i < 2; i++) {
         if (constants[i] != nullptr && constants[i]->IsZero()) {
           *result = 0;
@@ -308,11 +308,11 @@
         }
       }
       break;
-    case SpvOp::SpvOpUDiv:
-    case SpvOp::SpvOpSDiv:
-    case SpvOp::SpvOpSRem:
-    case SpvOp::SpvOpSMod:
-    case SpvOp::SpvOpUMod:
+    case spv::Op::OpUDiv:
+    case spv::Op::OpSDiv:
+    case spv::Op::OpSRem:
+    case spv::Op::OpSMod:
+    case spv::Op::OpUMod:
       // This changes undefined behaviour (ie divide by 0) into a 0.
       for (uint32_t i = 0; i < 2; i++) {
         if (constants[i] != nullptr && constants[i]->IsZero()) {
@@ -323,8 +323,8 @@
       break;
 
     // Shifting
-    case SpvOp::SpvOpShiftRightLogical:
-    case SpvOp::SpvOpShiftLeftLogical:
+    case spv::Op::OpShiftRightLogical:
+    case spv::Op::OpShiftLeftLogical:
       if (constants[1] != nullptr) {
         // When shifting by a value larger than the size of the result, the
         // result is undefined.  We are setting the undefined behaviour to a
@@ -339,7 +339,7 @@
       break;
 
     // Bitwise operations
-    case SpvOp::SpvOpBitwiseOr:
+    case spv::Op::OpBitwiseOr:
       for (uint32_t i = 0; i < 2; i++) {
         if (constants[i] != nullptr) {
           // TODO: Change the mask against a value based on the bit width of the
@@ -353,7 +353,7 @@
         }
       }
       break;
-    case SpvOp::SpvOpBitwiseAnd:
+    case spv::Op::OpBitwiseAnd:
       for (uint32_t i = 0; i < 2; i++) {
         if (constants[i] != nullptr) {
           if (constants[i]->IsZero()) {
@@ -365,7 +365,7 @@
       break;
 
     // Comparison
-    case SpvOp::SpvOpULessThan:
+    case spv::Op::OpULessThan:
       if (constants[0] != nullptr &&
           constants[0]->GetU32BitValue() == UINT32_MAX) {
         *result = false;
@@ -376,7 +376,7 @@
         return true;
       }
       break;
-    case SpvOp::SpvOpSLessThan:
+    case spv::Op::OpSLessThan:
       if (constants[0] != nullptr &&
           constants[0]->GetS32BitValue() == INT32_MAX) {
         *result = false;
@@ -388,7 +388,7 @@
         return true;
       }
       break;
-    case SpvOp::SpvOpUGreaterThan:
+    case spv::Op::OpUGreaterThan:
       if (constants[0] != nullptr && constants[0]->IsZero()) {
         *result = false;
         return true;
@@ -399,7 +399,7 @@
         return true;
       }
       break;
-    case SpvOp::SpvOpSGreaterThan:
+    case spv::Op::OpSGreaterThan:
       if (constants[0] != nullptr &&
           constants[0]->GetS32BitValue() == INT32_MIN) {
         *result = false;
@@ -411,7 +411,7 @@
         return true;
       }
       break;
-    case SpvOp::SpvOpULessThanEqual:
+    case spv::Op::OpULessThanEqual:
       if (constants[0] != nullptr && constants[0]->IsZero()) {
         *result = true;
         return true;
@@ -422,7 +422,7 @@
         return true;
       }
       break;
-    case SpvOp::SpvOpSLessThanEqual:
+    case spv::Op::OpSLessThanEqual:
       if (constants[0] != nullptr &&
           constants[0]->GetS32BitValue() == INT32_MIN) {
         *result = true;
@@ -434,7 +434,7 @@
         return true;
       }
       break;
-    case SpvOp::SpvOpUGreaterThanEqual:
+    case spv::Op::OpUGreaterThanEqual:
       if (constants[0] != nullptr &&
           constants[0]->GetU32BitValue() == UINT32_MAX) {
         *result = true;
@@ -445,7 +445,7 @@
         return true;
       }
       break;
-    case SpvOp::SpvOpSGreaterThanEqual:
+    case spv::Op::OpSGreaterThanEqual:
       if (constants[0] != nullptr &&
           constants[0]->GetS32BitValue() == INT32_MAX) {
         *result = true;
@@ -466,7 +466,7 @@
 bool InstructionFolder::FoldBinaryBooleanOpToConstant(
     Instruction* inst, const std::function<uint32_t(uint32_t)>& id_map,
     uint32_t* result) const {
-  SpvOp opcode = inst->opcode();
+  spv::Op opcode = inst->opcode();
   analysis::ConstantManager* const_manger = context_->get_constant_mgr();
 
   uint32_t ids[2];
@@ -484,7 +484,7 @@
 
   switch (opcode) {
     // Logical
-    case SpvOp::SpvOpLogicalOr:
+    case spv::Op::OpLogicalOr:
       for (uint32_t i = 0; i < 2; i++) {
         if (constants[i] != nullptr) {
           if (constants[i]->value()) {
@@ -494,7 +494,7 @@
         }
       }
       break;
-    case SpvOp::SpvOpLogicalAnd:
+    case spv::Op::OpLogicalAnd:
       for (uint32_t i = 0; i < 2; i++) {
         if (constants[i] != nullptr) {
           if (!constants[i]->value()) {
@@ -526,7 +526,7 @@
 }
 
 std::vector<uint32_t> InstructionFolder::FoldVectors(
-    SpvOp opcode, uint32_t num_dims,
+    spv::Op opcode, uint32_t num_dims,
     const std::vector<const analysis::Constant*>& operands) const {
   assert(IsFoldableOpcode(opcode) &&
          "Unhandled instruction opcode in FoldVectors");
@@ -570,44 +570,44 @@
   return result;
 }
 
-bool InstructionFolder::IsFoldableOpcode(SpvOp opcode) const {
+bool InstructionFolder::IsFoldableOpcode(spv::Op opcode) const {
   // NOTE: Extend to more opcodes as new cases are handled in the folder
   // functions.
   switch (opcode) {
-    case SpvOp::SpvOpBitwiseAnd:
-    case SpvOp::SpvOpBitwiseOr:
-    case SpvOp::SpvOpBitwiseXor:
-    case SpvOp::SpvOpIAdd:
-    case SpvOp::SpvOpIEqual:
-    case SpvOp::SpvOpIMul:
-    case SpvOp::SpvOpINotEqual:
-    case SpvOp::SpvOpISub:
-    case SpvOp::SpvOpLogicalAnd:
-    case SpvOp::SpvOpLogicalEqual:
-    case SpvOp::SpvOpLogicalNot:
-    case SpvOp::SpvOpLogicalNotEqual:
-    case SpvOp::SpvOpLogicalOr:
-    case SpvOp::SpvOpNot:
-    case SpvOp::SpvOpSDiv:
-    case SpvOp::SpvOpSelect:
-    case SpvOp::SpvOpSGreaterThan:
-    case SpvOp::SpvOpSGreaterThanEqual:
-    case SpvOp::SpvOpShiftLeftLogical:
-    case SpvOp::SpvOpShiftRightArithmetic:
-    case SpvOp::SpvOpShiftRightLogical:
-    case SpvOp::SpvOpSLessThan:
-    case SpvOp::SpvOpSLessThanEqual:
-    case SpvOp::SpvOpSMod:
-    case SpvOp::SpvOpSNegate:
-    case SpvOp::SpvOpSRem:
-    case SpvOp::SpvOpSConvert:
-    case SpvOp::SpvOpUConvert:
-    case SpvOp::SpvOpUDiv:
-    case SpvOp::SpvOpUGreaterThan:
-    case SpvOp::SpvOpUGreaterThanEqual:
-    case SpvOp::SpvOpULessThan:
-    case SpvOp::SpvOpULessThanEqual:
-    case SpvOp::SpvOpUMod:
+    case spv::Op::OpBitwiseAnd:
+    case spv::Op::OpBitwiseOr:
+    case spv::Op::OpBitwiseXor:
+    case spv::Op::OpIAdd:
+    case spv::Op::OpIEqual:
+    case spv::Op::OpIMul:
+    case spv::Op::OpINotEqual:
+    case spv::Op::OpISub:
+    case spv::Op::OpLogicalAnd:
+    case spv::Op::OpLogicalEqual:
+    case spv::Op::OpLogicalNot:
+    case spv::Op::OpLogicalNotEqual:
+    case spv::Op::OpLogicalOr:
+    case spv::Op::OpNot:
+    case spv::Op::OpSDiv:
+    case spv::Op::OpSelect:
+    case spv::Op::OpSGreaterThan:
+    case spv::Op::OpSGreaterThanEqual:
+    case spv::Op::OpShiftLeftLogical:
+    case spv::Op::OpShiftRightArithmetic:
+    case spv::Op::OpShiftRightLogical:
+    case spv::Op::OpSLessThan:
+    case spv::Op::OpSLessThanEqual:
+    case spv::Op::OpSMod:
+    case spv::Op::OpSNegate:
+    case spv::Op::OpSRem:
+    case spv::Op::OpSConvert:
+    case spv::Op::OpUConvert:
+    case spv::Op::OpUDiv:
+    case spv::Op::OpUGreaterThan:
+    case spv::Op::OpUGreaterThanEqual:
+    case spv::Op::OpULessThan:
+    case spv::Op::OpULessThanEqual:
+    case spv::Op::OpUMod:
       return true;
     default:
       return false;
@@ -627,7 +627,8 @@
     Instruction* inst, std::function<uint32_t(uint32_t)> id_map) const {
   analysis::ConstantManager* const_mgr = context_->get_constant_mgr();
 
-  if (!inst->IsFoldableByFoldScalar() && !HasConstFoldingRule(inst)) {
+  if (!inst->IsFoldableByFoldScalar() && !inst->IsFoldableByFoldVector() &&
+      !GetConstantFoldingRules().HasFoldingRule(inst)) {
     return nullptr;
   }
   // Collect the values of the constant parameters.
@@ -661,45 +662,87 @@
     }
   }
 
-  uint32_t result_val = 0;
   bool successful = false;
+
   // If all parameters are constant, fold the instruction to a constant.
-  if (!missing_constants && inst->IsFoldableByFoldScalar()) {
-    result_val = FoldScalars(inst->opcode(), constants);
-    successful = true;
+  if (inst->IsFoldableByFoldScalar()) {
+    uint32_t result_val = 0;
+
+    if (!missing_constants) {
+      result_val = FoldScalars(inst->opcode(), constants);
+      successful = true;
+    }
+
+    if (!successful) {
+      successful = FoldIntegerOpToConstant(inst, id_map, &result_val);
+    }
+
+    if (successful) {
+      const analysis::Constant* result_const =
+          const_mgr->GetConstant(const_mgr->GetType(inst), {result_val});
+      Instruction* folded_inst =
+          const_mgr->GetDefiningInstruction(result_const, inst->type_id());
+      return folded_inst;
+    }
+  } else if (inst->IsFoldableByFoldVector()) {
+    std::vector<uint32_t> result_val;
+
+    if (!missing_constants) {
+      if (Instruction* inst_type =
+              context_->get_def_use_mgr()->GetDef(inst->type_id())) {
+        result_val = FoldVectors(
+            inst->opcode(), inst_type->GetSingleWordInOperand(1), constants);
+        successful = true;
+      }
+    }
+
+    if (successful) {
+      const analysis::Constant* result_const =
+          const_mgr->GetNumericVectorConstantWithWords(
+              const_mgr->GetType(inst)->AsVector(), result_val);
+      Instruction* folded_inst =
+          const_mgr->GetDefiningInstruction(result_const, inst->type_id());
+      return folded_inst;
+    }
   }
 
-  if (!successful && inst->IsFoldableByFoldScalar()) {
-    successful = FoldIntegerOpToConstant(inst, id_map, &result_val);
-  }
-
-  if (successful) {
-    const analysis::Constant* result_const =
-        const_mgr->GetConstant(const_mgr->GetType(inst), {result_val});
-    Instruction* folded_inst =
-        const_mgr->GetDefiningInstruction(result_const, inst->type_id());
-    return folded_inst;
-  }
   return nullptr;
 }
 
 bool InstructionFolder::IsFoldableType(Instruction* type_inst) const {
+  return IsFoldableScalarType(type_inst) || IsFoldableVectorType(type_inst);
+}
+
+bool InstructionFolder::IsFoldableScalarType(Instruction* type_inst) const {
   // Support 32-bit integers.
-  if (type_inst->opcode() == SpvOpTypeInt) {
+  if (type_inst->opcode() == spv::Op::OpTypeInt) {
     return type_inst->GetSingleWordInOperand(0) == 32;
   }
   // Support booleans.
-  if (type_inst->opcode() == SpvOpTypeBool) {
+  if (type_inst->opcode() == spv::Op::OpTypeBool) {
     return true;
   }
   // Nothing else yet.
   return false;
 }
 
+bool InstructionFolder::IsFoldableVectorType(Instruction* type_inst) const {
+  // Support vectors with foldable components
+  if (type_inst->opcode() == spv::Op::OpTypeVector) {
+    uint32_t component_type_id = type_inst->GetSingleWordInOperand(0);
+    Instruction* def_component_type =
+        context_->get_def_use_mgr()->GetDef(component_type_id);
+    return def_component_type != nullptr &&
+           IsFoldableScalarType(def_component_type);
+  }
+  // Nothing else yet.
+  return false;
+}
+
 bool InstructionFolder::FoldInstruction(Instruction* inst) const {
   bool modified = false;
   Instruction* folded_inst(inst);
-  while (folded_inst->opcode() != SpvOpCopyObject &&
+  while (folded_inst->opcode() != spv::Op::OpCopyObject &&
          FoldInstructionInternal(&*folded_inst)) {
     modified = true;
   }
diff --git a/source/opt/fold.h b/source/opt/fold.h
index 9e7c470..42da65e 100644
--- a/source/opt/fold.h
+++ b/source/opt/fold.h
@@ -55,7 +55,7 @@
   // IsFoldableOpcode test. If any error occurs during folding, the folder will
   // fail with a call to assert.
   uint32_t FoldScalars(
-      SpvOp opcode,
+      spv::Op opcode,
       const std::vector<const analysis::Constant*>& operands) const;
 
   // Returns the result of performing an operation with the given |opcode| over
@@ -72,12 +72,12 @@
   // IsFoldableOpcode test. If any error occurs during folding, the folder will
   // fail with a call to assert.
   std::vector<uint32_t> FoldVectors(
-      SpvOp opcode, uint32_t num_dims,
+      spv::Op opcode, uint32_t num_dims,
       const std::vector<const analysis::Constant*>& operands) const;
 
   // Returns true if |opcode| represents an operation handled by FoldScalars or
   // FoldVectors.
-  bool IsFoldableOpcode(SpvOp opcode) const;
+  bool IsFoldableOpcode(spv::Op opcode) const;
 
   // Returns true if |cst| is supported by FoldScalars and FoldVectors.
   bool IsFoldableConstant(const analysis::Constant* cst) const;
@@ -86,6 +86,14 @@
   // result type is |type_inst|.
   bool IsFoldableType(Instruction* type_inst) const;
 
+  // Returns true if |FoldInstructionToConstant| could fold an instruction whose
+  // result type is |type_inst|.
+  bool IsFoldableScalarType(Instruction* type_inst) const;
+
+  // Returns true if |FoldInstructionToConstant| could fold an instruction whose
+  // result type is |type_inst|.
+  bool IsFoldableVectorType(Instruction* type_inst) const;
+
   // Tries to fold |inst| to a single constant, when the input ids to |inst|
   // have been substituted using |id_map|.  Returns a pointer to the OpConstant*
   // instruction if successful.  If necessary, a new constant instruction is
@@ -126,22 +134,22 @@
 
   // Returns the single-word result from performing the given unary operation on
   // the operand value which is passed in as a 32-bit word.
-  uint32_t UnaryOperate(SpvOp opcode, uint32_t operand) const;
+  uint32_t UnaryOperate(spv::Op opcode, uint32_t operand) const;
 
   // Returns the single-word result from performing the given binary operation
   // on the operand values which are passed in as two 32-bit word.
-  uint32_t BinaryOperate(SpvOp opcode, uint32_t a, uint32_t b) const;
+  uint32_t BinaryOperate(spv::Op opcode, uint32_t a, uint32_t b) const;
 
   // Returns the single-word result from performing the given ternary operation
   // on the operand values which are passed in as three 32-bit word.
-  uint32_t TernaryOperate(SpvOp opcode, uint32_t a, uint32_t b,
+  uint32_t TernaryOperate(spv::Op opcode, uint32_t a, uint32_t b,
                           uint32_t c) const;
 
   // Returns the single-word result from performing the given operation on the
   // operand words. This only works with 32-bit operations and uses boolean
   // convention that 0u is false, and anything else is boolean true.
   // TODO(qining): Support operands other than 32-bit wide.
-  uint32_t OperateWords(SpvOp opcode,
+  uint32_t OperateWords(spv::Op opcode,
                         const std::vector<uint32_t>& operand_words) const;
 
   bool FoldInstructionInternal(Instruction* inst) const;
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 7a51870..f6d6155 100644
--- a/source/opt/fold_spec_constant_op_and_composite_pass.cpp
+++ b/source/opt/fold_spec_constant_op_and_composite_pass.cpp
@@ -15,12 +15,9 @@
 #include "source/opt/fold_spec_constant_op_and_composite_pass.h"
 
 #include <algorithm>
-#include <initializer_list>
 #include <tuple>
 
 #include "source/opt/constants.h"
-#include "source/opt/fold.h"
-#include "source/opt/ir_context.h"
 #include "source/util/make_unique.h"
 
 namespace spvtools {
@@ -66,14 +63,14 @@
     if (const_mgr->GetType(inst) &&
         !const_mgr->GetType(inst)->decoration_empty())
       continue;
-    switch (SpvOp opcode = inst->opcode()) {
+    switch (spv::Op opcode = inst->opcode()) {
       // Records the values of Normal Constants.
-      case SpvOp::SpvOpConstantTrue:
-      case SpvOp::SpvOpConstantFalse:
-      case SpvOp::SpvOpConstant:
-      case SpvOp::SpvOpConstantNull:
-      case SpvOp::SpvOpConstantComposite:
-      case SpvOp::SpvOpSpecConstantComposite: {
+      case spv::Op::OpConstantTrue:
+      case spv::Op::OpConstantFalse:
+      case spv::Op::OpConstant:
+      case spv::Op::OpConstantNull:
+      case spv::Op::OpConstantComposite:
+      case spv::Op::OpSpecConstantComposite: {
         // 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
@@ -84,8 +81,8 @@
         if (auto const_value = const_mgr->GetConstantFromInst(inst)) {
           // Need to replace the OpSpecConstantComposite instruction with a
           // corresponding OpConstantComposite instruction.
-          if (opcode == SpvOp::SpvOpSpecConstantComposite) {
-            inst->SetOpcode(SpvOp::SpvOpConstantComposite);
+          if (opcode == spv::Op::OpSpecConstantComposite) {
+            inst->SetOpcode(spv::Op::OpConstantComposite);
             modified = true;
           }
           const_mgr->MapConstantToInst(const_value, inst);
@@ -99,7 +96,7 @@
       // 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 SpvOp::SpvOpSpecConstantOp:
+      case spv::Op::OpSpecConstantOp:
         modified |= ProcessOpSpecConstantOp(&inst_iter);
         break;
       default:
@@ -118,11 +115,11 @@
          "The first in-operand of OpSpecConstantOp instruction must be of "
          "SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER type");
 
-  switch (static_cast<SpvOp>(inst->GetSingleWordInOperand(0))) {
-    case SpvOp::SpvOpCompositeExtract:
-    case SpvOp::SpvOpVectorShuffle:
-    case SpvOp::SpvOpCompositeInsert:
-    case SpvOp::SpvOpQuantizeToF16:
+  switch (static_cast<spv::Op>(inst->GetSingleWordInOperand(0))) {
+    case spv::Op::OpCompositeExtract:
+    case spv::Op::OpVectorShuffle:
+    case spv::Op::OpCompositeInsert:
+    case spv::Op::OpQuantizeToF16:
       folded_inst = FoldWithInstructionFolder(pos);
       break;
     default:
@@ -165,7 +162,7 @@
   // instruction and pass it to the instruction folder.
   std::unique_ptr<Instruction> inst((*inst_iter_ptr)->Clone(context()));
   inst->SetOpcode(
-      static_cast<SpvOp>((*inst_iter_ptr)->GetSingleWordInOperand(0)));
+      static_cast<spv::Op>((*inst_iter_ptr)->GetSingleWordInOperand(0)));
   inst->RemoveOperand(2);
 
   // We want the current instruction to be replaced by an |OpConstant*|
@@ -289,7 +286,7 @@
   const Instruction* inst = &**pos;
   analysis::ConstantManager* const_mgr = context()->get_constant_mgr();
   const analysis::Type* result_type = const_mgr->GetType(inst);
-  SpvOp spec_opcode = static_cast<SpvOp>(inst->GetSingleWordInOperand(0));
+  spv::Op spec_opcode = static_cast<spv::Op>(inst->GetSingleWordInOperand(0));
   // Check and collect operands.
   std::vector<const analysis::Constant*> operands;
 
diff --git a/source/opt/folding_rules.cpp b/source/opt/folding_rules.cpp
index 3d803ad..293236d 100644
--- a/source/opt/folding_rules.cpp
+++ b/source/opt/folding_rules.cpp
@@ -14,7 +14,6 @@
 
 #include "source/opt/folding_rules.h"
 
-#include <climits>
 #include <limits>
 #include <memory>
 #include <utility>
@@ -27,15 +26,15 @@
 namespace opt {
 namespace {
 
-const uint32_t kExtractCompositeIdInIdx = 0;
-const uint32_t kInsertObjectIdInIdx = 0;
-const uint32_t kInsertCompositeIdInIdx = 1;
-const uint32_t kExtInstSetIdInIdx = 0;
-const uint32_t kExtInstInstructionInIdx = 1;
-const uint32_t kFMixXIdInIdx = 2;
-const uint32_t kFMixYIdInIdx = 3;
-const uint32_t kFMixAIdInIdx = 4;
-const uint32_t kStoreObjectInIdx = 1;
+constexpr uint32_t kExtractCompositeIdInIdx = 0;
+constexpr uint32_t kInsertObjectIdInIdx = 0;
+constexpr uint32_t kInsertCompositeIdInIdx = 1;
+constexpr uint32_t kExtInstSetIdInIdx = 0;
+constexpr uint32_t kExtInstInstructionInIdx = 1;
+constexpr uint32_t kFMixXIdInIdx = 2;
+constexpr uint32_t kFMixYIdInIdx = 3;
+constexpr uint32_t kFMixAIdInIdx = 4;
+constexpr uint32_t kStoreObjectInIdx = 1;
 
 // Some image instructions may contain an "image operands" argument.
 // Returns the operand index for the "image operands".
@@ -43,33 +42,33 @@
 int32_t ImageOperandsMaskInOperandIndex(Instruction* inst) {
   const auto opcode = inst->opcode();
   switch (opcode) {
-    case SpvOpImageSampleImplicitLod:
-    case SpvOpImageSampleExplicitLod:
-    case SpvOpImageSampleProjImplicitLod:
-    case SpvOpImageSampleProjExplicitLod:
-    case SpvOpImageFetch:
-    case SpvOpImageRead:
-    case SpvOpImageSparseSampleImplicitLod:
-    case SpvOpImageSparseSampleExplicitLod:
-    case SpvOpImageSparseSampleProjImplicitLod:
-    case SpvOpImageSparseSampleProjExplicitLod:
-    case SpvOpImageSparseFetch:
-    case SpvOpImageSparseRead:
+    case spv::Op::OpImageSampleImplicitLod:
+    case spv::Op::OpImageSampleExplicitLod:
+    case spv::Op::OpImageSampleProjImplicitLod:
+    case spv::Op::OpImageSampleProjExplicitLod:
+    case spv::Op::OpImageFetch:
+    case spv::Op::OpImageRead:
+    case spv::Op::OpImageSparseSampleImplicitLod:
+    case spv::Op::OpImageSparseSampleExplicitLod:
+    case spv::Op::OpImageSparseSampleProjImplicitLod:
+    case spv::Op::OpImageSparseSampleProjExplicitLod:
+    case spv::Op::OpImageSparseFetch:
+    case spv::Op::OpImageSparseRead:
       return inst->NumOperands() > 4 ? 2 : -1;
-    case SpvOpImageSampleDrefImplicitLod:
-    case SpvOpImageSampleDrefExplicitLod:
-    case SpvOpImageSampleProjDrefImplicitLod:
-    case SpvOpImageSampleProjDrefExplicitLod:
-    case SpvOpImageGather:
-    case SpvOpImageDrefGather:
-    case SpvOpImageSparseSampleDrefImplicitLod:
-    case SpvOpImageSparseSampleDrefExplicitLod:
-    case SpvOpImageSparseSampleProjDrefImplicitLod:
-    case SpvOpImageSparseSampleProjDrefExplicitLod:
-    case SpvOpImageSparseGather:
-    case SpvOpImageSparseDrefGather:
+    case spv::Op::OpImageSampleDrefImplicitLod:
+    case spv::Op::OpImageSampleDrefExplicitLod:
+    case spv::Op::OpImageSampleProjDrefImplicitLod:
+    case spv::Op::OpImageSampleProjDrefExplicitLod:
+    case spv::Op::OpImageGather:
+    case spv::Op::OpImageDrefGather:
+    case spv::Op::OpImageSparseSampleDrefImplicitLod:
+    case spv::Op::OpImageSparseSampleDrefExplicitLod:
+    case spv::Op::OpImageSparseSampleProjDrefImplicitLod:
+    case spv::Op::OpImageSparseSampleProjDrefExplicitLod:
+    case spv::Op::OpImageSparseGather:
+    case spv::Op::OpImageSparseDrefGather:
       return inst->NumOperands() > 5 ? 3 : -1;
-    case SpvOpImageWrite:
+    case spv::Op::OpImageWrite:
       return inst->NumOperands() > 3 ? 3 : -1;
     default:
       return -1;
@@ -304,7 +303,7 @@
 FoldingRule ReciprocalFDiv() {
   return [](IRContext* context, Instruction* inst,
             const std::vector<const analysis::Constant*>& constants) {
-    assert(inst->opcode() == SpvOpFDiv);
+    assert(inst->opcode() == spv::Op::OpFDiv);
     analysis::ConstantManager* const_mgr = context->get_constant_mgr();
     const analysis::Type* type =
         context->get_type_mgr()->GetType(inst->type_id());
@@ -333,7 +332,7 @@
         // Don't fold a null constant.
         return false;
       }
-      inst->SetOpcode(SpvOpFMul);
+      inst->SetOpcode(spv::Op::OpFMul);
       inst->SetInOperands(
           {{SPV_OPERAND_TYPE_ID, {inst->GetSingleWordInOperand(0u)}},
            {SPV_OPERAND_TYPE_ID, {id}}});
@@ -348,7 +347,8 @@
 FoldingRule MergeNegateArithmetic() {
   return [](IRContext* context, Instruction* inst,
             const std::vector<const analysis::Constant*>& constants) {
-    assert(inst->opcode() == SpvOpFNegate || inst->opcode() == SpvOpSNegate);
+    assert(inst->opcode() == spv::Op::OpFNegate ||
+           inst->opcode() == spv::Op::OpSNegate);
     (void)constants;
     const analysis::Type* type =
         context->get_type_mgr()->GetType(inst->type_id());
@@ -362,7 +362,7 @@
 
     if (op_inst->opcode() == inst->opcode()) {
       // Elide negates.
-      inst->SetOpcode(SpvOpCopyObject);
+      inst->SetOpcode(spv::Op::OpCopyObject);
       inst->SetInOperands(
           {{SPV_OPERAND_TYPE_ID, {op_inst->GetSingleWordInOperand(0u)}}});
       return true;
@@ -382,7 +382,8 @@
 FoldingRule MergeNegateMulDivArithmetic() {
   return [](IRContext* context, Instruction* inst,
             const std::vector<const analysis::Constant*>& constants) {
-    assert(inst->opcode() == SpvOpFNegate || inst->opcode() == SpvOpSNegate);
+    assert(inst->opcode() == spv::Op::OpFNegate ||
+           inst->opcode() == spv::Op::OpSNegate);
     (void)constants;
     analysis::ConstantManager* const_mgr = context->get_constant_mgr();
     const analysis::Type* type =
@@ -398,9 +399,10 @@
     uint32_t width = ElementWidth(type);
     if (width != 32 && width != 64) return false;
 
-    SpvOp opcode = op_inst->opcode();
-    if (opcode == SpvOpFMul || opcode == SpvOpFDiv || opcode == SpvOpIMul ||
-        opcode == SpvOpSDiv || opcode == SpvOpUDiv) {
+    spv::Op opcode = op_inst->opcode();
+    if (opcode == spv::Op::OpFMul || opcode == spv::Op::OpFDiv ||
+        opcode == spv::Op::OpIMul || opcode == spv::Op::OpSDiv ||
+        opcode == spv::Op::OpUDiv) {
       std::vector<const analysis::Constant*> op_constants =
           const_mgr->GetOperandConstants(op_inst);
       // Merge negate into mul or div if one operand is constant.
@@ -413,7 +415,8 @@
                                     : op_inst->GetSingleWordInOperand(1u);
         // Change this instruction to a mul/div.
         inst->SetOpcode(op_inst->opcode());
-        if (opcode == SpvOpFDiv || opcode == SpvOpUDiv || opcode == SpvOpSDiv) {
+        if (opcode == spv::Op::OpFDiv || opcode == spv::Op::OpUDiv ||
+            opcode == spv::Op::OpSDiv) {
           uint32_t op0 = zero_is_variable ? non_const_id : neg_id;
           uint32_t op1 = zero_is_variable ? neg_id : non_const_id;
           inst->SetInOperands(
@@ -440,7 +443,8 @@
 FoldingRule MergeNegateAddSubArithmetic() {
   return [](IRContext* context, Instruction* inst,
             const std::vector<const analysis::Constant*>& constants) {
-    assert(inst->opcode() == SpvOpFNegate || inst->opcode() == SpvOpSNegate);
+    assert(inst->opcode() == spv::Op::OpFNegate ||
+           inst->opcode() == spv::Op::OpSNegate);
     (void)constants;
     analysis::ConstantManager* const_mgr = context->get_constant_mgr();
     const analysis::Type* type =
@@ -456,14 +460,16 @@
     uint32_t width = ElementWidth(type);
     if (width != 32 && width != 64) return false;
 
-    if (op_inst->opcode() == SpvOpFAdd || op_inst->opcode() == SpvOpFSub ||
-        op_inst->opcode() == SpvOpIAdd || op_inst->opcode() == SpvOpISub) {
+    if (op_inst->opcode() == spv::Op::OpFAdd ||
+        op_inst->opcode() == spv::Op::OpFSub ||
+        op_inst->opcode() == spv::Op::OpIAdd ||
+        op_inst->opcode() == spv::Op::OpISub) {
       std::vector<const analysis::Constant*> op_constants =
           const_mgr->GetOperandConstants(op_inst);
       if (op_constants[0] || op_constants[1]) {
         bool zero_is_variable = op_constants[0] == nullptr;
-        bool is_add = (op_inst->opcode() == SpvOpFAdd) ||
-                      (op_inst->opcode() == SpvOpIAdd);
+        bool is_add = (op_inst->opcode() == spv::Op::OpFAdd) ||
+                      (op_inst->opcode() == spv::Op::OpIAdd);
         bool swap_operands = !is_add || zero_is_variable;
         bool negate_const = is_add;
         const analysis::Constant* c = ConstInput(op_constants);
@@ -481,7 +487,8 @@
         uint32_t op1 =
             zero_is_variable ? const_id : op_inst->GetSingleWordInOperand(1u);
         if (swap_operands) std::swap(op0, op1);
-        inst->SetOpcode(HasFloatingPoint(type) ? SpvOpFSub : SpvOpISub);
+        inst->SetOpcode(HasFloatingPoint(type) ? spv::Op::OpFSub
+                                               : spv::Op::OpISub);
         inst->SetInOperands(
             {{SPV_OPERAND_TYPE_ID, {op0}}, {SPV_OPERAND_TYPE_ID, {op1}}});
         return true;
@@ -512,7 +519,7 @@
 // id. Returns 0 if the result is not a valid value. The input types must be
 // Float.
 uint32_t PerformFloatingPointOperation(analysis::ConstantManager* const_mgr,
-                                       SpvOp opcode,
+                                       spv::Op opcode,
                                        const analysis::Constant* input1,
                                        const analysis::Constant* input2) {
   const analysis::Type* type = input1->type();
@@ -535,17 +542,17 @@
   }                                                                          \
   static_assert(true, "require extra semicolon")
   switch (opcode) {
-    case SpvOpFMul:
+    case spv::Op::OpFMul:
       FOLD_OP(*);
       break;
-    case SpvOpFDiv:
+    case spv::Op::OpFDiv:
       if (HasZero(input2)) return 0;
       FOLD_OP(/);
       break;
-    case SpvOpFAdd:
+    case spv::Op::OpFAdd:
       FOLD_OP(+);
       break;
-    case SpvOpFSub:
+    case spv::Op::OpFSub:
       FOLD_OP(-);
       break;
     default:
@@ -561,7 +568,8 @@
 // id. Returns 0 if the result is not a valid value. The input types must be
 // Integers.
 uint32_t PerformIntegerOperation(analysis::ConstantManager* const_mgr,
-                                 SpvOp opcode, const analysis::Constant* input1,
+                                 spv::Op opcode,
+                                 const analysis::Constant* input1,
                                  const analysis::Constant* input2) {
   assert(input1->type()->AsInteger());
   const analysis::Integer* type = input1->type()->AsInteger();
@@ -582,17 +590,17 @@
   }                                                      \
   static_assert(true, "require extra semicolon")
   switch (opcode) {
-    case SpvOpIMul:
+    case spv::Op::OpIMul:
       FOLD_OP(*);
       break;
-    case SpvOpSDiv:
-    case SpvOpUDiv:
+    case spv::Op::OpSDiv:
+    case spv::Op::OpUDiv:
       assert(false && "Should not merge integer division");
       break;
-    case SpvOpIAdd:
+    case spv::Op::OpIAdd:
       FOLD_OP(+);
       break;
-    case SpvOpISub:
+    case spv::Op::OpISub:
       FOLD_OP(-);
       break;
     default:
@@ -607,7 +615,7 @@
 // Performs |input1| |opcode| |input2| and returns the merged constant result
 // id. Returns 0 if the result is not a valid value. The input types must be
 // Integers, Floats or Vectors of such.
-uint32_t PerformOperation(analysis::ConstantManager* const_mgr, SpvOp opcode,
+uint32_t PerformOperation(analysis::ConstantManager* const_mgr, spv::Op opcode,
                           const analysis::Constant* input1,
                           const analysis::Constant* input2) {
   assert(input1 && input2);
@@ -667,7 +675,8 @@
 FoldingRule MergeMulMulArithmetic() {
   return [](IRContext* context, Instruction* inst,
             const std::vector<const analysis::Constant*>& constants) {
-    assert(inst->opcode() == SpvOpFMul || inst->opcode() == SpvOpIMul);
+    assert(inst->opcode() == spv::Op::OpFMul ||
+           inst->opcode() == spv::Op::OpIMul);
     analysis::ConstantManager* const_mgr = context->get_constant_mgr();
     const analysis::Type* type =
         context->get_type_mgr()->GetType(inst->type_id());
@@ -719,7 +728,7 @@
 FoldingRule MergeMulDivArithmetic() {
   return [](IRContext* context, Instruction* inst,
             const std::vector<const analysis::Constant*>& constants) {
-    assert(inst->opcode() == SpvOpFMul);
+    assert(inst->opcode() == spv::Op::OpFMul);
     analysis::ConstantManager* const_mgr = context->get_constant_mgr();
     analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
 
@@ -733,10 +742,10 @@
     for (uint32_t i = 0; i < 2; i++) {
       uint32_t op_id = inst->GetSingleWordInOperand(i);
       Instruction* op_inst = def_use_mgr->GetDef(op_id);
-      if (op_inst->opcode() == SpvOpFDiv) {
+      if (op_inst->opcode() == spv::Op::OpFDiv) {
         if (op_inst->GetSingleWordInOperand(1) ==
             inst->GetSingleWordInOperand(1 - i)) {
-          inst->SetOpcode(SpvOpCopyObject);
+          inst->SetOpcode(spv::Op::OpCopyObject);
           inst->SetInOperands(
               {{SPV_OPERAND_TYPE_ID, {op_inst->GetSingleWordInOperand(0)}}});
           return true;
@@ -749,7 +758,7 @@
     Instruction* other_inst = NonConstInput(context, constants[0], inst);
     if (!other_inst->IsFloatingPointFoldingAllowed()) return false;
 
-    if (other_inst->opcode() == SpvOpFDiv) {
+    if (other_inst->opcode() == spv::Op::OpFDiv) {
       std::vector<const analysis::Constant*> other_constants =
           const_mgr->GetOperandConstants(other_inst);
       const analysis::Constant* const_input2 = ConstInput(other_constants);
@@ -793,7 +802,8 @@
 FoldingRule MergeMulNegateArithmetic() {
   return [](IRContext* context, Instruction* inst,
             const std::vector<const analysis::Constant*>& constants) {
-    assert(inst->opcode() == SpvOpFMul || inst->opcode() == SpvOpIMul);
+    assert(inst->opcode() == spv::Op::OpFMul ||
+           inst->opcode() == spv::Op::OpIMul);
     analysis::ConstantManager* const_mgr = context->get_constant_mgr();
     const analysis::Type* type =
         context->get_type_mgr()->GetType(inst->type_id());
@@ -809,8 +819,8 @@
     if (uses_float && !other_inst->IsFloatingPointFoldingAllowed())
       return false;
 
-    if (other_inst->opcode() == SpvOpFNegate ||
-        other_inst->opcode() == SpvOpSNegate) {
+    if (other_inst->opcode() == spv::Op::OpFNegate ||
+        other_inst->opcode() == spv::Op::OpSNegate) {
       uint32_t neg_id = NegateConstant(const_mgr, const_input1);
 
       inst->SetInOperands(
@@ -833,7 +843,7 @@
 FoldingRule MergeDivDivArithmetic() {
   return [](IRContext* context, Instruction* inst,
             const std::vector<const analysis::Constant*>& constants) {
-    assert(inst->opcode() == SpvOpFDiv);
+    assert(inst->opcode() == spv::Op::OpFDiv);
     analysis::ConstantManager* const_mgr = context->get_constant_mgr();
     const analysis::Type* type =
         context->get_type_mgr()->GetType(inst->type_id());
@@ -856,10 +866,10 @@
 
       bool other_first_is_variable = other_constants[0] == nullptr;
 
-      SpvOp merge_op = inst->opcode();
+      spv::Op merge_op = inst->opcode();
       if (other_first_is_variable) {
         // Constants magnify.
-        merge_op = SpvOpFMul;
+        merge_op = spv::Op::OpFMul;
       }
 
       // This is an x / (*) case. Swap the inputs. Doesn't harm multiply
@@ -873,10 +883,10 @@
                                   ? other_inst->GetSingleWordInOperand(0u)
                                   : other_inst->GetSingleWordInOperand(1u);
 
-      SpvOp op = inst->opcode();
+      spv::Op op = inst->opcode();
       if (!first_is_variable && !other_first_is_variable) {
         // Effectively div of 1/x, so change to multiply.
-        op = SpvOpFMul;
+        op = spv::Op::OpFMul;
       }
 
       uint32_t op1 = merged_id;
@@ -904,7 +914,7 @@
 FoldingRule MergeDivMulArithmetic() {
   return [](IRContext* context, Instruction* inst,
             const std::vector<const analysis::Constant*>& constants) {
-    assert(inst->opcode() == SpvOpFDiv);
+    assert(inst->opcode() == spv::Op::OpFDiv);
     analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
     analysis::ConstantManager* const_mgr = context->get_constant_mgr();
 
@@ -918,11 +928,11 @@
     uint32_t op_id = inst->GetSingleWordInOperand(0);
     Instruction* op_inst = def_use_mgr->GetDef(op_id);
 
-    if (op_inst->opcode() == SpvOpFMul) {
+    if (op_inst->opcode() == spv::Op::OpFMul) {
       for (uint32_t i = 0; i < 2; i++) {
         if (op_inst->GetSingleWordInOperand(i) ==
             inst->GetSingleWordInOperand(1)) {
-          inst->SetOpcode(SpvOpCopyObject);
+          inst->SetOpcode(spv::Op::OpCopyObject);
           inst->SetInOperands({{SPV_OPERAND_TYPE_ID,
                                 {op_inst->GetSingleWordInOperand(1 - i)}}});
           return true;
@@ -936,7 +946,7 @@
     if (!other_inst->IsFloatingPointFoldingAllowed()) return false;
 
     bool first_is_variable = constants[0] == nullptr;
-    if (other_inst->opcode() == SpvOpFMul) {
+    if (other_inst->opcode() == spv::Op::OpFMul) {
       std::vector<const analysis::Constant*> other_constants =
           const_mgr->GetOperandConstants(other_inst);
       const analysis::Constant* const_input2 = ConstInput(other_constants);
@@ -976,7 +986,7 @@
 FoldingRule MergeDivNegateArithmetic() {
   return [](IRContext* context, Instruction* inst,
             const std::vector<const analysis::Constant*>& constants) {
-    assert(inst->opcode() == SpvOpFDiv);
+    assert(inst->opcode() == spv::Op::OpFDiv);
     analysis::ConstantManager* const_mgr = context->get_constant_mgr();
     if (!inst->IsFloatingPointFoldingAllowed()) return false;
 
@@ -986,7 +996,7 @@
     if (!other_inst->IsFloatingPointFoldingAllowed()) return false;
 
     bool first_is_variable = constants[0] == nullptr;
-    if (other_inst->opcode() == SpvOpFNegate) {
+    if (other_inst->opcode() == spv::Op::OpFNegate) {
       uint32_t neg_id = NegateConstant(const_mgr, const_input1);
 
       if (first_is_variable) {
@@ -1012,7 +1022,8 @@
 FoldingRule MergeAddNegateArithmetic() {
   return [](IRContext* context, Instruction* inst,
             const std::vector<const analysis::Constant*>& constants) {
-    assert(inst->opcode() == SpvOpFAdd || inst->opcode() == SpvOpIAdd);
+    assert(inst->opcode() == spv::Op::OpFAdd ||
+           inst->opcode() == spv::Op::OpIAdd);
     const analysis::Type* type =
         context->get_type_mgr()->GetType(inst->type_id());
     bool uses_float = HasFloatingPoint(type);
@@ -1024,9 +1035,10 @@
     if (uses_float && !other_inst->IsFloatingPointFoldingAllowed())
       return false;
 
-    if (other_inst->opcode() == SpvOpSNegate ||
-        other_inst->opcode() == SpvOpFNegate) {
-      inst->SetOpcode(HasFloatingPoint(type) ? SpvOpFSub : SpvOpISub);
+    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(
@@ -1045,7 +1057,8 @@
 FoldingRule MergeSubNegateArithmetic() {
   return [](IRContext* context, Instruction* inst,
             const std::vector<const analysis::Constant*>& constants) {
-    assert(inst->opcode() == SpvOpFSub || inst->opcode() == SpvOpISub);
+    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());
@@ -1061,15 +1074,15 @@
     if (uses_float && !other_inst->IsFloatingPointFoldingAllowed())
       return false;
 
-    if (other_inst->opcode() == SpvOpSNegate ||
-        other_inst->opcode() == SpvOpFNegate) {
+    if (other_inst->opcode() == spv::Op::OpSNegate ||
+        other_inst->opcode() == spv::Op::OpFNegate) {
       uint32_t op1 = 0;
       uint32_t op2 = 0;
-      SpvOp opcode = inst->opcode();
+      spv::Op opcode = inst->opcode();
       if (constants[0] != nullptr) {
         op1 = other_inst->GetSingleWordInOperand(0u);
         op2 = inst->GetSingleWordInOperand(0u);
-        opcode = HasFloatingPoint(type) ? SpvOpFAdd : SpvOpIAdd;
+        opcode = HasFloatingPoint(type) ? spv::Op::OpFAdd : spv::Op::OpIAdd;
       } else {
         op1 = NegateConstant(const_mgr, const_input1);
         op2 = other_inst->GetSingleWordInOperand(0u);
@@ -1093,7 +1106,8 @@
 FoldingRule MergeAddAddArithmetic() {
   return [](IRContext* context, Instruction* inst,
             const std::vector<const analysis::Constant*>& constants) {
-    assert(inst->opcode() == SpvOpFAdd || inst->opcode() == SpvOpIAdd);
+    assert(inst->opcode() == spv::Op::OpFAdd ||
+           inst->opcode() == spv::Op::OpIAdd);
     const analysis::Type* type =
         context->get_type_mgr()->GetType(inst->type_id());
     analysis::ConstantManager* const_mgr = context->get_constant_mgr();
@@ -1109,8 +1123,8 @@
     if (uses_float && !other_inst->IsFloatingPointFoldingAllowed())
       return false;
 
-    if (other_inst->opcode() == SpvOpFAdd ||
-        other_inst->opcode() == SpvOpIAdd) {
+    if (other_inst->opcode() == spv::Op::OpFAdd ||
+        other_inst->opcode() == spv::Op::OpIAdd) {
       std::vector<const analysis::Constant*> other_constants =
           const_mgr->GetOperandConstants(other_inst);
       const analysis::Constant* const_input2 = ConstInput(other_constants);
@@ -1140,7 +1154,8 @@
 FoldingRule MergeAddSubArithmetic() {
   return [](IRContext* context, Instruction* inst,
             const std::vector<const analysis::Constant*>& constants) {
-    assert(inst->opcode() == SpvOpFAdd || inst->opcode() == SpvOpIAdd);
+    assert(inst->opcode() == spv::Op::OpFAdd ||
+           inst->opcode() == spv::Op::OpIAdd);
     const analysis::Type* type =
         context->get_type_mgr()->GetType(inst->type_id());
     analysis::ConstantManager* const_mgr = context->get_constant_mgr();
@@ -1156,15 +1171,15 @@
     if (uses_float && !other_inst->IsFloatingPointFoldingAllowed())
       return false;
 
-    if (other_inst->opcode() == SpvOpFSub ||
-        other_inst->opcode() == SpvOpISub) {
+    if (other_inst->opcode() == spv::Op::OpFSub ||
+        other_inst->opcode() == spv::Op::OpISub) {
       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 first_is_variable = other_constants[0] == nullptr;
-      SpvOp op = inst->opcode();
+      spv::Op op = inst->opcode();
       uint32_t op1 = 0;
       uint32_t op2 = 0;
       if (first_is_variable) {
@@ -1199,7 +1214,8 @@
 FoldingRule MergeSubAddArithmetic() {
   return [](IRContext* context, Instruction* inst,
             const std::vector<const analysis::Constant*>& constants) {
-    assert(inst->opcode() == SpvOpFSub || inst->opcode() == SpvOpISub);
+    assert(inst->opcode() == spv::Op::OpFSub ||
+           inst->opcode() == spv::Op::OpISub);
     const analysis::Type* type =
         context->get_type_mgr()->GetType(inst->type_id());
     analysis::ConstantManager* const_mgr = context->get_constant_mgr();
@@ -1215,8 +1231,8 @@
     if (uses_float && !other_inst->IsFloatingPointFoldingAllowed())
       return false;
 
-    if (other_inst->opcode() == SpvOpFAdd ||
-        other_inst->opcode() == SpvOpIAdd) {
+    if (other_inst->opcode() == spv::Op::OpFAdd ||
+        other_inst->opcode() == spv::Op::OpIAdd) {
       std::vector<const analysis::Constant*> other_constants =
           const_mgr->GetOperandConstants(other_inst);
       const analysis::Constant* const_input2 = ConstInput(other_constants);
@@ -1231,7 +1247,7 @@
       // Subtract the constants.
       uint32_t merged_id = PerformOperation(const_mgr, inst->opcode(),
                                             const_input1, const_input2);
-      SpvOp op = inst->opcode();
+      spv::Op op = inst->opcode();
       uint32_t op1 = 0;
       uint32_t op2 = 0;
       if (constants[0] == nullptr) {
@@ -1264,7 +1280,8 @@
 FoldingRule MergeSubSubArithmetic() {
   return [](IRContext* context, Instruction* inst,
             const std::vector<const analysis::Constant*>& constants) {
-    assert(inst->opcode() == SpvOpFSub || inst->opcode() == SpvOpISub);
+    assert(inst->opcode() == spv::Op::OpFSub ||
+           inst->opcode() == spv::Op::OpISub);
     const analysis::Type* type =
         context->get_type_mgr()->GetType(inst->type_id());
     analysis::ConstantManager* const_mgr = context->get_constant_mgr();
@@ -1280,8 +1297,8 @@
     if (uses_float && !other_inst->IsFloatingPointFoldingAllowed())
       return false;
 
-    if (other_inst->opcode() == SpvOpFSub ||
-        other_inst->opcode() == SpvOpISub) {
+    if (other_inst->opcode() == spv::Op::OpFSub ||
+        other_inst->opcode() == spv::Op::OpISub) {
       std::vector<const analysis::Constant*> other_constants =
           const_mgr->GetOperandConstants(other_inst);
       const analysis::Constant* const_input2 = ConstInput(other_constants);
@@ -1292,9 +1309,9 @@
 
       // Merge the constants.
       uint32_t merged_id = 0;
-      SpvOp merge_op = inst->opcode();
+      spv::Op merge_op = inst->opcode();
       if (other_constants[0] == nullptr) {
-        merge_op = uses_float ? SpvOpFAdd : SpvOpIAdd;
+        merge_op = uses_float ? spv::Op::OpFAdd : spv::Op::OpIAdd;
       } else if (constants[0] == nullptr) {
         std::swap(const_input1, const_input2);
       }
@@ -1302,10 +1319,10 @@
           PerformOperation(const_mgr, merge_op, const_input1, const_input2);
       if (merged_id == 0) return false;
 
-      SpvOp op = inst->opcode();
+      spv::Op op = inst->opcode();
       if (constants[0] != nullptr && other_constants[0] != nullptr) {
         // Change the operation.
-        op = uses_float ? SpvOpFAdd : SpvOpIAdd;
+        op = uses_float ? spv::Op::OpFAdd : spv::Op::OpIAdd;
       }
 
       uint32_t op1 = 0;
@@ -1333,13 +1350,14 @@
   IRContext* context = inst->context();
   analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
   Instruction* sub_inst = def_use_mgr->GetDef(sub);
-  if (sub_inst->opcode() != SpvOpFSub && sub_inst->opcode() != SpvOpISub)
+  if (sub_inst->opcode() != spv::Op::OpFSub &&
+      sub_inst->opcode() != spv::Op::OpISub)
     return false;
-  if (sub_inst->opcode() == SpvOpFSub &&
+  if (sub_inst->opcode() == spv::Op::OpFSub &&
       !sub_inst->IsFloatingPointFoldingAllowed())
     return false;
   if (addend != sub_inst->GetSingleWordInOperand(1)) return false;
-  inst->SetOpcode(SpvOpCopyObject);
+  inst->SetOpcode(spv::Op::OpCopyObject);
   inst->SetInOperands(
       {{SPV_OPERAND_TYPE_ID, {sub_inst->GetSingleWordInOperand(0)}}});
   context->UpdateDefUse(inst);
@@ -1355,7 +1373,8 @@
 FoldingRule MergeGenericAddSubArithmetic() {
   return [](IRContext* context, Instruction* inst,
             const std::vector<const analysis::Constant*>&) {
-    assert(inst->opcode() == SpvOpFAdd || inst->opcode() == SpvOpIAdd);
+    assert(inst->opcode() == spv::Op::OpFAdd ||
+           inst->opcode() == spv::Op::OpIAdd);
     const analysis::Type* type =
         context->get_type_mgr()->GetType(inst->type_id());
     bool uses_float = HasFloatingPoint(type);
@@ -1383,7 +1402,8 @@
       IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
   Instruction* new_add_inst = ir_builder.AddBinaryOp(
       inst->type_id(), inst->opcode(), factor0_1, factor1_1);
-  inst->SetOpcode(inst->opcode() == SpvOpFAdd ? SpvOpFMul : SpvOpIMul);
+  inst->SetOpcode(inst->opcode() == spv::Op::OpFAdd ? 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);
@@ -1395,7 +1415,8 @@
 FoldingRule FactorAddMuls() {
   return [](IRContext* context, Instruction* inst,
             const std::vector<const analysis::Constant*>&) {
-    assert(inst->opcode() == SpvOpFAdd || inst->opcode() == SpvOpIAdd);
+    assert(inst->opcode() == spv::Op::OpFAdd ||
+           inst->opcode() == spv::Op::OpIAdd);
     const analysis::Type* type =
         context->get_type_mgr()->GetType(inst->type_id());
     bool uses_float = HasFloatingPoint(type);
@@ -1404,13 +1425,13 @@
     analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
     uint32_t add_op0 = inst->GetSingleWordInOperand(0);
     Instruction* add_op0_inst = def_use_mgr->GetDef(add_op0);
-    if (add_op0_inst->opcode() != SpvOpFMul &&
-        add_op0_inst->opcode() != SpvOpIMul)
+    if (add_op0_inst->opcode() != spv::Op::OpFMul &&
+        add_op0_inst->opcode() != spv::Op::OpIMul)
       return false;
     uint32_t add_op1 = inst->GetSingleWordInOperand(1);
     Instruction* add_op1_inst = def_use_mgr->GetDef(add_op1);
-    if (add_op1_inst->opcode() != SpvOpFMul &&
-        add_op1_inst->opcode() != SpvOpIMul)
+    if (add_op1_inst->opcode() != spv::Op::OpFMul &&
+        add_op1_inst->opcode() != spv::Op::OpIMul)
       return false;
 
     // Only perform this optimization if both of the muls only have one use.
@@ -1418,7 +1439,7 @@
     if (def_use_mgr->NumUses(add_op0_inst) > 1) return false;
     if (def_use_mgr->NumUses(add_op1_inst) > 1) return false;
 
-    if (add_op0_inst->opcode() == SpvOpFMul &&
+    if (add_op0_inst->opcode() == spv::Op::OpFMul &&
         (!add_op0_inst->IsFloatingPointFoldingAllowed() ||
          !add_op1_inst->IsFloatingPointFoldingAllowed()))
       return false;
@@ -1457,7 +1478,7 @@
   operands.push_back({SPV_OPERAND_TYPE_ID, {y}});
   operands.push_back({SPV_OPERAND_TYPE_ID, {a}});
 
-  inst->SetOpcode(SpvOpExtInst);
+  inst->SetOpcode(spv::Op::OpExtInst);
   inst->SetInOperands(std::move(operands));
 }
 
@@ -1468,7 +1489,7 @@
 // a + (x * y) = Fma x y a
 bool MergeMulAddArithmetic(IRContext* context, Instruction* inst,
                            const std::vector<const analysis::Constant*>&) {
-  assert(inst->opcode() == SpvOpFAdd);
+  assert(inst->opcode() == spv::Op::OpFAdd);
 
   if (!inst->IsFloatingPointFoldingAllowed()) {
     return false;
@@ -1479,7 +1500,7 @@
     uint32_t op_id = inst->GetSingleWordInOperand(i);
     Instruction* op_inst = def_use_mgr->GetDef(op_id);
 
-    if (op_inst->opcode() != SpvOpFMul) {
+    if (op_inst->opcode() != spv::Op::OpFMul) {
       continue;
     }
 
@@ -1514,7 +1535,7 @@
       sub->context(), sub,
       IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
 
-  Instruction* neg = ir_builder.AddUnaryOp(sub->type_id(), SpvOpFNegate,
+  Instruction* neg = ir_builder.AddUnaryOp(sub->type_id(), spv::Op::OpFNegate,
                                            negate_addition ? a : x);
   uint32_t neg_op = neg->result_id();  // -a : -x
 
@@ -1525,7 +1546,7 @@
   operands.push_back({SPV_OPERAND_TYPE_ID, {y}});
   operands.push_back({SPV_OPERAND_TYPE_ID, {negate_addition ? neg_op : a}});
 
-  sub->SetOpcode(SpvOpExtInst);
+  sub->SetOpcode(spv::Op::OpExtInst);
   sub->SetInOperands(std::move(operands));
 }
 
@@ -1536,7 +1557,7 @@
 // a - (x * y) = Fma -x y a
 bool MergeMulSubArithmetic(IRContext* context, Instruction* sub,
                            const std::vector<const analysis::Constant*>&) {
-  assert(sub->opcode() == SpvOpFSub);
+  assert(sub->opcode() == spv::Op::OpFSub);
 
   if (!sub->IsFloatingPointFoldingAllowed()) {
     return false;
@@ -1547,7 +1568,7 @@
     uint32_t op_id = sub->GetSingleWordInOperand(i);
     Instruction* mul = def_use_mgr->GetDef(op_id);
 
-    if (mul->opcode() != SpvOpFMul) {
+    if (mul->opcode() != spv::Op::OpFMul) {
       continue;
     }
 
@@ -1567,7 +1588,8 @@
 FoldingRule IntMultipleBy1() {
   return [](IRContext*, Instruction* inst,
             const std::vector<const analysis::Constant*>& constants) {
-    assert(inst->opcode() == SpvOpIMul && "Wrong opcode.  Should be OpIMul.");
+    assert(inst->opcode() == spv::Op::OpIMul &&
+           "Wrong opcode.  Should be OpIMul.");
     for (uint32_t i = 0; i < 2; i++) {
       if (constants[i] == nullptr) {
         continue;
@@ -1579,7 +1601,7 @@
         bool is_one = (width == 32) ? int_constant->GetU32BitValue() == 1u
                                     : int_constant->GetU64BitValue() == 1ull;
         if (is_one) {
-          inst->SetOpcode(SpvOpCopyObject);
+          inst->SetOpcode(spv::Op::OpCopyObject);
           inst->SetInOperands(
               {{SPV_OPERAND_TYPE_ID, {inst->GetSingleWordInOperand(1 - i)}}});
           return true;
@@ -1596,7 +1618,7 @@
 uint32_t GetNumOfElementsContributedByOperand(IRContext* context,
                                               const Instruction* inst,
                                               uint32_t index) {
-  assert(inst->opcode() == SpvOpCompositeConstruct);
+  assert(inst->opcode() == spv::Op::OpCompositeConstruct);
   analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
   analysis::TypeManager* type_mgr = context->get_type_mgr();
 
@@ -1627,14 +1649,17 @@
 // out-of-bounds. |inst| must be an |OpCompositeConstruct| instruction.
 std::vector<Operand> GetExtractOperandsForElementOfCompositeConstruct(
     IRContext* context, const Instruction* inst, uint32_t result_index) {
-  assert(inst->opcode() == SpvOpCompositeConstruct);
+  assert(inst->opcode() == spv::Op::OpCompositeConstruct);
   analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
   analysis::TypeManager* type_mgr = context->get_type_mgr();
 
   analysis::Type* result_type = type_mgr->GetType(inst->type_id());
   if (result_type->AsVector() == nullptr) {
-    uint32_t id = inst->GetSingleWordInOperand(result_index);
-    return {Operand(SPV_OPERAND_TYPE_ID, {id})};
+    if (result_index < inst->NumInOperands()) {
+      uint32_t id = inst->GetSingleWordInOperand(result_index);
+      return {Operand(SPV_OPERAND_TYPE_ID, {id})};
+    }
+    return {};
   }
 
   // If the result type is a vector, then vector operands are concatenated.
@@ -1666,7 +1691,7 @@
     const std::vector<const analysis::Constant*>&) {
   // If the input to an OpCompositeExtract is an OpCompositeConstruct,
   // then we can simply use the appropriate element in the construction.
-  assert(inst->opcode() == SpvOpCompositeExtract &&
+  assert(inst->opcode() == spv::Op::OpCompositeExtract &&
          "Wrong opcode.  Should be OpCompositeExtract.");
   analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
 
@@ -1678,7 +1703,7 @@
   uint32_t cid = inst->GetSingleWordInOperand(kExtractCompositeIdInIdx);
   Instruction* cinst = def_use_mgr->GetDef(cid);
 
-  if (cinst->opcode() != SpvOpCompositeConstruct) {
+  if (cinst->opcode() != spv::Op::OpCompositeConstruct) {
     return false;
   }
 
@@ -1700,7 +1725,7 @@
   if (operands.size() == 1) {
     // If there were no extra indices, then we have the final object.  No need
     // to extract any more.
-    inst->SetOpcode(SpvOpCopyObject);
+    inst->SetOpcode(spv::Op::OpCopyObject);
   }
 
   inst->SetInOperands(std::move(operands));
@@ -1738,8 +1763,8 @@
 bool HaveSameIndexesExceptForLast(Instruction* inst_1, Instruction* inst_2) {
   assert(inst_1->opcode() == inst_2->opcode() &&
          "Expecting the opcodes to be the same.");
-  assert((inst_1->opcode() == SpvOpCompositeInsert ||
-          inst_1->opcode() == SpvOpCompositeExtract) &&
+  assert((inst_1->opcode() == spv::Op::OpCompositeInsert ||
+          inst_1->opcode() == spv::Op::OpCompositeExtract) &&
          "Instructions must be OpCompositeInsert or OpCompositeExtract.");
 
   if (inst_1->NumInOperands() != inst_2->NumInOperands()) {
@@ -1747,7 +1772,7 @@
   }
 
   uint32_t first_index_position =
-      (inst_1->opcode() == SpvOpCompositeInsert ? 2 : 1);
+      (inst_1->opcode() == spv::Op::OpCompositeInsert ? 2 : 1);
   for (uint32_t i = first_index_position; i < inst_1->NumInOperands() - 1;
        i++) {
     if (inst_1->GetSingleWordInOperand(i) !=
@@ -1766,7 +1791,7 @@
 bool CompositeExtractFeedingConstruct(
     IRContext* context, Instruction* inst,
     const std::vector<const analysis::Constant*>&) {
-  assert(inst->opcode() == SpvOpCompositeConstruct &&
+  assert(inst->opcode() == spv::Op::OpCompositeConstruct &&
          "Wrong opcode.  Should be OpCompositeConstruct.");
   analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
   uint32_t original_id = 0;
@@ -1788,7 +1813,7 @@
       first_element_inst = element_inst;
     }
 
-    if (element_inst->opcode() != SpvOpCompositeExtract) {
+    if (element_inst->opcode() != spv::Op::OpCompositeExtract) {
       return false;
     }
 
@@ -1828,14 +1853,14 @@
 
   if (first_element_inst->NumInOperands() == 2) {
     // Simplify by using the original object.
-    inst->SetOpcode(SpvOpCopyObject);
+    inst->SetOpcode(spv::Op::OpCopyObject);
     inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {original_id}}});
     return true;
   }
 
   // Copies the original id and all indexes except for the last to the new
   // extract instruction.
-  inst->SetOpcode(SpvOpCompositeExtract);
+  inst->SetOpcode(spv::Op::OpCompositeExtract);
   inst->SetInOperands(std::vector<Operand>(first_element_inst->begin() + 2,
                                            first_element_inst->end() - 1));
   return true;
@@ -1844,13 +1869,13 @@
 FoldingRule InsertFeedingExtract() {
   return [](IRContext* context, Instruction* inst,
             const std::vector<const analysis::Constant*>&) {
-    assert(inst->opcode() == SpvOpCompositeExtract &&
+    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() != SpvOpCompositeInsert) {
+    if (cinst->opcode() != spv::Op::OpCompositeInsert) {
       return false;
     }
 
@@ -1870,7 +1895,7 @@
 
     // We are extracting the element that was inserted.
     if (i == inst->NumInOperands() && i + 1 == cinst->NumInOperands()) {
-      inst->SetOpcode(SpvOpCopyObject);
+      inst->SetOpcode(spv::Op::OpCopyObject);
       inst->SetInOperands(
           {{SPV_OPERAND_TYPE_ID,
             {cinst->GetSingleWordInOperand(kInsertObjectIdInIdx)}}});
@@ -1919,14 +1944,14 @@
 FoldingRule VectorShuffleFeedingExtract() {
   return [](IRContext* context, Instruction* inst,
             const std::vector<const analysis::Constant*>&) {
-    assert(inst->opcode() == SpvOpCompositeExtract &&
+    assert(inst->opcode() == spv::Op::OpCompositeExtract &&
            "Wrong opcode.  Should be OpCompositeExtract.");
     analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
     analysis::TypeManager* type_mgr = context->get_type_mgr();
     uint32_t cid = inst->GetSingleWordInOperand(kExtractCompositeIdInIdx);
     Instruction* cinst = def_use_mgr->GetDef(cid);
 
-    if (cinst->opcode() != SpvOpVectorShuffle) {
+    if (cinst->opcode() != spv::Op::OpVectorShuffle) {
       return false;
     }
 
@@ -1947,7 +1972,7 @@
     // Extracting an undefined value so fold this extract into an undef.
     const uint32_t undef_literal_value = 0xffffffff;
     if (new_index == undef_literal_value) {
-      inst->SetOpcode(SpvOpUndef);
+      inst->SetOpcode(spv::Op::OpUndef);
       inst->SetInOperands({});
       return true;
     }
@@ -1975,7 +2000,7 @@
 FoldingRule FMixFeedingExtract() {
   return [](IRContext* context, Instruction* inst,
             const std::vector<const analysis::Constant*>&) {
-    assert(inst->opcode() == SpvOpCompositeExtract &&
+    assert(inst->opcode() == spv::Op::OpCompositeExtract &&
            "Wrong opcode.  Should be OpCompositeExtract.");
     analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
     analysis::ConstantManager* const_mgr = context->get_constant_mgr();
@@ -1984,7 +2009,7 @@
         inst->GetSingleWordInOperand(kExtractCompositeIdInIdx);
     Instruction* composite_inst = def_use_mgr->GetDef(composite_id);
 
-    if (composite_inst->opcode() != SpvOpExtInst) {
+    if (composite_inst->opcode() != spv::Op::OpExtInst) {
       return false;
     }
 
@@ -2004,7 +2029,7 @@
     a->SetInOperand(kExtractCompositeIdInIdx, {a_id});
     context->get_instruction_folder().FoldInstruction(a.get());
 
-    if (a->opcode() != SpvOpCopyObject) {
+    if (a->opcode() != spv::Op::OpCopyObject) {
       return false;
     }
 
@@ -2066,7 +2091,7 @@
   analysis::DefUseManager* def_use_mgr = inst->context()->get_def_use_mgr();
   std::map<uint32_t, uint32_t> values_inserted;
   Instruction* current_inst = inst;
-  while (current_inst->opcode() == SpvOpCompositeInsert) {
+  while (current_inst->opcode() == spv::Op::OpCompositeInsert) {
     if (current_inst->NumInOperands() > inst->NumInOperands()) {
       // This is the catch the case
       //   %2 = OpCompositeInsert %m2x2int %v2int_1_0 %m2x2int_undef 0
@@ -2108,7 +2133,7 @@
 // Returns the type of the element that immediately contains the element being
 // inserted by the OpCompositeInsert instruction |inst|.
 const analysis::Type* GetContainerType(Instruction* inst) {
-  assert(inst->opcode() == SpvOpCompositeInsert);
+  assert(inst->opcode() == spv::Op::OpCompositeInsert);
   analysis::TypeManager* type_mgr = inst->context()->get_type_mgr();
   return GetElementType(inst->type_id(), inst->begin() + 4, inst->end() - 1,
                         type_mgr);
@@ -2140,7 +2165,7 @@
 // instruction is replaced with an OpCopyObject instead.
 void InsertConstructedObject(Instruction* inst, const Instruction* construct) {
   if (inst->NumInOperands() == 3) {
-    inst->SetOpcode(SpvOpCopyObject);
+    inst->SetOpcode(spv::Op::OpCopyObject);
     inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {construct->result_id()}}});
   } else {
     inst->SetInOperand(kInsertObjectIdInIdx, {construct->result_id()});
@@ -2153,7 +2178,7 @@
 bool CompositeInsertToCompositeConstruct(
     IRContext* context, Instruction* inst,
     const std::vector<const analysis::Constant*>&) {
-  assert(inst->opcode() == SpvOpCompositeInsert &&
+  assert(inst->opcode() == spv::Op::OpCompositeInsert &&
          "Wrong opcode.  Should be OpCompositeInsert.");
   if (inst->NumInOperands() < 3) return false;
 
@@ -2179,7 +2204,8 @@
   // itself, can be replaced by the value itself.
   return [](IRContext*, Instruction* inst,
             const std::vector<const analysis::Constant*>&) {
-    assert(inst->opcode() == SpvOpPhi && "Wrong opcode.  Should be OpPhi.");
+    assert(inst->opcode() == spv::Op::OpPhi &&
+           "Wrong opcode.  Should be OpPhi.");
 
     uint32_t incoming_value = 0;
 
@@ -2203,7 +2229,7 @@
     }
 
     // We have a single incoming value.  Simplify using that value.
-    inst->SetOpcode(SpvOpCopyObject);
+    inst->SetOpcode(spv::Op::OpCopyObject);
     inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {incoming_value}}});
     return true;
   };
@@ -2212,7 +2238,7 @@
 FoldingRule BitCastScalarOrVector() {
   return [](IRContext* context, Instruction* inst,
             const std::vector<const analysis::Constant*>& constants) {
-    assert(inst->opcode() == SpvOpBitcast && constants.size() == 1);
+    assert(inst->opcode() == spv::Op::OpBitcast && constants.size() == 1);
     if (constants[0] == nullptr) return false;
 
     const analysis::Type* type =
@@ -2232,7 +2258,7 @@
     auto new_feeder_id =
         const_mgr->GetDefiningInstruction(bitcasted_constant, inst->type_id())
             ->result_id();
-    inst->SetOpcode(SpvOpCopyObject);
+    inst->SetOpcode(spv::Op::OpCopyObject);
     inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {new_feeder_id}}});
     return true;
   };
@@ -2243,7 +2269,7 @@
   // constant can be replaced by one of the values
   return [](IRContext*, Instruction* inst,
             const std::vector<const analysis::Constant*>& constants) {
-    assert(inst->opcode() == SpvOpSelect &&
+    assert(inst->opcode() == spv::Op::OpSelect &&
            "Wrong opcode.  Should be OpSelect.");
     assert(inst->NumInOperands() == 3);
     assert(constants.size() == 3);
@@ -2253,14 +2279,14 @@
 
     if (true_id == false_id) {
       // Both results are the same, condition doesn't matter
-      inst->SetOpcode(SpvOpCopyObject);
+      inst->SetOpcode(spv::Op::OpCopyObject);
       inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {true_id}}});
       return true;
     } else if (constants[0]) {
       const analysis::Type* type = constants[0]->type();
       if (type->AsBool()) {
         // Scalar constant value, select the corresponding value.
-        inst->SetOpcode(SpvOpCopyObject);
+        inst->SetOpcode(spv::Op::OpCopyObject);
         if (constants[0]->AsNullConstant() ||
             !constants[0]->AsBoolConstant()->value()) {
           inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {false_id}}});
@@ -2272,7 +2298,7 @@
         assert(type->AsVector());
         if (constants[0]->AsNullConstant()) {
           // All values come from false id.
-          inst->SetOpcode(SpvOpCopyObject);
+          inst->SetOpcode(spv::Op::OpCopyObject);
           inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {false_id}}});
           return true;
         } else {
@@ -2299,7 +2325,7 @@
             }
           }
 
-          inst->SetOpcode(SpvOpVectorShuffle);
+          inst->SetOpcode(spv::Op::OpVectorShuffle);
           inst->SetInOperands(std::move(ops));
           return true;
         }
@@ -2359,7 +2385,8 @@
 FoldingRule RedundantFAdd() {
   return [](IRContext*, Instruction* inst,
             const std::vector<const analysis::Constant*>& constants) {
-    assert(inst->opcode() == SpvOpFAdd && "Wrong opcode.  Should be OpFAdd.");
+    assert(inst->opcode() == spv::Op::OpFAdd &&
+           "Wrong opcode.  Should be OpFAdd.");
     assert(constants.size() == 2);
 
     if (!inst->IsFloatingPointFoldingAllowed()) {
@@ -2370,7 +2397,7 @@
     FloatConstantKind kind1 = getFloatConstantKind(constants[1]);
 
     if (kind0 == FloatConstantKind::Zero || kind1 == FloatConstantKind::Zero) {
-      inst->SetOpcode(SpvOpCopyObject);
+      inst->SetOpcode(spv::Op::OpCopyObject);
       inst->SetInOperands({{SPV_OPERAND_TYPE_ID,
                             {inst->GetSingleWordInOperand(
                                 kind0 == FloatConstantKind::Zero ? 1 : 0)}}});
@@ -2384,7 +2411,8 @@
 FoldingRule RedundantFSub() {
   return [](IRContext*, Instruction* inst,
             const std::vector<const analysis::Constant*>& constants) {
-    assert(inst->opcode() == SpvOpFSub && "Wrong opcode.  Should be OpFSub.");
+    assert(inst->opcode() == spv::Op::OpFSub &&
+           "Wrong opcode.  Should be OpFSub.");
     assert(constants.size() == 2);
 
     if (!inst->IsFloatingPointFoldingAllowed()) {
@@ -2395,14 +2423,14 @@
     FloatConstantKind kind1 = getFloatConstantKind(constants[1]);
 
     if (kind0 == FloatConstantKind::Zero) {
-      inst->SetOpcode(SpvOpFNegate);
+      inst->SetOpcode(spv::Op::OpFNegate);
       inst->SetInOperands(
           {{SPV_OPERAND_TYPE_ID, {inst->GetSingleWordInOperand(1)}}});
       return true;
     }
 
     if (kind1 == FloatConstantKind::Zero) {
-      inst->SetOpcode(SpvOpCopyObject);
+      inst->SetOpcode(spv::Op::OpCopyObject);
       inst->SetInOperands(
           {{SPV_OPERAND_TYPE_ID, {inst->GetSingleWordInOperand(0)}}});
       return true;
@@ -2415,7 +2443,8 @@
 FoldingRule RedundantFMul() {
   return [](IRContext*, Instruction* inst,
             const std::vector<const analysis::Constant*>& constants) {
-    assert(inst->opcode() == SpvOpFMul && "Wrong opcode.  Should be OpFMul.");
+    assert(inst->opcode() == spv::Op::OpFMul &&
+           "Wrong opcode.  Should be OpFMul.");
     assert(constants.size() == 2);
 
     if (!inst->IsFloatingPointFoldingAllowed()) {
@@ -2426,7 +2455,7 @@
     FloatConstantKind kind1 = getFloatConstantKind(constants[1]);
 
     if (kind0 == FloatConstantKind::Zero || kind1 == FloatConstantKind::Zero) {
-      inst->SetOpcode(SpvOpCopyObject);
+      inst->SetOpcode(spv::Op::OpCopyObject);
       inst->SetInOperands({{SPV_OPERAND_TYPE_ID,
                             {inst->GetSingleWordInOperand(
                                 kind0 == FloatConstantKind::Zero ? 0 : 1)}}});
@@ -2434,7 +2463,7 @@
     }
 
     if (kind0 == FloatConstantKind::One || kind1 == FloatConstantKind::One) {
-      inst->SetOpcode(SpvOpCopyObject);
+      inst->SetOpcode(spv::Op::OpCopyObject);
       inst->SetInOperands({{SPV_OPERAND_TYPE_ID,
                             {inst->GetSingleWordInOperand(
                                 kind0 == FloatConstantKind::One ? 1 : 0)}}});
@@ -2448,7 +2477,8 @@
 FoldingRule RedundantFDiv() {
   return [](IRContext*, Instruction* inst,
             const std::vector<const analysis::Constant*>& constants) {
-    assert(inst->opcode() == SpvOpFDiv && "Wrong opcode.  Should be OpFDiv.");
+    assert(inst->opcode() == spv::Op::OpFDiv &&
+           "Wrong opcode.  Should be OpFDiv.");
     assert(constants.size() == 2);
 
     if (!inst->IsFloatingPointFoldingAllowed()) {
@@ -2459,14 +2489,14 @@
     FloatConstantKind kind1 = getFloatConstantKind(constants[1]);
 
     if (kind0 == FloatConstantKind::Zero) {
-      inst->SetOpcode(SpvOpCopyObject);
+      inst->SetOpcode(spv::Op::OpCopyObject);
       inst->SetInOperands(
           {{SPV_OPERAND_TYPE_ID, {inst->GetSingleWordInOperand(0)}}});
       return true;
     }
 
     if (kind1 == FloatConstantKind::One) {
-      inst->SetOpcode(SpvOpCopyObject);
+      inst->SetOpcode(spv::Op::OpCopyObject);
       inst->SetInOperands(
           {{SPV_OPERAND_TYPE_ID, {inst->GetSingleWordInOperand(0)}}});
       return true;
@@ -2479,7 +2509,7 @@
 FoldingRule RedundantFMix() {
   return [](IRContext* context, Instruction* inst,
             const std::vector<const analysis::Constant*>& constants) {
-    assert(inst->opcode() == SpvOpExtInst &&
+    assert(inst->opcode() == spv::Op::OpExtInst &&
            "Wrong opcode.  Should be OpExtInst.");
 
     if (!inst->IsFloatingPointFoldingAllowed()) {
@@ -2497,7 +2527,7 @@
       FloatConstantKind kind4 = getFloatConstantKind(constants[4]);
 
       if (kind4 == FloatConstantKind::Zero || kind4 == FloatConstantKind::One) {
-        inst->SetOpcode(SpvOpCopyObject);
+        inst->SetOpcode(spv::Op::OpCopyObject);
         inst->SetInOperands(
             {{SPV_OPERAND_TYPE_ID,
               {inst->GetSingleWordInOperand(kind4 == FloatConstantKind::Zero
@@ -2515,7 +2545,8 @@
 FoldingRule RedundantIAdd() {
   return [](IRContext* context, Instruction* inst,
             const std::vector<const analysis::Constant*>& constants) {
-    assert(inst->opcode() == SpvOpIAdd && "Wrong opcode. Should be OpIAdd.");
+    assert(inst->opcode() == spv::Op::OpIAdd &&
+           "Wrong opcode. Should be OpIAdd.");
 
     uint32_t operand = std::numeric_limits<uint32_t>::max();
     const analysis::Type* operand_type = nullptr;
@@ -2531,9 +2562,9 @@
       const analysis::Type* inst_type =
           context->get_type_mgr()->GetType(inst->type_id());
       if (inst_type->IsSame(operand_type)) {
-        inst->SetOpcode(SpvOpCopyObject);
+        inst->SetOpcode(spv::Op::OpCopyObject);
       } else {
-        inst->SetOpcode(SpvOpBitcast);
+        inst->SetOpcode(spv::Op::OpBitcast);
       }
       inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {operand}}});
       return true;
@@ -2547,7 +2578,8 @@
 FoldingRule DotProductDoingExtract() {
   return [](IRContext* context, Instruction* inst,
             const std::vector<const analysis::Constant*>& constants) {
-    assert(inst->opcode() == SpvOpDot && "Wrong opcode.  Should be OpDot.");
+    assert(inst->opcode() == spv::Op::OpDot &&
+           "Wrong opcode.  Should be OpDot.");
 
     analysis::ConstantManager* const_mgr = context->get_constant_mgr();
 
@@ -2573,7 +2605,7 @@
       std::vector<const analysis::Constant*> components;
       components = constants[i]->GetVectorComponents(const_mgr);
 
-      const uint32_t kNotFound = std::numeric_limits<uint32_t>::max();
+      constexpr uint32_t kNotFound = std::numeric_limits<uint32_t>::max();
 
       uint32_t component_with_one = kNotFound;
       bool all_others_zero = true;
@@ -2606,7 +2638,7 @@
       operands.push_back(
           {SPV_OPERAND_TYPE_LITERAL_INTEGER, {component_with_one}});
 
-      inst->SetOpcode(SpvOpCompositeExtract);
+      inst->SetOpcode(spv::Op::OpCompositeExtract);
       inst->SetInOperands(std::move(operands));
       return true;
     }
@@ -2621,20 +2653,22 @@
 FoldingRule StoringUndef() {
   return [](IRContext* context, Instruction* inst,
             const std::vector<const analysis::Constant*>&) {
-    assert(inst->opcode() == SpvOpStore && "Wrong opcode.  Should be OpStore.");
+    assert(inst->opcode() == spv::Op::OpStore &&
+           "Wrong opcode.  Should be OpStore.");
 
     analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
 
     // If this is a volatile store, the store cannot be removed.
     if (inst->NumInOperands() == 3) {
-      if (inst->GetSingleWordInOperand(2) & SpvMemoryAccessVolatileMask) {
+      if (inst->GetSingleWordInOperand(2) &
+          uint32_t(spv::MemoryAccessMask::Volatile)) {
         return false;
       }
     }
 
     uint32_t object_id = inst->GetSingleWordInOperand(kStoreObjectInIdx);
     Instruction* object_inst = def_use_mgr->GetDef(object_id);
-    if (object_inst->opcode() == SpvOpUndef) {
+    if (object_inst->opcode() == spv::Op::OpUndef) {
       inst->ToNop();
       return true;
     }
@@ -2645,7 +2679,7 @@
 FoldingRule VectorShuffleFeedingShuffle() {
   return [](IRContext* context, Instruction* inst,
             const std::vector<const analysis::Constant*>&) {
-    assert(inst->opcode() == SpvOpVectorShuffle &&
+    assert(inst->opcode() == spv::Op::OpVectorShuffle &&
            "Wrong opcode.  Should be OpVectorShuffle.");
 
     analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
@@ -2658,13 +2692,13 @@
     uint32_t op0_length = op0_type->element_count();
 
     bool feeder_is_op0 = true;
-    if (feeding_shuffle_inst->opcode() != SpvOpVectorShuffle) {
+    if (feeding_shuffle_inst->opcode() != spv::Op::OpVectorShuffle) {
       feeding_shuffle_inst =
           def_use_mgr->GetDef(inst->GetSingleWordInOperand(1));
       feeder_is_op0 = false;
     }
 
-    if (feeding_shuffle_inst->opcode() != SpvOpVectorShuffle) {
+    if (feeding_shuffle_inst->opcode() != spv::Op::OpVectorShuffle) {
       return false;
     }
 
@@ -2775,7 +2809,7 @@
 FoldingRule RemoveRedundantOperands() {
   return [](IRContext*, Instruction* inst,
             const std::vector<const analysis::Constant*>&) {
-    assert(inst->opcode() == SpvOpEntryPoint &&
+    assert(inst->opcode() == spv::Op::OpEntryPoint &&
            "Wrong opcode.  Should be OpEntryPoint.");
     bool has_redundant_operand = false;
     std::unordered_set<uint32_t> seen_operands;
@@ -2808,46 +2842,56 @@
             const std::vector<const analysis::Constant*>& constants) {
     const auto opcode = inst->opcode();
     (void)opcode;
-    assert((opcode == SpvOpImageSampleImplicitLod ||
-            opcode == SpvOpImageSampleExplicitLod ||
-            opcode == SpvOpImageSampleDrefImplicitLod ||
-            opcode == SpvOpImageSampleDrefExplicitLod ||
-            opcode == SpvOpImageSampleProjImplicitLod ||
-            opcode == SpvOpImageSampleProjExplicitLod ||
-            opcode == SpvOpImageSampleProjDrefImplicitLod ||
-            opcode == SpvOpImageSampleProjDrefExplicitLod ||
-            opcode == SpvOpImageFetch || opcode == SpvOpImageGather ||
-            opcode == SpvOpImageDrefGather || opcode == SpvOpImageRead ||
-            opcode == SpvOpImageWrite ||
-            opcode == SpvOpImageSparseSampleImplicitLod ||
-            opcode == SpvOpImageSparseSampleExplicitLod ||
-            opcode == SpvOpImageSparseSampleDrefImplicitLod ||
-            opcode == SpvOpImageSparseSampleDrefExplicitLod ||
-            opcode == SpvOpImageSparseSampleProjImplicitLod ||
-            opcode == SpvOpImageSparseSampleProjExplicitLod ||
-            opcode == SpvOpImageSparseSampleProjDrefImplicitLod ||
-            opcode == SpvOpImageSparseSampleProjDrefExplicitLod ||
-            opcode == SpvOpImageSparseFetch ||
-            opcode == SpvOpImageSparseGather ||
-            opcode == SpvOpImageSparseDrefGather ||
-            opcode == SpvOpImageSparseRead) &&
+    assert((opcode == spv::Op::OpImageSampleImplicitLod ||
+            opcode == spv::Op::OpImageSampleExplicitLod ||
+            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::OpImageFetch ||
+            opcode == spv::Op::OpImageGather ||
+            opcode == spv::Op::OpImageDrefGather ||
+            opcode == spv::Op::OpImageRead || opcode == spv::Op::OpImageWrite ||
+            opcode == spv::Op::OpImageSparseSampleImplicitLod ||
+            opcode == spv::Op::OpImageSparseSampleExplicitLod ||
+            opcode == spv::Op::OpImageSparseSampleDrefImplicitLod ||
+            opcode == spv::Op::OpImageSparseSampleDrefExplicitLod ||
+            opcode == spv::Op::OpImageSparseSampleProjImplicitLod ||
+            opcode == spv::Op::OpImageSparseSampleProjExplicitLod ||
+            opcode == spv::Op::OpImageSparseSampleProjDrefImplicitLod ||
+            opcode == spv::Op::OpImageSparseSampleProjDrefExplicitLod ||
+            opcode == spv::Op::OpImageSparseFetch ||
+            opcode == spv::Op::OpImageSparseGather ||
+            opcode == spv::Op::OpImageSparseDrefGather ||
+            opcode == spv::Op::OpImageSparseRead) &&
            "Wrong opcode.  Should be an image instruction.");
 
     int32_t operand_index = ImageOperandsMaskInOperandIndex(inst);
     if (operand_index >= 0) {
       auto image_operands = inst->GetSingleWordInOperand(operand_index);
-      if (image_operands & SpvImageOperandsOffsetMask) {
+      if (image_operands & uint32_t(spv::ImageOperandsMask::Offset)) {
         uint32_t offset_operand_index = operand_index + 1;
-        if (image_operands & SpvImageOperandsBiasMask) offset_operand_index++;
-        if (image_operands & SpvImageOperandsLodMask) offset_operand_index++;
-        if (image_operands & SpvImageOperandsGradMask)
+        if (image_operands & uint32_t(spv::ImageOperandsMask::Bias))
+          offset_operand_index++;
+        if (image_operands & uint32_t(spv::ImageOperandsMask::Lod))
+          offset_operand_index++;
+        if (image_operands & uint32_t(spv::ImageOperandsMask::Grad))
           offset_operand_index += 2;
-        assert(((image_operands & SpvImageOperandsConstOffsetMask) == 0) &&
+        assert(((image_operands &
+                 uint32_t(spv::ImageOperandsMask::ConstOffset)) == 0) &&
                "Offset and ConstOffset may not be used together");
         if (offset_operand_index < inst->NumOperands()) {
           if (constants[offset_operand_index]) {
-            image_operands = image_operands | SpvImageOperandsConstOffsetMask;
-            image_operands = image_operands & ~SpvImageOperandsOffsetMask;
+            if (constants[offset_operand_index]->IsZero()) {
+              inst->RemoveInOperand(offset_operand_index);
+            } else {
+              image_operands = image_operands |
+                               uint32_t(spv::ImageOperandsMask::ConstOffset);
+            }
+            image_operands =
+                image_operands & ~uint32_t(spv::ImageOperandsMask::Offset);
             inst->SetInOperand(operand_index, {image_operands});
             return true;
           }
@@ -2866,108 +2910,119 @@
   // 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.
-  rules_[SpvOpBitcast].push_back(BitCastScalarOrVector());
+  rules_[spv::Op::OpBitcast].push_back(BitCastScalarOrVector());
 
-  rules_[SpvOpCompositeConstruct].push_back(CompositeExtractFeedingConstruct);
+  rules_[spv::Op::OpCompositeConstruct].push_back(
+      CompositeExtractFeedingConstruct);
 
-  rules_[SpvOpCompositeExtract].push_back(InsertFeedingExtract());
-  rules_[SpvOpCompositeExtract].push_back(CompositeConstructFeedingExtract);
-  rules_[SpvOpCompositeExtract].push_back(VectorShuffleFeedingExtract());
-  rules_[SpvOpCompositeExtract].push_back(FMixFeedingExtract());
+  rules_[spv::Op::OpCompositeExtract].push_back(InsertFeedingExtract());
+  rules_[spv::Op::OpCompositeExtract].push_back(
+      CompositeConstructFeedingExtract);
+  rules_[spv::Op::OpCompositeExtract].push_back(VectorShuffleFeedingExtract());
+  rules_[spv::Op::OpCompositeExtract].push_back(FMixFeedingExtract());
 
-  rules_[SpvOpCompositeInsert].push_back(CompositeInsertToCompositeConstruct);
+  rules_[spv::Op::OpCompositeInsert].push_back(
+      CompositeInsertToCompositeConstruct);
 
-  rules_[SpvOpDot].push_back(DotProductDoingExtract());
+  rules_[spv::Op::OpDot].push_back(DotProductDoingExtract());
 
-  rules_[SpvOpEntryPoint].push_back(RemoveRedundantOperands());
+  rules_[spv::Op::OpEntryPoint].push_back(RemoveRedundantOperands());
 
-  rules_[SpvOpFAdd].push_back(RedundantFAdd());
-  rules_[SpvOpFAdd].push_back(MergeAddNegateArithmetic());
-  rules_[SpvOpFAdd].push_back(MergeAddAddArithmetic());
-  rules_[SpvOpFAdd].push_back(MergeAddSubArithmetic());
-  rules_[SpvOpFAdd].push_back(MergeGenericAddSubArithmetic());
-  rules_[SpvOpFAdd].push_back(FactorAddMuls());
-  rules_[SpvOpFAdd].push_back(MergeMulAddArithmetic);
+  rules_[spv::Op::OpFAdd].push_back(RedundantFAdd());
+  rules_[spv::Op::OpFAdd].push_back(MergeAddNegateArithmetic());
+  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(MergeMulAddArithmetic);
 
-  rules_[SpvOpFDiv].push_back(RedundantFDiv());
-  rules_[SpvOpFDiv].push_back(ReciprocalFDiv());
-  rules_[SpvOpFDiv].push_back(MergeDivDivArithmetic());
-  rules_[SpvOpFDiv].push_back(MergeDivMulArithmetic());
-  rules_[SpvOpFDiv].push_back(MergeDivNegateArithmetic());
+  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_[SpvOpFMul].push_back(RedundantFMul());
-  rules_[SpvOpFMul].push_back(MergeMulMulArithmetic());
-  rules_[SpvOpFMul].push_back(MergeMulDivArithmetic());
-  rules_[SpvOpFMul].push_back(MergeMulNegateArithmetic());
+  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_[SpvOpFNegate].push_back(MergeNegateArithmetic());
-  rules_[SpvOpFNegate].push_back(MergeNegateAddSubArithmetic());
-  rules_[SpvOpFNegate].push_back(MergeNegateMulDivArithmetic());
+  rules_[spv::Op::OpFNegate].push_back(MergeNegateArithmetic());
+  rules_[spv::Op::OpFNegate].push_back(MergeNegateAddSubArithmetic());
+  rules_[spv::Op::OpFNegate].push_back(MergeNegateMulDivArithmetic());
 
-  rules_[SpvOpFSub].push_back(RedundantFSub());
-  rules_[SpvOpFSub].push_back(MergeSubNegateArithmetic());
-  rules_[SpvOpFSub].push_back(MergeSubAddArithmetic());
-  rules_[SpvOpFSub].push_back(MergeSubSubArithmetic());
-  rules_[SpvOpFSub].push_back(MergeMulSubArithmetic);
+  rules_[spv::Op::OpFSub].push_back(RedundantFSub());
+  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(MergeMulSubArithmetic);
 
-  rules_[SpvOpIAdd].push_back(RedundantIAdd());
-  rules_[SpvOpIAdd].push_back(MergeAddNegateArithmetic());
-  rules_[SpvOpIAdd].push_back(MergeAddAddArithmetic());
-  rules_[SpvOpIAdd].push_back(MergeAddSubArithmetic());
-  rules_[SpvOpIAdd].push_back(MergeGenericAddSubArithmetic());
-  rules_[SpvOpIAdd].push_back(FactorAddMuls());
+  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_[SpvOpIMul].push_back(IntMultipleBy1());
-  rules_[SpvOpIMul].push_back(MergeMulMulArithmetic());
-  rules_[SpvOpIMul].push_back(MergeMulNegateArithmetic());
+  rules_[spv::Op::OpIMul].push_back(IntMultipleBy1());
+  rules_[spv::Op::OpIMul].push_back(MergeMulMulArithmetic());
+  rules_[spv::Op::OpIMul].push_back(MergeMulNegateArithmetic());
 
-  rules_[SpvOpISub].push_back(MergeSubNegateArithmetic());
-  rules_[SpvOpISub].push_back(MergeSubAddArithmetic());
-  rules_[SpvOpISub].push_back(MergeSubSubArithmetic());
+  rules_[spv::Op::OpISub].push_back(MergeSubNegateArithmetic());
+  rules_[spv::Op::OpISub].push_back(MergeSubAddArithmetic());
+  rules_[spv::Op::OpISub].push_back(MergeSubSubArithmetic());
 
-  rules_[SpvOpPhi].push_back(RedundantPhi());
+  rules_[spv::Op::OpPhi].push_back(RedundantPhi());
 
-  rules_[SpvOpSNegate].push_back(MergeNegateArithmetic());
-  rules_[SpvOpSNegate].push_back(MergeNegateMulDivArithmetic());
-  rules_[SpvOpSNegate].push_back(MergeNegateAddSubArithmetic());
+  rules_[spv::Op::OpSNegate].push_back(MergeNegateArithmetic());
+  rules_[spv::Op::OpSNegate].push_back(MergeNegateMulDivArithmetic());
+  rules_[spv::Op::OpSNegate].push_back(MergeNegateAddSubArithmetic());
 
-  rules_[SpvOpSelect].push_back(RedundantSelect());
+  rules_[spv::Op::OpSelect].push_back(RedundantSelect());
 
-  rules_[SpvOpStore].push_back(StoringUndef());
+  rules_[spv::Op::OpStore].push_back(StoringUndef());
 
-  rules_[SpvOpVectorShuffle].push_back(VectorShuffleFeedingShuffle());
+  rules_[spv::Op::OpVectorShuffle].push_back(VectorShuffleFeedingShuffle());
 
-  rules_[SpvOpImageSampleImplicitLod].push_back(UpdateImageOperands());
-  rules_[SpvOpImageSampleExplicitLod].push_back(UpdateImageOperands());
-  rules_[SpvOpImageSampleDrefImplicitLod].push_back(UpdateImageOperands());
-  rules_[SpvOpImageSampleDrefExplicitLod].push_back(UpdateImageOperands());
-  rules_[SpvOpImageSampleProjImplicitLod].push_back(UpdateImageOperands());
-  rules_[SpvOpImageSampleProjExplicitLod].push_back(UpdateImageOperands());
-  rules_[SpvOpImageSampleProjDrefImplicitLod].push_back(UpdateImageOperands());
-  rules_[SpvOpImageSampleProjDrefExplicitLod].push_back(UpdateImageOperands());
-  rules_[SpvOpImageFetch].push_back(UpdateImageOperands());
-  rules_[SpvOpImageGather].push_back(UpdateImageOperands());
-  rules_[SpvOpImageDrefGather].push_back(UpdateImageOperands());
-  rules_[SpvOpImageRead].push_back(UpdateImageOperands());
-  rules_[SpvOpImageWrite].push_back(UpdateImageOperands());
-  rules_[SpvOpImageSparseSampleImplicitLod].push_back(UpdateImageOperands());
-  rules_[SpvOpImageSparseSampleExplicitLod].push_back(UpdateImageOperands());
-  rules_[SpvOpImageSparseSampleDrefImplicitLod].push_back(
+  rules_[spv::Op::OpImageSampleImplicitLod].push_back(UpdateImageOperands());
+  rules_[spv::Op::OpImageSampleExplicitLod].push_back(UpdateImageOperands());
+  rules_[spv::Op::OpImageSampleDrefImplicitLod].push_back(
       UpdateImageOperands());
-  rules_[SpvOpImageSparseSampleDrefExplicitLod].push_back(
+  rules_[spv::Op::OpImageSampleDrefExplicitLod].push_back(
       UpdateImageOperands());
-  rules_[SpvOpImageSparseSampleProjImplicitLod].push_back(
+  rules_[spv::Op::OpImageSampleProjImplicitLod].push_back(
       UpdateImageOperands());
-  rules_[SpvOpImageSparseSampleProjExplicitLod].push_back(
+  rules_[spv::Op::OpImageSampleProjExplicitLod].push_back(
       UpdateImageOperands());
-  rules_[SpvOpImageSparseSampleProjDrefImplicitLod].push_back(
+  rules_[spv::Op::OpImageSampleProjDrefImplicitLod].push_back(
       UpdateImageOperands());
-  rules_[SpvOpImageSparseSampleProjDrefExplicitLod].push_back(
+  rules_[spv::Op::OpImageSampleProjDrefExplicitLod].push_back(
       UpdateImageOperands());
-  rules_[SpvOpImageSparseFetch].push_back(UpdateImageOperands());
-  rules_[SpvOpImageSparseGather].push_back(UpdateImageOperands());
-  rules_[SpvOpImageSparseDrefGather].push_back(UpdateImageOperands());
-  rules_[SpvOpImageSparseRead].push_back(UpdateImageOperands());
+  rules_[spv::Op::OpImageFetch].push_back(UpdateImageOperands());
+  rules_[spv::Op::OpImageGather].push_back(UpdateImageOperands());
+  rules_[spv::Op::OpImageDrefGather].push_back(UpdateImageOperands());
+  rules_[spv::Op::OpImageRead].push_back(UpdateImageOperands());
+  rules_[spv::Op::OpImageWrite].push_back(UpdateImageOperands());
+  rules_[spv::Op::OpImageSparseSampleImplicitLod].push_back(
+      UpdateImageOperands());
+  rules_[spv::Op::OpImageSparseSampleExplicitLod].push_back(
+      UpdateImageOperands());
+  rules_[spv::Op::OpImageSparseSampleDrefImplicitLod].push_back(
+      UpdateImageOperands());
+  rules_[spv::Op::OpImageSparseSampleDrefExplicitLod].push_back(
+      UpdateImageOperands());
+  rules_[spv::Op::OpImageSparseSampleProjImplicitLod].push_back(
+      UpdateImageOperands());
+  rules_[spv::Op::OpImageSparseSampleProjExplicitLod].push_back(
+      UpdateImageOperands());
+  rules_[spv::Op::OpImageSparseSampleProjDrefImplicitLod].push_back(
+      UpdateImageOperands());
+  rules_[spv::Op::OpImageSparseSampleProjDrefExplicitLod].push_back(
+      UpdateImageOperands());
+  rules_[spv::Op::OpImageSparseFetch].push_back(UpdateImageOperands());
+  rules_[spv::Op::OpImageSparseGather].push_back(UpdateImageOperands());
+  rules_[spv::Op::OpImageSparseDrefGather].push_back(UpdateImageOperands());
+  rules_[spv::Op::OpImageSparseRead].push_back(UpdateImageOperands());
 
   FeatureManager* feature_manager = context_->get_feature_mgr();
   // Add rules for GLSLstd450
diff --git a/source/opt/folding_rules.h b/source/opt/folding_rules.h
index f1a8639..b51e0ce 100644
--- a/source/opt/folding_rules.h
+++ b/source/opt/folding_rules.h
@@ -64,7 +64,7 @@
   virtual ~FoldingRules() = default;
 
   const FoldingRuleSet& GetRulesForInstruction(Instruction* inst) const {
-    if (inst->opcode() != SpvOpExtInst) {
+    if (inst->opcode() != spv::Op::OpExtInst) {
       auto it = rules_.find(inst->opcode());
       if (it != rules_.end()) {
         return it->second;
@@ -86,8 +86,14 @@
   virtual void AddFoldingRules();
 
  protected:
+  struct hasher {
+    size_t operator()(const spv::Op& op) const noexcept {
+      return std::hash<uint32_t>()(uint32_t(op));
+    }
+  };
+
   // The folding rules for core instructions.
-  std::unordered_map<uint32_t, FoldingRuleSet> rules_;
+  std::unordered_map<spv::Op, FoldingRuleSet, hasher> rules_;
 
   // The folding rules for extended instructions.
   struct Key {
diff --git a/source/opt/freeze_spec_constant_value_pass.cpp b/source/opt/freeze_spec_constant_value_pass.cpp
index 10e98fd..3f89e56 100644
--- a/source/opt/freeze_spec_constant_value_pass.cpp
+++ b/source/opt/freeze_spec_constant_value_pass.cpp
@@ -23,21 +23,21 @@
   auto ctx = context();
   ctx->module()->ForEachInst([&modified, ctx](Instruction* inst) {
     switch (inst->opcode()) {
-      case SpvOp::SpvOpSpecConstant:
-        inst->SetOpcode(SpvOp::SpvOpConstant);
+      case spv::Op::OpSpecConstant:
+        inst->SetOpcode(spv::Op::OpConstant);
         modified = true;
         break;
-      case SpvOp::SpvOpSpecConstantTrue:
-        inst->SetOpcode(SpvOp::SpvOpConstantTrue);
+      case spv::Op::OpSpecConstantTrue:
+        inst->SetOpcode(spv::Op::OpConstantTrue);
         modified = true;
         break;
-      case SpvOp::SpvOpSpecConstantFalse:
-        inst->SetOpcode(SpvOp::SpvOpConstantFalse);
+      case spv::Op::OpSpecConstantFalse:
+        inst->SetOpcode(spv::Op::OpConstantFalse);
         modified = true;
         break;
-      case SpvOp::SpvOpDecorate:
-        if (inst->GetSingleWordInOperand(1) ==
-            SpvDecoration::SpvDecorationSpecId) {
+      case spv::Op::OpDecorate:
+        if (spv::Decoration(inst->GetSingleWordInOperand(1)) ==
+            spv::Decoration::SpecId) {
           ctx->KillInst(inst);
           modified = true;
         }
diff --git a/source/opt/function.cpp b/source/opt/function.cpp
index bb51df3..2ee88ec 100644
--- a/source/opt/function.cpp
+++ b/source/opt/function.cpp
@@ -15,9 +15,7 @@
 #include "source/opt/function.h"
 
 #include <ostream>
-#include <sstream>
 
-#include "function.h"
 #include "ir_context.h"
 #include "source/util/bit_vector.h"
 
@@ -264,7 +262,7 @@
   std::ostringstream str;
   ForEachInst([&str, options](const Instruction* inst) {
     str << inst->PrettyPrint(options);
-    if (inst->opcode() != SpvOpFunctionEnd) {
+    if (inst->opcode() != spv::Op::OpFunctionEnd) {
       str << std::endl;
     }
   });
diff --git a/source/opt/function.h b/source/opt/function.h
index 146cbe3..8c0472c 100644
--- a/source/opt/function.h
+++ b/source/opt/function.h
@@ -253,7 +253,7 @@
   auto first_empty =
       std::remove_if(std::begin(blocks_), std::end(blocks_),
                      [](const std::unique_ptr<BasicBlock>& bb) -> bool {
-                       return bb->GetLabelInst()->opcode() == SpvOpNop;
+                       return bb->GetLabelInst()->opcode() == spv::Op::OpNop;
                      });
   blocks_.erase(first_empty, std::end(blocks_));
 }
diff --git a/source/opt/graphics_robust_access_pass.cpp b/source/opt/graphics_robust_access_pass.cpp
index 4652d72..e765c39 100644
--- a/source/opt/graphics_robust_access_pass.cpp
+++ b/source/opt/graphics_robust_access_pass.cpp
@@ -141,24 +141,17 @@
 
 #include "graphics_robust_access_pass.h"
 
-#include <algorithm>
-#include <cstring>
 #include <functional>
 #include <initializer_list>
-#include <limits>
 #include <utility>
 
-#include "constants.h"
-#include "def_use_manager.h"
 #include "function.h"
 #include "ir_context.h"
-#include "module.h"
 #include "pass.h"
 #include "source/diagnostic.h"
 #include "source/util/make_unique.h"
 #include "spirv-tools/libspirv.h"
 #include "spirv/unified1/GLSL.std.450.h"
-#include "spirv/unified1/spirv.h"
 #include "type_manager.h"
 #include "types.h"
 
@@ -194,14 +187,15 @@
 
 spv_result_t GraphicsRobustAccessPass::IsCompatibleModule() {
   auto* feature_mgr = context()->get_feature_mgr();
-  if (!feature_mgr->HasCapability(SpvCapabilityShader))
+  if (!feature_mgr->HasCapability(spv::Capability::Shader))
     return Fail() << "Can only process Shader modules";
-  if (feature_mgr->HasCapability(SpvCapabilityVariablePointers))
+  if (feature_mgr->HasCapability(spv::Capability::VariablePointers))
     return Fail() << "Can't process modules with VariablePointers capability";
-  if (feature_mgr->HasCapability(SpvCapabilityVariablePointersStorageBuffer))
+  if (feature_mgr->HasCapability(
+          spv::Capability::VariablePointersStorageBuffer))
     return Fail() << "Can't process modules with VariablePointersStorageBuffer "
                      "capability";
-  if (feature_mgr->HasCapability(SpvCapabilityRuntimeDescriptorArrayEXT)) {
+  if (feature_mgr->HasCapability(spv::Capability::RuntimeDescriptorArrayEXT)) {
     // These have a RuntimeArray outside of Block-decorated struct.  There
     // is no way to compute the array length from within SPIR-V.
     return Fail() << "Can't process modules with RuntimeDescriptorArrayEXT "
@@ -210,8 +204,9 @@
 
   {
     auto* inst = context()->module()->GetMemoryModel();
-    const auto addressing_model = inst->GetSingleWordOperand(0);
-    if (addressing_model != SpvAddressingModelLogical)
+    const auto addressing_model =
+        spv::AddressingModel(inst->GetSingleWordOperand(0));
+    if (addressing_model != spv::AddressingModel::Logical)
       return Fail() << "Addressing model must be Logical.  Found "
                     << inst->PrettyPrint();
   }
@@ -237,11 +232,11 @@
   for (auto& block : *function) {
     for (auto& inst : block) {
       switch (inst.opcode()) {
-        case SpvOpAccessChain:
-        case SpvOpInBoundsAccessChain:
+        case spv::Op::OpAccessChain:
+        case spv::Op::OpInBoundsAccessChain:
           access_chains.push_back(&inst);
           break;
-        case SpvOpImageTexelPointer:
+        case spv::Op::OpImageTexelPointer:
           image_texel_pointers.push_back(&inst);
           break;
         default:
@@ -268,7 +263,7 @@
   auto* def_use_mgr = context()->get_def_use_mgr();
   auto* type_mgr = context()->get_type_mgr();
   const bool have_int64_cap =
-      context()->get_feature_mgr()->HasCapability(SpvCapabilityInt64);
+      context()->get_feature_mgr()->HasCapability(spv::Capability::Int64);
 
   // Replaces one of the OpAccessChain index operands with a new value.
   // Updates def-use analysis.
@@ -451,7 +446,7 @@
       // It doesn't matter if 1 is signed or unsigned.
       auto* one = GetValueForType(1, wider_type);
       auto* count_minus_1 = InsertInst(
-          &inst, SpvOpISub, type_mgr->GetId(wider_type), TakeNextId(),
+          &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()}}});
       auto* zero = GetValueForType(0, wider_type);
@@ -486,15 +481,15 @@
     Instruction* index_inst = GetDef(index_id);
 
     switch (pointee_type->opcode()) {
-      case SpvOpTypeMatrix:  // Use column count
-      case SpvOpTypeVector:  // Use component count
+      case spv::Op::OpTypeMatrix:  // Use column count
+      case spv::Op::OpTypeVector:  // Use component count
       {
         const uint32_t count = pointee_type->GetSingleWordOperand(2);
         clamp_to_literal_count(idx, count);
         pointee_type = GetDef(pointee_type->GetSingleWordOperand(1));
       } break;
 
-      case SpvOpTypeArray: {
+      case spv::Op::OpTypeArray: {
         // The array length can be a spec constant, so go through the general
         // case.
         Instruction* array_len = GetDef(pointee_type->GetSingleWordOperand(2));
@@ -502,11 +497,11 @@
         pointee_type = GetDef(pointee_type->GetSingleWordOperand(1));
       } break;
 
-      case SpvOpTypeStruct: {
+      case spv::Op::OpTypeStruct: {
         // SPIR-V requires the index to be an OpConstant.
         // We need to know the index literal value so we can compute the next
         // pointee type.
-        if (index_inst->opcode() != SpvOpConstant ||
+        if (index_inst->opcode() != spv::Op::OpConstant ||
             !constant_mgr->GetConstantFromInst(index_inst)
                  ->type()
                  ->AsInteger()) {
@@ -537,7 +532,7 @@
         // No need to clamp this index.  We just checked that it's valid.
       } break;
 
-      case SpvOpTypeRuntimeArray: {
+      case spv::Op::OpTypeRuntimeArray: {
         auto* array_len = MakeRuntimeArrayLengthInst(&inst, idx);
         if (!array_len) {  // We've already signaled an error.
           return;
@@ -571,16 +566,16 @@
       module_status_.glsl_insts_id = TakeNextId();
       std::vector<uint32_t> words = spvtools::utils::MakeVector(glsl);
       auto import_inst = MakeUnique<Instruction>(
-          context(), SpvOpExtInstImport, 0, module_status_.glsl_insts_id,
+          context(), spv::Op::OpExtInstImport, 0, module_status_.glsl_insts_id,
           std::initializer_list<Operand>{
               Operand{SPV_OPERAND_TYPE_LITERAL_STRING, std::move(words)}});
       Instruction* inst = import_inst.get();
       context()->module()->AddExtInstImport(std::move(import_inst));
       module_status_.modified = true;
       context()->AnalyzeDefUse(inst);
-      // Reanalyze the feature list, since we added an extended instruction
-      // set improt.
-      context()->get_feature_mgr()->Analyze(context()->module());
+      // Invalidates the feature manager, since we added an extended instruction
+      // set import.
+      context()->ResetFeatureManager();
     }
   }
   return module_status_.glsl_insts_id;
@@ -609,8 +604,8 @@
   auto type_id = context()->get_type_mgr()->GetId(unsigned_type);
   auto conversion_id = TakeNextId();
   auto* conversion = InsertInst(
-      before_inst, (sign_extend ? SpvOpSConvert : SpvOpUConvert), type_id,
-      conversion_id, {{SPV_OPERAND_TYPE_ID, {value->result_id()}}});
+      before_inst, (sign_extend ? spv::Op::OpSConvert : spv::Op::OpUConvert),
+      type_id, conversion_id, {{SPV_OPERAND_TYPE_ID, {value->result_id()}}});
   return conversion;
 }
 
@@ -628,7 +623,7 @@
   (void)xwidth;
   (void)ywidth;
   auto* smin_inst = InsertInst(
-      where, SpvOpExtInst, x->type_id(), smin_id,
+      where, spv::Op::OpExtInst, x->type_id(), smin_id,
       {
           {SPV_OPERAND_TYPE_ID, {glsl_insts_id}},
           {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, {GLSLstd450UMin}},
@@ -655,7 +650,7 @@
   (void)minwidth;
   (void)maxwidth;
   auto* clamp_inst = InsertInst(
-      where, SpvOpExtInst, x->type_id(), clamp_id,
+      where, spv::Op::OpExtInst, x->type_id(), clamp_id,
       {
           {SPV_OPERAND_TYPE_ID, {glsl_insts_id}},
           {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, {GLSLstd450SClamp}},
@@ -689,13 +684,13 @@
   Instruction* pointer_to_containing_struct = nullptr;
   while (steps_remaining > 0) {
     switch (current_access_chain->opcode()) {
-      case SpvOpCopyObject:
+      case spv::Op::OpCopyObject:
         // Whoops. Walk right through this one.
         current_access_chain =
             GetDef(current_access_chain->GetSingleWordInOperand(0));
         break;
-      case SpvOpAccessChain:
-      case SpvOpInBoundsAccessChain: {
+      case spv::Op::OpAccessChain:
+      case spv::Op::OpInBoundsAccessChain: {
         const int first_index_operand = 3;
         // How many indices in this access chain contribute to getting us
         // to an element in the runtime array?
@@ -793,7 +788,8 @@
   analysis::Integer uint_type_for_query(32, false);
   auto* uint_type = type_mgr->GetRegisteredType(&uint_type_for_query);
   auto* array_len = InsertInst(
-      access_chain, SpvOpArrayLength, type_mgr->GetId(uint_type), array_len_id,
+      access_chain, spv::Op::OpArrayLength, type_mgr->GetId(uint_type),
+      array_len_id,
       {{SPV_OPERAND_TYPE_ID, {pointer_to_containing_struct->result_id()}},
        {SPV_OPERAND_TYPE_LITERAL_INTEGER, {member_index_of_runtime_array}}});
   return array_len;
@@ -839,11 +835,11 @@
 
   // Declare the ImageQuery capability if the module doesn't already have it.
   auto* feature_mgr = context()->get_feature_mgr();
-  if (!feature_mgr->HasCapability(SpvCapabilityImageQuery)) {
+  if (!feature_mgr->HasCapability(spv::Capability::ImageQuery)) {
     auto cap = MakeUnique<Instruction>(
-        context(), SpvOpCapability, 0, 0,
+        context(), spv::Op::OpCapability, 0, 0,
         std::initializer_list<Operand>{
-            {SPV_OPERAND_TYPE_CAPABILITY, {SpvCapabilityImageQuery}}});
+            {SPV_OPERAND_TYPE_CAPABILITY, {spv::Capability::ImageQuery}}});
     def_use_mgr->AnalyzeInstDefUse(cap.get());
     context()->AddCapability(std::move(cap));
     feature_mgr->Analyze(context()->module());
@@ -890,21 +886,21 @@
     const int arrayness_bonus = arrayed ? 1 : 0;
     int num_coords = 0;
     switch (dim) {
-      case SpvDimBuffer:
+      case spv::Dim::Buffer:
       case SpvDim1D:
         num_coords = 1;
         break;
-      case SpvDimCube:
+      case spv::Dim::Cube:
         // For cube, we need bounds for x, y, but not face.
-      case SpvDimRect:
+      case spv::Dim::Rect:
       case SpvDim2D:
         num_coords = 2;
         break;
       case SpvDim3D:
         num_coords = 3;
         break;
-      case SpvDimSubpassData:
-      case SpvDimMax:
+      case spv::Dim::SubpassData:
+      case spv::Dim::Max:
         return Fail() << "Invalid image dimension for OpImageTexelPointer: "
                       << int(dim);
         break;
@@ -941,12 +937,12 @@
 
   const uint32_t image_id = TakeNextId();
   auto* image =
-      InsertInst(image_texel_pointer, SpvOpLoad, image_type_id, image_id,
+      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();
   auto* query_size =
-      InsertInst(image_texel_pointer, SpvOpImageQuerySize,
+      InsertInst(image_texel_pointer, spv::Op::OpImageQuerySize,
                  type_mgr->GetTypeInstruction(query_size_type), query_size_id,
                  {{SPV_OPERAND_TYPE_ID, {image->result_id()}}});
 
@@ -962,7 +958,7 @@
   // in the face index ranging from 0 through 5. The inclusive upper bound
   // on the third coordinate therefore is multiplied by 6.
   auto* query_size_including_faces = query_size;
-  if (arrayed && (dim == SpvDimCube)) {
+  if (arrayed && (dim == spv::Dim::Cube)) {
     // Multiply the last coordinate by 6.
     auto* component_6 = constant_mgr->GetConstant(coord_component_type, {6});
     const uint32_t component_6_id =
@@ -974,7 +970,7 @@
         constant_mgr->GetDefiningInstruction(multiplicand);
     const auto query_size_including_faces_id = TakeNextId();
     query_size_including_faces = InsertInst(
-        image_texel_pointer, SpvOpIMul,
+        image_texel_pointer, spv::Op::OpIMul,
         type_mgr->GetTypeInstruction(query_size_type),
         query_size_including_faces_id,
         {{SPV_OPERAND_TYPE_ID, {query_size_including_faces->result_id()}},
@@ -998,7 +994,7 @@
 
   const uint32_t query_max_including_faces_id = TakeNextId();
   auto* query_max_including_faces = InsertInst(
-      image_texel_pointer, SpvOpISub,
+      image_texel_pointer, spv::Op::OpISub,
       type_mgr->GetTypeInstruction(query_size_type),
       query_max_including_faces_id,
       {{SPV_OPERAND_TYPE_ID, {query_size_including_faces->result_id()}},
@@ -1016,12 +1012,12 @@
     // Get the sample count via OpImageQuerySamples
     const auto query_samples_id = TakeNextId();
     auto* query_samples = InsertInst(
-        image_texel_pointer, SpvOpImageQuerySamples,
+        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();
-    auto* max_samples = InsertInst(image_texel_pointer, SpvOpImageQuerySamples,
+    auto* max_samples = InsertInst(image_texel_pointer, spv::Op::OpImageQuerySamples,
                                    query_samples->type_id(), max_samples_id,
                                    {{SPV_OPERAND_TYPE_ID, {query_samples_id}},
                                     {SPV_OPERAND_TYPE_ID, {component_1_id}}});
@@ -1043,7 +1039,7 @@
 }
 
 opt::Instruction* GraphicsRobustAccessPass::InsertInst(
-    opt::Instruction* where_inst, SpvOp opcode, uint32_t type_id,
+    opt::Instruction* where_inst, spv::Op opcode, uint32_t type_id,
     uint32_t result_id, const Instruction::OperandList& operands) {
   module_status_.modified = true;
   auto* result = where_inst->InsertBefore(
diff --git a/source/opt/graphics_robust_access_pass.h b/source/opt/graphics_robust_access_pass.h
index 8f4c9dc..a7ffe11 100644
--- a/source/opt/graphics_robust_access_pass.h
+++ b/source/opt/graphics_robust_access_pass.h
@@ -133,7 +133,7 @@
   // Returns a new instruction inserted before |where_inst|, and created from
   // the remaining arguments. Registers the definitions and uses of the new
   // instruction and also records its block.
-  opt::Instruction* InsertInst(opt::Instruction* where_inst, SpvOp opcode,
+  opt::Instruction* InsertInst(opt::Instruction* where_inst, spv::Op opcode,
                                uint32_t type_id, uint32_t result_id,
                                const Instruction::OperandList& operands);
 
diff --git a/source/opt/if_conversion.cpp b/source/opt/if_conversion.cpp
index 1232796..5912cf1 100644
--- a/source/opt/if_conversion.cpp
+++ b/source/opt/if_conversion.cpp
@@ -23,7 +23,7 @@
 namespace opt {
 
 Pass::Status IfConversion::Process() {
-  if (!context()->get_feature_mgr()->HasCapability(SpvCapabilityShader)) {
+  if (!context()->get_feature_mgr()->HasCapability(spv::Capability::Shader)) {
     return Status::SuccessWithoutChange;
   }
 
@@ -40,7 +40,7 @@
 
       // Get an insertion point.
       auto iter = block.begin();
-      while (iter != block.end() && iter->opcode() == SpvOpPhi) {
+      while (iter != block.end() && iter->opcode() == spv::Op::OpPhi) {
         ++iter;
       }
 
@@ -171,23 +171,26 @@
   *common = dominators->CommonDominator(inc0, inc1);
   if (!*common || cfg()->IsPseudoEntryBlock(*common)) return false;
   Instruction* branch = (*common)->terminator();
-  if (branch->opcode() != SpvOpBranchConditional) return false;
+  if (branch->opcode() != spv::Op::OpBranchConditional) return false;
   auto merge = (*common)->GetMergeInst();
-  if (!merge || merge->opcode() != SpvOpSelectionMerge) return false;
-  if (merge->GetSingleWordInOperand(1) == SpvSelectionControlDontFlattenMask)
+  if (!merge || merge->opcode() != spv::Op::OpSelectionMerge) return false;
+  if (spv::SelectionControlMask(merge->GetSingleWordInOperand(1)) ==
+      spv::SelectionControlMask::DontFlatten) {
     return false;
+  }
   if ((*common)->MergeBlockIdIfAny() != block->id()) return false;
 
   return true;
 }
 
 bool IfConversion::CheckPhiUsers(Instruction* phi, BasicBlock* block) {
-  return get_def_use_mgr()->WhileEachUser(phi, [block,
-                                                this](Instruction* user) {
-    if (user->opcode() == SpvOpPhi && context()->get_instr_block(user) == block)
-      return false;
-    return true;
-  });
+  return get_def_use_mgr()->WhileEachUser(
+      phi, [block, this](Instruction* user) {
+        if (user->opcode() == spv::Op::OpPhi &&
+            context()->get_instr_block(user) == block)
+          return false;
+        return true;
+      });
 }
 
 uint32_t IfConversion::SplatCondition(analysis::Vector* vec_data_ty,
@@ -207,9 +210,9 @@
 
 bool IfConversion::CheckType(uint32_t id) {
   Instruction* type = get_def_use_mgr()->GetDef(id);
-  SpvOp op = type->opcode();
-  if (spvOpcodeIsScalarType(op) || op == SpvOpTypePointer ||
-      op == SpvOpTypeVector)
+  spv::Op op = type->opcode();
+  if (spvOpcodeIsScalarType(op) || op == spv::Op::OpTypePointer ||
+      op == spv::Op::OpTypeVector)
     return true;
   return false;
 }
@@ -255,7 +258,7 @@
       });
 
   Instruction* insertion_pos = target_block->terminator();
-  if ((insertion_pos)->PreviousNode()->opcode() == SpvOpSelectionMerge) {
+  if ((insertion_pos)->PreviousNode()->opcode() == spv::Op::OpSelectionMerge) {
     insertion_pos = insertion_pos->PreviousNode();
   }
   inst->RemoveFromList();
diff --git a/source/opt/inline_opaque_pass.cpp b/source/opt/inline_opaque_pass.cpp
index fe9c679..90a4c22 100644
--- a/source/opt/inline_opaque_pass.cpp
+++ b/source/opt/inline_opaque_pass.cpp
@@ -21,26 +21,24 @@
 namespace spvtools {
 namespace opt {
 namespace {
-
-const uint32_t kTypePointerTypeIdInIdx = 1;
-
-}  // anonymous namespace
+constexpr uint32_t kTypePointerTypeIdInIdx = 1;
+}  // namespace
 
 bool InlineOpaquePass::IsOpaqueType(uint32_t typeId) {
   const Instruction* typeInst = get_def_use_mgr()->GetDef(typeId);
   switch (typeInst->opcode()) {
-    case SpvOpTypeSampler:
-    case SpvOpTypeImage:
-    case SpvOpTypeSampledImage:
+    case spv::Op::OpTypeSampler:
+    case spv::Op::OpTypeImage:
+    case spv::Op::OpTypeSampledImage:
       return true;
-    case SpvOpTypePointer:
+    case spv::Op::OpTypePointer:
       return IsOpaqueType(
           typeInst->GetSingleWordInOperand(kTypePointerTypeIdInIdx));
     default:
       break;
   }
   // TODO(greg-lunarg): Handle arrays containing opaque type
-  if (typeInst->opcode() != SpvOpTypeStruct) return false;
+  if (typeInst->opcode() != spv::Op::OpTypeStruct) return false;
   // Return true if any member is opaque
   return !typeInst->WhileEachInId([this](const uint32_t* tid) {
     if (IsOpaqueType(*tid)) return false;
diff --git a/source/opt/inline_pass.cpp b/source/opt/inline_pass.cpp
index e14516f..3f160b2 100644
--- a/source/opt/inline_pass.cpp
+++ b/source/opt/inline_pass.cpp
@@ -23,24 +23,24 @@
 #include "source/opt/reflect.h"
 #include "source/util/make_unique.h"
 
-// Indices of operands in SPIR-V instructions
-
-static const int kSpvFunctionCallFunctionId = 2;
-static const int kSpvFunctionCallArgumentId = 3;
-static const int kSpvReturnValueId = 0;
-
 namespace spvtools {
 namespace opt {
+namespace {
+// Indices of operands in SPIR-V instructions
+constexpr int kSpvFunctionCallFunctionId = 2;
+constexpr int kSpvFunctionCallArgumentId = 3;
+constexpr int kSpvReturnValueId = 0;
+}  // namespace
 
 uint32_t InlinePass::AddPointerToType(uint32_t type_id,
-                                      SpvStorageClass storage_class) {
+                                      spv::StorageClass storage_class) {
   uint32_t resultId = context()->TakeNextId();
   if (resultId == 0) {
     return resultId;
   }
 
   std::unique_ptr<Instruction> type_inst(
-      new Instruction(context(), SpvOpTypePointer, 0, resultId,
+      new Instruction(context(), spv::Op::OpTypePointer, 0, resultId,
                       {{spv_operand_type_t::SPV_OPERAND_TYPE_STORAGE_CLASS,
                         {uint32_t(storage_class)}},
                        {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {type_id}}}));
@@ -48,8 +48,8 @@
   analysis::Type* pointeeTy;
   std::unique_ptr<analysis::Pointer> pointerTy;
   std::tie(pointeeTy, pointerTy) =
-      context()->get_type_mgr()->GetTypeAndPointerType(type_id,
-                                                       SpvStorageClassFunction);
+      context()->get_type_mgr()->GetTypeAndPointerType(
+          type_id, spv::StorageClass::Function);
   context()->get_type_mgr()->RegisterType(resultId, *pointerTy);
   return resultId;
 }
@@ -57,7 +57,7 @@
 void InlinePass::AddBranch(uint32_t label_id,
                            std::unique_ptr<BasicBlock>* block_ptr) {
   std::unique_ptr<Instruction> newBranch(
-      new Instruction(context(), SpvOpBranch, 0, 0,
+      new Instruction(context(), spv::Op::OpBranch, 0, 0,
                       {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {label_id}}}));
   (*block_ptr)->AddInstruction(std::move(newBranch));
 }
@@ -66,7 +66,7 @@
                                uint32_t false_id,
                                std::unique_ptr<BasicBlock>* block_ptr) {
   std::unique_ptr<Instruction> newBranch(
-      new Instruction(context(), SpvOpBranchConditional, 0, 0,
+      new Instruction(context(), spv::Op::OpBranchConditional, 0, 0,
                       {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {cond_id}},
                        {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {true_id}},
                        {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {false_id}}}));
@@ -76,7 +76,7 @@
 void InlinePass::AddLoopMerge(uint32_t merge_id, uint32_t continue_id,
                               std::unique_ptr<BasicBlock>* block_ptr) {
   std::unique_ptr<Instruction> newLoopMerge(new Instruction(
-      context(), SpvOpLoopMerge, 0, 0,
+      context(), spv::Op::OpLoopMerge, 0, 0,
       {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {merge_id}},
        {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {continue_id}},
        {spv_operand_type_t::SPV_OPERAND_TYPE_LOOP_CONTROL, {0}}}));
@@ -88,7 +88,7 @@
                           const Instruction* line_inst,
                           const DebugScope& dbg_scope) {
   std::unique_ptr<Instruction> newStore(
-      new Instruction(context(), SpvOpStore, 0, 0,
+      new Instruction(context(), spv::Op::OpStore, 0, 0,
                       {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {ptr_id}},
                        {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {val_id}}}));
   if (line_inst != nullptr) {
@@ -103,7 +103,7 @@
                          const Instruction* line_inst,
                          const DebugScope& dbg_scope) {
   std::unique_ptr<Instruction> newLoad(
-      new Instruction(context(), SpvOpLoad, type_id, resultId,
+      new Instruction(context(), spv::Op::OpLoad, type_id, resultId,
                       {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {ptr_id}}}));
   if (line_inst != nullptr) {
     newLoad->AddDebugLine(line_inst);
@@ -114,27 +114,27 @@
 
 std::unique_ptr<Instruction> InlinePass::NewLabel(uint32_t label_id) {
   std::unique_ptr<Instruction> newLabel(
-      new Instruction(context(), SpvOpLabel, 0, label_id, {}));
+      new Instruction(context(), spv::Op::OpLabel, 0, label_id, {}));
   return newLabel;
 }
 
 uint32_t InlinePass::GetFalseId() {
   if (false_id_ != 0) return false_id_;
-  false_id_ = get_module()->GetGlobalValue(SpvOpConstantFalse);
+  false_id_ = get_module()->GetGlobalValue(spv::Op::OpConstantFalse);
   if (false_id_ != 0) return false_id_;
-  uint32_t boolId = get_module()->GetGlobalValue(SpvOpTypeBool);
+  uint32_t boolId = get_module()->GetGlobalValue(spv::Op::OpTypeBool);
   if (boolId == 0) {
     boolId = context()->TakeNextId();
     if (boolId == 0) {
       return 0;
     }
-    get_module()->AddGlobalValue(SpvOpTypeBool, boolId, 0);
+    get_module()->AddGlobalValue(spv::Op::OpTypeBool, boolId, 0);
   }
   false_id_ = context()->TakeNextId();
   if (false_id_ == 0) {
     return 0;
   }
-  get_module()->AddGlobalValue(SpvOpConstantFalse, false_id_, boolId);
+  get_module()->AddGlobalValue(spv::Op::OpConstantFalse, false_id_, boolId);
   return false_id_;
 }
 
@@ -157,10 +157,10 @@
     analysis::DebugInlinedAtContext* inlined_at_ctx) {
   auto callee_block_itr = calleeFn->begin();
   auto callee_var_itr = callee_block_itr->begin();
-  while (callee_var_itr->opcode() == SpvOp::SpvOpVariable ||
+  while (callee_var_itr->opcode() == spv::Op::OpVariable ||
          callee_var_itr->GetCommonDebugOpcode() ==
              CommonDebugInfoDebugDeclare) {
-    if (callee_var_itr->opcode() != SpvOp::SpvOpVariable) {
+    if (callee_var_itr->opcode() != spv::Op::OpVariable) {
       ++callee_var_itr;
       continue;
     }
@@ -191,10 +191,11 @@
          "Cannot create a return variable of type void.");
   // Find or create ptr to callee return type.
   uint32_t returnVarTypeId =
-      type_mgr->FindPointerToType(calleeTypeId, SpvStorageClassFunction);
+      type_mgr->FindPointerToType(calleeTypeId, spv::StorageClass::Function);
 
   if (returnVarTypeId == 0) {
-    returnVarTypeId = AddPointerToType(calleeTypeId, SpvStorageClassFunction);
+    returnVarTypeId =
+        AddPointerToType(calleeTypeId, spv::StorageClass::Function);
     if (returnVarTypeId == 0) {
       return 0;
     }
@@ -206,17 +207,18 @@
     return 0;
   }
 
-  std::unique_ptr<Instruction> var_inst(
-      new Instruction(context(), SpvOpVariable, returnVarTypeId, returnVarId,
-                      {{spv_operand_type_t::SPV_OPERAND_TYPE_STORAGE_CLASS,
-                        {SpvStorageClassFunction}}}));
+  std::unique_ptr<Instruction> var_inst(new Instruction(
+      context(), spv::Op::OpVariable, returnVarTypeId, returnVarId,
+      {{spv_operand_type_t::SPV_OPERAND_TYPE_STORAGE_CLASS,
+        {(uint32_t)spv::StorageClass::Function}}}));
   new_vars->push_back(std::move(var_inst));
   get_decoration_mgr()->CloneDecorations(calleeFn->result_id(), returnVarId);
   return returnVarId;
 }
 
 bool InlinePass::IsSameBlockOp(const Instruction* inst) const {
-  return inst->opcode() == SpvOpSampledImage || inst->opcode() == SpvOpImage;
+  return inst->opcode() == spv::Op::OpSampledImage ||
+         inst->opcode() == spv::Op::OpImage;
 }
 
 bool InlinePass::CloneSameBlockOps(
@@ -299,9 +301,9 @@
     std::unique_ptr<BasicBlock>* new_blk_ptr,
     UptrVectorIterator<BasicBlock> callee_first_block_itr) {
   auto callee_itr = callee_first_block_itr->begin();
-  while (callee_itr->opcode() == SpvOp::SpvOpVariable ||
+  while (callee_itr->opcode() == spv::Op::OpVariable ||
          callee_itr->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare) {
-    if (callee_itr->opcode() == SpvOp::SpvOpVariable &&
+    if (callee_itr->opcode() == spv::Op::OpVariable &&
         callee_itr->NumInOperands() == 2) {
       assert(callee2caller.count(callee_itr->result_id()) &&
              "Expected the variable to have already been mapped.");
@@ -330,7 +332,8 @@
     BasicBlock* new_blk_ptr, const Instruction* inst, uint32_t dbg_inlined_at) {
   // If we have return, it must be at the end of the callee. We will handle
   // it at the end.
-  if (inst->opcode() == SpvOpReturnValue || inst->opcode() == SpvOpReturn)
+  if (inst->opcode() == spv::Op::OpReturnValue ||
+      inst->opcode() == spv::Op::OpReturn)
     return true;
 
   // Copy callee instruction and remap all input Ids.
@@ -366,7 +369,7 @@
     analysis::DebugInlinedAtContext* inlined_at_ctx, Function* calleeFn,
     const Instruction* inst, uint32_t returnVarId) {
   // Store return value to return variable.
-  if (inst->opcode() == SpvOpReturnValue) {
+  if (inst->opcode() == spv::Op::OpReturnValue) {
     assert(returnVarId != 0);
     uint32_t valId = inst->GetInOperand(kSpvReturnValueId).words[0];
     const auto mapItr = callee2caller.find(valId);
@@ -388,7 +391,8 @@
   }
   if (returnLabelId == 0) return new_blk_ptr;
 
-  if (inst->opcode() == SpvOpReturn || inst->opcode() == SpvOpReturnValue)
+  if (inst->opcode() == spv::Op::OpReturn ||
+      inst->opcode() == spv::Op::OpReturnValue)
     AddBranch(returnLabelId, &new_blk_ptr);
   new_blocks->push_back(std::move(new_blk_ptr));
   return MakeUnique<BasicBlock>(NewLabel(returnLabelId));
@@ -499,7 +503,7 @@
   // Insert a modified copy of the loop merge into the first block.
   auto loop_merge_itr = last->tail();
   --loop_merge_itr;
-  assert(loop_merge_itr->opcode() == SpvOpLoopMerge);
+  assert(loop_merge_itr->opcode() == spv::Op::OpLoopMerge);
   std::unique_ptr<Instruction> cp_inst(loop_merge_itr->Clone(context()));
   first->tail().InsertBefore(std::move(cp_inst));
 
@@ -696,7 +700,7 @@
 }
 
 bool InlinePass::IsInlinableFunctionCall(const Instruction* inst) {
-  if (inst->opcode() != SpvOp::SpvOpFunctionCall) return false;
+  if (inst->opcode() != spv::Op::OpFunctionCall) return false;
   const uint32_t calleeFnId =
       inst->GetSingleWordOperand(kSpvFunctionCallFunctionId);
   const auto ci = inlinable_.find(calleeFnId);
@@ -738,7 +742,7 @@
 bool InlinePass::HasNoReturnInLoop(Function* func) {
   // If control not structured, do not do loop/return analysis
   // TODO: Analyze returns in non-structured control flow
-  if (!context()->get_feature_mgr()->HasCapability(SpvCapabilityShader))
+  if (!context()->get_feature_mgr()->HasCapability(spv::Capability::Shader))
     return false;
   const auto structured_analysis = context()->GetStructuredCFGAnalysis();
   // Search for returns in structured construct.
@@ -776,7 +780,7 @@
   if (func->cbegin() == func->cend()) return false;
 
   // Do not inline functions with DontInline flag.
-  if (func->control_mask() & SpvFunctionControlDontInlineMask) {
+  if (func->control_mask() & uint32_t(spv::FunctionControlMask::DontInline)) {
     return false;
   }
 
@@ -811,7 +815,7 @@
 
 bool InlinePass::ContainsAbortOtherThanUnreachable(Function* func) const {
   return !func->WhileEachInst([](Instruction* inst) {
-    return inst->opcode() == SpvOpUnreachable ||
+    return inst->opcode() == spv::Op::OpUnreachable ||
            !spvOpcodeIsAbort(inst->opcode());
   });
 }
diff --git a/source/opt/inline_pass.h b/source/opt/inline_pass.h
index d29c1e0..1c9d60e 100644
--- a/source/opt/inline_pass.h
+++ b/source/opt/inline_pass.h
@@ -44,7 +44,7 @@
 
   // Add pointer to type to module and return resultId.  Returns 0 if the type
   // could not be created.
-  uint32_t AddPointerToType(uint32_t type_id, SpvStorageClass storage_class);
+  uint32_t AddPointerToType(uint32_t type_id, spv::StorageClass storage_class);
 
   // Add unconditional branch to labelId to end of block block_ptr.
   void AddBranch(uint32_t labelId, std::unique_ptr<BasicBlock>* block_ptr);
diff --git a/source/opt/inst_bindless_check_pass.cpp b/source/opt/inst_bindless_check_pass.cpp
index c2c5d6c..8e7d4f8 100644
--- a/source/opt/inst_bindless_check_pass.cpp
+++ b/source/opt/inst_bindless_check_pass.cpp
@@ -16,88 +16,128 @@
 
 #include "inst_bindless_check_pass.h"
 
-namespace {
-
-// Input Operand Indices
-static const int kSpvImageSampleImageIdInIdx = 0;
-static const int kSpvSampledImageImageIdInIdx = 0;
-static const int kSpvSampledImageSamplerIdInIdx = 1;
-static const int kSpvImageSampledImageIdInIdx = 0;
-static const int kSpvCopyObjectOperandIdInIdx = 0;
-static const int kSpvLoadPtrIdInIdx = 0;
-static const int kSpvAccessChainBaseIdInIdx = 0;
-static const int kSpvAccessChainIndex0IdInIdx = 1;
-static const int kSpvTypeArrayTypeIdInIdx = 0;
-static const int kSpvTypeArrayLengthIdInIdx = 1;
-static const int kSpvConstantValueInIdx = 0;
-static const int kSpvVariableStorageClassInIdx = 0;
-static const int kSpvTypePtrTypeIdInIdx = 1;
-static const int kSpvTypeImageDim = 1;
-static const int kSpvTypeImageDepth = 2;
-static const int kSpvTypeImageArrayed = 3;
-static const int kSpvTypeImageMS = 4;
-static const int kSpvTypeImageSampled = 5;
-}  // anonymous namespace
+#include "source/spirv_constant.h"
 
 namespace spvtools {
 namespace opt {
+namespace {
+// Input Operand Indices
+constexpr int kSpvImageSampleImageIdInIdx = 0;
+constexpr int kSpvSampledImageImageIdInIdx = 0;
+constexpr int kSpvSampledImageSamplerIdInIdx = 1;
+constexpr int kSpvImageSampledImageIdInIdx = 0;
+constexpr int kSpvCopyObjectOperandIdInIdx = 0;
+constexpr int kSpvLoadPtrIdInIdx = 0;
+constexpr int kSpvAccessChainBaseIdInIdx = 0;
+constexpr int kSpvAccessChainIndex0IdInIdx = 1;
+constexpr int kSpvTypeArrayTypeIdInIdx = 0;
+constexpr int kSpvVariableStorageClassInIdx = 0;
+constexpr int kSpvTypePtrTypeIdInIdx = 1;
+constexpr int kSpvTypeImageDim = 1;
+constexpr int kSpvTypeImageDepth = 2;
+constexpr int kSpvTypeImageArrayed = 3;
+constexpr int kSpvTypeImageMS = 4;
+}  // namespace
 
-uint32_t InstBindlessCheckPass::GenDebugReadLength(
-    uint32_t var_id, InstructionBuilder* builder) {
-  uint32_t desc_set_idx =
-      var2desc_set_[var_id] + kDebugInputBindlessOffsetLengths;
-  uint32_t desc_set_idx_id = builder->GetUintConstantId(desc_set_idx);
-  uint32_t binding_idx_id = builder->GetUintConstantId(var2binding_[var_id]);
-  return GenDebugDirectRead({desc_set_idx_id, binding_idx_id}, builder);
+// This is a stub function for use with Import linkage
+// clang-format off
+// GLSL:
+//bool inst_bindless_check_desc(const uint shader_id, const uint inst_num, const uvec4 stage_info, const uint desc_set,
+//                              const uint binding, const uint desc_index, const uint byte_offset) {
+//}
+// clang-format on
+uint32_t InstBindlessCheckPass::GenDescCheckFunctionId() {
+  enum {
+    kShaderId = 0,
+    kInstructionIndex = 1,
+    kStageInfo = 2,
+    kDescSet = 3,
+    kDescBinding = 4,
+    kDescIndex = 5,
+    kByteOffset = 6,
+    kNumArgs
+  };
+  if (check_desc_func_id_ != 0) {
+    return check_desc_func_id_;
+  }
+
+  analysis::TypeManager* type_mgr = context()->get_type_mgr();
+  const analysis::Integer* uint_type = GetInteger(32, false);
+  const analysis::Vector v4uint(uint_type, 4);
+  const analysis::Type* v4uint_type = type_mgr->GetRegisteredType(&v4uint);
+  std::vector<const analysis::Type*> param_types(kNumArgs, uint_type);
+  param_types[2] = v4uint_type;
+
+  const uint32_t func_id = TakeNextId();
+  std::unique_ptr<Function> func =
+      StartFunction(func_id, type_mgr->GetBoolType(), param_types);
+
+  func->SetFunctionEnd(EndFunction());
+
+  static const std::string func_name{"inst_bindless_check_desc"};
+  context()->AddFunctionDeclaration(std::move(func));
+  context()->AddDebug2Inst(NewName(func_id, func_name));
+  std::vector<Operand> operands{
+      {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {func_id}},
+      {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER,
+       {uint32_t(spv::Decoration::LinkageAttributes)}},
+      {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_STRING,
+       utils::MakeVector(func_name.c_str())},
+      {spv_operand_type_t::SPV_OPERAND_TYPE_LINKAGE_TYPE,
+       {uint32_t(spv::LinkageType::Import)}},
+  };
+  get_decoration_mgr()->AddDecoration(spv::Op::OpDecorate, operands);
+
+  check_desc_func_id_ = func_id;
+  // Make sure function doesn't get processed by
+  // InstrumentPass::InstProcessCallTreeFromRoots()
+  param2output_func_id_[3] = func_id;
+  return check_desc_func_id_;
 }
 
-uint32_t InstBindlessCheckPass::GenDebugReadInit(uint32_t var_id,
-                                                 uint32_t desc_idx_id,
-                                                 InstructionBuilder* builder) {
-  uint32_t binding_idx_id = builder->GetUintConstantId(var2binding_[var_id]);
-  uint32_t u_desc_idx_id = GenUintCastCode(desc_idx_id, builder);
-  // If desc index checking is not enabled, we know the offset of initialization
-  // entries is 1, so we can avoid loading this value and just add 1 to the
-  // descriptor set.
-  if (!desc_idx_enabled_) {
-    uint32_t desc_set_idx_id =
-        builder->GetUintConstantId(var2desc_set_[var_id] + 1);
-    return GenDebugDirectRead({desc_set_idx_id, binding_idx_id, u_desc_idx_id},
-                              builder);
-  } else {
-    uint32_t desc_set_base_id =
-        builder->GetUintConstantId(kDebugInputBindlessInitOffset);
-    uint32_t desc_set_idx_id =
-        builder->GetUintConstantId(var2desc_set_[var_id]);
-    return GenDebugDirectRead(
-        {desc_set_base_id, desc_set_idx_id, binding_idx_id, u_desc_idx_id},
-        builder);
-  }
+// clang-format off
+// GLSL:
+// result = inst_bindless_check_desc(shader_id, inst_idx, stage_info, desc_set, binding, desc_idx, offset);
+//
+// clang-format on
+uint32_t InstBindlessCheckPass::GenDescCheckCall(
+    uint32_t inst_idx, uint32_t stage_idx, uint32_t var_id,
+    uint32_t desc_idx_id, uint32_t offset_id, InstructionBuilder* builder) {
+  const uint32_t func_id = GenDescCheckFunctionId();
+  const std::vector<uint32_t> args = {
+      builder->GetUintConstantId(shader_id_),
+      builder->GetUintConstantId(inst_idx),
+      GenStageInfo(stage_idx, builder),
+      builder->GetUintConstantId(var2desc_set_[var_id]),
+      builder->GetUintConstantId(var2binding_[var_id]),
+      GenUintCastCode(desc_idx_id, builder),
+      offset_id};
+  return GenReadFunctionCall(GetBoolId(), func_id, args, builder);
 }
 
 uint32_t InstBindlessCheckPass::CloneOriginalImage(
     uint32_t old_image_id, InstructionBuilder* builder) {
   Instruction* new_image_inst;
   Instruction* old_image_inst = get_def_use_mgr()->GetDef(old_image_id);
-  if (old_image_inst->opcode() == SpvOpLoad) {
+  if (old_image_inst->opcode() == spv::Op::OpLoad) {
     new_image_inst = builder->AddLoad(
         old_image_inst->type_id(),
         old_image_inst->GetSingleWordInOperand(kSpvLoadPtrIdInIdx));
-  } else if (old_image_inst->opcode() == SpvOp::SpvOpSampledImage) {
+  } else if (old_image_inst->opcode() == spv::Op::OpSampledImage) {
     uint32_t clone_id = CloneOriginalImage(
         old_image_inst->GetSingleWordInOperand(kSpvSampledImageImageIdInIdx),
         builder);
     new_image_inst = builder->AddBinaryOp(
-        old_image_inst->type_id(), SpvOpSampledImage, clone_id,
+        old_image_inst->type_id(), spv::Op::OpSampledImage, clone_id,
         old_image_inst->GetSingleWordInOperand(kSpvSampledImageSamplerIdInIdx));
-  } else if (old_image_inst->opcode() == SpvOp::SpvOpImage) {
+  } else if (old_image_inst->opcode() == spv::Op::OpImage) {
     uint32_t clone_id = CloneOriginalImage(
         old_image_inst->GetSingleWordInOperand(kSpvImageSampledImageIdInIdx),
         builder);
-    new_image_inst =
-        builder->AddUnaryOp(old_image_inst->type_id(), SpvOpImage, clone_id);
+    new_image_inst = builder->AddUnaryOp(old_image_inst->type_id(),
+                                         spv::Op::OpImage, clone_id);
   } else {
-    assert(old_image_inst->opcode() == SpvOp::SpvOpCopyObject &&
+    assert(old_image_inst->opcode() == spv::Op::OpCopyObject &&
            "expecting OpCopyObject");
     uint32_t clone_id = CloneOriginalImage(
         old_image_inst->GetSingleWordInOperand(kSpvCopyObjectOperandIdInIdx),
@@ -143,38 +183,38 @@
 
 uint32_t InstBindlessCheckPass::GetImageId(Instruction* inst) {
   switch (inst->opcode()) {
-    case SpvOp::SpvOpImageSampleImplicitLod:
-    case SpvOp::SpvOpImageSampleExplicitLod:
-    case SpvOp::SpvOpImageSampleDrefImplicitLod:
-    case SpvOp::SpvOpImageSampleDrefExplicitLod:
-    case SpvOp::SpvOpImageSampleProjImplicitLod:
-    case SpvOp::SpvOpImageSampleProjExplicitLod:
-    case SpvOp::SpvOpImageSampleProjDrefImplicitLod:
-    case SpvOp::SpvOpImageSampleProjDrefExplicitLod:
-    case SpvOp::SpvOpImageGather:
-    case SpvOp::SpvOpImageDrefGather:
-    case SpvOp::SpvOpImageQueryLod:
-    case SpvOp::SpvOpImageSparseSampleImplicitLod:
-    case SpvOp::SpvOpImageSparseSampleExplicitLod:
-    case SpvOp::SpvOpImageSparseSampleDrefImplicitLod:
-    case SpvOp::SpvOpImageSparseSampleDrefExplicitLod:
-    case SpvOp::SpvOpImageSparseSampleProjImplicitLod:
-    case SpvOp::SpvOpImageSparseSampleProjExplicitLod:
-    case SpvOp::SpvOpImageSparseSampleProjDrefImplicitLod:
-    case SpvOp::SpvOpImageSparseSampleProjDrefExplicitLod:
-    case SpvOp::SpvOpImageSparseGather:
-    case SpvOp::SpvOpImageSparseDrefGather:
-    case SpvOp::SpvOpImageFetch:
-    case SpvOp::SpvOpImageRead:
-    case SpvOp::SpvOpImageQueryFormat:
-    case SpvOp::SpvOpImageQueryOrder:
-    case SpvOp::SpvOpImageQuerySizeLod:
-    case SpvOp::SpvOpImageQuerySize:
-    case SpvOp::SpvOpImageQueryLevels:
-    case SpvOp::SpvOpImageQuerySamples:
-    case SpvOp::SpvOpImageSparseFetch:
-    case SpvOp::SpvOpImageSparseRead:
-    case SpvOp::SpvOpImageWrite:
+    case spv::Op::OpImageSampleImplicitLod:
+    case spv::Op::OpImageSampleExplicitLod:
+    case spv::Op::OpImageSampleDrefImplicitLod:
+    case spv::Op::OpImageSampleDrefExplicitLod:
+    case spv::Op::OpImageSampleProjImplicitLod:
+    case spv::Op::OpImageSampleProjExplicitLod:
+    case spv::Op::OpImageSampleProjDrefImplicitLod:
+    case spv::Op::OpImageSampleProjDrefExplicitLod:
+    case spv::Op::OpImageGather:
+    case spv::Op::OpImageDrefGather:
+    case spv::Op::OpImageQueryLod:
+    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::OpImageSparseGather:
+    case spv::Op::OpImageSparseDrefGather:
+    case spv::Op::OpImageFetch:
+    case spv::Op::OpImageRead:
+    case spv::Op::OpImageQueryFormat:
+    case spv::Op::OpImageQueryOrder:
+    case spv::Op::OpImageQuerySizeLod:
+    case spv::Op::OpImageQuerySize:
+    case spv::Op::OpImageQueryLevels:
+    case spv::Op::OpImageQuerySamples:
+    case spv::Op::OpImageSparseFetch:
+    case spv::Op::OpImageSparseRead:
+    case spv::Op::OpImageWrite:
       return inst->GetSingleWordInOperand(kSpvImageSampleImageIdInIdx);
     default:
       break;
@@ -190,56 +230,58 @@
 bool InstBindlessCheckPass::AnalyzeDescriptorReference(Instruction* ref_inst,
                                                        RefAnalysis* ref) {
   ref->ref_inst = ref_inst;
-  if (ref_inst->opcode() == SpvOpLoad || ref_inst->opcode() == SpvOpStore) {
+  if (ref_inst->opcode() == spv::Op::OpLoad ||
+      ref_inst->opcode() == spv::Op::OpStore) {
     ref->desc_load_id = 0;
     ref->ptr_id = ref_inst->GetSingleWordInOperand(kSpvLoadPtrIdInIdx);
     Instruction* ptr_inst = get_def_use_mgr()->GetDef(ref->ptr_id);
-    if (ptr_inst->opcode() != SpvOp::SpvOpAccessChain) return false;
+    if (ptr_inst->opcode() != spv::Op::OpAccessChain) return false;
     ref->var_id = ptr_inst->GetSingleWordInOperand(kSpvAccessChainBaseIdInIdx);
     Instruction* var_inst = get_def_use_mgr()->GetDef(ref->var_id);
-    if (var_inst->opcode() != SpvOp::SpvOpVariable) return false;
-    uint32_t storage_class =
-        var_inst->GetSingleWordInOperand(kSpvVariableStorageClassInIdx);
+    if (var_inst->opcode() != spv::Op::OpVariable) return false;
+    spv::StorageClass storage_class = spv::StorageClass(
+        var_inst->GetSingleWordInOperand(kSpvVariableStorageClassInIdx));
     switch (storage_class) {
-      case SpvStorageClassUniform:
-      case SpvStorageClassStorageBuffer:
+      case spv::StorageClass::Uniform:
+      case spv::StorageClass::StorageBuffer:
         break;
       default:
         return false;
         break;
     }
     // Check for deprecated storage block form
-    if (storage_class == SpvStorageClassUniform) {
+    if (storage_class == spv::StorageClass::Uniform) {
       uint32_t var_ty_id = var_inst->type_id();
       Instruction* var_ty_inst = get_def_use_mgr()->GetDef(var_ty_id);
       uint32_t ptr_ty_id =
           var_ty_inst->GetSingleWordInOperand(kSpvTypePtrTypeIdInIdx);
       Instruction* ptr_ty_inst = get_def_use_mgr()->GetDef(ptr_ty_id);
-      SpvOp ptr_ty_op = ptr_ty_inst->opcode();
+      spv::Op ptr_ty_op = ptr_ty_inst->opcode();
       uint32_t block_ty_id =
-          (ptr_ty_op == SpvOpTypeArray || ptr_ty_op == SpvOpTypeRuntimeArray)
+          (ptr_ty_op == spv::Op::OpTypeArray ||
+           ptr_ty_op == spv::Op::OpTypeRuntimeArray)
               ? ptr_ty_inst->GetSingleWordInOperand(kSpvTypeArrayTypeIdInIdx)
               : ptr_ty_id;
       assert(get_def_use_mgr()->GetDef(block_ty_id)->opcode() ==
-                 SpvOpTypeStruct &&
+                 spv::Op::OpTypeStruct &&
              "unexpected block type");
       bool block_found = get_decoration_mgr()->FindDecoration(
-          block_ty_id, SpvDecorationBlock,
+          block_ty_id, uint32_t(spv::Decoration::Block),
           [](const Instruction&) { return true; });
       if (!block_found) {
         // If block decoration not found, verify deprecated form of SSBO
         bool buffer_block_found = get_decoration_mgr()->FindDecoration(
-            block_ty_id, SpvDecorationBufferBlock,
+            block_ty_id, uint32_t(spv::Decoration::BufferBlock),
             [](const Instruction&) { return true; });
         USE_ASSERT(buffer_block_found && "block decoration not found");
-        storage_class = SpvStorageClassStorageBuffer;
+        storage_class = spv::StorageClass::StorageBuffer;
       }
     }
-    ref->strg_class = storage_class;
+    ref->strg_class = uint32_t(storage_class);
     Instruction* desc_type_inst = GetPointeeTypeInst(var_inst);
     switch (desc_type_inst->opcode()) {
-      case SpvOpTypeArray:
-      case SpvOpTypeRuntimeArray:
+      case spv::Op::OpTypeArray:
+      case spv::Op::OpTypeRuntimeArray:
         // A load through a descriptor array will have at least 3 operands. We
         // do not want to instrument loads of descriptors here which are part of
         // an image-based reference.
@@ -248,9 +290,18 @@
             ptr_inst->GetSingleWordInOperand(kSpvAccessChainIndex0IdInIdx);
         break;
       default:
-        ref->desc_idx_id = 0;
         break;
     }
+    auto decos =
+        context()->get_decoration_mgr()->GetDecorationsFor(ref->var_id, false);
+    for (const auto& deco : decos) {
+      spv::Decoration d = spv::Decoration(deco->GetSingleWordInOperand(1u));
+      if (d == spv::Decoration::DescriptorSet) {
+        ref->set = deco->GetSingleWordInOperand(2u);
+      } else if (d == spv::Decoration::Binding) {
+        ref->binding = deco->GetSingleWordInOperand(2u);
+      }
+    }
     return true;
   }
   // Reference is not load or store. If not an image-based reference, return.
@@ -261,29 +312,29 @@
   Instruction* desc_load_inst;
   for (;;) {
     desc_load_inst = get_def_use_mgr()->GetDef(desc_load_id);
-    if (desc_load_inst->opcode() == SpvOp::SpvOpSampledImage)
+    if (desc_load_inst->opcode() == spv::Op::OpSampledImage)
       desc_load_id =
           desc_load_inst->GetSingleWordInOperand(kSpvSampledImageImageIdInIdx);
-    else if (desc_load_inst->opcode() == SpvOp::SpvOpImage)
+    else if (desc_load_inst->opcode() == spv::Op::OpImage)
       desc_load_id =
           desc_load_inst->GetSingleWordInOperand(kSpvImageSampledImageIdInIdx);
-    else if (desc_load_inst->opcode() == SpvOp::SpvOpCopyObject)
+    else if (desc_load_inst->opcode() == spv::Op::OpCopyObject)
       desc_load_id =
           desc_load_inst->GetSingleWordInOperand(kSpvCopyObjectOperandIdInIdx);
     else
       break;
   }
-  if (desc_load_inst->opcode() != SpvOp::SpvOpLoad) {
+  if (desc_load_inst->opcode() != spv::Op::OpLoad) {
     // TODO(greg-lunarg): Handle additional possibilities?
     return false;
   }
   ref->desc_load_id = desc_load_id;
   ref->ptr_id = desc_load_inst->GetSingleWordInOperand(kSpvLoadPtrIdInIdx);
   Instruction* ptr_inst = get_def_use_mgr()->GetDef(ref->ptr_id);
-  if (ptr_inst->opcode() == SpvOp::SpvOpVariable) {
+  if (ptr_inst->opcode() == spv::Op::OpVariable) {
     ref->desc_idx_id = 0;
     ref->var_id = ref->ptr_id;
-  } else if (ptr_inst->opcode() == SpvOp::SpvOpAccessChain) {
+  } else if (ptr_inst->opcode() == spv::Op::OpAccessChain) {
     if (ptr_inst->NumInOperands() != 2) {
       assert(false && "unexpected bindless index number");
       return false;
@@ -292,7 +343,7 @@
         ptr_inst->GetSingleWordInOperand(kSpvAccessChainIndex0IdInIdx);
     ref->var_id = ptr_inst->GetSingleWordInOperand(kSpvAccessChainBaseIdInIdx);
     Instruction* var_inst = get_def_use_mgr()->GetDef(ref->var_id);
-    if (var_inst->opcode() != SpvOpVariable) {
+    if (var_inst->opcode() != spv::Op::OpVariable) {
       assert(false && "unexpected bindless base");
       return false;
     }
@@ -300,6 +351,16 @@
     // TODO(greg-lunarg): Handle additional possibilities?
     return false;
   }
+  auto decos =
+      context()->get_decoration_mgr()->GetDecorationsFor(ref->var_id, false);
+  for (const auto& deco : decos) {
+    spv::Decoration d = spv::Decoration(deco->GetSingleWordInOperand(1u));
+    if (d == spv::Decoration::DescriptorSet) {
+      ref->set = deco->GetSingleWordInOperand(2u);
+    } else if (d == spv::Decoration::Binding) {
+      ref->binding = deco->GetSingleWordInOperand(2u);
+    }
+  }
   return true;
 }
 
@@ -369,13 +430,13 @@
   uint32_t buff_ty_id;
   uint32_t ac_in_idx = 1;
   switch (desc_ty_inst->opcode()) {
-    case SpvOpTypeArray:
-    case SpvOpTypeRuntimeArray:
+    case spv::Op::OpTypeArray:
+    case spv::Op::OpTypeRuntimeArray:
       buff_ty_id = desc_ty_inst->GetSingleWordInOperand(0);
       ++ac_in_idx;
       break;
     default:
-      assert(desc_ty_inst->opcode() == SpvOpTypeStruct &&
+      assert(desc_ty_inst->opcode() == spv::Op::OpTypeStruct &&
              "unexpected descriptor type");
       buff_ty_id = desc_ty_inst->result_id();
       break;
@@ -393,19 +454,20 @@
     Instruction* curr_ty_inst = get_def_use_mgr()->GetDef(curr_ty_id);
     uint32_t curr_offset_id = 0;
     switch (curr_ty_inst->opcode()) {
-      case SpvOpTypeArray:
-      case SpvOpTypeRuntimeArray: {
+      case spv::Op::OpTypeArray:
+      case spv::Op::OpTypeRuntimeArray: {
         // Get array stride and multiply by current index
-        uint32_t arr_stride = FindStride(curr_ty_id, SpvDecorationArrayStride);
+        uint32_t arr_stride =
+            FindStride(curr_ty_id, uint32_t(spv::Decoration::ArrayStride));
         uint32_t arr_stride_id = builder->GetUintConstantId(arr_stride);
         uint32_t curr_idx_32b_id = Gen32BitCvtCode(curr_idx_id, builder);
         Instruction* curr_offset_inst = builder->AddBinaryOp(
-            GetUintId(), SpvOpIMul, arr_stride_id, curr_idx_32b_id);
+            GetUintId(), spv::Op::OpIMul, arr_stride_id, curr_idx_32b_id);
         curr_offset_id = curr_offset_inst->result_id();
         // Get element type for next step
         curr_ty_id = curr_ty_inst->GetSingleWordInOperand(0);
       } break;
-      case SpvOpTypeMatrix: {
+      case spv::Op::OpTypeMatrix: {
         assert(matrix_stride != 0 && "missing matrix stride");
         matrix_stride_id = builder->GetUintConstantId(matrix_stride);
         uint32_t vec_ty_id = curr_ty_inst->GetSingleWordInOperand(0);
@@ -423,40 +485,40 @@
         }
         uint32_t curr_idx_32b_id = Gen32BitCvtCode(curr_idx_id, builder);
         Instruction* curr_offset_inst = builder->AddBinaryOp(
-            GetUintId(), SpvOpIMul, col_stride_id, curr_idx_32b_id);
+            GetUintId(), spv::Op::OpIMul, col_stride_id, curr_idx_32b_id);
         curr_offset_id = curr_offset_inst->result_id();
         // Get element type for next step
         curr_ty_id = vec_ty_id;
         in_matrix = true;
       } break;
-      case SpvOpTypeVector: {
+      case spv::Op::OpTypeVector: {
         // If inside a row major matrix type, multiply index by matrix stride,
         // else multiply by component size
         uint32_t comp_ty_id = curr_ty_inst->GetSingleWordInOperand(0u);
         uint32_t curr_idx_32b_id = Gen32BitCvtCode(curr_idx_id, builder);
         if (in_matrix && !col_major) {
           Instruction* curr_offset_inst = builder->AddBinaryOp(
-              GetUintId(), SpvOpIMul, matrix_stride_id, curr_idx_32b_id);
+              GetUintId(), spv::Op::OpIMul, matrix_stride_id, curr_idx_32b_id);
           curr_offset_id = curr_offset_inst->result_id();
         } else {
           uint32_t comp_ty_sz = ByteSize(comp_ty_id, 0u, false, false);
           uint32_t comp_ty_sz_id = builder->GetUintConstantId(comp_ty_sz);
           Instruction* curr_offset_inst = builder->AddBinaryOp(
-              GetUintId(), SpvOpIMul, comp_ty_sz_id, curr_idx_32b_id);
+              GetUintId(), spv::Op::OpIMul, comp_ty_sz_id, curr_idx_32b_id);
           curr_offset_id = curr_offset_inst->result_id();
         }
         // Get element type for next step
         curr_ty_id = comp_ty_id;
       } break;
-      case SpvOpTypeStruct: {
+      case spv::Op::OpTypeStruct: {
         // Get buffer byte offset for the referenced member
         Instruction* curr_idx_inst = get_def_use_mgr()->GetDef(curr_idx_id);
-        assert(curr_idx_inst->opcode() == SpvOpConstant &&
+        assert(curr_idx_inst->opcode() == spv::Op::OpConstant &&
                "unexpected struct index");
         uint32_t member_idx = curr_idx_inst->GetSingleWordInOperand(0);
         uint32_t member_offset = 0xdeadbeef;
         bool found = get_decoration_mgr()->FindDecoration(
-            curr_ty_id, SpvDecorationOffset,
+            curr_ty_id, uint32_t(spv::Decoration::Offset),
             [&member_idx, &member_offset](const Instruction& deco_inst) {
               if (deco_inst.GetSingleWordInOperand(1u) != member_idx)
                 return false;
@@ -470,7 +532,7 @@
         // enclosing struct type at the member index. If none found, reset
         // stride to 0.
         found = get_decoration_mgr()->FindDecoration(
-            curr_ty_id, SpvDecorationMatrixStride,
+            curr_ty_id, uint32_t(spv::Decoration::MatrixStride),
             [&member_idx, &matrix_stride](const Instruction& deco_inst) {
               if (deco_inst.GetSingleWordInOperand(1u) != member_idx)
                 return false;
@@ -480,7 +542,7 @@
         if (!found) matrix_stride = 0;
         // Look for column major decoration
         found = get_decoration_mgr()->FindDecoration(
-            curr_ty_id, SpvDecorationColMajor,
+            curr_ty_id, uint32_t(spv::Decoration::ColMajor),
             [&member_idx, &col_major](const Instruction& deco_inst) {
               if (deco_inst.GetSingleWordInOperand(1u) != member_idx)
                 return false;
@@ -497,7 +559,7 @@
       sum_id = curr_offset_id;
     else {
       Instruction* sum_inst =
-          builder->AddBinaryOp(GetUintId(), SpvOpIAdd, sum_id, curr_offset_id);
+          builder->AddIAdd(GetUintId(), sum_id, curr_offset_id);
       sum_id = sum_inst->result_id();
     }
     ++ac_in_idx;
@@ -506,14 +568,12 @@
   uint32_t bsize = ByteSize(curr_ty_id, matrix_stride, col_major, in_matrix);
   uint32_t last = bsize - 1;
   uint32_t last_id = builder->GetUintConstantId(last);
-  Instruction* sum_inst =
-      builder->AddBinaryOp(GetUintId(), SpvOpIAdd, sum_id, last_id);
+  Instruction* sum_inst = builder->AddIAdd(GetUintId(), sum_id, last_id);
   return sum_inst->result_id();
 }
 
 void InstBindlessCheckPass::GenCheckCode(
-    uint32_t check_id, uint32_t error_id, uint32_t offset_id,
-    uint32_t length_id, uint32_t stage_idx, RefAnalysis* ref,
+    uint32_t check_id, RefAnalysis* ref,
     std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
   BasicBlock* back_blk_ptr = &*new_blocks->back();
   InstructionBuilder builder(
@@ -527,44 +587,39 @@
   std::unique_ptr<Instruction> merge_label(NewLabel(merge_blk_id));
   std::unique_ptr<Instruction> valid_label(NewLabel(valid_blk_id));
   std::unique_ptr<Instruction> invalid_label(NewLabel(invalid_blk_id));
-  (void)builder.AddConditionalBranch(check_id, valid_blk_id, invalid_blk_id,
-                                     merge_blk_id, SpvSelectionControlMaskNone);
+  (void)builder.AddConditionalBranch(
+      check_id, valid_blk_id, invalid_blk_id, merge_blk_id,
+      uint32_t(spv::SelectionControlMask::MaskNone));
   // Gen valid bounds branch
   std::unique_ptr<BasicBlock> new_blk_ptr(
       new BasicBlock(std::move(valid_label)));
   builder.SetInsertPoint(&*new_blk_ptr);
   uint32_t new_ref_id = CloneOriginalReference(ref, &builder);
+  uint32_t null_id = 0;
+  uint32_t ref_type_id = ref->ref_inst->type_id();
   (void)builder.AddBranch(merge_blk_id);
   new_blocks->push_back(std::move(new_blk_ptr));
   // Gen invalid block
   new_blk_ptr.reset(new BasicBlock(std::move(invalid_label)));
   builder.SetInsertPoint(&*new_blk_ptr);
-  uint32_t u_index_id = GenUintCastCode(ref->desc_idx_id, &builder);
-  if (offset_id != 0) {
-    // Buffer OOB
-    uint32_t u_offset_id = GenUintCastCode(offset_id, &builder);
-    uint32_t u_length_id = GenUintCastCode(length_id, &builder);
-    GenDebugStreamWrite(uid2offset_[ref->ref_inst->unique_id()], stage_idx,
-                        {error_id, u_index_id, u_offset_id, u_length_id},
-                        &builder);
-  } else if (buffer_bounds_enabled_ || texel_buffer_enabled_) {
-    // Uninitialized Descriptor - Return additional unused zero so all error
-    // modes will use same debug stream write function
-    uint32_t u_length_id = GenUintCastCode(length_id, &builder);
-    GenDebugStreamWrite(
-        uid2offset_[ref->ref_inst->unique_id()], stage_idx,
-        {error_id, u_index_id, u_length_id, builder.GetUintConstantId(0)},
-        &builder);
-  } else {
-    // Uninitialized Descriptor - Normal error return
-    uint32_t u_length_id = GenUintCastCode(length_id, &builder);
-    GenDebugStreamWrite(uid2offset_[ref->ref_inst->unique_id()], stage_idx,
-                        {error_id, u_index_id, u_length_id}, &builder);
+
+  // Generate a ConstantNull, converting to uint64 if the type cannot be a null.
+  if (new_ref_id != 0) {
+    analysis::TypeManager* type_mgr = context()->get_type_mgr();
+    analysis::Type* ref_type = type_mgr->GetType(ref_type_id);
+    if (ref_type->AsPointer() != nullptr) {
+      context()->AddCapability(spv::Capability::Int64);
+      uint32_t null_u64_id = GetNullId(GetUint64Id());
+      Instruction* null_ptr_inst = builder.AddUnaryOp(
+          ref_type_id, spv::Op::OpConvertUToPtr, null_u64_id);
+      null_id = null_ptr_inst->result_id();
+    } else {
+      null_id = GetNullId(ref_type_id);
+    }
   }
   // Remember last invalid block id
   uint32_t last_invalid_blk_id = new_blk_ptr->GetLabelInst()->result_id();
   // Gen zero for invalid  reference
-  uint32_t ref_type_id = ref->ref_inst->type_id();
   (void)builder.AddBranch(merge_blk_id);
   new_blocks->push_back(std::move(new_blk_ptr));
   // Gen merge block
@@ -575,8 +630,7 @@
   // reference.
   if (new_ref_id != 0) {
     Instruction* phi_inst = builder.AddPhi(
-        ref_type_id, {new_ref_id, valid_blk_id, GetNullId(ref_type_id),
-                      last_invalid_blk_id});
+        ref_type_id, {new_ref_id, valid_blk_id, null_id, last_invalid_blk_id});
     context()->ReplaceAllUsesWith(ref->ref_inst->result_id(),
                                   phi_inst->result_id());
   }
@@ -584,186 +638,67 @@
   context()->KillInst(ref->ref_inst);
 }
 
-void InstBindlessCheckPass::GenDescIdxCheckCode(
-    BasicBlock::iterator ref_inst_itr,
-    UptrVectorIterator<BasicBlock> ref_block_itr, uint32_t stage_idx,
-    std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
-  // Look for reference through indexed descriptor. If found, analyze and
-  // save components. If not, return.
-  RefAnalysis ref;
-  if (!AnalyzeDescriptorReference(&*ref_inst_itr, &ref)) return;
-  Instruction* ptr_inst = get_def_use_mgr()->GetDef(ref.ptr_id);
-  if (ptr_inst->opcode() != SpvOp::SpvOpAccessChain) return;
-  // If index and bound both compile-time constants and index < bound,
-  // return without changing
-  Instruction* var_inst = get_def_use_mgr()->GetDef(ref.var_id);
-  Instruction* desc_type_inst = GetPointeeTypeInst(var_inst);
-  uint32_t length_id = 0;
-  if (desc_type_inst->opcode() == SpvOpTypeArray) {
-    length_id =
-        desc_type_inst->GetSingleWordInOperand(kSpvTypeArrayLengthIdInIdx);
-    Instruction* index_inst = get_def_use_mgr()->GetDef(ref.desc_idx_id);
-    Instruction* length_inst = get_def_use_mgr()->GetDef(length_id);
-    if (index_inst->opcode() == SpvOpConstant &&
-        length_inst->opcode() == SpvOpConstant &&
-        index_inst->GetSingleWordInOperand(kSpvConstantValueInIdx) <
-            length_inst->GetSingleWordInOperand(kSpvConstantValueInIdx))
-      return;
-  } else if (!desc_idx_enabled_ ||
-             desc_type_inst->opcode() != SpvOpTypeRuntimeArray) {
-    return;
-  }
-  // Move original block's preceding instructions into first new block
-  std::unique_ptr<BasicBlock> new_blk_ptr;
-  MovePreludeCode(ref_inst_itr, ref_block_itr, &new_blk_ptr);
-  InstructionBuilder builder(
-      context(), &*new_blk_ptr,
-      IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
-  new_blocks->push_back(std::move(new_blk_ptr));
-  uint32_t error_id = builder.GetUintConstantId(kInstErrorBindlessBounds);
-  // If length id not yet set, descriptor array is runtime size so
-  // generate load of length from stage's debug input buffer.
-  if (length_id == 0) {
-    assert(desc_type_inst->opcode() == SpvOpTypeRuntimeArray &&
-           "unexpected bindless type");
-    length_id = GenDebugReadLength(ref.var_id, &builder);
-  }
-  // Generate full runtime bounds test code with true branch
-  // being full reference and false branch being debug output and zero
-  // for the referenced value.
-  uint32_t desc_idx_32b_id = Gen32BitCvtCode(ref.desc_idx_id, &builder);
-  uint32_t length_32b_id = Gen32BitCvtCode(length_id, &builder);
-  Instruction* ult_inst = builder.AddBinaryOp(GetBoolId(), SpvOpULessThan,
-                                              desc_idx_32b_id, length_32b_id);
-  ref.desc_idx_id = desc_idx_32b_id;
-  GenCheckCode(ult_inst->result_id(), error_id, 0u, length_id, stage_idx, &ref,
-               new_blocks);
-  // Move original block's remaining code into remainder/merge block and add
-  // to new blocks
-  BasicBlock* back_blk_ptr = &*new_blocks->back();
-  MovePostludeCode(ref_block_itr, back_blk_ptr);
-}
-
-void InstBindlessCheckPass::GenDescInitCheckCode(
+void InstBindlessCheckPass::GenDescCheckCode(
     BasicBlock::iterator ref_inst_itr,
     UptrVectorIterator<BasicBlock> ref_block_itr, uint32_t stage_idx,
     std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
   // Look for reference through descriptor. If not, return.
   RefAnalysis ref;
   if (!AnalyzeDescriptorReference(&*ref_inst_itr, &ref)) return;
+  std::unique_ptr<BasicBlock> new_blk_ptr;
+  // Move original block's preceding instructions into first new block
+  MovePreludeCode(ref_inst_itr, ref_block_itr, &new_blk_ptr);
+  InstructionBuilder builder(
+      context(), &*new_blk_ptr,
+      IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
+  new_blocks->push_back(std::move(new_blk_ptr));
   // Determine if we can only do initialization check
-  bool init_check = false;
-  if (ref.desc_load_id != 0 || !buffer_bounds_enabled_) {
-    init_check = true;
+  uint32_t ref_id = builder.GetUintConstantId(0u);
+  spv::Op op = ref.ref_inst->opcode();
+  if (ref.desc_load_id != 0) {
+    uint32_t num_in_oprnds = ref.ref_inst->NumInOperands();
+    if ((op == spv::Op::OpImageRead && num_in_oprnds == 2) ||
+        (op == spv::Op::OpImageFetch && num_in_oprnds == 2) ||
+        (op == spv::Op::OpImageWrite && num_in_oprnds == 3)) {
+      Instruction* image_inst = get_def_use_mgr()->GetDef(ref.image_id);
+      uint32_t image_ty_id = image_inst->type_id();
+      Instruction* image_ty_inst = get_def_use_mgr()->GetDef(image_ty_id);
+      if (spv::Dim(image_ty_inst->GetSingleWordInOperand(kSpvTypeImageDim)) ==
+          spv::Dim::Buffer) {
+        if ((image_ty_inst->GetSingleWordInOperand(kSpvTypeImageDepth) == 0) &&
+            (image_ty_inst->GetSingleWordInOperand(kSpvTypeImageArrayed) ==
+             0) &&
+            (image_ty_inst->GetSingleWordInOperand(kSpvTypeImageMS) == 0)) {
+          ref_id = GenUintCastCode(ref.ref_inst->GetSingleWordInOperand(1),
+                                   &builder);
+        }
+      }
+    }
   } else {
     // For now, only do bounds check for non-aggregate types. Otherwise
     // just do descriptor initialization check.
     // TODO(greg-lunarg): Do bounds check for aggregate loads and stores
     Instruction* ref_ptr_inst = get_def_use_mgr()->GetDef(ref.ptr_id);
     Instruction* pte_type_inst = GetPointeeTypeInst(ref_ptr_inst);
-    uint32_t pte_type_op = pte_type_inst->opcode();
-    if (pte_type_op == SpvOpTypeArray || pte_type_op == SpvOpTypeRuntimeArray ||
-        pte_type_op == SpvOpTypeStruct)
-      init_check = true;
+    spv::Op pte_type_op = pte_type_inst->opcode();
+    if (pte_type_op != spv::Op::OpTypeArray &&
+        pte_type_op != spv::Op::OpTypeRuntimeArray &&
+        pte_type_op != spv::Op::OpTypeStruct) {
+      ref_id = GenLastByteIdx(&ref, &builder);
+    }
   }
-  // If initialization check and not enabled, return
-  if (init_check && !desc_init_enabled_) return;
-  // Move original block's preceding instructions into first new block
-  std::unique_ptr<BasicBlock> new_blk_ptr;
-  MovePreludeCode(ref_inst_itr, ref_block_itr, &new_blk_ptr);
-  InstructionBuilder builder(
-      context(), &*new_blk_ptr,
-      IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
-  new_blocks->push_back(std::move(new_blk_ptr));
-  // If initialization check, use reference value of zero.
-  // Else use the index of the last byte referenced.
-  uint32_t ref_id = init_check ? builder.GetUintConstantId(0u)
-                               : GenLastByteIdx(&ref, &builder);
   // Read initialization/bounds from debug input buffer. If index id not yet
   // set, binding is single descriptor, so set index to constant 0.
   if (ref.desc_idx_id == 0) ref.desc_idx_id = builder.GetUintConstantId(0u);
-  uint32_t init_id = GenDebugReadInit(ref.var_id, ref.desc_idx_id, &builder);
-  // Generate runtime initialization/bounds test code with true branch
-  // being full reference and false branch being debug output and zero
-  // for the referenced value.
-  Instruction* ult_inst =
-      builder.AddBinaryOp(GetBoolId(), SpvOpULessThan, ref_id, init_id);
-  uint32_t error = init_check ? kInstErrorBindlessUninit
-                              : (ref.strg_class == SpvStorageClassUniform
-                                     ? kInstErrorBuffOOBUniform
-                                     : kInstErrorBuffOOBStorage);
-  uint32_t error_id = builder.GetUintConstantId(error);
-  GenCheckCode(ult_inst->result_id(), error_id, init_check ? 0 : ref_id,
-               init_check ? builder.GetUintConstantId(0u) : init_id, stage_idx,
-               &ref, new_blocks);
-  // Move original block's remaining code into remainder/merge block and add
-  // to new blocks
-  BasicBlock* back_blk_ptr = &*new_blocks->back();
-  MovePostludeCode(ref_block_itr, back_blk_ptr);
-}
+  uint32_t check_id =
+      GenDescCheckCall(ref.ref_inst->unique_id(), stage_idx, ref.var_id,
+                       ref.desc_idx_id, ref_id, &builder);
 
-void InstBindlessCheckPass::GenTexBuffCheckCode(
-    BasicBlock::iterator ref_inst_itr,
-    UptrVectorIterator<BasicBlock> ref_block_itr, uint32_t stage_idx,
-    std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
-  // Only process OpImageRead and OpImageWrite with no optional operands
-  Instruction* ref_inst = &*ref_inst_itr;
-  SpvOp op = ref_inst->opcode();
-  uint32_t num_in_oprnds = ref_inst->NumInOperands();
-  if (!((op == SpvOpImageRead && num_in_oprnds == 2) ||
-        (op == SpvOpImageFetch && num_in_oprnds == 2) ||
-        (op == SpvOpImageWrite && num_in_oprnds == 3)))
-    return;
-  // Pull components from descriptor reference
-  RefAnalysis ref;
-  if (!AnalyzeDescriptorReference(ref_inst, &ref)) return;
-  // Only process if image is texel buffer
-  Instruction* image_inst = get_def_use_mgr()->GetDef(ref.image_id);
-  uint32_t image_ty_id = image_inst->type_id();
-  Instruction* image_ty_inst = get_def_use_mgr()->GetDef(image_ty_id);
-  if (image_ty_inst->GetSingleWordInOperand(kSpvTypeImageDim) != SpvDimBuffer)
-    return;
-  if (image_ty_inst->GetSingleWordInOperand(kSpvTypeImageDepth) != 0) return;
-  if (image_ty_inst->GetSingleWordInOperand(kSpvTypeImageArrayed) != 0) return;
-  if (image_ty_inst->GetSingleWordInOperand(kSpvTypeImageMS) != 0) return;
-  // Enable ImageQuery Capability if not yet enabled
-  if (!get_feature_mgr()->HasCapability(SpvCapabilityImageQuery)) {
-    std::unique_ptr<Instruction> cap_image_query_inst(new Instruction(
-        context(), SpvOpCapability, 0, 0,
-        std::initializer_list<Operand>{
-            {SPV_OPERAND_TYPE_CAPABILITY, {SpvCapabilityImageQuery}}}));
-    get_def_use_mgr()->AnalyzeInstDefUse(&*cap_image_query_inst);
-    context()->AddCapability(std::move(cap_image_query_inst));
-  }
-  // Move original block's preceding instructions into first new block
-  std::unique_ptr<BasicBlock> new_blk_ptr;
-  MovePreludeCode(ref_inst_itr, ref_block_itr, &new_blk_ptr);
-  InstructionBuilder builder(
-      context(), &*new_blk_ptr,
-      IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
-  new_blocks->push_back(std::move(new_blk_ptr));
-  // Get texel coordinate
-  uint32_t coord_id =
-      GenUintCastCode(ref_inst->GetSingleWordInOperand(1), &builder);
-  // If index id not yet set, binding is single descriptor, so set index to
-  // constant 0.
-  if (ref.desc_idx_id == 0) ref.desc_idx_id = builder.GetUintConstantId(0u);
-  // Get texel buffer size.
-  Instruction* size_inst =
-      builder.AddUnaryOp(GetUintId(), SpvOpImageQuerySize, ref.image_id);
-  uint32_t size_id = size_inst->result_id();
   // Generate runtime initialization/bounds test code with true branch
-  // being full reference and false branch being debug output and zero
+  // being full reference and false branch being zero
   // for the referenced value.
-  Instruction* ult_inst =
-      builder.AddBinaryOp(GetBoolId(), SpvOpULessThan, coord_id, size_id);
-  uint32_t error =
-      (image_ty_inst->GetSingleWordInOperand(kSpvTypeImageSampled) == 2)
-          ? kInstErrorBuffOOBStorageTexel
-          : kInstErrorBuffOOBUniformTexel;
-  uint32_t error_id = builder.GetUintConstantId(error);
-  GenCheckCode(ult_inst->result_id(), error_id, coord_id, size_id, stage_idx,
-               &ref, new_blocks);
+  GenCheckCode(check_id, &ref, new_blocks);
+
   // Move original block's remaining code into remainder/merge block and add
   // to new blocks
   BasicBlock* back_blk_ptr = &*new_blocks->back();
@@ -773,56 +708,48 @@
 void InstBindlessCheckPass::InitializeInstBindlessCheck() {
   // Initialize base class
   InitializeInstrument();
-  // If runtime array length support or buffer bounds checking are enabled,
-  // create variable mappings. Length support is always enabled if descriptor
-  // init check is enabled.
-  if (desc_idx_enabled_ || buffer_bounds_enabled_ || texel_buffer_enabled_)
-    for (auto& anno : get_module()->annotations())
-      if (anno.opcode() == SpvOpDecorate) {
-        if (anno.GetSingleWordInOperand(1u) == SpvDecorationDescriptorSet)
-          var2desc_set_[anno.GetSingleWordInOperand(0u)] =
-              anno.GetSingleWordInOperand(2u);
-        else if (anno.GetSingleWordInOperand(1u) == SpvDecorationBinding)
-          var2binding_[anno.GetSingleWordInOperand(0u)] =
-              anno.GetSingleWordInOperand(2u);
+  for (auto& anno : get_module()->annotations()) {
+    if (anno.opcode() == spv::Op::OpDecorate) {
+      if (spv::Decoration(anno.GetSingleWordInOperand(1u)) ==
+          spv::Decoration::DescriptorSet) {
+        var2desc_set_[anno.GetSingleWordInOperand(0u)] =
+            anno.GetSingleWordInOperand(2u);
+      } else if (spv::Decoration(anno.GetSingleWordInOperand(1u)) ==
+                 spv::Decoration::Binding) {
+        var2binding_[anno.GetSingleWordInOperand(0u)] =
+            anno.GetSingleWordInOperand(2u);
       }
+    }
+  }
 }
 
 Pass::Status InstBindlessCheckPass::ProcessImpl() {
-  // Perform bindless bounds check on each entry point function in module
+  // The memory model and linkage must always be updated for spirv-link to work
+  // correctly.
+  AddStorageBufferExt();
+  if (!get_feature_mgr()->HasExtension(kSPV_KHR_physical_storage_buffer)) {
+    context()->AddExtension("SPV_KHR_physical_storage_buffer");
+  }
+
+  context()->AddCapability(spv::Capability::PhysicalStorageBufferAddresses);
+  Instruction* memory_model = get_module()->GetMemoryModel();
+  memory_model->SetInOperand(
+      0u, {uint32_t(spv::AddressingModel::PhysicalStorageBuffer64)});
+
+  context()->AddCapability(spv::Capability::Linkage);
+
   InstProcessFunction pfn =
       [this](BasicBlock::iterator ref_inst_itr,
              UptrVectorIterator<BasicBlock> ref_block_itr, uint32_t stage_idx,
              std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
-        return GenDescIdxCheckCode(ref_inst_itr, ref_block_itr, stage_idx,
-                                   new_blocks);
+        return GenDescCheckCode(ref_inst_itr, ref_block_itr, stage_idx,
+                                new_blocks);
       };
-  bool modified = InstProcessEntryPointCallTree(pfn);
-  if (desc_init_enabled_ || buffer_bounds_enabled_) {
-    // Perform descriptor initialization and/or buffer bounds check on each
-    // entry point function in module
-    pfn = [this](BasicBlock::iterator ref_inst_itr,
-                 UptrVectorIterator<BasicBlock> ref_block_itr,
-                 uint32_t stage_idx,
-                 std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
-      return GenDescInitCheckCode(ref_inst_itr, ref_block_itr, stage_idx,
-                                  new_blocks);
-    };
-    modified |= InstProcessEntryPointCallTree(pfn);
-  }
-  if (texel_buffer_enabled_) {
-    // Perform texel buffer bounds check on each entry point function in
-    // module. Generate after descriptor bounds and initialization checks.
-    pfn = [this](BasicBlock::iterator ref_inst_itr,
-                 UptrVectorIterator<BasicBlock> ref_block_itr,
-                 uint32_t stage_idx,
-                 std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
-      return GenTexBuffCheckCode(ref_inst_itr, ref_block_itr, stage_idx,
-                                 new_blocks);
-    };
-    modified |= InstProcessEntryPointCallTree(pfn);
-  }
-  return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
+
+  InstProcessEntryPointCallTree(pfn);
+  // This pass always changes the memory model, so that linking will work
+  // properly.
+  return Status::SuccessWithChange;
 }
 
 Pass::Status InstBindlessCheckPass::Process() {
diff --git a/source/opt/inst_bindless_check_pass.h b/source/opt/inst_bindless_check_pass.h
index e6e6ef4..f99b59d 100644
--- a/source/opt/inst_bindless_check_pass.h
+++ b/source/opt/inst_bindless_check_pass.h
@@ -28,16 +28,8 @@
 // external design may change as the layer evolves.
 class InstBindlessCheckPass : public InstrumentPass {
  public:
-  InstBindlessCheckPass(uint32_t desc_set, uint32_t shader_id,
-                        bool desc_idx_enable, bool desc_init_enable,
-                        bool buffer_bounds_enable, bool texel_buffer_enable,
-                        bool opt_direct_reads)
-      : InstrumentPass(desc_set, shader_id, kInstValidationIdBindless,
-                       opt_direct_reads),
-        desc_idx_enabled_(desc_idx_enable),
-        desc_init_enabled_(desc_init_enable),
-        buffer_bounds_enabled_(buffer_bounds_enable),
-        texel_buffer_enabled_(texel_buffer_enable) {}
+  InstBindlessCheckPass(uint32_t shader_id)
+      : InstrumentPass(0, shader_id, true) {}
 
   ~InstBindlessCheckPass() override = default;
 
@@ -47,91 +39,31 @@
   const char* name() const override { return "inst-bindless-check-pass"; }
 
  private:
-  // These functions do bindless checking instrumentation on a single
-  // instruction which references through a descriptor (ie references into an
-  // image or buffer). Refer to Vulkan API for further information on
-  // descriptors. GenDescIdxCheckCode checks that an index into a descriptor
-  // array (array of images or buffers) is in-bounds. GenDescInitCheckCode
-  // checks that the referenced descriptor has been initialized, if the
-  // SPV_EXT_descriptor_indexing extension is enabled, and initialized large
-  // enough to handle the reference, if RobustBufferAccess is disabled.
-  // GenDescInitCheckCode checks for uniform and storage buffer overrun.
-  // GenTexBuffCheckCode checks for texel buffer overrun and should be
-  // run after GenDescInitCheckCode to first make sure that the descriptor
-  // is initialized because it uses OpImageQuerySize on the descriptor.
-  //
-  // The functions are designed to be passed to
-  // InstrumentPass::InstProcessEntryPointCallTree(), which applies the
-  // function to each instruction in a module and replaces the instruction
-  // if warranted.
-  //
-  // If |ref_inst_itr| is a bindless reference, return in |new_blocks| the
-  // result of instrumenting it with validation code within its block at
-  // |ref_block_itr|.  The validation code first executes a check for the
-  // specific condition called for. If the check passes, it executes
-  // the remainder of the reference, otherwise writes a record to the debug
-  // output buffer stream including |function_idx, instruction_idx, stage_idx|
-  // and replaces the reference with the null value of the original type. The
-  // block at |ref_block_itr| can just be replaced with the blocks in
-  // |new_blocks|, which will contain at least two blocks. The last block will
-  // comprise all instructions following |ref_inst_itr|,
-  // preceded by a phi instruction.
-  //
-  // These instrumentation functions utilize GenDebugDirectRead() to read data
-  // from the debug input buffer, specifically the lengths of variable length
-  // descriptor arrays, and the initialization status of each descriptor.
-  // The format of the debug input buffer is documented in instrument.hpp.
-  //
-  // These instrumentation functions utilize GenDebugStreamWrite() to write its
-  // error records. The validation-specific part of the error record will
-  // have the format:
-  //
-  //    Validation Error Code (=kInstErrorBindlessBounds)
-  //    Descriptor Index
-  //    Descriptor Array Size
-  //
-  // The Descriptor Index is the index which has been determined to be
-  // out-of-bounds.
-  //
-  // The Descriptor Array Size is the size of the descriptor array which was
-  // indexed.
-  void GenDescIdxCheckCode(
-      BasicBlock::iterator ref_inst_itr,
-      UptrVectorIterator<BasicBlock> ref_block_itr, uint32_t stage_idx,
-      std::vector<std::unique_ptr<BasicBlock>>* new_blocks);
+  void GenDescCheckCode(BasicBlock::iterator ref_inst_itr,
+                        UptrVectorIterator<BasicBlock> ref_block_itr,
+                        uint32_t stage_idx,
+                        std::vector<std::unique_ptr<BasicBlock>>* new_blocks);
 
-  void GenDescInitCheckCode(
-      BasicBlock::iterator ref_inst_itr,
-      UptrVectorIterator<BasicBlock> ref_block_itr, uint32_t stage_idx,
-      std::vector<std::unique_ptr<BasicBlock>>* new_blocks);
+  uint32_t GenDescCheckFunctionId();
 
-  void GenTexBuffCheckCode(
-      BasicBlock::iterator ref_inst_itr,
-      UptrVectorIterator<BasicBlock> ref_block_itr, uint32_t stage_idx,
-      std::vector<std::unique_ptr<BasicBlock>>* new_blocks);
-
-  // Generate instructions into |builder| to read length of runtime descriptor
-  // array |var_id| from debug input buffer and return id of value.
-  uint32_t GenDebugReadLength(uint32_t var_id, InstructionBuilder* builder);
-
-  // Generate instructions into |builder| to read initialization status of
-  // descriptor array |image_id| at |index_id| from debug input buffer and
-  // return id of value.
-  uint32_t GenDebugReadInit(uint32_t image_id, uint32_t index_id,
-                            InstructionBuilder* builder);
+  uint32_t GenDescCheckCall(uint32_t inst_idx, uint32_t stage_idx,
+                            uint32_t var_id, uint32_t index_id,
+                            uint32_t byte_offset, InstructionBuilder* builder);
 
   // Analysis data for descriptor reference components, generated by
   // AnalyzeDescriptorReference. It is necessary and sufficient for further
   // analysis and regeneration of the reference.
   typedef struct RefAnalysis {
-    uint32_t desc_load_id;
-    uint32_t image_id;
-    uint32_t load_id;
-    uint32_t ptr_id;
-    uint32_t var_id;
-    uint32_t desc_idx_id;
-    uint32_t strg_class;
-    Instruction* ref_inst;
+    uint32_t desc_load_id{0};
+    uint32_t image_id{0};
+    uint32_t load_id{0};
+    uint32_t ptr_id{0};
+    uint32_t var_id{0};
+    uint32_t set{0};
+    uint32_t binding{0};
+    uint32_t desc_idx_id{0};
+    uint32_t strg_class{0};
+    Instruction* ref_inst{nullptr};
   } RefAnalysis;
 
   // Return size of type |ty_id| in bytes. Use |matrix_stride| and |col_major|
@@ -173,8 +105,7 @@
   // writes debug error output utilizing |ref|, |error_id|, |length_id| and
   // |stage_idx|. Generate merge block for valid and invalid branches. Kill
   // original reference.
-  void GenCheckCode(uint32_t check_id, uint32_t error_id, uint32_t offset_id,
-                    uint32_t length_id, uint32_t stage_idx, RefAnalysis* ref,
+  void GenCheckCode(uint32_t check_id, RefAnalysis* ref,
                     std::vector<std::unique_ptr<BasicBlock>>* new_blocks);
 
   // Initialize state for instrumenting bindless checking
@@ -184,23 +115,13 @@
   // GenDescInitCheckCode to every instruction in module.
   Pass::Status ProcessImpl();
 
-  // Enable instrumentation of runtime array length checking
-  bool desc_idx_enabled_;
-
-  // Enable instrumentation of descriptor initialization checking
-  bool desc_init_enabled_;
-
-  // Enable instrumentation of uniform and storage buffer overrun checking
-  bool buffer_bounds_enabled_;
-
-  // Enable instrumentation of texel buffer overrun checking
-  bool texel_buffer_enabled_;
-
   // Mapping from variable to descriptor set
   std::unordered_map<uint32_t, uint32_t> var2desc_set_;
 
   // Mapping from variable to binding
   std::unordered_map<uint32_t, uint32_t> var2binding_;
+
+  uint32_t check_desc_func_id_{0};
 };
 
 }  // namespace opt
diff --git a/source/opt/inst_buff_addr_check_pass.cpp b/source/opt/inst_buff_addr_check_pass.cpp
index 3318f88..e6c5508 100644
--- a/source/opt/inst_buff_addr_check_pass.cpp
+++ b/source/opt/inst_buff_addr_check_pass.cpp
@@ -22,9 +22,9 @@
 uint32_t InstBuffAddrCheckPass::CloneOriginalReference(
     Instruction* ref_inst, InstructionBuilder* builder) {
   // Clone original ref with new result id (if load)
-  assert(
-      (ref_inst->opcode() == SpvOpLoad || ref_inst->opcode() == SpvOpStore) &&
-      "unexpected ref");
+  assert((ref_inst->opcode() == spv::Op::OpLoad ||
+          ref_inst->opcode() == spv::Op::OpStore) &&
+         "unexpected ref");
   std::unique_ptr<Instruction> new_ref_inst(ref_inst->Clone(context()));
   uint32_t ref_result_id = ref_inst->result_id();
   uint32_t new_ref_id = 0;
@@ -41,24 +41,24 @@
 }
 
 bool InstBuffAddrCheckPass::IsPhysicalBuffAddrReference(Instruction* ref_inst) {
-  if (ref_inst->opcode() != SpvOpLoad && ref_inst->opcode() != SpvOpStore)
+  if (ref_inst->opcode() != spv::Op::OpLoad &&
+      ref_inst->opcode() != spv::Op::OpStore)
     return false;
   uint32_t ptr_id = ref_inst->GetSingleWordInOperand(0);
   analysis::DefUseManager* du_mgr = get_def_use_mgr();
   Instruction* ptr_inst = du_mgr->GetDef(ptr_id);
-  if (ptr_inst->opcode() != SpvOpAccessChain) return false;
+  if (ptr_inst->opcode() != spv::Op::OpAccessChain) return false;
   uint32_t ptr_ty_id = ptr_inst->type_id();
   Instruction* ptr_ty_inst = du_mgr->GetDef(ptr_ty_id);
-  if (ptr_ty_inst->GetSingleWordInOperand(0) !=
-      SpvStorageClassPhysicalStorageBufferEXT)
+  if (spv::StorageClass(ptr_ty_inst->GetSingleWordInOperand(0)) !=
+      spv::StorageClass::PhysicalStorageBufferEXT)
     return false;
   return true;
 }
 
 // TODO(greg-lunarg): Refactor with InstBindlessCheckPass::GenCheckCode() ??
 void InstBuffAddrCheckPass::GenCheckCode(
-    uint32_t check_id, uint32_t error_id, uint32_t ref_uptr_id,
-    uint32_t stage_idx, Instruction* ref_inst,
+    uint32_t check_id, Instruction* ref_inst,
     std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
   BasicBlock* back_blk_ptr = &*new_blocks->back();
   InstructionBuilder builder(
@@ -72,8 +72,9 @@
   std::unique_ptr<Instruction> merge_label(NewLabel(merge_blk_id));
   std::unique_ptr<Instruction> valid_label(NewLabel(valid_blk_id));
   std::unique_ptr<Instruction> invalid_label(NewLabel(invalid_blk_id));
-  (void)builder.AddConditionalBranch(check_id, valid_blk_id, invalid_blk_id,
-                                     merge_blk_id, SpvSelectionControlMaskNone);
+  (void)builder.AddConditionalBranch(
+      check_id, valid_blk_id, invalid_blk_id, merge_blk_id,
+      uint32_t(spv::SelectionControlMask::MaskNone));
   // Gen valid branch
   std::unique_ptr<BasicBlock> new_blk_ptr(
       new BasicBlock(std::move(valid_label)));
@@ -84,18 +85,6 @@
   // Gen invalid block
   new_blk_ptr.reset(new BasicBlock(std::move(invalid_label)));
   builder.SetInsertPoint(&*new_blk_ptr);
-  // Convert uptr from uint64 to 2 uint32
-  Instruction* lo_uptr_inst =
-      builder.AddUnaryOp(GetUintId(), SpvOpUConvert, ref_uptr_id);
-  Instruction* rshift_uptr_inst =
-      builder.AddBinaryOp(GetUint64Id(), SpvOpShiftRightLogical, ref_uptr_id,
-                          builder.GetUintConstantId(32));
-  Instruction* hi_uptr_inst = builder.AddUnaryOp(GetUintId(), SpvOpUConvert,
-                                                 rshift_uptr_inst->result_id());
-  GenDebugStreamWrite(
-      uid2offset_[ref_inst->unique_id()], stage_idx,
-      {error_id, lo_uptr_inst->result_id(), hi_uptr_inst->result_id()},
-      &builder);
   // Gen zero for invalid load. If pointer type, need to convert uint64
   // zero to pointer; cannot create ConstantNull of pointer type.
   uint32_t null_id = 0;
@@ -105,8 +94,8 @@
     analysis::Type* ref_type = type_mgr->GetType(ref_type_id);
     if (ref_type->AsPointer() != nullptr) {
       uint32_t null_u64_id = GetNullId(GetUint64Id());
-      Instruction* null_ptr_inst =
-          builder.AddUnaryOp(ref_type_id, SpvOpConvertUToPtr, null_u64_id);
+      Instruction* null_ptr_inst = builder.AddUnaryOp(
+          ref_type_id, spv::Op::OpConvertUToPtr, null_u64_id);
       null_id = null_ptr_inst->result_id();
     } else {
       null_id = GetNullId(ref_type_id);
@@ -130,77 +119,43 @@
   context()->KillInst(ref_inst);
 }
 
-uint32_t InstBuffAddrCheckPass::GetTypeAlignment(uint32_t type_id) {
-  Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
-  switch (type_inst->opcode()) {
-    case SpvOpTypeFloat:
-    case SpvOpTypeInt:
-    case SpvOpTypeVector:
-      return GetTypeLength(type_id);
-    case SpvOpTypeMatrix:
-      return GetTypeAlignment(type_inst->GetSingleWordInOperand(0));
-    case SpvOpTypeArray:
-    case SpvOpTypeRuntimeArray:
-      return GetTypeAlignment(type_inst->GetSingleWordInOperand(0));
-    case SpvOpTypeStruct: {
-      uint32_t max = 0;
-      type_inst->ForEachInId([&max, this](const uint32_t* iid) {
-        uint32_t alignment = GetTypeAlignment(*iid);
-        max = (alignment > max) ? alignment : max;
-      });
-      return max;
-    }
-    case SpvOpTypePointer:
-      assert(type_inst->GetSingleWordInOperand(0) ==
-                 SpvStorageClassPhysicalStorageBufferEXT &&
-             "unexpected pointer type");
-      return 8u;
-    default:
-      assert(false && "unexpected type");
-      return 0;
-  }
-}
-
 uint32_t InstBuffAddrCheckPass::GetTypeLength(uint32_t type_id) {
   Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
   switch (type_inst->opcode()) {
-    case SpvOpTypeFloat:
-    case SpvOpTypeInt:
+    case spv::Op::OpTypeFloat:
+    case spv::Op::OpTypeInt:
       return type_inst->GetSingleWordInOperand(0) / 8u;
-    case SpvOpTypeVector: {
-      uint32_t raw_cnt = type_inst->GetSingleWordInOperand(1);
-      uint32_t adj_cnt = (raw_cnt == 3u) ? 4u : raw_cnt;
-      return adj_cnt * GetTypeLength(type_inst->GetSingleWordInOperand(0));
-    }
-    case SpvOpTypeMatrix:
+    case spv::Op::OpTypeVector:
+    case spv::Op::OpTypeMatrix:
       return type_inst->GetSingleWordInOperand(1) *
              GetTypeLength(type_inst->GetSingleWordInOperand(0));
-    case SpvOpTypePointer:
-      assert(type_inst->GetSingleWordInOperand(0) ==
-                 SpvStorageClassPhysicalStorageBufferEXT &&
+    case spv::Op::OpTypePointer:
+      assert(spv::StorageClass(type_inst->GetSingleWordInOperand(0)) ==
+                 spv::StorageClass::PhysicalStorageBufferEXT &&
              "unexpected pointer type");
       return 8u;
-    case SpvOpTypeArray: {
+    case spv::Op::OpTypeArray: {
       uint32_t const_id = type_inst->GetSingleWordInOperand(1);
       Instruction* const_inst = get_def_use_mgr()->GetDef(const_id);
       uint32_t cnt = const_inst->GetSingleWordInOperand(0);
       return cnt * GetTypeLength(type_inst->GetSingleWordInOperand(0));
     }
-    case SpvOpTypeStruct: {
-      uint32_t len = 0;
-      type_inst->ForEachInId([&len, this](const uint32_t* iid) {
-        // Align struct length
-        uint32_t alignment = GetTypeAlignment(*iid);
-        uint32_t mod = len % alignment;
-        uint32_t diff = (mod != 0) ? alignment - mod : 0;
-        len += diff;
-        // Increment struct length by component length
-        uint32_t comp_len = GetTypeLength(*iid);
-        len += comp_len;
+    case spv::Op::OpTypeStruct: {
+      // Figure out the location of the last byte of the last member of the
+      // structure.
+      uint32_t last_offset = 0, last_len = 0;
+
+      get_decoration_mgr()->ForEachDecoration(
+          type_id, uint32_t(spv::Decoration::Offset),
+          [&last_offset](const Instruction& deco_inst) {
+            last_offset = deco_inst.GetSingleWordInOperand(3);
+          });
+      type_inst->ForEachInId([&last_len, this](const uint32_t* iid) {
+        last_len = GetTypeLength(*iid);
       });
-      return len;
+      return last_offset + last_len;
     }
-    case SpvOpTypeRuntimeArray:
+    case spv::Op::OpTypeRuntimeArray:
     default:
       assert(false && "unexpected type");
       return 0;
@@ -213,223 +168,91 @@
   uint32_t pid = TakeNextId();
   param_vec->push_back(pid);
   std::unique_ptr<Instruction> param_inst(new Instruction(
-      get_module()->context(), SpvOpFunctionParameter, type_id, pid, {}));
+      get_module()->context(), spv::Op::OpFunctionParameter, type_id, pid, {}));
   get_def_use_mgr()->AnalyzeInstDefUse(&*param_inst);
   (*input_func)->AddParameter(std::move(param_inst));
 }
 
+// This is a stub function for use with Import linkage
+// clang-format off
+// GLSL:
+//bool inst_bindless_search_and_test(const uint shader_id, const uint inst_num, const uvec4 stage_info,
+//				     const uint64 ref_ptr, const uint length) {
+//}
+// clang-format on
 uint32_t InstBuffAddrCheckPass::GetSearchAndTestFuncId() {
-  if (search_test_func_id_ == 0) {
-    // Generate function "bool search_and_test(uint64_t ref_ptr, uint32_t len)"
-    // which searches input buffer for buffer which most likely contains the
-    // pointer value |ref_ptr| and verifies that the entire reference of
-    // length |len| bytes is contained in the buffer.
-    search_test_func_id_ = TakeNextId();
-    analysis::TypeManager* type_mgr = context()->get_type_mgr();
-    std::vector<const analysis::Type*> param_types = {
-        type_mgr->GetType(GetUint64Id()), type_mgr->GetType(GetUintId())};
-    analysis::Function func_ty(type_mgr->GetType(GetBoolId()), param_types);
-    analysis::Type* reg_func_ty = type_mgr->GetRegisteredType(&func_ty);
-    std::unique_ptr<Instruction> func_inst(
-        new Instruction(get_module()->context(), SpvOpFunction, GetBoolId(),
-                        search_test_func_id_,
-                        {{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER,
-                          {SpvFunctionControlMaskNone}},
-                         {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
-                          {type_mgr->GetTypeInstruction(reg_func_ty)}}}));
-    get_def_use_mgr()->AnalyzeInstDefUse(&*func_inst);
-    std::unique_ptr<Function> input_func =
-        MakeUnique<Function>(std::move(func_inst));
-    std::vector<uint32_t> param_vec;
-    // Add ref_ptr and length parameters
-    AddParam(GetUint64Id(), &param_vec, &input_func);
-    AddParam(GetUintId(), &param_vec, &input_func);
-    // Empty first block.
-    uint32_t first_blk_id = TakeNextId();
-    std::unique_ptr<Instruction> first_blk_label(NewLabel(first_blk_id));
-    std::unique_ptr<BasicBlock> first_blk_ptr =
-        MakeUnique<BasicBlock>(std::move(first_blk_label));
-    InstructionBuilder builder(
-        context(), &*first_blk_ptr,
-        IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
-    uint32_t hdr_blk_id = TakeNextId();
-    // Branch to search loop header
-    std::unique_ptr<Instruction> hdr_blk_label(NewLabel(hdr_blk_id));
-    (void)builder.AddInstruction(MakeUnique<Instruction>(
-        context(), SpvOpBranch, 0, 0,
-        std::initializer_list<Operand>{{SPV_OPERAND_TYPE_ID, {hdr_blk_id}}}));
-    input_func->AddBasicBlock(std::move(first_blk_ptr));
-    // Linear search loop header block
-    // TODO(greg-lunarg): Implement binary search
-    std::unique_ptr<BasicBlock> hdr_blk_ptr =
-        MakeUnique<BasicBlock>(std::move(hdr_blk_label));
-    builder.SetInsertPoint(&*hdr_blk_ptr);
-    // Phi for search index. Starts with 1.
-    uint32_t cont_blk_id = TakeNextId();
-    std::unique_ptr<Instruction> cont_blk_label(NewLabel(cont_blk_id));
-    // Deal with def-use cycle caused by search loop index computation.
-    // Create Add and Phi instructions first, then do Def analysis on Add.
-    // Add Phi and Add instructions and do Use analysis later.
-    uint32_t idx_phi_id = TakeNextId();
-    uint32_t idx_inc_id = TakeNextId();
-    std::unique_ptr<Instruction> idx_inc_inst(new Instruction(
-        context(), SpvOpIAdd, GetUintId(), idx_inc_id,
-        {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {idx_phi_id}},
-         {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
-          {builder.GetUintConstantId(1u)}}}));
-    std::unique_ptr<Instruction> idx_phi_inst(new Instruction(
-        context(), SpvOpPhi, GetUintId(), idx_phi_id,
-        {{spv_operand_type_t::SPV_OPERAND_TYPE_ID,
-          {builder.GetUintConstantId(1u)}},
-         {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {first_blk_id}},
-         {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {idx_inc_id}},
-         {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {cont_blk_id}}}));
-    get_def_use_mgr()->AnalyzeInstDef(&*idx_inc_inst);
-    // Add (previously created) search index phi
-    (void)builder.AddInstruction(std::move(idx_phi_inst));
-    // LoopMerge
-    uint32_t bound_test_blk_id = TakeNextId();
-    std::unique_ptr<Instruction> bound_test_blk_label(
-        NewLabel(bound_test_blk_id));
-    (void)builder.AddInstruction(MakeUnique<Instruction>(
-        context(), SpvOpLoopMerge, 0, 0,
-        std::initializer_list<Operand>{
-            {SPV_OPERAND_TYPE_ID, {bound_test_blk_id}},
-            {SPV_OPERAND_TYPE_ID, {cont_blk_id}},
-            {SPV_OPERAND_TYPE_LITERAL_INTEGER, {SpvLoopControlMaskNone}}}));
-    // Branch to continue/work block
-    (void)builder.AddInstruction(MakeUnique<Instruction>(
-        context(), SpvOpBranch, 0, 0,
-        std::initializer_list<Operand>{{SPV_OPERAND_TYPE_ID, {cont_blk_id}}}));
-    input_func->AddBasicBlock(std::move(hdr_blk_ptr));
-    // Continue/Work Block. Read next buffer pointer and break if greater
-    // than ref_ptr arg.
-    std::unique_ptr<BasicBlock> cont_blk_ptr =
-        MakeUnique<BasicBlock>(std::move(cont_blk_label));
-    builder.SetInsertPoint(&*cont_blk_ptr);
-    // Add (previously created) search index increment now.
-    (void)builder.AddInstruction(std::move(idx_inc_inst));
-    // Load next buffer address from debug input buffer
-    uint32_t ibuf_id = GetInputBufferId();
-    uint32_t ibuf_ptr_id = GetInputBufferPtrId();
-    Instruction* uptr_ac_inst = builder.AddTernaryOp(
-        ibuf_ptr_id, SpvOpAccessChain, ibuf_id,
-        builder.GetUintConstantId(kDebugInputDataOffset), idx_inc_id);
-    uint32_t ibuf_type_id = GetInputBufferTypeId();
-    Instruction* uptr_load_inst =
-        builder.AddUnaryOp(ibuf_type_id, SpvOpLoad, uptr_ac_inst->result_id());
-    // If loaded address greater than ref_ptr arg, break, else branch back to
-    // loop header
-    Instruction* uptr_test_inst =
-        builder.AddBinaryOp(GetBoolId(), SpvOpUGreaterThan,
-                            uptr_load_inst->result_id(), param_vec[0]);
-    (void)builder.AddConditionalBranch(uptr_test_inst->result_id(),
-                                       bound_test_blk_id, hdr_blk_id,
-                                       kInvalidId, SpvSelectionControlMaskNone);
-    input_func->AddBasicBlock(std::move(cont_blk_ptr));
-    // Bounds test block. Read length of selected buffer and test that
-    // all len arg bytes are in buffer.
-    std::unique_ptr<BasicBlock> bound_test_blk_ptr =
-        MakeUnique<BasicBlock>(std::move(bound_test_blk_label));
-    builder.SetInsertPoint(&*bound_test_blk_ptr);
-    // Decrement index to point to previous/candidate buffer address
-    Instruction* cand_idx_inst = builder.AddBinaryOp(
-        GetUintId(), SpvOpISub, idx_inc_id, builder.GetUintConstantId(1u));
-    // Load candidate buffer address
-    Instruction* cand_ac_inst =
-        builder.AddTernaryOp(ibuf_ptr_id, SpvOpAccessChain, ibuf_id,
-                             builder.GetUintConstantId(kDebugInputDataOffset),
-                             cand_idx_inst->result_id());
-    Instruction* cand_load_inst =
-        builder.AddUnaryOp(ibuf_type_id, SpvOpLoad, cand_ac_inst->result_id());
-    // Compute offset of ref_ptr from candidate buffer address
-    Instruction* offset_inst = builder.AddBinaryOp(
-        ibuf_type_id, SpvOpISub, param_vec[0], cand_load_inst->result_id());
-    // Convert ref length to uint64
-    Instruction* ref_len_64_inst =
-        builder.AddUnaryOp(ibuf_type_id, SpvOpUConvert, param_vec[1]);
-    // Add ref length to ref offset to compute end of reference
-    Instruction* ref_end_inst =
-        builder.AddBinaryOp(ibuf_type_id, SpvOpIAdd, offset_inst->result_id(),
-                            ref_len_64_inst->result_id());
-    // Load starting index of lengths in input buffer and convert to uint32
-    Instruction* len_start_ac_inst =
-        builder.AddTernaryOp(ibuf_ptr_id, SpvOpAccessChain, ibuf_id,
-                             builder.GetUintConstantId(kDebugInputDataOffset),
-                             builder.GetUintConstantId(0u));
-    Instruction* len_start_load_inst = builder.AddUnaryOp(
-        ibuf_type_id, SpvOpLoad, len_start_ac_inst->result_id());
-    Instruction* len_start_32_inst = builder.AddUnaryOp(
-        GetUintId(), SpvOpUConvert, len_start_load_inst->result_id());
-    // Decrement search index to get candidate buffer length index
-    Instruction* cand_len_idx_inst =
-        builder.AddBinaryOp(GetUintId(), SpvOpISub, cand_idx_inst->result_id(),
-                            builder.GetUintConstantId(1u));
-    // Add candidate length index to start index
-    Instruction* len_idx_inst = builder.AddBinaryOp(
-        GetUintId(), SpvOpIAdd, cand_len_idx_inst->result_id(),
-        len_start_32_inst->result_id());
-    // Load candidate buffer length
-    Instruction* len_ac_inst =
-        builder.AddTernaryOp(ibuf_ptr_id, SpvOpAccessChain, ibuf_id,
-                             builder.GetUintConstantId(kDebugInputDataOffset),
-                             len_idx_inst->result_id());
-    Instruction* len_load_inst =
-        builder.AddUnaryOp(ibuf_type_id, SpvOpLoad, len_ac_inst->result_id());
-    // Test if reference end within candidate buffer length
-    Instruction* len_test_inst = builder.AddBinaryOp(
-        GetBoolId(), SpvOpULessThanEqual, ref_end_inst->result_id(),
-        len_load_inst->result_id());
-    // Return test result
-    (void)builder.AddInstruction(MakeUnique<Instruction>(
-        context(), SpvOpReturnValue, 0, 0,
-        std::initializer_list<Operand>{
-            {SPV_OPERAND_TYPE_ID, {len_test_inst->result_id()}}}));
-    // Close block
-    input_func->AddBasicBlock(std::move(bound_test_blk_ptr));
-    // Close function and add function to module
-    std::unique_ptr<Instruction> func_end_inst(
-        new Instruction(get_module()->context(), SpvOpFunctionEnd, 0, 0, {}));
-    get_def_use_mgr()->AnalyzeInstDefUse(&*func_end_inst);
-    input_func->SetFunctionEnd(std::move(func_end_inst));
-    context()->AddFunction(std::move(input_func));
-    context()->AddDebug2Inst(
-        NewGlobalName(search_test_func_id_, "search_and_test"));
+  enum {
+    kShaderId = 0,
+    kInstructionIndex = 1,
+    kStageInfo = 2,
+    kRefPtr = 3,
+    kLength = 4,
+    kNumArgs
+  };
+  if (search_test_func_id_ != 0) {
+    return search_test_func_id_;
   }
+  // Generate function "bool search_and_test(uint64_t ref_ptr, uint32_t len)"
+  // which searches input buffer for buffer which most likely contains the
+  // pointer value |ref_ptr| and verifies that the entire reference of
+  // length |len| bytes is contained in the buffer.
+  analysis::TypeManager* type_mgr = context()->get_type_mgr();
+  const analysis::Integer* uint_type = GetInteger(32, false);
+  const analysis::Vector v4uint(uint_type, 4);
+  const analysis::Type* v4uint_type = type_mgr->GetRegisteredType(&v4uint);
+
+  std::vector<const analysis::Type*> param_types = {
+      uint_type, uint_type, v4uint_type, type_mgr->GetType(GetUint64Id()),
+      uint_type};
+
+  const std::string func_name{"inst_buff_addr_search_and_test"};
+  const uint32_t func_id = TakeNextId();
+  std::unique_ptr<Function> func =
+      StartFunction(func_id, type_mgr->GetBoolType(), param_types);
+  func->SetFunctionEnd(EndFunction());
+  context()->AddFunctionDeclaration(std::move(func));
+  context()->AddDebug2Inst(NewName(func_id, func_name));
+
+  std::vector<Operand> operands{
+      {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {func_id}},
+      {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER,
+       {uint32_t(spv::Decoration::LinkageAttributes)}},
+      {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_STRING,
+       utils::MakeVector(func_name.c_str())},
+      {spv_operand_type_t::SPV_OPERAND_TYPE_LINKAGE_TYPE,
+       {uint32_t(spv::LinkageType::Import)}},
+  };
+  get_decoration_mgr()->AddDecoration(spv::Op::OpDecorate, operands);
+
+  search_test_func_id_ = func_id;
   return search_test_func_id_;
 }
 
 uint32_t InstBuffAddrCheckPass::GenSearchAndTest(Instruction* ref_inst,
                                                  InstructionBuilder* builder,
-                                                 uint32_t* ref_uptr_id) {
+                                                 uint32_t* ref_uptr_id,
+                                                 uint32_t stage_idx) {
   // Enable Int64 if necessary
-  if (!get_feature_mgr()->HasCapability(SpvCapabilityInt64)) {
-    std::unique_ptr<Instruction> cap_int64_inst(new Instruction(
-        context(), SpvOpCapability, 0, 0,
-        std::initializer_list<Operand>{
-            {SPV_OPERAND_TYPE_CAPABILITY, {SpvCapabilityInt64}}}));
-    get_def_use_mgr()->AnalyzeInstDefUse(&*cap_int64_inst);
-    context()->AddCapability(std::move(cap_int64_inst));
-  }
   // Convert reference pointer to uint64
-  uint32_t ref_ptr_id = ref_inst->GetSingleWordInOperand(0);
+  const uint32_t ref_ptr_id = ref_inst->GetSingleWordInOperand(0);
   Instruction* ref_uptr_inst =
-      builder->AddUnaryOp(GetUint64Id(), SpvOpConvertPtrToU, ref_ptr_id);
+      builder->AddUnaryOp(GetUint64Id(), spv::Op::OpConvertPtrToU, ref_ptr_id);
   *ref_uptr_id = ref_uptr_inst->result_id();
   // Compute reference length in bytes
   analysis::DefUseManager* du_mgr = get_def_use_mgr();
   Instruction* ref_ptr_inst = du_mgr->GetDef(ref_ptr_id);
-  uint32_t ref_ptr_ty_id = ref_ptr_inst->type_id();
+  const uint32_t ref_ptr_ty_id = ref_ptr_inst->type_id();
   Instruction* ref_ptr_ty_inst = du_mgr->GetDef(ref_ptr_ty_id);
-  uint32_t ref_len = GetTypeLength(ref_ptr_ty_inst->GetSingleWordInOperand(1));
-  uint32_t ref_len_id = builder->GetUintConstantId(ref_len);
+  const uint32_t ref_len =
+      GetTypeLength(ref_ptr_ty_inst->GetSingleWordInOperand(1));
   // Gen call to search and test function
-  const std::vector<uint32_t> args = {GetSearchAndTestFuncId(), *ref_uptr_id,
-                                      ref_len_id};
-  Instruction* call_inst =
-      builder->AddNaryOp(GetBoolId(), SpvOpFunctionCall, args);
-  uint32_t retval = call_inst->result_id();
-  return retval;
+  const uint32_t func_id = GetSearchAndTestFuncId();
+  const std::vector<uint32_t> args = {
+      builder->GetUintConstantId(shader_id_),
+      builder->GetUintConstantId(ref_inst->unique_id()),
+      GenStageInfo(stage_idx, builder), *ref_uptr_id,
+      builder->GetUintConstantId(ref_len)};
+  return GenReadFunctionCall(GetBoolId(), func_id, args, builder);
 }
 
 void InstBuffAddrCheckPass::GenBuffAddrCheckCode(
@@ -447,16 +270,16 @@
       context(), &*new_blk_ptr,
       IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
   new_blocks->push_back(std::move(new_blk_ptr));
-  uint32_t error_id = builder.GetUintConstantId(kInstErrorBuffAddrUnallocRef);
   // Generate code to do search and test if all bytes of reference
   // are within a listed buffer. Return reference pointer converted to uint64.
   uint32_t ref_uptr_id;
-  uint32_t valid_id = GenSearchAndTest(ref_inst, &builder, &ref_uptr_id);
+  uint32_t valid_id =
+      GenSearchAndTest(ref_inst, &builder, &ref_uptr_id, stage_idx);
   // Generate test of search results with true branch
   // being full reference and false branch being debug output and zero
   // for the referenced value.
-  GenCheckCode(valid_id, error_id, ref_uptr_id, stage_idx, ref_inst,
-               new_blocks);
+  GenCheckCode(valid_id, ref_inst, new_blocks);
+
   // Move original block's remaining code into remainder/merge block and add
   // to new blocks
   BasicBlock* back_blk_ptr = &*new_blocks->back();
@@ -471,6 +294,20 @@
 }
 
 Pass::Status InstBuffAddrCheckPass::ProcessImpl() {
+  // The memory model and linkage must always be updated for spirv-link to work
+  // correctly.
+  AddStorageBufferExt();
+  if (!get_feature_mgr()->HasExtension(kSPV_KHR_physical_storage_buffer)) {
+    context()->AddExtension("SPV_KHR_physical_storage_buffer");
+  }
+
+  context()->AddCapability(spv::Capability::PhysicalStorageBufferAddresses);
+  Instruction* memory_model = get_module()->GetMemoryModel();
+  memory_model->SetInOperand(
+      0u, {uint32_t(spv::AddressingModel::PhysicalStorageBuffer64)});
+
+  context()->AddCapability(spv::Capability::Int64);
+  context()->AddCapability(spv::Capability::Linkage);
   // Perform bindless bounds check on each entry point function in module
   InstProcessFunction pfn =
       [this](BasicBlock::iterator ref_inst_itr,
@@ -479,14 +316,13 @@
         return GenBuffAddrCheckCode(ref_inst_itr, ref_block_itr, stage_idx,
                                     new_blocks);
       };
-  bool modified = InstProcessEntryPointCallTree(pfn);
-  return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
+  InstProcessEntryPointCallTree(pfn);
+  // This pass always changes the memory model, so that linking will work
+  // properly.
+  return Status::SuccessWithChange;
 }
 
 Pass::Status InstBuffAddrCheckPass::Process() {
-  if (!get_feature_mgr()->HasCapability(
-          SpvCapabilityPhysicalStorageBufferAddressesEXT))
-    return Status::SuccessWithoutChange;
   InitInstBuffAddrCheck();
   return ProcessImpl();
 }
diff --git a/source/opt/inst_buff_addr_check_pass.h b/source/opt/inst_buff_addr_check_pass.h
index fb43c39..70076a3 100644
--- a/source/opt/inst_buff_addr_check_pass.h
+++ b/source/opt/inst_buff_addr_check_pass.h
@@ -29,10 +29,9 @@
 class InstBuffAddrCheckPass : public InstrumentPass {
  public:
   // For test harness only
-  InstBuffAddrCheckPass() : InstrumentPass(7, 23, kInstValidationIdBuffAddr) {}
+  InstBuffAddrCheckPass() : InstrumentPass(0, 23) {}
   // For all other interfaces
-  InstBuffAddrCheckPass(uint32_t desc_set, uint32_t shader_id)
-      : InstrumentPass(desc_set, shader_id, kInstValidationIdBuffAddr) {}
+  InstBuffAddrCheckPass(uint32_t shader_id) : InstrumentPass(0, shader_id) {}
 
   ~InstBuffAddrCheckPass() override = default;
 
@@ -42,10 +41,6 @@
   const char* name() const override { return "inst-buff-addr-check-pass"; }
 
  private:
-  // Return byte alignment of type |type_id|. Must be int, float, vector,
-  // matrix, struct, array or physical pointer. Uses std430 alignment.
-  uint32_t GetTypeAlignment(uint32_t type_id);
-
   // Return byte length of type |type_id|. Must be int, float, vector, matrix,
   // struct, array or physical pointer. Uses std430 alignment and sizes.
   uint32_t GetTypeLength(uint32_t type_id);
@@ -62,7 +57,7 @@
   // are within the buffer. Returns id of boolean value which is true if
   // search and test is successful, false otherwise.
   uint32_t GenSearchAndTest(Instruction* ref_inst, InstructionBuilder* builder,
-                            uint32_t* ref_uptr_id);
+                            uint32_t* ref_uptr_id, uint32_t stage_idx);
 
   // This function does checking instrumentation on a single
   // instruction which references through a physical storage buffer address.
@@ -115,8 +110,7 @@
   // writes debug error output utilizing |ref_inst|, |error_id| and
   // |stage_idx|. Generate merge block for valid and invalid reference blocks.
   // Kill original reference.
-  void GenCheckCode(uint32_t check_id, uint32_t error_id, uint32_t length_id,
-                    uint32_t stage_idx, Instruction* ref_inst,
+  void GenCheckCode(uint32_t check_id, Instruction* ref_inst,
                     std::vector<std::unique_ptr<BasicBlock>>* new_blocks);
 
   // Initialize state for instrumenting physical buffer address checking
diff --git a/source/opt/inst_debug_printf_pass.cpp b/source/opt/inst_debug_printf_pass.cpp
index 4218138..a48a28f 100644
--- a/source/opt/inst_debug_printf_pass.cpp
+++ b/source/opt/inst_debug_printf_pass.cpp
@@ -16,6 +16,7 @@
 
 #include "inst_debug_printf_pass.h"
 
+#include "source/spirv_constant.h"
 #include "source/util/string_utils.h"
 #include "spirv/unified1/NonSemanticDebugPrintf.h"
 
@@ -34,8 +35,8 @@
       const analysis::Type* c_ty = v_ty->element_type();
       uint32_t c_ty_id = type_mgr->GetId(c_ty);
       for (uint32_t c = 0; c < v_ty->element_count(); ++c) {
-        Instruction* c_inst = builder->AddIdLiteralOp(
-            c_ty_id, SpvOpCompositeExtract, val_inst->result_id(), c);
+        Instruction* c_inst =
+            builder->AddCompositeExtract(c_ty_id, val_inst->result_id(), {c});
         GenOutputValues(c_inst, val_ids, builder);
       }
       return;
@@ -44,8 +45,8 @@
       // Select between uint32 zero or one
       uint32_t zero_id = builder->GetUintConstantId(0);
       uint32_t one_id = builder->GetUintConstantId(1);
-      Instruction* sel_inst = builder->AddTernaryOp(
-          GetUintId(), SpvOpSelect, val_inst->result_id(), one_id, zero_id);
+      Instruction* sel_inst = builder->AddSelect(
+          GetUintId(), val_inst->result_id(), one_id, zero_id);
       val_ids->push_back(sel_inst->result_id());
       return;
     }
@@ -55,21 +56,21 @@
         case 16: {
           // Convert float16 to float32 and recurse
           Instruction* f32_inst = builder->AddUnaryOp(
-              GetFloatId(), SpvOpFConvert, val_inst->result_id());
+              GetFloatId(), spv::Op::OpFConvert, val_inst->result_id());
           GenOutputValues(f32_inst, val_ids, builder);
           return;
         }
         case 64: {
           // Bitcast float64 to uint64 and recurse
           Instruction* ui64_inst = builder->AddUnaryOp(
-              GetUint64Id(), SpvOpBitcast, val_inst->result_id());
+              GetUint64Id(), spv::Op::OpBitcast, val_inst->result_id());
           GenOutputValues(ui64_inst, val_ids, builder);
           return;
         }
         case 32: {
           // Bitcase float32 to uint32
-          Instruction* bc_inst = builder->AddUnaryOp(GetUintId(), SpvOpBitcast,
-                                                     val_inst->result_id());
+          Instruction* bc_inst = builder->AddUnaryOp(
+              GetUintId(), spv::Op::OpBitcast, val_inst->result_id());
           val_ids->push_back(bc_inst->result_id());
           return;
         }
@@ -85,17 +86,17 @@
           Instruction* ui64_inst = val_inst;
           if (i_ty->IsSigned()) {
             // Bitcast sint64 to uint64
-            ui64_inst = builder->AddUnaryOp(GetUint64Id(), SpvOpBitcast,
+            ui64_inst = builder->AddUnaryOp(GetUint64Id(), spv::Op::OpBitcast,
                                             val_inst->result_id());
           }
           // Break uint64 into 2x uint32
           Instruction* lo_ui64_inst = builder->AddUnaryOp(
-              GetUintId(), SpvOpUConvert, ui64_inst->result_id());
+              GetUintId(), spv::Op::OpUConvert, ui64_inst->result_id());
           Instruction* rshift_ui64_inst = builder->AddBinaryOp(
-              GetUint64Id(), SpvOpShiftRightLogical, ui64_inst->result_id(),
-              builder->GetUintConstantId(32));
+              GetUint64Id(), spv::Op::OpShiftRightLogical,
+              ui64_inst->result_id(), builder->GetUintConstantId(32));
           Instruction* hi_ui64_inst = builder->AddUnaryOp(
-              GetUintId(), SpvOpUConvert, rshift_ui64_inst->result_id());
+              GetUintId(), spv::Op::OpUConvert, rshift_ui64_inst->result_id());
           val_ids->push_back(lo_ui64_inst->result_id());
           val_ids->push_back(hi_ui64_inst->result_id());
           return;
@@ -104,12 +105,12 @@
           Instruction* ui8_inst = val_inst;
           if (i_ty->IsSigned()) {
             // Bitcast sint8 to uint8
-            ui8_inst = builder->AddUnaryOp(GetUint8Id(), SpvOpBitcast,
+            ui8_inst = builder->AddUnaryOp(GetUint8Id(), spv::Op::OpBitcast,
                                            val_inst->result_id());
           }
           // Convert uint8 to uint32
           Instruction* ui32_inst = builder->AddUnaryOp(
-              GetUintId(), SpvOpUConvert, ui8_inst->result_id());
+              GetUintId(), spv::Op::OpUConvert, ui8_inst->result_id());
           val_ids->push_back(ui32_inst->result_id());
           return;
         }
@@ -117,7 +118,7 @@
           Instruction* ui32_inst = val_inst;
           if (i_ty->IsSigned()) {
             // Bitcast sint32 to uint32
-            ui32_inst = builder->AddUnaryOp(GetUintId(), SpvOpBitcast,
+            ui32_inst = builder->AddUnaryOp(GetUintId(), spv::Op::OpBitcast,
                                             val_inst->result_id());
           }
           // uint32 needs no further processing
@@ -158,15 +159,17 @@
           return;
         }
         Instruction* opnd_inst = get_def_use_mgr()->GetDef(*iid);
-        if (opnd_inst->opcode() == SpvOpString) {
+        if (opnd_inst->opcode() == spv::Op::OpString) {
           uint32_t string_id_id = builder.GetUintConstantId(*iid);
           val_ids.push_back(string_id_id);
         } else {
           GenOutputValues(opnd_inst, &val_ids, &builder);
         }
       });
-  GenDebugStreamWrite(uid2offset_[printf_inst->unique_id()], stage_idx, val_ids,
-                      &builder);
+  GenDebugStreamWrite(
+      builder.GetUintConstantId(shader_id_),
+      builder.GetUintConstantId(uid2offset_[printf_inst->unique_id()]),
+      GenStageInfo(stage_idx, &builder), val_ids, &builder);
   context()->KillInst(printf_inst);
 }
 
@@ -176,7 +179,7 @@
     std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
   // If not DebugPrintf OpExtInst, return.
   Instruction* printf_inst = &*ref_inst_itr;
-  if (printf_inst->opcode() != SpvOpExtInst) return;
+  if (printf_inst->opcode() != spv::Op::OpExtInst) return;
   if (printf_inst->GetSingleWordInOperand(0) != ext_inst_printf_id_) return;
   if (printf_inst->GetSingleWordInOperand(1) !=
       NonSemanticDebugPrintfDebugPrintf)
@@ -208,9 +211,244 @@
   new_blocks->push_back(std::move(new_blk_ptr));
 }
 
+// Return id for output buffer
+uint32_t InstDebugPrintfPass::GetOutputBufferId() {
+  if (output_buffer_id_ == 0) {
+    // If not created yet, create one
+    analysis::DecorationManager* deco_mgr = get_decoration_mgr();
+    analysis::TypeManager* type_mgr = context()->get_type_mgr();
+    analysis::RuntimeArray* reg_uint_rarr_ty = GetUintRuntimeArrayType(32);
+    analysis::Integer* reg_uint_ty = GetInteger(32, false);
+    analysis::Type* reg_buf_ty =
+        GetStruct({reg_uint_ty, reg_uint_ty, reg_uint_rarr_ty});
+    uint32_t obufTyId = type_mgr->GetTypeInstruction(reg_buf_ty);
+    // By the Vulkan spec, a pre-existing struct containing a RuntimeArray
+    // must be a block, and will therefore be decorated with Block. Therefore
+    // the undecorated type returned here will not be pre-existing and can
+    // safely be decorated. Since this type is now decorated, it is out of
+    // sync with the TypeManager and therefore the TypeManager must be
+    // invalidated after this pass.
+    assert(context()->get_def_use_mgr()->NumUses(obufTyId) == 0 &&
+           "used struct type returned");
+    deco_mgr->AddDecoration(obufTyId, uint32_t(spv::Decoration::Block));
+    deco_mgr->AddMemberDecoration(obufTyId, kDebugOutputFlagsOffset,
+                                  uint32_t(spv::Decoration::Offset), 0);
+    deco_mgr->AddMemberDecoration(obufTyId, kDebugOutputSizeOffset,
+                                  uint32_t(spv::Decoration::Offset), 4);
+    deco_mgr->AddMemberDecoration(obufTyId, kDebugOutputDataOffset,
+                                  uint32_t(spv::Decoration::Offset), 8);
+    uint32_t obufTyPtrId_ =
+        type_mgr->FindPointerToType(obufTyId, spv::StorageClass::StorageBuffer);
+    output_buffer_id_ = TakeNextId();
+    std::unique_ptr<Instruction> newVarOp(new Instruction(
+        context(), spv::Op::OpVariable, obufTyPtrId_, output_buffer_id_,
+        {{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER,
+          {uint32_t(spv::StorageClass::StorageBuffer)}}}));
+    context()->AddGlobalValue(std::move(newVarOp));
+    context()->AddDebug2Inst(NewGlobalName(obufTyId, "OutputBuffer"));
+    context()->AddDebug2Inst(NewMemberName(obufTyId, 0, "flags"));
+    context()->AddDebug2Inst(NewMemberName(obufTyId, 1, "written_count"));
+    context()->AddDebug2Inst(NewMemberName(obufTyId, 2, "data"));
+    context()->AddDebug2Inst(NewGlobalName(output_buffer_id_, "output_buffer"));
+    deco_mgr->AddDecorationVal(
+        output_buffer_id_, uint32_t(spv::Decoration::DescriptorSet), desc_set_);
+    deco_mgr->AddDecorationVal(output_buffer_id_,
+                               uint32_t(spv::Decoration::Binding),
+                               GetOutputBufferBinding());
+    AddStorageBufferExt();
+    if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4)) {
+      // Add the new buffer to all entry points.
+      for (auto& entry : get_module()->entry_points()) {
+        entry.AddOperand({SPV_OPERAND_TYPE_ID, {output_buffer_id_}});
+        context()->AnalyzeUses(&entry);
+      }
+    }
+  }
+  return output_buffer_id_;
+}
+
+uint32_t InstDebugPrintfPass::GetOutputBufferPtrId() {
+  if (output_buffer_ptr_id_ == 0) {
+    output_buffer_ptr_id_ = context()->get_type_mgr()->FindPointerToType(
+        GetUintId(), spv::StorageClass::StorageBuffer);
+  }
+  return output_buffer_ptr_id_;
+}
+
+uint32_t InstDebugPrintfPass::GetOutputBufferBinding() {
+  return kDebugOutputPrintfStream;
+}
+
+void InstDebugPrintfPass::GenDebugOutputFieldCode(uint32_t base_offset_id,
+                                                  uint32_t field_offset,
+                                                  uint32_t field_value_id,
+                                                  InstructionBuilder* builder) {
+  // Cast value to 32-bit unsigned if necessary
+  uint32_t val_id = GenUintCastCode(field_value_id, builder);
+  // Store value
+  Instruction* data_idx_inst = builder->AddIAdd(
+      GetUintId(), base_offset_id, builder->GetUintConstantId(field_offset));
+  uint32_t buf_id = GetOutputBufferId();
+  uint32_t buf_uint_ptr_id = GetOutputBufferPtrId();
+  Instruction* achain_inst = builder->AddAccessChain(
+      buf_uint_ptr_id, buf_id,
+      {builder->GetUintConstantId(kDebugOutputDataOffset),
+       data_idx_inst->result_id()});
+  (void)builder->AddStore(achain_inst->result_id(), val_id);
+}
+
+uint32_t InstDebugPrintfPass::GetStreamWriteFunctionId(uint32_t param_cnt) {
+  enum {
+    kShaderId = 0,
+    kInstructionIndex = 1,
+    kStageInfo = 2,
+    kFirstParam = 3,
+  };
+  // Total param count is common params plus validation-specific
+  // params
+  if (param2output_func_id_[param_cnt] == 0) {
+    // Create function
+    param2output_func_id_[param_cnt] = TakeNextId();
+    analysis::TypeManager* type_mgr = context()->get_type_mgr();
+
+    const analysis::Type* uint_type = GetInteger(32, false);
+    const analysis::Vector v4uint(uint_type, 4);
+    const analysis::Type* v4uint_type = type_mgr->GetRegisteredType(&v4uint);
+
+    std::vector<const analysis::Type*> param_types(kFirstParam + param_cnt,
+                                                   uint_type);
+    param_types[kStageInfo] = v4uint_type;
+    std::unique_ptr<Function> output_func = StartFunction(
+        param2output_func_id_[param_cnt], type_mgr->GetVoidType(), param_types);
+
+    std::vector<uint32_t> param_ids = AddParameters(*output_func, param_types);
+
+    // Create first block
+    auto new_blk_ptr = MakeUnique<BasicBlock>(NewLabel(TakeNextId()));
+
+    InstructionBuilder builder(
+        context(), &*new_blk_ptr,
+        IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
+    // Gen test if debug output buffer size will not be exceeded.
+    const uint32_t val_spec_offset = kInstStageOutCnt;
+    const uint32_t obuf_record_sz = val_spec_offset + param_cnt;
+    const uint32_t buf_id = GetOutputBufferId();
+    const uint32_t buf_uint_ptr_id = GetOutputBufferPtrId();
+    Instruction* obuf_curr_sz_ac_inst = builder.AddAccessChain(
+        buf_uint_ptr_id, buf_id,
+        {builder.GetUintConstantId(kDebugOutputSizeOffset)});
+    // Fetch the current debug buffer written size atomically, adding the
+    // size of the record to be written.
+    uint32_t obuf_record_sz_id = builder.GetUintConstantId(obuf_record_sz);
+    uint32_t mask_none_id =
+        builder.GetUintConstantId(uint32_t(spv::MemoryAccessMask::MaskNone));
+    uint32_t scope_invok_id =
+        builder.GetUintConstantId(uint32_t(spv::Scope::Invocation));
+    Instruction* obuf_curr_sz_inst = builder.AddQuadOp(
+        GetUintId(), spv::Op::OpAtomicIAdd, obuf_curr_sz_ac_inst->result_id(),
+        scope_invok_id, mask_none_id, obuf_record_sz_id);
+    uint32_t obuf_curr_sz_id = obuf_curr_sz_inst->result_id();
+    // Compute new written size
+    Instruction* obuf_new_sz_inst =
+        builder.AddIAdd(GetUintId(), obuf_curr_sz_id,
+                        builder.GetUintConstantId(obuf_record_sz));
+    // Fetch the data bound
+    Instruction* obuf_bnd_inst =
+        builder.AddIdLiteralOp(GetUintId(), spv::Op::OpArrayLength,
+                               GetOutputBufferId(), kDebugOutputDataOffset);
+    // Test that new written size is less than or equal to debug output
+    // data bound
+    Instruction* obuf_safe_inst = builder.AddBinaryOp(
+        GetBoolId(), spv::Op::OpULessThanEqual, obuf_new_sz_inst->result_id(),
+        obuf_bnd_inst->result_id());
+    uint32_t merge_blk_id = TakeNextId();
+    uint32_t write_blk_id = TakeNextId();
+    std::unique_ptr<Instruction> merge_label(NewLabel(merge_blk_id));
+    std::unique_ptr<Instruction> write_label(NewLabel(write_blk_id));
+    (void)builder.AddConditionalBranch(
+        obuf_safe_inst->result_id(), write_blk_id, merge_blk_id, merge_blk_id,
+        uint32_t(spv::SelectionControlMask::MaskNone));
+    // Close safety test block and gen write block
+    output_func->AddBasicBlock(std::move(new_blk_ptr));
+    new_blk_ptr = MakeUnique<BasicBlock>(std::move(write_label));
+    builder.SetInsertPoint(&*new_blk_ptr);
+    // Generate common and stage-specific debug record members
+    GenDebugOutputFieldCode(obuf_curr_sz_id, kInstCommonOutSize,
+                            builder.GetUintConstantId(obuf_record_sz),
+                            &builder);
+    // Store Shader Id
+    GenDebugOutputFieldCode(obuf_curr_sz_id, kInstCommonOutShaderId,
+                            param_ids[kShaderId], &builder);
+    // Store Instruction Idx
+    GenDebugOutputFieldCode(obuf_curr_sz_id, kInstCommonOutInstructionIdx,
+                            param_ids[kInstructionIndex], &builder);
+    // Store stage info. Stage Idx + 3 words of stage-specific data.
+    for (uint32_t i = 0; i < 4; ++i) {
+      Instruction* field =
+          builder.AddCompositeExtract(GetUintId(), param_ids[kStageInfo], {i});
+      GenDebugOutputFieldCode(obuf_curr_sz_id, kInstCommonOutStageIdx + i,
+                              field->result_id(), &builder);
+    }
+    // Gen writes of validation specific data
+    for (uint32_t i = 0; i < param_cnt; ++i) {
+      GenDebugOutputFieldCode(obuf_curr_sz_id, val_spec_offset + i,
+                              param_ids[kFirstParam + i], &builder);
+    }
+    // Close write block and gen merge block
+    (void)builder.AddBranch(merge_blk_id);
+    output_func->AddBasicBlock(std::move(new_blk_ptr));
+    new_blk_ptr = MakeUnique<BasicBlock>(std::move(merge_label));
+    builder.SetInsertPoint(&*new_blk_ptr);
+    // Close merge block and function and add function to module
+    (void)builder.AddNullaryOp(0, spv::Op::OpReturn);
+
+    output_func->AddBasicBlock(std::move(new_blk_ptr));
+    output_func->SetFunctionEnd(EndFunction());
+    context()->AddFunction(std::move(output_func));
+
+    std::string name("stream_write_");
+    name += std::to_string(param_cnt);
+
+    context()->AddDebug2Inst(
+        NewGlobalName(param2output_func_id_[param_cnt], name));
+  }
+  return param2output_func_id_[param_cnt];
+}
+
+void InstDebugPrintfPass::GenDebugStreamWrite(
+    uint32_t shader_id, uint32_t instruction_idx_id, uint32_t stage_info_id,
+    const std::vector<uint32_t>& validation_ids, InstructionBuilder* builder) {
+  // Call debug output function. Pass func_idx, instruction_idx and
+  // validation ids as args.
+  uint32_t val_id_cnt = static_cast<uint32_t>(validation_ids.size());
+  std::vector<uint32_t> args = {shader_id, instruction_idx_id, stage_info_id};
+  (void)args.insert(args.end(), validation_ids.begin(), validation_ids.end());
+  (void)builder->AddFunctionCall(GetVoidId(),
+                                 GetStreamWriteFunctionId(val_id_cnt), args);
+}
+
+std::unique_ptr<Instruction> InstDebugPrintfPass::NewGlobalName(
+    uint32_t id, const std::string& name_str) {
+  std::string prefixed_name{"inst_printf_"};
+  prefixed_name += name_str;
+  return NewName(id, prefixed_name);
+}
+
+std::unique_ptr<Instruction> InstDebugPrintfPass::NewMemberName(
+    uint32_t id, uint32_t member_index, const std::string& name_str) {
+  return MakeUnique<Instruction>(
+      context(), spv::Op::OpMemberName, 0, 0,
+      std::initializer_list<Operand>{
+          {SPV_OPERAND_TYPE_ID, {id}},
+          {SPV_OPERAND_TYPE_LITERAL_INTEGER, {member_index}},
+          {SPV_OPERAND_TYPE_LITERAL_STRING, utils::MakeVector(name_str)}});
+}
+
 void InstDebugPrintfPass::InitializeInstDebugPrintf() {
   // Initialize base class
   InitializeInstrument();
+  output_buffer_id_ = 0;
+  output_buffer_ptr_id_ = 0;
 }
 
 Pass::Status InstDebugPrintfPass::ProcessImpl() {
@@ -239,15 +477,7 @@
     }
   }
   if (!non_sem_set_seen) {
-    for (auto c_itr = context()->module()->extension_begin();
-         c_itr != context()->module()->extension_end(); ++c_itr) {
-      const std::string ext_name = c_itr->GetInOperand(0).AsString();
-      if (ext_name == "SPV_KHR_non_semantic_info") {
-        context()->KillInst(&*c_itr);
-        break;
-      }
-    }
-    context()->get_feature_mgr()->RemoveExtension(kSPV_KHR_non_semantic_info);
+    context()->RemoveExtension(kSPV_KHR_non_semantic_info);
   }
   return Status::SuccessWithChange;
 }
diff --git a/source/opt/inst_debug_printf_pass.h b/source/opt/inst_debug_printf_pass.h
index 70b0a72..3a2078a 100644
--- a/source/opt/inst_debug_printf_pass.h
+++ b/source/opt/inst_debug_printf_pass.h
@@ -28,10 +28,10 @@
 class InstDebugPrintfPass : public InstrumentPass {
  public:
   // For test harness only
-  InstDebugPrintfPass() : InstrumentPass(7, 23, kInstValidationIdDebugPrintf) {}
+  InstDebugPrintfPass() : InstrumentPass(7, 23) {}
   // For all other interfaces
   InstDebugPrintfPass(uint32_t desc_set, uint32_t shader_id)
-      : InstrumentPass(desc_set, shader_id, kInstValidationIdDebugPrintf) {}
+      : InstrumentPass(desc_set, shader_id) {}
 
   ~InstDebugPrintfPass() override = default;
 
@@ -41,6 +41,104 @@
   const char* name() const override { return "inst-printf-pass"; }
 
  private:
+  // Gen code into |builder| to write |field_value_id| into debug output
+  // buffer at |base_offset_id| + |field_offset|.
+  void GenDebugOutputFieldCode(uint32_t base_offset_id, uint32_t field_offset,
+                               uint32_t field_value_id,
+                               InstructionBuilder* builder);
+
+  // Generate instructions in |builder| which will atomically fetch and
+  // increment the size of the debug output buffer stream of the current
+  // validation and write a record to the end of the stream, if enough space
+  // in the buffer remains. The record will contain the index of the function
+  // and instruction within that function |func_idx, instruction_idx| which
+  // generated the record. It will also contain additional information to
+  // identify the instance of the shader, depending on the stage |stage_idx|
+  // of the shader. Finally, the record will contain validation-specific
+  // data contained in |validation_ids| which will identify the validation
+  // error as well as the values involved in the error.
+  //
+  // The output buffer binding written to by the code generated by the function
+  // is determined by the validation id specified when each specific
+  // instrumentation pass is created.
+  //
+  // The output buffer is a sequence of 32-bit values with the following
+  // format (where all elements are unsigned 32-bit unless otherwise noted):
+  //
+  //     Size
+  //     Record0
+  //     Record1
+  //     Record2
+  //     ...
+  //
+  // Size is the number of 32-bit values that have been written or
+  // attempted to be written to the output buffer, excluding the Size. It is
+  // initialized to 0. If the size of attempts to write the buffer exceeds
+  // the actual size of the buffer, it is possible that this field can exceed
+  // the actual size of the buffer.
+  //
+  // Each Record* is a variable-length sequence of 32-bit values with the
+  // following format defined using static const offsets in the .cpp file:
+  //
+  //     Record Size
+  //     Shader ID
+  //     Instruction Index
+  //     Stage
+  //     Stage-specific Word 0
+  //     Stage-specific Word 1
+  //     ...
+  //     Validation Error Code
+  //     Validation-specific Word 0
+  //     Validation-specific Word 1
+  //     Validation-specific Word 2
+  //     ...
+  //
+  // Each record consists of three subsections: members common across all
+  // validation, members specific to the stage, and members specific to a
+  // validation.
+  //
+  // The Record Size is the number of 32-bit words in the record, including
+  // the Record Size word.
+  //
+  // Shader ID is a value that identifies which shader has generated the
+  // validation error. It is passed when the instrumentation pass is created.
+  //
+  // The Instruction Index is the position of the instruction within the
+  // SPIR-V file which is in error.
+  //
+  // The Stage is the pipeline stage which has generated the error as defined
+  // by the SpvExecutionModel_ enumeration. This is used to interpret the
+  // following Stage-specific words.
+  //
+  // The Stage-specific Words identify which invocation of the shader generated
+  // the error. Every stage will write a fixed number of words. Vertex shaders
+  // will write the Vertex and Instance ID. Fragment shaders will write
+  // FragCoord.xy. Compute shaders will write the GlobalInvocation ID.
+  // The tessellation eval shader will write the Primitive ID and TessCoords.uv.
+  // The tessellation control shader and geometry shader will write the
+  // Primitive ID and Invocation ID.
+  //
+  // The Validation Error Code specifies the exact error which has occurred.
+  // These are enumerated with the kInstError* static consts. This allows
+  // multiple validation layers to use the same, single output buffer.
+  //
+  // The Validation-specific Words are a validation-specific number of 32-bit
+  // words which give further information on the validation error that
+  // occurred. These are documented further in each file containing the
+  // validation-specific class which derives from this base class.
+  //
+  // Because the code that is generated checks against the size of the buffer
+  // before writing, the size of the debug out buffer can be used by the
+  // validation layer to control the number of error records that are written.
+  void GenDebugStreamWrite(uint32_t shader_id, uint32_t instruction_idx_id,
+                           uint32_t stage_info_id,
+                           const std::vector<uint32_t>& validation_ids,
+                           InstructionBuilder* builder);
+
+  // Return id for output function. Define if it doesn't exist with
+  // |val_spec_param_cnt| validation-specific uint32 parameters.
+  uint32_t GetStreamWriteFunctionId(uint32_t val_spec_param_cnt);
+
   // Generate instructions for OpDebugPrintf.
   //
   // If |ref_inst_itr| is an OpDebugPrintf, return in |new_blocks| the result
@@ -80,13 +178,37 @@
   void GenOutputCode(Instruction* printf_inst, uint32_t stage_idx,
                      std::vector<std::unique_ptr<BasicBlock>>* new_blocks);
 
+  // Set the name for a function or global variable, names will be
+  // prefixed to identify which instrumentation pass generated them.
+  std::unique_ptr<Instruction> NewGlobalName(uint32_t id,
+                                             const std::string& name_str);
+
+  // Set the name for a structure member
+  std::unique_ptr<Instruction> NewMemberName(uint32_t id, uint32_t member_index,
+                                             const std::string& name_str);
+
+  // Return id for debug output buffer
+  uint32_t GetOutputBufferId();
+
+  // Return id for buffer uint type
+  uint32_t GetOutputBufferPtrId();
+
+  // Return binding for output buffer for current validation.
+  uint32_t GetOutputBufferBinding();
+
   // Initialize state for instrumenting bindless checking
   void InitializeInstDebugPrintf();
 
   // Apply GenDebugPrintfCode to every instruction in module.
   Pass::Status ProcessImpl();
 
-  uint32_t ext_inst_printf_id_;
+  uint32_t ext_inst_printf_id_{0};
+
+  // id for output buffer variable
+  uint32_t output_buffer_id_{0};
+
+  // ptr type id for output buffer element
+  uint32_t output_buffer_ptr_id_{0};
 };
 
 }  // namespace opt
diff --git a/source/opt/instruction.cpp b/source/opt/instruction.cpp
index e775a99..aa4ae26 100644
--- a/source/opt/instruction.cpp
+++ b/source/opt/instruction.cpp
@@ -24,38 +24,37 @@
 
 namespace spvtools {
 namespace opt {
-
 namespace {
 // Indices used to get particular operands out of instructions using InOperand.
-const uint32_t kTypeImageDimIndex = 1;
-const uint32_t kLoadBaseIndex = 0;
-const uint32_t kPointerTypeStorageClassIndex = 0;
-const uint32_t kVariableStorageClassIndex = 0;
-const uint32_t kTypeImageSampledIndex = 5;
+constexpr uint32_t kTypeImageDimIndex = 1;
+constexpr uint32_t kLoadBaseIndex = 0;
+constexpr uint32_t kPointerTypeStorageClassIndex = 0;
+constexpr uint32_t kVariableStorageClassIndex = 0;
+constexpr uint32_t kTypeImageSampledIndex = 5;
 
 // Constants for OpenCL.DebugInfo.100 / NonSemantic.Shader.DebugInfo.100
 // extension instructions.
-const uint32_t kExtInstSetIdInIdx = 0;
-const uint32_t kExtInstInstructionInIdx = 1;
-const uint32_t kDebugScopeNumWords = 7;
-const uint32_t kDebugScopeNumWordsWithoutInlinedAt = 6;
-const uint32_t kDebugNoScopeNumWords = 5;
+constexpr uint32_t kExtInstSetIdInIdx = 0;
+constexpr uint32_t kExtInstInstructionInIdx = 1;
+constexpr uint32_t kDebugScopeNumWords = 7;
+constexpr uint32_t kDebugScopeNumWordsWithoutInlinedAt = 6;
+constexpr uint32_t kDebugNoScopeNumWords = 5;
 
 // Number of operands of an OpBranchConditional instruction
 // with weights.
-const uint32_t kOpBranchConditionalWithWeightsNumOperands = 5;
+constexpr uint32_t kOpBranchConditionalWithWeightsNumOperands = 5;
 }  // namespace
 
 Instruction::Instruction(IRContext* c)
     : utils::IntrusiveNodeBase<Instruction>(),
       context_(c),
-      opcode_(SpvOpNop),
+      opcode_(spv::Op::OpNop),
       has_type_id_(false),
       has_result_id_(false),
       unique_id_(c->TakeNextUniqueId()),
       dbg_scope_(kNoDebugScope, kNoInlinedAt) {}
 
-Instruction::Instruction(IRContext* c, SpvOp op)
+Instruction::Instruction(IRContext* c, spv::Op op)
     : utils::IntrusiveNodeBase<Instruction>(),
       context_(c),
       opcode_(op),
@@ -68,12 +67,13 @@
                          std::vector<Instruction>&& dbg_line)
     : utils::IntrusiveNodeBase<Instruction>(),
       context_(c),
-      opcode_(static_cast<SpvOp>(inst.opcode)),
+      opcode_(static_cast<spv::Op>(inst.opcode)),
       has_type_id_(inst.type_id != 0),
       has_result_id_(inst.result_id != 0),
       unique_id_(c->TakeNextUniqueId()),
       dbg_line_insts_(std::move(dbg_line)),
       dbg_scope_(kNoDebugScope, kNoInlinedAt) {
+  operands_.reserve(inst.num_operands);
   for (uint32_t i = 0; i < inst.num_operands; ++i) {
     const auto& current_payload = inst.operands[i];
     operands_.emplace_back(
@@ -88,11 +88,12 @@
                          const DebugScope& dbg_scope)
     : utils::IntrusiveNodeBase<Instruction>(),
       context_(c),
-      opcode_(static_cast<SpvOp>(inst.opcode)),
+      opcode_(static_cast<spv::Op>(inst.opcode)),
       has_type_id_(inst.type_id != 0),
       has_result_id_(inst.result_id != 0),
       unique_id_(c->TakeNextUniqueId()),
       dbg_scope_(dbg_scope) {
+  operands_.reserve(inst.num_operands);
   for (uint32_t i = 0; i < inst.num_operands; ++i) {
     const auto& current_payload = inst.operands[i];
     operands_.emplace_back(
@@ -101,7 +102,7 @@
   }
 }
 
-Instruction::Instruction(IRContext* c, SpvOp op, uint32_t ty_id,
+Instruction::Instruction(IRContext* c, spv::Op op, uint32_t ty_id,
                          uint32_t res_id, const OperandList& in_operands)
     : utils::IntrusiveNodeBase<Instruction>(),
       context_(c),
@@ -111,6 +112,14 @@
       unique_id_(c->TakeNextUniqueId()),
       operands_(),
       dbg_scope_(kNoDebugScope, kNoInlinedAt) {
+  size_t operands_size = in_operands.size();
+  if (has_type_id_) {
+    operands_size++;
+  }
+  if (has_result_id_) {
+    operands_size++;
+  }
+  operands_.reserve(operands_size);
   if (has_type_id_) {
     operands_.emplace_back(spv_operand_type_t::SPV_OPERAND_TYPE_TYPE_ID,
                            std::initializer_list<uint32_t>{ty_id});
@@ -179,7 +188,7 @@
 }
 
 bool Instruction::HasBranchWeights() const {
-  if (opcode_ == SpvOpBranchConditional &&
+  if (opcode_ == spv::Op::OpBranchConditional &&
       NumOperands() == kOpBranchConditionalWithWeightsNumOperands) {
     return true;
   }
@@ -208,13 +217,13 @@
       return false;
     }
 
-    if (address_def->opcode() == SpvOpVariable) {
+    if (address_def->opcode() == spv::Op::OpVariable) {
       if (address_def->IsReadOnlyPointer()) {
         return true;
       }
     }
 
-    if (address_def->opcode() == SpvOpLoad) {
+    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) {
@@ -235,12 +244,12 @@
   bool done = false;
   while (!done) {
     switch (base_inst->opcode()) {
-      case SpvOpAccessChain:
-      case SpvOpInBoundsAccessChain:
-      case SpvOpPtrAccessChain:
-      case SpvOpInBoundsPtrAccessChain:
-      case SpvOpImageTexelPointer:
-      case SpvOpCopyObject:
+      case spv::Op::OpAccessChain:
+      case spv::Op::OpInBoundsAccessChain:
+      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.
         base = base_inst->GetSingleWordInOperand(0);
@@ -255,20 +264,20 @@
 }
 
 bool Instruction::IsReadOnlyPointer() const {
-  if (context()->get_feature_mgr()->HasCapability(SpvCapabilityShader))
+  if (context()->get_feature_mgr()->HasCapability(spv::Capability::Shader))
     return IsReadOnlyPointerShaders();
   else
     return IsReadOnlyPointerKernel();
 }
 
 bool Instruction::IsVulkanStorageImage() const {
-  if (opcode() != SpvOpTypePointer) {
+  if (opcode() != spv::Op::OpTypePointer) {
     return false;
   }
 
-  uint32_t storage_class =
-      GetSingleWordInOperand(kPointerTypeStorageClassIndex);
-  if (storage_class != SpvStorageClassUniformConstant) {
+  spv::StorageClass storage_class =
+      spv::StorageClass(GetSingleWordInOperand(kPointerTypeStorageClassIndex));
+  if (storage_class != spv::StorageClass::UniformConstant) {
     return false;
   }
 
@@ -276,17 +285,18 @@
       context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1));
 
   // Unpack the optional layer of arraying.
-  if (base_type->opcode() == SpvOpTypeArray ||
-      base_type->opcode() == SpvOpTypeRuntimeArray) {
+  if (base_type->opcode() == spv::Op::OpTypeArray ||
+      base_type->opcode() == spv::Op::OpTypeRuntimeArray) {
     base_type = context()->get_def_use_mgr()->GetDef(
         base_type->GetSingleWordInOperand(0));
   }
 
-  if (base_type->opcode() != SpvOpTypeImage) {
+  if (base_type->opcode() != spv::Op::OpTypeImage) {
     return false;
   }
 
-  if (base_type->GetSingleWordInOperand(kTypeImageDimIndex) == SpvDimBuffer) {
+  if (spv::Dim(base_type->GetSingleWordInOperand(kTypeImageDimIndex)) ==
+      spv::Dim::Buffer) {
     return false;
   }
 
@@ -296,13 +306,13 @@
 }
 
 bool Instruction::IsVulkanSampledImage() const {
-  if (opcode() != SpvOpTypePointer) {
+  if (opcode() != spv::Op::OpTypePointer) {
     return false;
   }
 
-  uint32_t storage_class =
-      GetSingleWordInOperand(kPointerTypeStorageClassIndex);
-  if (storage_class != SpvStorageClassUniformConstant) {
+  spv::StorageClass storage_class =
+      spv::StorageClass(GetSingleWordInOperand(kPointerTypeStorageClassIndex));
+  if (storage_class != spv::StorageClass::UniformConstant) {
     return false;
   }
 
@@ -310,17 +320,18 @@
       context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1));
 
   // Unpack the optional layer of arraying.
-  if (base_type->opcode() == SpvOpTypeArray ||
-      base_type->opcode() == SpvOpTypeRuntimeArray) {
+  if (base_type->opcode() == spv::Op::OpTypeArray ||
+      base_type->opcode() == spv::Op::OpTypeRuntimeArray) {
     base_type = context()->get_def_use_mgr()->GetDef(
         base_type->GetSingleWordInOperand(0));
   }
 
-  if (base_type->opcode() != SpvOpTypeImage) {
+  if (base_type->opcode() != spv::Op::OpTypeImage) {
     return false;
   }
 
-  if (base_type->GetSingleWordInOperand(kTypeImageDimIndex) == SpvDimBuffer) {
+  if (spv::Dim(base_type->GetSingleWordInOperand(kTypeImageDimIndex)) ==
+      spv::Dim::Buffer) {
     return false;
   }
 
@@ -330,13 +341,13 @@
 }
 
 bool Instruction::IsVulkanStorageTexelBuffer() const {
-  if (opcode() != SpvOpTypePointer) {
+  if (opcode() != spv::Op::OpTypePointer) {
     return false;
   }
 
-  uint32_t storage_class =
-      GetSingleWordInOperand(kPointerTypeStorageClassIndex);
-  if (storage_class != SpvStorageClassUniformConstant) {
+  spv::StorageClass storage_class =
+      spv::StorageClass(GetSingleWordInOperand(kPointerTypeStorageClassIndex));
+  if (storage_class != spv::StorageClass::UniformConstant) {
     return false;
   }
 
@@ -344,17 +355,18 @@
       context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1));
 
   // Unpack the optional layer of arraying.
-  if (base_type->opcode() == SpvOpTypeArray ||
-      base_type->opcode() == SpvOpTypeRuntimeArray) {
+  if (base_type->opcode() == spv::Op::OpTypeArray ||
+      base_type->opcode() == spv::Op::OpTypeRuntimeArray) {
     base_type = context()->get_def_use_mgr()->GetDef(
         base_type->GetSingleWordInOperand(0));
   }
 
-  if (base_type->opcode() != SpvOpTypeImage) {
+  if (base_type->opcode() != spv::Op::OpTypeImage) {
     return false;
   }
 
-  if (base_type->GetSingleWordInOperand(kTypeImageDimIndex) != SpvDimBuffer) {
+  if (spv::Dim(base_type->GetSingleWordInOperand(kTypeImageDimIndex)) !=
+      spv::Dim::Buffer) {
     return false;
   }
 
@@ -366,7 +378,7 @@
 bool Instruction::IsVulkanStorageBuffer() const {
   // Is there a difference between a "Storage buffer" and a "dynamic storage
   // buffer" in SPIR-V and do we care about the difference?
-  if (opcode() != SpvOpTypePointer) {
+  if (opcode() != spv::Op::OpTypePointer) {
     return false;
   }
 
@@ -374,28 +386,28 @@
       context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1));
 
   // Unpack the optional layer of arraying.
-  if (base_type->opcode() == SpvOpTypeArray ||
-      base_type->opcode() == SpvOpTypeRuntimeArray) {
+  if (base_type->opcode() == spv::Op::OpTypeArray ||
+      base_type->opcode() == spv::Op::OpTypeRuntimeArray) {
     base_type = context()->get_def_use_mgr()->GetDef(
         base_type->GetSingleWordInOperand(0));
   }
 
-  if (base_type->opcode() != SpvOpTypeStruct) {
+  if (base_type->opcode() != spv::Op::OpTypeStruct) {
     return false;
   }
 
-  uint32_t storage_class =
-      GetSingleWordInOperand(kPointerTypeStorageClassIndex);
-  if (storage_class == SpvStorageClassUniform) {
+  spv::StorageClass storage_class =
+      spv::StorageClass(GetSingleWordInOperand(kPointerTypeStorageClassIndex));
+  if (storage_class == spv::StorageClass::Uniform) {
     bool is_buffer_block = false;
     context()->get_decoration_mgr()->ForEachDecoration(
-        base_type->result_id(), SpvDecorationBufferBlock,
+        base_type->result_id(), uint32_t(spv::Decoration::BufferBlock),
         [&is_buffer_block](const Instruction&) { is_buffer_block = true; });
     return is_buffer_block;
-  } else if (storage_class == SpvStorageClassStorageBuffer) {
+  } else if (storage_class == spv::StorageClass::StorageBuffer) {
     bool is_block = false;
     context()->get_decoration_mgr()->ForEachDecoration(
-        base_type->result_id(), SpvDecorationBlock,
+        base_type->result_id(), uint32_t(spv::Decoration::Block),
         [&is_block](const Instruction&) { is_block = true; });
     return is_block;
   }
@@ -403,13 +415,14 @@
 }
 
 bool Instruction::IsVulkanStorageBufferVariable() const {
-  if (opcode() != SpvOpVariable) {
+  if (opcode() != spv::Op::OpVariable) {
     return false;
   }
 
-  uint32_t storage_class = GetSingleWordInOperand(kVariableStorageClassIndex);
-  if (storage_class == SpvStorageClassStorageBuffer ||
-      storage_class == SpvStorageClassUniform) {
+  spv::StorageClass storage_class =
+      spv::StorageClass(GetSingleWordInOperand(kVariableStorageClassIndex));
+  if (storage_class == spv::StorageClass::StorageBuffer ||
+      storage_class == spv::StorageClass::Uniform) {
     Instruction* var_type = context()->get_def_use_mgr()->GetDef(type_id());
     return var_type != nullptr && var_type->IsVulkanStorageBuffer();
   }
@@ -418,13 +431,13 @@
 }
 
 bool Instruction::IsVulkanUniformBuffer() const {
-  if (opcode() != SpvOpTypePointer) {
+  if (opcode() != spv::Op::OpTypePointer) {
     return false;
   }
 
-  uint32_t storage_class =
-      GetSingleWordInOperand(kPointerTypeStorageClassIndex);
-  if (storage_class != SpvStorageClassUniform) {
+  spv::StorageClass storage_class =
+      spv::StorageClass(GetSingleWordInOperand(kPointerTypeStorageClassIndex));
+  if (storage_class != spv::StorageClass::Uniform) {
     return false;
   }
 
@@ -432,19 +445,19 @@
       context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1));
 
   // Unpack the optional layer of arraying.
-  if (base_type->opcode() == SpvOpTypeArray ||
-      base_type->opcode() == SpvOpTypeRuntimeArray) {
+  if (base_type->opcode() == spv::Op::OpTypeArray ||
+      base_type->opcode() == spv::Op::OpTypeRuntimeArray) {
     base_type = context()->get_def_use_mgr()->GetDef(
         base_type->GetSingleWordInOperand(0));
   }
 
-  if (base_type->opcode() != SpvOpTypeStruct) {
+  if (base_type->opcode() != spv::Op::OpTypeStruct) {
     return false;
   }
 
   bool is_block = false;
   context()->get_decoration_mgr()->ForEachDecoration(
-      base_type->result_id(), SpvDecorationBlock,
+      base_type->result_id(), uint32_t(spv::Decoration::Block),
       [&is_block](const Instruction&) { is_block = true; });
   return is_block;
 }
@@ -455,27 +468,27 @@
   }
 
   Instruction* type_def = context()->get_def_use_mgr()->GetDef(type_id());
-  if (type_def->opcode() != SpvOpTypePointer) {
+  if (type_def->opcode() != spv::Op::OpTypePointer) {
     return false;
   }
 
-  uint32_t storage_class =
-      type_def->GetSingleWordInOperand(kPointerTypeStorageClassIndex);
+  spv::StorageClass storage_class = spv::StorageClass(
+      type_def->GetSingleWordInOperand(kPointerTypeStorageClassIndex));
 
   switch (storage_class) {
-    case SpvStorageClassUniformConstant:
+    case spv::StorageClass::UniformConstant:
       if (!type_def->IsVulkanStorageImage() &&
           !type_def->IsVulkanStorageTexelBuffer()) {
         return true;
       }
       break;
-    case SpvStorageClassUniform:
+    case spv::StorageClass::Uniform:
       if (!type_def->IsVulkanStorageBuffer()) {
         return true;
       }
       break;
-    case SpvStorageClassPushConstant:
-    case SpvStorageClassInput:
+    case spv::StorageClass::PushConstant:
+    case spv::StorageClass::Input:
       return true;
     default:
       break;
@@ -483,7 +496,7 @@
 
   bool is_nonwritable = false;
   context()->get_decoration_mgr()->ForEachDecoration(
-      result_id(), SpvDecorationNonWritable,
+      result_id(), uint32_t(spv::Decoration::NonWritable),
       [&is_nonwritable](const Instruction&) { is_nonwritable = true; });
   return is_nonwritable;
 }
@@ -494,14 +507,14 @@
   }
 
   Instruction* type_def = context()->get_def_use_mgr()->GetDef(type_id());
-  if (type_def->opcode() != SpvOpTypePointer) {
+  if (type_def->opcode() != spv::Op::OpTypePointer) {
     return false;
   }
 
-  uint32_t storage_class =
-      type_def->GetSingleWordInOperand(kPointerTypeStorageClassIndex);
+  spv::StorageClass storage_class = spv::StorageClass(
+      type_def->GetSingleWordInOperand(kPointerTypeStorageClassIndex));
 
-  return storage_class == SpvStorageClassUniformConstant;
+  return storage_class == spv::StorageClass::UniformConstant;
 }
 
 void Instruction::UpdateLexicalScope(uint32_t scope) {
@@ -564,13 +577,13 @@
 bool Instruction::IsLineInst() const { return IsLine() || IsNoLine(); }
 
 bool Instruction::IsLine() const {
-  if (opcode() == SpvOpLine) return true;
+  if (opcode() == spv::Op::OpLine) return true;
   NonSemanticShaderDebugInfo100Instructions ext_opt = GetShader100DebugOpcode();
   return ext_opt == NonSemanticShaderDebugInfo100DebugLine;
 }
 
 bool Instruction::IsNoLine() const {
-  if (opcode() == SpvOpNoLine) return true;
+  if (opcode() == spv::Op::OpNoLine) return true;
   NonSemanticShaderDebugInfo100Instructions ext_opt = GetShader100DebugOpcode();
   return ext_opt == NonSemanticShaderDebugInfo100DebugNoLine;
 }
@@ -597,33 +610,35 @@
   }
 
   Instruction* type = context()->get_def_use_mgr()->GetDef(tid);
-  if (type->opcode() != SpvOpTypePointer) {
+  if (type->opcode() != spv::Op::OpTypePointer) {
     return false;
   }
 
   auto feature_mgr = context()->get_feature_mgr();
-  if (feature_mgr->HasCapability(SpvCapabilityAddresses)) {
+  if (feature_mgr->HasCapability(spv::Capability::Addresses)) {
     // TODO: The rules here could be more restrictive.
     return true;
   }
 
-  if (opcode() == SpvOpVariable || opcode() == SpvOpFunctionParameter) {
+  if (opcode() == spv::Op::OpVariable ||
+      opcode() == spv::Op::OpFunctionParameter) {
     return true;
   }
 
   // With variable pointers, there are more valid base pointer objects.
   // Variable pointers implicitly declares Variable pointers storage buffer.
-  SpvStorageClass storage_class =
-      static_cast<SpvStorageClass>(type->GetSingleWordInOperand(0));
-  if ((feature_mgr->HasCapability(SpvCapabilityVariablePointersStorageBuffer) &&
-       storage_class == SpvStorageClassStorageBuffer) ||
-      (feature_mgr->HasCapability(SpvCapabilityVariablePointers) &&
-       storage_class == SpvStorageClassWorkgroup)) {
+  spv::StorageClass storage_class =
+      static_cast<spv::StorageClass>(type->GetSingleWordInOperand(0));
+  if ((feature_mgr->HasCapability(
+           spv::Capability::VariablePointersStorageBuffer) &&
+       storage_class == spv::StorageClass::StorageBuffer) ||
+      (feature_mgr->HasCapability(spv::Capability::VariablePointers) &&
+       storage_class == spv::StorageClass::Workgroup)) {
     switch (opcode()) {
-      case SpvOpPhi:
-      case SpvOpSelect:
-      case SpvOpFunctionCall:
-      case SpvOpConstantNull:
+      case spv::Op::OpPhi:
+      case spv::Op::OpSelect:
+      case spv::Op::OpFunctionCall:
+      case spv::Op::OpConstantNull:
         return true;
       default:
         break;
@@ -641,7 +656,7 @@
 }
 
 OpenCLDebugInfo100Instructions Instruction::GetOpenCL100DebugOpcode() const {
-  if (opcode() != SpvOpExtInst) {
+  if (opcode() != spv::Op::OpExtInst) {
     return OpenCLDebugInfo100InstructionsMax;
   }
 
@@ -660,7 +675,7 @@
 
 NonSemanticShaderDebugInfo100Instructions Instruction::GetShader100DebugOpcode()
     const {
-  if (opcode() != SpvOpExtInst) {
+  if (opcode() != spv::Op::OpExtInst) {
     return NonSemanticShaderDebugInfo100InstructionsMax;
   }
 
@@ -682,7 +697,7 @@
 }
 
 CommonDebugInfoInstructions Instruction::GetCommonDebugOpcode() const {
-  if (opcode() != SpvOpExtInst) {
+  if (opcode() != spv::Op::OpExtInst) {
     return CommonDebugInfoInstructionsMax;
   }
 
@@ -712,31 +727,31 @@
   }
 
   Instruction* type = context()->get_def_use_mgr()->GetDef(tid);
-  return (type->opcode() == SpvOpTypeImage ||
-          type->opcode() == SpvOpTypeSampledImage);
+  return (type->opcode() == spv::Op::OpTypeImage ||
+          type->opcode() == spv::Op::OpTypeSampledImage);
 }
 
 bool Instruction::IsOpaqueType() const {
-  if (opcode() == SpvOpTypeStruct) {
+  if (opcode() == spv::Op::OpTypeStruct) {
     bool is_opaque = false;
     ForEachInOperand([&is_opaque, this](const uint32_t* op_id) {
       Instruction* type_inst = context()->get_def_use_mgr()->GetDef(*op_id);
       is_opaque |= type_inst->IsOpaqueType();
     });
     return is_opaque;
-  } else if (opcode() == SpvOpTypeArray) {
+  } else if (opcode() == spv::Op::OpTypeArray) {
     uint32_t sub_type_id = GetSingleWordInOperand(0);
     Instruction* sub_type_inst =
         context()->get_def_use_mgr()->GetDef(sub_type_id);
     return sub_type_inst->IsOpaqueType();
   } else {
-    return opcode() == SpvOpTypeRuntimeArray ||
+    return opcode() == spv::Op::OpTypeRuntimeArray ||
            spvOpcodeIsBaseOpaqueType(opcode());
   }
 }
 
 bool Instruction::IsFoldable() const {
-  return IsFoldableByFoldScalar() ||
+  return IsFoldableByFoldScalar() || IsFoldableByFoldVector() ||
          context()->get_instruction_folder().HasConstFoldingRule(this);
 }
 
@@ -747,7 +762,7 @@
   }
 
   Instruction* type = context()->get_def_use_mgr()->GetDef(type_id());
-  if (!folder.IsFoldableType(type)) {
+  if (!folder.IsFoldableScalarType(type)) {
     return false;
   }
 
@@ -758,29 +773,52 @@
     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());
-    return folder.IsFoldableType(def_inst_type);
+    return folder.IsFoldableScalarType(def_inst_type);
+  });
+}
+
+bool Instruction::IsFoldableByFoldVector() const {
+  const InstructionFolder& folder = context()->get_instruction_folder();
+  if (!folder.IsFoldableOpcode(opcode())) {
+    return false;
+  }
+
+  Instruction* type = context()->get_def_use_mgr()->GetDef(type_id());
+  if (!folder.IsFoldableVectorType(type)) {
+    return false;
+  }
+
+  // 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) {
+    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());
+    return folder.IsFoldableVectorType(def_inst_type);
   });
 }
 
 bool Instruction::IsFloatingPointFoldingAllowed() const {
   // TODO: Add the rules for kernels.  For now it will be pessimistic.
   // For now, do not support capabilities introduced by SPV_KHR_float_controls.
-  if (!context_->get_feature_mgr()->HasCapability(SpvCapabilityShader) ||
-      context_->get_feature_mgr()->HasCapability(SpvCapabilityDenormPreserve) ||
+  if (!context_->get_feature_mgr()->HasCapability(spv::Capability::Shader) ||
       context_->get_feature_mgr()->HasCapability(
-          SpvCapabilityDenormFlushToZero) ||
+          spv::Capability::DenormPreserve) ||
       context_->get_feature_mgr()->HasCapability(
-          SpvCapabilitySignedZeroInfNanPreserve) ||
+          spv::Capability::DenormFlushToZero) ||
       context_->get_feature_mgr()->HasCapability(
-          SpvCapabilityRoundingModeRTZ) ||
+          spv::Capability::SignedZeroInfNanPreserve) ||
       context_->get_feature_mgr()->HasCapability(
-          SpvCapabilityRoundingModeRTE)) {
+          spv::Capability::RoundingModeRTZ) ||
+      context_->get_feature_mgr()->HasCapability(
+          spv::Capability::RoundingModeRTE)) {
     return false;
   }
 
   bool is_nocontract = false;
   context_->get_decoration_mgr()->WhileEachDecoration(
-      result_id(), SpvDecorationNoContraction,
+      result_id(), uint32_t(spv::Decoration::NoContraction),
       [&is_nocontract](const Instruction&) {
         is_nocontract = true;
         return false;
@@ -816,101 +854,101 @@
 
 bool Instruction::IsOpcodeCodeMotionSafe() const {
   switch (opcode_) {
-    case SpvOpNop:
-    case SpvOpUndef:
-    case SpvOpLoad:
-    case SpvOpAccessChain:
-    case SpvOpInBoundsAccessChain:
-    case SpvOpArrayLength:
-    case SpvOpVectorExtractDynamic:
-    case SpvOpVectorInsertDynamic:
-    case SpvOpVectorShuffle:
-    case SpvOpCompositeConstruct:
-    case SpvOpCompositeExtract:
-    case SpvOpCompositeInsert:
-    case SpvOpCopyObject:
-    case SpvOpTranspose:
-    case SpvOpConvertFToU:
-    case SpvOpConvertFToS:
-    case SpvOpConvertSToF:
-    case SpvOpConvertUToF:
-    case SpvOpUConvert:
-    case SpvOpSConvert:
-    case SpvOpFConvert:
-    case SpvOpQuantizeToF16:
-    case SpvOpBitcast:
-    case SpvOpSNegate:
-    case SpvOpFNegate:
-    case SpvOpIAdd:
-    case SpvOpFAdd:
-    case SpvOpISub:
-    case SpvOpFSub:
-    case SpvOpIMul:
-    case SpvOpFMul:
-    case SpvOpUDiv:
-    case SpvOpSDiv:
-    case SpvOpFDiv:
-    case SpvOpUMod:
-    case SpvOpSRem:
-    case SpvOpSMod:
-    case SpvOpFRem:
-    case SpvOpFMod:
-    case SpvOpVectorTimesScalar:
-    case SpvOpMatrixTimesScalar:
-    case SpvOpVectorTimesMatrix:
-    case SpvOpMatrixTimesVector:
-    case SpvOpMatrixTimesMatrix:
-    case SpvOpOuterProduct:
-    case SpvOpDot:
-    case SpvOpIAddCarry:
-    case SpvOpISubBorrow:
-    case SpvOpUMulExtended:
-    case SpvOpSMulExtended:
-    case SpvOpAny:
-    case SpvOpAll:
-    case SpvOpIsNan:
-    case SpvOpIsInf:
-    case SpvOpLogicalEqual:
-    case SpvOpLogicalNotEqual:
-    case SpvOpLogicalOr:
-    case SpvOpLogicalAnd:
-    case SpvOpLogicalNot:
-    case SpvOpSelect:
-    case SpvOpIEqual:
-    case SpvOpINotEqual:
-    case SpvOpUGreaterThan:
-    case SpvOpSGreaterThan:
-    case SpvOpUGreaterThanEqual:
-    case SpvOpSGreaterThanEqual:
-    case SpvOpULessThan:
-    case SpvOpSLessThan:
-    case SpvOpULessThanEqual:
-    case SpvOpSLessThanEqual:
-    case SpvOpFOrdEqual:
-    case SpvOpFUnordEqual:
-    case SpvOpFOrdNotEqual:
-    case SpvOpFUnordNotEqual:
-    case SpvOpFOrdLessThan:
-    case SpvOpFUnordLessThan:
-    case SpvOpFOrdGreaterThan:
-    case SpvOpFUnordGreaterThan:
-    case SpvOpFOrdLessThanEqual:
-    case SpvOpFUnordLessThanEqual:
-    case SpvOpFOrdGreaterThanEqual:
-    case SpvOpFUnordGreaterThanEqual:
-    case SpvOpShiftRightLogical:
-    case SpvOpShiftRightArithmetic:
-    case SpvOpShiftLeftLogical:
-    case SpvOpBitwiseOr:
-    case SpvOpBitwiseXor:
-    case SpvOpBitwiseAnd:
-    case SpvOpNot:
-    case SpvOpBitFieldInsert:
-    case SpvOpBitFieldSExtract:
-    case SpvOpBitFieldUExtract:
-    case SpvOpBitReverse:
-    case SpvOpBitCount:
-    case SpvOpSizeOf:
+    case spv::Op::OpNop:
+    case spv::Op::OpUndef:
+    case spv::Op::OpLoad:
+    case spv::Op::OpAccessChain:
+    case spv::Op::OpInBoundsAccessChain:
+    case spv::Op::OpArrayLength:
+    case spv::Op::OpVectorExtractDynamic:
+    case spv::Op::OpVectorInsertDynamic:
+    case spv::Op::OpVectorShuffle:
+    case spv::Op::OpCompositeConstruct:
+    case spv::Op::OpCompositeExtract:
+    case spv::Op::OpCompositeInsert:
+    case spv::Op::OpCopyObject:
+    case spv::Op::OpTranspose:
+    case spv::Op::OpConvertFToU:
+    case spv::Op::OpConvertFToS:
+    case spv::Op::OpConvertSToF:
+    case spv::Op::OpConvertUToF:
+    case spv::Op::OpUConvert:
+    case spv::Op::OpSConvert:
+    case spv::Op::OpFConvert:
+    case spv::Op::OpQuantizeToF16:
+    case spv::Op::OpBitcast:
+    case spv::Op::OpSNegate:
+    case spv::Op::OpFNegate:
+    case spv::Op::OpIAdd:
+    case spv::Op::OpFAdd:
+    case spv::Op::OpISub:
+    case spv::Op::OpFSub:
+    case spv::Op::OpIMul:
+    case spv::Op::OpFMul:
+    case spv::Op::OpUDiv:
+    case spv::Op::OpSDiv:
+    case spv::Op::OpFDiv:
+    case spv::Op::OpUMod:
+    case spv::Op::OpSRem:
+    case spv::Op::OpSMod:
+    case spv::Op::OpFRem:
+    case spv::Op::OpFMod:
+    case spv::Op::OpVectorTimesScalar:
+    case spv::Op::OpMatrixTimesScalar:
+    case spv::Op::OpVectorTimesMatrix:
+    case spv::Op::OpMatrixTimesVector:
+    case spv::Op::OpMatrixTimesMatrix:
+    case spv::Op::OpOuterProduct:
+    case spv::Op::OpDot:
+    case spv::Op::OpIAddCarry:
+    case spv::Op::OpISubBorrow:
+    case spv::Op::OpUMulExtended:
+    case spv::Op::OpSMulExtended:
+    case spv::Op::OpAny:
+    case spv::Op::OpAll:
+    case spv::Op::OpIsNan:
+    case spv::Op::OpIsInf:
+    case spv::Op::OpLogicalEqual:
+    case spv::Op::OpLogicalNotEqual:
+    case spv::Op::OpLogicalOr:
+    case spv::Op::OpLogicalAnd:
+    case spv::Op::OpLogicalNot:
+    case spv::Op::OpSelect:
+    case spv::Op::OpIEqual:
+    case spv::Op::OpINotEqual:
+    case spv::Op::OpUGreaterThan:
+    case spv::Op::OpSGreaterThan:
+    case spv::Op::OpUGreaterThanEqual:
+    case spv::Op::OpSGreaterThanEqual:
+    case spv::Op::OpULessThan:
+    case spv::Op::OpSLessThan:
+    case spv::Op::OpULessThanEqual:
+    case spv::Op::OpSLessThanEqual:
+    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::OpShiftRightLogical:
+    case spv::Op::OpShiftRightArithmetic:
+    case spv::Op::OpShiftLeftLogical:
+    case spv::Op::OpBitwiseOr:
+    case spv::Op::OpBitwiseXor:
+    case spv::Op::OpBitwiseAnd:
+    case spv::Op::OpNot:
+    case spv::Op::OpBitFieldInsert:
+    case spv::Op::OpBitFieldSExtract:
+    case spv::Op::OpBitFieldUExtract:
+    case spv::Op::OpBitReverse:
+    case spv::Op::OpBitCount:
+    case spv::Op::OpSizeOf:
       return true;
     default:
       return false;
@@ -922,7 +960,7 @@
     return true;
   }
 
-  if (opcode() == SpvOpExtInst) {
+  if (opcode() == spv::Op::OpExtInst) {
     uint32_t instSetId =
         context()->get_feature_mgr()->GetExtInstImportId_GLSLstd450();
 
@@ -997,16 +1035,16 @@
   }
 
   switch (opcode()) {
-    case SpvOpDPdx:
-    case SpvOpDPdy:
-    case SpvOpFwidth:
-    case SpvOpDPdxFine:
-    case SpvOpDPdyFine:
-    case SpvOpFwidthFine:
-    case SpvOpDPdxCoarse:
-    case SpvOpDPdyCoarse:
-    case SpvOpFwidthCoarse:
-    case SpvOpImageQueryLod:
+    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:
+    case spv::Op::OpImageQueryLod:
       return true;
     default:
       return false;
@@ -1015,7 +1053,7 @@
 
 bool Instruction::IsNonSemanticInstruction() const {
   if (!HasResultId()) return false;
-  if (opcode() != SpvOpExtInst) return false;
+  if (opcode() != spv::Op::OpExtInst) return false;
 
   auto import_inst =
       context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(0));
@@ -1035,7 +1073,7 @@
     num_words = kDebugScopeNumWordsWithoutInlinedAt;
   }
   std::vector<uint32_t> operands = {
-      (num_words << 16) | static_cast<uint16_t>(SpvOpExtInst),
+      (num_words << 16) | static_cast<uint16_t>(spv::Op::OpExtInst),
       type_id,
       result_id,
       ext_set,
diff --git a/source/opt/instruction.h b/source/opt/instruction.h
index e79c628..c2617fb 100644
--- a/source/opt/instruction.h
+++ b/source/opt/instruction.h
@@ -36,8 +36,8 @@
 #include "source/util/string_utils.h"
 #include "spirv-tools/libspirv.h"
 
-const uint32_t kNoDebugScope = 0;
-const uint32_t kNoInlinedAt = 0;
+constexpr uint32_t kNoDebugScope = 0;
+constexpr uint32_t kNoInlinedAt = 0;
 
 namespace spvtools {
 namespace opt {
@@ -190,7 +190,7 @@
   Instruction()
       : utils::IntrusiveNodeBase<Instruction>(),
         context_(nullptr),
-        opcode_(SpvOpNop),
+        opcode_(spv::Op::OpNop),
         has_type_id_(false),
         has_result_id_(false),
         unique_id_(0),
@@ -200,7 +200,7 @@
   Instruction(IRContext*);
   // Creates an instruction with the given opcode |op| and no additional logical
   // operands.
-  Instruction(IRContext*, SpvOp);
+  Instruction(IRContext*, spv::Op);
   // Creates an instruction using the given spv_parsed_instruction_t |inst|. All
   // the data inside |inst| will be copied and owned in this instance. And keep
   // record of line-related debug instructions |dbg_line| ahead of this
@@ -213,7 +213,7 @@
 
   // Creates an instruction with the given opcode |op|, type id: |ty_id|,
   // result id: |res_id| and input operands: |in_operands|.
-  Instruction(IRContext* c, SpvOp op, uint32_t ty_id, uint32_t res_id,
+  Instruction(IRContext* c, spv::Op op, uint32_t ty_id, uint32_t res_id,
               const OperandList& in_operands);
 
   // TODO: I will want to remove these, but will first have to remove the use of
@@ -235,12 +235,12 @@
 
   IRContext* context() const { return context_; }
 
-  SpvOp opcode() const { return opcode_; }
+  spv::Op opcode() const { return opcode_; }
   // Sets the opcode of this instruction to a specific opcode. Note this may
   // invalidate the instruction.
   // TODO(qining): Remove this function when instruction building and insertion
   // is well implemented.
-  void SetOpcode(SpvOp op) { opcode_ = op; }
+  void SetOpcode(spv::Op op) { opcode_ = op; }
   uint32_t type_id() const {
     return has_type_id_ ? GetSingleWordOperand(0) : 0;
   }
@@ -294,6 +294,8 @@
   // It is the responsibility of the caller to make sure
   // that the instruction remains valid.
   inline void AddOperand(Operand&& operand);
+  // Adds a copy of |operand| to the list of operands of this instruction.
+  inline void AddOperand(const Operand& operand);
   // Gets the |index|-th logical operand as a single SPIR-V word. This method is
   // not expected to be used with logical operands consisting of multiple SPIR-V
   // words.
@@ -522,6 +524,10 @@
   // constant value by |FoldScalar|.
   bool IsFoldableByFoldScalar() const;
 
+  // Returns true if |this| is an instruction which could be folded into a
+  // constant value by |FoldVector|.
+  bool IsFoldableByFoldVector() const;
+
   // Returns true if we are allowed to fold or otherwise manipulate the
   // instruction that defines |id| in the given context. This includes not
   // handling NaN values.
@@ -625,7 +631,7 @@
   bool IsValidBaseImage() const;
 
   IRContext* context_;  // IR Context
-  SpvOp opcode_;        // Opcode
+  spv::Op opcode_;      // Opcode
   bool has_type_id_;    // True if the instruction has a type id
   bool has_result_id_;  // True if the instruction has a result id
   uint32_t unique_id_;  // Unique instruction id
@@ -676,6 +682,10 @@
   operands_.push_back(std::move(operand));
 }
 
+inline void Instruction::AddOperand(const Operand& operand) {
+  operands_.push_back(operand);
+}
+
 inline void Instruction::SetInOperand(uint32_t index,
                                       Operand::OperandData&& data) {
   SetOperand(index + TypeResultIdCount(), std::move(data));
@@ -732,12 +742,12 @@
 }
 
 inline bool Instruction::IsNop() const {
-  return opcode_ == SpvOpNop && !has_type_id_ && !has_result_id_ &&
+  return opcode_ == spv::Op::OpNop && !has_type_id_ && !has_result_id_ &&
          operands_.empty();
 }
 
 inline void Instruction::ToNop() {
-  opcode_ = SpvOpNop;
+  opcode_ = spv::Op::OpNop;
   has_type_id_ = false;
   has_result_id_ = false;
   operands_.clear();
@@ -879,12 +889,12 @@
 
 inline bool Instruction::HasLabels() const {
   switch (opcode_) {
-    case SpvOpSelectionMerge:
-    case SpvOpBranch:
-    case SpvOpLoopMerge:
-    case SpvOpBranchConditional:
-    case SpvOpSwitch:
-    case SpvOpPhi:
+    case spv::Op::OpSelectionMerge:
+    case spv::Op::OpBranch:
+    case spv::Op::OpLoopMerge:
+    case spv::Op::OpBranchConditional:
+    case spv::Op::OpSwitch:
+    case spv::Op::OpPhi:
       return true;
       break;
     default:
@@ -906,7 +916,7 @@
 bool Instruction::IsAtomicOp() const { return spvOpcodeIsAtomicOp(opcode()); }
 
 bool Instruction::IsConstant() const {
-  return IsCompileTimeConstantInst(opcode());
+  return IsConstantInst(opcode()) && !IsSpecConstantInst(opcode());
 }
 }  // namespace opt
 }  // namespace spvtools
diff --git a/source/opt/instrument_pass.cpp b/source/opt/instrument_pass.cpp
index d143d59..829de49 100644
--- a/source/opt/instrument_pass.cpp
+++ b/source/opt/instrument_pass.cpp
@@ -19,20 +19,12 @@
 #include "source/cfa.h"
 #include "source/spirv_constant.h"
 
-namespace {
-
-// Common Parameter Positions
-static const int kInstCommonParamInstIdx = 0;
-static const int kInstCommonParamCnt = 1;
-
-// Indices of operands in SPIR-V instructions
-static const int kEntryPointExecutionModelInIdx = 0;
-static const int kEntryPointFunctionIdInIdx = 1;
-
-}  // anonymous namespace
-
 namespace spvtools {
 namespace opt {
+namespace {
+// Indices of operands in SPIR-V instructions
+constexpr int kEntryPointFunctionIdInIdx = 1;
+}  // namespace
 
 void InstrumentPass::MovePreludeCode(
     BasicBlock::iterator ref_inst_itr,
@@ -59,7 +51,6 @@
 
 void InstrumentPass::MovePostludeCode(
     UptrVectorIterator<BasicBlock> ref_block_itr, BasicBlock* new_blk_ptr) {
-  // new_blk_ptr->reset(new BasicBlock(NewLabel(ref_block_itr->id())));
   // Move contents of original ref block.
   for (auto cii = ref_block_itr->begin(); cii != ref_block_itr->end();
        cii = ref_block_itr->begin()) {
@@ -82,55 +73,62 @@
 }
 
 std::unique_ptr<Instruction> InstrumentPass::NewLabel(uint32_t label_id) {
-  std::unique_ptr<Instruction> newLabel(
-      new Instruction(context(), SpvOpLabel, 0, label_id, {}));
-  get_def_use_mgr()->AnalyzeInstDefUse(&*newLabel);
-  return newLabel;
+  auto new_label =
+      MakeUnique<Instruction>(context(), spv::Op::OpLabel, 0, label_id,
+                              std::initializer_list<Operand>{});
+  get_def_use_mgr()->AnalyzeInstDefUse(&*new_label);
+  return new_label;
+}
+
+std::unique_ptr<Function> InstrumentPass::StartFunction(
+    uint32_t func_id, const analysis::Type* return_type,
+    const std::vector<const analysis::Type*>& param_types) {
+  analysis::TypeManager* type_mgr = context()->get_type_mgr();
+  analysis::Function* func_type = GetFunction(return_type, param_types);
+
+  const std::vector<Operand> operands{
+      {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER,
+       {uint32_t(spv::FunctionControlMask::MaskNone)}},
+      {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {type_mgr->GetId(func_type)}},
+  };
+  auto func_inst =
+      MakeUnique<Instruction>(context(), spv::Op::OpFunction,
+                              type_mgr->GetId(return_type), func_id, operands);
+  get_def_use_mgr()->AnalyzeInstDefUse(&*func_inst);
+  return MakeUnique<Function>(std::move(func_inst));
+}
+
+std::unique_ptr<Instruction> InstrumentPass::EndFunction() {
+  auto end = MakeUnique<Instruction>(context(), spv::Op::OpFunctionEnd, 0, 0,
+                                     std::initializer_list<Operand>{});
+  get_def_use_mgr()->AnalyzeInstDefUse(end.get());
+  return end;
+}
+
+std::vector<uint32_t> InstrumentPass::AddParameters(
+    Function& func, const std::vector<const analysis::Type*>& param_types) {
+  std::vector<uint32_t> param_ids;
+  param_ids.reserve(param_types.size());
+  for (const analysis::Type* param : param_types) {
+    uint32_t pid = TakeNextId();
+    param_ids.push_back(pid);
+    auto param_inst =
+        MakeUnique<Instruction>(context(), spv::Op::OpFunctionParameter,
+                                context()->get_type_mgr()->GetId(param), pid,
+                                std::initializer_list<Operand>{});
+    get_def_use_mgr()->AnalyzeInstDefUse(param_inst.get());
+    func.AddParameter(std::move(param_inst));
+  }
+  return param_ids;
 }
 
 std::unique_ptr<Instruction> InstrumentPass::NewName(
     uint32_t id, const std::string& name_str) {
-  std::unique_ptr<Instruction> new_name(new Instruction(
-      context(), SpvOpName, 0, 0,
+  return MakeUnique<Instruction>(
+      context(), spv::Op::OpName, 0, 0,
       std::initializer_list<Operand>{
           {SPV_OPERAND_TYPE_ID, {id}},
-          {SPV_OPERAND_TYPE_LITERAL_STRING, utils::MakeVector(name_str)}}));
-
-  return new_name;
-}
-
-std::unique_ptr<Instruction> InstrumentPass::NewGlobalName(
-    uint32_t id, const std::string& name_str) {
-  std::string prefixed_name;
-  switch (validation_id_) {
-    case kInstValidationIdBindless:
-      prefixed_name = "inst_bindless_";
-      break;
-    case kInstValidationIdBuffAddr:
-      prefixed_name = "inst_buff_addr_";
-      break;
-    case kInstValidationIdDebugPrintf:
-      prefixed_name = "inst_printf_";
-      break;
-    default:
-      assert(false);  // add new instrumentation pass here
-      prefixed_name = "inst_pass_";
-      break;
-  }
-  prefixed_name += name_str;
-  return NewName(id, prefixed_name);
-}
-
-std::unique_ptr<Instruction> InstrumentPass::NewMemberName(
-    uint32_t id, uint32_t member_index, const std::string& name_str) {
-  std::unique_ptr<Instruction> new_name(new Instruction(
-      context(), SpvOpMemberName, 0, 0,
-      std::initializer_list<Operand>{
-          {SPV_OPERAND_TYPE_ID, {id}},
-          {SPV_OPERAND_TYPE_LITERAL_INTEGER, {member_index}},
-          {SPV_OPERAND_TYPE_LITERAL_STRING, utils::MakeVector(name_str)}}));
-
-  return new_name;
+          {SPV_OPERAND_TYPE_LITERAL_STRING, utils::MakeVector(name_str)}});
 }
 
 uint32_t InstrumentPass::Gen32BitCvtCode(uint32_t val_id,
@@ -145,10 +143,10 @@
   analysis::Type* val_32b_reg_ty = type_mgr->GetRegisteredType(&val_32b_ty);
   uint32_t val_32b_reg_ty_id = type_mgr->GetId(val_32b_reg_ty);
   if (is_signed)
-    return builder->AddUnaryOp(val_32b_reg_ty_id, SpvOpSConvert, val_id)
+    return builder->AddUnaryOp(val_32b_reg_ty_id, spv::Op::OpSConvert, val_id)
         ->result_id();
   else
-    return builder->AddUnaryOp(val_32b_reg_ty_id, SpvOpUConvert, val_id)
+    return builder->AddUnaryOp(val_32b_reg_ty_id, spv::Op::OpUConvert, val_id)
         ->result_id();
 }
 
@@ -161,195 +159,124 @@
   uint32_t val_ty_id = get_def_use_mgr()->GetDef(val_32b_id)->type_id();
   analysis::Integer* val_ty = type_mgr->GetType(val_ty_id)->AsInteger();
   if (!val_ty->IsSigned()) return val_32b_id;
-  return builder->AddUnaryOp(GetUintId(), SpvOpBitcast, val_32b_id)
+  return builder->AddUnaryOp(GetUintId(), spv::Op::OpBitcast, val_32b_id)
       ->result_id();
 }
 
-void InstrumentPass::GenDebugOutputFieldCode(uint32_t base_offset_id,
-                                             uint32_t field_offset,
-                                             uint32_t field_value_id,
-                                             InstructionBuilder* builder) {
-  // Cast value to 32-bit unsigned if necessary
-  uint32_t val_id = GenUintCastCode(field_value_id, builder);
-  // Store value
-  Instruction* data_idx_inst =
-      builder->AddBinaryOp(GetUintId(), SpvOpIAdd, base_offset_id,
-                           builder->GetUintConstantId(field_offset));
-  uint32_t buf_id = GetOutputBufferId();
-  uint32_t buf_uint_ptr_id = GetOutputBufferPtrId();
-  Instruction* achain_inst =
-      builder->AddTernaryOp(buf_uint_ptr_id, SpvOpAccessChain, buf_id,
-                            builder->GetUintConstantId(kDebugOutputDataOffset),
-                            data_idx_inst->result_id());
-  (void)builder->AddBinaryOp(0, SpvOpStore, achain_inst->result_id(), val_id);
-}
-
-void InstrumentPass::GenCommonStreamWriteCode(uint32_t record_sz,
-                                              uint32_t inst_id,
-                                              uint32_t stage_idx,
-                                              uint32_t base_offset_id,
-                                              InstructionBuilder* builder) {
-  // Store record size
-  GenDebugOutputFieldCode(base_offset_id, kInstCommonOutSize,
-                          builder->GetUintConstantId(record_sz), builder);
-  // Store Shader Id
-  GenDebugOutputFieldCode(base_offset_id, kInstCommonOutShaderId,
-                          builder->GetUintConstantId(shader_id_), builder);
-  // Store Instruction Idx
-  GenDebugOutputFieldCode(base_offset_id, kInstCommonOutInstructionIdx, inst_id,
-                          builder);
-  // Store Stage Idx
-  GenDebugOutputFieldCode(base_offset_id, kInstCommonOutStageIdx,
-                          builder->GetUintConstantId(stage_idx), builder);
-}
-
-void InstrumentPass::GenFragCoordEltDebugOutputCode(
-    uint32_t base_offset_id, uint32_t uint_frag_coord_id, uint32_t element,
-    InstructionBuilder* builder) {
-  Instruction* element_val_inst = builder->AddIdLiteralOp(
-      GetUintId(), SpvOpCompositeExtract, uint_frag_coord_id, element);
-  GenDebugOutputFieldCode(base_offset_id, kInstFragOutFragCoordX + element,
-                          element_val_inst->result_id(), builder);
-}
-
 uint32_t InstrumentPass::GenVarLoad(uint32_t var_id,
                                     InstructionBuilder* builder) {
   Instruction* var_inst = get_def_use_mgr()->GetDef(var_id);
   uint32_t type_id = GetPointeeTypeId(var_inst);
-  Instruction* load_inst = builder->AddUnaryOp(type_id, SpvOpLoad, var_id);
+  Instruction* load_inst = builder->AddLoad(type_id, var_id);
   return load_inst->result_id();
 }
 
-void InstrumentPass::GenBuiltinOutputCode(uint32_t builtin_id,
-                                          uint32_t builtin_off,
-                                          uint32_t base_offset_id,
-                                          InstructionBuilder* builder) {
-  // Load and store builtin
-  uint32_t load_id = GenVarLoad(builtin_id, builder);
-  GenDebugOutputFieldCode(base_offset_id, builtin_off, load_id, builder);
-}
-
-void InstrumentPass::GenStageStreamWriteCode(uint32_t stage_idx,
-                                             uint32_t base_offset_id,
-                                             InstructionBuilder* builder) {
+uint32_t InstrumentPass::GenStageInfo(uint32_t stage_idx,
+                                      InstructionBuilder* builder) {
+  std::vector<uint32_t> ids(4, builder->GetUintConstantId(0));
+  ids[0] = builder->GetUintConstantId(stage_idx);
+  // %289 = OpCompositeConstruct %v4uint %uint_0 %285 %288 %uint_0
   // TODO(greg-lunarg): Add support for all stages
-  switch (stage_idx) {
-    case SpvExecutionModelVertex: {
+  switch (spv::ExecutionModel(stage_idx)) {
+    case spv::ExecutionModel::Vertex: {
       // Load and store VertexId and InstanceId
-      GenBuiltinOutputCode(
-          context()->GetBuiltinInputVarId(SpvBuiltInVertexIndex),
-          kInstVertOutVertexIndex, base_offset_id, builder);
-      GenBuiltinOutputCode(
-          context()->GetBuiltinInputVarId(SpvBuiltInInstanceIndex),
-          kInstVertOutInstanceIndex, base_offset_id, builder);
-    } break;
-    case SpvExecutionModelGLCompute:
-    case SpvExecutionModelTaskNV:
-    case SpvExecutionModelMeshNV:
-    case SpvExecutionModelTaskEXT:
-    case SpvExecutionModelMeshEXT: {
-      // Load and store GlobalInvocationId.
       uint32_t load_id = GenVarLoad(
-          context()->GetBuiltinInputVarId(SpvBuiltInGlobalInvocationId),
+          context()->GetBuiltinInputVarId(uint32_t(spv::BuiltIn::VertexIndex)),
           builder);
-      Instruction* x_inst = builder->AddIdLiteralOp(
-          GetUintId(), SpvOpCompositeExtract, load_id, 0);
-      Instruction* y_inst = builder->AddIdLiteralOp(
-          GetUintId(), SpvOpCompositeExtract, load_id, 1);
-      Instruction* z_inst = builder->AddIdLiteralOp(
-          GetUintId(), SpvOpCompositeExtract, load_id, 2);
-      GenDebugOutputFieldCode(base_offset_id, kInstCompOutGlobalInvocationIdX,
-                              x_inst->result_id(), builder);
-      GenDebugOutputFieldCode(base_offset_id, kInstCompOutGlobalInvocationIdY,
-                              y_inst->result_id(), builder);
-      GenDebugOutputFieldCode(base_offset_id, kInstCompOutGlobalInvocationIdZ,
-                              z_inst->result_id(), builder);
+      ids[1] = GenUintCastCode(load_id, builder);
+
+      load_id = GenVarLoad(context()->GetBuiltinInputVarId(
+                               uint32_t(spv::BuiltIn::InstanceIndex)),
+                           builder);
+      ids[2] = GenUintCastCode(load_id, builder);
     } break;
-    case SpvExecutionModelGeometry: {
+    case spv::ExecutionModel::GLCompute:
+    case spv::ExecutionModel::TaskNV:
+    case spv::ExecutionModel::MeshNV:
+    case spv::ExecutionModel::TaskEXT:
+    case spv::ExecutionModel::MeshEXT: {
+      // Load and store GlobalInvocationId.
+      uint32_t load_id = GenVarLoad(context()->GetBuiltinInputVarId(uint32_t(
+                                        spv::BuiltIn::GlobalInvocationId)),
+                                    builder);
+      for (uint32_t u = 0; u < 3u; ++u) {
+        ids[u + 1] = builder->AddCompositeExtract(GetUintId(), load_id, {u})
+                         ->result_id();
+      }
+    } break;
+    case spv::ExecutionModel::Geometry: {
       // Load and store PrimitiveId and InvocationId.
-      GenBuiltinOutputCode(
-          context()->GetBuiltinInputVarId(SpvBuiltInPrimitiveId),
-          kInstGeomOutPrimitiveId, base_offset_id, builder);
-      GenBuiltinOutputCode(
-          context()->GetBuiltinInputVarId(SpvBuiltInInvocationId),
-          kInstGeomOutInvocationId, base_offset_id, builder);
-    } break;
-    case SpvExecutionModelTessellationControl: {
-      // Load and store InvocationId and PrimitiveId
-      GenBuiltinOutputCode(
-          context()->GetBuiltinInputVarId(SpvBuiltInInvocationId),
-          kInstTessCtlOutInvocationId, base_offset_id, builder);
-      GenBuiltinOutputCode(
-          context()->GetBuiltinInputVarId(SpvBuiltInPrimitiveId),
-          kInstTessCtlOutPrimitiveId, base_offset_id, builder);
-    } break;
-    case SpvExecutionModelTessellationEvaluation: {
-      // Load and store PrimitiveId and TessCoord.uv
-      GenBuiltinOutputCode(
-          context()->GetBuiltinInputVarId(SpvBuiltInPrimitiveId),
-          kInstTessEvalOutPrimitiveId, base_offset_id, builder);
       uint32_t load_id = GenVarLoad(
-          context()->GetBuiltinInputVarId(SpvBuiltInTessCoord), builder);
+          context()->GetBuiltinInputVarId(uint32_t(spv::BuiltIn::PrimitiveId)),
+          builder);
+      ids[1] = load_id;
+      load_id = GenVarLoad(
+          context()->GetBuiltinInputVarId(uint32_t(spv::BuiltIn::InvocationId)),
+          builder);
+      ids[2] = load_id;
+    } break;
+    case spv::ExecutionModel::TessellationControl: {
+      // Load and store InvocationId and PrimitiveId
+      uint32_t load_id = GenVarLoad(
+          context()->GetBuiltinInputVarId(uint32_t(spv::BuiltIn::InvocationId)),
+          builder);
+      ids[1] = load_id;
+      load_id = GenVarLoad(
+          context()->GetBuiltinInputVarId(uint32_t(spv::BuiltIn::PrimitiveId)),
+          builder);
+      ids[2] = load_id;
+    } break;
+    case spv::ExecutionModel::TessellationEvaluation: {
+      // Load and store PrimitiveId and TessCoord.uv
+      uint32_t load_id = GenVarLoad(
+          context()->GetBuiltinInputVarId(uint32_t(spv::BuiltIn::PrimitiveId)),
+          builder);
+      ids[1] = load_id;
+      load_id = GenVarLoad(
+          context()->GetBuiltinInputVarId(uint32_t(spv::BuiltIn::TessCoord)),
+          builder);
       Instruction* uvec3_cast_inst =
-          builder->AddUnaryOp(GetVec3UintId(), SpvOpBitcast, load_id);
+          builder->AddUnaryOp(GetVec3UintId(), spv::Op::OpBitcast, load_id);
       uint32_t uvec3_cast_id = uvec3_cast_inst->result_id();
-      Instruction* u_inst = builder->AddIdLiteralOp(
-          GetUintId(), SpvOpCompositeExtract, uvec3_cast_id, 0);
-      Instruction* v_inst = builder->AddIdLiteralOp(
-          GetUintId(), SpvOpCompositeExtract, uvec3_cast_id, 1);
-      GenDebugOutputFieldCode(base_offset_id, kInstTessEvalOutTessCoordU,
-                              u_inst->result_id(), builder);
-      GenDebugOutputFieldCode(base_offset_id, kInstTessEvalOutTessCoordV,
-                              v_inst->result_id(), builder);
+      for (uint32_t u = 0; u < 2u; ++u) {
+        ids[u + 2] =
+            builder->AddCompositeExtract(GetUintId(), uvec3_cast_id, {u})
+                ->result_id();
+      }
     } break;
-    case SpvExecutionModelFragment: {
+    case spv::ExecutionModel::Fragment: {
       // Load FragCoord and convert to Uint
-      Instruction* frag_coord_inst = builder->AddUnaryOp(
-          GetVec4FloatId(), SpvOpLoad,
-          context()->GetBuiltinInputVarId(SpvBuiltInFragCoord));
+      Instruction* frag_coord_inst = builder->AddLoad(
+          GetVec4FloatId(),
+          context()->GetBuiltinInputVarId(uint32_t(spv::BuiltIn::FragCoord)));
       Instruction* uint_frag_coord_inst = builder->AddUnaryOp(
-          GetVec4UintId(), SpvOpBitcast, frag_coord_inst->result_id());
-      for (uint32_t u = 0; u < 2u; ++u)
-        GenFragCoordEltDebugOutputCode(
-            base_offset_id, uint_frag_coord_inst->result_id(), u, builder);
+          GetVec4UintId(), spv::Op::OpBitcast, frag_coord_inst->result_id());
+      for (uint32_t u = 0; u < 2u; ++u) {
+        ids[u + 1] =
+            builder
+                ->AddCompositeExtract(GetUintId(),
+                                      uint_frag_coord_inst->result_id(), {u})
+                ->result_id();
+      }
     } break;
-    case SpvExecutionModelRayGenerationNV:
-    case SpvExecutionModelIntersectionNV:
-    case SpvExecutionModelAnyHitNV:
-    case SpvExecutionModelClosestHitNV:
-    case SpvExecutionModelMissNV:
-    case SpvExecutionModelCallableNV: {
+    case spv::ExecutionModel::RayGenerationNV:
+    case spv::ExecutionModel::IntersectionNV:
+    case spv::ExecutionModel::AnyHitNV:
+    case spv::ExecutionModel::ClosestHitNV:
+    case spv::ExecutionModel::MissNV:
+    case spv::ExecutionModel::CallableNV: {
       // Load and store LaunchIdNV.
       uint32_t launch_id = GenVarLoad(
-          context()->GetBuiltinInputVarId(SpvBuiltInLaunchIdNV), builder);
-      Instruction* x_launch_inst = builder->AddIdLiteralOp(
-          GetUintId(), SpvOpCompositeExtract, launch_id, 0);
-      Instruction* y_launch_inst = builder->AddIdLiteralOp(
-          GetUintId(), SpvOpCompositeExtract, launch_id, 1);
-      Instruction* z_launch_inst = builder->AddIdLiteralOp(
-          GetUintId(), SpvOpCompositeExtract, launch_id, 2);
-      GenDebugOutputFieldCode(base_offset_id, kInstRayTracingOutLaunchIdX,
-                              x_launch_inst->result_id(), builder);
-      GenDebugOutputFieldCode(base_offset_id, kInstRayTracingOutLaunchIdY,
-                              y_launch_inst->result_id(), builder);
-      GenDebugOutputFieldCode(base_offset_id, kInstRayTracingOutLaunchIdZ,
-                              z_launch_inst->result_id(), builder);
+          context()->GetBuiltinInputVarId(uint32_t(spv::BuiltIn::LaunchIdNV)),
+          builder);
+      for (uint32_t u = 0; u < 3u; ++u) {
+        ids[u + 1] = builder->AddCompositeExtract(GetUintId(), launch_id, {u})
+                         ->result_id();
+      }
     } break;
     default: { assert(false && "unsupported stage"); } break;
   }
-}
-
-void InstrumentPass::GenDebugStreamWrite(
-    uint32_t instruction_idx, uint32_t stage_idx,
-    const std::vector<uint32_t>& validation_ids, InstructionBuilder* builder) {
-  // Call debug output function. Pass func_idx, instruction_idx and
-  // validation ids as args.
-  uint32_t val_id_cnt = static_cast<uint32_t>(validation_ids.size());
-  uint32_t output_func_id = GetStreamWriteFunctionId(stage_idx, val_id_cnt);
-  std::vector<uint32_t> args = {output_func_id,
-                                builder->GetUintConstantId(instruction_idx)};
-  (void)args.insert(args.end(), validation_ids.begin(), validation_ids.end());
-  (void)builder->AddNaryOp(GetVoidId(), SpvOpFunctionCall, args);
+  return builder->AddCompositeConstruct(GetVec4UintId(), ids)->result_id();
 }
 
 bool InstrumentPass::AllConstant(const std::vector<uint32_t>& ids) {
@@ -360,38 +287,37 @@
   return true;
 }
 
-uint32_t InstrumentPass::GenDebugDirectRead(
-    const std::vector<uint32_t>& offset_ids, InstructionBuilder* ref_builder) {
-  // Call debug input function. Pass func_idx and offset ids as args.
-  uint32_t off_id_cnt = static_cast<uint32_t>(offset_ids.size());
-  uint32_t input_func_id = GetDirectReadFunctionId(off_id_cnt);
-  std::vector<uint32_t> args = {input_func_id};
-  (void)args.insert(args.end(), offset_ids.begin(), offset_ids.end());
+uint32_t InstrumentPass::GenReadFunctionCall(
+    uint32_t return_id, uint32_t func_id,
+    const std::vector<uint32_t>& func_call_args,
+    InstructionBuilder* ref_builder) {
   // If optimizing direct reads and the call has already been generated,
   // use its result
   if (opt_direct_reads_) {
-    uint32_t res_id = call2id_[args];
+    uint32_t res_id = call2id_[func_call_args];
     if (res_id != 0) return res_id;
   }
-  // If the offsets are all constants, the call can be moved to the first block
-  // of the function where its result can be reused. One example where this is
-  // profitable is for uniform buffer references, of which there are often many.
+  // If the function arguments are all constants, the call can be moved to the
+  // first block of the function where its result can be reused. One example
+  // where this is profitable is for uniform buffer references, of which there
+  // are often many.
   InstructionBuilder builder(ref_builder->GetContext(),
                              &*ref_builder->GetInsertPoint(),
                              ref_builder->GetPreservedAnalysis());
-  bool insert_in_first_block = opt_direct_reads_ && AllConstant(offset_ids);
+  bool insert_in_first_block = opt_direct_reads_ && AllConstant(func_call_args);
   if (insert_in_first_block) {
     Instruction* insert_before = &*curr_func_->begin()->tail();
     builder.SetInsertPoint(insert_before);
   }
   uint32_t res_id =
-      builder.AddNaryOp(GetUintId(), SpvOpFunctionCall, args)->result_id();
-  if (insert_in_first_block) call2id_[args] = res_id;
+      builder.AddFunctionCall(return_id, func_id, func_call_args)->result_id();
+  if (insert_in_first_block) call2id_[func_call_args] = res_id;
   return res_id;
 }
 
 bool InstrumentPass::IsSameBlockOp(const Instruction* inst) const {
-  return inst->opcode() == SpvOpSampledImage || inst->opcode() == SpvOpImage;
+  return inst->opcode() == spv::Op::OpSampledImage ||
+         inst->opcode() == spv::Op::OpImage;
 }
 
 void InstrumentPass::CloneSameBlockOps(
@@ -454,79 +380,74 @@
       });
 }
 
-uint32_t InstrumentPass::GetOutputBufferPtrId() {
-  if (output_buffer_ptr_id_ == 0) {
-    output_buffer_ptr_id_ = context()->get_type_mgr()->FindPointerToType(
-        GetUintId(), SpvStorageClassStorageBuffer);
-  }
-  return output_buffer_ptr_id_;
+analysis::Integer* InstrumentPass::GetInteger(uint32_t width, bool is_signed) {
+  analysis::Integer i(width, is_signed);
+  analysis::Type* type = context()->get_type_mgr()->GetRegisteredType(&i);
+  assert(type && type->AsInteger());
+  return type->AsInteger();
 }
 
-uint32_t InstrumentPass::GetInputBufferTypeId() {
-  return (validation_id_ == kInstValidationIdBuffAddr) ? GetUint64Id()
-                                                       : GetUintId();
+analysis::Struct* InstrumentPass::GetStruct(
+    const std::vector<const analysis::Type*>& fields) {
+  analysis::Struct s(fields);
+  analysis::Type* type = context()->get_type_mgr()->GetRegisteredType(&s);
+  assert(type && type->AsStruct());
+  return type->AsStruct();
 }
 
-uint32_t InstrumentPass::GetInputBufferPtrId() {
-  if (input_buffer_ptr_id_ == 0) {
-    input_buffer_ptr_id_ = context()->get_type_mgr()->FindPointerToType(
-        GetInputBufferTypeId(), SpvStorageClassStorageBuffer);
-  }
-  return input_buffer_ptr_id_;
+analysis::RuntimeArray* InstrumentPass::GetRuntimeArray(
+    const analysis::Type* element) {
+  analysis::RuntimeArray r(element);
+  analysis::Type* type = context()->get_type_mgr()->GetRegisteredType(&r);
+  assert(type && type->AsRuntimeArray());
+  return type->AsRuntimeArray();
 }
 
-uint32_t InstrumentPass::GetOutputBufferBinding() {
-  switch (validation_id_) {
-    case kInstValidationIdBindless:
-      return kDebugOutputBindingStream;
-    case kInstValidationIdBuffAddr:
-      return kDebugOutputBindingStream;
-    case kInstValidationIdDebugPrintf:
-      return kDebugOutputPrintfStream;
-    default:
-      assert(false && "unexpected validation id");
-  }
-  return 0;
+analysis::Array* InstrumentPass::GetArray(const analysis::Type* element,
+                                          uint32_t length) {
+  uint32_t length_id = context()->get_constant_mgr()->GetUIntConstId(length);
+  analysis::Array::LengthInfo length_info{
+      length_id, {analysis::Array::LengthInfo::Case::kConstant, length}};
+
+  analysis::Array r(element, length_info);
+
+  analysis::Type* type = context()->get_type_mgr()->GetRegisteredType(&r);
+  assert(type && type->AsArray());
+  return type->AsArray();
 }
 
-uint32_t InstrumentPass::GetInputBufferBinding() {
-  switch (validation_id_) {
-    case kInstValidationIdBindless:
-      return kDebugInputBindingBindless;
-    case kInstValidationIdBuffAddr:
-      return kDebugInputBindingBuffAddr;
-    default:
-      assert(false && "unexpected validation id");
-  }
-  return 0;
+analysis::Function* InstrumentPass::GetFunction(
+    const analysis::Type* return_val,
+    const std::vector<const analysis::Type*>& args) {
+  analysis::Function func(return_val, args);
+  analysis::Type* type = context()->get_type_mgr()->GetRegisteredType(&func);
+  assert(type && type->AsFunction());
+  return type->AsFunction();
 }
 
-analysis::Type* InstrumentPass::GetUintXRuntimeArrayType(
-    uint32_t width, analysis::Type** rarr_ty) {
+analysis::RuntimeArray* InstrumentPass::GetUintXRuntimeArrayType(
+    uint32_t width, analysis::RuntimeArray** rarr_ty) {
   if (*rarr_ty == nullptr) {
-    analysis::DecorationManager* deco_mgr = get_decoration_mgr();
-    analysis::TypeManager* type_mgr = context()->get_type_mgr();
-    analysis::Integer uint_ty(width, false);
-    analysis::Type* reg_uint_ty = type_mgr->GetRegisteredType(&uint_ty);
-    analysis::RuntimeArray uint_rarr_ty_tmp(reg_uint_ty);
-    *rarr_ty = type_mgr->GetRegisteredType(&uint_rarr_ty_tmp);
-    uint32_t uint_arr_ty_id = type_mgr->GetTypeInstruction(*rarr_ty);
+    *rarr_ty = GetRuntimeArray(GetInteger(width, false));
+    uint32_t uint_arr_ty_id =
+        context()->get_type_mgr()->GetTypeInstruction(*rarr_ty);
     // By the Vulkan spec, a pre-existing RuntimeArray of uint must be part of
     // a block, and will therefore be decorated with an ArrayStride. Therefore
     // the undecorated type returned here will not be pre-existing and can
     // safely be decorated. Since this type is now decorated, it is out of
     // sync with the TypeManager and therefore the TypeManager must be
     // invalidated after this pass.
-    assert(context()->get_def_use_mgr()->NumUses(uint_arr_ty_id) == 0 &&
+    assert(get_def_use_mgr()->NumUses(uint_arr_ty_id) == 0 &&
            "used RuntimeArray type returned");
-    deco_mgr->AddDecorationVal(uint_arr_ty_id, SpvDecorationArrayStride,
-                               width / 8u);
+    get_decoration_mgr()->AddDecorationVal(
+        uint_arr_ty_id, uint32_t(spv::Decoration::ArrayStride), width / 8u);
   }
   return *rarr_ty;
 }
 
-analysis::Type* InstrumentPass::GetUintRuntimeArrayType(uint32_t width) {
-  analysis::Type** rarr_ty =
+analysis::RuntimeArray* InstrumentPass::GetUintRuntimeArrayType(
+    uint32_t width) {
+  analysis::RuntimeArray** rarr_ty =
       (width == 64) ? &uint64_rarr_ty_ : &uint32_rarr_ty_;
   return GetUintXRuntimeArrayType(width, rarr_ty);
 }
@@ -539,106 +460,6 @@
   storage_buffer_ext_defined_ = true;
 }
 
-// Return id for output buffer
-uint32_t InstrumentPass::GetOutputBufferId() {
-  if (output_buffer_id_ == 0) {
-    // If not created yet, create one
-    analysis::DecorationManager* deco_mgr = get_decoration_mgr();
-    analysis::TypeManager* type_mgr = context()->get_type_mgr();
-    analysis::Type* reg_uint_rarr_ty = GetUintRuntimeArrayType(32);
-    analysis::Integer uint_ty(32, false);
-    analysis::Type* reg_uint_ty = type_mgr->GetRegisteredType(&uint_ty);
-    analysis::Struct buf_ty({reg_uint_ty, reg_uint_rarr_ty});
-    analysis::Type* reg_buf_ty = type_mgr->GetRegisteredType(&buf_ty);
-    uint32_t obufTyId = type_mgr->GetTypeInstruction(reg_buf_ty);
-    // By the Vulkan spec, a pre-existing struct containing a RuntimeArray
-    // must be a block, and will therefore be decorated with Block. Therefore
-    // the undecorated type returned here will not be pre-existing and can
-    // safely be decorated. Since this type is now decorated, it is out of
-    // sync with the TypeManager and therefore the TypeManager must be
-    // invalidated after this pass.
-    assert(context()->get_def_use_mgr()->NumUses(obufTyId) == 0 &&
-           "used struct type returned");
-    deco_mgr->AddDecoration(obufTyId, SpvDecorationBlock);
-    deco_mgr->AddMemberDecoration(obufTyId, kDebugOutputSizeOffset,
-                                  SpvDecorationOffset, 0);
-    deco_mgr->AddMemberDecoration(obufTyId, kDebugOutputDataOffset,
-                                  SpvDecorationOffset, 4);
-    uint32_t obufTyPtrId_ =
-        type_mgr->FindPointerToType(obufTyId, SpvStorageClassStorageBuffer);
-    output_buffer_id_ = TakeNextId();
-    std::unique_ptr<Instruction> newVarOp(new Instruction(
-        context(), SpvOpVariable, obufTyPtrId_, output_buffer_id_,
-        {{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER,
-          {SpvStorageClassStorageBuffer}}}));
-    context()->AddGlobalValue(std::move(newVarOp));
-    context()->AddDebug2Inst(NewGlobalName(obufTyId, "OutputBuffer"));
-    context()->AddDebug2Inst(NewMemberName(obufTyId, 0, "written_count"));
-    context()->AddDebug2Inst(NewMemberName(obufTyId, 1, "data"));
-    context()->AddDebug2Inst(NewGlobalName(output_buffer_id_, "output_buffer"));
-    deco_mgr->AddDecorationVal(output_buffer_id_, SpvDecorationDescriptorSet,
-                               desc_set_);
-    deco_mgr->AddDecorationVal(output_buffer_id_, SpvDecorationBinding,
-                               GetOutputBufferBinding());
-    AddStorageBufferExt();
-    if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4)) {
-      // Add the new buffer to all entry points.
-      for (auto& entry : get_module()->entry_points()) {
-        entry.AddOperand({SPV_OPERAND_TYPE_ID, {output_buffer_id_}});
-        context()->AnalyzeUses(&entry);
-      }
-    }
-  }
-  return output_buffer_id_;
-}
-
-uint32_t InstrumentPass::GetInputBufferId() {
-  if (input_buffer_id_ == 0) {
-    // If not created yet, create one
-    analysis::DecorationManager* deco_mgr = get_decoration_mgr();
-    analysis::TypeManager* type_mgr = context()->get_type_mgr();
-    uint32_t width = (validation_id_ == kInstValidationIdBuffAddr) ? 64u : 32u;
-    analysis::Type* reg_uint_rarr_ty = GetUintRuntimeArrayType(width);
-    analysis::Struct buf_ty({reg_uint_rarr_ty});
-    analysis::Type* reg_buf_ty = type_mgr->GetRegisteredType(&buf_ty);
-    uint32_t ibufTyId = type_mgr->GetTypeInstruction(reg_buf_ty);
-    // By the Vulkan spec, a pre-existing struct containing a RuntimeArray
-    // must be a block, and will therefore be decorated with Block. Therefore
-    // the undecorated type returned here will not be pre-existing and can
-    // safely be decorated. Since this type is now decorated, it is out of
-    // sync with the TypeManager and therefore the TypeManager must be
-    // invalidated after this pass.
-    assert(context()->get_def_use_mgr()->NumUses(ibufTyId) == 0 &&
-           "used struct type returned");
-    deco_mgr->AddDecoration(ibufTyId, SpvDecorationBlock);
-    deco_mgr->AddMemberDecoration(ibufTyId, 0, SpvDecorationOffset, 0);
-    uint32_t ibufTyPtrId_ =
-        type_mgr->FindPointerToType(ibufTyId, SpvStorageClassStorageBuffer);
-    input_buffer_id_ = TakeNextId();
-    std::unique_ptr<Instruction> newVarOp(new Instruction(
-        context(), SpvOpVariable, ibufTyPtrId_, input_buffer_id_,
-        {{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER,
-          {SpvStorageClassStorageBuffer}}}));
-    context()->AddGlobalValue(std::move(newVarOp));
-    context()->AddDebug2Inst(NewGlobalName(ibufTyId, "InputBuffer"));
-    context()->AddDebug2Inst(NewMemberName(ibufTyId, 0, "data"));
-    context()->AddDebug2Inst(NewGlobalName(input_buffer_id_, "input_buffer"));
-    deco_mgr->AddDecorationVal(input_buffer_id_, SpvDecorationDescriptorSet,
-                               desc_set_);
-    deco_mgr->AddDecorationVal(input_buffer_id_, SpvDecorationBinding,
-                               GetInputBufferBinding());
-    AddStorageBufferExt();
-    if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4)) {
-      // Add the new buffer to all entry points.
-      for (auto& entry : get_module()->entry_points()) {
-        entry.AddOperand({SPV_OPERAND_TYPE_ID, {input_buffer_id_}});
-        context()->AnalyzeUses(&entry);
-      }
-    }
-  }
-  return input_buffer_id_;
-}
-
 uint32_t InstrumentPass::GetFloatId() {
   if (float_id_ == 0) {
     analysis::TypeManager* type_mgr = context()->get_type_mgr();
@@ -731,207 +552,6 @@
   return void_id_;
 }
 
-uint32_t InstrumentPass::GetStreamWriteFunctionId(uint32_t stage_idx,
-                                                  uint32_t val_spec_param_cnt) {
-  // Total param count is common params plus validation-specific
-  // params
-  uint32_t param_cnt = kInstCommonParamCnt + val_spec_param_cnt;
-  if (param2output_func_id_[param_cnt] == 0) {
-    // Create function
-    param2output_func_id_[param_cnt] = TakeNextId();
-    analysis::TypeManager* type_mgr = context()->get_type_mgr();
-    std::vector<const analysis::Type*> param_types;
-    for (uint32_t c = 0; c < param_cnt; ++c)
-      param_types.push_back(type_mgr->GetType(GetUintId()));
-    analysis::Function func_ty(type_mgr->GetType(GetVoidId()), param_types);
-    analysis::Type* reg_func_ty = type_mgr->GetRegisteredType(&func_ty);
-    std::unique_ptr<Instruction> func_inst(
-        new Instruction(get_module()->context(), SpvOpFunction, GetVoidId(),
-                        param2output_func_id_[param_cnt],
-                        {{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER,
-                          {SpvFunctionControlMaskNone}},
-                         {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
-                          {type_mgr->GetTypeInstruction(reg_func_ty)}}}));
-    get_def_use_mgr()->AnalyzeInstDefUse(&*func_inst);
-    std::unique_ptr<Function> output_func =
-        MakeUnique<Function>(std::move(func_inst));
-    // Add parameters
-    std::vector<uint32_t> param_vec;
-    for (uint32_t c = 0; c < param_cnt; ++c) {
-      uint32_t pid = TakeNextId();
-      param_vec.push_back(pid);
-      std::unique_ptr<Instruction> param_inst(
-          new Instruction(get_module()->context(), SpvOpFunctionParameter,
-                          GetUintId(), pid, {}));
-      get_def_use_mgr()->AnalyzeInstDefUse(&*param_inst);
-      output_func->AddParameter(std::move(param_inst));
-    }
-    // Create first block
-    uint32_t test_blk_id = TakeNextId();
-    std::unique_ptr<Instruction> test_label(NewLabel(test_blk_id));
-    std::unique_ptr<BasicBlock> new_blk_ptr =
-        MakeUnique<BasicBlock>(std::move(test_label));
-    InstructionBuilder builder(
-        context(), &*new_blk_ptr,
-        IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
-    // Gen test if debug output buffer size will not be exceeded.
-    uint32_t val_spec_offset = kInstStageOutCnt;
-    uint32_t obuf_record_sz = val_spec_offset + val_spec_param_cnt;
-    uint32_t buf_id = GetOutputBufferId();
-    uint32_t buf_uint_ptr_id = GetOutputBufferPtrId();
-    Instruction* obuf_curr_sz_ac_inst =
-        builder.AddBinaryOp(buf_uint_ptr_id, SpvOpAccessChain, buf_id,
-                            builder.GetUintConstantId(kDebugOutputSizeOffset));
-    // Fetch the current debug buffer written size atomically, adding the
-    // size of the record to be written.
-    uint32_t obuf_record_sz_id = builder.GetUintConstantId(obuf_record_sz);
-    uint32_t mask_none_id = builder.GetUintConstantId(SpvMemoryAccessMaskNone);
-    uint32_t scope_invok_id = builder.GetUintConstantId(SpvScopeInvocation);
-    Instruction* obuf_curr_sz_inst = builder.AddQuadOp(
-        GetUintId(), SpvOpAtomicIAdd, obuf_curr_sz_ac_inst->result_id(),
-        scope_invok_id, mask_none_id, obuf_record_sz_id);
-    uint32_t obuf_curr_sz_id = obuf_curr_sz_inst->result_id();
-    // Compute new written size
-    Instruction* obuf_new_sz_inst =
-        builder.AddBinaryOp(GetUintId(), SpvOpIAdd, obuf_curr_sz_id,
-                            builder.GetUintConstantId(obuf_record_sz));
-    // Fetch the data bound
-    Instruction* obuf_bnd_inst =
-        builder.AddIdLiteralOp(GetUintId(), SpvOpArrayLength,
-                               GetOutputBufferId(), kDebugOutputDataOffset);
-    // Test that new written size is less than or equal to debug output
-    // data bound
-    Instruction* obuf_safe_inst = builder.AddBinaryOp(
-        GetBoolId(), SpvOpULessThanEqual, obuf_new_sz_inst->result_id(),
-        obuf_bnd_inst->result_id());
-    uint32_t merge_blk_id = TakeNextId();
-    uint32_t write_blk_id = TakeNextId();
-    std::unique_ptr<Instruction> merge_label(NewLabel(merge_blk_id));
-    std::unique_ptr<Instruction> write_label(NewLabel(write_blk_id));
-    (void)builder.AddConditionalBranch(obuf_safe_inst->result_id(),
-                                       write_blk_id, merge_blk_id, merge_blk_id,
-                                       SpvSelectionControlMaskNone);
-    // Close safety test block and gen write block
-    output_func->AddBasicBlock(std::move(new_blk_ptr));
-    new_blk_ptr = MakeUnique<BasicBlock>(std::move(write_label));
-    builder.SetInsertPoint(&*new_blk_ptr);
-    // Generate common and stage-specific debug record members
-    GenCommonStreamWriteCode(obuf_record_sz, param_vec[kInstCommonParamInstIdx],
-                             stage_idx, obuf_curr_sz_id, &builder);
-    GenStageStreamWriteCode(stage_idx, obuf_curr_sz_id, &builder);
-    // Gen writes of validation specific data
-    for (uint32_t i = 0; i < val_spec_param_cnt; ++i) {
-      GenDebugOutputFieldCode(obuf_curr_sz_id, val_spec_offset + i,
-                              param_vec[kInstCommonParamCnt + i], &builder);
-    }
-    // Close write block and gen merge block
-    (void)builder.AddBranch(merge_blk_id);
-    output_func->AddBasicBlock(std::move(new_blk_ptr));
-    new_blk_ptr = MakeUnique<BasicBlock>(std::move(merge_label));
-    builder.SetInsertPoint(&*new_blk_ptr);
-    // Close merge block and function and add function to module
-    (void)builder.AddNullaryOp(0, SpvOpReturn);
-    output_func->AddBasicBlock(std::move(new_blk_ptr));
-    std::unique_ptr<Instruction> func_end_inst(
-        new Instruction(get_module()->context(), SpvOpFunctionEnd, 0, 0, {}));
-    get_def_use_mgr()->AnalyzeInstDefUse(&*func_end_inst);
-    output_func->SetFunctionEnd(std::move(func_end_inst));
-    context()->AddFunction(std::move(output_func));
-
-    std::string name("stream_write_");
-    name += std::to_string(param_cnt);
-
-    context()->AddDebug2Inst(
-        NewGlobalName(param2output_func_id_[param_cnt], name));
-  }
-  return param2output_func_id_[param_cnt];
-}
-
-uint32_t InstrumentPass::GetDirectReadFunctionId(uint32_t param_cnt) {
-  uint32_t func_id = param2input_func_id_[param_cnt];
-  if (func_id != 0) return func_id;
-  // Create input function for param_cnt.
-  func_id = TakeNextId();
-  analysis::TypeManager* type_mgr = context()->get_type_mgr();
-  std::vector<const analysis::Type*> param_types;
-  for (uint32_t c = 0; c < param_cnt; ++c)
-    param_types.push_back(type_mgr->GetType(GetUintId()));
-  uint32_t ibuf_type_id = GetInputBufferTypeId();
-  analysis::Function func_ty(type_mgr->GetType(ibuf_type_id), param_types);
-  analysis::Type* reg_func_ty = type_mgr->GetRegisteredType(&func_ty);
-  std::unique_ptr<Instruction> func_inst(new Instruction(
-      get_module()->context(), SpvOpFunction, ibuf_type_id, func_id,
-      {{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER,
-        {SpvFunctionControlMaskNone}},
-       {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
-        {type_mgr->GetTypeInstruction(reg_func_ty)}}}));
-  get_def_use_mgr()->AnalyzeInstDefUse(&*func_inst);
-  std::unique_ptr<Function> input_func =
-      MakeUnique<Function>(std::move(func_inst));
-  // Add parameters
-  std::vector<uint32_t> param_vec;
-  for (uint32_t c = 0; c < param_cnt; ++c) {
-    uint32_t pid = TakeNextId();
-    param_vec.push_back(pid);
-    std::unique_ptr<Instruction> param_inst(new Instruction(
-        get_module()->context(), SpvOpFunctionParameter, GetUintId(), pid, {}));
-    get_def_use_mgr()->AnalyzeInstDefUse(&*param_inst);
-    input_func->AddParameter(std::move(param_inst));
-  }
-  // Create block
-  uint32_t blk_id = TakeNextId();
-  std::unique_ptr<Instruction> blk_label(NewLabel(blk_id));
-  std::unique_ptr<BasicBlock> new_blk_ptr =
-      MakeUnique<BasicBlock>(std::move(blk_label));
-  InstructionBuilder builder(
-      context(), &*new_blk_ptr,
-      IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
-  // For each offset parameter, generate new offset with parameter, adding last
-  // loaded value if it exists, and load value from input buffer at new offset.
-  // Return last loaded value.
-  uint32_t buf_id = GetInputBufferId();
-  uint32_t buf_ptr_id = GetInputBufferPtrId();
-  uint32_t last_value_id = 0;
-  for (uint32_t p = 0; p < param_cnt; ++p) {
-    uint32_t offset_id;
-    if (p == 0) {
-      offset_id = param_vec[0];
-    } else {
-      if (ibuf_type_id != GetUintId()) {
-        Instruction* ucvt_inst =
-            builder.AddUnaryOp(GetUintId(), SpvOpUConvert, last_value_id);
-        last_value_id = ucvt_inst->result_id();
-      }
-      Instruction* offset_inst = builder.AddBinaryOp(
-          GetUintId(), SpvOpIAdd, last_value_id, param_vec[p]);
-      offset_id = offset_inst->result_id();
-    }
-    Instruction* ac_inst = builder.AddTernaryOp(
-        buf_ptr_id, SpvOpAccessChain, buf_id,
-        builder.GetUintConstantId(kDebugInputDataOffset), offset_id);
-    Instruction* load_inst =
-        builder.AddUnaryOp(ibuf_type_id, SpvOpLoad, ac_inst->result_id());
-    last_value_id = load_inst->result_id();
-  }
-  (void)builder.AddInstruction(MakeUnique<Instruction>(
-      context(), SpvOpReturnValue, 0, 0,
-      std::initializer_list<Operand>{{SPV_OPERAND_TYPE_ID, {last_value_id}}}));
-  // Close block and function and add function to module
-  input_func->AddBasicBlock(std::move(new_blk_ptr));
-  std::unique_ptr<Instruction> func_end_inst(
-      new Instruction(get_module()->context(), SpvOpFunctionEnd, 0, 0, {}));
-  get_def_use_mgr()->AnalyzeInstDefUse(&*func_end_inst);
-  input_func->SetFunctionEnd(std::move(func_end_inst));
-  context()->AddFunction(std::move(input_func));
-
-  std::string name("direct_read_");
-  name += std::to_string(param_cnt);
-  context()->AddDebug2Inst(NewGlobalName(func_id, name));
-
-  param2input_func_id_[param_cnt] = func_id;
-  return func_id;
-}
-
 void InstrumentPass::SplitBlock(
     BasicBlock::iterator inst_itr, UptrVectorIterator<BasicBlock> block_itr,
     std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
@@ -970,7 +590,7 @@
       // block. This will allow function calls to be inserted into the first
       // block without interfering with the instrumentation algorithm.
       if (opt_direct_reads_ && !first_block_split) {
-        if (ii->opcode() != SpvOpVariable) {
+        if (ii->opcode() != spv::Op::OpVariable) {
           SplitBlock(ii, bi, &new_blks);
           first_block_split = true;
         }
@@ -1001,7 +621,9 @@
       // Restart instrumenting at beginning of last new block,
       // but skip over any new phi or copy instruction.
       ii = bi->begin();
-      if (ii->opcode() == SpvOpPhi || ii->opcode() == SpvOpCopyObject) ++ii;
+      if (ii->opcode() == spv::Op::OpPhi ||
+          ii->opcode() == spv::Op::OpCopyObject)
+        ++ii;
       new_blks.clear();
     }
   }
@@ -1039,35 +661,24 @@
   // one model per module. In such cases we will need
   // to clone any functions which are in the call trees of entrypoints
   // with differing execution models.
-  uint32_t ecnt = 0;
-  uint32_t stage = SpvExecutionModelMax;
-  for (auto& e : get_module()->entry_points()) {
-    if (ecnt == 0)
-      stage = e.GetSingleWordInOperand(kEntryPointExecutionModelInIdx);
-    else if (e.GetSingleWordInOperand(kEntryPointExecutionModelInIdx) !=
-             stage) {
-      if (consumer()) {
-        std::string message = "Mixed stage shader module not supported";
-        consumer()(SPV_MSG_ERROR, 0, {0, 0, 0}, message.c_str());
-      }
-      return false;
-    }
-    ++ecnt;
-  }
+  spv::ExecutionModel stage = context()->GetStage();
   // Check for supported stages
-  if (stage != SpvExecutionModelVertex && stage != SpvExecutionModelFragment &&
-      stage != SpvExecutionModelGeometry &&
-      stage != SpvExecutionModelGLCompute &&
-      stage != SpvExecutionModelTessellationControl &&
-      stage != SpvExecutionModelTessellationEvaluation &&
-      stage != SpvExecutionModelTaskNV && stage != SpvExecutionModelMeshNV &&
-      stage != SpvExecutionModelRayGenerationNV &&
-      stage != SpvExecutionModelIntersectionNV &&
-      stage != SpvExecutionModelAnyHitNV &&
-      stage != SpvExecutionModelClosestHitNV &&
-      stage != SpvExecutionModelMissNV &&
-      stage != SpvExecutionModelCallableNV &&
-      stage != SpvExecutionModelTaskEXT && stage != SpvExecutionModelMeshEXT) {
+  if (stage != spv::ExecutionModel::Vertex &&
+      stage != spv::ExecutionModel::Fragment &&
+      stage != spv::ExecutionModel::Geometry &&
+      stage != spv::ExecutionModel::GLCompute &&
+      stage != spv::ExecutionModel::TessellationControl &&
+      stage != spv::ExecutionModel::TessellationEvaluation &&
+      stage != spv::ExecutionModel::TaskNV &&
+      stage != spv::ExecutionModel::MeshNV &&
+      stage != spv::ExecutionModel::RayGenerationNV &&
+      stage != spv::ExecutionModel::IntersectionNV &&
+      stage != spv::ExecutionModel::AnyHitNV &&
+      stage != spv::ExecutionModel::ClosestHitNV &&
+      stage != spv::ExecutionModel::MissNV &&
+      stage != spv::ExecutionModel::CallableNV &&
+      stage != spv::ExecutionModel::TaskEXT &&
+      stage != spv::ExecutionModel::MeshEXT) {
     if (consumer()) {
       std::string message = "Stage not supported by instrumentation";
       consumer()(SPV_MSG_ERROR, 0, {0, 0, 0}, message.c_str());
@@ -1079,15 +690,11 @@
   for (auto& e : get_module()->entry_points()) {
     roots.push(e.GetSingleWordInOperand(kEntryPointFunctionIdInIdx));
   }
-  bool modified = InstProcessCallTreeFromRoots(pfn, &roots, stage);
+  bool modified = InstProcessCallTreeFromRoots(pfn, &roots, uint32_t(stage));
   return modified;
 }
 
 void InstrumentPass::InitializeInstrument() {
-  output_buffer_id_ = 0;
-  output_buffer_ptr_id_ = 0;
-  input_buffer_ptr_id_ = 0;
-  input_buffer_id_ = 0;
   float_id_ = 0;
   v4float_id_ = 0;
   uint_id_ = 0;
diff --git a/source/opt/instrument_pass.h b/source/opt/instrument_pass.h
index 215b026..8b64374 100644
--- a/source/opt/instrument_pass.h
+++ b/source/opt/instrument_pass.h
@@ -56,13 +56,6 @@
 namespace spvtools {
 namespace opt {
 
-// Validation Ids
-// These are used to identify the general validation being done and map to
-// its output buffers.
-static const uint32_t kInstValidationIdBindless = 0;
-static const uint32_t kInstValidationIdBuffAddr = 1;
-static const uint32_t kInstValidationIdDebugPrintf = 2;
-
 class InstrumentPass : public Pass {
   using cbb_ptr = const BasicBlock*;
 
@@ -84,12 +77,11 @@
   // set |desc_set| for debug input and output buffers and writes |shader_id|
   // into debug output records. |opt_direct_reads| indicates that the pass
   // will see direct input buffer reads and should prepare to optimize them.
-  InstrumentPass(uint32_t desc_set, uint32_t shader_id, uint32_t validation_id,
+  InstrumentPass(uint32_t desc_set, uint32_t shader_id,
                  bool opt_direct_reads = false)
       : Pass(),
         desc_set_(desc_set),
         shader_id_(shader_id),
-        validation_id_(validation_id),
         opt_direct_reads_(opt_direct_reads) {}
 
   // Initialize state for instrumentation of module.
@@ -112,106 +104,12 @@
   void MovePostludeCode(UptrVectorIterator<BasicBlock> ref_block_itr,
                         BasicBlock* new_blk_ptr);
 
-  // Generate instructions in |builder| which will atomically fetch and
-  // increment the size of the debug output buffer stream of the current
-  // validation and write a record to the end of the stream, if enough space
-  // in the buffer remains. The record will contain the index of the function
-  // and instruction within that function |func_idx, instruction_idx| which
-  // generated the record. It will also contain additional information to
-  // identify the instance of the shader, depending on the stage |stage_idx|
-  // of the shader. Finally, the record will contain validation-specific
-  // data contained in |validation_ids| which will identify the validation
-  // error as well as the values involved in the error.
-  //
-  // The output buffer binding written to by the code generated by the function
-  // is determined by the validation id specified when each specific
-  // instrumentation pass is created.
-  //
-  // The output buffer is a sequence of 32-bit values with the following
-  // format (where all elements are unsigned 32-bit unless otherwise noted):
-  //
-  //     Size
-  //     Record0
-  //     Record1
-  //     Record2
-  //     ...
-  //
-  // Size is the number of 32-bit values that have been written or
-  // attempted to be written to the output buffer, excluding the Size. It is
-  // initialized to 0. If the size of attempts to write the buffer exceeds
-  // the actual size of the buffer, it is possible that this field can exceed
-  // the actual size of the buffer.
-  //
-  // Each Record* is a variable-length sequence of 32-bit values with the
-  // following format defined using static const offsets in the .cpp file:
-  //
-  //     Record Size
-  //     Shader ID
-  //     Instruction Index
-  //     Stage
-  //     Stage-specific Word 0
-  //     Stage-specific Word 1
-  //     ...
-  //     Validation Error Code
-  //     Validation-specific Word 0
-  //     Validation-specific Word 1
-  //     Validation-specific Word 2
-  //     ...
-  //
-  // Each record consists of three subsections: members common across all
-  // validation, members specific to the stage, and members specific to a
-  // validation.
-  //
-  // The Record Size is the number of 32-bit words in the record, including
-  // the Record Size word.
-  //
-  // Shader ID is a value that identifies which shader has generated the
-  // validation error. It is passed when the instrumentation pass is created.
-  //
-  // The Instruction Index is the position of the instruction within the
-  // SPIR-V file which is in error.
-  //
-  // The Stage is the pipeline stage which has generated the error as defined
-  // by the SpvExecutionModel_ enumeration. This is used to interpret the
-  // following Stage-specific words.
-  //
-  // The Stage-specific Words identify which invocation of the shader generated
-  // the error. Every stage will write a fixed number of words. Vertex shaders
-  // will write the Vertex and Instance ID. Fragment shaders will write
-  // FragCoord.xy. Compute shaders will write the GlobalInvocation ID.
-  // The tessellation eval shader will write the Primitive ID and TessCoords.uv.
-  // The tessellation control shader and geometry shader will write the
-  // Primitive ID and Invocation ID.
-  //
-  // The Validation Error Code specifies the exact error which has occurred.
-  // These are enumerated with the kInstError* static consts. This allows
-  // multiple validation layers to use the same, single output buffer.
-  //
-  // The Validation-specific Words are a validation-specific number of 32-bit
-  // words which give further information on the validation error that
-  // occurred. These are documented further in each file containing the
-  // validation-specific class which derives from this base class.
-  //
-  // Because the code that is generated checks against the size of the buffer
-  // before writing, the size of the debug out buffer can be used by the
-  // validation layer to control the number of error records that are written.
-  void GenDebugStreamWrite(uint32_t instruction_idx, uint32_t stage_idx,
-                           const std::vector<uint32_t>& validation_ids,
-                           InstructionBuilder* builder);
-
   // Return true if all instructions in |ids| are constants or spec constants.
   bool AllConstant(const std::vector<uint32_t>& ids);
 
-  // Generate in |builder| instructions to read the unsigned integer from the
-  // input buffer specified by the offsets in |offset_ids|. Given offsets
-  // o0, o1, ... oN, and input buffer ibuf, return the id for the value:
-  //
-  // ibuf[...ibuf[ibuf[o0]+o1]...+oN]
-  //
-  // The binding and the format of the input buffer is determined by each
-  // specific validation, which is specified at the creation of the pass.
-  uint32_t GenDebugDirectRead(const std::vector<uint32_t>& offset_ids,
-                              InstructionBuilder* builder);
+  uint32_t GenReadFunctionCall(uint32_t return_id, uint32_t func_id,
+                               const std::vector<uint32_t>& args,
+                               InstructionBuilder* builder);
 
   // Generate code to convert integer |value_id| to 32bit, if needed. Return
   // an id to the 32bit equivalent.
@@ -221,6 +119,15 @@
   // Return an id to the Uint equivalent.
   uint32_t GenUintCastCode(uint32_t value_id, InstructionBuilder* builder);
 
+  std::unique_ptr<Function> StartFunction(
+      uint32_t func_id, const analysis::Type* return_type,
+      const std::vector<const analysis::Type*>& param_types);
+
+  std::vector<uint32_t> AddParameters(
+      Function& func, const std::vector<const analysis::Type*>& param_types);
+
+  std::unique_ptr<Instruction> EndFunction();
+
   // Return new label.
   std::unique_ptr<Instruction> NewLabel(uint32_t label_id);
 
@@ -228,15 +135,6 @@
   std::unique_ptr<Instruction> NewName(uint32_t id,
                                        const std::string& name_str);
 
-  // Set the name for a function or global variable, names will be
-  // prefixed to identify which instrumentation pass generated them.
-  std::unique_ptr<Instruction> NewGlobalName(uint32_t id,
-                                             const std::string& name_str);
-
-  // Set the name for a structure member
-  std::unique_ptr<Instruction> NewMemberName(uint32_t id, uint32_t member_index,
-                                             const std::string& name_str);
-
   // Return id for 32-bit unsigned type
   uint32_t GetUintId();
 
@@ -252,37 +150,25 @@
   // Return id for void type
   uint32_t GetVoidId();
 
-  // Return pointer to type for runtime array of uint
-  analysis::Type* GetUintXRuntimeArrayType(uint32_t width,
-                                           analysis::Type** rarr_ty);
+  // Get registered type structures
+  analysis::Integer* GetInteger(uint32_t width, bool is_signed);
+  analysis::Struct* GetStruct(const std::vector<const analysis::Type*>& fields);
+  analysis::RuntimeArray* GetRuntimeArray(const analysis::Type* element);
+  analysis::Array* GetArray(const analysis::Type* element, uint32_t size);
+  analysis::Function* GetFunction(
+      const analysis::Type* return_val,
+      const std::vector<const analysis::Type*>& args);
 
   // Return pointer to type for runtime array of uint
-  analysis::Type* GetUintRuntimeArrayType(uint32_t width);
+  analysis::RuntimeArray* GetUintXRuntimeArrayType(
+      uint32_t width, analysis::RuntimeArray** rarr_ty);
 
-  // Return id for buffer uint type
-  uint32_t GetOutputBufferPtrId();
-
-  // Return id for buffer uint type
-  uint32_t GetInputBufferTypeId();
-
-  // Return id for buffer uint type
-  uint32_t GetInputBufferPtrId();
-
-  // Return binding for output buffer for current validation.
-  uint32_t GetOutputBufferBinding();
-
-  // Return binding for input buffer for current validation.
-  uint32_t GetInputBufferBinding();
+  // Return pointer to type for runtime array of uint
+  analysis::RuntimeArray* GetUintRuntimeArrayType(uint32_t width);
 
   // Add storage buffer extension if needed
   void AddStorageBufferExt();
 
-  // Return id for debug output buffer
-  uint32_t GetOutputBufferId();
-
-  // Return id for debug input buffer
-  uint32_t GetInputBufferId();
-
   // Return id for 32-bit float type
   uint32_t GetFloatId();
 
@@ -298,15 +184,6 @@
   // Return id for v3uint type
   uint32_t GetVec3UintId();
 
-  // Return id for output function. Define if it doesn't exist with
-  // |val_spec_param_cnt| validation-specific uint32 parameters.
-  uint32_t GetStreamWriteFunctionId(uint32_t stage_idx,
-                                    uint32_t val_spec_param_cnt);
-
-  // Return id for input function taking |param_cnt| uint32 parameters. Define
-  // if it doesn't exist.
-  uint32_t GetDirectReadFunctionId(uint32_t param_cnt);
-
   // Split block |block_itr| into two new blocks where the second block
   // contains |inst_itr| and place in |new_blocks|.
   void SplitBlock(BasicBlock::iterator inst_itr,
@@ -317,8 +194,8 @@
   // If code is generated for an instruction, replace the instruction's
   // block with the new blocks that are generated. Continue processing at the
   // top of the last new block.
-  bool InstrumentFunction(Function* func, uint32_t stage_idx,
-                          InstProcessFunction& pfn);
+  virtual bool InstrumentFunction(Function* func, uint32_t stage_idx,
+                                  InstProcessFunction& pfn);
 
   // Call |pfn| on all functions in the call tree of the function
   // ids in |roots|.
@@ -326,40 +203,11 @@
                                     std::queue<uint32_t>* roots,
                                     uint32_t stage_idx);
 
-  // Gen code into |builder| to write |field_value_id| into debug output
-  // buffer at |base_offset_id| + |field_offset|.
-  void GenDebugOutputFieldCode(uint32_t base_offset_id, uint32_t field_offset,
-                               uint32_t field_value_id,
-                               InstructionBuilder* builder);
-
-  // Generate instructions into |builder| which will write the members
-  // of the debug output record common for all stages and validations at
-  // |base_off|.
-  void GenCommonStreamWriteCode(uint32_t record_sz, uint32_t instruction_idx,
-                                uint32_t stage_idx, uint32_t base_off,
-                                InstructionBuilder* builder);
-
-  // Generate instructions into |builder| which will write
-  // |uint_frag_coord_id| at |component| of the record at |base_offset_id| of
-  // the debug output buffer .
-  void GenFragCoordEltDebugOutputCode(uint32_t base_offset_id,
-                                      uint32_t uint_frag_coord_id,
-                                      uint32_t component,
-                                      InstructionBuilder* builder);
-
   // Generate instructions into |builder| which will load |var_id| and return
   // its result id.
   uint32_t GenVarLoad(uint32_t var_id, InstructionBuilder* builder);
 
-  // Generate instructions into |builder| which will load the uint |builtin_id|
-  // and write it into the debug output buffer at |base_off| + |builtin_off|.
-  void GenBuiltinOutputCode(uint32_t builtin_id, uint32_t builtin_off,
-                            uint32_t base_off, InstructionBuilder* builder);
-
-  // Generate instructions into |builder| which will write the |stage_idx|-
-  // specific members of the debug output stream at |base_off|.
-  void GenStageStreamWriteCode(uint32_t stage_idx, uint32_t base_off,
-                               InstructionBuilder* builder);
+  uint32_t GenStageInfo(uint32_t stage_idx, InstructionBuilder* builder);
 
   // Return true if instruction must be in the same block that its result
   // is used.
@@ -395,62 +243,47 @@
   // Map from instruction's unique id to offset in original file.
   std::unordered_map<uint32_t, uint32_t> uid2offset_;
 
-  // result id for OpConstantFalse
-  uint32_t validation_id_;
-
-  // id for output buffer variable
-  uint32_t output_buffer_id_;
-
-  // ptr type id for output buffer element
-  uint32_t output_buffer_ptr_id_;
-
-  // ptr type id for input buffer element
-  uint32_t input_buffer_ptr_id_;
-
   // id for debug output function
   std::unordered_map<uint32_t, uint32_t> param2output_func_id_;
 
   // ids for debug input functions
   std::unordered_map<uint32_t, uint32_t> param2input_func_id_;
 
-  // id for input buffer variable
-  uint32_t input_buffer_id_;
-
   // id for 32-bit float type
-  uint32_t float_id_;
+  uint32_t float_id_{0};
 
   // id for v4float type
-  uint32_t v4float_id_;
+  uint32_t v4float_id_{0};
 
   // id for v4uint type
-  uint32_t v4uint_id_;
+  uint32_t v4uint_id_{0};
 
   // id for v3uint type
-  uint32_t v3uint_id_;
+  uint32_t v3uint_id_{0};
 
   // id for 32-bit unsigned type
-  uint32_t uint_id_;
+  uint32_t uint_id_{0};
 
   // id for 64-bit unsigned type
-  uint32_t uint64_id_;
+  uint32_t uint64_id_{0};
 
   // id for 8-bit unsigned type
-  uint32_t uint8_id_;
+  uint32_t uint8_id_{0};
 
   // id for bool type
-  uint32_t bool_id_;
+  uint32_t bool_id_{0};
 
   // id for void type
-  uint32_t void_id_;
+  uint32_t void_id_{0};
 
   // boolean to remember storage buffer extension
-  bool storage_buffer_ext_defined_;
+  bool storage_buffer_ext_defined_{false};
 
   // runtime array of uint type
-  analysis::Type* uint64_rarr_ty_;
+  analysis::RuntimeArray* uint64_rarr_ty_{nullptr};
 
   // runtime array of uint type
-  analysis::Type* uint32_rarr_ty_;
+  analysis::RuntimeArray* uint32_rarr_ty_{nullptr};
 
   // Pre-instrumentation same-block insts
   std::unordered_map<uint32_t, Instruction*> same_block_pre_;
@@ -475,11 +308,11 @@
   std::unordered_map<std::vector<uint32_t>, uint32_t, vector_hash_> call2id_;
 
   // Function currently being instrumented
-  Function* curr_func_;
+  Function* curr_func_{nullptr};
 
   // Optimize direct debug input buffer reads. Specifically, move all such
   // reads with constant args to first block and reuse them.
-  bool opt_direct_reads_;
+  bool opt_direct_reads_{false};
 };
 
 }  // namespace opt
diff --git a/source/opt/interface_var_sroa.cpp b/source/opt/interface_var_sroa.cpp
index 1b2cb36..08477cb 100644
--- a/source/opt/interface_var_sroa.cpp
+++ b/source/opt/interface_var_sroa.cpp
@@ -23,29 +23,28 @@
 #include "source/opt/type_manager.h"
 #include "source/util/make_unique.h"
 
-const static uint32_t kOpDecorateDecorationInOperandIndex = 1;
-const static uint32_t kOpDecorateLiteralInOperandIndex = 2;
-const static uint32_t kOpEntryPointInOperandInterface = 3;
-const static uint32_t kOpVariableStorageClassInOperandIndex = 0;
-const static uint32_t kOpTypeArrayElemTypeInOperandIndex = 0;
-const static uint32_t kOpTypeArrayLengthInOperandIndex = 1;
-const static uint32_t kOpTypeMatrixColCountInOperandIndex = 1;
-const static uint32_t kOpTypeMatrixColTypeInOperandIndex = 0;
-const static uint32_t kOpTypePtrTypeInOperandIndex = 1;
-const static uint32_t kOpConstantValueInOperandIndex = 0;
-
 namespace spvtools {
 namespace opt {
 namespace {
+constexpr uint32_t kOpDecorateDecorationInOperandIndex = 1;
+constexpr uint32_t kOpDecorateLiteralInOperandIndex = 2;
+constexpr uint32_t kOpEntryPointInOperandInterface = 3;
+constexpr uint32_t kOpVariableStorageClassInOperandIndex = 0;
+constexpr uint32_t kOpTypeArrayElemTypeInOperandIndex = 0;
+constexpr uint32_t kOpTypeArrayLengthInOperandIndex = 1;
+constexpr uint32_t kOpTypeMatrixColCountInOperandIndex = 1;
+constexpr uint32_t kOpTypeMatrixColTypeInOperandIndex = 0;
+constexpr uint32_t kOpTypePtrTypeInOperandIndex = 1;
+constexpr uint32_t kOpConstantValueInOperandIndex = 0;
 
 // Get the length of the OpTypeArray |array_type|.
 uint32_t GetArrayLength(analysis::DefUseManager* def_use_mgr,
                         Instruction* array_type) {
-  assert(array_type->opcode() == SpvOpTypeArray);
+  assert(array_type->opcode() == spv::Op::OpTypeArray);
   uint32_t const_int_id =
       array_type->GetSingleWordInOperand(kOpTypeArrayLengthInOperandIndex);
   Instruction* array_length_inst = def_use_mgr->GetDef(const_int_id);
-  assert(array_length_inst->opcode() == SpvOpConstant);
+  assert(array_length_inst->opcode() == spv::Op::OpConstant);
   return array_length_inst->GetSingleWordInOperand(
       kOpConstantValueInOperandIndex);
 }
@@ -53,7 +52,7 @@
 // Get the element type instruction of the OpTypeArray |array_type|.
 Instruction* GetArrayElementType(analysis::DefUseManager* def_use_mgr,
                                  Instruction* array_type) {
-  assert(array_type->opcode() == SpvOpTypeArray);
+  assert(array_type->opcode() == spv::Op::OpTypeArray);
   uint32_t elem_type_id =
       array_type->GetSingleWordInOperand(kOpTypeArrayElemTypeInOperandIndex);
   return def_use_mgr->GetDef(elem_type_id);
@@ -62,7 +61,7 @@
 // Get the column type instruction of the OpTypeMatrix |matrix_type|.
 Instruction* GetMatrixColumnType(analysis::DefUseManager* def_use_mgr,
                                  Instruction* matrix_type) {
-  assert(matrix_type->opcode() == SpvOpTypeMatrix);
+  assert(matrix_type->opcode() == spv::Op::OpTypeMatrix);
   uint32_t column_type_id =
       matrix_type->GetSingleWordInOperand(kOpTypeMatrixColTypeInOperandIndex);
   return def_use_mgr->GetDef(column_type_id);
@@ -77,14 +76,14 @@
   if (depth_to_component == 0) return type_id;
 
   Instruction* type_inst = def_use_mgr->GetDef(type_id);
-  if (type_inst->opcode() == SpvOpTypeArray) {
+  if (type_inst->opcode() == spv::Op::OpTypeArray) {
     uint32_t elem_type_id =
         type_inst->GetSingleWordInOperand(kOpTypeArrayElemTypeInOperandIndex);
     return GetComponentTypeOfArrayMatrix(def_use_mgr, elem_type_id,
                                          depth_to_component - 1);
   }
 
-  assert(type_inst->opcode() == SpvOpTypeMatrix);
+  assert(type_inst->opcode() == spv::Op::OpTypeMatrix);
   uint32_t column_type_id =
       type_inst->GetSingleWordInOperand(kOpTypeMatrixColTypeInOperandIndex);
   return GetComponentTypeOfArrayMatrix(def_use_mgr, column_type_id,
@@ -94,7 +93,7 @@
 // Creates an OpDecorate instruction whose Target is |var_id| and Decoration is
 // |decoration|. Adds |literal| as an extra operand of the instruction.
 void CreateDecoration(analysis::DecorationManager* decoration_mgr,
-                      uint32_t var_id, SpvDecoration decoration,
+                      uint32_t var_id, spv::Decoration decoration,
                       uint32_t literal) {
   std::vector<Operand> operands({
       {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {var_id}},
@@ -102,7 +101,7 @@
        {static_cast<uint32_t>(decoration)}},
       {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {literal}},
   });
-  decoration_mgr->AddDecoration(SpvOpDecorate, std::move(operands));
+  decoration_mgr->AddDecoration(spv::Op::OpDecorate, std::move(operands));
 }
 
 // Replaces load instructions with composite construct instructions in all the
@@ -128,8 +127,8 @@
 }
 
 // Returns the storage class of the instruction |var|.
-SpvStorageClass GetStorageClass(Instruction* var) {
-  return static_cast<SpvStorageClass>(
+spv::StorageClass GetStorageClass(Instruction* var) {
+  return static_cast<spv::StorageClass>(
       var->GetSingleWordInOperand(kOpVariableStorageClassInOperandIndex));
 }
 
@@ -137,16 +136,17 @@
 
 bool InterfaceVariableScalarReplacement::HasExtraArrayness(
     Instruction& entry_point, Instruction* var) {
-  SpvExecutionModel execution_model =
-      static_cast<SpvExecutionModel>(entry_point.GetSingleWordInOperand(0));
-  if (execution_model != SpvExecutionModelTessellationEvaluation &&
-      execution_model != SpvExecutionModelTessellationControl) {
+  spv::ExecutionModel execution_model =
+      static_cast<spv::ExecutionModel>(entry_point.GetSingleWordInOperand(0));
+  if (execution_model != spv::ExecutionModel::TessellationEvaluation &&
+      execution_model != spv::ExecutionModel::TessellationControl) {
     return false;
   }
-  if (!context()->get_decoration_mgr()->HasDecoration(var->result_id(),
-                                                      SpvDecorationPatch)) {
-    if (execution_model == SpvExecutionModelTessellationControl) return true;
-    return GetStorageClass(var) != SpvStorageClassOutput;
+  if (!context()->get_decoration_mgr()->HasDecoration(
+          var->result_id(), uint32_t(spv::Decoration::Patch))) {
+    if (execution_model == spv::ExecutionModel::TessellationControl)
+      return true;
+    return GetStorageClass(var) != spv::StorageClass::Output;
   }
   return false;
 }
@@ -163,7 +163,7 @@
 bool InterfaceVariableScalarReplacement::GetVariableLocation(
     Instruction* var, uint32_t* location) {
   return !context()->get_decoration_mgr()->WhileEachDecoration(
-      var->result_id(), SpvDecorationLocation,
+      var->result_id(), uint32_t(spv::Decoration::Location),
       [location](const Instruction& inst) {
         *location =
             inst.GetSingleWordInOperand(kOpDecorateLiteralInOperandIndex);
@@ -174,7 +174,7 @@
 bool InterfaceVariableScalarReplacement::GetVariableComponent(
     Instruction* var, uint32_t* component) {
   return !context()->get_decoration_mgr()->WhileEachDecoration(
-      var->result_id(), SpvDecorationComponent,
+      var->result_id(), uint32_t(spv::Decoration::Component),
       [component](const Instruction& inst) {
         *component =
             inst.GetSingleWordInOperand(kOpDecorateLiteralInOperandIndex);
@@ -190,11 +190,11 @@
        i < entry_point.NumInOperands(); ++i) {
     Instruction* interface_var = context()->get_def_use_mgr()->GetDef(
         entry_point.GetSingleWordInOperand(i));
-    assert(interface_var->opcode() == SpvOpVariable);
+    assert(interface_var->opcode() == spv::Op::OpVariable);
 
-    SpvStorageClass storage_class = GetStorageClass(interface_var);
-    if (storage_class != SpvStorageClassInput &&
-        storage_class != SpvStorageClassOutput) {
+    spv::StorageClass storage_class = GetStorageClass(interface_var);
+    if (storage_class != spv::StorageClass::Input &&
+        storage_class != spv::StorageClass::Output) {
       continue;
     }
 
@@ -205,10 +205,10 @@
 
 void InterfaceVariableScalarReplacement::KillInstructionAndUsers(
     Instruction* inst) {
-  if (inst->opcode() == SpvOpEntryPoint) {
+  if (inst->opcode() == spv::Op::OpEntryPoint) {
     return;
   }
-  if (inst->opcode() != SpvOpAccessChain) {
+  if (inst->opcode() != spv::Op::OpAccessChain) {
     context()->KillInst(inst);
     return;
   }
@@ -232,10 +232,10 @@
     uint32_t var_id) {
   context()->get_decoration_mgr()->RemoveDecorationsFrom(
       var_id, [](const Instruction& inst) {
-        uint32_t decoration =
-            inst.GetSingleWordInOperand(kOpDecorateDecorationInOperandIndex);
-        return decoration == SpvDecorationLocation ||
-               decoration == SpvDecorationComponent;
+        spv::Decoration decoration = spv::Decoration(
+            inst.GetSingleWordInOperand(kOpDecorateDecorationInOperandIndex));
+        return decoration == spv::Decoration::Location ||
+               decoration == spv::Decoration::Component;
       });
 }
 
@@ -307,9 +307,9 @@
   if (!vars.HasMultipleComponents()) {
     uint32_t var_id = vars.GetComponentVariable()->result_id();
     CreateDecoration(context()->get_decoration_mgr(), var_id,
-                     SpvDecorationLocation, *location);
+                     spv::Decoration::Location, *location);
     CreateDecoration(context()->get_decoration_mgr(), var_id,
-                     SpvDecorationComponent, component);
+                     spv::Decoration::Component, component);
     ++(*location);
     return;
   }
@@ -389,15 +389,15 @@
     std::unordered_map<Instruction*, Instruction*>* loads_to_component_values,
     std::unordered_map<Instruction*, Instruction*>*
         loads_for_access_chain_to_component_values) {
-  SpvOp opcode = interface_var_user->opcode();
-  if (opcode == SpvOpStore) {
+  spv::Op opcode = interface_var_user->opcode();
+  if (opcode == spv::Op::OpStore) {
     uint32_t value_id = interface_var_user->GetSingleWordInOperand(1);
     StoreComponentOfValueToScalarVar(value_id, interface_var_component_indices,
                                      scalar_var, extra_array_index,
                                      interface_var_user);
     return true;
   }
-  if (opcode == SpvOpLoad) {
+  if (opcode == spv::Op::OpLoad) {
     Instruction* scalar_load =
         LoadScalarVar(scalar_var, extra_array_index, interface_var_user);
     loads_to_component_values->insert({interface_var_user, scalar_load});
@@ -408,25 +408,25 @@
   // them only for the first element of the extra array.
   if (extra_array_index && *extra_array_index != 0) return true;
 
-  if (opcode == SpvOpDecorateId || opcode == SpvOpDecorateString ||
-      opcode == SpvOpDecorate) {
+  if (opcode == spv::Op::OpDecorateId || opcode == spv::Op::OpDecorateString ||
+      opcode == spv::Op::OpDecorate) {
     CloneAnnotationForVariable(interface_var_user, scalar_var->result_id());
     return true;
   }
 
-  if (opcode == SpvOpName) {
+  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;
   }
 
-  if (opcode == SpvOpEntryPoint) {
+  if (opcode == spv::Op::OpEntryPoint) {
     return ReplaceInterfaceVarInEntryPoint(interface_var, interface_var_user,
                                            scalar_var->result_id());
   }
 
-  if (opcode == SpvOpAccessChain) {
+  if (opcode == spv::Op::OpAccessChain) {
     ReplaceAccessChainWith(interface_var_user, interface_var_component_indices,
                            scalar_var,
                            loads_for_access_chain_to_component_values);
@@ -445,8 +445,8 @@
 
 void InterfaceVariableScalarReplacement::UseBaseAccessChainForAccessChain(
     Instruction* access_chain, Instruction* base_access_chain) {
-  assert(base_access_chain->opcode() == SpvOpAccessChain &&
-         access_chain->opcode() == SpvOpAccessChain &&
+  assert(base_access_chain->opcode() == spv::Op::OpAccessChain &&
+         access_chain->opcode() == spv::Op::OpAccessChain &&
          access_chain->GetSingleWordInOperand(0) ==
              base_access_chain->result_id());
   Instruction::OperandList new_operands;
@@ -470,10 +470,10 @@
   uint32_t ptr_type_id =
       GetPointerType(*component_type_id, GetStorageClass(var));
 
-  std::unique_ptr<Instruction> new_access_chain(
-      new Instruction(context(), SpvOpAccessChain, ptr_type_id, TakeNextId(),
-                      std::initializer_list<Operand>{
-                          {SPV_OPERAND_TYPE_ID, {var->result_id()}}}));
+  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()}}}));
   for (uint32_t index_id : index_ids) {
     new_access_chain->AddOperand({SPV_OPERAND_TYPE_ID, {index_id}});
   }
@@ -489,13 +489,13 @@
     Instruction* insert_before) {
   uint32_t ptr_type_id =
       GetPointerType(component_type_id, GetStorageClass(var));
-  uint32_t index_id = context()->get_constant_mgr()->GetUIntConst(index);
-  std::unique_ptr<Instruction> new_access_chain(
-      new Instruction(context(), SpvOpAccessChain, ptr_type_id, TakeNextId(),
-                      std::initializer_list<Operand>{
-                          {SPV_OPERAND_TYPE_ID, {var->result_id()}},
-                          {SPV_OPERAND_TYPE_ID, {index_id}},
-                      }));
+  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}},
+      }));
   Instruction* inst = new_access_chain.get();
   context()->get_def_use_mgr()->AnalyzeInstDefUse(inst);
   insert_before->InsertBefore(std::move(new_access_chain));
@@ -519,20 +519,20 @@
       [this, access_chain, &indexes, &interface_var_component_indices,
        scalar_var, loads_to_component_values](Instruction* user) {
         switch (user->opcode()) {
-          case SpvOpAccessChain: {
+          case spv::Op::OpAccessChain: {
             UseBaseAccessChainForAccessChain(user, access_chain);
             ReplaceAccessChainWith(user, interface_var_component_indices,
                                    scalar_var, loads_to_component_values);
             return;
           }
-          case SpvOpStore: {
+          case spv::Op::OpStore: {
             uint32_t value_id = user->GetSingleWordInOperand(1);
             StoreComponentOfValueToAccessChainToScalarVar(
                 value_id, interface_var_component_indices, scalar_var, indexes,
                 user);
             return;
           }
-          case SpvOpLoad: {
+          case spv::Op::OpLoad: {
             Instruction* value =
                 LoadAccessChainToVar(scalar_var, indexes, user);
             loads_to_component_values->insert({user, value});
@@ -546,9 +546,9 @@
 
 void InterfaceVariableScalarReplacement::CloneAnnotationForVariable(
     Instruction* annotation_inst, uint32_t var_id) {
-  assert(annotation_inst->opcode() == SpvOpDecorate ||
-         annotation_inst->opcode() == SpvOpDecorateId ||
-         annotation_inst->opcode() == SpvOpDecorateString);
+  assert(annotation_inst->opcode() == spv::Op::OpDecorate ||
+         annotation_inst->opcode() == spv::Op::OpDecorateId ||
+         annotation_inst->opcode() == spv::Op::OpDecorateString);
   std::unique_ptr<Instruction> new_inst(annotation_inst->Clone(context()));
   new_inst->SetInOperand(0, {var_id});
   context()->AddAnnotationInst(std::move(new_inst));
@@ -593,13 +593,13 @@
 
 uint32_t InterfaceVariableScalarReplacement::GetPointeeTypeIdOfVar(
     Instruction* var) {
-  assert(var->opcode() == SpvOpVariable);
+  assert(var->opcode() == spv::Op::OpVariable);
 
   uint32_t ptr_type_id = var->type_id();
   analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
   Instruction* ptr_type_inst = def_use_mgr->GetDef(ptr_type_id);
 
-  assert(ptr_type_inst->opcode() == SpvOpTypePointer &&
+  assert(ptr_type_inst->opcode() == spv::Op::OpTypePointer &&
          "Variable must have a pointer type.");
   return ptr_type_inst->GetSingleWordInOperand(kOpTypePtrTypeInOperandIndex);
 }
@@ -643,7 +643,7 @@
 Instruction* InterfaceVariableScalarReplacement::CreateLoad(
     uint32_t type_id, Instruction* ptr, Instruction* insert_before) {
   std::unique_ptr<Instruction> load(
-      new Instruction(context(), SpvOpLoad, type_id, TakeNextId(),
+      new Instruction(context(), spv::Op::OpLoad, type_id, TakeNextId(),
                       std::initializer_list<Operand>{
                           {SPV_OPERAND_TYPE_ID, {ptr->result_id()}}}));
   Instruction* load_inst = load.get();
@@ -660,7 +660,7 @@
       component_type_id, value_id, component_indices, extra_array_index));
 
   std::unique_ptr<Instruction> new_store(
-      new Instruction(context(), SpvOpStore));
+      new Instruction(context(), spv::Op::OpStore));
   new_store->AddOperand({SPV_OPERAND_TYPE_ID, {ptr->result_id()}});
   new_store->AddOperand(
       {SPV_OPERAND_TYPE_ID, {composite_extract->result_id()}});
@@ -678,7 +678,7 @@
     const std::vector<uint32_t>& indexes, const uint32_t* extra_first_index) {
   uint32_t component_id = TakeNextId();
   Instruction* composite_extract = new Instruction(
-      context(), SpvOpCompositeExtract, type_id, component_id,
+      context(), spv::Op::OpCompositeExtract, type_id, component_id,
       std::initializer_list<Operand>{{SPV_OPERAND_TYPE_ID, {composite_id}}});
   if (extra_first_index) {
     composite_extract->AddOperand(
@@ -731,8 +731,8 @@
                                             depth_to_component);
   }
   uint32_t new_id = context()->TakeNextId();
-  std::unique_ptr<Instruction> new_composite_construct(
-      new Instruction(context(), SpvOpCompositeConstruct, type_id, new_id, {}));
+  std::unique_ptr<Instruction> new_composite_construct(new Instruction(
+      context(), spv::Op::OpCompositeConstruct, type_id, new_id, {}));
   Instruction* composite_construct = new_composite_construct.get();
   def_use_mgr->AnalyzeInstDefUse(composite_construct);
 
@@ -781,7 +781,7 @@
     uint32_t elem_type_id, uint32_t array_length) {
   analysis::Type* elem_type = context()->get_type_mgr()->GetType(elem_type_id);
   uint32_t array_length_id =
-      context()->get_constant_mgr()->GetUIntConst(array_length);
+      context()->get_constant_mgr()->GetUIntConstId(array_length);
   analysis::Array array_type(
       elem_type,
       analysis::Array::LengthInfo{array_length_id, {0, array_length}});
@@ -789,7 +789,7 @@
 }
 
 uint32_t InterfaceVariableScalarReplacement::GetPointerType(
-    uint32_t type_id, SpvStorageClass storage_class) {
+    uint32_t type_id, spv::StorageClass storage_class) {
   analysis::Type* type = context()->get_type_mgr()->GetType(type_id);
   analysis::Pointer ptr_type(type, storage_class);
   return context()->get_type_mgr()->GetTypeInstruction(&ptr_type);
@@ -797,9 +797,9 @@
 
 InterfaceVariableScalarReplacement::NestedCompositeComponents
 InterfaceVariableScalarReplacement::CreateScalarInterfaceVarsForArray(
-    Instruction* interface_var_type, SpvStorageClass storage_class,
+    Instruction* interface_var_type, spv::StorageClass storage_class,
     uint32_t extra_array_length) {
-  assert(interface_var_type->opcode() == SpvOpTypeArray);
+  assert(interface_var_type->opcode() == spv::Op::OpTypeArray);
 
   analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
   uint32_t array_length = GetArrayLength(def_use_mgr, interface_var_type);
@@ -818,9 +818,9 @@
 
 InterfaceVariableScalarReplacement::NestedCompositeComponents
 InterfaceVariableScalarReplacement::CreateScalarInterfaceVarsForMatrix(
-    Instruction* interface_var_type, SpvStorageClass storage_class,
+    Instruction* interface_var_type, spv::StorageClass storage_class,
     uint32_t extra_array_length) {
-  assert(interface_var_type->opcode() == SpvOpTypeMatrix);
+  assert(interface_var_type->opcode() == spv::Op::OpTypeMatrix);
 
   analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
   uint32_t column_count = interface_var_type->GetSingleWordInOperand(
@@ -841,16 +841,16 @@
 
 InterfaceVariableScalarReplacement::NestedCompositeComponents
 InterfaceVariableScalarReplacement::CreateScalarInterfaceVarsForReplacement(
-    Instruction* interface_var_type, SpvStorageClass storage_class,
+    Instruction* interface_var_type, spv::StorageClass storage_class,
     uint32_t extra_array_length) {
   // Handle array case.
-  if (interface_var_type->opcode() == SpvOpTypeArray) {
+  if (interface_var_type->opcode() == spv::Op::OpTypeArray) {
     return CreateScalarInterfaceVarsForArray(interface_var_type, storage_class,
                                              extra_array_length);
   }
 
   // Handle matrix case.
-  if (interface_var_type->opcode() == SpvOpTypeMatrix) {
+  if (interface_var_type->opcode() == spv::Op::OpTypeMatrix) {
     return CreateScalarInterfaceVarsForMatrix(interface_var_type, storage_class,
                                               extra_array_length);
   }
@@ -865,7 +865,7 @@
       context()->get_type_mgr()->FindPointerToType(type_id, storage_class);
   uint32_t id = TakeNextId();
   std::unique_ptr<Instruction> variable(
-      new Instruction(context(), SpvOpVariable, ptr_type_id, id,
+      new Instruction(context(), spv::Op::OpVariable, ptr_type_id, id,
                       std::initializer_list<Operand>{
                           {SPV_OPERAND_TYPE_STORAGE_CLASS,
                            {static_cast<uint32_t>(storage_class)}}}));
@@ -948,8 +948,8 @@
       return Pass::Status::Failure;
     }
 
-    if (interface_var_type->opcode() != SpvOpTypeArray &&
-        interface_var_type->opcode() != SpvOpTypeMatrix) {
+    if (interface_var_type->opcode() != spv::Op::OpTypeArray &&
+        interface_var_type->opcode() != spv::Op::OpTypeMatrix) {
       continue;
     }
 
diff --git a/source/opt/interface_var_sroa.h b/source/opt/interface_var_sroa.h
index 23baad0..45ed371 100644
--- a/source/opt/interface_var_sroa.h
+++ b/source/opt/interface_var_sroa.h
@@ -90,10 +90,6 @@
   // |component|. Returns true whether the component exists or not.
   bool GetVariableComponent(Instruction* var, uint32_t* component);
 
-  // Returns the interface variable instruction whose result id is
-  // |interface_var_id|.
-  Instruction* GetInterfaceVariable(uint32_t interface_var_id);
-
   // Returns the type of |var| as an instruction.
   Instruction* GetTypeOfVariable(Instruction* var);
 
@@ -115,7 +111,7 @@
   // |extra_array_length| is not zero, adds the extra arrayness to the created
   // scalar variables.
   NestedCompositeComponents CreateScalarInterfaceVarsForReplacement(
-      Instruction* interface_var_type, SpvStorageClass storage_class,
+      Instruction* interface_var_type, spv::StorageClass storage_class,
       uint32_t extra_array_length);
 
   // Creates scalar variables with the storage classe |storage_class| to replace
@@ -123,7 +119,7 @@
   // If |extra_array_length| is not zero, adds the extra arrayness to all the
   // scalar variables.
   NestedCompositeComponents CreateScalarInterfaceVarsForArray(
-      Instruction* interface_var_type, SpvStorageClass storage_class,
+      Instruction* interface_var_type, spv::StorageClass storage_class,
       uint32_t extra_array_length);
 
   // Creates scalar variables with the storage classe |storage_class| to replace
@@ -131,7 +127,7 @@
   // with. If |extra_array_length| is not zero, adds the extra arrayness to all
   // the scalar variables.
   NestedCompositeComponents CreateScalarInterfaceVarsForMatrix(
-      Instruction* interface_var_type, SpvStorageClass storage_class,
+      Instruction* interface_var_type, spv::StorageClass storage_class,
       uint32_t extra_array_length);
 
   // Recursively adds Location and Component decorations to variables in
@@ -345,7 +341,7 @@
 
   // Returns the result id of OpTypePointer instrunction whose Type
   // operand is |type_id| and Storage Class operand is |storage_class|.
-  uint32_t GetPointerType(uint32_t type_id, SpvStorageClass storage_class);
+  uint32_t GetPointerType(uint32_t type_id, spv::StorageClass storage_class);
 
   // Kills an instrunction |inst| and its users.
   void KillInstructionAndUsers(Instruction* inst);
diff --git a/source/opt/interp_fixup_pass.cpp b/source/opt/interp_fixup_pass.cpp
index e8cdd99..2ec2147 100644
--- a/source/opt/interp_fixup_pass.cpp
+++ b/source/opt/interp_fixup_pass.cpp
@@ -19,17 +19,15 @@
 #include <set>
 #include <string>
 
-#include "ir_builder.h"
 #include "source/opt/ir_context.h"
 #include "type_manager.h"
 
 namespace spvtools {
 namespace opt {
-
 namespace {
 
 // Input Operand Indices
-static const int kSpvVariableStorageClassInIdx = 0;
+constexpr int kSpvVariableStorageClassInIdx = 0;
 
 // Folding rule function which attempts to replace |op(OpLoad(a),...)|
 // by |op(a,...)|, where |op| is one of the GLSLstd450 InterpolateAt*
@@ -45,12 +43,12 @@
   uint32_t op1_id = inst->GetSingleWordInOperand(2);
 
   Instruction* load_inst = ctx->get_def_use_mgr()->GetDef(op1_id);
-  if (load_inst->opcode() != SpvOpLoad) return false;
+  if (load_inst->opcode() != spv::Op::OpLoad) return false;
 
   Instruction* base_inst = load_inst->GetBaseAddress();
-  USE_ASSERT(base_inst->opcode() == SpvOpVariable &&
-             base_inst->GetSingleWordInOperand(kSpvVariableStorageClassInIdx) ==
-                 SpvStorageClassInput &&
+  USE_ASSERT(base_inst->opcode() == spv::Op::OpVariable &&
+             spv::StorageClass(base_inst->GetSingleWordInOperand(
+                 kSpvVariableStorageClassInIdx)) == spv::StorageClass::Input &&
              "unexpected interpolant in InterpolateAt*");
 
   uint32_t ptr_id = load_inst->GetSingleWordInOperand(0);
diff --git a/source/opt/invocation_interlock_placement_pass.cpp b/source/opt/invocation_interlock_placement_pass.cpp
new file mode 100644
index 0000000..642e2d2
--- /dev/null
+++ b/source/opt/invocation_interlock_placement_pass.cpp
@@ -0,0 +1,493 @@
+// Copyright (c) 2023 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/opt/invocation_interlock_placement_pass.h"
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <functional>
+#include <optional>
+#include <queue>
+#include <stack>
+#include <unordered_map>
+#include <unordered_set>
+#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/util/string_utils.h"
+
+namespace spvtools {
+namespace opt {
+
+namespace {
+constexpr uint32_t kEntryPointExecutionModelInIdx = 0;
+constexpr uint32_t kEntryPointFunctionIdInIdx = 1;
+constexpr uint32_t kFunctionCallFunctionIdInIdx = 0;
+}  // namespace
+
+bool InvocationInterlockPlacementPass::hasSingleNextBlock(uint32_t block_id,
+                                                          bool reverse_cfg) {
+  if (reverse_cfg) {
+    // We are traversing forward, so check whether there is a single successor.
+    BasicBlock* block = cfg()->block(block_id);
+
+    switch (block->tail()->opcode()) {
+      case spv::Op::OpBranchConditional:
+        return false;
+      case spv::Op::OpSwitch:
+        return block->tail()->NumInOperandWords() == 1;
+      default:
+        return !block->tail()->IsReturnOrAbort();
+    }
+  } else {
+    // We are traversing backward, so check whether there is a single
+    // predecessor.
+    return cfg()->preds(block_id).size() == 1;
+  }
+}
+
+void InvocationInterlockPlacementPass::forEachNext(
+    uint32_t block_id, bool reverse_cfg, std::function<void(uint32_t)> f) {
+  if (reverse_cfg) {
+    BasicBlock* block = cfg()->block(block_id);
+
+    block->ForEachSuccessorLabel([f](uint32_t succ_id) { f(succ_id); });
+  } else {
+    for (uint32_t pred_id : cfg()->preds(block_id)) {
+      f(pred_id);
+    }
+  }
+}
+
+void InvocationInterlockPlacementPass::addInstructionAtBlockBoundary(
+    BasicBlock* block, spv::Op opcode, bool at_end) {
+  if (at_end) {
+    assert(block->begin()->opcode() != spv::Op::OpPhi &&
+           "addInstructionAtBlockBoundary expects to be called with at_end == "
+           "true only if there is a single successor to block");
+    // Insert a begin instruction at the end of the block.
+    Instruction* begin_inst = new Instruction(context(), opcode);
+    begin_inst->InsertAfter(&*--block->tail());
+  } else {
+    assert(block->begin()->opcode() != spv::Op::OpPhi &&
+           "addInstructionAtBlockBoundary expects to be called with at_end == "
+           "false only if there is a single predecessor to block");
+    // Insert an end instruction at the beginning of the block.
+    Instruction* end_inst = new Instruction(context(), opcode);
+    end_inst->InsertBefore(&*block->begin());
+  }
+}
+
+bool InvocationInterlockPlacementPass::killDuplicateBegin(BasicBlock* block) {
+  bool found = false;
+
+  return context()->KillInstructionIf(
+      block->begin(), block->end(), [&found](Instruction* inst) {
+        if (inst->opcode() == spv::Op::OpBeginInvocationInterlockEXT) {
+          if (found) {
+            return true;
+          }
+          found = true;
+        }
+        return false;
+      });
+}
+
+bool InvocationInterlockPlacementPass::killDuplicateEnd(BasicBlock* block) {
+  std::vector<Instruction*> to_kill;
+  block->ForEachInst([&to_kill](Instruction* inst) {
+    if (inst->opcode() == spv::Op::OpEndInvocationInterlockEXT) {
+      to_kill.push_back(inst);
+    }
+  });
+
+  if (to_kill.size() <= 1) {
+    return false;
+  }
+
+  to_kill.pop_back();
+
+  for (Instruction* inst : to_kill) {
+    context()->KillInst(inst);
+  }
+
+  return true;
+}
+
+void InvocationInterlockPlacementPass::recordBeginOrEndInFunction(
+    Function* func) {
+  if (extracted_functions_.count(func)) {
+    return;
+  }
+
+  bool had_begin = false;
+  bool had_end = false;
+
+  func->ForEachInst([this, &had_begin, &had_end](Instruction* inst) {
+    switch (inst->opcode()) {
+      case spv::Op::OpBeginInvocationInterlockEXT:
+        had_begin = true;
+        break;
+      case spv::Op::OpEndInvocationInterlockEXT:
+        had_end = true;
+        break;
+      case spv::Op::OpFunctionCall: {
+        uint32_t function_id =
+            inst->GetSingleWordInOperand(kFunctionCallFunctionIdInIdx);
+        Function* inner_func = context()->GetFunction(function_id);
+        recordBeginOrEndInFunction(inner_func);
+        ExtractionResult result = extracted_functions_[inner_func];
+        had_begin = had_begin || result.had_begin;
+        had_end = had_end || result.had_end;
+        break;
+      }
+      default:
+        break;
+    }
+  });
+
+  ExtractionResult result = {had_begin, had_end};
+  extracted_functions_[func] = result;
+}
+
+bool InvocationInterlockPlacementPass::
+    removeBeginAndEndInstructionsFromFunction(Function* func) {
+  bool modified = false;
+  func->ForEachInst([this, &modified](Instruction* inst) {
+    switch (inst->opcode()) {
+      case spv::Op::OpBeginInvocationInterlockEXT:
+        context()->KillInst(inst);
+        modified = true;
+        break;
+      case spv::Op::OpEndInvocationInterlockEXT:
+        context()->KillInst(inst);
+        modified = true;
+        break;
+      default:
+        break;
+    }
+  });
+  return modified;
+}
+
+bool InvocationInterlockPlacementPass::extractInstructionsFromCalls(
+    std::vector<BasicBlock*> blocks) {
+  bool modified = false;
+
+  for (BasicBlock* block : blocks) {
+    block->ForEachInst([this, &modified](Instruction* inst) {
+      if (inst->opcode() == spv::Op::OpFunctionCall) {
+        uint32_t function_id =
+            inst->GetSingleWordInOperand(kFunctionCallFunctionIdInIdx);
+        Function* func = context()->GetFunction(function_id);
+        ExtractionResult result = extracted_functions_[func];
+
+        if (result.had_begin) {
+          Instruction* new_inst = new Instruction(
+              context(), spv::Op::OpBeginInvocationInterlockEXT);
+          new_inst->InsertBefore(inst);
+          modified = true;
+        }
+        if (result.had_end) {
+          Instruction* new_inst =
+              new Instruction(context(), spv::Op::OpEndInvocationInterlockEXT);
+          new_inst->InsertAfter(inst);
+          modified = true;
+        }
+      }
+    });
+  }
+  return modified;
+}
+
+void InvocationInterlockPlacementPass::recordExistingBeginAndEndBlock(
+    std::vector<BasicBlock*> blocks) {
+  for (BasicBlock* block : blocks) {
+    block->ForEachInst([this, block](Instruction* inst) {
+      switch (inst->opcode()) {
+        case spv::Op::OpBeginInvocationInterlockEXT:
+          begin_.insert(block->id());
+          break;
+        case spv::Op::OpEndInvocationInterlockEXT:
+          end_.insert(block->id());
+          break;
+        default:
+          break;
+      }
+    });
+  }
+}
+
+InvocationInterlockPlacementPass::BlockSet
+InvocationInterlockPlacementPass::computeReachableBlocks(
+    BlockSet& previous_inside, const BlockSet& starting_nodes,
+    bool reverse_cfg) {
+  BlockSet inside = starting_nodes;
+
+  std::deque<uint32_t> worklist;
+  worklist.insert(worklist.begin(), starting_nodes.begin(),
+                  starting_nodes.end());
+
+  while (!worklist.empty()) {
+    uint32_t block_id = worklist.front();
+    worklist.pop_front();
+
+    forEachNext(block_id, reverse_cfg,
+                [&inside, &previous_inside, &worklist](uint32_t next_id) {
+                  previous_inside.insert(next_id);
+                  if (inside.insert(next_id).second) {
+                    worklist.push_back(next_id);
+                  }
+                });
+  }
+
+  return inside;
+}
+
+bool InvocationInterlockPlacementPass::removeUnneededInstructions(
+    BasicBlock* block) {
+  bool modified = false;
+  if (!predecessors_after_begin_.count(block->id()) &&
+      after_begin_.count(block->id())) {
+    // None of the previous blocks are in the critical section, but this block
+    // is. This can only happen if this block already has at least one begin
+    // instruction. Leave the first begin instruction, and remove any others.
+    modified |= killDuplicateBegin(block);
+  } else if (predecessors_after_begin_.count(block->id())) {
+    // At least one previous block is in the critical section; remove all
+    // begin instructions in this block.
+    modified |= context()->KillInstructionIf(
+        block->begin(), block->end(), [](Instruction* inst) {
+          return inst->opcode() == spv::Op::OpBeginInvocationInterlockEXT;
+        });
+  }
+
+  if (!successors_before_end_.count(block->id()) &&
+      before_end_.count(block->id())) {
+    // Same as above
+    modified |= killDuplicateEnd(block);
+  } else if (successors_before_end_.count(block->id())) {
+    modified |= context()->KillInstructionIf(
+        block->begin(), block->end(), [](Instruction* inst) {
+          return inst->opcode() == spv::Op::OpEndInvocationInterlockEXT;
+        });
+  }
+  return modified;
+}
+
+BasicBlock* InvocationInterlockPlacementPass::splitEdge(BasicBlock* block,
+                                                        uint32_t succ_id) {
+  // Create a new block to replace the critical edge.
+  auto new_succ_temp = MakeUnique<BasicBlock>(
+      MakeUnique<Instruction>(context(), spv::Op::OpLabel, 0, TakeNextId(),
+                              std::initializer_list<Operand>{}));
+  auto* new_succ = new_succ_temp.get();
+
+  // Insert the new block into the function.
+  block->GetParent()->InsertBasicBlockAfter(std::move(new_succ_temp), block);
+
+  new_succ->AddInstruction(MakeUnique<Instruction>(
+      context(), spv::Op::OpBranch, 0, 0,
+      std::initializer_list<Operand>{
+          Operand(spv_operand_type_t::SPV_OPERAND_TYPE_ID, {succ_id})}));
+
+  assert(block->tail()->opcode() == spv::Op::OpBranchConditional ||
+         block->tail()->opcode() == spv::Op::OpSwitch);
+
+  // Update the first branch to successor to instead branch to
+  // the new successor. If there are multiple edges, we arbitrarily choose the
+  // first time it appears in the list. The other edges to `succ_id` will have
+  // to be split by another call to `splitEdge`.
+  block->tail()->WhileEachInId([new_succ, succ_id](uint32_t* branch_id) {
+    if (*branch_id == succ_id) {
+      *branch_id = new_succ->id();
+      return false;
+    }
+    return true;
+  });
+
+  return new_succ;
+}
+
+bool InvocationInterlockPlacementPass::placeInstructionsForEdge(
+    BasicBlock* block, uint32_t next_id, BlockSet& inside,
+    BlockSet& previous_inside, spv::Op opcode, bool reverse_cfg) {
+  bool modified = false;
+
+  if (previous_inside.count(next_id) && !inside.count(block->id())) {
+    // This block is not in the critical section but the next has at least one
+    // other previous block that is, so this block should be enter it as well.
+    // We need to add begin or end instructions to the edge.
+
+    modified = true;
+
+    if (hasSingleNextBlock(block->id(), reverse_cfg)) {
+      // This is the only next block.
+
+      // Additionally, because `next_id` is in `previous_inside`, we know that
+      // `next_id` has at least one previous block in `inside`. And because
+      // 'block` is not in `inside`, that means the `next_id` has to have at
+      // least one other previous block in `inside`.
+
+      // This is solely for a debug assertion. It is essentially recomputing the
+      // value of `previous_inside` to verify that it was computed correctly
+      // such that the above statement is true.
+      bool next_has_previous_inside = false;
+      // By passing !reverse_cfg to forEachNext, we are actually iterating over
+      // the previous blocks.
+      forEachNext(next_id, !reverse_cfg,
+                  [&next_has_previous_inside, inside](uint32_t previous_id) {
+                    if (inside.count(previous_id)) {
+                      next_has_previous_inside = true;
+                    }
+                  });
+      assert(next_has_previous_inside &&
+             "`previous_inside` must be the set of blocks with at least one "
+             "previous block in `inside`");
+
+      addInstructionAtBlockBoundary(block, opcode, reverse_cfg);
+    } else {
+      // This block has multiple next blocks. Split the edge and insert the
+      // instruction in the new next block.
+      BasicBlock* new_branch;
+      if (reverse_cfg) {
+        new_branch = splitEdge(block, next_id);
+      } else {
+        new_branch = splitEdge(cfg()->block(next_id), block->id());
+      }
+
+      auto inst = new Instruction(context(), opcode);
+      inst->InsertBefore(&*new_branch->tail());
+    }
+  }
+
+  return modified;
+}
+
+bool InvocationInterlockPlacementPass::placeInstructions(BasicBlock* block) {
+  bool modified = false;
+
+  block->ForEachSuccessorLabel([this, block, &modified](uint32_t succ_id) {
+    modified |= 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);
+  });
+
+  return modified;
+}
+
+bool InvocationInterlockPlacementPass::processFragmentShaderEntry(
+    Function* entry_func) {
+  bool modified = false;
+
+  // Save the original order of blocks in the function, so we don't iterate over
+  // newly-added blocks.
+  std::vector<BasicBlock*> original_blocks;
+  for (auto bi = entry_func->begin(); bi != entry_func->end(); ++bi) {
+    original_blocks.push_back(&*bi);
+  }
+
+  modified |= extractInstructionsFromCalls(original_blocks);
+  recordExistingBeginAndEndBlock(original_blocks);
+
+  after_begin_ = computeReachableBlocks(predecessors_after_begin_, begin_,
+                                        /* reverse_cfg= */ true);
+  before_end_ = computeReachableBlocks(successors_before_end_, end_,
+                                       /* reverse_cfg= */ false);
+
+  for (BasicBlock* block : original_blocks) {
+    modified |= removeUnneededInstructions(block);
+    modified |= placeInstructions(block);
+  }
+  return modified;
+}
+
+bool InvocationInterlockPlacementPass::isFragmentShaderInterlockEnabled() {
+  if (!context()->get_feature_mgr()->HasExtension(
+          kSPV_EXT_fragment_shader_interlock)) {
+    return false;
+  }
+
+  if (context()->get_feature_mgr()->HasCapability(
+          spv::Capability::FragmentShaderSampleInterlockEXT)) {
+    return true;
+  }
+
+  if (context()->get_feature_mgr()->HasCapability(
+          spv::Capability::FragmentShaderPixelInterlockEXT)) {
+    return true;
+  }
+
+  if (context()->get_feature_mgr()->HasCapability(
+          spv::Capability::FragmentShaderShadingRateInterlockEXT)) {
+    return true;
+  }
+
+  return false;
+}
+
+Pass::Status InvocationInterlockPlacementPass::Process() {
+  // Skip this pass if the necessary extension or capability is missing
+  if (!isFragmentShaderInterlockEnabled()) {
+    return Status::SuccessWithoutChange;
+  }
+
+  bool modified = false;
+
+  std::unordered_set<Function*> entry_points;
+  for (Instruction& entry_inst : context()->module()->entry_points()) {
+    uint32_t entry_id =
+        entry_inst.GetSingleWordInOperand(kEntryPointFunctionIdInIdx);
+    entry_points.insert(context()->GetFunction(entry_id));
+  }
+
+  for (auto fi = context()->module()->begin(); fi != context()->module()->end();
+       ++fi) {
+    Function* func = &*fi;
+    recordBeginOrEndInFunction(func);
+    if (!entry_points.count(func) && extracted_functions_.count(func)) {
+      modified |= removeBeginAndEndInstructionsFromFunction(func);
+    }
+  }
+
+  for (Instruction& entry_inst : context()->module()->entry_points()) {
+    uint32_t entry_id =
+        entry_inst.GetSingleWordInOperand(kEntryPointFunctionIdInIdx);
+    Function* entry_func = context()->GetFunction(entry_id);
+
+    auto execution_model = spv::ExecutionModel(
+        entry_inst.GetSingleWordInOperand(kEntryPointExecutionModelInIdx));
+
+    if (execution_model != spv::ExecutionModel::Fragment) {
+      continue;
+    }
+
+    modified |= processFragmentShaderEntry(entry_func);
+  }
+
+  return modified ? Pass::Status::SuccessWithChange
+                  : Pass::Status::SuccessWithoutChange;
+}
+
+}  // namespace opt
+}  // namespace spvtools
diff --git a/source/opt/invocation_interlock_placement_pass.h b/source/opt/invocation_interlock_placement_pass.h
new file mode 100644
index 0000000..4e85be8
--- /dev/null
+++ b/source/opt/invocation_interlock_placement_pass.h
@@ -0,0 +1,158 @@
+// Copyright (c) 2023 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_OPT_DEDUPE_INTERLOCK_INVOCATION_PASS_H_
+#define SOURCE_OPT_DEDUPE_INTERLOCK_INVOCATION_PASS_H_
+
+#include <algorithm>
+#include <array>
+#include <functional>
+#include <optional>
+#include <unordered_map>
+#include <unordered_set>
+
+#include "source/enum_set.h"
+#include "source/extensions.h"
+#include "source/opt/ir_context.h"
+#include "source/opt/module.h"
+#include "source/opt/pass.h"
+#include "source/spirv_target_env.h"
+
+namespace spvtools {
+namespace opt {
+
+// This pass will ensure that an entry point will only have at most one
+// OpBeginInterlockInvocationEXT and one OpEndInterlockInvocationEXT, in that
+// order
+class InvocationInterlockPlacementPass : public Pass {
+ public:
+  InvocationInterlockPlacementPass() {}
+  InvocationInterlockPlacementPass(const InvocationInterlockPlacementPass&) =
+      delete;
+  InvocationInterlockPlacementPass(InvocationInterlockPlacementPass&&) = delete;
+
+  const char* name() const override { return "dedupe-interlock-invocation"; }
+  Status Process() override;
+
+ private:
+  using BlockSet = std::unordered_set<uint32_t>;
+
+  // Specifies whether a function originally had a begin or end instruction.
+  struct ExtractionResult {
+    bool had_begin : 1;
+    bool had_end : 2;
+  };
+
+  // Check if a block has only a single next block, depending on the directing
+  // that we are traversing the CFG. If reverse_cfg is true, we are walking
+  // forward through the CFG, and will return if the block has only one
+  // successor. Otherwise, we are walking backward through the CFG, and will
+  // return if the block has only one predecessor.
+  bool hasSingleNextBlock(uint32_t block_id, bool reverse_cfg);
+
+  // Iterate over each of a block's predecessors or successors, depending on
+  // direction. If reverse_cfg is true, we are walking forward through the CFG,
+  // and need to iterate over the successors. Otherwise, we are walking backward
+  // through the CFG, and need to iterate over the predecessors.
+  void forEachNext(uint32_t block_id, bool reverse_cfg,
+                   std::function<void(uint32_t)> f);
+
+  // Add either a begin or end instruction to the edge of the basic block. If
+  // at_end is true, add the instruction to the end of the block; otherwise add
+  // the instruction to the beginning of the basic block.
+  void addInstructionAtBlockBoundary(BasicBlock* block, spv::Op opcode,
+                                     bool at_end);
+
+  // Remove every OpBeginInvocationInterlockEXT instruction in block after the
+  // first. Returns whether any instructions were removed.
+  bool killDuplicateBegin(BasicBlock* block);
+  // Remove every OpBeginInvocationInterlockEXT instruction in block before the
+  // last. Returns whether any instructions were removed.
+  bool killDuplicateEnd(BasicBlock* block);
+
+  // Records whether a function will potentially execute a begin or end
+  // instruction.
+  void recordBeginOrEndInFunction(Function* func);
+
+  // Recursively removes any begin or end instructions from func and any
+  // function func calls. Returns whether any instructions were removed.
+  bool removeBeginAndEndInstructionsFromFunction(Function* func);
+
+  // For every function call in any of the passed blocks, move any begin or end
+  // instructions outside of the function call. Returns whether any extractions
+  // occurred.
+  bool extractInstructionsFromCalls(std::vector<BasicBlock*> blocks);
+
+  // Finds the sets of blocks that contain OpBeginInvocationInterlockEXT and
+  // OpEndInvocationInterlockEXT, storing them in the member variables begin_
+  // and end_ respectively.
+  void recordExistingBeginAndEndBlock(std::vector<BasicBlock*> blocks);
+
+  // Compute the set of blocks including or after the barrier instruction, and
+  // the set of blocks with any previous blocks inside the barrier instruction.
+  // If reverse_cfg is true, move forward through the CFG, computing
+  // after_begin_ and predecessors_after_begin_computing after_begin_ and
+  // predecessors_after_begin_, otherwise, move backward through the CFG,
+  // computing before_end_ and successors_before_end_.
+  BlockSet computeReachableBlocks(BlockSet& in_set,
+                                  const BlockSet& starting_nodes,
+                                  bool reverse_cfg);
+
+  // Remove unneeded begin and end instructions in block.
+  bool removeUnneededInstructions(BasicBlock* block);
+
+  // Given a block which branches to multiple successors, and a specific
+  // successor, creates a new empty block, and update the branch instruction to
+  // branch to the new block instead.
+  BasicBlock* splitEdge(BasicBlock* block, uint32_t succ_id);
+
+  // 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);
+  // Calls placeInstructionsForEdge for each edge in block.
+  bool placeInstructions(BasicBlock* block);
+
+  // Processes a single fragment shader entry function.
+  bool processFragmentShaderEntry(Function* entry_func);
+
+  // Returns whether the module has the SPV_EXT_fragment_shader_interlock
+  // extension and one of the FragmentShader*InterlockEXT capabilities.
+  bool isFragmentShaderInterlockEnabled();
+
+  // Maps a function to whether that function originally held a begin or end
+  // instruction.
+  std::unordered_map<Function*, ExtractionResult> extracted_functions_;
+
+  // The set of blocks which have an OpBeginInvocationInterlockEXT instruction.
+  BlockSet begin_;
+  // The set of blocks which have an OpEndInvocationInterlockEXT instruction.
+  BlockSet end_;
+  // The set of blocks which either have a begin instruction, or have a
+  // predecessor which has a begin instruction.
+  BlockSet after_begin_;
+  // The set of blocks which either have an end instruction, or have a successor
+  // which have an end instruction.
+  BlockSet before_end_;
+  // The set of blocks which have a predecessor in after_begin_.
+  BlockSet predecessors_after_begin_;
+  // The set of blocks which have a successor in before_end_.
+  BlockSet successors_before_end_;
+};
+
+}  // namespace opt
+}  // namespace spvtools
+#endif  // SOURCE_OPT_DEDUPE_INTERLOCK_INVOCATION_PASS_H_
diff --git a/source/opt/ir_builder.h b/source/opt/ir_builder.h
index 9d4fa8f..f3e0afc 100644
--- a/source/opt/ir_builder.h
+++ b/source/opt/ir_builder.h
@@ -30,7 +30,7 @@
 
 // In SPIR-V, ids are encoded as uint16_t, this id is guaranteed to be always
 // invalid.
-const uint32_t kInvalidId = std::numeric_limits<uint32_t>::max();
+constexpr uint32_t kInvalidId = std::numeric_limits<uint32_t>::max();
 
 // Helper class to abstract instruction construction and insertion.
 // The instruction builder can preserve the following analyses (specified via
@@ -58,7 +58,7 @@
       : InstructionBuilder(context, parent_block, parent_block->end(),
                            preserved_analyses) {}
 
-  Instruction* AddNullaryOp(uint32_t type_id, SpvOp opcode) {
+  Instruction* AddNullaryOp(uint32_t type_id, spv::Op opcode) {
     uint32_t result_id = 0;
     if (type_id != 0) {
       result_id = GetContext()->TakeNextId();
@@ -71,7 +71,7 @@
     return AddInstruction(std::move(new_inst));
   }
 
-  Instruction* AddUnaryOp(uint32_t type_id, SpvOp opcode, uint32_t operand1) {
+  Instruction* AddUnaryOp(uint32_t type_id, spv::Op opcode, uint32_t operand1) {
     uint32_t result_id = 0;
     if (type_id != 0) {
       result_id = GetContext()->TakeNextId();
@@ -85,7 +85,7 @@
     return AddInstruction(std::move(newUnOp));
   }
 
-  Instruction* AddBinaryOp(uint32_t type_id, SpvOp opcode, uint32_t operand1,
+  Instruction* AddBinaryOp(uint32_t type_id, spv::Op opcode, uint32_t operand1,
                            uint32_t operand2) {
     uint32_t result_id = 0;
     if (type_id != 0) {
@@ -95,13 +95,14 @@
       }
     }
     std::unique_ptr<Instruction> newBinOp(new Instruction(
-        GetContext(), opcode, type_id, opcode == SpvOpStore ? 0 : result_id,
+        GetContext(), opcode, type_id,
+        opcode == spv::Op::OpStore ? 0 : result_id,
         {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand1}},
          {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand2}}}));
     return AddInstruction(std::move(newBinOp));
   }
 
-  Instruction* AddTernaryOp(uint32_t type_id, SpvOp opcode, uint32_t operand1,
+  Instruction* AddTernaryOp(uint32_t type_id, spv::Op opcode, uint32_t operand1,
                             uint32_t operand2, uint32_t operand3) {
     uint32_t result_id = 0;
     if (type_id != 0) {
@@ -118,7 +119,7 @@
     return AddInstruction(std::move(newTernOp));
   }
 
-  Instruction* AddQuadOp(uint32_t type_id, SpvOp opcode, uint32_t operand1,
+  Instruction* AddQuadOp(uint32_t type_id, spv::Op opcode, uint32_t operand1,
                          uint32_t operand2, uint32_t operand3,
                          uint32_t operand4) {
     uint32_t result_id = 0;
@@ -137,7 +138,7 @@
     return AddInstruction(std::move(newQuadOp));
   }
 
-  Instruction* AddIdLiteralOp(uint32_t type_id, SpvOp opcode, uint32_t id,
+  Instruction* AddIdLiteralOp(uint32_t type_id, spv::Op opcode, uint32_t id,
                               uint32_t uliteral) {
     uint32_t result_id = 0;
     if (type_id != 0) {
@@ -157,7 +158,7 @@
   // |typid| must be the id of the instruction's type.
   // |operands| must be a sequence of operand ids.
   // Use |result| for the result id if non-zero.
-  Instruction* AddNaryOp(uint32_t type_id, SpvOp opcode,
+  Instruction* AddNaryOp(uint32_t type_id, spv::Op opcode,
                          const std::vector<uint32_t>& operands,
                          uint32_t result = 0) {
     std::vector<Operand> ops;
@@ -174,10 +175,10 @@
   // Creates a new selection merge instruction.
   // The id |merge_id| is the merge basic block id.
   Instruction* AddSelectionMerge(
-      uint32_t merge_id,
-      uint32_t selection_control = SpvSelectionControlMaskNone) {
+      uint32_t merge_id, uint32_t selection_control = static_cast<uint32_t>(
+                             spv::SelectionControlMask::MaskNone)) {
     std::unique_ptr<Instruction> new_branch_merge(new Instruction(
-        GetContext(), SpvOpSelectionMerge, 0, 0,
+        GetContext(), spv::Op::OpSelectionMerge, 0, 0,
         {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {merge_id}},
          {spv_operand_type_t::SPV_OPERAND_TYPE_SELECTION_CONTROL,
           {selection_control}}}));
@@ -189,9 +190,10 @@
   // |continue_id| is the id of the continue block.
   // |loop_control| are the loop control flags to be added to the instruction.
   Instruction* AddLoopMerge(uint32_t merge_id, uint32_t continue_id,
-                            uint32_t loop_control = SpvLoopControlMaskNone) {
+                            uint32_t loop_control = static_cast<uint32_t>(
+                                spv::LoopControlMask::MaskNone)) {
     std::unique_ptr<Instruction> new_branch_merge(new Instruction(
-        GetContext(), SpvOpLoopMerge, 0, 0,
+        GetContext(), spv::Op::OpLoopMerge, 0, 0,
         {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {merge_id}},
          {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {continue_id}},
          {spv_operand_type_t::SPV_OPERAND_TYPE_LOOP_CONTROL, {loop_control}}}));
@@ -203,7 +205,7 @@
   // well formed.
   Instruction* AddBranch(uint32_t label_id) {
     std::unique_ptr<Instruction> new_branch(new Instruction(
-        GetContext(), SpvOpBranch, 0, 0,
+        GetContext(), spv::Op::OpBranch, 0, 0,
         {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {label_id}}}));
     return AddInstruction(std::move(new_branch));
   }
@@ -226,12 +228,13 @@
   Instruction* AddConditionalBranch(
       uint32_t cond_id, uint32_t true_id, uint32_t false_id,
       uint32_t merge_id = kInvalidId,
-      uint32_t selection_control = SpvSelectionControlMaskNone) {
+      uint32_t selection_control =
+          static_cast<uint32_t>(spv::SelectionControlMask::MaskNone)) {
     if (merge_id != kInvalidId) {
       AddSelectionMerge(merge_id, selection_control);
     }
     std::unique_ptr<Instruction> new_branch(new Instruction(
-        GetContext(), SpvOpBranchConditional, 0, 0,
+        GetContext(), spv::Op::OpBranchConditional, 0, 0,
         {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {cond_id}},
          {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {true_id}},
          {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {false_id}}}));
@@ -255,7 +258,8 @@
       uint32_t selector_id, uint32_t default_id,
       const std::vector<std::pair<Operand::OperandData, uint32_t>>& targets,
       uint32_t merge_id = kInvalidId,
-      uint32_t selection_control = SpvSelectionControlMaskNone) {
+      uint32_t selection_control =
+          static_cast<uint32_t>(spv::SelectionControlMask::MaskNone)) {
     if (merge_id != kInvalidId) {
       AddSelectionMerge(merge_id, selection_control);
     }
@@ -272,7 +276,7 @@
           Operand{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {target.second}});
     }
     std::unique_ptr<Instruction> new_switch(
-        new Instruction(GetContext(), SpvOpSwitch, 0, 0, operands));
+        new Instruction(GetContext(), spv::Op::OpSwitch, 0, 0, operands));
     return AddInstruction(std::move(new_switch));
   }
 
@@ -283,7 +287,7 @@
   Instruction* AddPhi(uint32_t type, const std::vector<uint32_t>& incomings,
                       uint32_t result = 0) {
     assert(incomings.size() % 2 == 0 && "A sequence of pairs is expected");
-    return AddNaryOp(type, SpvOpPhi, incomings, result);
+    return AddNaryOp(type, spv::Op::OpPhi, incomings, result);
   }
 
   // Creates an addition instruction.
@@ -294,7 +298,7 @@
   Instruction* AddIAdd(uint32_t type, uint32_t op1, uint32_t op2) {
     // TODO(1841): Handle id overflow.
     std::unique_ptr<Instruction> inst(new Instruction(
-        GetContext(), SpvOpIAdd, type, GetContext()->TakeNextId(),
+        GetContext(), spv::Op::OpIAdd, type, GetContext()->TakeNextId(),
         {{SPV_OPERAND_TYPE_ID, {op1}}, {SPV_OPERAND_TYPE_ID, {op2}}}));
     return AddInstruction(std::move(inst));
   }
@@ -308,7 +312,7 @@
     uint32_t type = GetContext()->get_type_mgr()->GetId(&bool_type);
     // TODO(1841): Handle id overflow.
     std::unique_ptr<Instruction> inst(new Instruction(
-        GetContext(), SpvOpULessThan, type, GetContext()->TakeNextId(),
+        GetContext(), spv::Op::OpULessThan, type, GetContext()->TakeNextId(),
         {{SPV_OPERAND_TYPE_ID, {op1}}, {SPV_OPERAND_TYPE_ID, {op2}}}));
     return AddInstruction(std::move(inst));
   }
@@ -322,7 +326,7 @@
     uint32_t type = GetContext()->get_type_mgr()->GetId(&bool_type);
     // TODO(1841): Handle id overflow.
     std::unique_ptr<Instruction> inst(new Instruction(
-        GetContext(), SpvOpSLessThan, type, GetContext()->TakeNextId(),
+        GetContext(), spv::Op::OpSLessThan, type, GetContext()->TakeNextId(),
         {{SPV_OPERAND_TYPE_ID, {op1}}, {SPV_OPERAND_TYPE_ID, {op2}}}));
     return AddInstruction(std::move(inst));
   }
@@ -352,7 +356,7 @@
                          uint32_t false_value) {
     // TODO(1841): Handle id overflow.
     std::unique_ptr<Instruction> select(new Instruction(
-        GetContext(), SpvOpSelect, type, GetContext()->TakeNextId(),
+        GetContext(), spv::Op::OpSelect, type, GetContext()->TakeNextId(),
         std::initializer_list<Operand>{{SPV_OPERAND_TYPE_ID, {cond}},
                                        {SPV_OPERAND_TYPE_ID, {true_value}},
                                        {SPV_OPERAND_TYPE_ID, {false_value}}}));
@@ -378,7 +382,7 @@
     }
     // TODO(1841): Handle id overflow.
     std::unique_ptr<Instruction> construct(
-        new Instruction(GetContext(), SpvOpCompositeConstruct, type,
+        new Instruction(GetContext(), spv::Op::OpCompositeConstruct, type,
                         GetContext()->TakeNextId(), ops));
     return AddInstruction(std::move(construct));
   }
@@ -436,6 +440,22 @@
     return GetContext()->get_constant_mgr()->GetDefiningInstruction(constant);
   }
 
+  Instruction* GetBoolConstant(bool value) {
+    analysis::Bool type;
+    uint32_t type_id = GetContext()->get_type_mgr()->GetTypeInstruction(&type);
+    analysis::Type* rebuilt_type =
+        GetContext()->get_type_mgr()->GetType(type_id);
+    uint32_t word = value;
+    const analysis::Constant* constant =
+        GetContext()->get_constant_mgr()->GetConstant(rebuilt_type, {word});
+    return GetContext()->get_constant_mgr()->GetDefiningInstruction(constant);
+  }
+
+  uint32_t GetBoolConstantId(bool value) {
+    Instruction* inst = GetBoolConstant(value);
+    return (inst != nullptr ? inst->result_id() : 0);
+  }
+
   Instruction* AddCompositeExtract(uint32_t type, uint32_t id_of_composite,
                                    const std::vector<uint32_t>& index_list) {
     std::vector<Operand> operands;
@@ -447,7 +467,7 @@
 
     // TODO(1841): Handle id overflow.
     std::unique_ptr<Instruction> new_inst(
-        new Instruction(GetContext(), SpvOpCompositeExtract, type,
+        new Instruction(GetContext(), spv::Op::OpCompositeExtract, type,
                         GetContext()->TakeNextId(), operands));
     return AddInstruction(std::move(new_inst));
   }
@@ -455,7 +475,7 @@
   // Creates an unreachable instruction.
   Instruction* AddUnreachable() {
     std::unique_ptr<Instruction> select(
-        new Instruction(GetContext(), SpvOpUnreachable, 0, 0,
+        new Instruction(GetContext(), spv::Op::OpUnreachable, 0, 0,
                         std::initializer_list<Operand>{}));
     return AddInstruction(std::move(select));
   }
@@ -471,18 +491,25 @@
 
     // TODO(1841): Handle id overflow.
     std::unique_ptr<Instruction> new_inst(
-        new Instruction(GetContext(), SpvOpAccessChain, type_id,
+        new Instruction(GetContext(), spv::Op::OpAccessChain, type_id,
                         GetContext()->TakeNextId(), operands));
     return AddInstruction(std::move(new_inst));
   }
 
-  Instruction* AddLoad(uint32_t type_id, uint32_t base_ptr_id) {
+  Instruction* AddLoad(uint32_t type_id, uint32_t base_ptr_id,
+                       uint32_t alignment = 0) {
     std::vector<Operand> operands;
     operands.push_back({SPV_OPERAND_TYPE_ID, {base_ptr_id}});
+    if (alignment != 0) {
+      operands.push_back(
+          {SPV_OPERAND_TYPE_MEMORY_ACCESS,
+           {static_cast<uint32_t>(spv::MemoryAccessMask::Aligned)}});
+      operands.push_back({SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER, {alignment}});
+    }
 
     // TODO(1841): Handle id overflow.
     std::unique_ptr<Instruction> new_inst(
-        new Instruction(GetContext(), SpvOpLoad, type_id,
+        new Instruction(GetContext(), spv::Op::OpLoad, type_id,
                         GetContext()->TakeNextId(), operands));
     return AddInstruction(std::move(new_inst));
   }
@@ -491,7 +518,7 @@
     std::vector<Operand> operands;
     operands.push_back({SPV_OPERAND_TYPE_ID, {storage_class}});
     std::unique_ptr<Instruction> new_inst(
-        new Instruction(GetContext(), SpvOpVariable, type_id,
+        new Instruction(GetContext(), spv::Op::OpVariable, type_id,
                         GetContext()->TakeNextId(), operands));
     return AddInstruction(std::move(new_inst));
   }
@@ -502,7 +529,7 @@
     operands.push_back({SPV_OPERAND_TYPE_ID, {obj_id}});
 
     std::unique_ptr<Instruction> new_inst(
-        new Instruction(GetContext(), SpvOpStore, 0, 0, operands));
+        new Instruction(GetContext(), spv::Op::OpStore, 0, 0, operands));
     return AddInstruction(std::move(new_inst));
   }
 
@@ -518,8 +545,9 @@
     if (result_id == 0) {
       return nullptr;
     }
-    std::unique_ptr<Instruction> new_inst(new Instruction(
-        GetContext(), SpvOpFunctionCall, result_type, result_id, operands));
+    std::unique_ptr<Instruction> new_inst(
+        new Instruction(GetContext(), spv::Op::OpFunctionCall, result_type,
+                        result_id, operands));
     return AddInstruction(std::move(new_inst));
   }
 
@@ -538,8 +566,9 @@
       return nullptr;
     }
 
-    std::unique_ptr<Instruction> new_inst(new Instruction(
-        GetContext(), SpvOpVectorShuffle, result_type, result_id, operands));
+    std::unique_ptr<Instruction> new_inst(
+        new Instruction(GetContext(), spv::Op::OpVectorShuffle, result_type,
+                        result_id, operands));
     return AddInstruction(std::move(new_inst));
   }
 
@@ -560,7 +589,7 @@
     }
 
     std::unique_ptr<Instruction> new_inst(new Instruction(
-        GetContext(), SpvOpExtInst, result_type, result_id, operands));
+        GetContext(), spv::Op::OpExtInst, result_type, result_id, operands));
     return AddInstruction(std::move(new_inst));
   }
 
diff --git a/source/opt/ir_context.cpp b/source/opt/ir_context.cpp
index c9c3f1b..239d316 100644
--- a/source/opt/ir_context.cpp
+++ b/source/opt/ir_context.cpp
@@ -19,26 +19,23 @@
 #include "OpenCLDebugInfo100.h"
 #include "source/latest_version_glsl_std_450_header.h"
 #include "source/opt/log.h"
-#include "source/opt/mem_pass.h"
 #include "source/opt/reflect.h"
 
-namespace {
-
-static const int kSpvDecorateTargetIdInIdx = 0;
-static const int kSpvDecorateDecorationInIdx = 1;
-static const int kSpvDecorateBuiltinInIdx = 2;
-static const int kEntryPointInterfaceInIdx = 3;
-static const int kEntryPointFunctionIdInIdx = 1;
-
-// Constants for OpenCL.DebugInfo.100 / NonSemantic.Shader.DebugInfo.100
-// extension instructions.
-static const uint32_t kDebugFunctionOperandFunctionIndex = 13;
-static const uint32_t kDebugGlobalVariableOperandVariableIndex = 11;
-
-}  // anonymous namespace
-
 namespace spvtools {
 namespace opt {
+namespace {
+constexpr int kSpvDecorateTargetIdInIdx = 0;
+constexpr int kSpvDecorateDecorationInIdx = 1;
+constexpr int kSpvDecorateBuiltinInIdx = 2;
+constexpr int kEntryPointInterfaceInIdx = 3;
+constexpr int kEntryPointFunctionIdInIdx = 1;
+constexpr int kEntryPointExecutionModelInIdx = 0;
+
+// Constants for OpenCL.DebugInfo.100 / NonSemantic.Shader.DebugInfo.100
+// extension instructions.
+constexpr uint32_t kDebugFunctionOperandFunctionIndex = 13;
+constexpr uint32_t kDebugGlobalVariableOperandVariableIndex = 11;
+}  // namespace
 
 void IRContext::BuildInvalidAnalyses(IRContext::Analysis set) {
   set = Analysis(set & ~valid_analyses_);
@@ -152,6 +149,9 @@
   if (analyses_to_invalidate & kAnalysisConstants) {
     constant_mgr_.reset(nullptr);
   }
+  if (analyses_to_invalidate & kAnalysisLiveness) {
+    liveness_mgr_.reset(nullptr);
+  }
   if (analyses_to_invalidate & kAnalysisTypes) {
     type_mgr_.reset(nullptr);
   }
@@ -195,7 +195,8 @@
   if (constant_mgr_ && IsConstantInst(inst->opcode())) {
     constant_mgr_->RemoveId(inst->result_id());
   }
-  if (inst->opcode() == SpvOpCapability || inst->opcode() == SpvOpExtension) {
+  if (inst->opcode() == spv::Op::OpCapability ||
+      inst->opcode() == spv::Op::OpExtension) {
     // 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
@@ -219,6 +220,28 @@
   return next_instruction;
 }
 
+bool IRContext::KillInstructionIf(Module::inst_iterator begin,
+                                  Module::inst_iterator end,
+                                  std::function<bool(Instruction*)> condition) {
+  bool removed = false;
+  for (auto it = begin; it != end;) {
+    if (!condition(&*it)) {
+      ++it;
+      continue;
+    }
+
+    removed = true;
+    // `it` is an iterator on an intrusive list. Next is invalidated on the
+    // current node when an instruction is killed. The iterator must be moved
+    // forward before deleting the node.
+    auto instruction = &*it;
+    ++it;
+    KillInst(instruction);
+  }
+
+  return removed;
+}
+
 void IRContext::CollectNonSemanticTree(
     Instruction* inst, std::unordered_set<Instruction*>* to_kill) {
   if (!inst->HasResultId()) return;
@@ -250,6 +273,36 @@
   return false;
 }
 
+bool IRContext::RemoveCapability(spv::Capability capability) {
+  const bool removed = KillInstructionIf(
+      module()->capability_begin(), module()->capability_end(),
+      [capability](Instruction* inst) {
+        return static_cast<spv::Capability>(inst->GetSingleWordOperand(0)) ==
+               capability;
+      });
+
+  if (removed && feature_mgr_ != nullptr) {
+    feature_mgr_->RemoveCapability(capability);
+  }
+
+  return removed;
+}
+
+bool IRContext::RemoveExtension(Extension extension) {
+  const std::string_view extensionName = ExtensionToString(extension);
+  const bool removed = KillInstructionIf(
+      module()->extension_begin(), module()->extension_end(),
+      [&extensionName](Instruction* inst) {
+        return inst->GetOperand(0).AsString() == extensionName;
+      });
+
+  if (removed && feature_mgr_ != nullptr) {
+    feature_mgr_->RemoveExtension(extension);
+  }
+
+  return removed;
+}
+
 bool IRContext::ReplaceAllUsesWith(uint32_t before, uint32_t after) {
   return ReplaceAllUsesWithPredicate(before, after,
                                      [](Instruction*) { return true; });
@@ -398,8 +451,8 @@
   if (AreAnalysesValid(kAnalysisDebugInfo)) {
     get_debug_info_mgr()->AnalyzeDebugInst(inst);
   }
-  if (id_to_name_ &&
-      (inst->opcode() == SpvOpName || inst->opcode() == SpvOpMemberName)) {
+  if (id_to_name_ && (inst->opcode() == spv::Op::OpName ||
+                      inst->opcode() == spv::Op::OpMemberName)) {
     id_to_name_->insert({inst->GetSingleWordInOperand(0), inst});
   }
 }
@@ -427,7 +480,7 @@
   const auto opcode = inst->opcode();
   const uint32_t id = inst->result_id();
   // Kill id of OpFunction from DebugFunction.
-  if (opcode == SpvOpFunction) {
+  if (opcode == spv::Op::OpFunction) {
     for (auto it = module()->ext_inst_debuginfo_begin();
          it != module()->ext_inst_debuginfo_end(); ++it) {
       if (it->GetOpenCL100DebugOpcode() != OpenCLDebugInfo100DebugFunction)
@@ -441,7 +494,7 @@
     }
   }
   // Kill id of OpVariable for global variable from DebugGlobalVariable.
-  if (opcode == SpvOpVariable || IsConstantInst(opcode)) {
+  if (opcode == spv::Op::OpVariable || IsConstantInst(opcode)) {
     for (auto it = module()->ext_inst_debuginfo_begin();
          it != module()->ext_inst_debuginfo_end(); ++it) {
       if (it->GetCommonDebugOpcode() != CommonDebugInfoDebugGlobalVariable)
@@ -457,255 +510,259 @@
 }
 
 void IRContext::AddCombinatorsForCapability(uint32_t capability) {
-  if (capability == SpvCapabilityShader) {
-    combinator_ops_[0].insert({SpvOpNop,
-                               SpvOpUndef,
-                               SpvOpConstant,
-                               SpvOpConstantTrue,
-                               SpvOpConstantFalse,
-                               SpvOpConstantComposite,
-                               SpvOpConstantSampler,
-                               SpvOpConstantNull,
-                               SpvOpTypeVoid,
-                               SpvOpTypeBool,
-                               SpvOpTypeInt,
-                               SpvOpTypeFloat,
-                               SpvOpTypeVector,
-                               SpvOpTypeMatrix,
-                               SpvOpTypeImage,
-                               SpvOpTypeSampler,
-                               SpvOpTypeSampledImage,
-                               SpvOpTypeAccelerationStructureNV,
-                               SpvOpTypeAccelerationStructureKHR,
-                               SpvOpTypeRayQueryKHR,
-                               SpvOpTypeArray,
-                               SpvOpTypeRuntimeArray,
-                               SpvOpTypeStruct,
-                               SpvOpTypeOpaque,
-                               SpvOpTypePointer,
-                               SpvOpTypeFunction,
-                               SpvOpTypeEvent,
-                               SpvOpTypeDeviceEvent,
-                               SpvOpTypeReserveId,
-                               SpvOpTypeQueue,
-                               SpvOpTypePipe,
-                               SpvOpTypeForwardPointer,
-                               SpvOpVariable,
-                               SpvOpImageTexelPointer,
-                               SpvOpLoad,
-                               SpvOpAccessChain,
-                               SpvOpInBoundsAccessChain,
-                               SpvOpArrayLength,
-                               SpvOpVectorExtractDynamic,
-                               SpvOpVectorInsertDynamic,
-                               SpvOpVectorShuffle,
-                               SpvOpCompositeConstruct,
-                               SpvOpCompositeExtract,
-                               SpvOpCompositeInsert,
-                               SpvOpCopyObject,
-                               SpvOpTranspose,
-                               SpvOpSampledImage,
-                               SpvOpImageSampleImplicitLod,
-                               SpvOpImageSampleExplicitLod,
-                               SpvOpImageSampleDrefImplicitLod,
-                               SpvOpImageSampleDrefExplicitLod,
-                               SpvOpImageSampleProjImplicitLod,
-                               SpvOpImageSampleProjExplicitLod,
-                               SpvOpImageSampleProjDrefImplicitLod,
-                               SpvOpImageSampleProjDrefExplicitLod,
-                               SpvOpImageFetch,
-                               SpvOpImageGather,
-                               SpvOpImageDrefGather,
-                               SpvOpImageRead,
-                               SpvOpImage,
-                               SpvOpImageQueryFormat,
-                               SpvOpImageQueryOrder,
-                               SpvOpImageQuerySizeLod,
-                               SpvOpImageQuerySize,
-                               SpvOpImageQueryLevels,
-                               SpvOpImageQuerySamples,
-                               SpvOpConvertFToU,
-                               SpvOpConvertFToS,
-                               SpvOpConvertSToF,
-                               SpvOpConvertUToF,
-                               SpvOpUConvert,
-                               SpvOpSConvert,
-                               SpvOpFConvert,
-                               SpvOpQuantizeToF16,
-                               SpvOpBitcast,
-                               SpvOpSNegate,
-                               SpvOpFNegate,
-                               SpvOpIAdd,
-                               SpvOpFAdd,
-                               SpvOpISub,
-                               SpvOpFSub,
-                               SpvOpIMul,
-                               SpvOpFMul,
-                               SpvOpUDiv,
-                               SpvOpSDiv,
-                               SpvOpFDiv,
-                               SpvOpUMod,
-                               SpvOpSRem,
-                               SpvOpSMod,
-                               SpvOpFRem,
-                               SpvOpFMod,
-                               SpvOpVectorTimesScalar,
-                               SpvOpMatrixTimesScalar,
-                               SpvOpVectorTimesMatrix,
-                               SpvOpMatrixTimesVector,
-                               SpvOpMatrixTimesMatrix,
-                               SpvOpOuterProduct,
-                               SpvOpDot,
-                               SpvOpIAddCarry,
-                               SpvOpISubBorrow,
-                               SpvOpUMulExtended,
-                               SpvOpSMulExtended,
-                               SpvOpAny,
-                               SpvOpAll,
-                               SpvOpIsNan,
-                               SpvOpIsInf,
-                               SpvOpLogicalEqual,
-                               SpvOpLogicalNotEqual,
-                               SpvOpLogicalOr,
-                               SpvOpLogicalAnd,
-                               SpvOpLogicalNot,
-                               SpvOpSelect,
-                               SpvOpIEqual,
-                               SpvOpINotEqual,
-                               SpvOpUGreaterThan,
-                               SpvOpSGreaterThan,
-                               SpvOpUGreaterThanEqual,
-                               SpvOpSGreaterThanEqual,
-                               SpvOpULessThan,
-                               SpvOpSLessThan,
-                               SpvOpULessThanEqual,
-                               SpvOpSLessThanEqual,
-                               SpvOpFOrdEqual,
-                               SpvOpFUnordEqual,
-                               SpvOpFOrdNotEqual,
-                               SpvOpFUnordNotEqual,
-                               SpvOpFOrdLessThan,
-                               SpvOpFUnordLessThan,
-                               SpvOpFOrdGreaterThan,
-                               SpvOpFUnordGreaterThan,
-                               SpvOpFOrdLessThanEqual,
-                               SpvOpFUnordLessThanEqual,
-                               SpvOpFOrdGreaterThanEqual,
-                               SpvOpFUnordGreaterThanEqual,
-                               SpvOpShiftRightLogical,
-                               SpvOpShiftRightArithmetic,
-                               SpvOpShiftLeftLogical,
-                               SpvOpBitwiseOr,
-                               SpvOpBitwiseXor,
-                               SpvOpBitwiseAnd,
-                               SpvOpNot,
-                               SpvOpBitFieldInsert,
-                               SpvOpBitFieldSExtract,
-                               SpvOpBitFieldUExtract,
-                               SpvOpBitReverse,
-                               SpvOpBitCount,
-                               SpvOpPhi,
-                               SpvOpImageSparseSampleImplicitLod,
-                               SpvOpImageSparseSampleExplicitLod,
-                               SpvOpImageSparseSampleDrefImplicitLod,
-                               SpvOpImageSparseSampleDrefExplicitLod,
-                               SpvOpImageSparseSampleProjImplicitLod,
-                               SpvOpImageSparseSampleProjExplicitLod,
-                               SpvOpImageSparseSampleProjDrefImplicitLod,
-                               SpvOpImageSparseSampleProjDrefExplicitLod,
-                               SpvOpImageSparseFetch,
-                               SpvOpImageSparseGather,
-                               SpvOpImageSparseDrefGather,
-                               SpvOpImageSparseTexelsResident,
-                               SpvOpImageSparseRead,
-                               SpvOpSizeOf});
+  spv::Capability cap = spv::Capability(capability);
+  if (cap == spv::Capability::Shader) {
+    combinator_ops_[0].insert(
+        {(uint32_t)spv::Op::OpNop,
+         (uint32_t)spv::Op::OpUndef,
+         (uint32_t)spv::Op::OpConstant,
+         (uint32_t)spv::Op::OpConstantTrue,
+         (uint32_t)spv::Op::OpConstantFalse,
+         (uint32_t)spv::Op::OpConstantComposite,
+         (uint32_t)spv::Op::OpConstantSampler,
+         (uint32_t)spv::Op::OpConstantNull,
+         (uint32_t)spv::Op::OpTypeVoid,
+         (uint32_t)spv::Op::OpTypeBool,
+         (uint32_t)spv::Op::OpTypeInt,
+         (uint32_t)spv::Op::OpTypeFloat,
+         (uint32_t)spv::Op::OpTypeVector,
+         (uint32_t)spv::Op::OpTypeMatrix,
+         (uint32_t)spv::Op::OpTypeImage,
+         (uint32_t)spv::Op::OpTypeSampler,
+         (uint32_t)spv::Op::OpTypeSampledImage,
+         (uint32_t)spv::Op::OpTypeAccelerationStructureNV,
+         (uint32_t)spv::Op::OpTypeAccelerationStructureKHR,
+         (uint32_t)spv::Op::OpTypeRayQueryKHR,
+         (uint32_t)spv::Op::OpTypeHitObjectNV,
+         (uint32_t)spv::Op::OpTypeArray,
+         (uint32_t)spv::Op::OpTypeRuntimeArray,
+         (uint32_t)spv::Op::OpTypeStruct,
+         (uint32_t)spv::Op::OpTypeOpaque,
+         (uint32_t)spv::Op::OpTypePointer,
+         (uint32_t)spv::Op::OpTypeFunction,
+         (uint32_t)spv::Op::OpTypeEvent,
+         (uint32_t)spv::Op::OpTypeDeviceEvent,
+         (uint32_t)spv::Op::OpTypeReserveId,
+         (uint32_t)spv::Op::OpTypeQueue,
+         (uint32_t)spv::Op::OpTypePipe,
+         (uint32_t)spv::Op::OpTypeForwardPointer,
+         (uint32_t)spv::Op::OpVariable,
+         (uint32_t)spv::Op::OpImageTexelPointer,
+         (uint32_t)spv::Op::OpLoad,
+         (uint32_t)spv::Op::OpAccessChain,
+         (uint32_t)spv::Op::OpInBoundsAccessChain,
+         (uint32_t)spv::Op::OpArrayLength,
+         (uint32_t)spv::Op::OpVectorExtractDynamic,
+         (uint32_t)spv::Op::OpVectorInsertDynamic,
+         (uint32_t)spv::Op::OpVectorShuffle,
+         (uint32_t)spv::Op::OpCompositeConstruct,
+         (uint32_t)spv::Op::OpCompositeExtract,
+         (uint32_t)spv::Op::OpCompositeInsert,
+         (uint32_t)spv::Op::OpCopyObject,
+         (uint32_t)spv::Op::OpTranspose,
+         (uint32_t)spv::Op::OpSampledImage,
+         (uint32_t)spv::Op::OpImageSampleImplicitLod,
+         (uint32_t)spv::Op::OpImageSampleExplicitLod,
+         (uint32_t)spv::Op::OpImageSampleDrefImplicitLod,
+         (uint32_t)spv::Op::OpImageSampleDrefExplicitLod,
+         (uint32_t)spv::Op::OpImageSampleProjImplicitLod,
+         (uint32_t)spv::Op::OpImageSampleProjExplicitLod,
+         (uint32_t)spv::Op::OpImageSampleProjDrefImplicitLod,
+         (uint32_t)spv::Op::OpImageSampleProjDrefExplicitLod,
+         (uint32_t)spv::Op::OpImageFetch,
+         (uint32_t)spv::Op::OpImageGather,
+         (uint32_t)spv::Op::OpImageDrefGather,
+         (uint32_t)spv::Op::OpImageRead,
+         (uint32_t)spv::Op::OpImage,
+         (uint32_t)spv::Op::OpImageQueryFormat,
+         (uint32_t)spv::Op::OpImageQueryOrder,
+         (uint32_t)spv::Op::OpImageQuerySizeLod,
+         (uint32_t)spv::Op::OpImageQuerySize,
+         (uint32_t)spv::Op::OpImageQueryLevels,
+         (uint32_t)spv::Op::OpImageQuerySamples,
+         (uint32_t)spv::Op::OpConvertFToU,
+         (uint32_t)spv::Op::OpConvertFToS,
+         (uint32_t)spv::Op::OpConvertSToF,
+         (uint32_t)spv::Op::OpConvertUToF,
+         (uint32_t)spv::Op::OpUConvert,
+         (uint32_t)spv::Op::OpSConvert,
+         (uint32_t)spv::Op::OpFConvert,
+         (uint32_t)spv::Op::OpQuantizeToF16,
+         (uint32_t)spv::Op::OpBitcast,
+         (uint32_t)spv::Op::OpSNegate,
+         (uint32_t)spv::Op::OpFNegate,
+         (uint32_t)spv::Op::OpIAdd,
+         (uint32_t)spv::Op::OpFAdd,
+         (uint32_t)spv::Op::OpISub,
+         (uint32_t)spv::Op::OpFSub,
+         (uint32_t)spv::Op::OpIMul,
+         (uint32_t)spv::Op::OpFMul,
+         (uint32_t)spv::Op::OpUDiv,
+         (uint32_t)spv::Op::OpSDiv,
+         (uint32_t)spv::Op::OpFDiv,
+         (uint32_t)spv::Op::OpUMod,
+         (uint32_t)spv::Op::OpSRem,
+         (uint32_t)spv::Op::OpSMod,
+         (uint32_t)spv::Op::OpFRem,
+         (uint32_t)spv::Op::OpFMod,
+         (uint32_t)spv::Op::OpVectorTimesScalar,
+         (uint32_t)spv::Op::OpMatrixTimesScalar,
+         (uint32_t)spv::Op::OpVectorTimesMatrix,
+         (uint32_t)spv::Op::OpMatrixTimesVector,
+         (uint32_t)spv::Op::OpMatrixTimesMatrix,
+         (uint32_t)spv::Op::OpOuterProduct,
+         (uint32_t)spv::Op::OpDot,
+         (uint32_t)spv::Op::OpIAddCarry,
+         (uint32_t)spv::Op::OpISubBorrow,
+         (uint32_t)spv::Op::OpUMulExtended,
+         (uint32_t)spv::Op::OpSMulExtended,
+         (uint32_t)spv::Op::OpAny,
+         (uint32_t)spv::Op::OpAll,
+         (uint32_t)spv::Op::OpIsNan,
+         (uint32_t)spv::Op::OpIsInf,
+         (uint32_t)spv::Op::OpLogicalEqual,
+         (uint32_t)spv::Op::OpLogicalNotEqual,
+         (uint32_t)spv::Op::OpLogicalOr,
+         (uint32_t)spv::Op::OpLogicalAnd,
+         (uint32_t)spv::Op::OpLogicalNot,
+         (uint32_t)spv::Op::OpSelect,
+         (uint32_t)spv::Op::OpIEqual,
+         (uint32_t)spv::Op::OpINotEqual,
+         (uint32_t)spv::Op::OpUGreaterThan,
+         (uint32_t)spv::Op::OpSGreaterThan,
+         (uint32_t)spv::Op::OpUGreaterThanEqual,
+         (uint32_t)spv::Op::OpSGreaterThanEqual,
+         (uint32_t)spv::Op::OpULessThan,
+         (uint32_t)spv::Op::OpSLessThan,
+         (uint32_t)spv::Op::OpULessThanEqual,
+         (uint32_t)spv::Op::OpSLessThanEqual,
+         (uint32_t)spv::Op::OpFOrdEqual,
+         (uint32_t)spv::Op::OpFUnordEqual,
+         (uint32_t)spv::Op::OpFOrdNotEqual,
+         (uint32_t)spv::Op::OpFUnordNotEqual,
+         (uint32_t)spv::Op::OpFOrdLessThan,
+         (uint32_t)spv::Op::OpFUnordLessThan,
+         (uint32_t)spv::Op::OpFOrdGreaterThan,
+         (uint32_t)spv::Op::OpFUnordGreaterThan,
+         (uint32_t)spv::Op::OpFOrdLessThanEqual,
+         (uint32_t)spv::Op::OpFUnordLessThanEqual,
+         (uint32_t)spv::Op::OpFOrdGreaterThanEqual,
+         (uint32_t)spv::Op::OpFUnordGreaterThanEqual,
+         (uint32_t)spv::Op::OpShiftRightLogical,
+         (uint32_t)spv::Op::OpShiftRightArithmetic,
+         (uint32_t)spv::Op::OpShiftLeftLogical,
+         (uint32_t)spv::Op::OpBitwiseOr,
+         (uint32_t)spv::Op::OpBitwiseXor,
+         (uint32_t)spv::Op::OpBitwiseAnd,
+         (uint32_t)spv::Op::OpNot,
+         (uint32_t)spv::Op::OpBitFieldInsert,
+         (uint32_t)spv::Op::OpBitFieldSExtract,
+         (uint32_t)spv::Op::OpBitFieldUExtract,
+         (uint32_t)spv::Op::OpBitReverse,
+         (uint32_t)spv::Op::OpBitCount,
+         (uint32_t)spv::Op::OpPhi,
+         (uint32_t)spv::Op::OpImageSparseSampleImplicitLod,
+         (uint32_t)spv::Op::OpImageSparseSampleExplicitLod,
+         (uint32_t)spv::Op::OpImageSparseSampleDrefImplicitLod,
+         (uint32_t)spv::Op::OpImageSparseSampleDrefExplicitLod,
+         (uint32_t)spv::Op::OpImageSparseSampleProjImplicitLod,
+         (uint32_t)spv::Op::OpImageSparseSampleProjExplicitLod,
+         (uint32_t)spv::Op::OpImageSparseSampleProjDrefImplicitLod,
+         (uint32_t)spv::Op::OpImageSparseSampleProjDrefExplicitLod,
+         (uint32_t)spv::Op::OpImageSparseFetch,
+         (uint32_t)spv::Op::OpImageSparseGather,
+         (uint32_t)spv::Op::OpImageSparseDrefGather,
+         (uint32_t)spv::Op::OpImageSparseTexelsResident,
+         (uint32_t)spv::Op::OpImageSparseRead,
+         (uint32_t)spv::Op::OpSizeOf});
   }
 }
 
 void IRContext::AddCombinatorsForExtension(Instruction* extension) {
-  assert(extension->opcode() == SpvOpExtInstImport &&
+  assert(extension->opcode() == spv::Op::OpExtInstImport &&
          "Expecting an import of an extension's instruction set.");
   const std::string extension_name = extension->GetInOperand(0).AsString();
   if (extension_name == "GLSL.std.450") {
-    combinator_ops_[extension->result_id()] = {GLSLstd450Round,
-                                               GLSLstd450RoundEven,
-                                               GLSLstd450Trunc,
-                                               GLSLstd450FAbs,
-                                               GLSLstd450SAbs,
-                                               GLSLstd450FSign,
-                                               GLSLstd450SSign,
-                                               GLSLstd450Floor,
-                                               GLSLstd450Ceil,
-                                               GLSLstd450Fract,
-                                               GLSLstd450Radians,
-                                               GLSLstd450Degrees,
-                                               GLSLstd450Sin,
-                                               GLSLstd450Cos,
-                                               GLSLstd450Tan,
-                                               GLSLstd450Asin,
-                                               GLSLstd450Acos,
-                                               GLSLstd450Atan,
-                                               GLSLstd450Sinh,
-                                               GLSLstd450Cosh,
-                                               GLSLstd450Tanh,
-                                               GLSLstd450Asinh,
-                                               GLSLstd450Acosh,
-                                               GLSLstd450Atanh,
-                                               GLSLstd450Atan2,
-                                               GLSLstd450Pow,
-                                               GLSLstd450Exp,
-                                               GLSLstd450Log,
-                                               GLSLstd450Exp2,
-                                               GLSLstd450Log2,
-                                               GLSLstd450Sqrt,
-                                               GLSLstd450InverseSqrt,
-                                               GLSLstd450Determinant,
-                                               GLSLstd450MatrixInverse,
-                                               GLSLstd450ModfStruct,
-                                               GLSLstd450FMin,
-                                               GLSLstd450UMin,
-                                               GLSLstd450SMin,
-                                               GLSLstd450FMax,
-                                               GLSLstd450UMax,
-                                               GLSLstd450SMax,
-                                               GLSLstd450FClamp,
-                                               GLSLstd450UClamp,
-                                               GLSLstd450SClamp,
-                                               GLSLstd450FMix,
-                                               GLSLstd450IMix,
-                                               GLSLstd450Step,
-                                               GLSLstd450SmoothStep,
-                                               GLSLstd450Fma,
-                                               GLSLstd450FrexpStruct,
-                                               GLSLstd450Ldexp,
-                                               GLSLstd450PackSnorm4x8,
-                                               GLSLstd450PackUnorm4x8,
-                                               GLSLstd450PackSnorm2x16,
-                                               GLSLstd450PackUnorm2x16,
-                                               GLSLstd450PackHalf2x16,
-                                               GLSLstd450PackDouble2x32,
-                                               GLSLstd450UnpackSnorm2x16,
-                                               GLSLstd450UnpackUnorm2x16,
-                                               GLSLstd450UnpackHalf2x16,
-                                               GLSLstd450UnpackSnorm4x8,
-                                               GLSLstd450UnpackUnorm4x8,
-                                               GLSLstd450UnpackDouble2x32,
-                                               GLSLstd450Length,
-                                               GLSLstd450Distance,
-                                               GLSLstd450Cross,
-                                               GLSLstd450Normalize,
-                                               GLSLstd450FaceForward,
-                                               GLSLstd450Reflect,
-                                               GLSLstd450Refract,
-                                               GLSLstd450FindILsb,
-                                               GLSLstd450FindSMsb,
-                                               GLSLstd450FindUMsb,
-                                               GLSLstd450InterpolateAtCentroid,
-                                               GLSLstd450InterpolateAtSample,
-                                               GLSLstd450InterpolateAtOffset,
-                                               GLSLstd450NMin,
-                                               GLSLstd450NMax,
-                                               GLSLstd450NClamp};
+    combinator_ops_[extension->result_id()] = {
+        (uint32_t)GLSLstd450Round,
+        (uint32_t)GLSLstd450RoundEven,
+        (uint32_t)GLSLstd450Trunc,
+        (uint32_t)GLSLstd450FAbs,
+        (uint32_t)GLSLstd450SAbs,
+        (uint32_t)GLSLstd450FSign,
+        (uint32_t)GLSLstd450SSign,
+        (uint32_t)GLSLstd450Floor,
+        (uint32_t)GLSLstd450Ceil,
+        (uint32_t)GLSLstd450Fract,
+        (uint32_t)GLSLstd450Radians,
+        (uint32_t)GLSLstd450Degrees,
+        (uint32_t)GLSLstd450Sin,
+        (uint32_t)GLSLstd450Cos,
+        (uint32_t)GLSLstd450Tan,
+        (uint32_t)GLSLstd450Asin,
+        (uint32_t)GLSLstd450Acos,
+        (uint32_t)GLSLstd450Atan,
+        (uint32_t)GLSLstd450Sinh,
+        (uint32_t)GLSLstd450Cosh,
+        (uint32_t)GLSLstd450Tanh,
+        (uint32_t)GLSLstd450Asinh,
+        (uint32_t)GLSLstd450Acosh,
+        (uint32_t)GLSLstd450Atanh,
+        (uint32_t)GLSLstd450Atan2,
+        (uint32_t)GLSLstd450Pow,
+        (uint32_t)GLSLstd450Exp,
+        (uint32_t)GLSLstd450Log,
+        (uint32_t)GLSLstd450Exp2,
+        (uint32_t)GLSLstd450Log2,
+        (uint32_t)GLSLstd450Sqrt,
+        (uint32_t)GLSLstd450InverseSqrt,
+        (uint32_t)GLSLstd450Determinant,
+        (uint32_t)GLSLstd450MatrixInverse,
+        (uint32_t)GLSLstd450ModfStruct,
+        (uint32_t)GLSLstd450FMin,
+        (uint32_t)GLSLstd450UMin,
+        (uint32_t)GLSLstd450SMin,
+        (uint32_t)GLSLstd450FMax,
+        (uint32_t)GLSLstd450UMax,
+        (uint32_t)GLSLstd450SMax,
+        (uint32_t)GLSLstd450FClamp,
+        (uint32_t)GLSLstd450UClamp,
+        (uint32_t)GLSLstd450SClamp,
+        (uint32_t)GLSLstd450FMix,
+        (uint32_t)GLSLstd450IMix,
+        (uint32_t)GLSLstd450Step,
+        (uint32_t)GLSLstd450SmoothStep,
+        (uint32_t)GLSLstd450Fma,
+        (uint32_t)GLSLstd450FrexpStruct,
+        (uint32_t)GLSLstd450Ldexp,
+        (uint32_t)GLSLstd450PackSnorm4x8,
+        (uint32_t)GLSLstd450PackUnorm4x8,
+        (uint32_t)GLSLstd450PackSnorm2x16,
+        (uint32_t)GLSLstd450PackUnorm2x16,
+        (uint32_t)GLSLstd450PackHalf2x16,
+        (uint32_t)GLSLstd450PackDouble2x32,
+        (uint32_t)GLSLstd450UnpackSnorm2x16,
+        (uint32_t)GLSLstd450UnpackUnorm2x16,
+        (uint32_t)GLSLstd450UnpackHalf2x16,
+        (uint32_t)GLSLstd450UnpackSnorm4x8,
+        (uint32_t)GLSLstd450UnpackUnorm4x8,
+        (uint32_t)GLSLstd450UnpackDouble2x32,
+        (uint32_t)GLSLstd450Length,
+        (uint32_t)GLSLstd450Distance,
+        (uint32_t)GLSLstd450Cross,
+        (uint32_t)GLSLstd450Normalize,
+        (uint32_t)GLSLstd450FaceForward,
+        (uint32_t)GLSLstd450Reflect,
+        (uint32_t)GLSLstd450Refract,
+        (uint32_t)GLSLstd450FindILsb,
+        (uint32_t)GLSLstd450FindSMsb,
+        (uint32_t)GLSLstd450FindUMsb,
+        (uint32_t)GLSLstd450InterpolateAtCentroid,
+        (uint32_t)GLSLstd450InterpolateAtSample,
+        (uint32_t)GLSLstd450InterpolateAtOffset,
+        (uint32_t)GLSLstd450NMin,
+        (uint32_t)GLSLstd450NMax,
+        (uint32_t)GLSLstd450NClamp};
   } else {
     // Map the result id to the empty set.
     combinator_ops_[extension->result_id()];
@@ -713,8 +770,9 @@
 }
 
 void IRContext::InitializeCombinators() {
-  get_feature_mgr()->GetCapabilities()->ForEach(
-      [this](SpvCapability cap) { AddCombinatorsForCapability(cap); });
+  for (auto capability : get_feature_mgr()->GetCapabilities()) {
+    AddCombinatorsForCapability(uint32_t(capability));
+  }
 
   for (auto& extension : module()->ext_inst_imports()) {
     AddCombinatorsForExtension(&extension);
@@ -724,8 +782,8 @@
 }
 
 void IRContext::RemoveFromIdToName(const Instruction* inst) {
-  if (id_to_name_ &&
-      (inst->opcode() == SpvOpName || inst->opcode() == SpvOpMemberName)) {
+  if (id_to_name_ && (inst->opcode() == spv::Op::OpName ||
+                      inst->opcode() == spv::Op::OpMemberName)) {
     auto range = id_to_name_->equal_range(inst->GetSingleWordInOperand(0));
     for (auto it = range.first; it != range.second; ++it) {
       if (it->second == inst) {
@@ -754,15 +812,17 @@
 
 uint32_t IRContext::FindBuiltinInputVar(uint32_t builtin) {
   for (auto& a : module_->annotations()) {
-    if (a.opcode() != SpvOpDecorate) continue;
-    if (a.GetSingleWordInOperand(kSpvDecorateDecorationInIdx) !=
-        SpvDecorationBuiltIn)
+    if (spv::Op(a.opcode()) != spv::Op::OpDecorate) continue;
+    if (spv::Decoration(a.GetSingleWordInOperand(
+            kSpvDecorateDecorationInIdx)) != spv::Decoration::BuiltIn)
       continue;
     if (a.GetSingleWordInOperand(kSpvDecorateBuiltinInIdx) != builtin) continue;
     uint32_t target_id = a.GetSingleWordInOperand(kSpvDecorateTargetIdInIdx);
     Instruction* b_var = get_def_use_mgr()->GetDef(target_id);
-    if (b_var->opcode() != SpvOpVariable) continue;
-    if (b_var->GetSingleWordInOperand(0) != SpvStorageClassInput) continue;
+    if (b_var->opcode() != spv::Op::OpVariable) continue;
+    if (spv::StorageClass(b_var->GetSingleWordInOperand(0)) !=
+        spv::StorageClass::Input)
+      continue;
     return target_id;
   }
   return 0;
@@ -798,39 +858,39 @@
     // TODO(greg-lunarg): Add support for all builtins
     analysis::TypeManager* type_mgr = get_type_mgr();
     analysis::Type* reg_type;
-    switch (builtin) {
-      case SpvBuiltInFragCoord: {
+    switch (spv::BuiltIn(builtin)) {
+      case spv::BuiltIn::FragCoord: {
         analysis::Float float_ty(32);
         analysis::Type* reg_float_ty = type_mgr->GetRegisteredType(&float_ty);
         analysis::Vector v4float_ty(reg_float_ty, 4);
         reg_type = type_mgr->GetRegisteredType(&v4float_ty);
         break;
       }
-      case SpvBuiltInVertexIndex:
-      case SpvBuiltInInstanceIndex:
-      case SpvBuiltInPrimitiveId:
-      case SpvBuiltInInvocationId:
-      case SpvBuiltInSubgroupLocalInvocationId: {
+      case spv::BuiltIn::VertexIndex:
+      case spv::BuiltIn::InstanceIndex:
+      case spv::BuiltIn::PrimitiveId:
+      case spv::BuiltIn::InvocationId:
+      case spv::BuiltIn::SubgroupLocalInvocationId: {
         analysis::Integer uint_ty(32, false);
         reg_type = type_mgr->GetRegisteredType(&uint_ty);
         break;
       }
-      case SpvBuiltInGlobalInvocationId:
-      case SpvBuiltInLaunchIdNV: {
+      case spv::BuiltIn::GlobalInvocationId:
+      case spv::BuiltIn::LaunchIdNV: {
         analysis::Integer uint_ty(32, false);
         analysis::Type* reg_uint_ty = type_mgr->GetRegisteredType(&uint_ty);
         analysis::Vector v3uint_ty(reg_uint_ty, 3);
         reg_type = type_mgr->GetRegisteredType(&v3uint_ty);
         break;
       }
-      case SpvBuiltInTessCoord: {
+      case spv::BuiltIn::TessCoord: {
         analysis::Float float_ty(32);
         analysis::Type* reg_float_ty = type_mgr->GetRegisteredType(&float_ty);
         analysis::Vector v3float_ty(reg_float_ty, 3);
         reg_type = type_mgr->GetRegisteredType(&v3float_ty);
         break;
       }
-      case SpvBuiltInSubgroupLtMask: {
+      case spv::BuiltIn::SubgroupLtMask: {
         analysis::Integer uint_ty(32, false);
         analysis::Type* reg_uint_ty = type_mgr->GetRegisteredType(&uint_ty);
         analysis::Vector v4uint_ty(reg_uint_ty, 4);
@@ -844,17 +904,17 @@
     }
     uint32_t type_id = type_mgr->GetTypeInstruction(reg_type);
     uint32_t varTyPtrId =
-        type_mgr->FindPointerToType(type_id, SpvStorageClassInput);
+        type_mgr->FindPointerToType(type_id, spv::StorageClass::Input);
     // TODO(1841): Handle id overflow.
     var_id = TakeNextId();
     std::unique_ptr<Instruction> newVarOp(
-        new Instruction(this, SpvOpVariable, varTyPtrId, var_id,
+        new Instruction(this, spv::Op::OpVariable, varTyPtrId, var_id,
                         {{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER,
-                          {SpvStorageClassInput}}}));
+                          {uint32_t(spv::StorageClass::Input)}}}));
     get_def_use_mgr()->AnalyzeInstDefUse(&*newVarOp);
     module()->AddGlobalValue(std::move(newVarOp));
-    get_decoration_mgr()->AddDecorationVal(var_id, SpvDecorationBuiltIn,
-                                           builtin);
+    get_decoration_mgr()->AddDecorationVal(
+        var_id, uint32_t(spv::Decoration::BuiltIn), builtin);
     AddVarToEntryPoints(var_id);
   }
   builtin_var_id_map_[builtin] = var_id;
@@ -864,7 +924,7 @@
 void IRContext::AddCalls(const Function* func, std::queue<uint32_t>* todo) {
   for (auto bi = func->begin(); bi != func->end(); ++bi)
     for (auto ii = bi->begin(); ii != bi->end(); ++ii)
-      if (ii->opcode() == SpvOpFunctionCall)
+      if (ii->opcode() == spv::Op::OpFunctionCall)
         todo->push(ii->GetSingleWordInOperand(0));
 }
 
@@ -889,12 +949,12 @@
   for (auto& a : annotations()) {
     // TODO: Handle group decorations as well.  Currently not generate by any
     // front-end, but could be coming.
-    if (a.opcode() == SpvOp::SpvOpDecorate) {
-      if (a.GetSingleWordOperand(1) ==
-          SpvDecoration::SpvDecorationLinkageAttributes) {
+    if (a.opcode() == spv::Op::OpDecorate) {
+      if (spv::Decoration(a.GetSingleWordOperand(1)) ==
+          spv::Decoration::LinkageAttributes) {
         uint32_t lastOperand = a.NumOperands() - 1;
-        if (a.GetSingleWordOperand(lastOperand) ==
-            SpvLinkageType::SpvLinkageTypeExport) {
+        if (spv::LinkageType(a.GetSingleWordOperand(lastOperand)) ==
+            spv::LinkageType::Export) {
           uint32_t id = a.GetSingleWordOperand(0);
           if (GetFunction(id)) {
             roots.push(id);
@@ -1058,5 +1118,26 @@
   return GetDominatorAnalysis(enclosing_function)
       ->Dominates(enclosing_function->entry().get(), &bb);
 }
+
+spv::ExecutionModel IRContext::GetStage() {
+  const auto& entry_points = module()->entry_points();
+  if (entry_points.empty()) {
+    return spv::ExecutionModel::Max;
+  }
+
+  uint32_t stage = entry_points.begin()->GetSingleWordInOperand(
+      kEntryPointExecutionModelInIdx);
+  auto it = std::find_if(
+      entry_points.begin(), entry_points.end(), [stage](const Instruction& x) {
+        return x.GetSingleWordInOperand(kEntryPointExecutionModelInIdx) !=
+               stage;
+      });
+  if (it != entry_points.end()) {
+    EmitErrorMessage("Mixed stage shader module not supported", &(*it));
+  }
+
+  return static_cast<spv::ExecutionModel>(stage);
+}
+
 }  // namespace opt
 }  // namespace spvtools
diff --git a/source/opt/ir_context.h b/source/opt/ir_context.h
index 2f27942..de3c410 100644
--- a/source/opt/ir_context.h
+++ b/source/opt/ir_context.h
@@ -27,6 +27,7 @@
 #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"
@@ -35,6 +36,7 @@
 #include "source/opt/dominator_analysis.h"
 #include "source/opt/feature_manager.h"
 #include "source/opt/fold.h"
+#include "source/opt/liveness.h"
 #include "source/opt/loop_descriptor.h"
 #include "source/opt/module.h"
 #include "source/opt/register_pressure.h"
@@ -81,6 +83,7 @@
     kAnalysisConstants = 1 << 14,
     kAnalysisTypes = 1 << 15,
     kAnalysisDebugInfo = 1 << 16,
+    kAnalysisLiveness = 1 << 17,
     kAnalysisEnd = 1 << 17
   };
 
@@ -151,13 +154,19 @@
   inline IteratorRange<Module::inst_iterator> capabilities();
   inline IteratorRange<Module::const_inst_iterator> capabilities() const;
 
+  // Iterators for extensions instructions contained in this module.
+  inline Module::inst_iterator extension_begin();
+  inline Module::inst_iterator extension_end();
+  inline IteratorRange<Module::inst_iterator> extensions();
+  inline IteratorRange<Module::const_inst_iterator> extensions() const;
+
   // Iterators for types, constants and global variables instructions.
   inline Module::inst_iterator types_values_begin();
   inline Module::inst_iterator types_values_end();
   inline IteratorRange<Module::inst_iterator> types_values();
   inline IteratorRange<Module::const_inst_iterator> types_values() const;
 
-  // Iterators for extension instructions contained in this module.
+  // Iterators for ext_inst import instructions contained in this module.
   inline Module::inst_iterator ext_inst_import_begin();
   inline Module::inst_iterator ext_inst_import_end();
   inline IteratorRange<Module::inst_iterator> ext_inst_imports();
@@ -201,13 +210,20 @@
   inline IteratorRange<Module::const_inst_iterator> ext_inst_debuginfo() const;
 
   // Add |capability| to the module, if it is not already enabled.
-  inline void AddCapability(SpvCapability capability);
-
+  inline void AddCapability(spv::Capability capability);
   // Appends a capability instruction to this module.
   inline void AddCapability(std::unique_ptr<Instruction>&& c);
+  // Removes instruction declaring `capability` from this module.
+  // Returns true if the capability was removed, false otherwise.
+  bool RemoveCapability(spv::Capability capability);
+
   // Appends an extension instruction to this module.
   inline void AddExtension(const std::string& ext_name);
   inline void AddExtension(std::unique_ptr<Instruction>&& e);
+  // Removes instruction declaring `extension` from this module.
+  // Returns true if the extension was removed, false otherwise.
+  bool RemoveExtension(Extension extension);
+
   // Appends an extended instruction set instruction to this module.
   inline void AddExtInstImport(const std::string& name);
   inline void AddExtInstImport(std::unique_ptr<Instruction>&& e);
@@ -236,6 +252,8 @@
   inline void AddType(std::unique_ptr<Instruction>&& t);
   // Appends a constant, global variable, or OpUndef instruction to this module.
   inline void AddGlobalValue(std::unique_ptr<Instruction>&& v);
+  // Prepends a function declaration to this module.
+  inline void AddFunctionDeclaration(std::unique_ptr<Function>&& f);
   // Appends a function to this module.
   inline void AddFunction(std::unique_ptr<Function>&& f);
 
@@ -248,6 +266,15 @@
     return def_use_mgr_.get();
   }
 
+  // Returns a pointer to a liveness manager.  If the liveness manager is
+  // invalid, it is rebuilt first.
+  analysis::LivenessManager* get_liveness_mgr() {
+    if (!AreAnalysesValid(kAnalysisLiveness)) {
+      BuildLivenessManager();
+    }
+    return liveness_mgr_.get();
+  }
+
   // Returns a pointer to a value number table.  If the liveness analysis is
   // invalid, it is rebuilt first.
   ValueNumberTable* GetValueNumberTable() {
@@ -367,6 +394,11 @@
   // having more than one name. This method returns the first one it finds.
   inline Instruction* GetMemberName(uint32_t struct_type_id, uint32_t index);
 
+  // Copy names from |old_id| to |new_id|. Only copy member name if index is
+  // less than |max_member_index|.
+  inline void CloneNames(const uint32_t old_id, const uint32_t new_id,
+                         const uint32_t max_member_index = UINT32_MAX);
+
   // Sets the message consumer to the given |consumer|. |consumer| which will be
   // invoked every time there is a message to be communicated to the outside.
   void SetMessageConsumer(MessageConsumer c) { consumer_ = std::move(c); }
@@ -406,6 +438,15 @@
   // instruction exists.
   Instruction* KillInst(Instruction* inst);
 
+  // Deletes all the instruction in the range [`begin`; `end`[, for which the
+  // unary predicate `condition` returned true.
+  // Returns true if at least one instruction was removed, false otherwise.
+  //
+  // Pointer and iterator pointing to the deleted instructions become invalid.
+  // However other pointers and iterators are still valid.
+  bool KillInstructionIf(Module::inst_iterator begin, Module::inst_iterator end,
+                         std::function<bool(Instruction*)> condition);
+
   // Collects the non-semantic instruction tree that uses |inst|'s result id
   // to be killed later.
   void CollectNonSemanticTree(Instruction* inst,
@@ -475,14 +516,14 @@
     if (!AreAnalysesValid(kAnalysisCombinators)) {
       InitializeCombinators();
     }
-    const uint32_t kExtInstSetIdInIndx = 0;
-    const uint32_t kExtInstInstructionInIndx = 1;
+    constexpr uint32_t kExtInstSetIdInIndx = 0;
+    constexpr uint32_t kExtInstInstructionInIndx = 1;
 
-    if (inst->opcode() != SpvOpExtInst) {
-      return combinator_ops_[0].count(inst->opcode()) != 0;
+    if (inst->opcode() != spv::Op::OpExtInst) {
+      return combinator_ops_[0].count(uint32_t(inst->opcode())) != 0;
     } else {
       uint32_t set = inst->GetSingleWordInOperand(kExtInstSetIdInIndx);
-      uint32_t op = inst->GetSingleWordInOperand(kExtInstInstructionInIndx);
+      auto op = inst->GetSingleWordInOperand(kExtInstInstructionInIndx);
       return combinator_ops_[set].count(op) != 0;
     }
   }
@@ -591,7 +632,7 @@
   }
 
   Function* GetFunction(Instruction* inst) {
-    if (inst->opcode() != SpvOpFunction) {
+    if (inst->opcode() != spv::Op::OpFunction) {
       return nullptr;
     }
     return GetFunction(inst->result_id());
@@ -625,6 +666,21 @@
   // the function that contains |bb|.
   bool IsReachable(const opt::BasicBlock& bb);
 
+  // Return the stage of the module. Will generate error if entry points don't
+  // all have the same stage.
+  spv::ExecutionModel GetStage();
+
+  // Returns true of the current target environment is at least that of the
+  // given environment.
+  bool IsTargetEnvAtLeast(spv_target_env env) {
+    // A bit of a hack. We assume that the target environments are appended to
+    // the enum, so that there is an appropriate order.
+    return syntax_context_->target_env >= env;
+  }
+
+  // Return the target environment for the current context.
+  spv_target_env GetTargetEnv() const { return syntax_context_->target_env; }
+
  private:
   // Builds the def-use manager from scratch, even if it was already valid.
   void BuildDefUseManager() {
@@ -632,6 +688,12 @@
     valid_analyses_ = valid_analyses_ | kAnalysisDefUse;
   }
 
+  // Builds the liveness manager from scratch, even if it was already valid.
+  void BuildLivenessManager() {
+    liveness_mgr_ = MakeUnique<analysis::LivenessManager>(this);
+    valid_analyses_ = valid_analyses_ | kAnalysisLiveness;
+  }
+
   // Builds the instruction-block map for the whole module.
   void BuildInstrToBlockMapping() {
     instr_to_block_.clear();
@@ -735,7 +797,8 @@
 
   // Analyzes the features in the owned module. Builds the manager if required.
   void AnalyzeFeatures() {
-    feature_mgr_ = MakeUnique<FeatureManager>(grammar_);
+    feature_mgr_ =
+        std::unique_ptr<FeatureManager>(new FeatureManager(grammar_));
     feature_mgr_->Analyze(module());
   }
 
@@ -852,6 +915,9 @@
 
   std::unique_ptr<StructuredCFGAnalysis> struct_cfg_analysis_;
 
+  // The liveness manager for |module_|.
+  std::unique_ptr<analysis::LivenessManager> liveness_mgr_;
+
   // The maximum legal value for the id bound.
   uint32_t max_id_bound_;
 
@@ -924,6 +990,22 @@
   return ((const Module*)module())->capabilities();
 }
 
+Module::inst_iterator IRContext::extension_begin() {
+  return module()->extension_begin();
+}
+
+Module::inst_iterator IRContext::extension_end() {
+  return module()->extension_end();
+}
+
+IteratorRange<Module::inst_iterator> IRContext::extensions() {
+  return module()->extensions();
+}
+
+IteratorRange<Module::const_inst_iterator> IRContext::extensions() const {
+  return ((const Module*)module())->extensions();
+}
+
 Module::inst_iterator IRContext::types_values_begin() {
   return module()->types_values_begin();
 }
@@ -1014,10 +1096,10 @@
   return ((const Module*)module_.get())->ext_inst_debuginfo();
 }
 
-void IRContext::AddCapability(SpvCapability capability) {
+void IRContext::AddCapability(spv::Capability capability) {
   if (!get_feature_mgr()->HasCapability(capability)) {
     std::unique_ptr<Instruction> capability_inst(new Instruction(
-        this, SpvOpCapability, 0, 0,
+        this, spv::Op::OpCapability, 0, 0,
         {{SPV_OPERAND_TYPE_CAPABILITY, {static_cast<uint32_t>(capability)}}}));
     AddCapability(std::move(capability_inst));
   }
@@ -1027,7 +1109,7 @@
   AddCombinatorsForCapability(c->GetSingleWordInOperand(0));
   if (feature_mgr_ != nullptr) {
     feature_mgr_->AddCapability(
-        static_cast<SpvCapability>(c->GetSingleWordInOperand(0)));
+        static_cast<spv::Capability>(c->GetSingleWordInOperand(0)));
   }
   if (AreAnalysesValid(kAnalysisDefUse)) {
     get_def_use_mgr()->AnalyzeInstDefUse(c.get());
@@ -1038,7 +1120,7 @@
 void IRContext::AddExtension(const std::string& ext_name) {
   std::vector<uint32_t> ext_words = spvtools::utils::MakeVector(ext_name);
   AddExtension(std::unique_ptr<Instruction>(
-      new Instruction(this, SpvOpExtension, 0u, 0u,
+      new Instruction(this, spv::Op::OpExtension, 0u, 0u,
                       {{SPV_OPERAND_TYPE_LITERAL_STRING, ext_words}})));
 }
 
@@ -1055,7 +1137,7 @@
 void IRContext::AddExtInstImport(const std::string& name) {
   std::vector<uint32_t> ext_words = spvtools::utils::MakeVector(name);
   AddExtInstImport(std::unique_ptr<Instruction>(
-      new Instruction(this, SpvOpExtInstImport, 0u, TakeNextId(),
+      new Instruction(this, spv::Op::OpExtInstImport, 0u, TakeNextId(),
                       {{SPV_OPERAND_TYPE_LITERAL_STRING, ext_words}})));
 }
 
@@ -1088,7 +1170,8 @@
 
 void IRContext::AddDebug2Inst(std::unique_ptr<Instruction>&& d) {
   if (AreAnalysesValid(kAnalysisNameMap)) {
-    if (d->opcode() == SpvOpName || d->opcode() == SpvOpMemberName) {
+    if (d->opcode() == spv::Op::OpName ||
+        d->opcode() == spv::Op::OpMemberName) {
       // OpName and OpMemberName do not have result-ids. The target of the
       // instruction is at InOperand index 0.
       id_to_name_->insert({d->GetSingleWordInOperand(0), d.get()});
@@ -1132,6 +1215,10 @@
   module()->AddGlobalValue(std::move(v));
 }
 
+void IRContext::AddFunctionDeclaration(std::unique_ptr<Function>&& f) {
+  module()->AddFunctionDeclaration(std::move(f));
+}
+
 void IRContext::AddFunction(std::unique_ptr<Function>&& f) {
   module()->AddFunction(std::move(f));
 }
@@ -1151,8 +1238,8 @@
 void IRContext::BuildIdToNameMap() {
   id_to_name_ = MakeUnique<std::multimap<uint32_t, Instruction*>>();
   for (Instruction& debug_inst : debugs2()) {
-    if (debug_inst.opcode() == SpvOpMemberName ||
-        debug_inst.opcode() == SpvOpName) {
+    if (debug_inst.opcode() == spv::Op::OpMemberName ||
+        debug_inst.opcode() == spv::Op::OpName) {
       id_to_name_->insert({debug_inst.GetSingleWordInOperand(0), &debug_inst});
     }
   }
@@ -1175,7 +1262,7 @@
   auto result = id_to_name_->equal_range(struct_type_id);
   for (auto i = result.first; i != result.second; ++i) {
     auto* name_instr = i->second;
-    if (name_instr->opcode() == SpvOpMemberName &&
+    if (name_instr->opcode() == spv::Op::OpMemberName &&
         name_instr->GetSingleWordInOperand(1) == index) {
       return name_instr;
     }
@@ -1183,6 +1270,25 @@
   return nullptr;
 }
 
+void IRContext::CloneNames(const uint32_t old_id, const uint32_t new_id,
+                           const uint32_t max_member_index) {
+  std::vector<std::unique_ptr<Instruction>> names_to_add;
+  auto names = GetNames(old_id);
+  for (auto n : names) {
+    Instruction* old_name_inst = n.second;
+    if (old_name_inst->opcode() == spv::Op::OpMemberName) {
+      auto midx = old_name_inst->GetSingleWordInOperand(1);
+      if (midx >= max_member_index) continue;
+    }
+    std::unique_ptr<Instruction> new_name_inst(old_name_inst->Clone(this));
+    new_name_inst->SetInOperand(0, {new_id});
+    names_to_add.push_back(std::move(new_name_inst));
+  }
+  // We can't add the new names when we are iterating over name range above.
+  // We can add all the new names now.
+  for (auto& new_name : names_to_add) AddDebug2Inst(std::move(new_name));
+}
+
 }  // namespace opt
 }  // namespace spvtools
 
diff --git a/source/opt/ir_loader.cpp b/source/opt/ir_loader.cpp
index 734ad55..e9b7bbf 100644
--- a/source/opt/ir_loader.cpp
+++ b/source/opt/ir_loader.cpp
@@ -24,12 +24,13 @@
 #include "source/opt/reflect.h"
 #include "source/util/make_unique.h"
 
-static const uint32_t kExtInstSetIndex = 4;
-static const uint32_t kLexicalScopeIndex = 5;
-static const uint32_t kInlinedAtIndex = 6;
-
 namespace spvtools {
 namespace opt {
+namespace {
+constexpr uint32_t kExtInstSetIndex = 4;
+constexpr uint32_t kLexicalScopeIndex = 5;
+constexpr uint32_t kInlinedAtIndex = 6;
+}  // namespace
 
 IrLoader::IrLoader(const MessageConsumer& consumer, Module* m)
     : consumer_(consumer),
@@ -39,9 +40,9 @@
       last_dbg_scope_(kNoDebugScope, kNoInlinedAt) {}
 
 bool IsLineInst(const spv_parsed_instruction_t* inst) {
-  const auto opcode = static_cast<SpvOp>(inst->opcode);
+  const auto opcode = static_cast<spv::Op>(inst->opcode);
   if (IsOpLineInst(opcode)) return true;
-  if (opcode != SpvOpExtInst) return false;
+  if (opcode != spv::Op::OpExtInst) return false;
   if (inst->ext_inst_type != SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100)
     return false;
   const uint32_t ext_inst_index = inst->words[kExtInstSetIndex];
@@ -63,8 +64,9 @@
   // If it is a DebugScope or DebugNoScope of debug extension, we do not
   // create a new instruction, but simply keep the information in
   // struct DebugScope.
-  const auto opcode = static_cast<SpvOp>(inst->opcode);
-  if (opcode == SpvOpExtInst && spvExtInstIsDebugInfo(inst->ext_inst_type)) {
+  const auto opcode = static_cast<spv::Op>(inst->opcode);
+  if (opcode == spv::Op::OpExtInst &&
+      spvExtInstIsDebugInfo(inst->ext_inst_type)) {
     const uint32_t ext_inst_index = inst->words[kExtInstSetIndex];
     if (inst->ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 ||
         inst->ext_inst_type ==
@@ -130,13 +132,13 @@
 
   // Handle function and basic block boundaries first, then normal
   // instructions.
-  if (opcode == SpvOpFunction) {
+  if (opcode == spv::Op::OpFunction) {
     if (function_ != nullptr) {
       Error(consumer_, src, loc, "function inside function");
       return false;
     }
     function_ = MakeUnique<Function>(std::move(spv_inst));
-  } else if (opcode == SpvOpFunctionEnd) {
+  } else if (opcode == spv::Op::OpFunctionEnd) {
     if (function_ == nullptr) {
       Error(consumer_, src, loc,
             "OpFunctionEnd without corresponding OpFunction");
@@ -149,7 +151,7 @@
     function_->SetFunctionEnd(std::move(spv_inst));
     module_->AddFunction(std::move(function_));
     function_ = nullptr;
-  } else if (opcode == SpvOpLabel) {
+  } else if (opcode == spv::Op::OpLabel) {
     if (function_ == nullptr) {
       Error(consumer_, src, loc, "OpLabel outside function");
       return false;
@@ -179,20 +181,20 @@
   } else {
     if (function_ == nullptr) {  // Outside function definition
       SPIRV_ASSERT(consumer_, block_ == nullptr);
-      if (opcode == SpvOpCapability) {
+      if (opcode == spv::Op::OpCapability) {
         module_->AddCapability(std::move(spv_inst));
-      } else if (opcode == SpvOpExtension) {
+      } else if (opcode == spv::Op::OpExtension) {
         module_->AddExtension(std::move(spv_inst));
-      } else if (opcode == SpvOpExtInstImport) {
+      } else if (opcode == spv::Op::OpExtInstImport) {
         module_->AddExtInstImport(std::move(spv_inst));
-      } else if (opcode == SpvOpMemoryModel) {
+      } else if (opcode == spv::Op::OpMemoryModel) {
         module_->SetMemoryModel(std::move(spv_inst));
-      } else if (opcode == SpvOpSamplerImageAddressingModeNV) {
+      } else if (opcode == spv::Op::OpSamplerImageAddressingModeNV) {
         module_->SetSampledImageAddressMode(std::move(spv_inst));
-      } else if (opcode == SpvOpEntryPoint) {
+      } else if (opcode == spv::Op::OpEntryPoint) {
         module_->AddEntryPoint(std::move(spv_inst));
-      } else if (opcode == SpvOpExecutionMode ||
-                 opcode == SpvOpExecutionModeId) {
+      } else if (opcode == spv::Op::OpExecutionMode ||
+                 opcode == spv::Op::OpExecutionModeId) {
         module_->AddExecutionMode(std::move(spv_inst));
       } else if (IsDebug1Inst(opcode)) {
         module_->AddDebug1Inst(std::move(spv_inst));
@@ -204,13 +206,13 @@
         module_->AddAnnotationInst(std::move(spv_inst));
       } else if (IsTypeInst(opcode)) {
         module_->AddType(std::move(spv_inst));
-      } else if (IsConstantInst(opcode) || opcode == SpvOpVariable ||
-                 opcode == SpvOpUndef) {
+      } else if (IsConstantInst(opcode) || opcode == spv::Op::OpVariable ||
+                 opcode == spv::Op::OpUndef) {
         module_->AddGlobalValue(std::move(spv_inst));
-      } else if (opcode == SpvOpExtInst &&
+      } else if (opcode == spv::Op::OpExtInst &&
                  spvExtInstIsDebugInfo(inst->ext_inst_type)) {
         module_->AddExtInstDebugInfo(std::move(spv_inst));
-      } else if (opcode == SpvOpExtInst &&
+      } else if (opcode == spv::Op::OpExtInst &&
                  spvExtInstIsNonSemantic(inst->ext_inst_type)) {
         // If there are no functions, add the non-semantic instructions to the
         // global values. Otherwise append it to the list of the last function.
@@ -229,11 +231,11 @@
         return false;
       }
     } else {
-      if (opcode == SpvOpLoopMerge || opcode == SpvOpSelectionMerge)
+      if (opcode == spv::Op::OpLoopMerge || opcode == spv::Op::OpSelectionMerge)
         last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt);
       if (last_dbg_scope_.GetLexicalScope() != kNoDebugScope)
         spv_inst->SetDebugScope(last_dbg_scope_);
-      if (opcode == SpvOpExtInst &&
+      if (opcode == spv::Op::OpExtInst &&
           spvExtInstIsDebugInfo(inst->ext_inst_type)) {
         const uint32_t ext_inst_index = inst->words[kExtInstSetIndex];
         if (inst->ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) {
@@ -322,7 +324,7 @@
         }
       } else {
         if (block_ == nullptr) {  // Inside function but outside blocks
-          if (opcode != SpvOpFunctionParameter) {
+          if (opcode != spv::Op::OpFunctionParameter) {
             Errorf(consumer_, src, loc,
                    "Non-OpFunctionParameter (opcode: %d) found inside "
                    "function but outside basic block",
diff --git a/source/opt/licm_pass.cpp b/source/opt/licm_pass.cpp
index 82851fd..f2a6e4d 100644
--- a/source/opt/licm_pass.cpp
+++ b/source/opt/licm_pass.cpp
@@ -15,7 +15,6 @@
 #include "source/opt/licm_pass.h"
 
 #include <queue>
-#include <utility>
 
 #include "source/opt/module.h"
 #include "source/opt/pass.h"
@@ -85,7 +84,7 @@
   bool modified = false;
   std::function<bool(Instruction*)> hoist_inst =
       [this, &loop, &modified](Instruction* inst) {
-        if (loop->ShouldHoistInstruction(this->context(), inst)) {
+        if (loop->ShouldHoistInstruction(*inst)) {
           if (!HoistInstruction(loop, inst)) {
             return false;
           }
@@ -126,8 +125,8 @@
   }
   Instruction* insertion_point = &*pre_header_bb->tail();
   Instruction* previous_node = insertion_point->PreviousNode();
-  if (previous_node && (previous_node->opcode() == SpvOpLoopMerge ||
-                        previous_node->opcode() == SpvOpSelectionMerge)) {
+  if (previous_node && (previous_node->opcode() == spv::Op::OpLoopMerge ||
+                        previous_node->opcode() == spv::Op::OpSelectionMerge)) {
     insertion_point = previous_node;
   }
 
diff --git a/source/opt/liveness.cpp b/source/opt/liveness.cpp
new file mode 100644
index 0000000..336f3ae
--- /dev/null
+++ b/source/opt/liveness.cpp
@@ -0,0 +1,332 @@
+// Copyright (c) 2022 The Khronos Group Inc.
+// Copyright (c) 2022 LunarG Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/opt/liveness.h"
+
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace opt {
+namespace analysis {
+namespace {
+constexpr uint32_t kDecorationLocationInIdx = 2;
+constexpr uint32_t kOpDecorateMemberMemberInIdx = 1;
+constexpr uint32_t kOpDecorateMemberLocationInIdx = 3;
+constexpr uint32_t kOpDecorateBuiltInLiteralInIdx = 2;
+constexpr uint32_t kOpDecorateMemberBuiltInLiteralInIdx = 3;
+}  // namespace
+
+LivenessManager::LivenessManager(IRContext* ctx) : ctx_(ctx), computed_(false) {
+  // Liveness sets computed when queried
+}
+
+void LivenessManager::InitializeAnalysis() {
+  live_locs_.clear();
+  live_builtins_.clear();
+  // Mark all builtins live for frag shader.
+  if (context()->GetStage() == spv::ExecutionModel::Fragment) {
+    live_builtins_.insert(uint32_t(spv::BuiltIn::PointSize));
+    live_builtins_.insert(uint32_t(spv::BuiltIn::ClipDistance));
+    live_builtins_.insert(uint32_t(spv::BuiltIn::CullDistance));
+  }
+}
+
+bool LivenessManager::IsAnalyzedBuiltin(uint32_t bi) {
+  // There are only three builtins that can be analyzed and removed between
+  // two stages: PointSize, ClipDistance and CullDistance. All others are
+  // always consumed implicitly by the downstream stage.
+  const auto builtin = spv::BuiltIn(bi);
+  return builtin == spv::BuiltIn::PointSize ||
+         builtin == spv::BuiltIn::ClipDistance ||
+         builtin == spv::BuiltIn::CullDistance;
+}
+
+bool LivenessManager::AnalyzeBuiltIn(uint32_t id) {
+  auto deco_mgr = context()->get_decoration_mgr();
+  bool saw_builtin = false;
+  // Analyze all builtin decorations of |id|.
+  (void)deco_mgr->ForEachDecoration(
+      id, uint32_t(spv::Decoration::BuiltIn),
+      [this, &saw_builtin](const Instruction& deco_inst) {
+        saw_builtin = true;
+        // No need to process builtins in frag shader. All assumed used.
+        if (context()->GetStage() == spv::ExecutionModel::Fragment) return;
+        uint32_t builtin = uint32_t(spv::BuiltIn::Max);
+        if (deco_inst.opcode() == spv::Op::OpDecorate)
+          builtin =
+              deco_inst.GetSingleWordInOperand(kOpDecorateBuiltInLiteralInIdx);
+        else if (deco_inst.opcode() == spv::Op::OpMemberDecorate)
+          builtin = deco_inst.GetSingleWordInOperand(
+              kOpDecorateMemberBuiltInLiteralInIdx);
+        else
+          assert(false && "unexpected decoration");
+        if (IsAnalyzedBuiltin(builtin)) live_builtins_.insert(builtin);
+      });
+  return saw_builtin;
+}
+
+void LivenessManager::MarkLocsLive(uint32_t start, uint32_t count) {
+  auto finish = start + count;
+  for (uint32_t u = start; u < finish; ++u) {
+    live_locs_.insert(u);
+  }
+}
+
+uint32_t LivenessManager::GetLocSize(const analysis::Type* type) const {
+  auto arr_type = type->AsArray();
+  if (arr_type) {
+    auto comp_type = arr_type->element_type();
+    auto len_info = arr_type->length_info();
+    assert(len_info.words[0] == analysis::Array::LengthInfo::kConstant &&
+           "unexpected array length");
+    auto comp_len = len_info.words[1];
+    return comp_len * GetLocSize(comp_type);
+  }
+  auto struct_type = type->AsStruct();
+  if (struct_type) {
+    uint32_t size = 0u;
+    for (auto& el_type : struct_type->element_types())
+      size += GetLocSize(el_type);
+    return size;
+  }
+  auto mat_type = type->AsMatrix();
+  if (mat_type) {
+    auto cnt = mat_type->element_count();
+    auto comp_type = mat_type->element_type();
+    return cnt * GetLocSize(comp_type);
+  }
+  auto vec_type = type->AsVector();
+  if (vec_type) {
+    auto comp_type = vec_type->element_type();
+    if (comp_type->AsInteger()) return 1;
+    auto float_type = comp_type->AsFloat();
+    assert(float_type && "unexpected vector component type");
+    auto width = float_type->width();
+    if (width == 32 || width == 16) return 1;
+    assert(width == 64 && "unexpected float type width");
+    auto comp_cnt = vec_type->element_count();
+    return (comp_cnt > 2) ? 2 : 1;
+  }
+  assert((type->AsInteger() || type->AsFloat()) && "unexpected input type");
+  return 1;
+}
+
+const analysis::Type* LivenessManager::GetComponentType(
+    uint32_t index, const analysis::Type* agg_type) const {
+  auto arr_type = agg_type->AsArray();
+  if (arr_type) return arr_type->element_type();
+  auto struct_type = agg_type->AsStruct();
+  if (struct_type) return struct_type->element_types()[index];
+  auto mat_type = agg_type->AsMatrix();
+  if (mat_type) return mat_type->element_type();
+  auto vec_type = agg_type->AsVector();
+  assert(vec_type && "unexpected non-aggregate type");
+  return vec_type->element_type();
+}
+
+uint32_t LivenessManager::GetLocOffset(uint32_t index,
+                                       const analysis::Type* agg_type) const {
+  auto arr_type = agg_type->AsArray();
+  if (arr_type) return index * GetLocSize(arr_type->element_type());
+  auto struct_type = agg_type->AsStruct();
+  if (struct_type) {
+    uint32_t offset = 0u;
+    uint32_t cnt = 0u;
+    for (auto& el_type : struct_type->element_types()) {
+      if (cnt == index) break;
+      offset += GetLocSize(el_type);
+      ++cnt;
+    }
+    return offset;
+  }
+  auto mat_type = agg_type->AsMatrix();
+  if (mat_type) return index * GetLocSize(mat_type->element_type());
+  auto vec_type = agg_type->AsVector();
+  assert(vec_type && "unexpected non-aggregate type");
+  auto comp_type = vec_type->element_type();
+  auto flt_type = comp_type->AsFloat();
+  if (flt_type && flt_type->width() == 64u && index >= 2u) return 1;
+  return 0;
+}
+
+void LivenessManager::AnalyzeAccessChainLoc(const Instruction* ac,
+                                            const analysis::Type** curr_type,
+                                            uint32_t* offset, bool* no_loc,
+                                            bool is_patch, bool input) {
+  analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
+  analysis::TypeManager* type_mgr = context()->get_type_mgr();
+  analysis::DecorationManager* deco_mgr = context()->get_decoration_mgr();
+  // For tesc, tese and geom input variables, and tesc output variables,
+  // first array index does not contribute to offset.
+  auto stage = context()->GetStage();
+  bool skip_first_index = false;
+  if ((input && (stage == spv::ExecutionModel::TessellationControl ||
+                 stage == spv::ExecutionModel::TessellationEvaluation ||
+                 stage == spv::ExecutionModel::Geometry)) ||
+      (!input && stage == spv::ExecutionModel::TessellationControl))
+    skip_first_index = !is_patch;
+  uint32_t ocnt = 0;
+  ac->WhileEachInOperand([this, &ocnt, def_use_mgr, type_mgr, deco_mgr,
+                          curr_type, offset, no_loc,
+                          skip_first_index](const uint32_t* opnd) {
+    if (ocnt >= 1) {
+      // Skip first index's contribution to offset if indicated
+      if (ocnt == 1 && skip_first_index) {
+        auto arr_type = (*curr_type)->AsArray();
+        assert(arr_type && "unexpected wrapper type");
+        *curr_type = arr_type->element_type();
+        ocnt++;
+        return true;
+      }
+      // If any non-constant index, mark the entire current object and return.
+      auto idx_inst = def_use_mgr->GetDef(*opnd);
+      if (idx_inst->opcode() != spv::Op::OpConstant) return false;
+      // If current type is struct, look for location decoration on member and
+      // reset offset if found.
+      auto index = idx_inst->GetSingleWordInOperand(0);
+      auto str_type = (*curr_type)->AsStruct();
+      if (str_type) {
+        uint32_t loc = 0;
+        auto str_type_id = type_mgr->GetId(str_type);
+        bool no_mem_loc = deco_mgr->WhileEachDecoration(
+            str_type_id, uint32_t(spv::Decoration::Location),
+            [&loc, index, no_loc](const Instruction& deco) {
+              assert(deco.opcode() == spv::Op::OpMemberDecorate &&
+                     "unexpected decoration");
+              if (deco.GetSingleWordInOperand(kOpDecorateMemberMemberInIdx) ==
+                  index) {
+                loc =
+                    deco.GetSingleWordInOperand(kOpDecorateMemberLocationInIdx);
+                *no_loc = false;
+                return false;
+              }
+              return true;
+            });
+        if (!no_mem_loc) {
+          *offset = loc;
+          *curr_type = GetComponentType(index, *curr_type);
+          ocnt++;
+          return true;
+        }
+      }
+
+      // Update offset and current type based on constant index.
+      *offset += GetLocOffset(index, *curr_type);
+      *curr_type = GetComponentType(index, *curr_type);
+    }
+    ocnt++;
+    return true;
+  });
+}
+
+void LivenessManager::MarkRefLive(const Instruction* ref, Instruction* var) {
+  analysis::TypeManager* type_mgr = context()->get_type_mgr();
+  analysis::DecorationManager* deco_mgr = context()->get_decoration_mgr();
+  // Find variable location if present.
+  uint32_t loc = 0;
+  auto var_id = var->result_id();
+  bool no_loc = deco_mgr->WhileEachDecoration(
+      var_id, uint32_t(spv::Decoration::Location),
+      [&loc](const Instruction& deco) {
+        assert(deco.opcode() == spv::Op::OpDecorate && "unexpected decoration");
+        loc = deco.GetSingleWordInOperand(kDecorationLocationInIdx);
+        return false;
+      });
+  // Find patch decoration if present
+  bool is_patch = !deco_mgr->WhileEachDecoration(
+      var_id, uint32_t(spv::Decoration::Patch), [](const Instruction& deco) {
+        if (deco.opcode() != spv::Op::OpDecorate)
+          assert(false && "unexpected decoration");
+        return false;
+      });
+  // If use is a load, mark all locations of var
+  auto ptr_type = type_mgr->GetType(var->type_id())->AsPointer();
+  assert(ptr_type && "unexpected var type");
+  auto var_type = ptr_type->pointee_type();
+  if (ref->opcode() == spv::Op::OpLoad) {
+    assert(!no_loc && "missing input variable location");
+    MarkLocsLive(loc, GetLocSize(var_type));
+    return;
+  }
+  // Mark just those locations indicated by access chain
+  assert((ref->opcode() == spv::Op::OpAccessChain ||
+          ref->opcode() == spv::Op::OpInBoundsAccessChain) &&
+         "unexpected use of input variable");
+  // Traverse access chain, compute location offset and type of reference
+  // through constant indices and mark those locs live. Assert if no location
+  // found.
+  uint32_t offset = loc;
+  auto curr_type = var_type;
+  AnalyzeAccessChainLoc(ref, &curr_type, &offset, &no_loc, is_patch);
+  assert(!no_loc && "missing input variable location");
+  MarkLocsLive(offset, GetLocSize(curr_type));
+}
+
+void LivenessManager::ComputeLiveness() {
+  InitializeAnalysis();
+  analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
+  analysis::TypeManager* type_mgr = context()->get_type_mgr();
+  // Process all input variables
+  for (auto& var : context()->types_values()) {
+    if (var.opcode() != spv::Op::OpVariable) {
+      continue;
+    }
+    analysis::Type* var_type = type_mgr->GetType(var.type_id());
+    analysis::Pointer* ptr_type = var_type->AsPointer();
+    if (ptr_type->storage_class() != spv::StorageClass::Input) {
+      continue;
+    }
+    // If var is builtin, mark live if analyzed and continue to next variable
+    auto var_id = var.result_id();
+    if (AnalyzeBuiltIn(var_id)) continue;
+    // If interface block with builtin members, mark live if analyzed and
+    // continue to next variable. Input interface blocks will only appear
+    // in tesc, tese and geom shaders. Will need to strip off one level of
+    // arrayness to get to block type.
+    auto pte_type = ptr_type->pointee_type();
+    auto arr_type = pte_type->AsArray();
+    if (arr_type) {
+      auto elt_type = arr_type->element_type();
+      auto str_type = elt_type->AsStruct();
+      if (str_type) {
+        auto str_type_id = type_mgr->GetId(str_type);
+        if (AnalyzeBuiltIn(str_type_id)) continue;
+      }
+    }
+    // Mark all used locations of var live
+    def_use_mgr->ForEachUser(var_id, [this, &var](Instruction* user) {
+      auto op = user->opcode();
+      if (op == spv::Op::OpEntryPoint || op == spv::Op::OpName ||
+          op == spv::Op::OpDecorate || user->IsNonSemanticInstruction()) {
+        return;
+      }
+      MarkRefLive(user, &var);
+    });
+  }
+}
+
+void LivenessManager::GetLiveness(std::unordered_set<uint32_t>* live_locs,
+                                  std::unordered_set<uint32_t>* live_builtins) {
+  if (!computed_) {
+    ComputeLiveness();
+    computed_ = true;
+  }
+  *live_locs = live_locs_;
+  *live_builtins = live_builtins_;
+}
+
+}  // namespace analysis
+}  // namespace opt
+}  // namespace spvtools
diff --git a/source/opt/liveness.h b/source/opt/liveness.h
new file mode 100644
index 0000000..7d8a9fb
--- /dev/null
+++ b/source/opt/liveness.h
@@ -0,0 +1,99 @@
+// Copyright (c) 2022 The Khronos Group Inc.
+// Copyright (c) 2022 LunarG Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_OPT_LIVENESS_H_
+#define SOURCE_OPT_LIVENESS_H_
+
+#include <cstdint>
+#include <unordered_set>
+
+namespace spvtools {
+namespace opt {
+
+class IRContext;
+class Instruction;
+
+namespace analysis {
+
+class Type;
+
+// This class represents the liveness of the input variables of a module
+class LivenessManager {
+ public:
+  LivenessManager(IRContext* ctx);
+
+  // Copy liveness info into |live_locs| and |builtin_locs|.
+  void GetLiveness(std::unordered_set<uint32_t>* live_locs,
+                   std::unordered_set<uint32_t>* live_builtins);
+
+  // Return true if builtin |bi| is being analyzed.
+  bool IsAnalyzedBuiltin(uint32_t bi);
+
+  // Determine starting loc |offset| and the type |cur_type| of
+  // access chain |ac|. Set |no_loc| to true if no loc found.
+  // |is_patch| indicates if patch variable. |input| is true
+  // if input variable, otherwise output variable.
+  void AnalyzeAccessChainLoc(const Instruction* ac,
+                             const analysis::Type** curr_type, uint32_t* offset,
+                             bool* no_loc, bool is_patch, bool input = true);
+
+  // Return size of |type_id| in units of locations
+  uint32_t GetLocSize(const analysis::Type* type) const;
+
+ private:
+  IRContext* context() const { return ctx_; }
+
+  // Initialize analysis
+  void InitializeAnalysis();
+
+  // Analyze |id| for builtin var and struct members. Return true if builtins
+  // found.
+  bool AnalyzeBuiltIn(uint32_t id);
+
+  // Mark all live locations resulting from |user| of |var| at |loc|.
+  void MarkRefLive(const Instruction* user, Instruction* var);
+
+  // Mark |count| locations starting at location |start|.
+  void MarkLocsLive(uint32_t start, uint32_t count);
+
+  // Return type of component of aggregate type |agg_type| at |index|
+  const analysis::Type* GetComponentType(uint32_t index,
+                                         const analysis::Type* agg_type) const;
+
+  // Return offset of |index| into aggregate type |agg_type| in units of
+  // input locations
+  uint32_t GetLocOffset(uint32_t index, const analysis::Type* agg_type) const;
+
+  // Populate live_locs_ and live_builtins_
+  void ComputeLiveness();
+
+  // IR context that owns this liveness manager.
+  IRContext* ctx_;
+
+  // True if live_locs_ and live_builtins_ are computed
+  bool computed_;
+
+  // Live locations
+  std::unordered_set<uint32_t> live_locs_;
+
+  // Live builtins
+  std::unordered_set<uint32_t> live_builtins_;
+};
+
+}  // namespace analysis
+}  // namespace opt
+}  // namespace spvtools
+
+#endif  // SOURCE_OPT_LIVENESS_H_
diff --git a/source/opt/local_access_chain_convert_pass.cpp b/source/opt/local_access_chain_convert_pass.cpp
index d11682f..fac4cea 100644
--- a/source/opt/local_access_chain_convert_pass.cpp
+++ b/source/opt/local_access_chain_convert_pass.cpp
@@ -16,23 +16,19 @@
 
 #include "source/opt/local_access_chain_convert_pass.h"
 
-#include "ir_builder.h"
 #include "ir_context.h"
 #include "iterator.h"
 #include "source/util/string_utils.h"
 
 namespace spvtools {
 namespace opt {
-
 namespace {
-
-const uint32_t kStoreValIdInIdx = 1;
-const uint32_t kAccessChainPtrIdInIdx = 0;
-
-}  // anonymous namespace
+constexpr uint32_t kStoreValIdInIdx = 1;
+constexpr uint32_t kAccessChainPtrIdInIdx = 0;
+}  // namespace
 
 void LocalAccessChainConvertPass::BuildAndAppendInst(
-    SpvOp opcode, uint32_t typeId, uint32_t resultId,
+    spv::Op opcode, uint32_t typeId, uint32_t resultId,
     const std::vector<Operand>& in_opnds,
     std::vector<std::unique_ptr<Instruction>>* newInsts) {
   std::unique_ptr<Instruction> newInst(
@@ -51,9 +47,9 @@
 
   *varId = ptrInst->GetSingleWordInOperand(kAccessChainPtrIdInIdx);
   const Instruction* varInst = get_def_use_mgr()->GetDef(*varId);
-  assert(varInst->opcode() == SpvOpVariable);
+  assert(varInst->opcode() == spv::Op::OpVariable);
   *varPteTypeId = GetPointeeTypeId(varInst);
-  BuildAndAppendInst(SpvOpLoad, *varPteTypeId, ldResultId,
+  BuildAndAppendInst(spv::Op::OpLoad, *varPteTypeId, ldResultId,
                      {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {*varId}}},
                      newInsts);
   return ldResultId;
@@ -108,7 +104,8 @@
 
   new_inst[0]->UpdateDebugInfoFrom(original_load);
   context()->get_decoration_mgr()->CloneDecorations(
-      original_load->result_id(), ldResultId, {SpvDecorationRelaxedPrecision});
+      original_load->result_id(), ldResultId,
+      {spv::Decoration::RelaxedPrecision});
   original_load->InsertBefore(std::move(new_inst));
   context()->get_debug_info_mgr()->AnalyzeDebugInst(
       original_load->PreviousNode());
@@ -123,7 +120,7 @@
   new_operands.emplace_back(
       Operand({spv_operand_type_t::SPV_OPERAND_TYPE_ID, {ldResultId}}));
   AppendConstantOperands(address_inst, &new_operands);
-  original_load->SetOpcode(SpvOpCompositeExtract);
+  original_load->SetOpcode(spv::Op::OpCompositeExtract);
   original_load->ReplaceOperands(new_operands);
   context()->UpdateDefUse(original_load);
   return true;
@@ -136,7 +133,7 @@
     // An access chain with no indices is essentially a copy.  However, we still
     // have to create a new store because the old ones will be deleted.
     BuildAndAppendInst(
-        SpvOpStore, 0, 0,
+        spv::Op::OpStore, 0, 0,
         {{spv_operand_type_t::SPV_OPERAND_TYPE_ID,
           {ptrInst->GetSingleWordInOperand(kAccessChainPtrIdInIdx)}},
          {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {valId}}},
@@ -154,7 +151,7 @@
   }
 
   context()->get_decoration_mgr()->CloneDecorations(
-      varId, ldResultId, {SpvDecorationRelaxedPrecision});
+      varId, ldResultId, {spv::Decoration::RelaxedPrecision});
 
   // Build and append Insert
   const uint32_t insResultId = TakeNextId();
@@ -165,14 +162,14 @@
       {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {valId}},
       {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {ldResultId}}};
   AppendConstantOperands(ptrInst, &ins_in_opnds);
-  BuildAndAppendInst(SpvOpCompositeInsert, varPteTypeId, insResultId,
+  BuildAndAppendInst(spv::Op::OpCompositeInsert, varPteTypeId, insResultId,
                      ins_in_opnds, newInsts);
 
   context()->get_decoration_mgr()->CloneDecorations(
-      varId, insResultId, {SpvDecorationRelaxedPrecision});
+      varId, insResultId, {spv::Decoration::RelaxedPrecision});
 
   // Build and append Store
-  BuildAndAppendInst(SpvOpStore, 0, 0,
+  BuildAndAppendInst(spv::Op::OpStore, 0, 0,
                      {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {varId}},
                       {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {insResultId}}},
                      newInsts);
@@ -185,7 +182,7 @@
   return acp->WhileEachInId([&inIdx, this](const uint32_t* tid) {
     if (inIdx > 0) {
       Instruction* opInst = get_def_use_mgr()->GetDef(*tid);
-      if (opInst->opcode() != SpvOpConstant) return false;
+      if (opInst->opcode() != spv::Op::OpConstant) return false;
       const auto* index =
           context()->get_constant_mgr()->GetConstantFromInst(opInst);
       int64_t index_value = index->GetSignExtendedValue();
@@ -204,13 +201,13 @@
             user->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare) {
           return true;
         }
-        SpvOp op = user->opcode();
-        if (IsNonPtrAccessChain(op) || op == SpvOpCopyObject) {
+        spv::Op op = user->opcode();
+        if (IsNonPtrAccessChain(op) || op == spv::Op::OpCopyObject) {
           if (!HasOnlySupportedRefs(user->result_id())) {
             return false;
           }
-        } else if (op != SpvOpStore && op != SpvOpLoad && op != SpvOpName &&
-                   !IsNonTypeDecorate(op)) {
+        } else if (op != spv::Op::OpStore && op != spv::Op::OpLoad &&
+                   op != spv::Op::OpName && !IsNonTypeDecorate(op)) {
           return false;
         }
         return true;
@@ -225,12 +222,12 @@
   for (auto bi = func->begin(); bi != func->end(); ++bi) {
     for (auto ii = bi->begin(); ii != bi->end(); ++ii) {
       switch (ii->opcode()) {
-        case SpvOpStore:
-        case SpvOpLoad: {
+        case spv::Op::OpStore:
+        case spv::Op::OpLoad: {
           uint32_t varId;
           Instruction* ptrInst = GetPtr(&*ii, &varId);
           if (!IsTargetVar(varId)) break;
-          const SpvOp op = ptrInst->opcode();
+          const spv::Op op = ptrInst->opcode();
           // Rule out variables with non-supported refs eg function calls
           if (!HasOnlySupportedRefs(varId)) {
             seen_non_target_vars_.insert(varId);
@@ -276,7 +273,7 @@
     std::vector<Instruction*> dead_instructions;
     for (auto ii = bi->begin(); ii != bi->end(); ++ii) {
       switch (ii->opcode()) {
-        case SpvOpLoad: {
+        case spv::Op::OpLoad: {
           uint32_t varId;
           Instruction* ptrInst = GetPtr(&*ii, &varId);
           if (!IsNonPtrAccessChain(ptrInst->opcode())) break;
@@ -286,7 +283,7 @@
           }
           modified = true;
         } break;
-        case SpvOpStore: {
+        case spv::Op::OpStore: {
           uint32_t varId;
           Instruction* store = &*ii;
           Instruction* ptrInst = GetPtr(store, &varId);
@@ -347,7 +344,7 @@
   // for the capability.  This pass is only looking at function scope symbols,
   // so we do not care if there are variable pointers on storage buffers.
   if (context()->get_feature_mgr()->HasCapability(
-          SpvCapabilityVariablePointers))
+          spv::Capability::VariablePointers))
     return false;
   // If any extension not in allowlist, return false
   for (auto& ei : get_module()->extensions()) {
@@ -359,7 +356,7 @@
   // around unknown extended
   // instruction sets even if they are non-semantic
   for (auto& inst : context()->module()->ext_inst_imports()) {
-    assert(inst.opcode() == SpvOpExtInstImport &&
+    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.") &&
@@ -375,7 +372,8 @@
   // support required in KillNamesAndDecorates().
   // TODO(greg-lunarg): Add support for OpGroupDecorate
   for (auto& ai : get_module()->annotations())
-    if (ai.opcode() == SpvOpGroupDecorate) return Status::SuccessWithoutChange;
+    if (ai.opcode() == spv::Op::OpGroupDecorate)
+      return Status::SuccessWithoutChange;
   // Do not process if any disallowed extensions are enabled
   if (!AllExtensionsSupported()) return Status::SuccessWithoutChange;
 
@@ -399,60 +397,38 @@
 
 void LocalAccessChainConvertPass::InitExtensions() {
   extensions_allowlist_.clear();
-  extensions_allowlist_.insert({
-      "SPV_AMD_shader_explicit_vertex_parameter",
-      "SPV_AMD_shader_trinary_minmax",
-      "SPV_AMD_gcn_shader",
-      "SPV_KHR_shader_ballot",
-      "SPV_AMD_shader_ballot",
-      "SPV_AMD_gpu_shader_half_float",
-      "SPV_KHR_shader_draw_parameters",
-      "SPV_KHR_subgroup_vote",
-      "SPV_KHR_8bit_storage",
-      "SPV_KHR_16bit_storage",
-      "SPV_KHR_device_group",
-      "SPV_KHR_multiview",
-      "SPV_NVX_multiview_per_view_attributes",
-      "SPV_NV_viewport_array2",
-      "SPV_NV_stereo_view_rendering",
-      "SPV_NV_sample_mask_override_coverage",
-      "SPV_NV_geometry_shader_passthrough",
-      "SPV_AMD_texture_gather_bias_lod",
-      "SPV_KHR_storage_buffer_storage_class",
-      // SPV_KHR_variable_pointers
-      //   Currently do not support extended pointer expressions
-      "SPV_AMD_gpu_shader_int16",
-      "SPV_KHR_post_depth_coverage",
-      "SPV_KHR_shader_atomic_counter_ops",
-      "SPV_EXT_shader_stencil_export",
-      "SPV_EXT_shader_viewport_index_layer",
-      "SPV_AMD_shader_image_load_store_lod",
-      "SPV_AMD_shader_fragment_mask",
-      "SPV_EXT_fragment_fully_covered",
-      "SPV_AMD_gpu_shader_half_float_fetch",
-      "SPV_GOOGLE_decorate_string",
-      "SPV_GOOGLE_hlsl_functionality1",
-      "SPV_GOOGLE_user_type",
-      "SPV_NV_shader_subgroup_partitioned",
-      "SPV_EXT_demote_to_helper_invocation",
-      "SPV_EXT_descriptor_indexing",
-      "SPV_NV_fragment_shader_barycentric",
-      "SPV_NV_compute_shader_derivatives",
-      "SPV_NV_shader_image_footprint",
-      "SPV_NV_shading_rate",
-      "SPV_NV_mesh_shader",
-      "SPV_NV_ray_tracing",
-      "SPV_KHR_ray_tracing",
-      "SPV_KHR_ray_query",
-      "SPV_EXT_fragment_invocation_density",
-      "SPV_KHR_terminate_invocation",
-      "SPV_KHR_subgroup_uniform_control_flow",
-      "SPV_KHR_integer_dot_product",
-      "SPV_EXT_shader_image_int64",
-      "SPV_KHR_non_semantic_info",
-      "SPV_KHR_uniform_group_instructions",
-      "SPV_KHR_fragment_shader_barycentric",
-  });
+  extensions_allowlist_.insert(
+      {"SPV_AMD_shader_explicit_vertex_parameter",
+       "SPV_AMD_shader_trinary_minmax", "SPV_AMD_gcn_shader",
+       "SPV_KHR_shader_ballot", "SPV_AMD_shader_ballot",
+       "SPV_AMD_gpu_shader_half_float", "SPV_KHR_shader_draw_parameters",
+       "SPV_KHR_subgroup_vote", "SPV_KHR_8bit_storage", "SPV_KHR_16bit_storage",
+       "SPV_KHR_device_group", "SPV_KHR_multiview",
+       "SPV_NVX_multiview_per_view_attributes", "SPV_NV_viewport_array2",
+       "SPV_NV_stereo_view_rendering", "SPV_NV_sample_mask_override_coverage",
+       "SPV_NV_geometry_shader_passthrough", "SPV_AMD_texture_gather_bias_lod",
+       "SPV_KHR_storage_buffer_storage_class",
+       // SPV_KHR_variable_pointers
+       //   Currently do not support extended pointer expressions
+       "SPV_AMD_gpu_shader_int16", "SPV_KHR_post_depth_coverage",
+       "SPV_KHR_shader_atomic_counter_ops", "SPV_EXT_shader_stencil_export",
+       "SPV_EXT_shader_viewport_index_layer",
+       "SPV_AMD_shader_image_load_store_lod", "SPV_AMD_shader_fragment_mask",
+       "SPV_EXT_fragment_fully_covered", "SPV_AMD_gpu_shader_half_float_fetch",
+       "SPV_GOOGLE_decorate_string", "SPV_GOOGLE_hlsl_functionality1",
+       "SPV_GOOGLE_user_type", "SPV_NV_shader_subgroup_partitioned",
+       "SPV_EXT_demote_to_helper_invocation", "SPV_EXT_descriptor_indexing",
+       "SPV_NV_fragment_shader_barycentric",
+       "SPV_NV_compute_shader_derivatives", "SPV_NV_shader_image_footprint",
+       "SPV_NV_shading_rate", "SPV_NV_mesh_shader", "SPV_NV_ray_tracing",
+       "SPV_KHR_ray_tracing", "SPV_KHR_ray_query",
+       "SPV_EXT_fragment_invocation_density", "SPV_KHR_terminate_invocation",
+       "SPV_KHR_subgroup_uniform_control_flow", "SPV_KHR_integer_dot_product",
+       "SPV_EXT_shader_image_int64", "SPV_KHR_non_semantic_info",
+       "SPV_KHR_uniform_group_instructions",
+       "SPV_KHR_fragment_shader_barycentric", "SPV_KHR_vulkan_memory_model",
+       "SPV_NV_bindless_texture", "SPV_EXT_shader_atomic_float_add",
+       "SPV_EXT_fragment_shader_interlock"});
 }
 
 bool LocalAccessChainConvertPass::AnyIndexIsOutOfBounds(
diff --git a/source/opt/local_access_chain_convert_pass.h b/source/opt/local_access_chain_convert_pass.h
index c3731b1..0cda196 100644
--- a/source/opt/local_access_chain_convert_pass.h
+++ b/source/opt/local_access_chain_convert_pass.h
@@ -64,7 +64,7 @@
 
   // Build instruction from |opcode|, |typeId|, |resultId|, and |in_opnds|.
   // Append to |newInsts|.
-  void BuildAndAppendInst(SpvOp opcode, uint32_t typeId, uint32_t resultId,
+  void BuildAndAppendInst(spv::Op opcode, uint32_t typeId, uint32_t resultId,
                           const std::vector<Operand>& in_opnds,
                           std::vector<std::unique_ptr<Instruction>>* newInsts);
 
diff --git a/source/opt/local_single_block_elim_pass.cpp b/source/opt/local_single_block_elim_pass.cpp
index a58e8e4..0acffda 100644
--- a/source/opt/local_single_block_elim_pass.cpp
+++ b/source/opt/local_single_block_elim_pass.cpp
@@ -18,16 +18,13 @@
 
 #include <vector>
 
-#include "source/opt/iterator.h"
 #include "source/util/string_utils.h"
 
 namespace spvtools {
 namespace opt {
 namespace {
-
-const uint32_t kStoreValIdInIdx = 1;
-
-}  // anonymous namespace
+constexpr uint32_t kStoreValIdInIdx = 1;
+}  // namespace
 
 bool LocalSingleBlockLoadStoreElimPass::HasOnlySupportedRefs(uint32_t ptrId) {
   if (supported_ref_ptrs_.find(ptrId) != supported_ref_ptrs_.end()) return true;
@@ -37,13 +34,13 @@
             dbg_op == CommonDebugInfoDebugValue) {
           return true;
         }
-        SpvOp op = user->opcode();
-        if (IsNonPtrAccessChain(op) || op == SpvOpCopyObject) {
+        spv::Op op = user->opcode();
+        if (IsNonPtrAccessChain(op) || op == spv::Op::OpCopyObject) {
           if (!HasOnlySupportedRefs(user->result_id())) {
             return false;
           }
-        } else if (op != SpvOpStore && op != SpvOpLoad && op != SpvOpName &&
-                   !IsNonTypeDecorate(op)) {
+        } else if (op != spv::Op::OpStore && op != spv::Op::OpLoad &&
+                   op != spv::Op::OpName && !IsNonTypeDecorate(op)) {
           return false;
         }
         return true;
@@ -68,7 +65,7 @@
     for (auto ii = next; ii != bi->end(); ii = next) {
       ++next;
       switch (ii->opcode()) {
-        case SpvOpStore: {
+        case spv::Op::OpStore: {
           // Verify store variable is target type
           uint32_t varId;
           Instruction* ptrInst = GetPtr(&*ii, &varId);
@@ -77,7 +74,7 @@
           // If a store to the whole variable, remember it for succeeding
           // loads and stores. Otherwise forget any previous store to that
           // variable.
-          if (ptrInst->opcode() == SpvOpVariable) {
+          if (ptrInst->opcode() == spv::Op::OpVariable) {
             // If a previous store to same variable, mark the store
             // for deletion if not still used. Don't delete store
             // if debugging; let ssa-rewrite and DCE handle it
@@ -114,14 +111,14 @@
             var2load_.erase(varId);
           }
         } break;
-        case SpvOpLoad: {
+        case spv::Op::OpLoad: {
           // Verify store variable is target type
           uint32_t varId;
           Instruction* ptrInst = GetPtr(&*ii, &varId);
           if (!IsTargetVar(varId)) continue;
           if (!HasOnlySupportedRefs(varId)) continue;
           uint32_t replId = 0;
-          if (ptrInst->opcode() == SpvOpVariable) {
+          if (ptrInst->opcode() == spv::Op::OpVariable) {
             // If a load from a variable, look for a previous store or
             // load from that variable and use its value.
             auto si = var2store_.find(varId);
@@ -146,11 +143,11 @@
             instructions_to_kill.push_back(&*ii);
             modified = true;
           } else {
-            if (ptrInst->opcode() == SpvOpVariable)
+            if (ptrInst->opcode() == spv::Op::OpVariable)
               var2load_[varId] = &*ii;  // register load
           }
         } break;
-        case SpvOpFunctionCall: {
+        case spv::Op::OpFunctionCall: {
           // Conservatively assume all locals are redefined for now.
           // TODO(): Handle more optimally
           var2store_.clear();
@@ -192,7 +189,7 @@
   // around unknown extended
   // instruction sets even if they are non-semantic
   for (auto& inst : context()->module()->ext_inst_imports()) {
-    assert(inst.opcode() == SpvOpExtInstImport &&
+    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.") &&
@@ -205,14 +202,15 @@
 
 Pass::Status LocalSingleBlockLoadStoreElimPass::ProcessImpl() {
   // Assumes relaxed logical addressing only (see instruction.h).
-  if (context()->get_feature_mgr()->HasCapability(SpvCapabilityAddresses))
+  if (context()->get_feature_mgr()->HasCapability(spv::Capability::Addresses))
     return Status::SuccessWithoutChange;
 
   // Do not process if module contains OpGroupDecorate. Additional
   // support required in KillNamesAndDecorates().
   // TODO(greg-lunarg): Add support for OpGroupDecorate
   for (auto& ai : get_module()->annotations())
-    if (ai.opcode() == SpvOpGroupDecorate) return Status::SuccessWithoutChange;
+    if (ai.opcode() == spv::Op::OpGroupDecorate)
+      return Status::SuccessWithoutChange;
   // If any extensions in the module are not explicitly supported,
   // return unmodified.
   if (!AllExtensionsSupported()) return Status::SuccessWithoutChange;
@@ -235,60 +233,63 @@
 
 void LocalSingleBlockLoadStoreElimPass::InitExtensions() {
   extensions_allowlist_.clear();
-  extensions_allowlist_.insert({
-      "SPV_AMD_shader_explicit_vertex_parameter",
-      "SPV_AMD_shader_trinary_minmax",
-      "SPV_AMD_gcn_shader",
-      "SPV_KHR_shader_ballot",
-      "SPV_AMD_shader_ballot",
-      "SPV_AMD_gpu_shader_half_float",
-      "SPV_KHR_shader_draw_parameters",
-      "SPV_KHR_subgroup_vote",
-      "SPV_KHR_8bit_storage",
-      "SPV_KHR_16bit_storage",
-      "SPV_KHR_device_group",
-      "SPV_KHR_multiview",
-      "SPV_NVX_multiview_per_view_attributes",
-      "SPV_NV_viewport_array2",
-      "SPV_NV_stereo_view_rendering",
-      "SPV_NV_sample_mask_override_coverage",
-      "SPV_NV_geometry_shader_passthrough",
-      "SPV_AMD_texture_gather_bias_lod",
-      "SPV_KHR_storage_buffer_storage_class",
-      "SPV_KHR_variable_pointers",
-      "SPV_AMD_gpu_shader_int16",
-      "SPV_KHR_post_depth_coverage",
-      "SPV_KHR_shader_atomic_counter_ops",
-      "SPV_EXT_shader_stencil_export",
-      "SPV_EXT_shader_viewport_index_layer",
-      "SPV_AMD_shader_image_load_store_lod",
-      "SPV_AMD_shader_fragment_mask",
-      "SPV_EXT_fragment_fully_covered",
-      "SPV_AMD_gpu_shader_half_float_fetch",
-      "SPV_GOOGLE_decorate_string",
-      "SPV_GOOGLE_hlsl_functionality1",
-      "SPV_GOOGLE_user_type",
-      "SPV_NV_shader_subgroup_partitioned",
-      "SPV_EXT_demote_to_helper_invocation",
-      "SPV_EXT_descriptor_indexing",
-      "SPV_NV_fragment_shader_barycentric",
-      "SPV_NV_compute_shader_derivatives",
-      "SPV_NV_shader_image_footprint",
-      "SPV_NV_shading_rate",
-      "SPV_NV_mesh_shader",
-      "SPV_NV_ray_tracing",
-      "SPV_KHR_ray_tracing",
-      "SPV_KHR_ray_query",
-      "SPV_EXT_fragment_invocation_density",
-      "SPV_EXT_physical_storage_buffer",
-      "SPV_KHR_terminate_invocation",
-      "SPV_KHR_subgroup_uniform_control_flow",
-      "SPV_KHR_integer_dot_product",
-      "SPV_EXT_shader_image_int64",
-      "SPV_KHR_non_semantic_info",
-      "SPV_KHR_uniform_group_instructions",
-      "SPV_KHR_fragment_shader_barycentric",
-  });
+  extensions_allowlist_.insert({"SPV_AMD_shader_explicit_vertex_parameter",
+                                "SPV_AMD_shader_trinary_minmax",
+                                "SPV_AMD_gcn_shader",
+                                "SPV_KHR_shader_ballot",
+                                "SPV_AMD_shader_ballot",
+                                "SPV_AMD_gpu_shader_half_float",
+                                "SPV_KHR_shader_draw_parameters",
+                                "SPV_KHR_subgroup_vote",
+                                "SPV_KHR_8bit_storage",
+                                "SPV_KHR_16bit_storage",
+                                "SPV_KHR_device_group",
+                                "SPV_KHR_multiview",
+                                "SPV_NVX_multiview_per_view_attributes",
+                                "SPV_NV_viewport_array2",
+                                "SPV_NV_stereo_view_rendering",
+                                "SPV_NV_sample_mask_override_coverage",
+                                "SPV_NV_geometry_shader_passthrough",
+                                "SPV_AMD_texture_gather_bias_lod",
+                                "SPV_KHR_storage_buffer_storage_class",
+                                "SPV_KHR_variable_pointers",
+                                "SPV_AMD_gpu_shader_int16",
+                                "SPV_KHR_post_depth_coverage",
+                                "SPV_KHR_shader_atomic_counter_ops",
+                                "SPV_EXT_shader_stencil_export",
+                                "SPV_EXT_shader_viewport_index_layer",
+                                "SPV_AMD_shader_image_load_store_lod",
+                                "SPV_AMD_shader_fragment_mask",
+                                "SPV_EXT_fragment_fully_covered",
+                                "SPV_AMD_gpu_shader_half_float_fetch",
+                                "SPV_GOOGLE_decorate_string",
+                                "SPV_GOOGLE_hlsl_functionality1",
+                                "SPV_GOOGLE_user_type",
+                                "SPV_NV_shader_subgroup_partitioned",
+                                "SPV_EXT_demote_to_helper_invocation",
+                                "SPV_EXT_descriptor_indexing",
+                                "SPV_NV_fragment_shader_barycentric",
+                                "SPV_NV_compute_shader_derivatives",
+                                "SPV_NV_shader_image_footprint",
+                                "SPV_NV_shading_rate",
+                                "SPV_NV_mesh_shader",
+                                "SPV_NV_ray_tracing",
+                                "SPV_KHR_ray_tracing",
+                                "SPV_KHR_ray_query",
+                                "SPV_EXT_fragment_invocation_density",
+                                "SPV_EXT_physical_storage_buffer",
+                                "SPV_KHR_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"});
 }
 
 }  // namespace opt
diff --git a/source/opt/local_single_store_elim_pass.cpp b/source/opt/local_single_store_elim_pass.cpp
index 81648c7..77b3420 100644
--- a/source/opt/local_single_store_elim_pass.cpp
+++ b/source/opt/local_single_store_elim_pass.cpp
@@ -17,19 +17,14 @@
 #include "source/opt/local_single_store_elim_pass.h"
 
 #include "source/cfa.h"
-#include "source/latest_version_glsl_std_450_header.h"
-#include "source/opt/iterator.h"
 #include "source/util/string_utils.h"
 
 namespace spvtools {
 namespace opt {
-
 namespace {
-
-const uint32_t kStoreValIdInIdx = 1;
-const uint32_t kVariableInitIdInIdx = 1;
-
-}  // anonymous namespace
+constexpr uint32_t kStoreValIdInIdx = 1;
+constexpr uint32_t kVariableInitIdInIdx = 1;
+}  // namespace
 
 bool LocalSingleStoreElimPass::LocalSingleStoreElim(Function* func) {
   bool modified = false;
@@ -37,7 +32,7 @@
   // Check all function scope variables in |func|.
   BasicBlock* entry_block = &*func->begin();
   for (Instruction& inst : *entry_block) {
-    if (inst.opcode() != SpvOpVariable) {
+    if (inst.opcode() != spv::Op::OpVariable) {
       break;
     }
 
@@ -57,7 +52,7 @@
   // around unknown extended
   // instruction sets even if they are non-semantic
   for (auto& inst : context()->module()->ext_inst_imports()) {
-    assert(inst.opcode() == SpvOpExtInstImport &&
+    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.") &&
@@ -70,7 +65,7 @@
 
 Pass::Status LocalSingleStoreElimPass::ProcessImpl() {
   // Assumes relaxed logical addressing only (see instruction.h)
-  if (context()->get_feature_mgr()->HasCapability(SpvCapabilityAddresses))
+  if (context()->get_feature_mgr()->HasCapability(spv::Capability::Addresses))
     return Status::SuccessWithoutChange;
 
   // Do not process if any disallowed extensions are enabled
@@ -91,57 +86,60 @@
 }
 
 void LocalSingleStoreElimPass::InitExtensionAllowList() {
-  extensions_allowlist_.insert({
-      "SPV_AMD_shader_explicit_vertex_parameter",
-      "SPV_AMD_shader_trinary_minmax",
-      "SPV_AMD_gcn_shader",
-      "SPV_KHR_shader_ballot",
-      "SPV_AMD_shader_ballot",
-      "SPV_AMD_gpu_shader_half_float",
-      "SPV_KHR_shader_draw_parameters",
-      "SPV_KHR_subgroup_vote",
-      "SPV_KHR_8bit_storage",
-      "SPV_KHR_16bit_storage",
-      "SPV_KHR_device_group",
-      "SPV_KHR_multiview",
-      "SPV_NVX_multiview_per_view_attributes",
-      "SPV_NV_viewport_array2",
-      "SPV_NV_stereo_view_rendering",
-      "SPV_NV_sample_mask_override_coverage",
-      "SPV_NV_geometry_shader_passthrough",
-      "SPV_AMD_texture_gather_bias_lod",
-      "SPV_KHR_storage_buffer_storage_class",
-      "SPV_KHR_variable_pointers",
-      "SPV_AMD_gpu_shader_int16",
-      "SPV_KHR_post_depth_coverage",
-      "SPV_KHR_shader_atomic_counter_ops",
-      "SPV_EXT_shader_stencil_export",
-      "SPV_EXT_shader_viewport_index_layer",
-      "SPV_AMD_shader_image_load_store_lod",
-      "SPV_AMD_shader_fragment_mask",
-      "SPV_EXT_fragment_fully_covered",
-      "SPV_AMD_gpu_shader_half_float_fetch",
-      "SPV_GOOGLE_decorate_string",
-      "SPV_GOOGLE_hlsl_functionality1",
-      "SPV_NV_shader_subgroup_partitioned",
-      "SPV_EXT_descriptor_indexing",
-      "SPV_NV_fragment_shader_barycentric",
-      "SPV_NV_compute_shader_derivatives",
-      "SPV_NV_shader_image_footprint",
-      "SPV_NV_shading_rate",
-      "SPV_NV_mesh_shader",
-      "SPV_NV_ray_tracing",
-      "SPV_KHR_ray_query",
-      "SPV_EXT_fragment_invocation_density",
-      "SPV_EXT_physical_storage_buffer",
-      "SPV_KHR_terminate_invocation",
-      "SPV_KHR_subgroup_uniform_control_flow",
-      "SPV_KHR_integer_dot_product",
-      "SPV_EXT_shader_image_int64",
-      "SPV_KHR_non_semantic_info",
-      "SPV_KHR_uniform_group_instructions",
-      "SPV_KHR_fragment_shader_barycentric",
-  });
+  extensions_allowlist_.insert({"SPV_AMD_shader_explicit_vertex_parameter",
+                                "SPV_AMD_shader_trinary_minmax",
+                                "SPV_AMD_gcn_shader",
+                                "SPV_KHR_shader_ballot",
+                                "SPV_AMD_shader_ballot",
+                                "SPV_AMD_gpu_shader_half_float",
+                                "SPV_KHR_shader_draw_parameters",
+                                "SPV_KHR_subgroup_vote",
+                                "SPV_KHR_8bit_storage",
+                                "SPV_KHR_16bit_storage",
+                                "SPV_KHR_device_group",
+                                "SPV_KHR_multiview",
+                                "SPV_NVX_multiview_per_view_attributes",
+                                "SPV_NV_viewport_array2",
+                                "SPV_NV_stereo_view_rendering",
+                                "SPV_NV_sample_mask_override_coverage",
+                                "SPV_NV_geometry_shader_passthrough",
+                                "SPV_AMD_texture_gather_bias_lod",
+                                "SPV_KHR_storage_buffer_storage_class",
+                                "SPV_KHR_variable_pointers",
+                                "SPV_AMD_gpu_shader_int16",
+                                "SPV_KHR_post_depth_coverage",
+                                "SPV_KHR_shader_atomic_counter_ops",
+                                "SPV_EXT_shader_stencil_export",
+                                "SPV_EXT_shader_viewport_index_layer",
+                                "SPV_AMD_shader_image_load_store_lod",
+                                "SPV_AMD_shader_fragment_mask",
+                                "SPV_EXT_fragment_fully_covered",
+                                "SPV_AMD_gpu_shader_half_float_fetch",
+                                "SPV_GOOGLE_decorate_string",
+                                "SPV_GOOGLE_hlsl_functionality1",
+                                "SPV_NV_shader_subgroup_partitioned",
+                                "SPV_EXT_descriptor_indexing",
+                                "SPV_NV_fragment_shader_barycentric",
+                                "SPV_NV_compute_shader_derivatives",
+                                "SPV_NV_shader_image_footprint",
+                                "SPV_NV_shading_rate",
+                                "SPV_NV_mesh_shader",
+                                "SPV_NV_ray_tracing",
+                                "SPV_KHR_ray_query",
+                                "SPV_EXT_fragment_invocation_density",
+                                "SPV_EXT_physical_storage_buffer",
+                                "SPV_KHR_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"});
 }
 bool LocalSingleStoreElimPass::ProcessVariable(Instruction* var_inst) {
   std::vector<Instruction*> users;
@@ -194,7 +192,7 @@
 
   for (Instruction* user : users) {
     switch (user->opcode()) {
-      case SpvOpStore:
+      case spv::Op::OpStore:
         // Since we are in the relaxed addressing mode, the use has to be the
         // base address of the store, and not the value being store.  Otherwise,
         // we would have a pointer to a pointer to function scope memory, which
@@ -206,19 +204,19 @@
           return nullptr;
         }
         break;
-      case SpvOpAccessChain:
-      case SpvOpInBoundsAccessChain:
+      case spv::Op::OpAccessChain:
+      case spv::Op::OpInBoundsAccessChain:
         if (FeedsAStore(user)) {
           // Has a partial store.  Cannot propagate that.
           return nullptr;
         }
         break;
-      case SpvOpLoad:
-      case SpvOpImageTexelPointer:
-      case SpvOpName:
-      case SpvOpCopyObject:
+      case spv::Op::OpLoad:
+      case spv::Op::OpImageTexelPointer:
+      case spv::Op::OpName:
+      case spv::Op::OpCopyObject:
         break;
-      case SpvOpExtInst: {
+      case spv::Op::OpExtInst: {
         auto dbg_op = user->GetCommonDebugOpcode();
         if (dbg_op == CommonDebugInfoDebugDeclare ||
             dbg_op == CommonDebugInfoDebugValue) {
@@ -243,7 +241,7 @@
   analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
   def_use_mgr->ForEachUser(var_inst, [users, this](Instruction* user) {
     users->push_back(user);
-    if (user->opcode() == SpvOpCopyObject) {
+    if (user->opcode() == spv::Op::OpCopyObject) {
       FindUses(user, users);
     }
   });
@@ -253,15 +251,15 @@
   analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
   return !def_use_mgr->WhileEachUser(inst, [this](Instruction* user) {
     switch (user->opcode()) {
-      case SpvOpStore:
+      case spv::Op::OpStore:
         return false;
-      case SpvOpAccessChain:
-      case SpvOpInBoundsAccessChain:
-      case SpvOpCopyObject:
+      case spv::Op::OpAccessChain:
+      case spv::Op::OpInBoundsAccessChain:
+      case spv::Op::OpCopyObject:
         return !FeedsAStore(user);
-      case SpvOpLoad:
-      case SpvOpImageTexelPointer:
-      case SpvOpName:
+      case spv::Op::OpLoad:
+      case spv::Op::OpImageTexelPointer:
+      case spv::Op::OpName:
         return true;
       default:
         // Don't know if this instruction modifies the variable.
@@ -279,7 +277,7 @@
       context()->GetDominatorAnalysis(store_block->GetParent());
 
   uint32_t stored_id;
-  if (store_inst->opcode() == SpvOpStore)
+  if (store_inst->opcode() == spv::Op::OpStore)
     stored_id = store_inst->GetSingleWordInOperand(kStoreValIdInIdx);
   else
     stored_id = store_inst->GetSingleWordInOperand(kVariableInitIdInIdx);
@@ -287,12 +285,12 @@
   *all_rewritten = true;
   bool modified = false;
   for (Instruction* use : uses) {
-    if (use->opcode() == SpvOpStore) continue;
+    if (use->opcode() == spv::Op::OpStore) continue;
     auto dbg_op = use->GetCommonDebugOpcode();
     if (dbg_op == CommonDebugInfoDebugDeclare ||
         dbg_op == CommonDebugInfoDebugValue)
       continue;
-    if (use->opcode() == SpvOpLoad &&
+    if (use->opcode() == spv::Op::OpLoad &&
         dominator_analysis->Dominates(store_inst, use)) {
       modified = true;
       context()->KillNamesAndDecorates(use->result_id());
diff --git a/source/opt/loop_dependence.cpp b/source/opt/loop_dependence.cpp
index d8de699..e41c044 100644
--- a/source/opt/loop_dependence.cpp
+++ b/source/opt/loop_dependence.cpp
@@ -15,14 +15,12 @@
 #include "source/opt/loop_dependence.h"
 
 #include <functional>
-#include <memory>
 #include <numeric>
 #include <string>
 #include <utility>
 #include <vector>
 
 #include "source/opt/instruction.h"
-#include "source/opt/scalar_analysis.h"
 #include "source/opt/scalar_analysis_nodes.h"
 
 namespace spvtools {
@@ -192,8 +190,8 @@
   Instruction* destination_access_chain = GetOperandDefinition(destination, 0);
 
   auto num_access_chains =
-      (source_access_chain->opcode() == SpvOpAccessChain) +
-      (destination_access_chain->opcode() == SpvOpAccessChain);
+      (source_access_chain->opcode() == spv::Op::OpAccessChain) +
+      (destination_access_chain->opcode() == spv::Op::OpAccessChain);
 
   // If neither is an access chain, then they are load/store to a variable.
   if (num_access_chains == 0) {
@@ -211,7 +209,8 @@
 
   // If only one is an access chain, it could be accessing a part of a struct
   if (num_access_chains == 1) {
-    auto source_is_chain = source_access_chain->opcode() == SpvOpAccessChain;
+    auto source_is_chain =
+        source_access_chain->opcode() == spv::Op::OpAccessChain;
     auto access_chain =
         source_is_chain ? source_access_chain : destination_access_chain;
     auto variable =
@@ -238,8 +237,8 @@
       GetOperandDefinition(destination_access_chain, 0);
 
   // Nested access chains are not supported yet, bail out.
-  if (source_array->opcode() == SpvOpAccessChain ||
-      destination_array->opcode() == SpvOpAccessChain) {
+  if (source_array->opcode() == spv::Op::OpAccessChain ||
+      destination_array->opcode() == spv::Op::OpAccessChain) {
     for (auto& entry : distance_vector->GetEntries()) {
       entry = DistanceEntry();
     }
diff --git a/source/opt/loop_dependence_helpers.cpp b/source/opt/loop_dependence_helpers.cpp
index de27a0a..5d7d994 100644
--- a/source/opt/loop_dependence_helpers.cpp
+++ b/source/opt/loop_dependence_helpers.cpp
@@ -12,8 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "source/opt/loop_dependence.h"
-
 #include <ostream>
 #include <set>
 #include <string>
@@ -23,7 +21,7 @@
 
 #include "source/opt/basic_block.h"
 #include "source/opt/instruction.h"
-#include "source/opt/scalar_analysis.h"
+#include "source/opt/loop_dependence.h"
 #include "source/opt/scalar_analysis_nodes.h"
 
 namespace spvtools {
@@ -54,20 +52,20 @@
   }
   Instruction* lower_inst = GetOperandDefinition(cond_inst, 0);
   switch (cond_inst->opcode()) {
-    case SpvOpULessThan:
-    case SpvOpSLessThan:
-    case SpvOpULessThanEqual:
-    case SpvOpSLessThanEqual:
-    case SpvOpUGreaterThan:
-    case SpvOpSGreaterThan:
-    case SpvOpUGreaterThanEqual:
-    case SpvOpSGreaterThanEqual: {
+    case spv::Op::OpULessThan:
+    case spv::Op::OpSLessThan:
+    case spv::Op::OpULessThanEqual:
+    case spv::Op::OpSLessThanEqual:
+    case spv::Op::OpUGreaterThan:
+    case spv::Op::OpSGreaterThan:
+    case spv::Op::OpUGreaterThanEqual:
+    case spv::Op::OpSGreaterThanEqual: {
       // If we have a phi we are looking at the induction variable. We look
       // through the phi to the initial value of the phi upon entering the loop.
-      if (lower_inst->opcode() == SpvOpPhi) {
+      if (lower_inst->opcode() == spv::Op::OpPhi) {
         lower_inst = GetOperandDefinition(lower_inst, 0);
         // We don't handle looking through multiple phis.
-        if (lower_inst->opcode() == SpvOpPhi) {
+        if (lower_inst->opcode() == spv::Op::OpPhi) {
           return nullptr;
         }
       }
@@ -86,8 +84,8 @@
   }
   Instruction* upper_inst = GetOperandDefinition(cond_inst, 1);
   switch (cond_inst->opcode()) {
-    case SpvOpULessThan:
-    case SpvOpSLessThan: {
+    case spv::Op::OpULessThan:
+    case spv::Op::OpSLessThan: {
       // When we have a < condition we must subtract 1 from the analyzed upper
       // instruction.
       SENode* upper_bound = scalar_evolution_.SimplifyExpression(
@@ -96,8 +94,8 @@
               scalar_evolution_.CreateConstant(1)));
       return upper_bound;
     }
-    case SpvOpUGreaterThan:
-    case SpvOpSGreaterThan: {
+    case spv::Op::OpUGreaterThan:
+    case spv::Op::OpSGreaterThan: {
       // When we have a > condition we must add 1 to the analyzed upper
       // instruction.
       SENode* upper_bound =
@@ -106,10 +104,10 @@
               scalar_evolution_.CreateConstant(1)));
       return upper_bound;
     }
-    case SpvOpULessThanEqual:
-    case SpvOpSLessThanEqual:
-    case SpvOpUGreaterThanEqual:
-    case SpvOpSGreaterThanEqual: {
+    case spv::Op::OpULessThanEqual:
+    case spv::Op::OpSLessThanEqual:
+    case spv::Op::OpUGreaterThanEqual:
+    case spv::Op::OpSGreaterThanEqual: {
       // We don't need to modify the results of analyzing when we have <= or >=.
       SENode* upper_bound = scalar_evolution_.SimplifyExpression(
           scalar_evolution_.AnalyzeInstruction(upper_inst));
diff --git a/source/opt/loop_descriptor.cpp b/source/opt/loop_descriptor.cpp
index 13982d1..cbfc2e7 100644
--- a/source/opt/loop_descriptor.cpp
+++ b/source/opt/loop_descriptor.cpp
@@ -15,17 +15,14 @@
 #include "source/opt/loop_descriptor.h"
 
 #include <algorithm>
-#include <iostream>
 #include <limits>
 #include <stack>
-#include <type_traits>
 #include <utility>
 #include <vector>
 
 #include "source/opt/cfg.h"
 #include "source/opt/constants.h"
 #include "source/opt/dominator_tree.h"
-#include "source/opt/ir_builder.h"
 #include "source/opt/ir_context.h"
 #include "source/opt/iterator.h"
 #include "source/opt/tree_iterator.h"
@@ -39,7 +36,7 @@
 Instruction* Loop::GetInductionStepOperation(
     const Instruction* induction) const {
   // Induction must be a phi instruction.
-  assert(induction->opcode() == SpvOpPhi);
+  assert(induction->opcode() == spv::Op::OpPhi);
 
   Instruction* step = nullptr;
 
@@ -75,8 +72,8 @@
     return nullptr;
   }
 
-  if (def_use_manager->GetDef(lhs)->opcode() != SpvOp::SpvOpConstant &&
-      def_use_manager->GetDef(rhs)->opcode() != SpvOp::SpvOpConstant) {
+  if (def_use_manager->GetDef(lhs)->opcode() != spv::Op::OpConstant &&
+      def_use_manager->GetDef(rhs)->opcode() != spv::Op::OpConstant) {
     return nullptr;
   }
 
@@ -85,31 +82,31 @@
 
 // Returns true if the |step| operation is an induction variable step operation
 // which is currently handled.
-bool Loop::IsSupportedStepOp(SpvOp step) const {
+bool Loop::IsSupportedStepOp(spv::Op step) const {
   switch (step) {
-    case SpvOp::SpvOpISub:
-    case SpvOp::SpvOpIAdd:
+    case spv::Op::OpISub:
+    case spv::Op::OpIAdd:
       return true;
     default:
       return false;
   }
 }
 
-bool Loop::IsSupportedCondition(SpvOp condition) const {
+bool Loop::IsSupportedCondition(spv::Op condition) const {
   switch (condition) {
     // <
-    case SpvOp::SpvOpULessThan:
-    case SpvOp::SpvOpSLessThan:
+    case spv::Op::OpULessThan:
+    case spv::Op::OpSLessThan:
     // >
-    case SpvOp::SpvOpUGreaterThan:
-    case SpvOp::SpvOpSGreaterThan:
+    case spv::Op::OpUGreaterThan:
+    case spv::Op::OpSGreaterThan:
 
     // >=
-    case SpvOp::SpvOpSGreaterThanEqual:
-    case SpvOp::SpvOpUGreaterThanEqual:
+    case spv::Op::OpSGreaterThanEqual:
+    case spv::Op::OpUGreaterThanEqual:
     // <=
-    case SpvOp::SpvOpSLessThanEqual:
-    case SpvOp::SpvOpULessThanEqual:
+    case spv::Op::OpSLessThanEqual:
+    case spv::Op::OpULessThanEqual:
 
       return true;
     default:
@@ -117,7 +114,8 @@
   }
 }
 
-int64_t Loop::GetResidualConditionValue(SpvOp condition, int64_t initial_value,
+int64_t Loop::GetResidualConditionValue(spv::Op condition,
+                                        int64_t initial_value,
                                         int64_t step_value,
                                         size_t number_of_iterations,
                                         size_t factor) {
@@ -128,13 +126,13 @@
   // loop where just less than or greater than. Adding or subtracting one should
   // give a functionally equivalent value.
   switch (condition) {
-    case SpvOp::SpvOpSGreaterThanEqual:
-    case SpvOp::SpvOpUGreaterThanEqual: {
+    case spv::Op::OpSGreaterThanEqual:
+    case spv::Op::OpUGreaterThanEqual: {
       remainder -= 1;
       break;
     }
-    case SpvOp::SpvOpSLessThanEqual:
-    case SpvOp::SpvOpULessThanEqual: {
+    case spv::Op::OpSLessThanEqual:
+    case spv::Op::OpULessThanEqual: {
       remainder += 1;
       break;
     }
@@ -152,7 +150,7 @@
   }
   Instruction* branch_conditional = &*condition_block->tail();
   if (!branch_conditional ||
-      branch_conditional->opcode() != SpvOpBranchConditional) {
+      branch_conditional->opcode() != spv::Op::OpBranchConditional) {
     return nullptr;
   }
   Instruction* condition_inst = context_->get_def_use_mgr()->GetDef(
@@ -318,7 +316,7 @@
 void Loop::SetPreHeaderBlock(BasicBlock* preheader) {
   if (preheader) {
     assert(!IsInsideLoop(preheader) && "The preheader block is in the loop");
-    assert(preheader->tail()->opcode() == SpvOpBranch &&
+    assert(preheader->tail()->opcode() == spv::Op::OpBranch &&
            "The preheader block does not unconditionally branch to the header "
            "block");
     assert(preheader->tail()->GetSingleWordOperand(0) ==
@@ -387,7 +385,7 @@
 
 namespace {
 
-static inline bool IsBasicBlockSafeToClone(IRContext* context, BasicBlock* bb) {
+inline bool IsBasicBlockSafeToClone(IRContext* context, BasicBlock* bb) {
   for (Instruction& inst : *bb) {
     if (!inst.IsBranch() && !context->IsCombinatorInstruction(&inst))
       return false;
@@ -443,7 +441,7 @@
                 BasicBlock* parent = ir_context->get_instr_block(use);
                 assert(parent && "Invalid analysis");
                 if (IsInsideLoop(parent)) return true;
-                if (use->opcode() != SpvOpPhi) return false;
+                if (use->opcode() != spv::Op::OpPhi) return false;
                 return exit_blocks.count(parent->id());
               }))
         return false;
@@ -452,25 +450,20 @@
   return true;
 }
 
-bool Loop::ShouldHoistInstruction(IRContext* context, Instruction* inst) {
-  return AreAllOperandsOutsideLoop(context, inst) &&
-         inst->IsOpcodeCodeMotionSafe();
+bool Loop::ShouldHoistInstruction(const Instruction& inst) const {
+  return inst.IsOpcodeCodeMotionSafe() && AreAllOperandsOutsideLoop(inst) &&
+         (!inst.IsLoad() || inst.IsReadOnlyLoad());
 }
 
-bool Loop::AreAllOperandsOutsideLoop(IRContext* context, Instruction* inst) {
-  analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
-  bool all_outside_loop = true;
+bool Loop::AreAllOperandsOutsideLoop(const Instruction& inst) const {
+  analysis::DefUseManager* def_use_mgr = GetContext()->get_def_use_mgr();
 
-  const std::function<void(uint32_t*)> operand_outside_loop =
-      [this, &def_use_mgr, &all_outside_loop](uint32_t* id) {
-        if (this->IsInsideLoop(def_use_mgr->GetDef(*id))) {
-          all_outside_loop = false;
-          return;
-        }
+  const std::function<bool(const uint32_t*)> operand_outside_loop =
+      [this, &def_use_mgr](const uint32_t* id) {
+        return !this->IsInsideLoop(def_use_mgr->GetDef(*id));
       };
 
-  inst->ForEachInId(operand_outside_loop);
-  return all_outside_loop;
+  return inst.WhileEachInId(operand_outside_loop);
 }
 
 void Loop::ComputeLoopStructuredOrder(
@@ -486,7 +479,7 @@
     ordered_loop_blocks->push_back(loop_preheader_);
 
   bool is_shader =
-      context_->get_feature_mgr()->HasCapability(SpvCapabilityShader);
+      context_->get_feature_mgr()->HasCapability(spv::Capability::Shader);
   if (!is_shader) {
     cfg.ForEachBlockInReversePostOrder(
         loop_header_, [ordered_loop_blocks, this](BasicBlock* bb) {
@@ -647,7 +640,7 @@
   const Instruction& branch = *bb->ctail();
 
   // Make sure the branch is a conditional branch.
-  if (branch.opcode() != SpvOpBranchConditional) return nullptr;
+  if (branch.opcode() != spv::Op::OpBranchConditional) return nullptr;
 
   // Make sure one of the two possible branches is to the merge block.
   if (branch.GetSingleWordInOperand(1) == loop_merge_->id() ||
@@ -716,7 +709,7 @@
   }
 
   // If this is a subtraction step we should negate the step value.
-  if (step_inst->opcode() == SpvOp::SpvOpISub) {
+  if (step_inst->opcode() == spv::Op::OpISub) {
     step_value = -step_value;
   }
 
@@ -753,7 +746,7 @@
 // |step_value| where diff is calculated differently according to the
 // |condition| and uses the |condition_value| and |init_value|. If diff /
 // |step_value| is NOT cleanly divisible then we add one to the sum.
-int64_t Loop::GetIterations(SpvOp condition, int64_t condition_value,
+int64_t Loop::GetIterations(spv::Op condition, int64_t condition_value,
                             int64_t init_value, int64_t step_value) const {
   if (step_value == 0) {
     return 0;
@@ -762,8 +755,8 @@
   int64_t diff = 0;
 
   switch (condition) {
-    case SpvOp::SpvOpSLessThan:
-    case SpvOp::SpvOpULessThan: {
+    case spv::Op::OpSLessThan:
+    case spv::Op::OpULessThan: {
       // If the condition is not met to begin with the loop will never iterate.
       if (!(init_value < condition_value)) return 0;
 
@@ -778,8 +771,8 @@
 
       break;
     }
-    case SpvOp::SpvOpSGreaterThan:
-    case SpvOp::SpvOpUGreaterThan: {
+    case spv::Op::OpSGreaterThan:
+    case spv::Op::OpUGreaterThan: {
       // If the condition is not met to begin with the loop will never iterate.
       if (!(init_value > condition_value)) return 0;
 
@@ -795,12 +788,12 @@
       break;
     }
 
-    case SpvOp::SpvOpSGreaterThanEqual:
-    case SpvOp::SpvOpUGreaterThanEqual: {
+    case spv::Op::OpSGreaterThanEqual:
+    case spv::Op::OpUGreaterThanEqual: {
       // If the condition is not met to begin with the loop will never iterate.
       if (!(init_value >= condition_value)) return 0;
 
-      // We subtract one to make it the same as SpvOpGreaterThan as it is
+      // We subtract one to make it the same as spv::Op::OpGreaterThan as it is
       // functionally equivalent.
       diff = init_value - (condition_value - 1);
 
@@ -814,13 +807,13 @@
       break;
     }
 
-    case SpvOp::SpvOpSLessThanEqual:
-    case SpvOp::SpvOpULessThanEqual: {
+    case spv::Op::OpSLessThanEqual:
+    case spv::Op::OpULessThanEqual: {
       // If the condition is not met to begin with the loop will never iterate.
       if (!(init_value <= condition_value)) return 0;
 
-      // We add one to make it the same as SpvOpLessThan as it is functionally
-      // equivalent.
+      // We add one to make it the same as spv::Op::OpLessThan as it is
+      // functionally equivalent.
       diff = (condition_value + 1) - init_value;
 
       // If the operation is a less than operation then the diff and step must
@@ -854,7 +847,7 @@
 void Loop::GetInductionVariables(
     std::vector<Instruction*>& induction_variables) const {
   for (Instruction& inst : *loop_header_) {
-    if (inst.opcode() == SpvOp::SpvOpPhi) {
+    if (inst.opcode() == spv::Op::OpPhi) {
       induction_variables.push_back(&inst);
     }
   }
@@ -867,7 +860,7 @@
 
   Instruction* induction = nullptr;
   // Verify that the branch instruction is a conditional branch.
-  if (branch_inst.opcode() == SpvOp::SpvOpBranchConditional) {
+  if (branch_inst.opcode() == spv::Op::OpBranchConditional) {
     // From the branch instruction find the branch condition.
     analysis::DefUseManager* def_use_manager = context_->get_def_use_mgr();
 
@@ -883,7 +876,8 @@
           def_use_manager->GetDef(condition->GetSingleWordOperand(2));
 
       // Make sure the variable instruction used is a phi.
-      if (!variable_inst || variable_inst->opcode() != SpvOpPhi) return nullptr;
+      if (!variable_inst || variable_inst->opcode() != spv::Op::OpPhi)
+        return nullptr;
 
       // Make sure the phi instruction only has two incoming blocks. Each
       // incoming block will be represented by two in operands in the phi
diff --git a/source/opt/loop_descriptor.h b/source/opt/loop_descriptor.h
index df01227..d451496 100644
--- a/source/opt/loop_descriptor.h
+++ b/source/opt/loop_descriptor.h
@@ -296,12 +296,12 @@
   // as a nested child loop.
   inline void SetParent(Loop* parent) { parent_ = parent; }
 
-  // Returns true is the instruction is invariant and safe to move wrt loop
-  bool ShouldHoistInstruction(IRContext* context, Instruction* inst);
+  // Returns true is the instruction is invariant and safe to move wrt loop.
+  bool ShouldHoistInstruction(const Instruction& inst) const;
 
   // Returns true if all operands of inst are in basic blocks not contained in
-  // loop
-  bool AreAllOperandsOutsideLoop(IRContext* context, Instruction* inst);
+  // loop.
+  bool AreAllOperandsOutsideLoop(const Instruction& inst) const;
 
   // Extract the initial value from the |induction| variable and store it in
   // |value|. If the function couldn't find the initial value of |induction|
@@ -316,12 +316,12 @@
   // Returns true if we can deduce the number of loop iterations in the step
   // operation |step|. IsSupportedCondition must also be true for the condition
   // instruction.
-  bool IsSupportedStepOp(SpvOp step) const;
+  bool IsSupportedStepOp(spv::Op step) const;
 
   // Returns true if we can deduce the number of loop iterations in the
   // condition operation |condition|. IsSupportedStepOp must also be true for
   // the step instruction.
-  bool IsSupportedCondition(SpvOp condition) const;
+  bool IsSupportedCondition(spv::Op condition) const;
 
   // Creates the list of the loop's basic block in structured order and store
   // the result in |ordered_loop_blocks|. If |include_pre_header| is true, the
@@ -335,7 +335,7 @@
   // Given the loop |condition|, |initial_value|, |step_value|, the trip count
   // |number_of_iterations|, and the |unroll_factor| requested, get the new
   // condition value for the residual loop.
-  static int64_t GetResidualConditionValue(SpvOp condition,
+  static int64_t GetResidualConditionValue(spv::Op condition,
                                            int64_t initial_value,
                                            int64_t step_value,
                                            size_t number_of_iterations,
@@ -400,7 +400,7 @@
   // the induction variable. This method will return the number of iterations in
   // a loop with those values for a given |condition|.  Returns 0 if the number
   // of iterations could not be computed.
-  int64_t GetIterations(SpvOp condition, int64_t condition_value,
+  int64_t GetIterations(spv::Op condition, int64_t condition_value,
                         int64_t init_value, int64_t step_value) const;
 
   // This is to allow for loops to be removed mid iteration without invalidating
diff --git a/source/opt/loop_fission.cpp b/source/opt/loop_fission.cpp
index b4df8c6..2ae05c3 100644
--- a/source/opt/loop_fission.cpp
+++ b/source/opt/loop_fission.cpp
@@ -110,10 +110,10 @@
 };
 
 bool LoopFissionImpl::MovableInstruction(const Instruction& inst) const {
-  return inst.opcode() == SpvOp::SpvOpLoad ||
-         inst.opcode() == SpvOp::SpvOpStore ||
-         inst.opcode() == SpvOp::SpvOpSelectionMerge ||
-         inst.opcode() == SpvOp::SpvOpPhi || inst.IsOpcodeCodeMotionSafe();
+  return inst.opcode() == spv::Op::OpLoad ||
+         inst.opcode() == spv::Op::OpStore ||
+         inst.opcode() == spv::Op::OpSelectionMerge ||
+         inst.opcode() == spv::Op::OpPhi || inst.IsOpcodeCodeMotionSafe();
 }
 
 void LoopFissionImpl::TraverseUseDef(Instruction* inst,
@@ -143,14 +143,14 @@
     // same labels (i.e phis). We already preempt the inclusion of
     // OpSelectionMerge by adding related instructions to the seen_instructions_
     // set.
-    if (user->opcode() == SpvOp::SpvOpLoopMerge ||
-        user->opcode() == SpvOp::SpvOpLabel)
+    if (user->opcode() == spv::Op::OpLoopMerge ||
+        user->opcode() == spv::Op::OpLabel)
       return;
 
     // If the |report_loads| flag is set, set the class field
     // load_used_in_condition_ to false. This is used to check that none of the
     // condition checks in the loop rely on loads.
-    if (user->opcode() == SpvOp::SpvOpLoad && report_loads) {
+    if (user->opcode() == spv::Op::OpLoad && report_loads) {
       load_used_in_condition_ = true;
     }
 
@@ -167,7 +167,7 @@
     user->ForEachInOperand(traverse_operand);
 
     // For the first traversal we want to ignore the users of the phi.
-    if (ignore_phi_users && user->opcode() == SpvOp::SpvOpPhi) return;
+    if (ignore_phi_users && user->opcode() == spv::Op::OpPhi) return;
 
     // Traverse each user with this lambda.
     def_use->ForEachUser(user, traverser_functor);
@@ -214,7 +214,7 @@
 
     for (Instruction& inst : block) {
       // Ignore all instructions related to control flow.
-      if (inst.opcode() == SpvOp::SpvOpSelectionMerge || inst.IsBranch()) {
+      if (inst.opcode() == spv::Op::OpSelectionMerge || inst.IsBranch()) {
         TraverseUseDef(&inst, &instructions_to_ignore, true, true);
       }
     }
@@ -229,8 +229,8 @@
 
     for (Instruction& inst : block) {
       // Record the order that each load/store is seen.
-      if (inst.opcode() == SpvOp::SpvOpLoad ||
-          inst.opcode() == SpvOp::SpvOpStore) {
+      if (inst.opcode() == spv::Op::OpLoad ||
+          inst.opcode() == spv::Op::OpStore) {
         instruction_order_[&inst] = instruction_order_.size();
       }
 
@@ -292,9 +292,9 @@
 
   // Populate the above lists.
   for (Instruction* inst : cloned_loop_instructions_) {
-    if (inst->opcode() == SpvOp::SpvOpStore) {
+    if (inst->opcode() == spv::Op::OpStore) {
       set_one_stores.push_back(inst);
-    } else if (inst->opcode() == SpvOp::SpvOpLoad) {
+    } else if (inst->opcode() == spv::Op::OpLoad) {
       set_one_loads.push_back(inst);
     }
 
@@ -316,7 +316,7 @@
 
     // Look at the dependency between the loads in the original and stores in
     // the cloned loops.
-    if (inst->opcode() == SpvOp::SpvOpLoad) {
+    if (inst->opcode() == spv::Op::OpLoad) {
       for (Instruction* store : set_one_stores) {
         DistanceVector vec{loop_depth};
 
@@ -334,7 +334,7 @@
           }
         }
       }
-    } else if (inst->opcode() == SpvOp::SpvOpStore) {
+    } else if (inst->opcode() == spv::Op::OpStore) {
       for (Instruction* load : set_one_loads) {
         DistanceVector vec{loop_depth};
 
@@ -387,7 +387,7 @@
       if (cloned_loop_instructions_.count(&inst) == 1 &&
           original_loop_instructions_.count(&inst) == 0) {
         instructions_to_kill.push_back(&inst);
-        if (inst.opcode() == SpvOp::SpvOpPhi) {
+        if (inst.opcode() == spv::Op::OpPhi) {
           context_->ReplaceAllUsesWith(
               inst.result_id(), clone_results.value_map_[inst.result_id()]);
         }
diff --git a/source/opt/loop_fusion.cpp b/source/opt/loop_fusion.cpp
index f3aab28..dc63553 100644
--- a/source/opt/loop_fusion.cpp
+++ b/source/opt/loop_fusion.cpp
@@ -23,7 +23,6 @@
 
 namespace spvtools {
 namespace opt {
-
 namespace {
 
 // Append all the loops nested in |loop| to |loops|.
@@ -193,14 +192,15 @@
   // in LCSSA form.
   for (auto block : block_to_check) {
     for (auto& inst : *block) {
-      if (inst.opcode() == SpvOpStore) {
+      if (inst.opcode() == spv::Op::OpStore) {
         // Get the definition of the target to check it's function scope so
         // there are no observable side effects.
         auto variable =
             context_->get_def_use_mgr()->GetDef(inst.GetSingleWordInOperand(0));
 
-        if (variable->opcode() != SpvOpVariable ||
-            variable->GetSingleWordInOperand(0) != SpvStorageClassFunction) {
+        if (variable->opcode() != spv::Op::OpVariable ||
+            spv::StorageClass(variable->GetSingleWordInOperand(0)) !=
+                spv::StorageClass::Function) {
           return false;
         }
 
@@ -209,7 +209,7 @@
         context_->get_def_use_mgr()->ForEachUse(
             inst.GetSingleWordInOperand(0),
             [&is_used](Instruction* use_inst, uint32_t) {
-              if (use_inst->opcode() == SpvOpLoad) {
+              if (use_inst->opcode() == spv::Op::OpLoad) {
                 is_used = true;
               }
             });
@@ -217,11 +217,11 @@
         if (is_used) {
           return false;
         }
-      } else if (inst.opcode() == SpvOpPhi) {
+      } else if (inst.opcode() == spv::Op::OpPhi) {
         if (inst.NumInOperands() != 2) {
           return false;
         }
-      } else if (inst.opcode() != SpvOpBranch) {
+      } else if (inst.opcode() != spv::Op::OpBranch) {
         return false;
       }
     }
@@ -234,10 +234,12 @@
   for (const auto& block : loop->GetBlocks()) {
     for (const auto& inst : *containing_function_->FindBlock(block)) {
       auto opcode = inst.opcode();
-      if (opcode == SpvOpFunctionCall || opcode == SpvOpControlBarrier ||
-          opcode == SpvOpMemoryBarrier || opcode == SpvOpTypeNamedBarrier ||
-          opcode == SpvOpNamedBarrierInitialize ||
-          opcode == SpvOpMemoryNamedBarrier) {
+      if (opcode == spv::Op::OpFunctionCall ||
+          opcode == spv::Op::OpControlBarrier ||
+          opcode == spv::Op::OpMemoryBarrier ||
+          opcode == spv::Op::OpTypeNamedBarrier ||
+          opcode == spv::Op::OpNamedBarrierInitialize ||
+          opcode == spv::Op::OpMemoryNamedBarrier) {
         return true;
       }
     }
@@ -344,7 +346,7 @@
     auto access_location = context_->get_def_use_mgr()->GetDef(
         instruction->GetSingleWordInOperand(0));
 
-    while (access_location->opcode() == SpvOpAccessChain) {
+    while (access_location->opcode() == spv::Op::OpAccessChain) {
       access_location = context_->get_def_use_mgr()->GetDef(
           access_location->GetSingleWordInOperand(0));
     }
@@ -366,9 +368,9 @@
     }
 
     for (auto& instruction : *containing_function_->FindBlock(block_id)) {
-      if (instruction.opcode() == SpvOpLoad) {
+      if (instruction.opcode() == spv::Op::OpLoad) {
         loads.push_back(&instruction);
-      } else if (instruction.opcode() == SpvOpStore) {
+      } else if (instruction.opcode() == spv::Op::OpStore) {
         stores.push_back(&instruction);
       }
     }
@@ -556,7 +558,7 @@
   // Update merge block id in the header of |loop_0_| to the merge block of
   // |loop_1_|.
   loop_0_->GetHeaderBlock()->ForEachInst([this](Instruction* inst) {
-    if (inst->opcode() == SpvOpLoopMerge) {
+    if (inst->opcode() == spv::Op::OpLoopMerge) {
       inst->SetInOperand(0, {loop_1_->GetMergeBlock()->id()});
     }
   });
@@ -564,7 +566,7 @@
   // Update condition branch target in |loop_0_| to the merge block of
   // |loop_1_|.
   condition_block_of_0->ForEachInst([this](Instruction* inst) {
-    if (inst->opcode() == SpvOpBranchConditional) {
+    if (inst->opcode() == spv::Op::OpBranchConditional) {
       auto loop_0_merge_block_id = loop_0_->GetMergeBlock()->id();
 
       if (inst->GetSingleWordInOperand(1) == loop_0_merge_block_id) {
@@ -579,7 +581,8 @@
   // the header of |loop_1_| to the header of |loop_0_|.
   std::vector<Instruction*> instructions_to_move{};
   for (auto& instruction : *loop_1_->GetHeaderBlock()) {
-    if (instruction.opcode() == SpvOpPhi && &instruction != induction_1_) {
+    if (instruction.opcode() == spv::Op::OpPhi &&
+        &instruction != induction_1_) {
       instructions_to_move.push_back(&instruction);
     }
   }
diff --git a/source/opt/loop_fusion_pass.cpp b/source/opt/loop_fusion_pass.cpp
index bd8444a..097430f 100644
--- a/source/opt/loop_fusion_pass.cpp
+++ b/source/opt/loop_fusion_pass.cpp
@@ -14,7 +14,6 @@
 
 #include "source/opt/loop_fusion_pass.h"
 
-#include "source/opt/ir_context.h"
 #include "source/opt/loop_descriptor.h"
 #include "source/opt/loop_fusion.h"
 #include "source/opt/register_pressure.h"
diff --git a/source/opt/loop_peeling.cpp b/source/opt/loop_peeling.cpp
index 34f0a8d..25c6db1 100644
--- a/source/opt/loop_peeling.cpp
+++ b/source/opt/loop_peeling.cpp
@@ -12,23 +12,37 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <algorithm>
+#include "source/opt/loop_peeling.h"
+
 #include <functional>
 #include <memory>
-#include <unordered_map>
 #include <unordered_set>
 #include <vector>
 
 #include "source/opt/ir_builder.h"
 #include "source/opt/ir_context.h"
 #include "source/opt/loop_descriptor.h"
-#include "source/opt/loop_peeling.h"
 #include "source/opt/loop_utils.h"
 #include "source/opt/scalar_analysis.h"
 #include "source/opt/scalar_analysis_nodes.h"
 
 namespace spvtools {
 namespace opt {
+namespace {
+// Gather the set of blocks for all the path from |entry| to |root|.
+void GetBlocksInPath(uint32_t block, uint32_t entry,
+                     std::unordered_set<uint32_t>* blocks_in_path,
+                     const CFG& cfg) {
+  for (uint32_t pid : cfg.preds(block)) {
+    if (blocks_in_path->insert(pid).second) {
+      if (pid != entry) {
+        GetBlocksInPath(pid, entry, blocks_in_path, cfg);
+      }
+    }
+  }
+}
+}  // namespace
+
 size_t LoopPeelingPass::code_grow_threshold_ = 1000;
 
 void LoopPeeling::DuplicateAndConnectLoop(
@@ -186,7 +200,7 @@
   operations->insert(iterator);
   iterator->ForEachInId([def_use_mgr, loop, operations, this](uint32_t* id) {
     Instruction* insn = def_use_mgr->GetDef(*id);
-    if (insn->opcode() == SpvOpLabel) {
+    if (insn->opcode() == spv::Op::OpLabel) {
       return;
     }
     if (operations->count(insn)) {
@@ -199,19 +213,6 @@
   });
 }
 
-// Gather the set of blocks for all the path from |entry| to |root|.
-static void GetBlocksInPath(uint32_t block, uint32_t entry,
-                            std::unordered_set<uint32_t>* blocks_in_path,
-                            const CFG& cfg) {
-  for (uint32_t pid : cfg.preds(block)) {
-    if (blocks_in_path->insert(pid).second) {
-      if (pid != entry) {
-        GetBlocksInPath(pid, entry, blocks_in_path, cfg);
-      }
-    }
-  }
-}
-
 bool LoopPeeling::IsConditionCheckSideEffectFree() const {
   CFG& cfg = *context_->cfg();
 
@@ -231,9 +232,9 @@
       if (!bb->WhileEachInst([this](Instruction* insn) {
             if (insn->IsBranch()) return true;
             switch (insn->opcode()) {
-              case SpvOpLabel:
-              case SpvOpSelectionMerge:
-              case SpvOpLoopMerge:
+              case spv::Op::OpLabel:
+              case spv::Op::OpSelectionMerge:
+              case spv::Op::OpLoopMerge:
                 return true;
               default:
                 break;
@@ -322,7 +323,7 @@
 
   BasicBlock* condition_block = cfg.block(condition_block_id);
   Instruction* exit_condition = condition_block->terminator();
-  assert(exit_condition->opcode() == SpvOpBranchConditional);
+  assert(exit_condition->opcode() == spv::Op::OpBranchConditional);
   BasicBlock::iterator insert_point = condition_block->tail();
   if (condition_block->GetMergeInst()) {
     --insert_point;
@@ -350,7 +351,7 @@
   // TODO(1841): Handle id overflow.
   std::unique_ptr<BasicBlock> new_bb =
       MakeUnique<BasicBlock>(std::unique_ptr<Instruction>(new Instruction(
-          context_, SpvOpLabel, 0, context_->TakeNextId(), {})));
+          context_, spv::Op::OpLabel, 0, context_->TakeNextId(), {})));
   // Update the loop descriptor.
   Loop* in_loop = (*loop_utils_.GetLoopDescriptor())[bb];
   if (in_loop) {
@@ -791,18 +792,18 @@
   return 0;
 }
 
-static bool IsHandledCondition(SpvOp opcode) {
+static bool IsHandledCondition(spv::Op opcode) {
   switch (opcode) {
-    case SpvOpIEqual:
-    case SpvOpINotEqual:
-    case SpvOpUGreaterThan:
-    case SpvOpSGreaterThan:
-    case SpvOpUGreaterThanEqual:
-    case SpvOpSGreaterThanEqual:
-    case SpvOpULessThan:
-    case SpvOpSLessThan:
-    case SpvOpULessThanEqual:
-    case SpvOpSLessThanEqual:
+    case spv::Op::OpIEqual:
+    case spv::Op::OpINotEqual:
+    case spv::Op::OpUGreaterThan:
+    case spv::Op::OpSGreaterThan:
+    case spv::Op::OpUGreaterThanEqual:
+    case spv::Op::OpSGreaterThanEqual:
+    case spv::Op::OpULessThan:
+    case spv::Op::OpSLessThan:
+    case spv::Op::OpULessThanEqual:
+    case spv::Op::OpSLessThanEqual:
       return true;
     default:
       return false;
@@ -811,7 +812,7 @@
 
 LoopPeelingPass::LoopPeelingInfo::Direction
 LoopPeelingPass::LoopPeelingInfo::GetPeelingInfo(BasicBlock* bb) const {
-  if (bb->terminator()->opcode() != SpvOpBranchConditional) {
+  if (bb->terminator()->opcode() != spv::Op::OpBranchConditional) {
     return GetNoneDirection();
   }
 
@@ -886,27 +887,27 @@
   switch (condition->opcode()) {
     default:
       return GetNoneDirection();
-    case SpvOpIEqual:
-    case SpvOpINotEqual:
+    case spv::Op::OpIEqual:
+    case spv::Op::OpINotEqual:
       return HandleEquality(lhs, rhs);
-    case SpvOpUGreaterThan:
-    case SpvOpSGreaterThan: {
+    case spv::Op::OpUGreaterThan:
+    case spv::Op::OpSGreaterThan: {
       cmp_operator = CmpOperator::kGT;
       break;
     }
-    case SpvOpULessThan:
-    case SpvOpSLessThan: {
+    case spv::Op::OpULessThan:
+    case spv::Op::OpSLessThan: {
       cmp_operator = CmpOperator::kLT;
       break;
     }
     // We add one to transform >= into > and <= into <.
-    case SpvOpUGreaterThanEqual:
-    case SpvOpSGreaterThanEqual: {
+    case spv::Op::OpUGreaterThanEqual:
+    case spv::Op::OpSGreaterThanEqual: {
       cmp_operator = CmpOperator::kGE;
       break;
     }
-    case SpvOpULessThanEqual:
-    case SpvOpSLessThanEqual: {
+    case spv::Op::OpULessThanEqual:
+    case spv::Op::OpSLessThanEqual: {
       cmp_operator = CmpOperator::kLE;
       break;
     }
diff --git a/source/opt/loop_unroller.cpp b/source/opt/loop_unroller.cpp
index 6f4e6f4..d9e34f2 100644
--- a/source/opt/loop_unroller.cpp
+++ b/source/opt/loop_unroller.cpp
@@ -15,7 +15,6 @@
 #include "source/opt/loop_unroller.h"
 
 #include <limits>
-#include <map>
 #include <memory>
 #include <unordered_map>
 #include <utility>
@@ -68,10 +67,10 @@
 namespace {
 
 // Loop control constant value for DontUnroll flag.
-static const uint32_t kLoopControlDontUnrollIndex = 2;
+constexpr uint32_t kLoopControlDontUnrollIndex = 2;
 
 // Operand index of the loop control parameter of the OpLoopMerge.
-static const uint32_t kLoopControlIndex = 2;
+constexpr uint32_t kLoopControlIndex = 2;
 
 // This utility class encapsulates some of the state we need to maintain between
 // loop unrolls. Specifically it maintains key blocks and the induction variable
@@ -336,8 +335,7 @@
 
 // Retrieve the index of the OpPhi instruction |phi| which corresponds to the
 // incoming |block| id.
-static uint32_t GetPhiIndexFromLabel(const BasicBlock* block,
-                                     const Instruction* phi) {
+uint32_t GetPhiIndexFromLabel(const BasicBlock* block, const Instruction* phi) {
   for (uint32_t i = 1; i < phi->NumInOperands(); i += 2) {
     if (block->id() == phi->GetSingleWordInOperand(i)) {
       return i;
@@ -382,7 +380,7 @@
                                                           size_t factor) {
   // TODO(1841): Handle id overflow.
   std::unique_ptr<Instruction> new_label{new Instruction(
-      context_, SpvOp::SpvOpLabel, 0, context_->TakeNextId(), {})};
+      context_, spv::Op::OpLabel, 0, context_->TakeNextId(), {})};
   std::unique_ptr<BasicBlock> new_exit_bb{new BasicBlock(std::move(new_label))};
   new_exit_bb->SetParent(&function_);
 
@@ -991,7 +989,7 @@
 
   // Check that we can find and process the induction variable.
   const Instruction* induction = loop_->FindConditionVariable(condition);
-  if (!induction || induction->opcode() != SpvOpPhi) return false;
+  if (!induction || induction->opcode() != spv::Op::OpPhi) return false;
 
   // Check that we can find the number of loop iterations.
   if (!loop_->FindNumberOfIterations(induction, &*condition->ctail(), nullptr))
@@ -1002,7 +1000,7 @@
   // iteration counts. This can cause timeouts and memouts during fuzzing that
   // are not classed as bugs. To avoid this noise, loop unrolling is not applied
   // to loops with large iteration counts when fuzzing.
-  const size_t kFuzzerIterationLimit = 100;
+  constexpr size_t kFuzzerIterationLimit = 100;
   size_t num_iterations;
   loop_->FindNumberOfIterations(induction, &*condition->ctail(),
                                 &num_iterations);
@@ -1015,7 +1013,7 @@
   // block.
   const Instruction& branch = *loop_->GetLatchBlock()->ctail();
   bool branching_assumption =
-      branch.opcode() == SpvOpBranch &&
+      branch.opcode() == spv::Op::OpBranch &&
       branch.GetSingleWordInOperand(0) == loop_->GetHeaderBlock()->id();
   if (!branching_assumption) {
     return false;
@@ -1043,10 +1041,10 @@
   // exit the loop.
   for (uint32_t label_id : loop_->GetBlocks()) {
     const BasicBlock* block = context_->cfg()->block(label_id);
-    if (block->ctail()->opcode() == SpvOp::SpvOpKill ||
-        block->ctail()->opcode() == SpvOp::SpvOpReturn ||
-        block->ctail()->opcode() == SpvOp::SpvOpReturnValue ||
-        block->ctail()->opcode() == SpvOp::SpvOpTerminateInvocation) {
+    if (block->ctail()->opcode() == spv::Op::OpKill ||
+        block->ctail()->opcode() == spv::Op::OpReturn ||
+        block->ctail()->opcode() == spv::Op::OpReturnValue ||
+        block->ctail()->opcode() == spv::Op::OpTerminateInvocation) {
       return false;
     }
   }
diff --git a/source/opt/loop_unswitch_pass.cpp b/source/opt/loop_unswitch_pass.cpp
index 1ee7e5e..41f1a80 100644
--- a/source/opt/loop_unswitch_pass.cpp
+++ b/source/opt/loop_unswitch_pass.cpp
@@ -17,7 +17,6 @@
 #include <functional>
 #include <list>
 #include <memory>
-#include <type_traits>
 #include <unordered_map>
 #include <unordered_set>
 #include <utility>
@@ -31,18 +30,12 @@
 #include "source/opt/ir_builder.h"
 #include "source/opt/ir_context.h"
 #include "source/opt/loop_descriptor.h"
-
 #include "source/opt/loop_utils.h"
 
 namespace spvtools {
 namespace opt {
 namespace {
-
-static const uint32_t kTypePointerStorageClassInIdx = 0;
-
-}  // anonymous namespace
-
-namespace {
+constexpr uint32_t kTypePointerStorageClassInIdx = 0;
 
 // This class handle the unswitch procedure for a given loop.
 // The unswitch will not happen if:
@@ -77,7 +70,7 @@
       }
 
       if (bb->terminator()->IsBranch() &&
-          bb->terminator()->opcode() != SpvOpBranch) {
+          bb->terminator()->opcode() != spv::Op::OpBranch) {
         if (IsConditionNonConstantLoopInvariant(bb->terminator())) {
           switch_block_ = bb;
           break;
@@ -104,7 +97,7 @@
     // TODO(1841): Handle id overflow.
     BasicBlock* bb = &*ip.InsertBefore(std::unique_ptr<BasicBlock>(
         new BasicBlock(std::unique_ptr<Instruction>(new Instruction(
-            context_, SpvOpLabel, 0, context_->TakeNextId(), {})))));
+            context_, spv::Op::OpLabel, 0, context_->TakeNextId(), {})))));
     bb->SetParent(function_);
     def_use_mgr->AnalyzeInstDef(bb->GetLabelInst());
     context_->set_instr_block(bb->GetLabelInst(), bb);
@@ -113,7 +106,7 @@
   }
 
   Instruction* GetValueForDefaultPathForSwitch(Instruction* switch_inst) {
-    assert(switch_inst->opcode() == SpvOpSwitch &&
+    assert(switch_inst->opcode() == spv::Op::OpSwitch &&
            "The given instructoin must be an OpSwitch.");
 
     // Find a value that can be used to select the default path.
@@ -291,7 +284,7 @@
     /////////////////////////////
 
     Instruction* iv_condition = &*switch_block_->tail();
-    SpvOp iv_opcode = iv_condition->opcode();
+    spv::Op iv_opcode = iv_condition->opcode();
     Instruction* condition =
         def_use_mgr->GetDef(iv_condition->GetOperand(0).words[0]);
 
@@ -304,7 +297,7 @@
     std::vector<std::pair<Instruction*, BasicBlock*>> constant_branch;
     // Special case for the original loop
     Instruction* original_loop_constant_value;
-    if (iv_opcode == SpvOpBranchConditional) {
+    if (iv_opcode == spv::Op::OpBranchConditional) {
       constant_branch.emplace_back(
           cst_mgr->GetDefiningInstruction(cst_mgr->GetConstant(cond_type, {0})),
           nullptr);
@@ -401,7 +394,7 @@
     // Delete the old jump
     context_->KillInst(&*if_block->tail());
     InstructionBuilder builder(context_, if_block);
-    if (iv_opcode == SpvOpBranchConditional) {
+    if (iv_opcode == spv::Op::OpBranchConditional) {
       assert(constant_branch.size() == 1);
       builder.AddConditionalBranch(
           condition->result_id(), original_loop_target->id(),
@@ -509,7 +502,8 @@
     bool& is_uniform = dynamically_uniform_[var->result_id()];
     is_uniform = false;
 
-    dec_mgr->WhileEachDecoration(var->result_id(), SpvDecorationUniform,
+    dec_mgr->WhileEachDecoration(var->result_id(),
+                                 uint32_t(spv::Decoration::Uniform),
                                  [&is_uniform](const Instruction&) {
                                    is_uniform = true;
                                    return false;
@@ -526,14 +520,14 @@
     if (!post_dom_tree.Dominates(parent->id(), entry->id())) {
       return is_uniform = false;
     }
-    if (var->opcode() == SpvOpLoad) {
+    if (var->opcode() == spv::Op::OpLoad) {
       const uint32_t PtrTypeId =
           def_use_mgr->GetDef(var->GetSingleWordInOperand(0))->type_id();
       const Instruction* PtrTypeInst = def_use_mgr->GetDef(PtrTypeId);
-      uint32_t storage_class =
-          PtrTypeInst->GetSingleWordInOperand(kTypePointerStorageClassInIdx);
-      if (storage_class != SpvStorageClassUniform &&
-          storage_class != SpvStorageClassUniformConstant) {
+      auto storage_class = spv::StorageClass(
+          PtrTypeInst->GetSingleWordInOperand(kTypePointerStorageClassInIdx));
+      if (storage_class != spv::StorageClass::Uniform &&
+          storage_class != spv::StorageClass::UniformConstant) {
         return is_uniform = false;
       }
     } else {
@@ -553,7 +547,7 @@
   // dynamically uniform.
   bool IsConditionNonConstantLoopInvariant(Instruction* insn) {
     assert(insn->IsBranch());
-    assert(insn->opcode() != SpvOpBranch);
+    assert(insn->opcode() != spv::Op::OpBranch);
     analysis::DefUseManager* def_use_mgr = context_->get_def_use_mgr();
 
     Instruction* condition = def_use_mgr->GetDef(insn->GetOperand(0).words[0]);
diff --git a/source/opt/loop_utils.cpp b/source/opt/loop_utils.cpp
index 8c6d355..20494e1 100644
--- a/source/opt/loop_utils.cpp
+++ b/source/opt/loop_utils.cpp
@@ -28,12 +28,11 @@
 
 namespace spvtools {
 namespace opt {
-
 namespace {
 // Return true if |bb| is dominated by at least one block in |exits|
-static inline bool DominatesAnExit(BasicBlock* bb,
-                                   const std::unordered_set<BasicBlock*>& exits,
-                                   const DominatorTree& dom_tree) {
+inline bool DominatesAnExit(BasicBlock* bb,
+                            const std::unordered_set<BasicBlock*>& exits,
+                            const DominatorTree& dom_tree) {
   for (BasicBlock* e_bb : exits)
     if (dom_tree.Dominates(bb, e_bb)) return true;
   return false;
@@ -71,10 +70,10 @@
     // UpdateManagers.
     void RewriteUse(BasicBlock* bb, Instruction* user, uint32_t operand_index) {
       assert(
-          (user->opcode() != SpvOpPhi || bb != GetParent(user)) &&
+          (user->opcode() != spv::Op::OpPhi || bb != GetParent(user)) &&
           "The root basic block must be the incoming edge if |user| is a phi "
           "instruction");
-      assert((user->opcode() == SpvOpPhi || bb == GetParent(user)) &&
+      assert((user->opcode() == spv::Op::OpPhi || bb == GetParent(user)) &&
              "The root basic block must be the instruction parent if |user| is "
              "not "
              "phi instruction");
@@ -293,7 +292,7 @@
             assert(use_parent);
             if (blocks.count(use_parent->id())) return;
 
-            if (use->opcode() == SpvOpPhi) {
+            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)) {
@@ -355,7 +354,7 @@
     // TODO(1841): Handle id overflow.
     BasicBlock& exit = *insert_pt.InsertBefore(std::unique_ptr<BasicBlock>(
         new BasicBlock(std::unique_ptr<Instruction>(new Instruction(
-            context_, SpvOpLabel, 0, context_->TakeNextId(), {})))));
+            context_, spv::Op::OpLabel, 0, context_->TakeNextId(), {})))));
     exit.SetParent(function);
 
     // Redirect in loop predecessors to |exit| block.
@@ -494,7 +493,7 @@
   // Create a new exit block/label for the new loop.
   // TODO(1841): Handle id overflow.
   std::unique_ptr<Instruction> new_label{new Instruction(
-      context_, SpvOp::SpvOpLabel, 0, context_->TakeNextId(), {})};
+      context_, spv::Op::OpLabel, 0, context_->TakeNextId(), {})};
   std::unique_ptr<BasicBlock> new_exit_bb{new BasicBlock(std::move(new_label))};
   new_exit_bb->SetParent(loop_->GetMergeBlock()->GetParent());
 
@@ -680,9 +679,9 @@
     const BasicBlock* bb = cfg.block(id);
     size_t bb_size = 0;
     bb->ForEachInst([&bb_size](const Instruction* insn) {
-      if (insn->opcode() == SpvOpLabel) return;
+      if (insn->opcode() == spv::Op::OpLabel) return;
       if (insn->IsNop()) return;
-      if (insn->opcode() == SpvOpPhi) return;
+      if (insn->opcode() == spv::Op::OpPhi) return;
       bb_size++;
     });
     block_sizes_[bb->id()] = bb_size;
diff --git a/source/opt/mem_pass.cpp b/source/opt/mem_pass.cpp
index ca4889b..9972c4f 100644
--- a/source/opt/mem_pass.cpp
+++ b/source/opt/mem_pass.cpp
@@ -22,32 +22,27 @@
 
 #include "source/cfa.h"
 #include "source/opt/basic_block.h"
-#include "source/opt/dominator_analysis.h"
 #include "source/opt/ir_context.h"
-#include "source/opt/iterator.h"
 
 namespace spvtools {
 namespace opt {
-
 namespace {
-
-const uint32_t kCopyObjectOperandInIdx = 0;
-const uint32_t kTypePointerStorageClassInIdx = 0;
-const uint32_t kTypePointerTypeIdInIdx = 1;
-
+constexpr uint32_t kCopyObjectOperandInIdx = 0;
+constexpr uint32_t kTypePointerStorageClassInIdx = 0;
+constexpr uint32_t kTypePointerTypeIdInIdx = 1;
 }  // namespace
 
 bool MemPass::IsBaseTargetType(const Instruction* typeInst) const {
   switch (typeInst->opcode()) {
-    case SpvOpTypeInt:
-    case SpvOpTypeFloat:
-    case SpvOpTypeBool:
-    case SpvOpTypeVector:
-    case SpvOpTypeMatrix:
-    case SpvOpTypeImage:
-    case SpvOpTypeSampler:
-    case SpvOpTypeSampledImage:
-    case SpvOpTypePointer:
+    case spv::Op::OpTypeInt:
+    case spv::Op::OpTypeFloat:
+    case spv::Op::OpTypeBool:
+    case spv::Op::OpTypeVector:
+    case spv::Op::OpTypeMatrix:
+    case spv::Op::OpTypeImage:
+    case spv::Op::OpTypeSampler:
+    case spv::Op::OpTypeSampledImage:
+    case spv::Op::OpTypePointer:
       return true;
     default:
       break;
@@ -57,14 +52,14 @@
 
 bool MemPass::IsTargetType(const Instruction* typeInst) const {
   if (IsBaseTargetType(typeInst)) return true;
-  if (typeInst->opcode() == SpvOpTypeArray) {
+  if (typeInst->opcode() == spv::Op::OpTypeArray) {
     if (!IsTargetType(
             get_def_use_mgr()->GetDef(typeInst->GetSingleWordOperand(1)))) {
       return false;
     }
     return true;
   }
-  if (typeInst->opcode() != SpvOpTypeStruct) return false;
+  if (typeInst->opcode() != spv::Op::OpTypeStruct) return false;
   // All struct members must be math type
   return typeInst->WhileEachInId([this](const uint32_t* tid) {
     Instruction* compTypeInst = get_def_use_mgr()->GetDef(*tid);
@@ -73,23 +68,29 @@
   });
 }
 
-bool MemPass::IsNonPtrAccessChain(const SpvOp opcode) const {
-  return opcode == SpvOpAccessChain || opcode == SpvOpInBoundsAccessChain;
+bool MemPass::IsNonPtrAccessChain(const spv::Op opcode) const {
+  return opcode == spv::Op::OpAccessChain ||
+         opcode == spv::Op::OpInBoundsAccessChain;
 }
 
 bool MemPass::IsPtr(uint32_t ptrId) {
   uint32_t varId = ptrId;
   Instruction* ptrInst = get_def_use_mgr()->GetDef(varId);
-  while (ptrInst->opcode() == SpvOpCopyObject) {
+  if (ptrInst->opcode() == spv::Op::OpFunction) {
+    // A function is not a pointer, but it's return type could be, which will
+    // erroneously lead to this function returning true later on
+    return false;
+  }
+  while (ptrInst->opcode() == spv::Op::OpCopyObject) {
     varId = ptrInst->GetSingleWordInOperand(kCopyObjectOperandInIdx);
     ptrInst = get_def_use_mgr()->GetDef(varId);
   }
-  const SpvOp op = ptrInst->opcode();
-  if (op == SpvOpVariable || IsNonPtrAccessChain(op)) return true;
+  const spv::Op op = ptrInst->opcode();
+  if (op == spv::Op::OpVariable || 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() == SpvOpTypePointer;
+  return varTypeInst->opcode() == spv::Op::OpTypePointer;
 }
 
 Instruction* MemPass::GetPtr(uint32_t ptrId, uint32_t* varId) {
@@ -97,24 +98,24 @@
   Instruction* ptrInst = get_def_use_mgr()->GetDef(*varId);
   Instruction* varInst;
 
-  if (ptrInst->opcode() == SpvOpConstantNull) {
+  if (ptrInst->opcode() == spv::Op::OpConstantNull) {
     *varId = 0;
     return ptrInst;
   }
 
-  if (ptrInst->opcode() != SpvOpVariable &&
-      ptrInst->opcode() != SpvOpFunctionParameter) {
+  if (ptrInst->opcode() != spv::Op::OpVariable &&
+      ptrInst->opcode() != spv::Op::OpFunctionParameter) {
     varInst = ptrInst->GetBaseAddress();
   } else {
     varInst = ptrInst;
   }
-  if (varInst->opcode() == SpvOpVariable) {
+  if (varInst->opcode() == spv::Op::OpVariable) {
     *varId = varInst->result_id();
   } else {
     *varId = 0;
   }
 
-  while (ptrInst->opcode() == SpvOpCopyObject) {
+  while (ptrInst->opcode() == spv::Op::OpCopyObject) {
     uint32_t temp = ptrInst->GetSingleWordInOperand(0);
     ptrInst = get_def_use_mgr()->GetDef(temp);
   }
@@ -123,8 +124,9 @@
 }
 
 Instruction* MemPass::GetPtr(Instruction* ip, uint32_t* varId) {
-  assert(ip->opcode() == SpvOpStore || ip->opcode() == SpvOpLoad ||
-         ip->opcode() == SpvOpImageTexelPointer || ip->IsAtomicWithLoad());
+  assert(ip->opcode() == spv::Op::OpStore || ip->opcode() == spv::Op::OpLoad ||
+         ip->opcode() == spv::Op::OpImageTexelPointer ||
+         ip->IsAtomicWithLoad());
 
   // All of these opcode place the pointer in position 0.
   const uint32_t ptrId = ip->GetSingleWordInOperand(0);
@@ -133,8 +135,8 @@
 
 bool MemPass::HasOnlyNamesAndDecorates(uint32_t id) const {
   return get_def_use_mgr()->WhileEachUser(id, [this](Instruction* user) {
-    SpvOp op = user->opcode();
-    if (op != SpvOpName && !IsNonTypeDecorate(op)) {
+    spv::Op op = user->opcode();
+    if (op != spv::Op::OpName && !IsNonTypeDecorate(op)) {
       return false;
     }
     return true;
@@ -147,14 +149,15 @@
 
 bool MemPass::HasLoads(uint32_t varId) const {
   return !get_def_use_mgr()->WhileEachUser(varId, [this](Instruction* user) {
-    SpvOp op = user->opcode();
+    spv::Op op = user->opcode();
     // TODO(): The following is slightly conservative. Could be
     // better handling of non-store/name.
-    if (IsNonPtrAccessChain(op) || op == SpvOpCopyObject) {
+    if (IsNonPtrAccessChain(op) || op == spv::Op::OpCopyObject) {
       if (HasLoads(user->result_id())) {
         return false;
       }
-    } else if (op != SpvOpStore && op != SpvOpName && !IsNonTypeDecorate(op)) {
+    } else if (op != spv::Op::OpStore && op != spv::Op::OpName &&
+               !IsNonTypeDecorate(op)) {
       return false;
     }
     return true;
@@ -164,12 +167,12 @@
 bool MemPass::IsLiveVar(uint32_t varId) const {
   const Instruction* varInst = get_def_use_mgr()->GetDef(varId);
   // assume live if not a variable eg. function parameter
-  if (varInst->opcode() != SpvOpVariable) return true;
+  if (varInst->opcode() != spv::Op::OpVariable) return true;
   // non-function scope vars are live
   const uint32_t varTypeId = varInst->type_id();
   const Instruction* varTypeInst = get_def_use_mgr()->GetDef(varTypeId);
-  if (varTypeInst->GetSingleWordInOperand(kTypePointerStorageClassInIdx) !=
-      SpvStorageClassFunction)
+  if (spv::StorageClass(varTypeInst->GetSingleWordInOperand(
+          kTypePointerStorageClassInIdx)) != spv::StorageClass::Function)
     return true;
   // test if variable is loaded from
   return HasLoads(varId);
@@ -177,10 +180,10 @@
 
 void MemPass::AddStores(uint32_t ptr_id, std::queue<Instruction*>* insts) {
   get_def_use_mgr()->ForEachUser(ptr_id, [this, insts](Instruction* user) {
-    SpvOp op = user->opcode();
+    spv::Op op = user->opcode();
     if (IsNonPtrAccessChain(op)) {
       AddStores(user->result_id(), insts);
-    } else if (op == SpvOpStore) {
+    } else if (op == spv::Op::OpStore) {
       insts->push(user);
     }
   });
@@ -193,7 +196,7 @@
   while (!deadInsts.empty()) {
     Instruction* di = deadInsts.front();
     // Don't delete labels
-    if (di->opcode() == SpvOpLabel) {
+    if (di->opcode() == spv::Op::OpLabel) {
       deadInsts.pop();
       continue;
     }
@@ -202,7 +205,7 @@
     di->ForEachInId([&ids](uint32_t* iid) { ids.insert(*iid); });
     uint32_t varId = 0;
     // Remember variable if dead load
-    if (di->opcode() == SpvOpLoad) (void)GetPtr(di, &varId);
+    if (di->opcode() == spv::Op::OpLoad) (void)GetPtr(di, &varId);
     if (call_back) {
       call_back(di);
     }
@@ -230,9 +233,9 @@
         dbg_op == CommonDebugInfoDebugValue) {
       return true;
     }
-    SpvOp op = user->opcode();
-    if (op != SpvOpStore && op != SpvOpLoad && op != SpvOpName &&
-        !IsNonTypeDecorate(op)) {
+    spv::Op op = user->opcode();
+    if (op != spv::Op::OpStore && op != spv::Op::OpLoad &&
+        op != spv::Op::OpName && !IsNonTypeDecorate(op)) {
       return false;
     }
     return true;
@@ -248,7 +251,7 @@
   }
 
   std::unique_ptr<Instruction> undef_inst(
-      new Instruction(context(), SpvOpUndef, type_id, undefId, {}));
+      new Instruction(context(), spv::Op::OpUndef, type_id, undefId, {}));
   get_def_use_mgr()->AnalyzeInstDefUse(&*undef_inst);
   get_module()->AddGlobalValue(std::move(undef_inst));
   type2undefs_[type_id] = undefId;
@@ -264,11 +267,11 @@
     return false;
   if (seen_target_vars_.find(varId) != seen_target_vars_.end()) return true;
   const Instruction* varInst = get_def_use_mgr()->GetDef(varId);
-  if (varInst->opcode() != SpvOpVariable) return false;
+  if (varInst->opcode() != spv::Op::OpVariable) return false;
   const uint32_t varTypeId = varInst->type_id();
   const Instruction* varTypeInst = get_def_use_mgr()->GetDef(varTypeId);
-  if (varTypeInst->GetSingleWordInOperand(kTypePointerStorageClassInIdx) !=
-      SpvStorageClassFunction) {
+  if (spv::StorageClass(varTypeInst->GetSingleWordInOperand(
+          kTypePointerStorageClassInIdx)) != spv::StorageClass::Function) {
     seen_non_target_vars_.insert(varId);
     return false;
   }
@@ -489,8 +492,8 @@
   for (auto& blk : *func) {
     for (auto& inst : blk) {
       switch (inst.opcode()) {
-        case SpvOpStore:
-        case SpvOpLoad: {
+        case spv::Op::OpStore:
+        case spv::Op::OpLoad: {
           uint32_t varId;
           (void)GetPtr(&inst, &varId);
           if (!IsTargetVar(varId)) break;
diff --git a/source/opt/mem_pass.h b/source/opt/mem_pass.h
index 5a77670..aef9e5f 100644
--- a/source/opt/mem_pass.h
+++ b/source/opt/mem_pass.h
@@ -80,7 +80,7 @@
   bool IsTargetType(const Instruction* typeInst) const;
 
   // Returns true if |opcode| is a non-ptr access chain op
-  bool IsNonPtrAccessChain(const SpvOp opcode) const;
+  bool IsNonPtrAccessChain(const spv::Op opcode) const;
 
   // Given the id |ptrId|, return true if the top-most non-CopyObj is
   // a variable, a non-ptr access chain or a parameter of pointer type.
@@ -117,8 +117,8 @@
   bool CFGCleanup(Function* func);
 
   // Return true if |op| is supported decorate.
-  inline bool IsNonTypeDecorate(uint32_t op) const {
-    return (op == SpvOpDecorate || op == SpvOpDecorateId);
+  inline bool IsNonTypeDecorate(spv::Op op) const {
+    return (op == spv::Op::OpDecorate || op == spv::Op::OpDecorateId);
   }
 
   // Return the id of an undef value with type |type_id|.  Create and insert an
diff --git a/source/opt/merge_return_pass.cpp b/source/opt/merge_return_pass.cpp
index 7710dea..c262ea0 100644
--- a/source/opt/merge_return_pass.cpp
+++ b/source/opt/merge_return_pass.cpp
@@ -30,7 +30,7 @@
 
 Pass::Status MergeReturnPass::Process() {
   bool is_shader =
-      context()->get_feature_mgr()->HasCapability(SpvCapabilityShader);
+      context()->get_feature_mgr()->HasCapability(spv::Capability::Shader);
 
   bool failed = false;
   ProcessFunction pfn = [&failed, is_shader, this](Function* function) {
@@ -74,16 +74,16 @@
 
 void MergeReturnPass::GenerateState(BasicBlock* block) {
   if (Instruction* mergeInst = block->GetMergeInst()) {
-    if (mergeInst->opcode() == SpvOpLoopMerge) {
+    if (mergeInst->opcode() == spv::Op::OpLoopMerge) {
       // If new loop, break to this loop merge block
       state_.emplace_back(mergeInst, mergeInst);
     } else {
       auto branchInst = mergeInst->NextNode();
-      if (branchInst->opcode() == SpvOpSwitch) {
+      if (branchInst->opcode() == spv::Op::OpSwitch) {
         // If switch inside of loop, break to innermost loop merge block.
         // Otherwise need to break to this switch merge block.
         auto lastMergeInst = state_.back().BreakMergeInst();
-        if (lastMergeInst && lastMergeInst->opcode() == SpvOpLoopMerge)
+        if (lastMergeInst && lastMergeInst->opcode() == spv::Op::OpLoopMerge)
           state_.emplace_back(lastMergeInst, mergeInst);
         else
           state_.emplace_back(mergeInst, mergeInst);
@@ -174,7 +174,7 @@
 void MergeReturnPass::CreateReturnBlock() {
   // Create a label for the new return block
   std::unique_ptr<Instruction> return_label(
-      new Instruction(context(), SpvOpLabel, 0u, TakeNextId(), {}));
+      new Instruction(context(), spv::Op::OpLabel, 0u, TakeNextId(), {}));
 
   // Create the new basic block
   std::unique_ptr<BasicBlock> return_block(
@@ -195,37 +195,41 @@
     // Load and return the final return value
     uint32_t loadId = TakeNextId();
     block->AddInstruction(MakeUnique<Instruction>(
-        context(), SpvOpLoad, function_->type_id(), loadId,
+        context(), spv::Op::OpLoad, function_->type_id(), loadId,
         std::initializer_list<Operand>{
             {SPV_OPERAND_TYPE_ID, {return_value_->result_id()}}}));
     Instruction* var_inst = block->terminator();
     context()->AnalyzeDefUse(var_inst);
     context()->set_instr_block(var_inst, block);
     context()->get_decoration_mgr()->CloneDecorations(
-        return_value_->result_id(), loadId, {SpvDecorationRelaxedPrecision});
+        return_value_->result_id(), loadId,
+        {spv::Decoration::RelaxedPrecision});
 
     block->AddInstruction(MakeUnique<Instruction>(
-        context(), SpvOpReturnValue, 0, 0,
+        context(), spv::Op::OpReturnValue, 0, 0,
         std::initializer_list<Operand>{{SPV_OPERAND_TYPE_ID, {loadId}}}));
     context()->AnalyzeDefUse(block->terminator());
     context()->set_instr_block(block->terminator(), block);
   } else {
-    block->AddInstruction(MakeUnique<Instruction>(context(), SpvOpReturn));
+    block->AddInstruction(
+        MakeUnique<Instruction>(context(), spv::Op::OpReturn));
     context()->AnalyzeDefUse(block->terminator());
     context()->set_instr_block(block->terminator(), block);
   }
 }
 
 void MergeReturnPass::ProcessStructuredBlock(BasicBlock* block) {
-  SpvOp tail_opcode = block->tail()->opcode();
-  if (tail_opcode == SpvOpReturn || tail_opcode == SpvOpReturnValue) {
+  spv::Op tail_opcode = block->tail()->opcode();
+  if (tail_opcode == spv::Op::OpReturn ||
+      tail_opcode == spv::Op::OpReturnValue) {
     if (!return_flag_) {
       AddReturnFlag();
     }
   }
 
-  if (tail_opcode == SpvOpReturn || tail_opcode == SpvOpReturnValue ||
-      tail_opcode == SpvOpUnreachable) {
+  if (tail_opcode == spv::Op::OpReturn ||
+      tail_opcode == spv::Op::OpReturnValue ||
+      tail_opcode == spv::Op::OpUnreachable) {
     assert(CurrentState().InBreakable() &&
            "Should be in the placeholder construct.");
     BranchToBlock(block, CurrentState().BreakMergeId());
@@ -234,8 +238,8 @@
 }
 
 void MergeReturnPass::BranchToBlock(BasicBlock* block, uint32_t target) {
-  if (block->tail()->opcode() == SpvOpReturn ||
-      block->tail()->opcode() == SpvOpReturnValue) {
+  if (block->tail()->opcode() == spv::Op::OpReturn ||
+      block->tail()->opcode() == spv::Op::OpReturnValue) {
     RecordReturned(block);
     RecordReturnValue(block);
   }
@@ -247,7 +251,7 @@
   UpdatePhiNodes(block, target_block);
 
   Instruction* return_inst = block->terminator();
-  return_inst->SetOpcode(SpvOpBranch);
+  return_inst->SetOpcode(spv::Op::OpBranch);
   return_inst->ReplaceOperands({{SPV_OPERAND_TYPE_ID, {target}}});
   context()->get_def_use_mgr()->AnalyzeInstDefUse(return_inst);
   new_edges_[target_block].insert(block->id());
@@ -276,7 +280,7 @@
         &inst,
         [&users_to_update, &dom_tree, &inst, inst_bb, this](Instruction* user) {
           BasicBlock* user_bb = nullptr;
-          if (user->opcode() != SpvOpPhi) {
+          if (user->opcode() != spv::Op::OpPhi) {
             user_bb = context()->get_instr_block(user);
           } else {
             // For OpPhi, the use should be considered to be in the predecessor.
@@ -325,15 +329,16 @@
     // instruction.  If not, the Spir-V will be invalid.
     Instruction* inst_type = get_def_use_mgr()->GetDef(inst.type_id());
     bool regenerateInstruction = false;
-    if (inst_type->opcode() == SpvOpTypePointer) {
+    if (inst_type->opcode() == spv::Op::OpTypePointer) {
       if (!context()->get_feature_mgr()->HasCapability(
-              SpvCapabilityVariablePointers)) {
+              spv::Capability::VariablePointers)) {
         regenerateInstruction = true;
       }
 
-      uint32_t storage_class = inst_type->GetSingleWordInOperand(0);
-      if (storage_class != SpvStorageClassWorkgroup &&
-          storage_class != SpvStorageClassStorageBuffer) {
+      auto storage_class =
+          spv::StorageClass(inst_type->GetSingleWordInOperand(0));
+      if (storage_class != spv::StorageClass::Workgroup &&
+          storage_class != spv::StorageClass::StorageBuffer) {
         regenerateInstruction = true;
       }
     }
@@ -343,7 +348,7 @@
       uint32_t new_id = TakeNextId();
       regen_inst->SetResultId(new_id);
       Instruction* insert_pos = &*merge_block->begin();
-      while (insert_pos->opcode() == SpvOpPhi) {
+      while (insert_pos->opcode() == spv::Op::OpPhi) {
         insert_pos = insert_pos->NextNode();
       }
       new_phi = insert_pos->InsertBefore(std::move(regen_inst));
@@ -459,7 +464,7 @@
 
   // Leave the phi instructions behind.
   auto iter = block->begin();
-  while (iter->opcode() == SpvOpPhi) {
+  while (iter->opcode() == spv::Op::OpPhi) {
     ++iter;
   }
 
@@ -478,7 +483,7 @@
 
   // If |block| was a continue target for a loop |old_body| is now the correct
   // continue target.
-  if (break_merge_inst->opcode() == SpvOpLoopMerge &&
+  if (break_merge_inst->opcode() == spv::Op::OpLoopMerge &&
       break_merge_inst->GetSingleWordInOperand(1) == block->id()) {
     break_merge_inst->SetInOperand(1, {old_body->id()});
     context()->UpdateDefUse(break_merge_inst);
@@ -531,8 +536,8 @@
 }
 
 void MergeReturnPass::RecordReturned(BasicBlock* block) {
-  if (block->tail()->opcode() != SpvOpReturn &&
-      block->tail()->opcode() != SpvOpReturnValue)
+  if (block->tail()->opcode() != spv::Op::OpReturn &&
+      block->tail()->opcode() != spv::Op::OpReturnValue)
     return;
 
   assert(return_flag_ && "Did not generate the return flag variable.");
@@ -550,7 +555,7 @@
   }
 
   std::unique_ptr<Instruction> return_store(new Instruction(
-      context(), SpvOpStore, 0, 0,
+      context(), spv::Op::OpStore, 0, 0,
       std::initializer_list<Operand>{
           {SPV_OPERAND_TYPE_ID, {return_flag_->result_id()}},
           {SPV_OPERAND_TYPE_ID, {constant_true_->result_id()}}}));
@@ -563,7 +568,7 @@
 
 void MergeReturnPass::RecordReturnValue(BasicBlock* block) {
   auto terminator = *block->tail();
-  if (terminator.opcode() != SpvOpReturnValue) {
+  if (terminator.opcode() != spv::Op::OpReturnValue) {
     return;
   }
 
@@ -571,7 +576,7 @@
          "Did not generate the variable to hold the return value.");
 
   std::unique_ptr<Instruction> value_store(new Instruction(
-      context(), SpvOpStore, 0, 0,
+      context(), spv::Op::OpStore, 0, 0,
       std::initializer_list<Operand>{
           {SPV_OPERAND_TYPE_ID, {return_value_->result_id()}},
           {SPV_OPERAND_TYPE_ID, {terminator.GetSingleWordInOperand(0u)}}}));
@@ -586,17 +591,19 @@
   if (return_value_) return;
 
   uint32_t return_type_id = function_->type_id();
-  if (get_def_use_mgr()->GetDef(return_type_id)->opcode() == SpvOpTypeVoid)
+  if (get_def_use_mgr()->GetDef(return_type_id)->opcode() ==
+      spv::Op::OpTypeVoid)
     return;
 
   uint32_t return_ptr_type = context()->get_type_mgr()->FindPointerToType(
-      return_type_id, SpvStorageClassFunction);
+      return_type_id, spv::StorageClass::Function);
 
   uint32_t var_id = TakeNextId();
-  std::unique_ptr<Instruction> returnValue(new Instruction(
-      context(), SpvOpVariable, return_ptr_type, var_id,
-      std::initializer_list<Operand>{
-          {SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}));
+  std::unique_ptr<Instruction> returnValue(
+      new Instruction(context(), spv::Op::OpVariable, return_ptr_type, var_id,
+                      std::initializer_list<Operand>{
+                          {SPV_OPERAND_TYPE_STORAGE_CLASS,
+                           {uint32_t(spv::StorageClass::Function)}}}));
 
   auto insert_iter = function_->begin()->begin();
   insert_iter.InsertBefore(std::move(returnValue));
@@ -606,7 +613,7 @@
   context()->set_instr_block(return_value_, entry_block);
 
   context()->get_decoration_mgr()->CloneDecorations(
-      function_->result_id(), var_id, {SpvDecorationRelaxedPrecision});
+      function_->result_id(), var_id, {spv::Decoration::RelaxedPrecision});
 }
 
 void MergeReturnPass::AddReturnFlag() {
@@ -625,14 +632,14 @@
       const_mgr->GetDefiningInstruction(false_const)->result_id();
 
   uint32_t bool_ptr_id =
-      type_mgr->FindPointerToType(bool_id, SpvStorageClassFunction);
+      type_mgr->FindPointerToType(bool_id, spv::StorageClass::Function);
 
   uint32_t var_id = TakeNextId();
   std::unique_ptr<Instruction> returnFlag(new Instruction(
-      context(), SpvOpVariable, bool_ptr_id, var_id,
-      std::initializer_list<Operand>{
-          {SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}},
-          {SPV_OPERAND_TYPE_ID, {const_false_id}}}));
+      context(), spv::Op::OpVariable, bool_ptr_id, var_id,
+      std::initializer_list<Operand>{{SPV_OPERAND_TYPE_STORAGE_CLASS,
+                                      {uint32_t(spv::StorageClass::Function)}},
+                                     {SPV_OPERAND_TYPE_ID, {const_false_id}}}));
 
   auto insert_iter = function_->begin()->begin();
 
@@ -648,8 +655,8 @@
   std::vector<BasicBlock*> return_blocks;
   for (auto& block : *function) {
     Instruction& terminator = *block.tail();
-    if (terminator.opcode() == SpvOpReturn ||
-        terminator.opcode() == SpvOpReturnValue) {
+    if (terminator.opcode() == spv::Op::OpReturn ||
+        terminator.opcode() == spv::Op::OpReturnValue) {
       return_blocks.push_back(&block);
     }
   }
@@ -670,7 +677,7 @@
   // Create new return.
   std::vector<Operand> phi_ops;
   for (auto block : return_blocks) {
-    if (block->tail()->opcode() == SpvOpReturnValue) {
+    if (block->tail()->opcode() == spv::Op::OpReturnValue) {
       phi_ops.push_back(
           {SPV_OPERAND_TYPE_ID, {block->tail()->GetSingleWordInOperand(0u)}});
       phi_ops.push_back({SPV_OPERAND_TYPE_ID, {block->id()}});
@@ -682,12 +689,12 @@
     uint32_t phi_result_id = TakeNextId();
     uint32_t phi_type_id = function->type_id();
     std::unique_ptr<Instruction> phi_inst(new Instruction(
-        context(), SpvOpPhi, phi_type_id, phi_result_id, phi_ops));
+        context(), spv::Op::OpPhi, phi_type_id, phi_result_id, phi_ops));
     ret_block_iter->AddInstruction(std::move(phi_inst));
     BasicBlock::iterator phiIter = ret_block_iter->tail();
 
     std::unique_ptr<Instruction> return_inst(
-        new Instruction(context(), SpvOpReturnValue, 0u, 0u,
+        new Instruction(context(), spv::Op::OpReturnValue, 0u, 0u,
                         {{SPV_OPERAND_TYPE_ID, {phi_result_id}}}));
     ret_block_iter->AddInstruction(std::move(return_inst));
     BasicBlock::iterator ret = ret_block_iter->tail();
@@ -697,14 +704,14 @@
     get_def_use_mgr()->AnalyzeInstDef(&*ret);
   } else {
     std::unique_ptr<Instruction> return_inst(
-        new Instruction(context(), SpvOpReturn));
+        new Instruction(context(), spv::Op::OpReturn));
     ret_block_iter->AddInstruction(std::move(return_inst));
   }
 
   // Replace returns with branches
   for (auto block : return_blocks) {
     context()->ForgetUses(block->terminator());
-    block->tail()->SetOpcode(SpvOpBranch);
+    block->tail()->SetOpcode(spv::Op::OpBranch);
     block->tail()->ReplaceOperands({{SPV_OPERAND_TYPE_ID, {return_id}}});
     get_def_use_mgr()->AnalyzeInstUse(block->terminator());
     get_def_use_mgr()->AnalyzeInstUse(block->GetLabelInst());
@@ -789,7 +796,7 @@
 
 BasicBlock* MergeReturnPass::CreateContinueTarget(uint32_t header_label_id) {
   std::unique_ptr<Instruction> label(
-      new Instruction(context(), SpvOpLabel, 0u, TakeNextId(), {}));
+      new Instruction(context(), spv::Op::OpLabel, 0u, TakeNextId(), {}));
 
   // Create the new basic block
   std::unique_ptr<BasicBlock> block(new BasicBlock(std::move(label)));
@@ -824,7 +831,7 @@
   // block to make sure the OpVariable instructions remain in the entry block.
   BasicBlock* start_block = &*function_->begin();
   auto split_pos = start_block->begin();
-  while (split_pos->opcode() == SpvOpVariable) {
+  while (split_pos->opcode() == spv::Op::OpVariable) {
     ++split_pos;
   }
 
@@ -865,7 +872,7 @@
     if (struct_cfg_analysis->IsContinueBlock(bb.id())) {
       // |bb| must be an empty block ending with a branch to the header.
       Instruction* inst = &*bb.begin();
-      if (inst->opcode() != SpvOpBranch) {
+      if (inst->opcode() != spv::Op::OpBranch) {
         return true;
       }
 
@@ -875,7 +882,7 @@
       }
     } else if (struct_cfg_analysis->IsMergeBlock(bb.id())) {
       // |bb| must be an empty block ending with OpUnreachable.
-      if (bb.begin()->opcode() != SpvOpUnreachable) {
+      if (bb.begin()->opcode() != spv::Op::OpUnreachable) {
         return true;
       }
     } else {
diff --git a/source/opt/module.cpp b/source/opt/module.cpp
index c98af8f..a9710c6 100644
--- a/source/opt/module.cpp
+++ b/source/opt/module.cpp
@@ -69,14 +69,14 @@
   return const_insts;
 }
 
-uint32_t Module::GetGlobalValue(SpvOp opcode) const {
+uint32_t Module::GetGlobalValue(spv::Op opcode) const {
   for (auto& inst : types_values_) {
     if (inst.opcode() == opcode) return inst.result_id();
   }
   return 0;
 }
 
-void Module::AddGlobalValue(SpvOp opcode, uint32_t result_id,
+void Module::AddGlobalValue(spv::Op opcode, uint32_t result_id,
                             uint32_t type_id) {
   std::unique_ptr<Instruction> newGlobal(
       new Instruction(context(), opcode, type_id, result_id, {}));
@@ -159,7 +159,6 @@
     if (between_merge_and_branch && i->IsLineInst()) {
       return;
     }
-    between_merge_and_branch = false;
     if (last_line_inst != nullptr) {
       // If the current instruction is OpLine or DebugLine and it is the same
       // as the last line instruction that is still effective (can be applied
@@ -181,28 +180,30 @@
                                      ->get_feature_mgr()
                                      ->GetExtInstImportId_Shader100DebugInfo();
         if (shader_set_id != 0) {
-          binary->push_back((5 << 16) | static_cast<uint16_t>(SpvOpExtInst));
+          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);
         } else {
-          binary->push_back((1 << 16) | static_cast<uint16_t>(SpvOpNoLine));
+          binary->push_back((1 << 16) |
+                            static_cast<uint16_t>(spv::Op::OpNoLine));
         }
         last_line_inst = nullptr;
       }
     }
 
-    if (opcode == SpvOpLabel) {
+    if (opcode == spv::Op::OpLabel) {
       between_label_and_phi_var = true;
-    } else if (opcode != SpvOpVariable && opcode != SpvOpPhi &&
+    } else if (opcode != spv::Op::OpVariable && opcode != spv::Op::OpPhi &&
                !spvtools::opt::IsOpLineInst(opcode)) {
       between_label_and_phi_var = false;
     }
 
     if (!(skip_nop && i->IsNop())) {
       const auto& scope = i->GetDebugScope();
-      if (scope != last_scope) {
+      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.
@@ -221,9 +222,11 @@
       i->ToBinaryWithoutAttachedDebugInsts(binary);
     }
     // Update the last line instruction.
+    between_merge_and_branch = false;
     if (spvOpcodeIsBlockTerminator(opcode) || i->IsNoLine()) {
       last_line_inst = nullptr;
-    } else if (opcode == SpvOpLoopMerge || opcode == SpvOpSelectionMerge) {
+    } else if (opcode == spv::Op::OpLoopMerge ||
+               opcode == spv::Op::OpSelectionMerge) {
       between_merge_and_branch = true;
       last_line_inst = nullptr;
     } else if (i->IsLine()) {
@@ -272,7 +275,7 @@
 std::ostream& operator<<(std::ostream& str, const Module& module) {
   module.ForEachInst([&str](const Instruction* inst) {
     str << *inst;
-    if (inst->opcode() != SpvOpFunctionEnd) {
+    if (inst->opcode() != spv::Op::OpFunctionEnd) {
       str << std::endl;
     }
   });
diff --git a/source/opt/module.h b/source/opt/module.h
index 7a6be46..98c16dc 100644
--- a/source/opt/module.h
+++ b/source/opt/module.h
@@ -17,6 +17,7 @@
 
 #include <functional>
 #include <memory>
+#include <string_view>
 #include <unordered_map>
 #include <utility>
 #include <vector>
@@ -119,6 +120,9 @@
   // Appends a constant, global variable, or OpUndef instruction to this module.
   inline void AddGlobalValue(std::unique_ptr<Instruction> v);
 
+  // Prepends a function declaration to this module.
+  inline void AddFunctionDeclaration(std::unique_ptr<Function> f);
+
   // Appends a function to this module.
   inline void AddFunction(std::unique_ptr<Function> f);
 
@@ -136,10 +140,10 @@
   std::vector<const Instruction*> GetConstants() const;
 
   // Return result id of global value with |opcode|, 0 if not present.
-  uint32_t GetGlobalValue(SpvOp opcode) const;
+  uint32_t GetGlobalValue(spv::Op opcode) const;
 
   // Add global value with |opcode|, |result_id| and |type_id|
-  void AddGlobalValue(SpvOp opcode, uint32_t result_id, uint32_t type_id);
+  void AddGlobalValue(spv::Op opcode, uint32_t result_id, uint32_t type_id);
 
   inline uint32_t id_bound() const { return header_.bound; }
 
@@ -379,6 +383,11 @@
   types_values_.push_back(std::move(v));
 }
 
+inline void Module::AddFunctionDeclaration(std::unique_ptr<Function> f) {
+  // function declarations must come before function definitions.
+  functions_.emplace(functions_.begin(), std::move(f));
+}
+
 inline void Module::AddFunction(std::unique_ptr<Function> f) {
   functions_.emplace_back(std::move(f));
 }
diff --git a/source/opt/optimizer.cpp b/source/opt/optimizer.cpp
index 381589b..675bd1b 100644
--- a/source/opt/optimizer.cpp
+++ b/source/opt/optimizer.cpp
@@ -15,6 +15,7 @@
 #include "spirv-tools/optimizer.hpp"
 
 #include <cassert>
+#include <charconv>
 #include <memory>
 #include <string>
 #include <unordered_map>
@@ -60,6 +61,7 @@
 
   spv_target_env target_env;      // Target environment.
   opt::PassManager pass_manager;  // Internal implementation pass manager.
+  std::unordered_set<uint32_t> live_locs;  // Arg to debug dead output passes
 };
 
 Optimizer::Optimizer(spv_target_env env) : impl_(new Impl(env)) {
@@ -108,7 +110,7 @@
 // The legalization problem is essentially a very general copy propagation
 // problem.  The optimization we use are all used to either do copy propagation
 // or enable more copy propagation.
-Optimizer& Optimizer::RegisterLegalizationPasses() {
+Optimizer& Optimizer::RegisterLegalizationPasses(bool preserve_interface) {
   return
       // Wrap OpKill instructions so all other code can be inlined.
       RegisterPass(CreateWrapOpKillPass())
@@ -128,16 +130,16 @@
           // Propagate the value stored to the loads in very simple cases.
           .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
           .RegisterPass(CreateLocalSingleStoreElimPass())
-          .RegisterPass(CreateAggressiveDCEPass())
+          .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
           // Split up aggregates so they are easier to deal with.
           .RegisterPass(CreateScalarReplacementPass(0))
           // Remove loads and stores so everything is in intermediate values.
           // Takes care of copy propagation of non-members.
           .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
           .RegisterPass(CreateLocalSingleStoreElimPass())
-          .RegisterPass(CreateAggressiveDCEPass())
+          .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
           .RegisterPass(CreateLocalMultiStoreElimPass())
-          .RegisterPass(CreateAggressiveDCEPass())
+          .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
           // Propagate constants to get as many constant conditions on branches
           // as possible.
           .RegisterPass(CreateCCPPass())
@@ -146,7 +148,7 @@
           // Copy propagate members.  Cleans up code sequences generated by
           // scalar replacement.  Also important for removing OpPhi nodes.
           .RegisterPass(CreateSimplificationPass())
-          .RegisterPass(CreateAggressiveDCEPass())
+          .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
           .RegisterPass(CreateCopyPropagateArraysPass())
           // May need loop unrolling here see
           // https://github.com/Microsoft/DirectXShaderCompiler/pull/930
@@ -155,30 +157,35 @@
           .RegisterPass(CreateVectorDCEPass())
           .RegisterPass(CreateDeadInsertElimPass())
           .RegisterPass(CreateReduceLoadSizePass())
-          .RegisterPass(CreateAggressiveDCEPass())
-          .RegisterPass(CreateInterpolateFixupPass());
+          .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
+          .RegisterPass(CreateInterpolateFixupPass())
+          .RegisterPass(CreateInvocationInterlockPlacementPass());
 }
 
-Optimizer& Optimizer::RegisterPerformancePasses() {
+Optimizer& Optimizer::RegisterLegalizationPasses() {
+  return RegisterLegalizationPasses(false);
+}
+
+Optimizer& Optimizer::RegisterPerformancePasses(bool preserve_interface) {
   return RegisterPass(CreateWrapOpKillPass())
       .RegisterPass(CreateDeadBranchElimPass())
       .RegisterPass(CreateMergeReturnPass())
       .RegisterPass(CreateInlineExhaustivePass())
       .RegisterPass(CreateEliminateDeadFunctionsPass())
-      .RegisterPass(CreateAggressiveDCEPass())
+      .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
       .RegisterPass(CreatePrivateToLocalPass())
       .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
       .RegisterPass(CreateLocalSingleStoreElimPass())
-      .RegisterPass(CreateAggressiveDCEPass())
+      .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
       .RegisterPass(CreateScalarReplacementPass())
       .RegisterPass(CreateLocalAccessChainConvertPass())
       .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
       .RegisterPass(CreateLocalSingleStoreElimPass())
-      .RegisterPass(CreateAggressiveDCEPass())
+      .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
       .RegisterPass(CreateLocalMultiStoreElimPass())
-      .RegisterPass(CreateAggressiveDCEPass())
+      .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
       .RegisterPass(CreateCCPPass())
-      .RegisterPass(CreateAggressiveDCEPass())
+      .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
       .RegisterPass(CreateLoopUnrollPass(true))
       .RegisterPass(CreateDeadBranchElimPass())
       .RegisterPass(CreateRedundancyEliminationPass())
@@ -188,9 +195,9 @@
       .RegisterPass(CreateLocalAccessChainConvertPass())
       .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
       .RegisterPass(CreateLocalSingleStoreElimPass())
-      .RegisterPass(CreateAggressiveDCEPass())
+      .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
       .RegisterPass(CreateSSARewritePass())
-      .RegisterPass(CreateAggressiveDCEPass())
+      .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
       .RegisterPass(CreateVectorDCEPass())
       .RegisterPass(CreateDeadInsertElimPass())
       .RegisterPass(CreateDeadBranchElimPass())
@@ -198,7 +205,7 @@
       .RegisterPass(CreateIfConversionPass())
       .RegisterPass(CreateCopyPropagateArraysPass())
       .RegisterPass(CreateReduceLoadSizePass())
-      .RegisterPass(CreateAggressiveDCEPass())
+      .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
       .RegisterPass(CreateBlockMergePass())
       .RegisterPass(CreateRedundancyEliminationPass())
       .RegisterPass(CreateDeadBranchElimPass())
@@ -206,7 +213,11 @@
       .RegisterPass(CreateSimplificationPass());
 }
 
-Optimizer& Optimizer::RegisterSizePasses() {
+Optimizer& Optimizer::RegisterPerformancePasses() {
+  return RegisterPerformancePasses(false);
+}
+
+Optimizer& Optimizer::RegisterSizePasses(bool preserve_interface) {
   return RegisterPass(CreateWrapOpKillPass())
       .RegisterPass(CreateDeadBranchElimPass())
       .RegisterPass(CreateMergeReturnPass())
@@ -223,12 +234,12 @@
       .RegisterPass(CreateLocalSingleStoreElimPass())
       .RegisterPass(CreateIfConversionPass())
       .RegisterPass(CreateSimplificationPass())
-      .RegisterPass(CreateAggressiveDCEPass())
+      .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
       .RegisterPass(CreateDeadBranchElimPass())
       .RegisterPass(CreateBlockMergePass())
       .RegisterPass(CreateLocalAccessChainConvertPass())
       .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
-      .RegisterPass(CreateAggressiveDCEPass())
+      .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
       .RegisterPass(CreateCopyPropagateArraysPass())
       .RegisterPass(CreateVectorDCEPass())
       .RegisterPass(CreateDeadInsertElimPass())
@@ -238,10 +249,12 @@
       .RegisterPass(CreateLocalMultiStoreElimPass())
       .RegisterPass(CreateRedundancyEliminationPass())
       .RegisterPass(CreateSimplificationPass())
-      .RegisterPass(CreateAggressiveDCEPass())
+      .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
       .RegisterPass(CreateCFGCleanupPass());
 }
 
+Optimizer& Optimizer::RegisterSizePasses() { return RegisterSizePasses(false); }
+
 bool Optimizer::RegisterPassesFromFlags(const std::vector<std::string>& flags) {
   for (const auto& flag : flags) {
     if (!RegisterPassFromFlag(flag)) {
@@ -418,27 +431,16 @@
     RegisterPass(CreateWorkaround1209Pass());
   } else if (pass_name == "replace-invalid-opcode") {
     RegisterPass(CreateReplaceInvalidOpcodePass());
-  } else if (pass_name == "inst-bindless-check") {
-    RegisterPass(CreateInstBindlessCheckPass(7, 23, false, false));
+  } else if (pass_name == "inst-bindless-check" ||
+             pass_name == "inst-desc-idx-check" ||
+             pass_name == "inst-buff-oob-check") {
+    // preserve legacy names
+    RegisterPass(CreateInstBindlessCheckPass(23));
     RegisterPass(CreateSimplificationPass());
     RegisterPass(CreateDeadBranchElimPass());
     RegisterPass(CreateBlockMergePass());
-    RegisterPass(CreateAggressiveDCEPass(true));
-  } else if (pass_name == "inst-desc-idx-check") {
-    RegisterPass(CreateInstBindlessCheckPass(7, 23, true, true));
-    RegisterPass(CreateSimplificationPass());
-    RegisterPass(CreateDeadBranchElimPass());
-    RegisterPass(CreateBlockMergePass());
-    RegisterPass(CreateAggressiveDCEPass(true));
-  } else if (pass_name == "inst-buff-oob-check") {
-    RegisterPass(CreateInstBindlessCheckPass(7, 23, false, false, true, true));
-    RegisterPass(CreateSimplificationPass());
-    RegisterPass(CreateDeadBranchElimPass());
-    RegisterPass(CreateBlockMergePass());
-    RegisterPass(CreateAggressiveDCEPass(true));
   } else if (pass_name == "inst-buff-addr-check") {
-    RegisterPass(CreateInstBuffAddrCheckPass(7, 23));
-    RegisterPass(CreateAggressiveDCEPass(true));
+    RegisterPass(CreateInstBuffAddrCheckPass(23));
   } else if (pass_name == "convert-relaxed-to-half") {
     RegisterPass(CreateConvertRelaxedToHalfPass());
   } else if (pass_name == "relax-float-ops") {
@@ -524,7 +526,7 @@
   } else if (pass_name == "remove-dont-inline") {
     RegisterPass(CreateRemoveDontInlinePass());
   } else if (pass_name == "eliminate-dead-input-components") {
-    RegisterPass(CreateEliminateDeadInputComponentsPass());
+    RegisterPass(CreateEliminateDeadInputComponentsSafePass());
   } else if (pass_name == "fix-func-call-param") {
     RegisterPass(CreateFixFuncCallArgumentsPass());
   } else if (pass_name == "convert-to-sampled-image") {
@@ -547,6 +549,39 @@
              pass_args.c_str());
       return false;
     }
+  } else if (pass_name == "switch-descriptorset") {
+    if (pass_args.size() == 0) {
+      Error(consumer(), nullptr, {},
+            "--switch-descriptorset requires a from:to argument.");
+      return false;
+    }
+    uint32_t from_set, to_set;
+    const char* start = pass_args.data();
+    const char* end = pass_args.data() + pass_args.size();
+
+    auto result = std::from_chars(start, end, from_set);
+    if (result.ec != std::errc()) {
+      Errorf(consumer(), nullptr, {},
+             "Invalid argument for --switch-descriptorset: %s",
+             pass_args.c_str());
+      return false;
+    }
+    start = result.ptr;
+    if (start[0] != ':') {
+      Errorf(consumer(), nullptr, {},
+             "Invalid argument for --switch-descriptorset: %s",
+             pass_args.c_str());
+      return false;
+    }
+    start++;
+    result = std::from_chars(start, end, to_set);
+    if (result.ec != std::errc() || result.ptr != end) {
+      Errorf(consumer(), nullptr, {},
+             "Invalid argument for --switch-descriptorset: %s",
+             pass_args.c_str());
+      return false;
+    }
+    RegisterPass(CreateSwitchDescriptorSetPass(from_set, to_set));
   } else {
     Errorf(consumer(), nullptr, {},
            "Unknown flag '--%s'. Use --help for a list of valid flags",
@@ -786,12 +821,18 @@
 
 Optimizer::PassToken CreateAggressiveDCEPass() {
   return MakeUnique<Optimizer::PassToken::Impl>(
-      MakeUnique<opt::AggressiveDCEPass>(false));
+      MakeUnique<opt::AggressiveDCEPass>(false, false));
 }
 
 Optimizer::PassToken CreateAggressiveDCEPass(bool preserve_interface) {
   return MakeUnique<Optimizer::PassToken::Impl>(
-      MakeUnique<opt::AggressiveDCEPass>(preserve_interface));
+      MakeUnique<opt::AggressiveDCEPass>(preserve_interface, false));
+}
+
+Optimizer::PassToken CreateAggressiveDCEPass(bool preserve_interface,
+                                             bool remove_outputs) {
+  return MakeUnique<Optimizer::PassToken::Impl>(
+      MakeUnique<opt::AggressiveDCEPass>(preserve_interface, remove_outputs));
 }
 
 Optimizer::PassToken CreateRemoveUnusedInterfaceVariablesPass() {
@@ -938,14 +979,9 @@
       MakeUnique<opt::UpgradeMemoryModel>());
 }
 
-Optimizer::PassToken CreateInstBindlessCheckPass(
-    uint32_t desc_set, uint32_t shader_id, bool desc_length_enable,
-    bool desc_init_enable, bool buff_oob_enable, bool texbuff_oob_enable) {
+Optimizer::PassToken CreateInstBindlessCheckPass(uint32_t shader_id) {
   return MakeUnique<Optimizer::PassToken::Impl>(
-      MakeUnique<opt::InstBindlessCheckPass>(
-          desc_set, shader_id, desc_length_enable, desc_init_enable,
-          buff_oob_enable, texbuff_oob_enable,
-          desc_length_enable || desc_init_enable || buff_oob_enable));
+      MakeUnique<opt::InstBindlessCheckPass>(shader_id));
 }
 
 Optimizer::PassToken CreateInstDebugPrintfPass(uint32_t desc_set,
@@ -954,10 +990,9 @@
       MakeUnique<opt::InstDebugPrintfPass>(desc_set, shader_id));
 }
 
-Optimizer::PassToken CreateInstBuffAddrCheckPass(uint32_t desc_set,
-                                                 uint32_t shader_id) {
+Optimizer::PassToken CreateInstBuffAddrCheckPass(uint32_t shader_id) {
   return MakeUnique<Optimizer::PassToken::Impl>(
-      MakeUnique<opt::InstBuffAddrCheckPass>(desc_set, shader_id));
+      MakeUnique<opt::InstBuffAddrCheckPass>(shader_id));
 }
 
 Optimizer::PassToken CreateConvertRelaxedToHalfPass() {
@@ -1016,7 +1051,34 @@
 
 Optimizer::PassToken CreateEliminateDeadInputComponentsPass() {
   return MakeUnique<Optimizer::PassToken::Impl>(
-      MakeUnique<opt::EliminateDeadInputComponentsPass>());
+      MakeUnique<opt::EliminateDeadIOComponentsPass>(spv::StorageClass::Input,
+                                                     /* safe_mode */ false));
+}
+
+Optimizer::PassToken CreateEliminateDeadOutputComponentsPass() {
+  return MakeUnique<Optimizer::PassToken::Impl>(
+      MakeUnique<opt::EliminateDeadIOComponentsPass>(spv::StorageClass::Output,
+                                                     /* safe_mode */ false));
+}
+
+Optimizer::PassToken CreateEliminateDeadInputComponentsSafePass() {
+  return MakeUnique<Optimizer::PassToken::Impl>(
+      MakeUnique<opt::EliminateDeadIOComponentsPass>(spv::StorageClass::Input,
+                                                     /* safe_mode */ true));
+}
+
+Optimizer::PassToken CreateAnalyzeLiveInputPass(
+    std::unordered_set<uint32_t>* live_locs,
+    std::unordered_set<uint32_t>* live_builtins) {
+  return MakeUnique<Optimizer::PassToken::Impl>(
+      MakeUnique<opt::AnalyzeLiveInputPass>(live_locs, live_builtins));
+}
+
+Optimizer::PassToken CreateEliminateDeadOutputStoresPass(
+    std::unordered_set<uint32_t>* live_locs,
+    std::unordered_set<uint32_t>* live_builtins) {
+  return MakeUnique<Optimizer::PassToken::Impl>(
+      MakeUnique<opt::EliminateDeadOutputStoresPass>(live_locs, live_builtins));
 }
 
 Optimizer::PassToken CreateConvertToSampledImagePass(
@@ -1040,4 +1102,111 @@
   return MakeUnique<Optimizer::PassToken::Impl>(
       MakeUnique<opt::FixFuncCallArgumentsPass>());
 }
+
+Optimizer::PassToken CreateTrimCapabilitiesPass() {
+  return MakeUnique<Optimizer::PassToken::Impl>(
+      MakeUnique<opt::TrimCapabilitiesPass>());
+}
+
+Optimizer::PassToken CreateSwitchDescriptorSetPass(uint32_t from, uint32_t to) {
+  return MakeUnique<Optimizer::PassToken::Impl>(
+      MakeUnique<opt::SwitchDescriptorSetPass>(from, to));
+}
+
+Optimizer::PassToken CreateInvocationInterlockPlacementPass() {
+  return MakeUnique<Optimizer::PassToken::Impl>(
+      MakeUnique<opt::InvocationInterlockPlacementPass>());
+}
 }  // namespace spvtools
+
+extern "C" {
+
+SPIRV_TOOLS_EXPORT spv_optimizer_t* spvOptimizerCreate(spv_target_env env) {
+  return reinterpret_cast<spv_optimizer_t*>(new spvtools::Optimizer(env));
+}
+
+SPIRV_TOOLS_EXPORT void spvOptimizerDestroy(spv_optimizer_t* optimizer) {
+  delete reinterpret_cast<spvtools::Optimizer*>(optimizer);
+}
+
+SPIRV_TOOLS_EXPORT void spvOptimizerSetMessageConsumer(
+    spv_optimizer_t* optimizer, spv_message_consumer consumer) {
+  reinterpret_cast<spvtools::Optimizer*>(optimizer)->
+      SetMessageConsumer(
+          [consumer](spv_message_level_t level, const char* source,
+                     const spv_position_t& position, const char* message) {
+            return consumer(level, source, &position, message);
+          });
+}
+
+SPIRV_TOOLS_EXPORT void spvOptimizerRegisterLegalizationPasses(
+    spv_optimizer_t* optimizer) {
+  reinterpret_cast<spvtools::Optimizer*>(optimizer)->
+      RegisterLegalizationPasses();
+}
+
+SPIRV_TOOLS_EXPORT void spvOptimizerRegisterPerformancePasses(
+    spv_optimizer_t* optimizer) {
+  reinterpret_cast<spvtools::Optimizer*>(optimizer)->
+      RegisterPerformancePasses();
+}
+
+SPIRV_TOOLS_EXPORT void spvOptimizerRegisterSizePasses(
+    spv_optimizer_t* optimizer) {
+  reinterpret_cast<spvtools::Optimizer*>(optimizer)->RegisterSizePasses();
+}
+
+SPIRV_TOOLS_EXPORT bool spvOptimizerRegisterPassFromFlag(
+    spv_optimizer_t* optimizer, const char* flag)
+{
+  return reinterpret_cast<spvtools::Optimizer*>(optimizer)->
+      RegisterPassFromFlag(flag);
+}
+
+SPIRV_TOOLS_EXPORT bool spvOptimizerRegisterPassesFromFlags(
+    spv_optimizer_t* optimizer, const char** flags, const size_t flag_count) {
+  std::vector<std::string> opt_flags;
+  for (uint32_t i = 0; i < flag_count; i++) {
+    opt_flags.emplace_back(flags[i]);
+  }
+
+  return reinterpret_cast<spvtools::Optimizer*>(optimizer)->
+      RegisterPassesFromFlags(opt_flags);
+}
+
+SPIRV_TOOLS_EXPORT
+spv_result_t spvOptimizerRun(spv_optimizer_t* optimizer,
+                             const uint32_t* binary,
+                             const size_t word_count,
+                             spv_binary* optimized_binary,
+                             const spv_optimizer_options options) {
+  std::vector<uint32_t> optimized;
+
+  if (!reinterpret_cast<spvtools::Optimizer*>(optimizer)->
+      Run(binary, word_count, &optimized, options)) {
+    return SPV_ERROR_INTERNAL;
+  }
+
+  auto result_binary = new spv_binary_t();
+  if (!result_binary) {
+      *optimized_binary = nullptr;
+      return SPV_ERROR_OUT_OF_MEMORY;
+  }
+
+  result_binary->code = new uint32_t[optimized.size()];
+  if (!result_binary->code) {
+      delete result_binary;
+      *optimized_binary = nullptr;
+      return SPV_ERROR_OUT_OF_MEMORY;
+  }
+  result_binary->wordCount = optimized.size();
+
+  memcpy(result_binary->code, optimized.data(),
+         optimized.size() * sizeof(uint32_t));
+
+  *optimized_binary = result_binary;
+
+  return SPV_SUCCESS;
+}
+
+}  // extern "C"
diff --git a/source/opt/pass.cpp b/source/opt/pass.cpp
index 017aad1..75c3740 100644
--- a/source/opt/pass.cpp
+++ b/source/opt/pass.cpp
@@ -21,11 +21,8 @@
 
 namespace spvtools {
 namespace opt {
-
 namespace {
-
-const uint32_t kTypePointerTypeIdInIdx = 1;
-
+constexpr uint32_t kTypePointerTypeIdInIdx = 1;
 }  // namespace
 
 Pass::Pass() : consumer_(nullptr), context_(nullptr), already_run_(false) {}
@@ -56,11 +53,11 @@
 
 Instruction* Pass::GetBaseType(uint32_t ty_id) {
   Instruction* ty_inst = get_def_use_mgr()->GetDef(ty_id);
-  if (ty_inst->opcode() == SpvOpTypeMatrix) {
+  if (ty_inst->opcode() == spv::Op::OpTypeMatrix) {
     uint32_t vty_id = ty_inst->GetSingleWordInOperand(0);
     ty_inst = get_def_use_mgr()->GetDef(vty_id);
   }
-  if (ty_inst->opcode() == SpvOpTypeVector) {
+  if (ty_inst->opcode() == spv::Op::OpTypeVector) {
     uint32_t cty_id = ty_inst->GetSingleWordInOperand(0);
     ty_inst = get_def_use_mgr()->GetDef(cty_id);
   }
@@ -69,12 +66,12 @@
 
 bool Pass::IsFloat(uint32_t ty_id, uint32_t width) {
   Instruction* ty_inst = GetBaseType(ty_id);
-  if (ty_inst->opcode() != SpvOpTypeFloat) return false;
+  if (ty_inst->opcode() != spv::Op::OpTypeFloat) return false;
   return ty_inst->GetSingleWordInOperand(0) == width;
 }
 
 uint32_t Pass::GetNullId(uint32_t type_id) {
-  if (IsFloat(type_id, 16)) context()->AddCapability(SpvCapabilityFloat16);
+  if (IsFloat(type_id, 16)) context()->AddCapability(spv::Capability::Float16);
   analysis::TypeManager* type_mgr = context()->get_type_mgr();
   analysis::ConstantManager* const_mgr = context()->get_constant_mgr();
   const analysis::Type* type = type_mgr->GetType(type_id);
diff --git a/source/opt/passes.h b/source/opt/passes.h
index 21354c7..305f578 100644
--- a/source/opt/passes.h
+++ b/source/opt/passes.h
@@ -19,6 +19,7 @@
 
 #include "source/opt/aggressive_dead_code_elim_pass.h"
 #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/ccp_pass.h"
 #include "source/opt/cfg_cleanup_pass.h"
@@ -34,8 +35,9 @@
 #include "source/opt/desc_sroa.h"
 #include "source/opt/eliminate_dead_constant_pass.h"
 #include "source/opt/eliminate_dead_functions_pass.h"
-#include "source/opt/eliminate_dead_input_components_pass.h"
+#include "source/opt/eliminate_dead_io_components_pass.h"
 #include "source/opt/eliminate_dead_members_pass.h"
+#include "source/opt/eliminate_dead_output_stores_pass.h"
 #include "source/opt/empty_pass.h"
 #include "source/opt/fix_func_call_arguments.h"
 #include "source/opt/fix_storage_class.h"
@@ -51,6 +53,7 @@
 #include "source/opt/inst_debug_printf_pass.h"
 #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/licm_pass.h"
 #include "source/opt/local_access_chain_convert_pass.h"
 #include "source/opt/local_redundancy_elimination.h"
@@ -80,6 +83,8 @@
 #include "source/opt/strength_reduction_pass.h"
 #include "source/opt/strip_debug_info_pass.h"
 #include "source/opt/strip_nonsemantic_info_pass.h"
+#include "source/opt/switch_descriptorset_pass.h"
+#include "source/opt/trim_capabilities_pass.h"
 #include "source/opt/unify_const_pass.h"
 #include "source/opt/upgrade_memory_model.h"
 #include "source/opt/vector_dce.h"
diff --git a/source/opt/private_to_local_pass.cpp b/source/opt/private_to_local_pass.cpp
index 80fb4c5..4904e05 100644
--- a/source/opt/private_to_local_pass.cpp
+++ b/source/opt/private_to_local_pass.cpp
@@ -24,10 +24,8 @@
 namespace spvtools {
 namespace opt {
 namespace {
-
-const uint32_t kVariableStorageClassInIdx = 0;
-const uint32_t kSpvTypePointerTypeIdInIdx = 1;
-
+constexpr uint32_t kVariableStorageClassInIdx = 0;
+constexpr uint32_t kSpvTypePointerTypeIdInIdx = 1;
 }  // namespace
 
 Pass::Status PrivateToLocalPass::Process() {
@@ -35,18 +33,18 @@
 
   // Private variables require the shader capability.  If this is not a shader,
   // there is no work to do.
-  if (context()->get_feature_mgr()->HasCapability(SpvCapabilityAddresses))
+  if (context()->get_feature_mgr()->HasCapability(spv::Capability::Addresses))
     return Status::SuccessWithoutChange;
 
   std::vector<std::pair<Instruction*, Function*>> variables_to_move;
   std::unordered_set<uint32_t> localized_variables;
   for (auto& inst : context()->types_values()) {
-    if (inst.opcode() != SpvOpVariable) {
+    if (inst.opcode() != spv::Op::OpVariable) {
       continue;
     }
 
-    if (inst.GetSingleWordInOperand(kVariableStorageClassInIdx) !=
-        SpvStorageClassPrivate) {
+    if (spv::StorageClass(inst.GetSingleWordInOperand(
+            kVariableStorageClassInIdx)) != spv::StorageClass::Private) {
       continue;
     }
 
@@ -123,7 +121,8 @@
   context()->ForgetUses(variable);
 
   // Update the storage class of the variable.
-  variable->SetInOperand(kVariableStorageClassInIdx, {SpvStorageClassFunction});
+  variable->SetInOperand(kVariableStorageClassInIdx,
+                         {uint32_t(spv::StorageClass::Function)});
 
   // Update the type as well.
   uint32_t new_type_id = GetNewType(variable->type_id());
@@ -147,7 +146,7 @@
   uint32_t pointee_type_id =
       old_type_inst->GetSingleWordInOperand(kSpvTypePointerTypeIdInIdx);
   uint32_t new_type_id =
-      type_mgr->FindPointerToType(pointee_type_id, SpvStorageClassFunction);
+      type_mgr->FindPointerToType(pointee_type_id, spv::StorageClass::Function);
   if (new_type_id != 0) {
     context()->UpdateDefUse(context()->get_def_use_mgr()->GetDef(new_type_id));
   }
@@ -161,17 +160,17 @@
     return true;
   }
   switch (inst->opcode()) {
-    case SpvOpLoad:
-    case SpvOpStore:
-    case SpvOpImageTexelPointer:  // Treat like a load
+    case spv::Op::OpLoad:
+    case spv::Op::OpStore:
+    case spv::Op::OpImageTexelPointer:  // Treat like a load
       return true;
-    case SpvOpAccessChain:
+    case spv::Op::OpAccessChain:
       return context()->get_def_use_mgr()->WhileEachUser(
           inst, [this](const Instruction* user) {
             if (!IsValidUse(user)) return false;
             return true;
           });
-    case SpvOpName:
+    case spv::Op::OpName:
       return true;
     default:
       return spvOpcodeIsDecoration(inst->opcode());
@@ -188,13 +187,13 @@
     return true;
   }
   switch (inst->opcode()) {
-    case SpvOpLoad:
-    case SpvOpStore:
-    case SpvOpImageTexelPointer:  // Treat like a load
+    case spv::Op::OpLoad:
+    case spv::Op::OpStore:
+    case spv::Op::OpImageTexelPointer:  // Treat like a load
       // The type is fine because it is the type pointed to, and that does not
       // change.
       break;
-    case SpvOpAccessChain: {
+    case spv::Op::OpAccessChain: {
       context()->ForgetUses(inst);
       uint32_t new_type_id = GetNewType(inst->type_id());
       if (new_type_id == 0) {
@@ -208,8 +207,8 @@
         return false;
       }
     } break;
-    case SpvOpName:
-    case SpvOpEntryPoint:  // entry points will be updated separately.
+    case spv::Op::OpName:
+    case spv::Op::OpEntryPoint:  // entry points will be updated separately.
       break;
     default:
       assert(spvOpcodeIsDecoration(inst->opcode()) &&
diff --git a/source/opt/propagator.cpp b/source/opt/propagator.cpp
index 6a1f1aa..9cd6174 100644
--- a/source/opt/propagator.cpp
+++ b/source/opt/propagator.cpp
@@ -134,7 +134,7 @@
   // defined at an instruction D that should be simulated again, then the output
   // of D might affect |instr|, so we should simulate |instr| again.
   bool has_operands_to_simulate = false;
-  if (instr->opcode() == SpvOpPhi) {
+  if (instr->opcode() == spv::Op::OpPhi) {
     // For Phi instructions, an operand causes the Phi to be simulated again if
     // the operand comes from an edge that has not yet been traversed or if its
     // definition should be simulated again.
@@ -189,7 +189,7 @@
   // statement in it.
   if (!BlockHasBeenSimulated(block)) {
     block->ForEachInst([this, &changed](Instruction* instr) {
-      if (instr->opcode() != SpvOpPhi) {
+      if (instr->opcode() != spv::Op::OpPhi) {
         changed |= Simulate(instr);
       }
     });
diff --git a/source/opt/propagator.h b/source/opt/propagator.h
index ac7c0e7..71212c9 100644
--- a/source/opt/propagator.h
+++ b/source/opt/propagator.h
@@ -153,10 +153,10 @@
 //   std::map<uint32_t, uint32_t> values;
 //   const auto visit_fn = [&ctx, &values](Instruction* instr,
 //                                         BasicBlock** dest_bb) {
-//     if (instr->opcode() == SpvOpStore) {
+//     if (instr->opcode() == spv::Op::OpStore) {
 //       uint32_t rhs_id = instr->GetSingleWordOperand(1);
 //       Instruction* rhs_def = ctx->get_def_use_mgr()->GetDef(rhs_id);
-//       if (rhs_def->opcode() == SpvOpConstant) {
+//       if (rhs_def->opcode() == spv::Op::OpConstant) {
 //         uint32_t val = rhs_def->GetSingleWordOperand(2);
 //         values[rhs_id] = val;
 //         return SSAPropagator::kInteresting;
diff --git a/source/opt/reduce_load_size.cpp b/source/opt/reduce_load_size.cpp
index 56491b2..73a90f0 100644
--- a/source/opt/reduce_load_size.cpp
+++ b/source/opt/reduce_load_size.cpp
@@ -22,23 +22,20 @@
 #include "source/opt/ir_context.h"
 #include "source/util/bit_vector.h"
 
-namespace {
-
-const uint32_t kExtractCompositeIdInIdx = 0;
-const uint32_t kVariableStorageClassInIdx = 0;
-const uint32_t kLoadPointerInIdx = 0;
-
-}  // namespace
-
 namespace spvtools {
 namespace opt {
+namespace {
+constexpr uint32_t kExtractCompositeIdInIdx = 0;
+constexpr uint32_t kVariableStorageClassInIdx = 0;
+constexpr uint32_t kLoadPointerInIdx = 0;
+}  // namespace
 
 Pass::Status ReduceLoadSize::Process() {
   bool modified = false;
 
   for (auto& func : *get_module()) {
     func.ForEachInst([&modified, this](Instruction* inst) {
-      if (inst->opcode() == SpvOpCompositeExtract) {
+      if (inst->opcode() == spv::Op::OpCompositeExtract) {
         if (ShouldReplaceExtract(inst)) {
           modified |= ReplaceExtract(inst);
         }
@@ -50,7 +47,7 @@
 }
 
 bool ReduceLoadSize::ReplaceExtract(Instruction* inst) {
-  assert(inst->opcode() == SpvOpCompositeExtract &&
+  assert(inst->opcode() == spv::Op::OpCompositeExtract &&
          "Wrong opcode.  Should be OpCompositeExtract.");
   analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
   analysis::TypeManager* type_mgr = context()->get_type_mgr();
@@ -60,7 +57,7 @@
       inst->GetSingleWordInOperand(kExtractCompositeIdInIdx);
   Instruction* composite_inst = def_use_mgr->GetDef(composite_id);
 
-  if (composite_inst->opcode() != SpvOpLoad) {
+  if (composite_inst->opcode() != spv::Op::OpLoad) {
     return false;
   }
 
@@ -71,16 +68,16 @@
   }
 
   Instruction* var = composite_inst->GetBaseAddress();
-  if (var == nullptr || var->opcode() != SpvOpVariable) {
+  if (var == nullptr || var->opcode() != spv::Op::OpVariable) {
     return false;
   }
 
-  SpvStorageClass storage_class = static_cast<SpvStorageClass>(
+  spv::StorageClass storage_class = static_cast<spv::StorageClass>(
       var->GetSingleWordInOperand(kVariableStorageClassInIdx));
   switch (storage_class) {
-    case SpvStorageClassUniform:
-    case SpvStorageClassUniformConstant:
-    case SpvStorageClassInput:
+    case spv::StorageClass::Uniform:
+    case spv::StorageClass::UniformConstant:
+    case spv::StorageClass::Input:
       break;
     default:
       return false;
@@ -124,7 +121,7 @@
   Instruction* op_inst = def_use_mgr->GetDef(
       inst->GetSingleWordInOperand(kExtractCompositeIdInIdx));
 
-  if (op_inst->opcode() != SpvOpLoad) {
+  if (op_inst->opcode() != spv::Op::OpLoad) {
     return false;
   }
 
@@ -139,7 +136,7 @@
   all_elements_used =
       !def_use_mgr->WhileEachUser(op_inst, [&elements_used](Instruction* use) {
         if (use->IsCommonDebugInstr()) return true;
-        if (use->opcode() != SpvOpCompositeExtract ||
+        if (use->opcode() != spv::Op::OpCompositeExtract ||
             use->NumInOperands() == 1) {
           return false;
         }
diff --git a/source/opt/reflect.h b/source/opt/reflect.h
index c2ffb0b..ec7c2dd 100644
--- a/source/opt/reflect.h
+++ b/source/opt/reflect.h
@@ -16,6 +16,7 @@
 #define SOURCE_OPT_REFLECT_H_
 
 #include "source/latest_version_spirv_header.h"
+#include "source/opcode.h"
 
 namespace spvtools {
 namespace opt {
@@ -24,41 +25,36 @@
 // following functions tend to be outdated and should be updated when SPIR-V
 // version bumps.
 
-inline bool IsDebug1Inst(SpvOp opcode) {
-  return (opcode >= SpvOpSourceContinued && opcode <= SpvOpSourceExtension) ||
-         opcode == SpvOpString;
+inline bool IsDebug1Inst(spv::Op opcode) {
+  return (opcode >= spv::Op::OpSourceContinued &&
+          opcode <= spv::Op::OpSourceExtension) ||
+         opcode == spv::Op::OpString;
 }
-inline bool IsDebug2Inst(SpvOp opcode) {
-  return opcode == SpvOpName || opcode == SpvOpMemberName;
+inline bool IsDebug2Inst(spv::Op opcode) {
+  return opcode == spv::Op::OpName || opcode == spv::Op::OpMemberName;
 }
-inline bool IsDebug3Inst(SpvOp opcode) {
-  return opcode == SpvOpModuleProcessed;
+inline bool IsDebug3Inst(spv::Op opcode) {
+  return opcode == spv::Op::OpModuleProcessed;
 }
-inline bool IsOpLineInst(SpvOp opcode) {
-  return opcode == SpvOpLine || opcode == SpvOpNoLine;
+inline bool IsOpLineInst(spv::Op opcode) {
+  return opcode == spv::Op::OpLine || opcode == spv::Op::OpNoLine;
 }
-inline bool IsAnnotationInst(SpvOp opcode) {
-  return (opcode >= SpvOpDecorate && opcode <= SpvOpGroupMemberDecorate) ||
-         opcode == SpvOpDecorateId || opcode == SpvOpDecorateStringGOOGLE ||
-         opcode == SpvOpMemberDecorateStringGOOGLE;
+inline bool IsAnnotationInst(spv::Op opcode) {
+  return (opcode >= spv::Op::OpDecorate &&
+          opcode <= spv::Op::OpGroupMemberDecorate) ||
+         opcode == spv::Op::OpDecorateId ||
+         opcode == spv::Op::OpDecorateStringGOOGLE ||
+         opcode == spv::Op::OpMemberDecorateStringGOOGLE;
 }
-inline bool IsTypeInst(SpvOp opcode) {
-  return (opcode >= SpvOpTypeVoid && opcode <= SpvOpTypeForwardPointer) ||
-         opcode == SpvOpTypePipeStorage || opcode == SpvOpTypeNamedBarrier ||
-         opcode == SpvOpTypeAccelerationStructureNV ||
-         opcode == SpvOpTypeAccelerationStructureKHR ||
-         opcode == SpvOpTypeRayQueryKHR ||
-         opcode == SpvOpTypeCooperativeMatrixNV;
+inline bool IsTypeInst(spv::Op opcode) {
+  return spvOpcodeGeneratesType(opcode) ||
+         opcode == spv::Op::OpTypeForwardPointer;
 }
-inline bool IsConstantInst(SpvOp opcode) {
-  return (opcode >= SpvOpConstantTrue && opcode <= SpvOpSpecConstantOp) ||
-         opcode == SpvOpConstantFunctionPointerINTEL;
+inline bool IsConstantInst(spv::Op opcode) {
+  return spvOpcodeIsConstant(opcode);
 }
-inline bool IsCompileTimeConstantInst(SpvOp opcode) {
-  return opcode >= SpvOpConstantTrue && opcode <= SpvOpConstantNull;
-}
-inline bool IsSpecConstantInst(SpvOp opcode) {
-  return opcode >= SpvOpSpecConstantTrue && opcode <= SpvOpSpecConstantOp;
+inline bool IsSpecConstantInst(spv::Op opcode) {
+  return spvOpcodeIsSpecConstant(opcode);
 }
 
 }  // namespace opt
diff --git a/source/opt/register_pressure.cpp b/source/opt/register_pressure.cpp
index 1ad3373..34a8ba3 100644
--- a/source/opt/register_pressure.cpp
+++ b/source/opt/register_pressure.cpp
@@ -26,7 +26,6 @@
 
 namespace spvtools {
 namespace opt {
-
 namespace {
 // Predicate for the FilterIterator to only consider instructions that are not
 // phi instructions defined in the basic block |bb|.
@@ -36,7 +35,7 @@
       : context_(context), bb_(bb) {}
 
   bool operator()(Instruction* insn) const {
-    return !(insn->opcode() == SpvOpPhi &&
+    return !(insn->opcode() == spv::Op::OpPhi &&
              context_->get_instr_block(insn) == bb_);
   }
 
@@ -49,9 +48,9 @@
 // physical register.
 bool CreatesRegisterUsage(Instruction* insn) {
   if (!insn->HasResultId()) return false;
-  if (insn->opcode() == SpvOpUndef) return false;
+  if (insn->opcode() == spv::Op::OpUndef) return false;
   if (IsConstantInst(insn->opcode())) return false;
-  if (insn->opcode() == SpvOpLabel) return false;
+  if (insn->opcode() == spv::Op::OpLabel) return false;
   return true;
 }
 
@@ -147,7 +146,7 @@
 
     live_inout->live_in_ = live_inout->live_out_;
     for (Instruction& insn : make_range(bb->rbegin(), bb->rend())) {
-      if (insn.opcode() == SpvOpPhi) {
+      if (insn.opcode() == spv::Op::OpPhi) {
         live_inout->live_in_.insert(&insn);
         break;
       }
@@ -224,7 +223,7 @@
       for (Instruction& insn : make_range(bb.rbegin(), bb.rend())) {
         // If it is a phi instruction, the register pressure will not change
         // anymore.
-        if (insn.opcode() == SpvOpPhi) {
+        if (insn.opcode() == spv::Op::OpPhi) {
           break;
         }
 
@@ -271,7 +270,7 @@
   RegisterLiveness::RegisterClass reg_class{type, false};
 
   insn->context()->get_decoration_mgr()->WhileEachDecoration(
-      insn->result_id(), SpvDecorationUniform,
+      insn->result_id(), uint32_t(spv::Decoration::Uniform),
       [&reg_class](const Instruction&) {
         reg_class.is_uniform_ = true;
         return false;
@@ -325,7 +324,7 @@
         loop_reg_pressure->used_registers_, live_inout->used_registers_);
 
     for (Instruction& insn : *bb) {
-      if (insn.opcode() == SpvOpPhi || !CreatesRegisterUsage(&insn) ||
+      if (insn.opcode() == spv::Op::OpPhi || !CreatesRegisterUsage(&insn) ||
           seen_insn.count(insn.result_id())) {
         continue;
       }
@@ -386,7 +385,7 @@
       [&l1, &l2](Instruction* insn) {
         BasicBlock* bb = insn->context()->get_instr_block(insn);
         return insn->HasResultId() &&
-               !(insn->opcode() == SpvOpPhi &&
+               !(insn->opcode() == spv::Op::OpPhi &&
                  (bb == l1.GetHeaderBlock() || bb == l2.GetHeaderBlock()));
       });
 
@@ -403,7 +402,7 @@
                      live_inout_info->live_out_.size());
 
     for (Instruction& insn : *bb) {
-      if (insn.opcode() == SpvOpPhi || !CreatesRegisterUsage(&insn) ||
+      if (insn.opcode() == spv::Op::OpPhi || !CreatesRegisterUsage(&insn) ||
           seen_insn.count(insn.result_id())) {
         continue;
       }
@@ -434,7 +433,7 @@
                      live_inout_info->live_out_.size());
 
     for (Instruction& insn : *bb) {
-      if (insn.opcode() == SpvOpPhi || !CreatesRegisterUsage(&insn) ||
+      if (insn.opcode() == spv::Op::OpPhi || !CreatesRegisterUsage(&insn) ||
           seen_insn.count(insn.result_id())) {
         continue;
       }
@@ -532,7 +531,7 @@
 
     std::unordered_set<uint32_t> die_in_block;
     for (Instruction& insn : make_range(bb->rbegin(), bb->rend())) {
-      if (insn.opcode() == SpvOpPhi) {
+      if (insn.opcode() == spv::Op::OpPhi) {
         break;
       }
 
diff --git a/source/opt/relax_float_ops_pass.cpp b/source/opt/relax_float_ops_pass.cpp
index 3fcf879..df925a2 100644
--- a/source/opt/relax_float_ops_pass.cpp
+++ b/source/opt/relax_float_ops_pass.cpp
@@ -25,7 +25,7 @@
   return target_ops_core_f_rslt_.count(inst->opcode()) != 0 ||
          target_ops_core_f_opnd_.count(inst->opcode()) != 0 ||
          sample_ops_.count(inst->opcode()) != 0 ||
-         (inst->opcode() == SpvOpExtInst &&
+         (inst->opcode() == spv::Op::OpExtInst &&
           inst->GetSingleWordInOperand(0) ==
               context()->get_feature_mgr()->GetExtInstImportId_GLSLstd450() &&
           target_ops_450_.count(inst->GetSingleWordInOperand(1)) != 0);
@@ -46,8 +46,9 @@
 
 bool RelaxFloatOpsPass::IsRelaxed(uint32_t r_id) {
   for (auto r_inst : get_decoration_mgr()->GetDecorationsFor(r_id, false))
-    if (r_inst->opcode() == SpvOpDecorate &&
-        r_inst->GetSingleWordInOperand(1) == SpvDecorationRelaxedPrecision)
+    if (r_inst->opcode() == spv::Op::OpDecorate &&
+        spv::Decoration(r_inst->GetSingleWordInOperand(1)) ==
+            spv::Decoration::RelaxedPrecision)
       return true;
   return false;
 }
@@ -58,7 +59,8 @@
   if (!IsFloat32(r_inst)) return false;
   if (IsRelaxed(r_id)) return false;
   if (!IsRelaxable(r_inst)) return false;
-  get_decoration_mgr()->AddDecoration(r_id, SpvDecorationRelaxedPrecision);
+  get_decoration_mgr()->AddDecoration(
+      r_id, uint32_t(spv::Decoration::RelaxedPrecision));
   return true;
 }
 
@@ -87,48 +89,48 @@
 
 void RelaxFloatOpsPass::Initialize() {
   target_ops_core_f_rslt_ = {
-      SpvOpLoad,
-      SpvOpPhi,
-      SpvOpVectorExtractDynamic,
-      SpvOpVectorInsertDynamic,
-      SpvOpVectorShuffle,
-      SpvOpCompositeExtract,
-      SpvOpCompositeConstruct,
-      SpvOpCompositeInsert,
-      SpvOpCopyObject,
-      SpvOpTranspose,
-      SpvOpConvertSToF,
-      SpvOpConvertUToF,
-      SpvOpFConvert,
-      // SpvOpQuantizeToF16,
-      SpvOpFNegate,
-      SpvOpFAdd,
-      SpvOpFSub,
-      SpvOpFMul,
-      SpvOpFDiv,
-      SpvOpFMod,
-      SpvOpVectorTimesScalar,
-      SpvOpMatrixTimesScalar,
-      SpvOpVectorTimesMatrix,
-      SpvOpMatrixTimesVector,
-      SpvOpMatrixTimesMatrix,
-      SpvOpOuterProduct,
-      SpvOpDot,
-      SpvOpSelect,
+      spv::Op::OpLoad,
+      spv::Op::OpPhi,
+      spv::Op::OpVectorExtractDynamic,
+      spv::Op::OpVectorInsertDynamic,
+      spv::Op::OpVectorShuffle,
+      spv::Op::OpCompositeExtract,
+      spv::Op::OpCompositeConstruct,
+      spv::Op::OpCompositeInsert,
+      spv::Op::OpCopyObject,
+      spv::Op::OpTranspose,
+      spv::Op::OpConvertSToF,
+      spv::Op::OpConvertUToF,
+      spv::Op::OpFConvert,
+      // spv::Op::OpQuantizeToF16,
+      spv::Op::OpFNegate,
+      spv::Op::OpFAdd,
+      spv::Op::OpFSub,
+      spv::Op::OpFMul,
+      spv::Op::OpFDiv,
+      spv::Op::OpFMod,
+      spv::Op::OpVectorTimesScalar,
+      spv::Op::OpMatrixTimesScalar,
+      spv::Op::OpVectorTimesMatrix,
+      spv::Op::OpMatrixTimesVector,
+      spv::Op::OpMatrixTimesMatrix,
+      spv::Op::OpOuterProduct,
+      spv::Op::OpDot,
+      spv::Op::OpSelect,
   };
   target_ops_core_f_opnd_ = {
-      SpvOpFOrdEqual,
-      SpvOpFUnordEqual,
-      SpvOpFOrdNotEqual,
-      SpvOpFUnordNotEqual,
-      SpvOpFOrdLessThan,
-      SpvOpFUnordLessThan,
-      SpvOpFOrdGreaterThan,
-      SpvOpFUnordGreaterThan,
-      SpvOpFOrdLessThanEqual,
-      SpvOpFUnordLessThanEqual,
-      SpvOpFOrdGreaterThanEqual,
-      SpvOpFUnordGreaterThanEqual,
+      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,
   };
   target_ops_450_ = {
       GLSLstd450Round, GLSLstd450RoundEven, GLSLstd450Trunc, GLSLstd450FAbs,
@@ -147,31 +149,31 @@
       GLSLstd450Ldexp, GLSLstd450Length, GLSLstd450Distance, GLSLstd450Cross,
       GLSLstd450Normalize, GLSLstd450FaceForward, GLSLstd450Reflect,
       GLSLstd450Refract, GLSLstd450NMin, GLSLstd450NMax, GLSLstd450NClamp};
-  sample_ops_ = {SpvOpImageSampleImplicitLod,
-                 SpvOpImageSampleExplicitLod,
-                 SpvOpImageSampleDrefImplicitLod,
-                 SpvOpImageSampleDrefExplicitLod,
-                 SpvOpImageSampleProjImplicitLod,
-                 SpvOpImageSampleProjExplicitLod,
-                 SpvOpImageSampleProjDrefImplicitLod,
-                 SpvOpImageSampleProjDrefExplicitLod,
-                 SpvOpImageFetch,
-                 SpvOpImageGather,
-                 SpvOpImageDrefGather,
-                 SpvOpImageRead,
-                 SpvOpImageSparseSampleImplicitLod,
-                 SpvOpImageSparseSampleExplicitLod,
-                 SpvOpImageSparseSampleDrefImplicitLod,
-                 SpvOpImageSparseSampleDrefExplicitLod,
-                 SpvOpImageSparseSampleProjImplicitLod,
-                 SpvOpImageSparseSampleProjExplicitLod,
-                 SpvOpImageSparseSampleProjDrefImplicitLod,
-                 SpvOpImageSparseSampleProjDrefExplicitLod,
-                 SpvOpImageSparseFetch,
-                 SpvOpImageSparseGather,
-                 SpvOpImageSparseDrefGather,
-                 SpvOpImageSparseTexelsResident,
-                 SpvOpImageSparseRead};
+  sample_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::OpImageSparseSampleImplicitLod,
+                 spv::Op::OpImageSparseSampleExplicitLod,
+                 spv::Op::OpImageSparseSampleDrefImplicitLod,
+                 spv::Op::OpImageSparseSampleDrefExplicitLod,
+                 spv::Op::OpImageSparseSampleProjImplicitLod,
+                 spv::Op::OpImageSparseSampleProjExplicitLod,
+                 spv::Op::OpImageSparseSampleProjDrefImplicitLod,
+                 spv::Op::OpImageSparseSampleProjDrefExplicitLod,
+                 spv::Op::OpImageSparseFetch,
+                 spv::Op::OpImageSparseGather,
+                 spv::Op::OpImageSparseDrefGather,
+                 spv::Op::OpImageSparseTexelsResident,
+                 spv::Op::OpImageSparseRead};
 }
 
 }  // namespace opt
diff --git a/source/opt/relax_float_ops_pass.h b/source/opt/relax_float_ops_pass.h
index 5ee3d73..9e4606f 100644
--- a/source/opt/relax_float_ops_pass.h
+++ b/source/opt/relax_float_ops_pass.h
@@ -61,17 +61,23 @@
   // Initialize state for converting to half
   void Initialize();
 
+  struct hasher {
+    size_t operator()(const spv::Op& op) const noexcept {
+      return std::hash<uint32_t>()(uint32_t(op));
+    }
+  };
+
   // Set of float result core operations to be processed
-  std::unordered_set<uint32_t> target_ops_core_f_rslt_;
+  std::unordered_set<spv::Op, hasher> target_ops_core_f_rslt_;
 
   // Set of float operand core operations to be processed
-  std::unordered_set<uint32_t> target_ops_core_f_opnd_;
+  std::unordered_set<spv::Op, hasher> target_ops_core_f_opnd_;
 
   // Set of 450 extension operations to be processed
   std::unordered_set<uint32_t> target_ops_450_;
 
   // Set of sample operations
-  std::unordered_set<uint32_t> sample_ops_;
+  std::unordered_set<spv::Op, hasher> sample_ops_;
 };
 
 }  // namespace opt
diff --git a/source/opt/remove_dontinline_pass.cpp b/source/opt/remove_dontinline_pass.cpp
index 4dd1cd4..3750bc1 100644
--- a/source/opt/remove_dontinline_pass.cpp
+++ b/source/opt/remove_dontinline_pass.cpp
@@ -37,10 +37,11 @@
   uint32_t function_control =
       function_inst->GetSingleWordInOperand(kFunctionControlInOperandIdx);
 
-  if ((function_control & SpvFunctionControlDontInlineMask) == 0) {
+  if ((function_control & uint32_t(spv::FunctionControlMask::DontInline)) ==
+      0) {
     return false;
   }
-  function_control &= ~SpvFunctionControlDontInlineMask;
+  function_control &= ~uint32_t(spv::FunctionControlMask::DontInline);
   function_inst->SetInOperand(kFunctionControlInOperandIdx, {function_control});
   return true;
 }
diff --git a/source/opt/remove_duplicates_pass.cpp b/source/opt/remove_duplicates_pass.cpp
index 1ed8e2a..0df559b 100644
--- a/source/opt/remove_duplicates_pass.cpp
+++ b/source/opt/remove_duplicates_pass.cpp
@@ -15,8 +15,6 @@
 #include "source/opt/remove_duplicates_pass.h"
 
 #include <algorithm>
-#include <cstring>
-#include <limits>
 #include <string>
 #include <unordered_map>
 #include <unordered_set>
@@ -25,7 +23,6 @@
 #include "source/opcode.h"
 #include "source/opt/decoration_manager.h"
 #include "source/opt/ir_context.h"
-#include "source/opt/reflect.h"
 
 namespace spvtools {
 namespace opt {
@@ -70,7 +67,7 @@
     return modified;
   }
 
-  std::unordered_map<std::string, SpvId> ext_inst_imports;
+  std::unordered_map<std::string, spv::Id> ext_inst_imports;
   for (auto* i = &*context()->ext_inst_import_begin(); i;) {
     auto res = ext_inst_imports.emplace(i->GetInOperand(0u).AsString(),
                                         i->result_id());
@@ -101,7 +98,8 @@
   std::vector<analysis::ForwardPointer> visited_forward_pointers;
   std::vector<Instruction*> to_delete;
   for (auto* i = &*context()->types_values_begin(); i; i = i->NextNode()) {
-    const bool is_i_forward_pointer = i->opcode() == SpvOpTypeForwardPointer;
+    const bool is_i_forward_pointer =
+        i->opcode() == spv::Op::OpTypeForwardPointer;
 
     // We only care about types.
     if (!spvOpcodeGeneratesType(i->opcode()) && !is_i_forward_pointer) {
@@ -110,7 +108,7 @@
 
     if (!is_i_forward_pointer) {
       // Is the current type equal to one of the types we have already visited?
-      SpvId id_to_keep = 0u;
+      spv::Id id_to_keep = 0u;
       analysis::Type* i_type = type_manager.GetType(i->result_id());
       assert(i_type);
       // TODO(dneto0): Use a trie to avoid quadratic behaviour? Extract the
@@ -137,7 +135,7 @@
     } else {
       analysis::ForwardPointer i_type(
           i->GetSingleWordInOperand(0u),
-          (SpvStorageClass)i->GetSingleWordInOperand(1u));
+          (spv::StorageClass)i->GetSingleWordInOperand(1u));
       i_type.SetTargetPointer(
           type_manager.GetType(i_type.target_id())->AsPointer());
 
diff --git a/source/opt/remove_unused_interface_variables_pass.cpp b/source/opt/remove_unused_interface_variables_pass.cpp
index 31e87bd..d4df1b2 100644
--- a/source/opt/remove_unused_interface_variables_pass.cpp
+++ b/source/opt/remove_unused_interface_variables_pass.cpp
@@ -31,13 +31,14 @@
         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() != SpvOpVariable) return;
-          auto storage_class = var->GetSingleWordInOperand(0);
-          if (storage_class != SpvStorageClassFunction &&
+          if (!var || var->opcode() != spv::Op::OpVariable) return;
+          auto storage_class =
+              spv::StorageClass(var->GetSingleWordInOperand(0));
+          if (storage_class != spv::StorageClass::Function &&
               (parent_.get_module()->version() >=
                    SPV_SPIRV_VERSION_WORD(1, 4) ||
-               storage_class == SpvStorageClassInput ||
-               storage_class == SpvStorageClassOutput))
+               storage_class == spv::StorageClass::Input ||
+               storage_class == spv::StorageClass::Output))
             used_variables_.insert(*id);
         });
     return false;
@@ -90,4 +91,4 @@
   return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange);
 }
 }  // namespace opt
-}  // namespace spvtools
\ No newline at end of file
+}  // namespace spvtools
diff --git a/source/opt/remove_unused_interface_variables_pass.h b/source/opt/remove_unused_interface_variables_pass.h
index 7f11187..a4cb108 100644
--- a/source/opt/remove_unused_interface_variables_pass.h
+++ b/source/opt/remove_unused_interface_variables_pass.h
@@ -12,6 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#ifndef SOURCE_OPT_REMOVE_UNUSED_INTERFACE_VARIABLES_PASS_H_
+#define SOURCE_OPT_REMOVE_UNUSED_INTERFACE_VARIABLES_PASS_H_
+
 #include "source/opt/pass.h"
 namespace spvtools {
 namespace opt {
@@ -23,4 +26,6 @@
   Status Process() override;
 };
 }  // namespace opt
-}  // namespace spvtools
\ No newline at end of file
+}  // namespace spvtools
+
+#endif  // SOURCE_OPT_REMOVE_UNUSED_INTERFACE_VARIABLES_PASS_H_
\ No newline at end of file
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 e97593e..59745e1 100644
--- a/source/opt/replace_desc_array_access_using_var_index.cpp
+++ b/source/opt/replace_desc_array_access_using_var_index.cpp
@@ -21,11 +21,10 @@
 namespace spvtools {
 namespace opt {
 namespace {
-
-const uint32_t kOpAccessChainInOperandIndexes = 1;
-const uint32_t kOpTypePointerInOperandType = 1;
-const uint32_t kOpTypeArrayInOperandType = 0;
-const uint32_t kOpTypeStructInOperandMember = 0;
+constexpr uint32_t kOpAccessChainInOperandIndexes = 1;
+constexpr uint32_t kOpTypePointerInOperandType = 1;
+constexpr uint32_t kOpTypeArrayInOperandType = 0;
+constexpr uint32_t kOpTypeStructInOperandMember = 0;
 IRContext::Analysis kAnalysisDefUseAndInstrToBlockMapping =
     IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping;
 
@@ -54,8 +53,8 @@
   std::vector<Instruction*> work_list;
   get_def_use_mgr()->ForEachUser(var, [&work_list](Instruction* use) {
     switch (use->opcode()) {
-      case SpvOpAccessChain:
-      case SpvOpInBoundsAccessChain:
+      case spv::Op::OpAccessChain:
+      case spv::Op::OpInBoundsAccessChain:
         work_list.push_back(use);
         break;
       default:
@@ -132,8 +131,8 @@
     Instruction* operand = get_def_use_mgr()->GetDef(*idp);
     if (context()->get_instr_block(operand) != nullptr &&
         (HasImageOrImagePtrType(operand) ||
-         operand->opcode() == SpvOpAccessChain ||
-         operand->opcode() == SpvOpInBoundsAccessChain)) {
+         operand->opcode() == spv::Op::OpAccessChain ||
+         operand->opcode() == spv::Op::OpInBoundsAccessChain)) {
       work_list.push(operand);
     }
   };
@@ -158,22 +157,22 @@
 
 bool ReplaceDescArrayAccessUsingVarIndex::IsImageOrImagePtrType(
     const Instruction* type_inst) const {
-  if (type_inst->opcode() == SpvOpTypeImage ||
-      type_inst->opcode() == SpvOpTypeSampler ||
-      type_inst->opcode() == SpvOpTypeSampledImage) {
+  if (type_inst->opcode() == spv::Op::OpTypeImage ||
+      type_inst->opcode() == spv::Op::OpTypeSampler ||
+      type_inst->opcode() == spv::Op::OpTypeSampledImage) {
     return true;
   }
-  if (type_inst->opcode() == SpvOpTypePointer) {
+  if (type_inst->opcode() == spv::Op::OpTypePointer) {
     Instruction* pointee_type_inst = get_def_use_mgr()->GetDef(
         type_inst->GetSingleWordInOperand(kOpTypePointerInOperandType));
     return IsImageOrImagePtrType(pointee_type_inst);
   }
-  if (type_inst->opcode() == SpvOpTypeArray) {
+  if (type_inst->opcode() == spv::Op::OpTypeArray) {
     Instruction* element_type_inst = get_def_use_mgr()->GetDef(
         type_inst->GetSingleWordInOperand(kOpTypeArrayInOperandType));
     return IsImageOrImagePtrType(element_type_inst);
   }
-  if (type_inst->opcode() != SpvOpTypeStruct) return false;
+  if (type_inst->opcode() != spv::Op::OpTypeStruct) return false;
   for (uint32_t in_operand_idx = kOpTypeStructInOperandMember;
        in_operand_idx < type_inst->NumInOperands(); ++in_operand_idx) {
     Instruction* member_type_inst = get_def_use_mgr()->GetDef(
@@ -186,16 +185,16 @@
 bool ReplaceDescArrayAccessUsingVarIndex::IsConcreteType(
     uint32_t type_id) const {
   Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
-  if (type_inst->opcode() == SpvOpTypeInt ||
-      type_inst->opcode() == SpvOpTypeFloat) {
+  if (type_inst->opcode() == spv::Op::OpTypeInt ||
+      type_inst->opcode() == spv::Op::OpTypeFloat) {
     return true;
   }
-  if (type_inst->opcode() == SpvOpTypeVector ||
-      type_inst->opcode() == SpvOpTypeMatrix ||
-      type_inst->opcode() == SpvOpTypeArray) {
+  if (type_inst->opcode() == spv::Op::OpTypeVector ||
+      type_inst->opcode() == spv::Op::OpTypeMatrix ||
+      type_inst->opcode() == spv::Op::OpTypeArray) {
     return IsConcreteType(type_inst->GetSingleWordInOperand(0));
   }
-  if (type_inst->opcode() == SpvOpTypeStruct) {
+  if (type_inst->opcode() == spv::Op::OpTypeStruct) {
     for (uint32_t i = 0; i < type_inst->NumInOperands(); ++i) {
       if (!IsConcreteType(type_inst->GetSingleWordInOperand(i))) return false;
     }
@@ -322,8 +321,8 @@
 }
 
 BasicBlock* ReplaceDescArrayAccessUsingVarIndex::CreateNewBlock() const {
-  auto* new_block = new BasicBlock(std::unique_ptr<Instruction>(
-      new Instruction(context(), SpvOpLabel, 0, context()->TakeNextId(), {})));
+  auto* new_block = new BasicBlock(std::unique_ptr<Instruction>(new Instruction(
+      context(), spv::Op::OpLabel, 0, context()->TakeNextId(), {})));
   get_def_use_mgr()->AnalyzeInstDefUse(new_block->GetLabelInst());
   context()->set_instr_block(new_block->GetLabelInst(), new_block);
   return new_block;
@@ -332,7 +331,7 @@
 void ReplaceDescArrayAccessUsingVarIndex::UseConstIndexForAccessChain(
     Instruction* access_chain, uint32_t const_element_idx) const {
   uint32_t const_element_idx_id =
-      context()->get_constant_mgr()->GetUIntConst(const_element_idx);
+      context()->get_constant_mgr()->GetUIntConstId(const_element_idx);
   access_chain->SetInOperand(kOpAccessChainInOperandIndexes,
                              {const_element_idx_id});
 }
@@ -422,7 +421,7 @@
     uint32_t old_incoming_block_id, uint32_t new_incoming_block_id) const {
   context()->ReplaceAllUsesWithPredicate(
       old_incoming_block_id, new_incoming_block_id,
-      [](Instruction* use) { return use->opcode() == SpvOpPhi; });
+      [](Instruction* use) { return use->opcode() == spv::Op::OpPhi; });
 }
 
 }  // namespace opt
diff --git a/source/opt/replace_invalid_opc.cpp b/source/opt/replace_invalid_opc.cpp
index 1dcd06f..1b97c0e 100644
--- a/source/opt/replace_invalid_opc.cpp
+++ b/source/opt/replace_invalid_opc.cpp
@@ -23,16 +23,16 @@
 Pass::Status ReplaceInvalidOpcodePass::Process() {
   bool modified = false;
 
-  if (context()->get_feature_mgr()->HasCapability(SpvCapabilityLinkage)) {
+  if (context()->get_feature_mgr()->HasCapability(spv::Capability::Linkage)) {
     return Status::SuccessWithoutChange;
   }
 
-  SpvExecutionModel execution_model = GetExecutionModel();
-  if (execution_model == SpvExecutionModelKernel) {
+  spv::ExecutionModel execution_model = GetExecutionModel();
+  if (execution_model == spv::ExecutionModel::Kernel) {
     // We do not handle kernels.
     return Status::SuccessWithoutChange;
   }
-  if (execution_model == SpvExecutionModelMax) {
+  if (execution_model == spv::ExecutionModel::Max) {
     // Mixed execution models for the entry points.  This case is not currently
     // handled.
     return Status::SuccessWithoutChange;
@@ -44,19 +44,19 @@
   return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange);
 }
 
-SpvExecutionModel ReplaceInvalidOpcodePass::GetExecutionModel() {
-  SpvExecutionModel result = SpvExecutionModelMax;
+spv::ExecutionModel ReplaceInvalidOpcodePass::GetExecutionModel() {
+  spv::ExecutionModel result = spv::ExecutionModel::Max;
   bool first = true;
   for (Instruction& entry_point : get_module()->entry_points()) {
     if (first) {
-      result =
-          static_cast<SpvExecutionModel>(entry_point.GetSingleWordInOperand(0));
+      result = static_cast<spv::ExecutionModel>(
+          entry_point.GetSingleWordInOperand(0));
       first = false;
     } else {
-      SpvExecutionModel current_model =
-          static_cast<SpvExecutionModel>(entry_point.GetSingleWordInOperand(0));
+      spv::ExecutionModel current_model = static_cast<spv::ExecutionModel>(
+          entry_point.GetSingleWordInOperand(0));
       if (current_model != result) {
-        result = SpvExecutionModelMax;
+        result = spv::ExecutionModel::Max;
         break;
       }
     }
@@ -65,13 +65,13 @@
 }
 
 bool ReplaceInvalidOpcodePass::RewriteFunction(Function* function,
-                                               SpvExecutionModel model) {
+                                               spv::ExecutionModel model) {
   bool modified = false;
   Instruction* last_line_dbg_inst = nullptr;
   function->ForEachInst(
       [model, &modified, &last_line_dbg_inst, this](Instruction* inst) {
         // Track the debug information so we can have a meaningful message.
-        if (inst->opcode() == SpvOpLabel || inst->IsNoLine()) {
+        if (inst->opcode() == spv::Op::OpLabel || inst->IsNoLine()) {
           last_line_dbg_inst = nullptr;
           return;
         } else if (inst->IsLine()) {
@@ -80,15 +80,16 @@
         }
 
         bool replace = false;
-        if (model != SpvExecutionModelFragment &&
+        if (model != spv::ExecutionModel::Fragment &&
             IsFragmentShaderOnlyInstruction(inst)) {
           replace = true;
         }
 
-        if (model != SpvExecutionModelTessellationControl &&
-            model != SpvExecutionModelGLCompute) {
-          if (inst->opcode() == SpvOpControlBarrier) {
-            assert(model != SpvExecutionModelKernel &&
+        if (model != spv::ExecutionModel::TessellationControl &&
+            model != spv::ExecutionModel::GLCompute &&
+            !context()->IsTargetEnvAtLeast(SPV_ENV_UNIVERSAL_1_3)) {
+          if (inst->opcode() == spv::Op::OpControlBarrier) {
+            assert(model != spv::ExecutionModel::Kernel &&
                    "Expecting to be working on a shader module.");
             replace = true;
           }
@@ -101,7 +102,7 @@
           } else {
             // Get the name of the source file.
             uint32_t file_name_id = 0;
-            if (last_line_dbg_inst->opcode() == SpvOpLine) {
+            if (last_line_dbg_inst->opcode() == spv::Op::OpLine) {
               file_name_id = last_line_dbg_inst->GetSingleWordInOperand(0);
             } else {  // Shader100::DebugLine
               uint32_t debug_source_id =
@@ -131,26 +132,26 @@
 bool ReplaceInvalidOpcodePass::IsFragmentShaderOnlyInstruction(
     Instruction* inst) {
   switch (inst->opcode()) {
-    case SpvOpDPdx:
-    case SpvOpDPdy:
-    case SpvOpFwidth:
-    case SpvOpDPdxFine:
-    case SpvOpDPdyFine:
-    case SpvOpFwidthFine:
-    case SpvOpDPdxCoarse:
-    case SpvOpDPdyCoarse:
-    case SpvOpFwidthCoarse:
-    case SpvOpImageSampleImplicitLod:
-    case SpvOpImageSampleDrefImplicitLod:
-    case SpvOpImageSampleProjImplicitLod:
-    case SpvOpImageSampleProjDrefImplicitLod:
-    case SpvOpImageSparseSampleImplicitLod:
-    case SpvOpImageSparseSampleDrefImplicitLod:
-    case SpvOpImageQueryLod:
+    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:
+    case spv::Op::OpImageSampleImplicitLod:
+    case spv::Op::OpImageSampleDrefImplicitLod:
+    case spv::Op::OpImageSampleProjImplicitLod:
+    case spv::Op::OpImageSampleProjDrefImplicitLod:
+    case spv::Op::OpImageSparseSampleImplicitLod:
+    case spv::Op::OpImageSparseSampleDrefImplicitLod:
+    case spv::Op::OpImageQueryLod:
       // TODO: Teach |ReplaceInstruction| to handle block terminators.  Then
       // uncomment the OpKill case.
-      // case SpvOpKill:
-      // case SpvOpTerminateInstruction:
+      // case spv::Op::OpKill:
+      // case spv::Op::OpTerminateInstruction:
       return true;
     default:
       return false;
@@ -183,7 +184,7 @@
   analysis::TypeManager* type_mgr = context()->get_type_mgr();
 
   Instruction* type = context()->get_def_use_mgr()->GetDef(type_id);
-  if (type->opcode() == SpvOpTypeVector) {
+  if (type->opcode() == spv::Op::OpTypeVector) {
     uint32_t component_const =
         GetSpecialConstant(type->GetSingleWordInOperand(0));
     std::vector<uint32_t> ids;
@@ -192,7 +193,8 @@
     }
     special_const = const_mgr->GetConstant(type_mgr->GetType(type_id), ids);
   } else {
-    assert(type->opcode() == SpvOpTypeInt || type->opcode() == SpvOpTypeFloat);
+    assert(type->opcode() == spv::Op::OpTypeInt ||
+           type->opcode() == spv::Op::OpTypeFloat);
     std::vector<uint32_t> literal_words;
     for (uint32_t i = 0; i < type->GetSingleWordInOperand(0); i += 32) {
       literal_words.push_back(0xDEADBEEF);
@@ -204,7 +206,7 @@
   return const_mgr->GetDefiningInstruction(special_const)->result_id();
 }
 
-std::string ReplaceInvalidOpcodePass::BuildWarningMessage(SpvOp opcode) {
+std::string ReplaceInvalidOpcodePass::BuildWarningMessage(spv::Op opcode) {
   spv_opcode_desc opcode_info;
   context()->grammar().lookupOpcode(opcode, &opcode_info);
   std::string message = "Removing ";
diff --git a/source/opt/replace_invalid_opc.h b/source/opt/replace_invalid_opc.h
index 426bcac..3f0d16b 100644
--- a/source/opt/replace_invalid_opc.h
+++ b/source/opt/replace_invalid_opc.h
@@ -34,13 +34,13 @@
  private:
   // Returns the execution model that is used by every entry point in the
   // module. If more than one execution model is used in the module, then the
-  // return value is SpvExecutionModelMax.
-  SpvExecutionModel GetExecutionModel();
+  // return value is spv::ExecutionModel::Max.
+  spv::ExecutionModel GetExecutionModel();
 
   // Replaces all instructions in |function| that are invalid with execution
   // model |mode|, but valid for another shader model, with a special constant
   // value.  See |GetSpecialConstant|.
-  bool RewriteFunction(Function* function, SpvExecutionModel mode);
+  bool RewriteFunction(Function* function, spv::ExecutionModel mode);
 
   // Returns true if |inst| is valid for fragment shaders only.
   bool IsFragmentShaderOnlyInstruction(Instruction* inst);
@@ -58,7 +58,7 @@
   // width of the type has been reached. For a vector, each element of the
   // constant will be constructed the same way.
   uint32_t GetSpecialConstant(uint32_t type_id);
-  std::string BuildWarningMessage(SpvOp opcode);
+  std::string BuildWarningMessage(spv::Op opcode);
 };
 
 }  // namespace opt
diff --git a/source/opt/scalar_analysis.cpp b/source/opt/scalar_analysis.cpp
index 2b0a824..26cc8b3 100644
--- a/source/opt/scalar_analysis.cpp
+++ b/source/opt/scalar_analysis.cpp
@@ -14,7 +14,6 @@
 
 #include "source/opt/scalar_analysis.h"
 
-#include <algorithm>
 #include <functional>
 #include <string>
 #include <utility>
@@ -97,7 +96,7 @@
 
 SENode* ScalarEvolutionAnalysis::AnalyzeMultiplyOp(
     const Instruction* multiply) {
-  assert(multiply->opcode() == SpvOp::SpvOpIMul &&
+  assert(multiply->opcode() == spv::Op::OpIMul &&
          "Multiply node did not come from a multiply instruction");
   analysis::DefUseManager* def_use = context_->get_def_use_mgr();
 
@@ -168,21 +167,21 @@
 
   SENode* output = nullptr;
   switch (inst->opcode()) {
-    case SpvOp::SpvOpPhi: {
+    case spv::Op::OpPhi: {
       output = AnalyzePhiInstruction(inst);
       break;
     }
-    case SpvOp::SpvOpConstant:
-    case SpvOp::SpvOpConstantNull: {
+    case spv::Op::OpConstant:
+    case spv::Op::OpConstantNull: {
       output = AnalyzeConstant(inst);
       break;
     }
-    case SpvOp::SpvOpISub:
-    case SpvOp::SpvOpIAdd: {
+    case spv::Op::OpISub:
+    case spv::Op::OpIAdd: {
       output = AnalyzeAddOp(inst);
       break;
     }
-    case SpvOp::SpvOpIMul: {
+    case spv::Op::OpIMul: {
       output = AnalyzeMultiplyOp(inst);
       break;
     }
@@ -196,9 +195,9 @@
 }
 
 SENode* ScalarEvolutionAnalysis::AnalyzeConstant(const Instruction* inst) {
-  if (inst->opcode() == SpvOp::SpvOpConstantNull) return CreateConstant(0);
+  if (inst->opcode() == spv::Op::OpConstantNull) return CreateConstant(0);
 
-  assert(inst->opcode() == SpvOp::SpvOpConstant);
+  assert(inst->opcode() == spv::Op::OpConstant);
   assert(inst->NumInOperands() == 1);
   int64_t value = 0;
 
@@ -226,8 +225,8 @@
 // Handles both addition and subtraction. If the |sub| flag is set then the
 // addition will be op1+(-op2) otherwise op1+op2.
 SENode* ScalarEvolutionAnalysis::AnalyzeAddOp(const Instruction* inst) {
-  assert((inst->opcode() == SpvOp::SpvOpIAdd ||
-          inst->opcode() == SpvOp::SpvOpISub) &&
+  assert((inst->opcode() == spv::Op::OpIAdd ||
+          inst->opcode() == spv::Op::OpISub) &&
          "Add node must be created from a OpIAdd or OpISub instruction");
 
   analysis::DefUseManager* def_use = context_->get_def_use_mgr();
@@ -239,7 +238,7 @@
       AnalyzeInstruction(def_use->GetDef(inst->GetSingleWordInOperand(1)));
 
   // To handle subtraction we wrap the second operand in a unary negation node.
-  if (inst->opcode() == SpvOp::SpvOpISub) {
+  if (inst->opcode() == spv::Op::OpISub) {
     op2 = CreateNegation(op2);
   }
 
@@ -573,7 +572,7 @@
 };
 
 template <typename T>
-static void PushToString(T id, std::u32string* str) {
+void PushToString(T id, std::u32string* str) {
   PushToStringImpl<T, sizeof(T)>{}(id, str);
 }
 
@@ -928,8 +927,8 @@
 
 // Remove |node| from the |mul| chain (of the form A * ... * |node| * ... * Z),
 // if |node| is not in the chain, returns the original chain.
-static SENode* RemoveOneNodeFromMultiplyChain(SEMultiplyNode* mul,
-                                              const SENode* node) {
+SENode* RemoveOneNodeFromMultiplyChain(SEMultiplyNode* mul,
+                                       const SENode* node) {
   SENode* lhs = mul->GetChildren()[0];
   SENode* rhs = mul->GetChildren()[1];
   if (lhs == node) {
diff --git a/source/opt/scalar_analysis_simplification.cpp b/source/opt/scalar_analysis_simplification.cpp
index 3c1ecc0..3c0947c 100644
--- a/source/opt/scalar_analysis_simplification.cpp
+++ b/source/opt/scalar_analysis_simplification.cpp
@@ -12,16 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "source/opt/scalar_analysis.h"
-
 #include <functional>
 #include <map>
 #include <memory>
 #include <set>
-#include <unordered_set>
 #include <utility>
 #include <vector>
 
+#include "source/opt/scalar_analysis.h"
+
 // Simplifies scalar analysis DAGs.
 //
 // 1. Given a node passed to SimplifyExpression we first simplify the graph by
diff --git a/source/opt/scalar_replacement_pass.cpp b/source/opt/scalar_replacement_pass.cpp
index e27c828..38c8aec 100644
--- a/source/opt/scalar_replacement_pass.cpp
+++ b/source/opt/scalar_replacement_pass.cpp
@@ -19,19 +19,18 @@
 #include <tuple>
 #include <utility>
 
-#include "source/enum_string_mapping.h"
 #include "source/extensions.h"
 #include "source/opt/reflect.h"
 #include "source/opt/types.h"
 #include "source/util/make_unique.h"
-#include "types.h"
-
-static const uint32_t kDebugValueOperandValueIndex = 5;
-static const uint32_t kDebugValueOperandExpressionIndex = 6;
-static const uint32_t kDebugDeclareOperandVariableIndex = 5;
 
 namespace spvtools {
 namespace opt {
+namespace {
+constexpr uint32_t kDebugValueOperandValueIndex = 5;
+constexpr uint32_t kDebugValueOperandExpressionIndex = 6;
+constexpr uint32_t kDebugDeclareOperandVariableIndex = 5;
+}  // namespace
 
 Pass::Status ScalarReplacementPass::Process() {
   Status status = Status::SuccessWithoutChange;
@@ -56,7 +55,7 @@
   for (auto iter = entry.begin(); iter != entry.end(); ++iter) {
     // Function storage class OpVariables must appear as the first instructions
     // of the entry block.
-    if (iter->opcode() != SpvOpVariable) break;
+    if (iter->opcode() != spv::Op::OpVariable) break;
 
     Instruction* varInst = &*iter;
     if (CanReplaceVariable(varInst)) {
@@ -105,29 +104,29 @@
         }
         if (!IsAnnotationInst(user->opcode())) {
           switch (user->opcode()) {
-            case SpvOpLoad:
+            case spv::Op::OpLoad:
               if (ReplaceWholeLoad(user, replacements)) {
                 dead.push_back(user);
               } else {
                 return false;
               }
               break;
-            case SpvOpStore:
+            case spv::Op::OpStore:
               if (ReplaceWholeStore(user, replacements)) {
                 dead.push_back(user);
               } else {
                 return false;
               }
               break;
-            case SpvOpAccessChain:
-            case SpvOpInBoundsAccessChain:
+            case spv::Op::OpAccessChain:
+            case spv::Op::OpInBoundsAccessChain:
               if (ReplaceAccessChain(user, replacements))
                 dead.push_back(user);
               else
                 return false;
               break;
-            case SpvOpName:
-            case SpvOpMemberName:
+            case spv::Op::OpName:
+            case spv::Op::OpMemberName:
               break;
             default:
               assert(false && "Unexpected opcode");
@@ -155,7 +154,7 @@
 
   // Attempt to further scalarize.
   for (auto var : replacements) {
-    if (var->opcode() == SpvOpVariable) {
+    if (var->opcode() == spv::Op::OpVariable) {
       if (get_def_use_mgr()->NumUsers(var) == 0) {
         context()->KillInst(var);
       } else if (CanReplaceVariable(var)) {
@@ -179,7 +178,7 @@
   int32_t idx = 0;
   for (const auto* var : replacements) {
     Instruction* insert_before = var->NextNode();
-    while (insert_before->opcode() == SpvOpVariable)
+    while (insert_before->opcode() == spv::Op::OpVariable)
       insert_before = insert_before->NextNode();
     assert(insert_before != nullptr && "unexpected end of list");
     Instruction* added_dbg_value =
@@ -190,7 +189,7 @@
     if (added_dbg_value == nullptr) return false;
     added_dbg_value->AddOperand(
         {SPV_OPERAND_TYPE_ID,
-         {context()->get_constant_mgr()->GetSIntConst(idx)}});
+         {context()->get_constant_mgr()->GetSIntConstId(idx)}});
     added_dbg_value->SetOperand(kDebugValueOperandExpressionIndex,
                                 {deref_expr->result_id()});
     if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse)) {
@@ -216,7 +215,7 @@
     // Append 'Indexes' operand.
     new_dbg_value->AddOperand(
         {SPV_OPERAND_TYPE_ID,
-         {context()->get_constant_mgr()->GetSIntConst(idx)}});
+         {context()->get_constant_mgr()->GetSIntConstId(idx)}});
     // Insert the new DebugValue to the basic block.
     auto* added_instr = dbg_value->InsertBefore(std::move(new_dbg_value));
     get_def_use_mgr()->AnalyzeInstDefUse(added_instr);
@@ -236,7 +235,7 @@
   BasicBlock::iterator where(load);
   for (auto var : replacements) {
     // Create a load of each replacement variable.
-    if (var->opcode() != SpvOpVariable) {
+    if (var->opcode() != spv::Op::OpVariable) {
       loads.push_back(var);
       continue;
     }
@@ -247,7 +246,7 @@
       return false;
     }
     std::unique_ptr<Instruction> newLoad(
-        new Instruction(context(), SpvOpLoad, type->result_id(), loadId,
+        new Instruction(context(), spv::Op::OpLoad, type->result_id(), loadId,
                         std::initializer_list<Operand>{
                             {SPV_OPERAND_TYPE_ID, {var->result_id()}}}));
     // Copy memory access attributes which start at index 1. Index 0 is the
@@ -269,8 +268,9 @@
     return false;
   }
   where = load;
-  std::unique_ptr<Instruction> compositeConstruct(new Instruction(
-      context(), SpvOpCompositeConstruct, load->type_id(), compositeId, {}));
+  std::unique_ptr<Instruction> compositeConstruct(
+      new Instruction(context(), spv::Op::OpCompositeConstruct, load->type_id(),
+                      compositeId, {}));
   for (auto l : loads) {
     Operand op(SPV_OPERAND_TYPE_ID,
                std::initializer_list<uint32_t>{l->result_id()});
@@ -294,7 +294,7 @@
   uint32_t elementIndex = 0;
   for (auto var : replacements) {
     // Create the extract.
-    if (var->opcode() != SpvOpVariable) {
+    if (var->opcode() != spv::Op::OpVariable) {
       elementIndex++;
       continue;
     }
@@ -305,7 +305,7 @@
       return false;
     }
     std::unique_ptr<Instruction> extract(new Instruction(
-        context(), SpvOpCompositeExtract, type->result_id(), extractId,
+        context(), spv::Op::OpCompositeExtract, type->result_id(), extractId,
         std::initializer_list<Operand>{
             {SPV_OPERAND_TYPE_ID, {storeInput}},
             {SPV_OPERAND_TYPE_LITERAL_INTEGER, {elementIndex++}}}));
@@ -316,7 +316,7 @@
 
     // Create the store.
     std::unique_ptr<Instruction> newStore(
-        new Instruction(context(), SpvOpStore, 0, 0,
+        new Instruction(context(), spv::Op::OpStore, 0, 0,
                         std::initializer_list<Operand>{
                             {SPV_OPERAND_TYPE_ID, {var->result_id()}},
                             {SPV_OPERAND_TYPE_ID, {extractId}}}));
@@ -390,7 +390,7 @@
 
   uint32_t elem = 0;
   switch (type->opcode()) {
-    case SpvOpTypeStruct:
+    case spv::Op::OpTypeStruct:
       type->ForEachInOperand(
           [this, inst, &elem, replacements, &components_used](uint32_t* id) {
             if (!components_used || components_used->count(elem)) {
@@ -401,7 +401,7 @@
             elem++;
           });
       break;
-    case SpvOpTypeArray:
+    case spv::Op::OpTypeArray:
       for (uint32_t i = 0; i != GetArrayLength(type); ++i) {
         if (!components_used || components_used->count(i)) {
           CreateVariable(type->GetSingleWordInOperand(0u), inst, i,
@@ -413,8 +413,8 @@
       }
       break;
 
-    case SpvOpTypeMatrix:
-    case SpvOpTypeVector:
+    case spv::Op::OpTypeMatrix:
+    case spv::Op::OpTypeVector:
       for (uint32_t i = 0; i != GetNumElements(type); ++i) {
         CreateVariable(type->GetSingleWordInOperand(0u), inst, i, replacements);
       }
@@ -440,20 +440,20 @@
   // no type or member decorations that are necessary to transfer.
   for (auto inst :
        get_decoration_mgr()->GetDecorationsFor(source->result_id(), false)) {
-    assert(inst->opcode() == SpvOpDecorate);
-    uint32_t decoration = inst->GetSingleWordInOperand(1u);
-    if (decoration == SpvDecorationInvariant ||
-        decoration == SpvDecorationRestrict) {
+    assert(inst->opcode() == spv::Op::OpDecorate);
+    auto decoration = spv::Decoration(inst->GetSingleWordInOperand(1u));
+    if (decoration == spv::Decoration::Invariant ||
+        decoration == spv::Decoration::Restrict) {
       for (auto var : *replacements) {
         if (var == nullptr) {
           continue;
         }
 
-        std::unique_ptr<Instruction> annotation(
-            new Instruction(context(), SpvOpDecorate, 0, 0,
-                            std::initializer_list<Operand>{
-                                {SPV_OPERAND_TYPE_ID, {var->result_id()}},
-                                {SPV_OPERAND_TYPE_DECORATION, {decoration}}}));
+        std::unique_ptr<Instruction> annotation(new Instruction(
+            context(), spv::Op::OpDecorate, 0, 0,
+            std::initializer_list<Operand>{
+                {SPV_OPERAND_TYPE_ID, {var->result_id()}},
+                {SPV_OPERAND_TYPE_DECORATION, {uint32_t(decoration)}}}));
         for (uint32_t i = 2; i < inst->NumInOperands(); ++i) {
           Operand copy(inst->GetInOperand(i));
           annotation->AddOperand(std::move(copy));
@@ -466,60 +466,32 @@
 }
 
 void ScalarReplacementPass::CreateVariable(
-    uint32_t typeId, Instruction* varInst, uint32_t index,
+    uint32_t type_id, Instruction* var_inst, uint32_t index,
     std::vector<Instruction*>* replacements) {
-  uint32_t ptrId = GetOrCreatePointerType(typeId);
+  uint32_t ptr_id = GetOrCreatePointerType(type_id);
   uint32_t id = TakeNextId();
 
   if (id == 0) {
     replacements->push_back(nullptr);
   }
 
-  std::unique_ptr<Instruction> variable(new Instruction(
-      context(), SpvOpVariable, ptrId, id,
-      std::initializer_list<Operand>{
-          {SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}));
+  std::unique_ptr<Instruction> variable(
+      new Instruction(context(), spv::Op::OpVariable, ptr_id, id,
+                      std::initializer_list<Operand>{
+                          {SPV_OPERAND_TYPE_STORAGE_CLASS,
+                           {uint32_t(spv::StorageClass::Function)}}}));
 
-  BasicBlock* block = context()->get_instr_block(varInst);
+  BasicBlock* block = context()->get_instr_block(var_inst);
   block->begin().InsertBefore(std::move(variable));
   Instruction* inst = &*block->begin();
 
   // If varInst was initialized, make sure to initialize its replacement.
-  GetOrCreateInitialValue(varInst, index, inst);
+  GetOrCreateInitialValue(var_inst, index, inst);
   get_def_use_mgr()->AnalyzeInstDefUse(inst);
   context()->set_instr_block(inst, block);
 
-  // Copy decorations from the member to the new variable.
-  Instruction* typeInst = GetStorageType(varInst);
-  for (auto dec_inst :
-       get_decoration_mgr()->GetDecorationsFor(typeInst->result_id(), false)) {
-    uint32_t decoration;
-    if (dec_inst->opcode() != SpvOpMemberDecorate) {
-      continue;
-    }
-
-    if (dec_inst->GetSingleWordInOperand(1) != index) {
-      continue;
-    }
-
-    decoration = dec_inst->GetSingleWordInOperand(2u);
-    switch (decoration) {
-      case SpvDecorationRelaxedPrecision: {
-        std::unique_ptr<Instruction> new_dec_inst(
-            new Instruction(context(), SpvOpDecorate, 0, 0, {}));
-        new_dec_inst->AddOperand(Operand(SPV_OPERAND_TYPE_ID, {id}));
-        for (uint32_t i = 2; i < dec_inst->NumInOperandWords(); ++i) {
-          new_dec_inst->AddOperand(Operand(dec_inst->GetInOperand(i)));
-        }
-        context()->AddAnnotationInst(std::move(new_dec_inst));
-      } break;
-      default:
-        break;
-    }
-  }
-
-  // Update the DebugInfo debug information.
-  inst->UpdateDebugInfoFrom(varInst);
+  CopyDecorationsToVariable(var_inst, inst, index);
+  inst->UpdateDebugInfoFrom(var_inst);
 
   replacements->push_back(inst);
 }
@@ -528,57 +500,17 @@
   auto iter = pointee_to_pointer_.find(id);
   if (iter != pointee_to_pointer_.end()) return iter->second;
 
-  analysis::Type* pointeeTy;
-  std::unique_ptr<analysis::Pointer> pointerTy;
-  std::tie(pointeeTy, pointerTy) =
-      context()->get_type_mgr()->GetTypeAndPointerType(id,
-                                                       SpvStorageClassFunction);
-  uint32_t ptrId = 0;
-  if (pointeeTy->IsUniqueType()) {
-    // Non-ambiguous type, just ask the type manager for an id.
-    ptrId = context()->get_type_mgr()->GetTypeInstruction(pointerTy.get());
-    pointee_to_pointer_[id] = ptrId;
-    return ptrId;
-  }
-
-  // Ambiguous type. We must perform a linear search to try and find the right
-  // type.
-  for (auto global : context()->types_values()) {
-    if (global.opcode() == SpvOpTypePointer &&
-        global.GetSingleWordInOperand(0u) == SpvStorageClassFunction &&
-        global.GetSingleWordInOperand(1u) == id) {
-      if (get_decoration_mgr()->GetDecorationsFor(id, false).empty()) {
-        // Only reuse a decoration-less pointer of the correct type.
-        ptrId = global.result_id();
-        break;
-      }
-    }
-  }
-
-  if (ptrId != 0) {
-    pointee_to_pointer_[id] = ptrId;
-    return ptrId;
-  }
-
-  ptrId = TakeNextId();
-  context()->AddType(MakeUnique<Instruction>(
-      context(), SpvOpTypePointer, 0, ptrId,
-      std::initializer_list<Operand>{
-          {SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}},
-          {SPV_OPERAND_TYPE_ID, {id}}}));
-  Instruction* ptr = &*--context()->types_values_end();
-  get_def_use_mgr()->AnalyzeInstDefUse(ptr);
-  pointee_to_pointer_[id] = ptrId;
-  // Register with the type manager if necessary.
-  context()->get_type_mgr()->RegisterType(ptrId, *pointerTy);
-
-  return ptrId;
+  analysis::TypeManager* type_mgr = context()->get_type_mgr();
+  uint32_t ptr_type_id =
+      type_mgr->FindPointerToType(id, spv::StorageClass::Function);
+  pointee_to_pointer_[id] = ptr_type_id;
+  return ptr_type_id;
 }
 
 void ScalarReplacementPass::GetOrCreateInitialValue(Instruction* source,
                                                     uint32_t index,
                                                     Instruction* newVar) {
-  assert(source->opcode() == SpvOpVariable);
+  assert(source->opcode() == spv::Op::OpVariable);
   if (source->NumInOperands() < 2) return;
 
   uint32_t initId = source->GetSingleWordInOperand(1u);
@@ -586,14 +518,14 @@
   Instruction* init = get_def_use_mgr()->GetDef(initId);
   uint32_t newInitId = 0;
   // TODO(dnovillo): Refactor this with constant propagation.
-  if (init->opcode() == SpvOpConstantNull) {
+  if (init->opcode() == spv::Op::OpConstantNull) {
     // Initialize to appropriate NULL.
     auto iter = type_to_null_.find(storageId);
     if (iter == type_to_null_.end()) {
       newInitId = TakeNextId();
       type_to_null_[storageId] = newInitId;
       context()->AddGlobalValue(
-          MakeUnique<Instruction>(context(), SpvOpConstantNull, storageId,
+          MakeUnique<Instruction>(context(), spv::Op::OpConstantNull, storageId,
                                   newInitId, std::initializer_list<Operand>{}));
       Instruction* newNull = &*--context()->types_values_end();
       get_def_use_mgr()->AnalyzeInstDefUse(newNull);
@@ -604,18 +536,19 @@
     // Create a new constant extract.
     newInitId = TakeNextId();
     context()->AddGlobalValue(MakeUnique<Instruction>(
-        context(), SpvOpSpecConstantOp, storageId, newInitId,
+        context(), spv::Op::OpSpecConstantOp, storageId, newInitId,
         std::initializer_list<Operand>{
-            {SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER, {SpvOpCompositeExtract}},
+            {SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER,
+             {uint32_t(spv::Op::OpCompositeExtract)}},
             {SPV_OPERAND_TYPE_ID, {init->result_id()}},
             {SPV_OPERAND_TYPE_LITERAL_INTEGER, {index}}}));
     Instruction* newSpecConst = &*--context()->types_values_end();
     get_def_use_mgr()->AnalyzeInstDefUse(newSpecConst);
-  } else if (init->opcode() == SpvOpConstantComposite) {
+  } else if (init->opcode() == spv::Op::OpConstantComposite) {
     // Get the appropriate index constant.
     newInitId = init->GetSingleWordInOperand(index);
     Instruction* element = get_def_use_mgr()->GetDef(newInitId);
-    if (element->opcode() == SpvOpUndef) {
+    if (element->opcode() == spv::Op::OpUndef) {
       // Undef is not a valid initializer for a variable.
       newInitId = 0;
     }
@@ -630,7 +563,7 @@
 
 uint64_t ScalarReplacementPass::GetArrayLength(
     const Instruction* arrayType) const {
-  assert(arrayType->opcode() == SpvOpTypeArray);
+  assert(arrayType->opcode() == spv::Op::OpTypeArray);
   const Instruction* length =
       get_def_use_mgr()->GetDef(arrayType->GetSingleWordInOperand(1u));
   return context()
@@ -640,8 +573,8 @@
 }
 
 uint64_t ScalarReplacementPass::GetNumElements(const Instruction* type) const {
-  assert(type->opcode() == SpvOpTypeVector ||
-         type->opcode() == SpvOpTypeMatrix);
+  assert(type->opcode() == spv::Op::OpTypeVector ||
+         type->opcode() == spv::Op::OpTypeMatrix);
   const Operand& op = type->GetInOperand(1u);
   assert(op.words.size() <= 2);
   uint64_t len = 0;
@@ -659,7 +592,7 @@
 
 Instruction* ScalarReplacementPass::GetStorageType(
     const Instruction* inst) const {
-  assert(inst->opcode() == SpvOpVariable);
+  assert(inst->opcode() == spv::Op::OpVariable);
 
   uint32_t ptrTypeId = inst->type_id();
   uint32_t typeId =
@@ -669,10 +602,11 @@
 
 bool ScalarReplacementPass::CanReplaceVariable(
     const Instruction* varInst) const {
-  assert(varInst->opcode() == SpvOpVariable);
+  assert(varInst->opcode() == spv::Op::OpVariable);
 
   // Can only replace function scope variables.
-  if (varInst->GetSingleWordInOperand(0u) != SpvStorageClassFunction) {
+  if (spv::StorageClass(varInst->GetSingleWordInOperand(0u)) !=
+      spv::StorageClass::Function) {
     return false;
   }
 
@@ -702,14 +636,14 @@
   }
 
   switch (typeInst->opcode()) {
-    case SpvOpTypeStruct:
+    case spv::Op::OpTypeStruct:
       // Don't bother with empty structs or very large structs.
       if (typeInst->NumInOperands() == 0 ||
           IsLargerThanSizeLimit(typeInst->NumInOperands())) {
         return false;
       }
       return true;
-    case SpvOpTypeArray:
+    case spv::Op::OpTypeArray:
       if (IsSpecConstant(typeInst->GetSingleWordInOperand(1u))) {
         return false;
       }
@@ -721,12 +655,12 @@
       // re-enabled.
       //// Specifically including matrix and vector in an attempt to reduce the
       //// number of vector registers required.
-      // case SpvOpTypeMatrix:
-      // case SpvOpTypeVector:
+      // case spv::Op::OpTypeMatrix:
+      // case spv::Op::OpTypeVector:
       //  if (IsLargerThanSizeLimit(GetNumElements(typeInst))) return false;
       //  return true;
 
-    case SpvOpTypeRuntimeArray:
+    case spv::Op::OpTypeRuntimeArray:
     default:
       return false;
   }
@@ -737,26 +671,28 @@
   for (auto inst :
        get_decoration_mgr()->GetDecorationsFor(typeInst->result_id(), false)) {
     uint32_t decoration;
-    if (inst->opcode() == SpvOpDecorate) {
+    if (inst->opcode() == spv::Op::OpDecorate) {
       decoration = inst->GetSingleWordInOperand(1u);
     } else {
-      assert(inst->opcode() == SpvOpMemberDecorate);
+      assert(inst->opcode() == spv::Op::OpMemberDecorate);
       decoration = inst->GetSingleWordInOperand(2u);
     }
 
-    switch (decoration) {
-      case SpvDecorationRowMajor:
-      case SpvDecorationColMajor:
-      case SpvDecorationArrayStride:
-      case SpvDecorationMatrixStride:
-      case SpvDecorationCPacked:
-      case SpvDecorationInvariant:
-      case SpvDecorationRestrict:
-      case SpvDecorationOffset:
-      case SpvDecorationAlignment:
-      case SpvDecorationAlignmentId:
-      case SpvDecorationMaxByteOffset:
-      case SpvDecorationRelaxedPrecision:
+    switch (spv::Decoration(decoration)) {
+      case spv::Decoration::RowMajor:
+      case spv::Decoration::ColMajor:
+      case spv::Decoration::ArrayStride:
+      case spv::Decoration::MatrixStride:
+      case spv::Decoration::CPacked:
+      case spv::Decoration::Invariant:
+      case spv::Decoration::Restrict:
+      case spv::Decoration::Offset:
+      case spv::Decoration::Alignment:
+      case spv::Decoration::AlignmentId:
+      case spv::Decoration::MaxByteOffset:
+      case spv::Decoration::RelaxedPrecision:
+      case spv::Decoration::AliasedPointer:
+      case spv::Decoration::RestrictPointer:
         break;
       default:
         return false;
@@ -769,14 +705,16 @@
 bool ScalarReplacementPass::CheckAnnotations(const Instruction* varInst) const {
   for (auto inst :
        get_decoration_mgr()->GetDecorationsFor(varInst->result_id(), false)) {
-    assert(inst->opcode() == SpvOpDecorate);
-    uint32_t decoration = inst->GetSingleWordInOperand(1u);
+    assert(inst->opcode() == spv::Op::OpDecorate);
+    auto decoration = spv::Decoration(inst->GetSingleWordInOperand(1u));
     switch (decoration) {
-      case SpvDecorationInvariant:
-      case SpvDecorationRestrict:
-      case SpvDecorationAlignment:
-      case SpvDecorationAlignmentId:
-      case SpvDecorationMaxByteOffset:
+      case spv::Decoration::Invariant:
+      case spv::Decoration::Restrict:
+      case spv::Decoration::Alignment:
+      case spv::Decoration::AlignmentId:
+      case spv::Decoration::MaxByteOffset:
+      case spv::Decoration::AliasedPointer:
+      case spv::Decoration::RestrictPointer:
         break;
       default:
         return false;
@@ -816,8 +754,8 @@
     // Annotations are check as a group separately.
     if (!IsAnnotationInst(user->opcode())) {
       switch (user->opcode()) {
-        case SpvOpAccessChain:
-        case SpvOpInBoundsAccessChain:
+        case spv::Op::OpAccessChain:
+        case spv::Op::OpInBoundsAccessChain:
           if (index == 2u && user->NumInOperands() > 1) {
             uint32_t id = user->GetSingleWordInOperand(1u);
             const Instruction* opInst = get_def_use_mgr()->GetDef(id);
@@ -835,16 +773,16 @@
             ok = false;
           }
           break;
-        case SpvOpLoad:
+        case spv::Op::OpLoad:
           if (!CheckLoad(user, index)) ok = false;
           stats->num_full_accesses++;
           break;
-        case SpvOpStore:
+        case spv::Op::OpStore:
           if (!CheckStore(user, index)) ok = false;
           stats->num_full_accesses++;
           break;
-        case SpvOpName:
-        case SpvOpMemberName:
+        case spv::Op::OpName:
+        case spv::Op::OpMemberName:
           break;
         default:
           ok = false;
@@ -861,24 +799,24 @@
   get_def_use_mgr()->ForEachUse(
       inst, [this, &ok](const Instruction* user, uint32_t index) {
         switch (user->opcode()) {
-          case SpvOpAccessChain:
-          case SpvOpInBoundsAccessChain:
+          case spv::Op::OpAccessChain:
+          case spv::Op::OpInBoundsAccessChain:
             if (index != 2u) {
               ok = false;
             } else {
               if (!CheckUsesRelaxed(user)) ok = false;
             }
             break;
-          case SpvOpLoad:
+          case spv::Op::OpLoad:
             if (!CheckLoad(user, index)) ok = false;
             break;
-          case SpvOpStore:
+          case spv::Op::OpStore:
             if (!CheckStore(user, index)) ok = false;
             break;
-          case SpvOpImageTexelPointer:
+          case spv::Op::OpImageTexelPointer:
             if (!CheckImageTexelPointer(index)) ok = false;
             break;
-          case SpvOpExtInst:
+          case spv::Op::OpExtInst:
             if (user->GetCommonDebugOpcode() != CommonDebugInfoDebugDeclare ||
                 !CheckDebugDeclare(index))
               ok = false;
@@ -900,7 +838,8 @@
                                       uint32_t index) const {
   if (index != 2u) return false;
   if (inst->NumInOperands() >= 2 &&
-      inst->GetSingleWordInOperand(1u) & SpvMemoryAccessVolatileMask)
+      inst->GetSingleWordInOperand(1u) &
+          uint32_t(spv::MemoryAccessMask::Volatile))
     return false;
   return true;
 }
@@ -909,7 +848,8 @@
                                        uint32_t index) const {
   if (index != 0u) return false;
   if (inst->NumInOperands() >= 3 &&
-      inst->GetSingleWordInOperand(2u) & SpvMemoryAccessVolatileMask)
+      inst->GetSingleWordInOperand(2u) &
+          uint32_t(spv::MemoryAccessMask::Volatile))
     return false;
   return true;
 }
@@ -936,11 +876,11 @@
   def_use_mgr->WhileEachUser(inst, [&result, def_use_mgr,
                                     this](Instruction* use) {
     switch (use->opcode()) {
-      case SpvOpLoad: {
+      case spv::Op::OpLoad: {
         // Look for extract from the load.
         std::vector<uint32_t> t;
         if (def_use_mgr->WhileEachUser(use, [&t](Instruction* use2) {
-              if (use2->opcode() != SpvOpCompositeExtract ||
+              if (use2->opcode() != spv::Op::OpCompositeExtract ||
                   use2->NumInOperands() <= 1) {
                 return false;
               }
@@ -954,13 +894,13 @@
           return false;
         }
       }
-      case SpvOpName:
-      case SpvOpMemberName:
-      case SpvOpStore:
+      case spv::Op::OpName:
+      case spv::Op::OpMemberName:
+      case spv::Op::OpStore:
         // No components are used.
         return true;
-      case SpvOpAccessChain:
-      case SpvOpInBoundsAccessChain: {
+      case spv::Op::OpAccessChain:
+      case spv::Op::OpInBoundsAccessChain: {
         // Add the first index it if is a constant.
         // TODO: Could be improved by checking if the address is used in a load.
         analysis::ConstantManager* const_mgr = context()->get_constant_mgr();
@@ -988,16 +928,16 @@
 
 uint64_t ScalarReplacementPass::GetMaxLegalIndex(
     const Instruction* var_inst) const {
-  assert(var_inst->opcode() == SpvOpVariable &&
+  assert(var_inst->opcode() == spv::Op::OpVariable &&
          "|var_inst| must be a variable instruction.");
   Instruction* type = GetStorageType(var_inst);
   switch (type->opcode()) {
-    case SpvOpTypeStruct:
+    case spv::Op::OpTypeStruct:
       return type->NumInOperands();
-    case SpvOpTypeArray:
+    case spv::Op::OpTypeArray:
       return GetArrayLength(type);
-    case SpvOpTypeMatrix:
-    case SpvOpTypeVector:
+    case spv::Op::OpTypeMatrix:
+    case spv::Op::OpTypeVector:
       return GetNumElements(type);
     default:
       return 0;
@@ -1005,5 +945,69 @@
   return 0;
 }
 
+void ScalarReplacementPass::CopyDecorationsToVariable(Instruction* from,
+                                                      Instruction* to,
+                                                      uint32_t member_index) {
+  CopyPointerDecorationsToVariable(from, to);
+  CopyNecessaryMemberDecorationsToVariable(from, to, member_index);
+}
+
+void ScalarReplacementPass::CopyPointerDecorationsToVariable(Instruction* from,
+                                                             Instruction* to) {
+  // The RestrictPointer and AliasedPointer decorations are copied to all
+  // members even if the new variable does not contain a pointer. It does
+  // not hurt to do so.
+  for (auto dec_inst :
+       get_decoration_mgr()->GetDecorationsFor(from->result_id(), false)) {
+    uint32_t decoration;
+    decoration = dec_inst->GetSingleWordInOperand(1u);
+    switch (spv::Decoration(decoration)) {
+      case spv::Decoration::AliasedPointer:
+      case spv::Decoration::RestrictPointer: {
+        std::unique_ptr<Instruction> new_dec_inst(dec_inst->Clone(context()));
+        new_dec_inst->SetInOperand(0, {to->result_id()});
+        context()->AddAnnotationInst(std::move(new_dec_inst));
+      } break;
+      default:
+        break;
+    }
+  }
+}
+
+void ScalarReplacementPass::CopyNecessaryMemberDecorationsToVariable(
+    Instruction* from, Instruction* to, uint32_t member_index) {
+  Instruction* type_inst = GetStorageType(from);
+  for (auto dec_inst :
+       get_decoration_mgr()->GetDecorationsFor(type_inst->result_id(), false)) {
+    uint32_t decoration;
+    if (dec_inst->opcode() == spv::Op::OpMemberDecorate) {
+      if (dec_inst->GetSingleWordInOperand(1) != member_index) {
+        continue;
+      }
+
+      decoration = dec_inst->GetSingleWordInOperand(2u);
+      switch (spv::Decoration(decoration)) {
+        case spv::Decoration::ArrayStride:
+        case spv::Decoration::Alignment:
+        case spv::Decoration::AlignmentId:
+        case spv::Decoration::MaxByteOffset:
+        case spv::Decoration::MaxByteOffsetId:
+        case spv::Decoration::RelaxedPrecision: {
+          std::unique_ptr<Instruction> new_dec_inst(
+              new Instruction(context(), spv::Op::OpDecorate, 0, 0, {}));
+          new_dec_inst->AddOperand(
+              Operand(SPV_OPERAND_TYPE_ID, {to->result_id()}));
+          for (uint32_t i = 2; i < dec_inst->NumInOperandWords(); ++i) {
+            new_dec_inst->AddOperand(Operand(dec_inst->GetInOperand(i)));
+          }
+          context()->AddAnnotationInst(std::move(new_dec_inst));
+        } break;
+        default:
+          break;
+      }
+    }
+  }
+}
+
 }  // namespace opt
 }  // namespace spvtools
diff --git a/source/opt/scalar_replacement_pass.h b/source/opt/scalar_replacement_pass.h
index 6a66dfb..c73ecfd 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 const uint32_t kDefaultLimit = 100;
+  static constexpr uint32_t kDefaultLimit = 100;
 
  public:
   ScalarReplacementPass(uint32_t limit = kDefaultLimit)
@@ -104,10 +104,10 @@
   // Returns true if the uses of |inst| are acceptable for scalarization.
   //
   // Recursively checks all the uses of |inst|. For |inst| specifically, only
-  // allows SpvOpAccessChain, SpvOpInBoundsAccessChain, SpvOpLoad and
-  // SpvOpStore. Access chains must have the first index be a compile-time
-  // constant. Subsequent uses of access chains (including other access chains)
-  // are checked in a more relaxed manner.
+  // allows spv::Op::OpAccessChain, spv::Op::OpInBoundsAccessChain,
+  // spv::Op::OpLoad and spv::Op::OpStore. Access chains must have the first
+  // index be a compile-time constant. Subsequent uses of access chains
+  // (including other access chains) are checked in a more relaxed manner.
   bool CheckUses(const Instruction* inst) const;
 
   // Helper function for the above |CheckUses|.
@@ -262,9 +262,26 @@
   // that we will be willing to split.
   bool IsLargerThanSizeLimit(uint64_t length) const;
 
+  // Copies all relevant decorations from `from` to `to`. This includes
+  // decorations applied to the variable, and to the members of the type.
+  // It is assumed that `to` is a variable that is intended to replace the
+  // `member_index`th member of `from`.
+  void CopyDecorationsToVariable(Instruction* from, Instruction* to,
+                                 uint32_t member_index);
+
+  // Copies pointer related decoration from `from` to `to` if they exist.
+  void CopyPointerDecorationsToVariable(Instruction* from, Instruction* to);
+
+  // Copies decorations that are needed from the `member_index` of `from` to
+  // `to, if there was one.
+  void CopyNecessaryMemberDecorationsToVariable(Instruction* from,
+                                                Instruction* to,
+                                                uint32_t member_index);
+
   // Limit on the number of members in an object that will be replaced.
   // 0 means there is no limit.
   uint32_t max_num_elements_;
+
   // This has to be big enough to fit "scalar-replacement=" followed by a
   // uint32_t number written in decimal (so 10 digits), and then a
   // terminating nul.
diff --git a/source/opt/set_spec_constant_default_value_pass.cpp b/source/opt/set_spec_constant_default_value_pass.cpp
index 4def2b0..d2aa9b1 100644
--- a/source/opt/set_spec_constant_default_value_pass.cpp
+++ b/source/opt/set_spec_constant_default_value_pass.cpp
@@ -21,8 +21,6 @@
 #include <vector>
 
 #include "source/opt/def_use_manager.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/parse_number.h"
@@ -30,7 +28,6 @@
 
 namespace spvtools {
 namespace opt {
-
 namespace {
 using utils::EncodeNumberStatus;
 using utils::NumberType;
@@ -139,9 +136,9 @@
 // decoration.
 bool CanHaveSpecIdDecoration(const Instruction& inst) {
   switch (inst.opcode()) {
-    case SpvOp::SpvOpSpecConstant:
-    case SpvOp::SpvOpSpecConstantFalse:
-    case SpvOp::SpvOpSpecConstantTrue:
+    case spv::Op::OpSpecConstant:
+    case spv::Op::OpSpecConstantFalse:
+    case spv::Op::OpSpecConstantTrue:
       return true;
     default:
       return false;
@@ -165,7 +162,7 @@
   if (def_use_mgr->WhileEachUser(&decoration_group_defining_inst,
                                  [&group_decorate_inst](Instruction* user) {
                                    if (user->opcode() ==
-                                       SpvOp::SpvOpGroupDecorate) {
+                                       spv::Op::OpGroupDecorate) {
                                      group_decorate_inst = user;
                                      return false;
                                    }
@@ -217,16 +214,16 @@
 
 Pass::Status SetSpecConstantDefaultValuePass::Process() {
   // The operand index of decoration target in an OpDecorate instruction.
-  const uint32_t kTargetIdOperandIndex = 0;
+  constexpr uint32_t kTargetIdOperandIndex = 0;
   // The operand index of the decoration literal in an OpDecorate instruction.
-  const uint32_t kDecorationOperandIndex = 1;
+  constexpr uint32_t kDecorationOperandIndex = 1;
   // The operand index of Spec id literal value in an OpDecorate SpecId
   // instruction.
-  const uint32_t kSpecIdLiteralOperandIndex = 2;
+  constexpr uint32_t kSpecIdLiteralOperandIndex = 2;
   // The number of operands in an OpDecorate SpecId instruction.
-  const uint32_t kOpDecorateSpecIdNumOperands = 3;
+  constexpr uint32_t kOpDecorateSpecIdNumOperands = 3;
   // The in-operand index of the default value in a OpSpecConstant instruction.
-  const uint32_t kOpSpecConstantLiteralInOperandIndex = 0;
+  constexpr uint32_t kOpSpecConstantLiteralInOperandIndex = 0;
 
   bool modified = false;
   // Scan through all the annotation instructions to find 'OpDecorate SpecId'
@@ -240,10 +237,10 @@
   // default value of the target spec constant.
   for (Instruction& inst : context()->annotations()) {
     // Only process 'OpDecorate SpecId' instructions
-    if (inst.opcode() != SpvOp::SpvOpDecorate) continue;
+    if (inst.opcode() != spv::Op::OpDecorate) continue;
     if (inst.NumOperands() != kOpDecorateSpecIdNumOperands) continue;
     if (inst.GetSingleWordInOperand(kDecorationOperandIndex) !=
-        uint32_t(SpvDecoration::SpvDecorationSpecId)) {
+        uint32_t(spv::Decoration::SpecId)) {
       continue;
     }
 
@@ -255,7 +252,7 @@
     // target_id might be a decoration group id.
     Instruction* spec_inst = nullptr;
     if (Instruction* target_inst = get_def_use_mgr()->GetDef(target_id)) {
-      if (target_inst->opcode() == SpvOp::SpvOpDecorationGroup) {
+      if (target_inst->opcode() == spv::Op::OpDecorationGroup) {
         spec_inst =
             GetSpecIdTargetFromDecorationGroup(*target_inst, get_def_use_mgr());
       } else {
@@ -301,7 +298,7 @@
     // Update the operand bit patterns of the spec constant defining
     // instruction.
     switch (spec_inst->opcode()) {
-      case SpvOp::SpvOpSpecConstant:
+      case spv::Op::OpSpecConstant:
         // If the new value is the same with the original value, no
         // need to do anything. Otherwise update the operand words.
         if (spec_inst->GetInOperand(kOpSpecConstantLiteralInOperandIndex)
@@ -311,19 +308,19 @@
           modified = true;
         }
         break;
-      case SpvOp::SpvOpSpecConstantTrue:
+      case spv::Op::OpSpecConstantTrue:
         // If the new value is also 'true', no need to change anything.
         // Otherwise, set the opcode to OpSpecConstantFalse;
         if (!static_cast<bool>(bit_pattern.front())) {
-          spec_inst->SetOpcode(SpvOp::SpvOpSpecConstantFalse);
+          spec_inst->SetOpcode(spv::Op::OpSpecConstantFalse);
           modified = true;
         }
         break;
-      case SpvOp::SpvOpSpecConstantFalse:
+      case spv::Op::OpSpecConstantFalse:
         // If the new value is also 'false', no need to change anything.
         // Otherwise, set the opcode to OpSpecConstantTrue;
         if (static_cast<bool>(bit_pattern.front())) {
-          spec_inst->SetOpcode(SpvOp::SpvOpSpecConstantTrue);
+          spec_inst->SetOpcode(spv::Op::OpSpecConstantTrue);
           modified = true;
         }
         break;
diff --git a/source/opt/simplification_pass.cpp b/source/opt/simplification_pass.cpp
index 43ec15f..f8ffc03 100644
--- a/source/opt/simplification_pass.cpp
+++ b/source/opt/simplification_pass.cpp
@@ -14,7 +14,6 @@
 
 #include "source/opt/simplification_pass.h"
 
-#include <set>
 #include <unordered_set>
 #include <vector>
 
@@ -69,12 +68,12 @@
        &folder, &inst_seen, this](BasicBlock* bb) {
         for (Instruction* inst = &*bb->begin(); inst; inst = inst->NextNode()) {
           inst_seen.insert(inst);
-          if (inst->opcode() == SpvOpPhi) {
+          if (inst->opcode() == spv::Op::OpPhi) {
             process_phis.insert(inst);
           }
 
           bool is_foldable_copy =
-              inst->opcode() == SpvOpCopyObject &&
+              inst->opcode() == spv::Op::OpCopyObject &&
               context()->get_decoration_mgr()->HaveSubsetOfDecorations(
                   inst->result_id(), inst->GetSingleWordInOperand(0));
 
@@ -91,7 +90,7 @@
 
             AddNewOperands(inst, &inst_seen, &work_list);
 
-            if (inst->opcode() == SpvOpCopyObject) {
+            if (inst->opcode() == spv::Op::OpCopyObject) {
               context()->ReplaceAllUsesWithPredicate(
                   inst->result_id(), inst->GetSingleWordInOperand(0),
                   [](Instruction* user) {
@@ -104,7 +103,7 @@
                   });
               inst_to_kill.insert(inst);
               in_work_list.insert(inst);
-            } else if (inst->opcode() == SpvOpNop) {
+            } else if (inst->opcode() == spv::Op::OpNop) {
               inst_to_kill.insert(inst);
               in_work_list.insert(inst);
             }
@@ -121,7 +120,7 @@
     inst_seen.insert(inst);
 
     bool is_foldable_copy =
-        inst->opcode() == SpvOpCopyObject &&
+        inst->opcode() == spv::Op::OpCopyObject &&
         context()->get_decoration_mgr()->HaveSubsetOfDecorations(
             inst->result_id(), inst->GetSingleWordInOperand(0));
 
@@ -130,7 +129,7 @@
       context()->AnalyzeUses(inst);
       get_def_use_mgr()->ForEachUser(
           inst, [&work_list, &in_work_list](Instruction* use) {
-            if (!use->IsDecoration() && use->opcode() != SpvOpName &&
+            if (!use->IsDecoration() && use->opcode() != spv::Op::OpName &&
                 in_work_list.insert(use).second) {
               work_list.push_back(use);
             }
@@ -138,7 +137,7 @@
 
       AddNewOperands(inst, &inst_seen, &work_list);
 
-      if (inst->opcode() == SpvOpCopyObject) {
+      if (inst->opcode() == spv::Op::OpCopyObject) {
         context()->ReplaceAllUsesWithPredicate(
             inst->result_id(), inst->GetSingleWordInOperand(0),
             [](Instruction* user) {
@@ -150,7 +149,7 @@
             });
         inst_to_kill.insert(inst);
         in_work_list.insert(inst);
-      } else if (inst->opcode() == SpvOpNop) {
+      } else if (inst->opcode() == spv::Op::OpNop) {
         inst_to_kill.insert(inst);
         in_work_list.insert(inst);
       }
diff --git a/source/opt/spread_volatile_semantics.cpp b/source/opt/spread_volatile_semantics.cpp
index b61fd0f..e552ba5 100644
--- a/source/opt/spread_volatile_semantics.cpp
+++ b/source/opt/spread_volatile_semantics.cpp
@@ -15,38 +15,37 @@
 #include "source/opt/spread_volatile_semantics.h"
 
 #include "source/opt/decoration_manager.h"
-#include "source/opt/ir_builder.h"
 #include "source/spirv_constant.h"
 
 namespace spvtools {
 namespace opt {
 namespace {
-
-const uint32_t kOpDecorateInOperandBuiltinDecoration = 2u;
-const uint32_t kOpLoadInOperandMemoryOperands = 1u;
-const uint32_t kOpEntryPointInOperandEntryPoint = 1u;
-const uint32_t kOpEntryPointInOperandInterface = 3u;
+constexpr uint32_t kOpDecorateInOperandBuiltinDecoration = 2u;
+constexpr uint32_t kOpLoadInOperandMemoryOperands = 1u;
+constexpr uint32_t kOpEntryPointInOperandEntryPoint = 1u;
+constexpr uint32_t kOpEntryPointInOperandInterface = 3u;
 
 bool HasBuiltinDecoration(analysis::DecorationManager* decoration_manager,
                           uint32_t var_id, uint32_t built_in) {
   return decoration_manager->FindDecoration(
-      var_id, SpvDecorationBuiltIn, [built_in](const Instruction& inst) {
+      var_id, uint32_t(spv::Decoration::BuiltIn),
+      [built_in](const Instruction& inst) {
         return built_in == inst.GetSingleWordInOperand(
                                kOpDecorateInOperandBuiltinDecoration);
       });
 }
 
-bool IsBuiltInForRayTracingVolatileSemantics(uint32_t built_in) {
+bool IsBuiltInForRayTracingVolatileSemantics(spv::BuiltIn built_in) {
   switch (built_in) {
-    case SpvBuiltInSMIDNV:
-    case SpvBuiltInWarpIDNV:
-    case SpvBuiltInSubgroupSize:
-    case SpvBuiltInSubgroupLocalInvocationId:
-    case SpvBuiltInSubgroupEqMask:
-    case SpvBuiltInSubgroupGeMask:
-    case SpvBuiltInSubgroupGtMask:
-    case SpvBuiltInSubgroupLeMask:
-    case SpvBuiltInSubgroupLtMask:
+    case spv::BuiltIn::SMIDNV:
+    case spv::BuiltIn::WarpIDNV:
+    case spv::BuiltIn::SubgroupSize:
+    case spv::BuiltIn::SubgroupLocalInvocationId:
+    case spv::BuiltIn::SubgroupEqMask:
+    case spv::BuiltIn::SubgroupGeMask:
+    case spv::BuiltIn::SubgroupGtMask:
+    case spv::BuiltIn::SubgroupLeMask:
+    case spv::BuiltIn::SubgroupLtMask:
       return true;
     default:
       return false;
@@ -56,16 +55,17 @@
 bool HasBuiltinForRayTracingVolatileSemantics(
     analysis::DecorationManager* decoration_manager, uint32_t var_id) {
   return decoration_manager->FindDecoration(
-      var_id, SpvDecorationBuiltIn, [](const Instruction& inst) {
-        uint32_t built_in =
-            inst.GetSingleWordInOperand(kOpDecorateInOperandBuiltinDecoration);
+      var_id, uint32_t(spv::Decoration::BuiltIn), [](const Instruction& inst) {
+        spv::BuiltIn built_in = spv::BuiltIn(
+            inst.GetSingleWordInOperand(kOpDecorateInOperandBuiltinDecoration));
         return IsBuiltInForRayTracingVolatileSemantics(built_in);
       });
 }
 
 bool HasVolatileDecoration(analysis::DecorationManager* decoration_manager,
                            uint32_t var_id) {
-  return decoration_manager->HasDecoration(var_id, SpvDecorationVolatile);
+  return decoration_manager->HasDecoration(var_id,
+                                           uint32_t(spv::Decoration::Volatile));
 }
 
 }  // namespace
@@ -76,7 +76,7 @@
   }
   const bool is_vk_memory_model_enabled =
       context()->get_feature_mgr()->HasCapability(
-          SpvCapabilityVulkanMemoryModel);
+          spv::Capability::VulkanMemoryModel);
   CollectTargetsForVolatileSemantics(is_vk_memory_model_enabled);
 
   // If VulkanMemoryModel capability is not enabled, we have to set Volatile
@@ -128,15 +128,16 @@
         }
         uint32_t memory_operands =
             load->GetSingleWordInOperand(kOpLoadInOperandMemoryOperands);
-        return (memory_operands & SpvMemoryAccessVolatileMask) != 0;
+        return (memory_operands & uint32_t(spv::MemoryAccessMask::Volatile)) !=
+               0;
       },
       funcs);
 }
 
 bool SpreadVolatileSemantics::HasInterfaceInConflictOfVolatileSemantics() {
   for (Instruction& entry_point : get_module()->entry_points()) {
-    SpvExecutionModel execution_model =
-        static_cast<SpvExecutionModel>(entry_point.GetSingleWordInOperand(0));
+    spv::ExecutionModel execution_model =
+        static_cast<spv::ExecutionModel>(entry_point.GetSingleWordInOperand(0));
     for (uint32_t operand_index = kOpEntryPointInOperandInterface;
          operand_index < entry_point.NumInOperands(); ++operand_index) {
       uint32_t var_id = entry_point.GetSingleWordInOperand(operand_index);
@@ -170,8 +171,8 @@
 void SpreadVolatileSemantics::CollectTargetsForVolatileSemantics(
     const bool is_vk_memory_model_enabled) {
   for (Instruction& entry_point : get_module()->entry_points()) {
-    SpvExecutionModel execution_model =
-        static_cast<SpvExecutionModel>(entry_point.GetSingleWordInOperand(0));
+    spv::ExecutionModel execution_model =
+        static_cast<spv::ExecutionModel>(entry_point.GetSingleWordInOperand(0));
     for (uint32_t operand_index = kOpEntryPointInOperandInterface;
          operand_index < entry_point.NumInOperands(); ++operand_index) {
       uint32_t var_id = entry_point.GetSingleWordInOperand(operand_index);
@@ -194,9 +195,10 @@
     return;
   }
   get_decoration_mgr()->AddDecoration(
-      SpvOpDecorate, {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {var_id}},
-                      {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER,
-                       {SpvDecorationVolatile}}});
+      spv::Op::OpDecorate,
+      {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {var_id}},
+       {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER,
+        {uint32_t(spv::Decoration::Volatile)}}});
 }
 
 bool SpreadVolatileSemantics::VisitLoadsOfPointersToVariableInEntries(
@@ -217,17 +219,17 @@
             return true;
           }
 
-          if (user->opcode() == SpvOpAccessChain ||
-              user->opcode() == SpvOpInBoundsAccessChain ||
-              user->opcode() == SpvOpPtrAccessChain ||
-              user->opcode() == SpvOpInBoundsPtrAccessChain ||
-              user->opcode() == SpvOpCopyObject) {
+          if (user->opcode() == spv::Op::OpAccessChain ||
+              user->opcode() == spv::Op::OpInBoundsAccessChain ||
+              user->opcode() == spv::Op::OpPtrAccessChain ||
+              user->opcode() == spv::Op::OpInBoundsPtrAccessChain ||
+              user->opcode() == spv::Op::OpCopyObject) {
             if (ptr_id == user->GetSingleWordInOperand(0))
               worklist.push_back(user->result_id());
             return true;
           }
 
-          if (user->opcode() != SpvOpLoad) {
+          if (user->opcode() != spv::Op::OpLoad) {
             return true;
           }
 
@@ -250,12 +252,12 @@
         [](Instruction* load) {
           if (load->NumInOperands() <= kOpLoadInOperandMemoryOperands) {
             load->AddOperand({SPV_OPERAND_TYPE_MEMORY_ACCESS,
-                              {SpvMemoryAccessVolatileMask}});
+                              {uint32_t(spv::MemoryAccessMask::Volatile)}});
             return true;
           }
           uint32_t memory_operands =
               load->GetSingleWordInOperand(kOpLoadInOperandMemoryOperands);
-          memory_operands |= SpvMemoryAccessVolatileMask;
+          memory_operands |= uint32_t(spv::MemoryAccessMask::Volatile);
           load->SetInOperand(kOpLoadInOperandMemoryOperands, {memory_operands});
           return true;
         },
@@ -264,29 +266,29 @@
 }
 
 bool SpreadVolatileSemantics::IsTargetForVolatileSemantics(
-    uint32_t var_id, SpvExecutionModel execution_model) {
+    uint32_t var_id, spv::ExecutionModel execution_model) {
   analysis::DecorationManager* decoration_manager =
       context()->get_decoration_mgr();
-  if (execution_model == SpvExecutionModelFragment) {
+  if (execution_model == spv::ExecutionModel::Fragment) {
     return get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 6) &&
            HasBuiltinDecoration(decoration_manager, var_id,
-                                SpvBuiltInHelperInvocation);
+                                uint32_t(spv::BuiltIn::HelperInvocation));
   }
 
-  if (execution_model == SpvExecutionModelIntersectionKHR ||
-      execution_model == SpvExecutionModelIntersectionNV) {
+  if (execution_model == spv::ExecutionModel::IntersectionKHR ||
+      execution_model == spv::ExecutionModel::IntersectionNV) {
     if (HasBuiltinDecoration(decoration_manager, var_id,
-                             SpvBuiltInRayTmaxKHR)) {
+                             uint32_t(spv::BuiltIn::RayTmaxKHR))) {
       return true;
     }
   }
 
   switch (execution_model) {
-    case SpvExecutionModelRayGenerationKHR:
-    case SpvExecutionModelClosestHitKHR:
-    case SpvExecutionModelMissKHR:
-    case SpvExecutionModelCallableKHR:
-    case SpvExecutionModelIntersectionKHR:
+    case spv::ExecutionModel::RayGenerationKHR:
+    case spv::ExecutionModel::ClosestHitKHR:
+    case spv::ExecutionModel::MissKHR:
+    case spv::ExecutionModel::CallableKHR:
+    case spv::ExecutionModel::IntersectionKHR:
       return HasBuiltinForRayTracingVolatileSemantics(decoration_manager,
                                                       var_id);
     default:
diff --git a/source/opt/spread_volatile_semantics.h b/source/opt/spread_volatile_semantics.h
index 014858d..4cbb526 100644
--- a/source/opt/spread_volatile_semantics.h
+++ b/source/opt/spread_volatile_semantics.h
@@ -39,7 +39,8 @@
   // have an execution model.
   bool HasNoExecutionModel() {
     return get_module()->entry_points().empty() &&
-           context()->get_feature_mgr()->HasCapability(SpvCapabilityLinkage);
+           context()->get_feature_mgr()->HasCapability(
+               spv::Capability::Linkage);
   }
 
   // Iterates interface variables and spreads the Volatile semantics if it has
@@ -52,7 +53,7 @@
   // VUID-StandaloneSpirv-VulkanMemoryModel-04678 or
   // VUID-StandaloneSpirv-VulkanMemoryModel-04679.
   bool IsTargetForVolatileSemantics(uint32_t var_id,
-                                    SpvExecutionModel execution_model);
+                                    spv::ExecutionModel execution_model);
 
   // Collects interface variables that need the volatile semantics.
   // |is_vk_memory_model_enabled| is true if VulkanMemoryModel capability is
diff --git a/source/opt/ssa_rewrite_pass.cpp b/source/opt/ssa_rewrite_pass.cpp
index 22d8110..3eb4ec3 100644
--- a/source/opt/ssa_rewrite_pass.cpp
+++ b/source/opt/ssa_rewrite_pass.cpp
@@ -48,7 +48,6 @@
 #include "source/opt/cfg.h"
 #include "source/opt/mem_pass.h"
 #include "source/opt/types.h"
-#include "source/util/make_unique.h"
 
 // Debug logging (0: Off, 1-N: Verbosity level).  Replace this with the
 // implementation done for
@@ -63,10 +62,9 @@
 
 namespace spvtools {
 namespace opt {
-
 namespace {
-const uint32_t kStoreValIdInIdx = 1;
-const uint32_t kVariableInitIdInIdx = 1;
+constexpr uint32_t kStoreValIdInIdx = 1;
+constexpr uint32_t kVariableInitIdInIdx = 1;
 }  // namespace
 
 std::string SSARewriter::PhiCandidate::PrettyPrint(const CFG* cfg) const {
@@ -300,12 +298,12 @@
 
 void SSARewriter::ProcessStore(Instruction* inst, BasicBlock* bb) {
   auto opcode = inst->opcode();
-  assert((opcode == SpvOpStore || opcode == SpvOpVariable) &&
+  assert((opcode == spv::Op::OpStore || opcode == spv::Op::OpVariable) &&
          "Expecting a store or a variable definition instruction.");
 
   uint32_t var_id = 0;
   uint32_t val_id = 0;
-  if (opcode == SpvOpStore) {
+  if (opcode == spv::Op::OpStore) {
     (void)pass_->GetPtr(inst, &var_id);
     val_id = inst->GetSingleWordInOperand(kStoreValIdInIdx);
   } else if (inst->NumInOperands() >= 2) {
@@ -443,9 +441,9 @@
 
   for (auto& inst : *bb) {
     auto opcode = inst.opcode();
-    if (opcode == SpvOpStore || opcode == SpvOpVariable) {
+    if (opcode == spv::Op::OpStore || opcode == spv::Op::OpVariable) {
       ProcessStore(&inst, bb);
-    } else if (inst.opcode() == SpvOpLoad) {
+    } else if (inst.opcode() == spv::Op::OpLoad) {
       if (!ProcessLoad(&inst, bb)) {
         return false;
       }
@@ -545,7 +543,7 @@
     // Generate a new OpPhi instruction and insert it in its basic
     // block.
     std::unique_ptr<Instruction> phi_inst(
-        new Instruction(pass_->context(), SpvOpPhi, type_id,
+        new Instruction(pass_->context(), spv::Op::OpPhi, type_id,
                         phi_candidate->result_id(), phi_operands));
     generated_phis.push_back(phi_inst.get());
     pass_->get_def_use_mgr()->AnalyzeInstDef(&*phi_inst);
@@ -554,7 +552,7 @@
     insert_it = insert_it.InsertBefore(std::move(phi_inst));
     pass_->context()->get_decoration_mgr()->CloneDecorations(
         phi_candidate->var_id(), phi_candidate->result_id(),
-        {SpvDecorationRelaxedPrecision});
+        {spv::Decoration::RelaxedPrecision});
 
     // Add DebugValue for the new OpPhi instruction.
     insert_it->SetDebugScope(local_var->GetDebugScope());
diff --git a/source/opt/strength_reduction_pass.cpp b/source/opt/strength_reduction_pass.cpp
index ab7c4eb..16a7869 100644
--- a/source/opt/strength_reduction_pass.cpp
+++ b/source/opt/strength_reduction_pass.cpp
@@ -14,12 +14,8 @@
 
 #include "source/opt/strength_reduction_pass.h"
 
-#include <algorithm>
-#include <cstdio>
 #include <cstring>
 #include <memory>
-#include <unordered_map>
-#include <unordered_set>
 #include <utility>
 #include <vector>
 
@@ -28,6 +24,8 @@
 #include "source/opt/log.h"
 #include "source/opt/reflect.h"
 
+namespace spvtools {
+namespace opt {
 namespace {
 // Count the number of trailing zeros in the binary representation of
 // |constVal|.
@@ -53,9 +51,6 @@
 
 }  // namespace
 
-namespace spvtools {
-namespace opt {
-
 Pass::Status StrengthReductionPass::Process() {
   // Initialize the member variables on a per module basis.
   bool modified = false;
@@ -70,7 +65,7 @@
 
 bool StrengthReductionPass::ReplaceMultiplyByPowerOf2(
     BasicBlock::iterator* inst) {
-  assert((*inst)->opcode() == SpvOp::SpvOpIMul &&
+  assert((*inst)->opcode() == spv::Op::OpIMul &&
          "Only works for multiplication of integers.");
   bool modified = false;
 
@@ -84,7 +79,7 @@
   for (int i = 0; i < 2; i++) {
     uint32_t opId = (*inst)->GetSingleWordInOperand(i);
     Instruction* opInst = get_def_use_mgr()->GetDef(opId);
-    if (opInst->opcode() == SpvOp::SpvOpConstant) {
+    if (opInst->opcode() == spv::Op::OpConstant) {
       // We found a constant operand.
       uint32_t constVal = opInst->GetSingleWordOperand(2);
 
@@ -101,7 +96,7 @@
                              {shiftConstResultId});
         newOperands.push_back(shiftOperand);
         std::unique_ptr<Instruction> newInstruction(
-            new Instruction(context(), SpvOp::SpvOpShiftLeftLogical,
+            new Instruction(context(), spv::Op::OpShiftLeftLogical,
                             (*inst)->type_id(), newResultId, newOperands));
 
         // Insert the new instruction and update the data structures.
@@ -133,7 +128,7 @@
   for (auto iter = get_module()->types_values_begin();
        iter != get_module()->types_values_end(); ++iter) {
     switch (iter->opcode()) {
-      case SpvOp::SpvOpConstant:
+      case spv::Op::OpConstant:
         if (iter->type_id() == uint32_type_id_) {
           uint32_t value = iter->GetSingleWordOperand(2);
           if (value <= 32) constant_ids_[value] = iter->result_id();
@@ -159,9 +154,8 @@
     uint32_t resultId = TakeNextId();
     Operand constant(spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER,
                      {val});
-    std::unique_ptr<Instruction> newConstant(
-        new Instruction(context(), SpvOp::SpvOpConstant, uint32_type_id_,
-                        resultId, {constant}));
+    std::unique_ptr<Instruction> newConstant(new Instruction(
+        context(), spv::Op::OpConstant, uint32_type_id_, resultId, {constant}));
     get_module()->AddGlobalValue(std::move(newConstant));
 
     // Notify the DefUseManager about this constant.
@@ -184,7 +178,7 @@
     for (auto& bb : func) {
       for (auto inst = bb.begin(); inst != bb.end(); ++inst) {
         switch (inst->opcode()) {
-          case SpvOp::SpvOpIMul:
+          case spv::Op::OpIMul:
             if (ReplaceMultiplyByPowerOf2(&inst)) modified = true;
             break;
           default:
diff --git a/source/opt/strip_debug_info_pass.cpp b/source/opt/strip_debug_info_pass.cpp
index 6a0ebf2..f81bced 100644
--- a/source/opt/strip_debug_info_pass.cpp
+++ b/source/opt/strip_debug_info_pass.cpp
@@ -37,13 +37,13 @@
   if (uses_non_semantic_info) {
     for (auto& inst : context()->module()->debugs1()) {
       switch (inst.opcode()) {
-        case SpvOpString: {
+        case spv::Op::OpString: {
           analysis::DefUseManager* def_use = context()->get_def_use_mgr();
 
           // see if this string is used anywhere by a non-semantic instruction
           bool no_nonsemantic_use =
               def_use->WhileEachUser(&inst, [def_use](Instruction* use) {
-                if (use->opcode() == SpvOpExtInst) {
+                if (use->opcode() == spv::Op::OpExtInst) {
                   auto ext_inst_set =
                       def_use->GetDef(use->GetSingleWordInOperand(0u));
                   const std::string extension_name =
@@ -83,7 +83,8 @@
   // when that instruction is killed, which will lead to a double kill.
   std::sort(to_kill.begin(), to_kill.end(),
             [](Instruction* lhs, Instruction* rhs) -> bool {
-              if (lhs->opcode() == SpvOpName && rhs->opcode() != SpvOpName)
+              if (lhs->opcode() == spv::Op::OpName &&
+                  rhs->opcode() != spv::Op::OpName)
                 return true;
               return false;
             });
diff --git a/source/opt/strip_nonsemantic_info_pass.cpp b/source/opt/strip_nonsemantic_info_pass.cpp
index cd1fbb6..3886835 100644
--- a/source/opt/strip_nonsemantic_info_pass.cpp
+++ b/source/opt/strip_nonsemantic_info_pass.cpp
@@ -14,7 +14,6 @@
 
 #include "source/opt/strip_nonsemantic_info_pass.h"
 
-#include <cstring>
 #include <vector>
 
 #include "source/opt/instruction.h"
@@ -32,27 +31,31 @@
   bool other_uses_for_decorate_string = false;
   for (auto& inst : context()->module()->annotations()) {
     switch (inst.opcode()) {
-      case SpvOpDecorateStringGOOGLE:
-        if (inst.GetSingleWordInOperand(1) == SpvDecorationHlslSemanticGOOGLE ||
-            inst.GetSingleWordInOperand(1) == SpvDecorationUserTypeGOOGLE) {
+      case spv::Op::OpDecorateStringGOOGLE:
+        if (spv::Decoration(inst.GetSingleWordInOperand(1)) ==
+                spv::Decoration::HlslSemanticGOOGLE ||
+            spv::Decoration(inst.GetSingleWordInOperand(1)) ==
+                spv::Decoration::UserTypeGOOGLE) {
           to_remove.push_back(&inst);
         } else {
           other_uses_for_decorate_string = true;
         }
         break;
 
-      case SpvOpMemberDecorateStringGOOGLE:
-        if (inst.GetSingleWordInOperand(2) == SpvDecorationHlslSemanticGOOGLE ||
-            inst.GetSingleWordInOperand(2) == SpvDecorationUserTypeGOOGLE) {
+      case spv::Op::OpMemberDecorateStringGOOGLE:
+        if (spv::Decoration(inst.GetSingleWordInOperand(2)) ==
+                spv::Decoration::HlslSemanticGOOGLE ||
+            spv::Decoration(inst.GetSingleWordInOperand(2)) ==
+                spv::Decoration::UserTypeGOOGLE) {
           to_remove.push_back(&inst);
         } else {
           other_uses_for_decorate_string = true;
         }
         break;
 
-      case SpvOpDecorateId:
-        if (inst.GetSingleWordInOperand(1) ==
-            SpvDecorationHlslCounterBufferGOOGLE) {
+      case spv::Op::OpDecorateId:
+        if (spv::Decoration(inst.GetSingleWordInOperand(1)) ==
+            spv::Decoration::HlslCounterBufferGOOGLE) {
           to_remove.push_back(&inst);
         }
         break;
@@ -79,7 +82,7 @@
   // remove any extended inst imports that are non semantic
   std::unordered_set<uint32_t> non_semantic_sets;
   for (auto& inst : context()->module()->ext_inst_imports()) {
-    assert(inst.opcode() == SpvOpExtInstImport &&
+    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.")) {
@@ -93,7 +96,7 @@
   if (!non_semantic_sets.empty()) {
     context()->module()->ForEachInst(
         [&non_semantic_sets, &to_remove](Instruction* inst) {
-          if (inst->opcode() == SpvOpExtInst) {
+          if (inst->opcode() == spv::Op::OpExtInst) {
             if (non_semantic_sets.find(inst->GetSingleWordInOperand(0)) !=
                 non_semantic_sets.end()) {
               to_remove.push_back(inst);
diff --git a/source/opt/struct_cfg_analysis.cpp b/source/opt/struct_cfg_analysis.cpp
index 203db87..290b4bf 100644
--- a/source/opt/struct_cfg_analysis.cpp
+++ b/source/opt/struct_cfg_analysis.cpp
@@ -16,18 +16,17 @@
 
 #include "source/opt/ir_context.h"
 
-namespace {
-const uint32_t kMergeNodeIndex = 0;
-const uint32_t kContinueNodeIndex = 1;
-}  // namespace
-
 namespace spvtools {
 namespace opt {
+namespace {
+constexpr uint32_t kMergeNodeIndex = 0;
+constexpr uint32_t kContinueNodeIndex = 1;
+}  // namespace
 
 StructuredCFGAnalysis::StructuredCFGAnalysis(IRContext* ctx) : context_(ctx) {
   // If this is not a shader, there are no merge instructions, and not
   // structured CFG to analyze.
-  if (!context_->get_feature_mgr()->HasCapability(SpvCapabilityShader)) {
+  if (!context_->get_feature_mgr()->HasCapability(spv::Capability::Shader)) {
     return;
   }
 
@@ -82,7 +81,7 @@
           merge_inst->GetSingleWordInOperand(kMergeNodeIndex);
       new_state.cinfo.containing_construct = block->id();
 
-      if (merge_inst->opcode() == SpvOpLoopMerge) {
+      if (merge_inst->opcode() == spv::Op::OpLoopMerge) {
         new_state.cinfo.containing_loop = block->id();
         new_state.cinfo.containing_switch = 0;
         new_state.continue_node =
@@ -98,7 +97,7 @@
         new_state.cinfo.in_continue = state.back().cinfo.in_continue;
         new_state.continue_node = state.back().continue_node;
 
-        if (merge_inst->NextNode()->opcode() == SpvOpSwitch) {
+        if (merge_inst->NextNode()->opcode() == spv::Op::OpSwitch) {
           new_state.cinfo.containing_switch = block->id();
         } else {
           new_state.cinfo.containing_switch =
@@ -226,7 +225,7 @@
     for (auto& bb : func) {
       if (IsInContainingLoopsContinueConstruct(bb.id())) {
         for (const Instruction& inst : bb) {
-          if (inst.opcode() == SpvOpFunctionCall) {
+          if (inst.opcode() == spv::Op::OpFunctionCall) {
             funcs_to_process.push(inst.GetSingleWordInOperand(0));
           }
         }
diff --git a/source/opt/switch_descriptorset_pass.cpp b/source/opt/switch_descriptorset_pass.cpp
new file mode 100644
index 0000000..f07c917
--- /dev/null
+++ b/source/opt/switch_descriptorset_pass.cpp
@@ -0,0 +1,46 @@
+// Copyright (c) 2023 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/switch_descriptorset_pass.h"
+
+#include "source/opt/ir_builder.h"
+#include "source/util/string_utils.h"
+
+namespace spvtools {
+namespace opt {
+
+Pass::Status SwitchDescriptorSetPass::Process() {
+  Status status = Status::SuccessWithoutChange;
+  auto* deco_mgr = context()->get_decoration_mgr();
+
+  for (Instruction& var : context()->types_values()) {
+    if (var.opcode() != spv::Op::OpVariable) {
+      continue;
+    }
+    auto decos = deco_mgr->GetDecorationsFor(var.result_id(), false);
+    for (const auto& deco : decos) {
+      spv::Decoration d = spv::Decoration(deco->GetSingleWordInOperand(1u));
+      if (d == spv::Decoration::DescriptorSet &&
+          deco->GetSingleWordInOperand(2u) == ds_from_) {
+        deco->SetInOperand(2u, {ds_to_});
+        status = Status::SuccessWithChange;
+        break;
+      }
+    }
+  }
+  return status;
+}
+
+}  // namespace opt
+}  // namespace spvtools
diff --git a/source/opt/switch_descriptorset_pass.h b/source/opt/switch_descriptorset_pass.h
new file mode 100644
index 0000000..2084e9c
--- /dev/null
+++ b/source/opt/switch_descriptorset_pass.h
@@ -0,0 +1,52 @@
+// Copyright (c) 2023 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.
+
+#pragma once
+
+#include <cstdio>
+#include <memory>
+#include <queue>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+#include "source/opt/pass.h"
+
+namespace spvtools {
+namespace opt {
+
+// See optimizer.hpp for documentation.
+class SwitchDescriptorSetPass : public Pass {
+ public:
+  SwitchDescriptorSetPass(uint32_t ds_from, uint32_t ds_to)
+      : ds_from_(ds_from), ds_to_(ds_to) {}
+
+  const char* name() const override { return "switch-descriptorset"; }
+
+  Status Process() override;
+
+  IRContext::Analysis GetPreservedAnalyses() override {
+    // this pass preserves everything except decorations
+    uint32_t mask = ((IRContext::kAnalysisEnd << 1) - 1);
+    mask &= ~static_cast<uint32_t>(IRContext::kAnalysisDecorations);
+    return static_cast<IRContext::Analysis>(mask);
+  }
+
+ private:
+  uint32_t ds_from_;
+  uint32_t ds_to_;
+};
+
+}  // namespace opt
+}  // namespace spvtools
diff --git a/source/opt/trim_capabilities_pass.cpp b/source/opt/trim_capabilities_pass.cpp
new file mode 100644
index 0000000..5df1999
--- /dev/null
+++ b/source/opt/trim_capabilities_pass.cpp
@@ -0,0 +1,570 @@
+// Copyright (c) 2023 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/opt/trim_capabilities_pass.h"
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <functional>
+#include <optional>
+#include <queue>
+#include <stack>
+#include <unordered_map>
+#include <unordered_set>
+#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/util/string_utils.h"
+
+namespace spvtools {
+namespace opt {
+
+namespace {
+constexpr uint32_t kOpTypeFloatSizeIndex = 0;
+constexpr uint32_t kOpTypePointerStorageClassIndex = 0;
+constexpr uint32_t kTypeArrayTypeIndex = 0;
+constexpr uint32_t kOpTypeScalarBitWidthIndex = 0;
+constexpr uint32_t kTypePointerTypeIdInIndex = 1;
+constexpr uint32_t kOpTypeIntSizeIndex = 0;
+constexpr uint32_t kOpTypeImageArrayedIndex = 3;
+constexpr uint32_t kOpTypeImageMSIndex = kOpTypeImageArrayedIndex + 1;
+constexpr uint32_t kOpTypeImageSampledIndex = kOpTypeImageMSIndex + 1;
+
+// DFS visit of the type defined by `instruction`.
+// If `condition` is true, children of the current node are visited.
+// If `condition` is false, the children of the current node are ignored.
+template <class UnaryPredicate>
+static void DFSWhile(const Instruction* instruction, UnaryPredicate condition) {
+  std::stack<uint32_t> instructions_to_visit;
+  instructions_to_visit.push(instruction->result_id());
+  const auto* def_use_mgr = instruction->context()->get_def_use_mgr();
+
+  while (!instructions_to_visit.empty()) {
+    const Instruction* item = def_use_mgr->GetDef(instructions_to_visit.top());
+    instructions_to_visit.pop();
+
+    if (!condition(item)) {
+      continue;
+    }
+
+    if (item->opcode() == spv::Op::OpTypePointer) {
+      instructions_to_visit.push(
+          item->GetSingleWordInOperand(kTypePointerTypeIdInIndex));
+      continue;
+    }
+
+    if (item->opcode() == spv::Op::OpTypeMatrix ||
+        item->opcode() == spv::Op::OpTypeVector ||
+        item->opcode() == spv::Op::OpTypeArray ||
+        item->opcode() == spv::Op::OpTypeRuntimeArray) {
+      instructions_to_visit.push(
+          item->GetSingleWordInOperand(kTypeArrayTypeIndex));
+      continue;
+    }
+
+    if (item->opcode() == spv::Op::OpTypeStruct) {
+      item->ForEachInOperand([&instructions_to_visit](const uint32_t* op_id) {
+        instructions_to_visit.push(*op_id);
+      });
+      continue;
+    }
+  }
+}
+
+// Walks the type defined by `instruction` (OpType* only).
+// Returns `true` if any call to `predicate` with the type/subtype returns true.
+template <class UnaryPredicate>
+static bool AnyTypeOf(const Instruction* instruction,
+                      UnaryPredicate predicate) {
+  assert(IsTypeInst(instruction->opcode()) &&
+         "AnyTypeOf called with a non-type instruction.");
+
+  bool found_one = false;
+  DFSWhile(instruction, [&found_one, predicate](const Instruction* node) {
+    if (found_one || predicate(node)) {
+      found_one = true;
+      return false;
+    }
+
+    return true;
+  });
+  return found_one;
+}
+
+static bool is16bitType(const Instruction* instruction) {
+  if (instruction->opcode() != spv::Op::OpTypeInt &&
+      instruction->opcode() != spv::Op::OpTypeFloat) {
+    return false;
+  }
+
+  return instruction->GetSingleWordInOperand(kOpTypeScalarBitWidthIndex) == 16;
+}
+
+static bool Has16BitCapability(const FeatureManager* feature_manager) {
+  const CapabilitySet& capabilities = feature_manager->GetCapabilities();
+  return capabilities.contains(spv::Capability::Float16) ||
+         capabilities.contains(spv::Capability::Int16);
+}
+
+}  // namespace
+
+// ============== Begin opcode handler implementations. =======================
+//
+// Adding support for a new capability should only require adding a new handler,
+// and updating the
+// kSupportedCapabilities/kUntouchableCapabilities/kFordiddenCapabilities lists.
+//
+// Handler names follow the following convention:
+//  Handler_<Opcode>_<Capability>()
+
+static std::optional<spv::Capability> Handler_OpTypeFloat_Float64(
+    const Instruction* instruction) {
+  assert(instruction->opcode() == spv::Op::OpTypeFloat &&
+         "This handler only support OpTypeFloat opcodes.");
+
+  const uint32_t size =
+      instruction->GetSingleWordInOperand(kOpTypeFloatSizeIndex);
+  return size == 64 ? std::optional(spv::Capability::Float64) : std::nullopt;
+}
+
+static std::optional<spv::Capability>
+Handler_OpTypePointer_StorageInputOutput16(const Instruction* instruction) {
+  assert(instruction->opcode() == spv::Op::OpTypePointer &&
+         "This handler only support OpTypePointer opcodes.");
+
+  // This capability is only required if the variable has an Input/Output
+  // storage class.
+  spv::StorageClass storage_class = spv::StorageClass(
+      instruction->GetSingleWordInOperand(kOpTypePointerStorageClassIndex));
+  if (storage_class != spv::StorageClass::Input &&
+      storage_class != spv::StorageClass::Output) {
+    return std::nullopt;
+  }
+
+  if (!Has16BitCapability(instruction->context()->get_feature_mgr())) {
+    return std::nullopt;
+  }
+
+  return AnyTypeOf(instruction, is16bitType)
+             ? std::optional(spv::Capability::StorageInputOutput16)
+             : std::nullopt;
+}
+
+static std::optional<spv::Capability>
+Handler_OpTypePointer_StoragePushConstant16(const Instruction* instruction) {
+  assert(instruction->opcode() == spv::Op::OpTypePointer &&
+         "This handler only support OpTypePointer opcodes.");
+
+  // This capability is only required if the variable has a PushConstant storage
+  // class.
+  spv::StorageClass storage_class = spv::StorageClass(
+      instruction->GetSingleWordInOperand(kOpTypePointerStorageClassIndex));
+  if (storage_class != spv::StorageClass::PushConstant) {
+    return std::nullopt;
+  }
+
+  if (!Has16BitCapability(instruction->context()->get_feature_mgr())) {
+    return std::nullopt;
+  }
+
+  return AnyTypeOf(instruction, is16bitType)
+             ? std::optional(spv::Capability::StoragePushConstant16)
+             : std::nullopt;
+}
+
+static std::optional<spv::Capability>
+Handler_OpTypePointer_StorageUniformBufferBlock16(
+    const Instruction* instruction) {
+  assert(instruction->opcode() == spv::Op::OpTypePointer &&
+         "This handler only support OpTypePointer opcodes.");
+
+  // This capability is only required if the variable has a Uniform storage
+  // class.
+  spv::StorageClass storage_class = spv::StorageClass(
+      instruction->GetSingleWordInOperand(kOpTypePointerStorageClassIndex));
+  if (storage_class != spv::StorageClass::Uniform) {
+    return std::nullopt;
+  }
+
+  if (!Has16BitCapability(instruction->context()->get_feature_mgr())) {
+    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::BufferBlock)) {
+          return false;
+        }
+
+        return AnyTypeOf(item, is16bitType);
+      });
+
+  return matchesCondition
+             ? std::optional(spv::Capability::StorageUniformBufferBlock16)
+             : std::nullopt;
+}
+
+static std::optional<spv::Capability> Handler_OpTypePointer_StorageUniform16(
+    const Instruction* instruction) {
+  assert(instruction->opcode() == spv::Op::OpTypePointer &&
+         "This handler only support OpTypePointer opcodes.");
+
+  // This capability is only required if the variable has a Uniform storage
+  // class.
+  spv::StorageClass storage_class = spv::StorageClass(
+      instruction->GetSingleWordInOperand(kOpTypePointerStorageClassIndex));
+  if (storage_class != spv::StorageClass::Uniform) {
+    return std::nullopt;
+  }
+
+  const auto* feature_manager = instruction->context()->get_feature_mgr();
+  if (!Has16BitCapability(feature_manager)) {
+    return std::nullopt;
+  }
+
+  const bool hasBufferBlockCapability =
+      feature_manager->GetCapabilities().contains(
+          spv::Capability::StorageUniformBufferBlock16);
+  const auto* decoration_mgr = instruction->context()->get_decoration_mgr();
+  bool found16bitType = false;
+
+  DFSWhile(instruction, [decoration_mgr, hasBufferBlockCapability,
+                         &found16bitType](const Instruction* item) {
+    if (found16bitType) {
+      return false;
+    }
+
+    if (hasBufferBlockCapability &&
+        decoration_mgr->HasDecoration(item->result_id(),
+                                      spv::Decoration::BufferBlock)) {
+      return false;
+    }
+
+    if (is16bitType(item)) {
+      found16bitType = true;
+      return false;
+    }
+
+    return true;
+  });
+
+  return found16bitType ? std::optional(spv::Capability::StorageUniform16)
+                        : std::nullopt;
+}
+
+static std::optional<spv::Capability> Handler_OpTypeInt_Int64(
+    const Instruction* instruction) {
+  assert(instruction->opcode() == spv::Op::OpTypeInt &&
+         "This handler only support OpTypeInt opcodes.");
+
+  const uint32_t size =
+      instruction->GetSingleWordInOperand(kOpTypeIntSizeIndex);
+  return size == 64 ? std::optional(spv::Capability::Int64) : std::nullopt;
+}
+
+static std::optional<spv::Capability> Handler_OpTypeImage_ImageMSArray(
+    const Instruction* instruction) {
+  assert(instruction->opcode() == spv::Op::OpTypeImage &&
+         "This handler only support OpTypeImage opcodes.");
+
+  const uint32_t arrayed =
+      instruction->GetSingleWordInOperand(kOpTypeImageArrayedIndex);
+  const uint32_t ms = instruction->GetSingleWordInOperand(kOpTypeImageMSIndex);
+  const uint32_t sampled =
+      instruction->GetSingleWordInOperand(kOpTypeImageSampledIndex);
+
+  return arrayed == 1 && sampled == 2 && ms == 1
+             ? std::optional(spv::Capability::ImageMSArray)
+             : std::nullopt;
+}
+
+// Opcode of interest to determine capabilities requirements.
+constexpr std::array<std::pair<spv::Op, OpcodeHandler>, 8> kOpcodeHandlers{{
+    // clang-format off
+    {spv::Op::OpTypeFloat,   Handler_OpTypeFloat_Float64 },
+    {spv::Op::OpTypeImage,   Handler_OpTypeImage_ImageMSArray},
+    {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},
+    // clang-format on
+}};
+
+// ==============  End opcode handler implementations.  =======================
+
+namespace {
+ExtensionSet getExtensionsRelatedTo(const CapabilitySet& capabilities,
+                                    const AssemblyGrammar& grammar) {
+  ExtensionSet output;
+  const spv_operand_desc_t* desc = nullptr;
+  for (auto capability : capabilities) {
+    if (SPV_SUCCESS != grammar.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]);
+    }
+  }
+
+  return output;
+}
+}  // namespace
+
+TrimCapabilitiesPass::TrimCapabilitiesPass()
+    : supportedCapabilities_(
+          TrimCapabilitiesPass::kSupportedCapabilities.cbegin(),
+          TrimCapabilitiesPass::kSupportedCapabilities.cend()),
+      forbiddenCapabilities_(
+          TrimCapabilitiesPass::kForbiddenCapabilities.cbegin(),
+          TrimCapabilitiesPass::kForbiddenCapabilities.cend()),
+      untouchableCapabilities_(
+          TrimCapabilitiesPass::kUntouchableCapabilities.cbegin(),
+          TrimCapabilitiesPass::kUntouchableCapabilities.cend()),
+      opcodeHandlers_(kOpcodeHandlers.cbegin(), kOpcodeHandlers.cend()) {}
+
+void TrimCapabilitiesPass::addInstructionRequirementsForOpcode(
+    spv::Op opcode, CapabilitySet* capabilities,
+    ExtensionSet* extensions) const {
+  // Ignoring OpBeginInvocationInterlockEXT and OpEndInvocationInterlockEXT
+  // because they have three possible capabilities, only one of which is needed
+  if (opcode == spv::Op::OpBeginInvocationInterlockEXT ||
+      opcode == spv::Op::OpEndInvocationInterlockEXT) {
+    return;
+  }
+
+  const spv_opcode_desc_t* desc = {};
+  auto result = context()->grammar().lookupOpcode(opcode, &desc);
+  if (result != SPV_SUCCESS) {
+    return;
+  }
+
+  addSupportedCapabilitiesToSet(desc, capabilities);
+  addSupportedExtensionsToSet(desc, extensions);
+}
+
+void TrimCapabilitiesPass::addInstructionRequirementsForOperand(
+    const Operand& operand, CapabilitySet* capabilities,
+    ExtensionSet* extensions) const {
+  // No supported capability relies on a 2+-word operand.
+  if (operand.words.size() != 1) {
+    return;
+  }
+
+  // No supported capability relies on a literal string operand or an ID.
+  if (operand.type == SPV_OPERAND_TYPE_LITERAL_STRING ||
+      operand.type == SPV_OPERAND_TYPE_ID ||
+      operand.type == SPV_OPERAND_TYPE_RESULT_ID) {
+    return;
+  }
+
+  // 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);
+    if (result != SPV_SUCCESS) {
+      return;
+    }
+    addSupportedCapabilitiesToSet(desc, capabilities);
+    addSupportedExtensionsToSet(desc, extensions);
+    return;
+  }
+
+  // case 2: operand can be a bitmask, we need to decompose the lookup.
+  for (uint32_t i = 0; i < 32; i++) {
+    const uint32_t mask = (1 << i) & operand.words[0];
+    if (!mask) {
+      continue;
+    }
+
+    const spv_operand_desc_t* desc = {};
+    auto result = context()->grammar().lookupOperand(operand.type, mask, &desc);
+    if (result != SPV_SUCCESS) {
+      continue;
+    }
+
+    addSupportedCapabilitiesToSet(desc, capabilities);
+    addSupportedExtensionsToSet(desc, extensions);
+  }
+}
+
+void TrimCapabilitiesPass::addInstructionRequirements(
+    Instruction* instruction, CapabilitySet* capabilities,
+    ExtensionSet* extensions) const {
+  // Ignoring OpCapability and OpExtension instructions.
+  if (instruction->opcode() == spv::Op::OpCapability ||
+      instruction->opcode() == spv::Op::OpExtension) {
+    return;
+  }
+
+  addInstructionRequirementsForOpcode(instruction->opcode(), capabilities,
+                                      extensions);
+
+  // Second case: one of the opcode operand is gated by a capability.
+  const uint32_t operandCount = instruction->NumOperands();
+  for (uint32_t i = 0; i < operandCount; i++) {
+    addInstructionRequirementsForOperand(instruction->GetOperand(i),
+                                         capabilities, extensions);
+  }
+
+  // Last case: some complex logic needs to be run to determine capabilities.
+  auto[begin, end] = opcodeHandlers_.equal_range(instruction->opcode());
+  for (auto it = begin; it != end; it++) {
+    const OpcodeHandler handler = it->second;
+    auto result = handler(instruction);
+    if (!result.has_value()) {
+      continue;
+    }
+
+    capabilities->insert(*result);
+  }
+}
+
+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);
+  if (result != SPV_SUCCESS) {
+    return;
+  }
+  addSupportedExtensionsToSet(desc, extensions);
+}
+
+std::pair<CapabilitySet, ExtensionSet>
+TrimCapabilitiesPass::DetermineRequiredCapabilitiesAndExtensions() const {
+  CapabilitySet required_capabilities;
+  ExtensionSet required_extensions;
+
+  get_module()->ForEachInst([&](Instruction* instruction) {
+    addInstructionRequirements(instruction, &required_capabilities,
+                               &required_extensions);
+  });
+
+  for (auto capability : required_capabilities) {
+    AddExtensionsForOperand(SPV_OPERAND_TYPE_CAPABILITY,
+                            static_cast<uint32_t>(capability),
+                            &required_extensions);
+  }
+
+#if !defined(NDEBUG)
+  // Debug only. We check the outputted required capabilities against the
+  // supported capabilities list. The supported capabilities list is useful for
+  // API users to quickly determine if they can use the pass or not. But this
+  // list has to remain up-to-date with the pass code. If we can detect a
+  // capability as required, but it's not listed, it means the list is
+  // out-of-sync. This method is not ideal, but should cover most cases.
+  {
+    for (auto capability : required_capabilities) {
+      assert(supportedCapabilities_.contains(capability) &&
+             "Module is using a capability that is not listed as supported.");
+    }
+  }
+#endif
+
+  return std::make_pair(std::move(required_capabilities),
+                        std::move(required_extensions));
+}
+
+Pass::Status TrimCapabilitiesPass::TrimUnrequiredCapabilities(
+    const CapabilitySet& required_capabilities) const {
+  const FeatureManager* feature_manager = context()->get_feature_mgr();
+  CapabilitySet capabilities_to_trim;
+  for (auto capability : feature_manager->GetCapabilities()) {
+    // Some capabilities cannot be safely removed. Leaving them untouched.
+    if (untouchableCapabilities_.contains(capability)) {
+      continue;
+    }
+
+    // If the capability is unsupported, don't trim it.
+    if (!supportedCapabilities_.contains(capability)) {
+      continue;
+    }
+
+    if (required_capabilities.contains(capability)) {
+      continue;
+    }
+
+    capabilities_to_trim.insert(capability);
+  }
+
+  for (auto capability : capabilities_to_trim) {
+    context()->RemoveCapability(capability);
+  }
+
+  return capabilities_to_trim.size() == 0 ? Pass::Status::SuccessWithoutChange
+                                          : Pass::Status::SuccessWithChange;
+}
+
+Pass::Status TrimCapabilitiesPass::TrimUnrequiredExtensions(
+    const ExtensionSet& required_extensions) const {
+  const auto supported_extensions =
+      getExtensionsRelatedTo(supportedCapabilities_, context()->grammar());
+
+  bool modified_module = false;
+  for (auto extension : supported_extensions) {
+    if (required_extensions.contains(extension)) {
+      continue;
+    }
+
+    if (context()->RemoveExtension(extension)) {
+      modified_module = true;
+    }
+  }
+
+  return modified_module ? Pass::Status::SuccessWithChange
+                         : Pass::Status::SuccessWithoutChange;
+}
+
+bool TrimCapabilitiesPass::HasForbiddenCapabilities() const {
+  // EnumSet.HasAnyOf returns `true` if the given set is empty.
+  if (forbiddenCapabilities_.size() == 0) {
+    return false;
+  }
+
+  const auto& capabilities = context()->get_feature_mgr()->GetCapabilities();
+  return capabilities.HasAnyOf(forbiddenCapabilities_);
+}
+
+Pass::Status TrimCapabilitiesPass::Process() {
+  if (HasForbiddenCapabilities()) {
+    return Status::SuccessWithoutChange;
+  }
+
+  auto[required_capabilities, required_extensions] =
+      DetermineRequiredCapabilitiesAndExtensions();
+
+  Pass::Status capStatus = TrimUnrequiredCapabilities(required_capabilities);
+  Pass::Status extStatus = TrimUnrequiredExtensions(required_extensions);
+
+  return capStatus == Pass::Status::SuccessWithChange ||
+                 extStatus == Pass::Status::SuccessWithChange
+             ? Pass::Status::SuccessWithChange
+             : Pass::Status::SuccessWithoutChange;
+}
+
+}  // namespace opt
+}  // namespace spvtools
diff --git a/source/opt/trim_capabilities_pass.h b/source/opt/trim_capabilities_pass.h
new file mode 100644
index 0000000..9202b2e
--- /dev/null
+++ b/source/opt/trim_capabilities_pass.h
@@ -0,0 +1,197 @@
+// Copyright (c) 2023 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_OPT_TRIM_CAPABILITIES_PASS_H_
+#define SOURCE_OPT_TRIM_CAPABILITIES_PASS_H_
+
+#include <algorithm>
+#include <array>
+#include <functional>
+#include <optional>
+#include <unordered_map>
+#include <unordered_set>
+
+#include "source/enum_set.h"
+#include "source/extensions.h"
+#include "source/opt/ir_context.h"
+#include "source/opt/module.h"
+#include "source/opt/pass.h"
+#include "source/spirv_target_env.h"
+
+namespace spvtools {
+namespace opt {
+
+// This is required for NDK build. The unordered_set/unordered_map
+// implementation don't work with class enums.
+struct ClassEnumHash {
+  std::size_t operator()(spv::Capability value) const {
+    using StoringType = typename std::underlying_type_t<spv::Capability>;
+    return std::hash<StoringType>{}(static_cast<StoringType>(value));
+  }
+
+  std::size_t operator()(spv::Op value) const {
+    using StoringType = typename std::underlying_type_t<spv::Op>;
+    return std::hash<StoringType>{}(static_cast<StoringType>(value));
+  }
+};
+
+// An opcode handler is a function which, given an instruction, returns either
+// the required capability, or nothing.
+// Each handler checks one case for a capability requirement.
+//
+// Example:
+//  - `OpTypeImage` can have operand `A` operand which requires capability 1
+//  - `OpTypeImage` can also have operand `B` which requires capability 2.
+//    -> We have 2 handlers: `Handler_OpTypeImage_1` and
+//    `Handler_OpTypeImage_2`.
+using OpcodeHandler =
+    std::optional<spv::Capability> (*)(const Instruction* instruction);
+
+// This pass tried to remove superfluous capabilities declared in the module.
+// - If all the capabilities listed by an extension are removed, the extension
+//   is also trimmed.
+// - If the module countains any capability listed in `kForbiddenCapabilities`,
+//   the module is left untouched.
+// - No capabilities listed in `kUntouchableCapabilities` are trimmed, even when
+//   not used.
+// - Only capabilitied listed in `kSupportedCapabilities` are supported.
+// - If the module contains unsupported capabilities, results might be
+//   incorrect.
+class TrimCapabilitiesPass : public Pass {
+ private:
+  // All the capabilities supported by this optimization pass. If your module
+  // contains unsupported instruction, the pass could yield bad results.
+  static constexpr std::array kSupportedCapabilities{
+      // clang-format off
+      spv::Capability::Float64,
+      spv::Capability::FragmentShaderPixelInterlockEXT,
+      spv::Capability::FragmentShaderSampleInterlockEXT,
+      spv::Capability::FragmentShaderShadingRateInterlockEXT,
+      spv::Capability::Groups,
+      spv::Capability::Int64,
+      spv::Capability::Linkage,
+      spv::Capability::MinLod,
+      spv::Capability::RayQueryKHR,
+      spv::Capability::RayTracingKHR,
+      spv::Capability::RayTraversalPrimitiveCullingKHR,
+      spv::Capability::Shader,
+      spv::Capability::ShaderClockKHR,
+      spv::Capability::StorageInputOutput16,
+      spv::Capability::StoragePushConstant16,
+      spv::Capability::StorageUniform16,
+      spv::Capability::StorageUniformBufferBlock16,
+      spv::Capability::ImageMSArray
+      // clang-format on
+  };
+
+  // Those capabilities disable all transformation of the module.
+  static constexpr std::array kForbiddenCapabilities{
+      spv::Capability::Linkage,
+  };
+
+  // Those capabilities are never removed from a module because we cannot
+  // guess from the SPIR-V only if they are required or not.
+  static constexpr std::array kUntouchableCapabilities{
+      spv::Capability::Shader,
+  };
+
+ public:
+  TrimCapabilitiesPass();
+  TrimCapabilitiesPass(const TrimCapabilitiesPass&) = delete;
+  TrimCapabilitiesPass(TrimCapabilitiesPass&&) = delete;
+
+ 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];
+      if (supportedCapabilities_.contains(capability)) {
+        output->insert(capability);
+      }
+    }
+  }
+
+  // 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`.
+  template <class Descriptor>
+  inline void addSupportedExtensionsToSet(const Descriptor* const descriptor,
+                                          ExtensionSet* output) const {
+    if (descriptor->minVersion <=
+        spvVersionForTargetEnv(context()->GetTargetEnv())) {
+      return;
+    }
+    output->insert(descriptor->extensions,
+                   descriptor->extensions + descriptor->numExtensions);
+  }
+
+  void addInstructionRequirementsForOpcode(spv::Op opcode,
+                                           CapabilitySet* capabilities,
+                                           ExtensionSet* extensions) const;
+  void addInstructionRequirementsForOperand(const Operand& operand,
+                                            CapabilitySet* capabilities,
+                                            ExtensionSet* extensions) const;
+
+  // Given an `instruction`, determines the capabilities it requires, and output
+  // them in `capabilities`. The returned capabilities form a subset of
+  // kSupportedCapabilities.
+  void addInstructionRequirements(Instruction* instruction,
+                                  CapabilitySet* capabilities,
+                                  ExtensionSet* extensions) const;
+
+  // Given an operand `type` and `value`, adds the extensions it would require
+  // to `extensions`.
+  void AddExtensionsForOperand(const spv_operand_type_t type,
+                               const uint32_t value,
+                               ExtensionSet* extensions) const;
+
+  // Returns the list of required capabilities and extensions for the module.
+  // The returned capabilities form a subset of kSupportedCapabilities.
+  std::pair<CapabilitySet, ExtensionSet>
+  DetermineRequiredCapabilitiesAndExtensions() const;
+
+  // Trims capabilities not listed in `required_capabilities` if possible.
+  // Returns whether or not the module was modified.
+  Pass::Status TrimUnrequiredCapabilities(
+      const CapabilitySet& required_capabilities) const;
+
+  // Trims extensions not listed in `required_extensions` if supported by this
+  // pass. An extensions is considered supported as soon as one capability this
+  // pass support requires it.
+  Pass::Status TrimUnrequiredExtensions(
+      const ExtensionSet& required_extensions) const;
+
+  // Returns if the analyzed module contains any forbidden capability.
+  bool HasForbiddenCapabilities() const;
+
+ public:
+  const char* name() const override { return "trim-capabilities"; }
+  Status Process() override;
+
+ private:
+  const CapabilitySet supportedCapabilities_;
+  const CapabilitySet forbiddenCapabilities_;
+  const CapabilitySet untouchableCapabilities_;
+  const std::unordered_multimap<spv::Op, OpcodeHandler, ClassEnumHash>
+      opcodeHandlers_;
+};
+
+}  // namespace opt
+}  // namespace spvtools
+#endif  // SOURCE_OPT_TRIM_CAPABILITIES_H_
diff --git a/source/opt/type_manager.cpp b/source/opt/type_manager.cpp
index a0006f5..2dcc259 100644
--- a/source/opt/type_manager.cpp
+++ b/source/opt/type_manager.cpp
@@ -29,10 +29,8 @@
 namespace opt {
 namespace analysis {
 namespace {
-
-const int kSpvTypePointerStorageClass = 1;
-const int kSpvTypePointerTypeIdInIdx = 2;
-
+constexpr int kSpvTypePointerStorageClass = 1;
+constexpr int kSpvTypePointerTypeIdInIdx = 2;
 }  // namespace
 
 TypeManager::TypeManager(const MessageConsumer& consumer, IRContext* c)
@@ -49,7 +47,7 @@
 }
 
 std::pair<Type*, std::unique_ptr<Pointer>> TypeManager::GetTypeAndPointerType(
-    uint32_t id, SpvStorageClass sc) const {
+    uint32_t id, spv::StorageClass sc) const {
   Type* type = GetType(id);
   if (type) {
     return std::make_pair(type, MakeUnique<Pointer>(type, sc));
@@ -180,7 +178,7 @@
   if (iter == id_to_type_.end()) return;
 
   auto& type = iter->second;
-  if (!type->IsUniqueType(true)) {
+  if (!type->IsUniqueType()) {
     auto tIter = type_to_id_.find(type);
     if (tIter != type_to_id_.end() && tIter->second == id) {
       // |type| currently maps to |id|.
@@ -220,10 +218,10 @@
 
   RegisterType(id, *type);
   switch (type->kind()) {
-#define DefineParameterlessCase(kind)                                     \
-  case Type::k##kind:                                                     \
-    typeInst = MakeUnique<Instruction>(context(), SpvOpType##kind, 0, id, \
-                                       std::initializer_list<Operand>{}); \
+#define DefineParameterlessCase(kind)                                         \
+  case Type::k##kind:                                                         \
+    typeInst = MakeUnique<Instruction>(context(), spv::Op::OpType##kind, 0,   \
+                                       id, std::initializer_list<Operand>{}); \
     break
     DefineParameterlessCase(Void);
     DefineParameterlessCase(Bool);
@@ -236,10 +234,11 @@
     DefineParameterlessCase(NamedBarrier);
     DefineParameterlessCase(AccelerationStructureNV);
     DefineParameterlessCase(RayQueryKHR);
+    DefineParameterlessCase(HitObjectNV);
 #undef DefineParameterlessCase
     case Type::kInteger:
       typeInst = MakeUnique<Instruction>(
-          context(), SpvOpTypeInt, 0, id,
+          context(), spv::Op::OpTypeInt, 0, id,
           std::initializer_list<Operand>{
               {SPV_OPERAND_TYPE_LITERAL_INTEGER, {type->AsInteger()->width()}},
               {SPV_OPERAND_TYPE_LITERAL_INTEGER,
@@ -247,7 +246,7 @@
       break;
     case Type::kFloat:
       typeInst = MakeUnique<Instruction>(
-          context(), SpvOpTypeFloat, 0, id,
+          context(), spv::Op::OpTypeFloat, 0, id,
           std::initializer_list<Operand>{
               {SPV_OPERAND_TYPE_LITERAL_INTEGER, {type->AsFloat()->width()}}});
       break;
@@ -257,7 +256,7 @@
         return 0;
       }
       typeInst =
-          MakeUnique<Instruction>(context(), SpvOpTypeVector, 0, id,
+          MakeUnique<Instruction>(context(), spv::Op::OpTypeVector, 0, id,
                                   std::initializer_list<Operand>{
                                       {SPV_OPERAND_TYPE_ID, {subtype}},
                                       {SPV_OPERAND_TYPE_LITERAL_INTEGER,
@@ -270,7 +269,7 @@
         return 0;
       }
       typeInst =
-          MakeUnique<Instruction>(context(), SpvOpTypeMatrix, 0, id,
+          MakeUnique<Instruction>(context(), spv::Op::OpTypeMatrix, 0, id,
                                   std::initializer_list<Operand>{
                                       {SPV_OPERAND_TYPE_ID, {subtype}},
                                       {SPV_OPERAND_TYPE_LITERAL_INTEGER,
@@ -284,7 +283,7 @@
         return 0;
       }
       typeInst = MakeUnique<Instruction>(
-          context(), SpvOpTypeImage, 0, id,
+          context(), spv::Op::OpTypeImage, 0, id,
           std::initializer_list<Operand>{
               {SPV_OPERAND_TYPE_ID, {subtype}},
               {SPV_OPERAND_TYPE_DIMENSIONALITY,
@@ -308,7 +307,7 @@
         return 0;
       }
       typeInst = MakeUnique<Instruction>(
-          context(), SpvOpTypeSampledImage, 0, id,
+          context(), spv::Op::OpTypeSampledImage, 0, id,
           std::initializer_list<Operand>{{SPV_OPERAND_TYPE_ID, {subtype}}});
       break;
     }
@@ -318,7 +317,7 @@
         return 0;
       }
       typeInst = MakeUnique<Instruction>(
-          context(), SpvOpTypeArray, 0, id,
+          context(), spv::Op::OpTypeArray, 0, id,
           std::initializer_list<Operand>{
               {SPV_OPERAND_TYPE_ID, {subtype}},
               {SPV_OPERAND_TYPE_ID, {type->AsArray()->LengthId()}}});
@@ -331,7 +330,7 @@
         return 0;
       }
       typeInst = MakeUnique<Instruction>(
-          context(), SpvOpTypeRuntimeArray, 0, id,
+          context(), spv::Op::OpTypeRuntimeArray, 0, id,
           std::initializer_list<Operand>{{SPV_OPERAND_TYPE_ID, {subtype}}});
       break;
     }
@@ -346,7 +345,7 @@
         ops.push_back(Operand(SPV_OPERAND_TYPE_ID, {member_type_id}));
       }
       typeInst =
-          MakeUnique<Instruction>(context(), SpvOpTypeStruct, 0, id, ops);
+          MakeUnique<Instruction>(context(), spv::Op::OpTypeStruct, 0, id, ops);
       break;
     }
     case Type::kOpaque: {
@@ -354,7 +353,7 @@
       // Convert to null-terminated packed UTF-8 string.
       std::vector<uint32_t> words = spvtools::utils::MakeVector(opaque->name());
       typeInst = MakeUnique<Instruction>(
-          context(), SpvOpTypeOpaque, 0, id,
+          context(), spv::Op::OpTypeOpaque, 0, id,
           std::initializer_list<Operand>{
               {SPV_OPERAND_TYPE_LITERAL_STRING, words}});
       break;
@@ -366,7 +365,7 @@
         return 0;
       }
       typeInst = MakeUnique<Instruction>(
-          context(), SpvOpTypePointer, 0, id,
+          context(), spv::Op::OpTypePointer, 0, id,
           std::initializer_list<Operand>{
               {SPV_OPERAND_TYPE_STORAGE_CLASS,
                {static_cast<uint32_t>(pointer->storage_class())}},
@@ -388,20 +387,20 @@
         }
         ops.push_back(Operand(SPV_OPERAND_TYPE_ID, {paramater_type_id}));
       }
-      typeInst =
-          MakeUnique<Instruction>(context(), SpvOpTypeFunction, 0, id, ops);
+      typeInst = MakeUnique<Instruction>(context(), spv::Op::OpTypeFunction, 0,
+                                         id, ops);
       break;
     }
     case Type::kPipe:
       typeInst = MakeUnique<Instruction>(
-          context(), SpvOpTypePipe, 0, id,
+          context(), spv::Op::OpTypePipe, 0, id,
           std::initializer_list<Operand>{
               {SPV_OPERAND_TYPE_ACCESS_QUALIFIER,
                {static_cast<uint32_t>(type->AsPipe()->access_qualifier())}}});
       break;
     case Type::kForwardPointer:
       typeInst = MakeUnique<Instruction>(
-          context(), SpvOpTypeForwardPointer, 0, 0,
+          context(), spv::Op::OpTypeForwardPointer, 0, 0,
           std::initializer_list<Operand>{
               {SPV_OPERAND_TYPE_ID, {type->AsForwardPointer()->target_id()}},
               {SPV_OPERAND_TYPE_STORAGE_CLASS,
@@ -416,7 +415,7 @@
         return 0;
       }
       typeInst = MakeUnique<Instruction>(
-          context(), SpvOpTypeCooperativeMatrixNV, 0, id,
+          context(), spv::Op::OpTypeCooperativeMatrixNV, 0, id,
           std::initializer_list<Operand>{
               {SPV_OPERAND_TYPE_ID, {component_type}},
               {SPV_OPERAND_TYPE_SCOPE_ID, {coop_mat->scope_id()}},
@@ -424,6 +423,23 @@
               {SPV_OPERAND_TYPE_ID, {coop_mat->columns_id()}}});
       break;
     }
+    case Type::kCooperativeMatrixKHR: {
+      auto coop_mat = type->AsCooperativeMatrixKHR();
+      uint32_t const component_type =
+          GetTypeInstruction(coop_mat->component_type());
+      if (component_type == 0) {
+        return 0;
+      }
+      typeInst = MakeUnique<Instruction>(
+          context(), spv::Op::OpTypeCooperativeMatrixKHR, 0, id,
+          std::initializer_list<Operand>{
+              {SPV_OPERAND_TYPE_ID, {component_type}},
+              {SPV_OPERAND_TYPE_SCOPE_ID, {coop_mat->scope_id()}},
+              {SPV_OPERAND_TYPE_ID, {coop_mat->rows_id()}},
+              {SPV_OPERAND_TYPE_ID, {coop_mat->columns_id()}},
+              {SPV_OPERAND_TYPE_ID, {coop_mat->use_id()}}});
+      break;
+    }
     default:
       assert(false && "Unexpected type");
       break;
@@ -435,10 +451,10 @@
 }
 
 uint32_t TypeManager::FindPointerToType(uint32_t type_id,
-                                        SpvStorageClass storage_class) {
+                                        spv::StorageClass storage_class) {
   Type* pointeeTy = GetType(type_id);
   Pointer pointerTy(pointeeTy, storage_class);
-  if (pointeeTy->IsUniqueType(true)) {
+  if (pointeeTy->IsUniqueType()) {
     // Non-ambiguous type. Get the pointer type through the type manager.
     return GetTypeInstruction(&pointerTy);
   }
@@ -447,11 +463,11 @@
   Module::inst_iterator type_itr = context()->module()->types_values_begin();
   for (; type_itr != context()->module()->types_values_end(); ++type_itr) {
     const Instruction* type_inst = &*type_itr;
-    if (type_inst->opcode() == SpvOpTypePointer &&
+    if (type_inst->opcode() == spv::Op::OpTypePointer &&
         type_inst->GetSingleWordOperand(kSpvTypePointerTypeIdInIdx) ==
             type_id &&
-        type_inst->GetSingleWordOperand(kSpvTypePointerStorageClass) ==
-            storage_class)
+        spv::StorageClass(type_inst->GetSingleWordOperand(
+            kSpvTypePointerStorageClass)) == storage_class)
       return type_inst->result_id();
   }
 
@@ -459,7 +475,7 @@
   // TODO(1841): Handle id overflow.
   uint32_t resultId = context()->TakeNextId();
   std::unique_ptr<Instruction> type_inst(
-      new Instruction(context(), SpvOpTypePointer, 0, resultId,
+      new Instruction(context(), spv::Op::OpTypePointer, 0, resultId,
                       {{spv_operand_type_t::SPV_OPERAND_TYPE_STORAGE_CLASS,
                         {uint32_t(storage_class)}},
                        {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {type_id}}}));
@@ -476,7 +492,7 @@
     for (auto pair : structTy->element_decorations()) {
       uint32_t element = pair.first;
       for (auto vec : pair.second) {
-        CreateDecoration(id, vec, element);
+        CreateDecoration(id, vec, /* is_member */ true, element);
       }
     }
   }
@@ -484,10 +500,10 @@
 
 void TypeManager::CreateDecoration(uint32_t target,
                                    const std::vector<uint32_t>& decoration,
-                                   uint32_t element) {
+                                   bool is_member, uint32_t element) {
   std::vector<Operand> ops;
   ops.push_back(Operand(SPV_OPERAND_TYPE_ID, {target}));
-  if (element != 0) {
+  if (is_member) {
     ops.push_back(Operand(SPV_OPERAND_TYPE_LITERAL_INTEGER, {element}));
   }
   ops.push_back(Operand(SPV_OPERAND_TYPE_DECORATION, {decoration[0]}));
@@ -495,8 +511,8 @@
     ops.push_back(Operand(SPV_OPERAND_TYPE_LITERAL_INTEGER, {decoration[i]}));
   }
   context()->AddAnnotationInst(MakeUnique<Instruction>(
-      context(), (element == 0 ? SpvOpDecorate : SpvOpMemberDecorate), 0, 0,
-      ops));
+      context(), (is_member ? spv::Op::OpMemberDecorate : spv::Op::OpDecorate),
+      0, 0, ops));
   Instruction* inst = &*--context()->annotation_end();
   context()->get_def_use_mgr()->AnalyzeInstUse(inst);
 }
@@ -529,6 +545,7 @@
     DefineNoSubtypeCase(NamedBarrier);
     DefineNoSubtypeCase(AccelerationStructureNV);
     DefineNoSubtypeCase(RayQueryKHR);
+    DefineNoSubtypeCase(HitObjectNV);
 #undef DefineNoSubtypeCase
     case Type::kVector: {
       const Vector* vec_ty = type.AsVector();
@@ -628,6 +645,14 @@
           cm_type->columns_id());
       break;
     }
+    case Type::kCooperativeMatrixKHR: {
+      const CooperativeMatrixKHR* cm_type = type.AsCooperativeMatrixKHR();
+      const Type* component_type = cm_type->component_type();
+      rebuilt_ty = MakeUnique<CooperativeMatrixKHR>(
+          RebuildType(*component_type), cm_type->scope_id(), cm_type->rows_id(),
+          cm_type->columns_id(), cm_type->use_id());
+      break;
+    }
     default:
       assert(false && "Unhandled type");
       return nullptr;
@@ -665,46 +690,47 @@
 
   Type* type = nullptr;
   switch (inst.opcode()) {
-    case SpvOpTypeVoid:
+    case spv::Op::OpTypeVoid:
       type = new Void();
       break;
-    case SpvOpTypeBool:
+    case spv::Op::OpTypeBool:
       type = new Bool();
       break;
-    case SpvOpTypeInt:
+    case spv::Op::OpTypeInt:
       type = new Integer(inst.GetSingleWordInOperand(0),
                          inst.GetSingleWordInOperand(1));
       break;
-    case SpvOpTypeFloat:
+    case spv::Op::OpTypeFloat:
       type = new Float(inst.GetSingleWordInOperand(0));
       break;
-    case SpvOpTypeVector:
+    case spv::Op::OpTypeVector:
       type = new Vector(GetType(inst.GetSingleWordInOperand(0)),
                         inst.GetSingleWordInOperand(1));
       break;
-    case SpvOpTypeMatrix:
+    case spv::Op::OpTypeMatrix:
       type = new Matrix(GetType(inst.GetSingleWordInOperand(0)),
                         inst.GetSingleWordInOperand(1));
       break;
-    case SpvOpTypeImage: {
-      const SpvAccessQualifier access =
-          inst.NumInOperands() < 8
-              ? SpvAccessQualifierReadOnly
-              : static_cast<SpvAccessQualifier>(inst.GetSingleWordInOperand(7));
+    case spv::Op::OpTypeImage: {
+      const spv::AccessQualifier access =
+          inst.NumInOperands() < 8 ? spv::AccessQualifier::ReadOnly
+                                   : static_cast<spv::AccessQualifier>(
+                                         inst.GetSingleWordInOperand(7));
       type = new Image(
           GetType(inst.GetSingleWordInOperand(0)),
-          static_cast<SpvDim>(inst.GetSingleWordInOperand(1)),
+          static_cast<spv::Dim>(inst.GetSingleWordInOperand(1)),
           inst.GetSingleWordInOperand(2), inst.GetSingleWordInOperand(3) == 1,
           inst.GetSingleWordInOperand(4) == 1, inst.GetSingleWordInOperand(5),
-          static_cast<SpvImageFormat>(inst.GetSingleWordInOperand(6)), access);
+          static_cast<spv::ImageFormat>(inst.GetSingleWordInOperand(6)),
+          access);
     } break;
-    case SpvOpTypeSampler:
+    case spv::Op::OpTypeSampler:
       type = new Sampler();
       break;
-    case SpvOpTypeSampledImage:
+    case spv::Op::OpTypeSampledImage:
       type = new SampledImage(GetType(inst.GetSingleWordInOperand(0)));
       break;
-    case SpvOpTypeArray: {
+    case spv::Op::OpTypeArray: {
       const uint32_t length_id = inst.GetSingleWordInOperand(1);
       const Instruction* length_constant_inst = id_to_constant_inst_[length_id];
       assert(length_constant_inst);
@@ -717,11 +743,11 @@
       // Only OpSpecConstant has a SpecId.
       uint32_t spec_id = 0u;
       bool has_spec_id = false;
-      if (length_constant_inst->opcode() == SpvOpSpecConstant) {
+      if (length_constant_inst->opcode() == spv::Op::OpSpecConstant) {
         context()->get_decoration_mgr()->ForEachDecoration(
-            length_id, SpvDecorationSpecId,
+            length_id, uint32_t(spv::Decoration::SpecId),
             [&spec_id, &has_spec_id](const Instruction& decoration) {
-              assert(decoration.opcode() == SpvOpDecorate);
+              assert(decoration.opcode() == spv::Op::OpDecorate);
               spec_id = decoration.GetSingleWordOperand(2u);
               has_spec_id = true;
             });
@@ -730,7 +756,8 @@
       if (has_spec_id) {
         extra_words.push_back(spec_id);
       }
-      if ((opcode == SpvOpConstant) || (opcode == SpvOpSpecConstant)) {
+      if ((opcode == spv::Op::OpConstant) ||
+          (opcode == spv::Op::OpSpecConstant)) {
         // Always include the literal constant words.  In the spec constant
         // case, the constant might not be overridden, so it's still
         // significant.
@@ -754,7 +781,7 @@
         return type;
       }
     } break;
-    case SpvOpTypeRuntimeArray:
+    case spv::Op::OpTypeRuntimeArray:
       type = new RuntimeArray(GetType(inst.GetSingleWordInOperand(0)));
       if (id_to_incomplete_type_.count(inst.GetSingleWordInOperand(0))) {
         incomplete_types_.emplace_back(inst.result_id(), type);
@@ -762,7 +789,7 @@
         return type;
       }
       break;
-    case SpvOpTypeStruct: {
+    case spv::Op::OpTypeStruct: {
       std::vector<const Type*> element_types;
       bool incomplete_type = false;
       for (uint32_t i = 0; i < inst.NumInOperands(); ++i) {
@@ -780,14 +807,14 @@
         return type;
       }
     } break;
-    case SpvOpTypeOpaque: {
+    case spv::Op::OpTypeOpaque: {
       type = new Opaque(inst.GetInOperand(0).AsString());
     } break;
-    case SpvOpTypePointer: {
+    case spv::Op::OpTypePointer: {
       uint32_t pointee_type_id = inst.GetSingleWordInOperand(1);
       type = new Pointer(
           GetType(pointee_type_id),
-          static_cast<SpvStorageClass>(inst.GetSingleWordInOperand(0)));
+          static_cast<spv::StorageClass>(inst.GetSingleWordInOperand(0)));
 
       if (id_to_incomplete_type_.count(pointee_type_id)) {
         incomplete_types_.emplace_back(inst.result_id(), type);
@@ -797,7 +824,7 @@
       id_to_incomplete_type_.erase(inst.result_id());
 
     } break;
-    case SpvOpTypeFunction: {
+    case spv::Op::OpTypeFunction: {
       bool incomplete_type = false;
       uint32_t return_type_id = inst.GetSingleWordInOperand(0);
       if (id_to_incomplete_type_.count(return_type_id)) {
@@ -821,49 +848,58 @@
         return type;
       }
     } break;
-    case SpvOpTypeEvent:
+    case spv::Op::OpTypeEvent:
       type = new Event();
       break;
-    case SpvOpTypeDeviceEvent:
+    case spv::Op::OpTypeDeviceEvent:
       type = new DeviceEvent();
       break;
-    case SpvOpTypeReserveId:
+    case spv::Op::OpTypeReserveId:
       type = new ReserveId();
       break;
-    case SpvOpTypeQueue:
+    case spv::Op::OpTypeQueue:
       type = new Queue();
       break;
-    case SpvOpTypePipe:
+    case spv::Op::OpTypePipe:
       type = new Pipe(
-          static_cast<SpvAccessQualifier>(inst.GetSingleWordInOperand(0)));
+          static_cast<spv::AccessQualifier>(inst.GetSingleWordInOperand(0)));
       break;
-    case SpvOpTypeForwardPointer: {
+    case spv::Op::OpTypeForwardPointer: {
       // Handling of forward pointers is different from the other types.
       uint32_t target_id = inst.GetSingleWordInOperand(0);
-      type = new ForwardPointer(target_id, static_cast<SpvStorageClass>(
+      type = new ForwardPointer(target_id, static_cast<spv::StorageClass>(
                                                inst.GetSingleWordInOperand(1)));
       incomplete_types_.emplace_back(target_id, type);
       id_to_incomplete_type_[target_id] = type;
       return type;
     }
-    case SpvOpTypePipeStorage:
+    case spv::Op::OpTypePipeStorage:
       type = new PipeStorage();
       break;
-    case SpvOpTypeNamedBarrier:
+    case spv::Op::OpTypeNamedBarrier:
       type = new NamedBarrier();
       break;
-    case SpvOpTypeAccelerationStructureNV:
+    case spv::Op::OpTypeAccelerationStructureNV:
       type = new AccelerationStructureNV();
       break;
-    case SpvOpTypeCooperativeMatrixNV:
+    case spv::Op::OpTypeCooperativeMatrixNV:
       type = new CooperativeMatrixNV(GetType(inst.GetSingleWordInOperand(0)),
                                      inst.GetSingleWordInOperand(1),
                                      inst.GetSingleWordInOperand(2),
                                      inst.GetSingleWordInOperand(3));
       break;
-    case SpvOpTypeRayQueryKHR:
+    case spv::Op::OpTypeCooperativeMatrixKHR:
+      type = new CooperativeMatrixKHR(
+          GetType(inst.GetSingleWordInOperand(0)),
+          inst.GetSingleWordInOperand(1), inst.GetSingleWordInOperand(2),
+          inst.GetSingleWordInOperand(3), inst.GetSingleWordInOperand(4));
+      break;
+    case spv::Op::OpTypeRayQueryKHR:
       type = new RayQueryKHR();
       break;
+    case spv::Op::OpTypeHitObjectNV:
+      type = new HitObjectNV();
+      break;
     default:
       SPIRV_UNIMPLEMENTED(consumer_, "unhandled type");
       break;
@@ -886,11 +922,11 @@
 }
 
 void TypeManager::AttachDecoration(const Instruction& inst, Type* type) {
-  const SpvOp opcode = inst.opcode();
+  const spv::Op opcode = inst.opcode();
   if (!IsAnnotationInst(opcode)) return;
 
   switch (opcode) {
-    case SpvOpDecorate: {
+    case spv::Op::OpDecorate: {
       const auto count = inst.NumOperands();
       std::vector<uint32_t> data;
       for (uint32_t i = 1; i < count; ++i) {
@@ -898,7 +934,7 @@
       }
       type->AddDecoration(std::move(data));
     } break;
-    case SpvOpMemberDecorate: {
+    case spv::Op::OpMemberDecorate: {
       const auto count = inst.NumOperands();
       const uint32_t index = inst.GetSingleWordOperand(1);
       std::vector<uint32_t> data;
diff --git a/source/opt/type_manager.h b/source/opt/type_manager.h
index 72e37f4..a70c371 100644
--- a/source/opt/type_manager.h
+++ b/source/opt/type_manager.h
@@ -99,7 +99,7 @@
   //
   // |id| must be a registered type.
   std::pair<Type*, std::unique_ptr<Pointer>> GetTypeAndPointerType(
-      uint32_t id, SpvStorageClass sc) const;
+      uint32_t id, spv::StorageClass sc) const;
 
   // Returns an id for a declaration representing |type|.  Returns 0 if the type
   // does not exists, and could not be generated.
@@ -112,7 +112,7 @@
   // Find pointer to type and storage in module, return its resultId.  If it is
   // not found, a new type is created, and its id is returned.  Returns 0 if the
   // type could not be created.
-  uint32_t FindPointerToType(uint32_t type_id, SpvStorageClass storage_class);
+  uint32_t FindPointerToType(uint32_t type_id, spv::StorageClass storage_class);
 
   // Registers |id| to |type|.
   //
@@ -139,18 +139,22 @@
   const Type* GetMemberType(const Type* parent_type,
                             const std::vector<uint32_t>& access_chain);
 
-  Type* GetUIntType() {
-    Integer int_type(32, false);
-    return GetRegisteredType(&int_type);
-  }
+  // Attaches the decoration encoded in |inst| to |type|. Does nothing if the
+  // given instruction is not a decoration instruction. Assumes the target is
+  // |type| (e.g. should be called in loop of |type|'s decorations).
+  void AttachDecoration(const Instruction& inst, Type* type);
+
+  Type* GetUIntType() { return GetIntType(32, false); }
 
   uint32_t GetUIntTypeId() { return GetTypeInstruction(GetUIntType()); }
 
-  Type* GetSIntType() {
-    Integer int_type(32, true);
+  Type* GetIntType(int32_t bitWidth, bool isSigned) {
+    Integer int_type(bitWidth, isSigned);
     return GetRegisteredType(&int_type);
   }
 
+  Type* GetSIntType() { return GetIntType(32, true); }
+
   uint32_t GetSIntTypeId() { return GetTypeInstruction(GetSIntType()); }
 
   Type* GetFloatType() {
@@ -243,19 +247,15 @@
 
   // Create the annotation instruction.
   //
-  // If |element| is zero, an OpDecorate is created, other an OpMemberDecorate
-  // is created. The annotation is registered with the DefUseManager and the
-  // DecorationManager.
+  // If |is_member| is false, an OpDecorate of |decoration| on |id| is created,
+  // otherwise an OpMemberDecorate is created at |element|. The annotation is
+  // registered with the DefUseManager and the DecorationManager.
   void CreateDecoration(uint32_t id, const std::vector<uint32_t>& decoration,
-                        uint32_t element = 0);
+                        bool is_member = false, uint32_t element = 0);
 
   // Creates and returns a type from the given SPIR-V |inst|. Returns nullptr if
   // the given instruction is not for defining a type.
   Type* RecordIfTypeDefinition(const Instruction& inst);
-  // Attaches the decoration encoded in |inst| to |type|. Does nothing if the
-  // given instruction is not a decoration instruction. Assumes the target is
-  // |type| (e.g. should be called in loop of |type|'s decorations).
-  void AttachDecoration(const Instruction& inst, Type* type);
 
   // Returns an equivalent pointer to |type| built in terms of pointers owned by
   // |type_pool_|. For example, if |type| is a vec3 of bool, it will be rebuilt
diff --git a/source/opt/types.cpp b/source/opt/types.cpp
index 056aceb..b18b8cb 100644
--- a/source/opt/types.cpp
+++ b/source/opt/types.cpp
@@ -16,7 +16,6 @@
 
 #include <algorithm>
 #include <cassert>
-#include <climits>
 #include <cstdint>
 #include <sstream>
 #include <string>
@@ -24,7 +23,6 @@
 
 #include "source/util/hash_combine.h"
 #include "source/util/make_unique.h"
-#include "spirv/unified1/spirv.h"
 
 namespace spvtools {
 namespace opt {
@@ -65,7 +63,7 @@
   return true;
 }
 
-}  // anonymous namespace
+}  // namespace
 
 std::string Type::GetDecorationStr() const {
   std::ostringstream oss;
@@ -86,10 +84,9 @@
   return CompareTwoVectors(decorations_, that->decorations_);
 }
 
-bool Type::IsUniqueType(bool allowVariablePointers) const {
+bool Type::IsUniqueType() const {
   switch (kind_) {
     case kPointer:
-      return !allowVariablePointers;
     case kStruct:
     case kArray:
     case kRuntimeArray:
@@ -131,7 +128,9 @@
     DeclareKindCase(NamedBarrier);
     DeclareKindCase(AccelerationStructureNV);
     DeclareKindCase(CooperativeMatrixNV);
+    DeclareKindCase(CooperativeMatrixKHR);
     DeclareKindCase(RayQueryKHR);
+    DeclareKindCase(HitObjectNV);
 #undef DeclareKindCase
     default:
       assert(false && "Unhandled type");
@@ -177,7 +176,9 @@
     DeclareKindCase(NamedBarrier);
     DeclareKindCase(AccelerationStructureNV);
     DeclareKindCase(CooperativeMatrixNV);
+    DeclareKindCase(CooperativeMatrixKHR);
     DeclareKindCase(RayQueryKHR);
+    DeclareKindCase(HitObjectNV);
 #undef DeclareKindCase
     default:
       assert(false && "Unhandled type");
@@ -231,7 +232,9 @@
     DeclareKindCase(NamedBarrier);
     DeclareKindCase(AccelerationStructureNV);
     DeclareKindCase(CooperativeMatrixNV);
+    DeclareKindCase(CooperativeMatrixKHR);
     DeclareKindCase(RayQueryKHR);
+    DeclareKindCase(HitObjectNV);
 #undef DeclareKindCase
     default:
       assert(false && "Unhandled type");
@@ -357,8 +360,9 @@
   return element_type_->ComputeHashValue(hash, seen);
 }
 
-Image::Image(Type* type, SpvDim dimen, uint32_t d, bool array, bool multisample,
-             uint32_t sampling, SpvImageFormat f, SpvAccessQualifier qualifier)
+Image::Image(Type* type, spv::Dim dimen, uint32_t d, bool array,
+             bool multisample, uint32_t sampling, spv::ImageFormat f,
+             spv::AccessQualifier qualifier)
     : Type(kImage),
       sampled_type_(type),
       dim_(dimen),
@@ -383,9 +387,9 @@
 
 std::string Image::str() const {
   std::ostringstream oss;
-  oss << "image(" << sampled_type_->str() << ", " << dim_ << ", " << depth_
-      << ", " << arrayed_ << ", " << ms_ << ", " << sampled_ << ", " << format_
-      << ", " << access_qualifier_ << ")";
+  oss << "image(" << sampled_type_->str() << ", " << uint32_t(dim_) << ", "
+      << depth_ << ", " << arrayed_ << ", " << ms_ << ", " << sampled_ << ", "
+      << uint32_t(format_) << ", " << uint32_t(access_qualifier_) << ")";
   return oss.str();
 }
 
@@ -557,7 +561,7 @@
   return hash_combine(hash, name_);
 }
 
-Pointer::Pointer(const Type* type, SpvStorageClass sc)
+Pointer::Pointer(const Type* type, spv::StorageClass sc)
     : Type(kPointer), pointee_type_(type), storage_class_(sc) {}
 
 bool Pointer::IsSameImpl(const Type* that, IsSameCache* seen) const {
@@ -636,7 +640,7 @@
 
 std::string Pipe::str() const {
   std::ostringstream oss;
-  oss << "pipe(" << access_qualifier_ << ")";
+  oss << "pipe(" << uint32_t(access_qualifier_) << ")";
   return oss.str();
 }
 
@@ -707,6 +711,45 @@
          columns_id_ == mt->columns_id_ && HasSameDecorations(that);
 }
 
+CooperativeMatrixKHR::CooperativeMatrixKHR(const Type* type,
+                                           const uint32_t scope,
+                                           const uint32_t rows,
+                                           const uint32_t columns,
+                                           const uint32_t use)
+    : Type(kCooperativeMatrixKHR),
+      component_type_(type),
+      scope_id_(scope),
+      rows_id_(rows),
+      columns_id_(columns),
+      use_id_(use) {
+  assert(type != nullptr);
+  assert(scope != 0);
+  assert(rows != 0);
+  assert(columns != 0);
+}
+
+std::string CooperativeMatrixKHR::str() const {
+  std::ostringstream oss;
+  oss << "<" << component_type_->str() << ", " << scope_id_ << ", " << rows_id_
+      << ", " << columns_id_ << ", " << use_id_ << ">";
+  return oss.str();
+}
+
+size_t CooperativeMatrixKHR::ComputeExtraStateHash(size_t hash,
+                                                   SeenTypes* seen) const {
+  hash = hash_combine(hash, scope_id_, rows_id_, columns_id_, use_id_);
+  return component_type_->ComputeHashValue(hash, seen);
+}
+
+bool CooperativeMatrixKHR::IsSameImpl(const Type* that,
+                                      IsSameCache* seen) const {
+  const CooperativeMatrixKHR* mt = that->AsCooperativeMatrixKHR();
+  if (!mt) return false;
+  return component_type_->IsSameImpl(mt->component_type_, seen) &&
+         scope_id_ == mt->scope_id_ && rows_id_ == mt->rows_id_ &&
+         columns_id_ == mt->columns_id_ && HasSameDecorations(that);
+}
+
 }  // namespace analysis
 }  // namespace opt
 }  // namespace spvtools
diff --git a/source/opt/types.h b/source/opt/types.h
index a92669e..16a948c 100644
--- a/source/opt/types.h
+++ b/source/opt/types.h
@@ -60,7 +60,9 @@
 class NamedBarrier;
 class AccelerationStructureNV;
 class CooperativeMatrixNV;
+class CooperativeMatrixKHR;
 class RayQueryKHR;
+class HitObjectNV;
 
 // 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>.
@@ -99,7 +101,9 @@
     kNamedBarrier,
     kAccelerationStructureNV,
     kCooperativeMatrixNV,
+    kCooperativeMatrixKHR,
     kRayQueryKHR,
+    kHitObjectNV,
     kLast
   };
 
@@ -146,12 +150,16 @@
   // Returns a clone of |this| minus any decorations.
   std::unique_ptr<Type> RemoveDecorations() const;
 
-  // Returns true if this type must be unique.
+  // Returns true if this cannot hash to the same value as another type in the
+  // module. For example, structs are not unique types because the module could
+  // have two types
   //
-  // If variable pointers are allowed, then pointers are not required to be
-  // unique.
-  // TODO(alanbaker): Update this if variable pointers become a core feature.
-  bool IsUniqueType(bool allowVariablePointers = false) const;
+  //  %1 = OpTypeStruct %int
+  //  %2 = OpTypeStruct %int
+  //
+  // The only way to distinguish these types is the result id. The type manager
+  // will hash them to the same value.
+  bool IsUniqueType() const;
 
   bool operator==(const Type& other) const;
 
@@ -195,7 +203,9 @@
   DeclareCastMethod(NamedBarrier)
   DeclareCastMethod(AccelerationStructureNV)
   DeclareCastMethod(CooperativeMatrixNV)
+  DeclareCastMethod(CooperativeMatrixKHR)
   DeclareCastMethod(RayQueryKHR)
+  DeclareCastMethod(HitObjectNV)
 #undef DeclareCastMethod
 
 protected:
@@ -302,9 +312,9 @@
 
 class Image : public Type {
  public:
-  Image(Type* type, SpvDim dimen, uint32_t d, bool array, bool multisample,
-        uint32_t sampling, SpvImageFormat f,
-        SpvAccessQualifier qualifier = SpvAccessQualifierReadOnly);
+  Image(Type* type, spv::Dim dimen, uint32_t d, bool array, bool multisample,
+        uint32_t sampling, spv::ImageFormat f,
+        spv::AccessQualifier qualifier = spv::AccessQualifier::ReadOnly);
   Image(const Image&) = default;
 
   std::string str() const override;
@@ -313,13 +323,13 @@
   const Image* AsImage() const override { return this; }
 
   const Type* sampled_type() const { return sampled_type_; }
-  SpvDim dim() const { return dim_; }
+  spv::Dim dim() const { return dim_; }
   uint32_t depth() const { return depth_; }
   bool is_arrayed() const { return arrayed_; }
   bool is_multisampled() const { return ms_; }
   uint32_t sampled() const { return sampled_; }
-  SpvImageFormat format() const { return format_; }
-  SpvAccessQualifier access_qualifier() const { return access_qualifier_; }
+  spv::ImageFormat format() const { return format_; }
+  spv::AccessQualifier access_qualifier() const { return access_qualifier_; }
 
   size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override;
 
@@ -327,13 +337,13 @@
   bool IsSameImpl(const Type* that, IsSameCache*) const override;
 
   Type* sampled_type_;
-  SpvDim dim_;
+  spv::Dim dim_;
   uint32_t depth_;
   bool arrayed_;
   bool ms_;
   uint32_t sampled_;
-  SpvImageFormat format_;
-  SpvAccessQualifier access_qualifier_;
+  spv::ImageFormat format_;
+  spv::AccessQualifier access_qualifier_;
 };
 
 class SampledImage : public Type {
@@ -491,12 +501,12 @@
 
 class Pointer : public Type {
  public:
-  Pointer(const Type* pointee, SpvStorageClass sc);
+  Pointer(const Type* pointee, spv::StorageClass sc);
   Pointer(const Pointer&) = default;
 
   std::string str() const override;
   const Type* pointee_type() const { return pointee_type_; }
-  SpvStorageClass storage_class() const { return storage_class_; }
+  spv::StorageClass storage_class() const { return storage_class_; }
 
   Pointer* AsPointer() override { return this; }
   const Pointer* AsPointer() const override { return this; }
@@ -509,7 +519,7 @@
   bool IsSameImpl(const Type* that, IsSameCache*) const override;
 
   const Type* pointee_type_;
-  SpvStorageClass storage_class_;
+  spv::StorageClass storage_class_;
 };
 
 class Function : public Type {
@@ -540,7 +550,7 @@
 
 class Pipe : public Type {
  public:
-  Pipe(SpvAccessQualifier qualifier)
+  Pipe(spv::AccessQualifier qualifier)
       : Type(kPipe), access_qualifier_(qualifier) {}
   Pipe(const Pipe&) = default;
 
@@ -549,19 +559,19 @@
   Pipe* AsPipe() override { return this; }
   const Pipe* AsPipe() const override { return this; }
 
-  SpvAccessQualifier access_qualifier() const { return access_qualifier_; }
+  spv::AccessQualifier access_qualifier() const { return access_qualifier_; }
 
   size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override;
 
  private:
   bool IsSameImpl(const Type* that, IsSameCache*) const override;
 
-  SpvAccessQualifier access_qualifier_;
+  spv::AccessQualifier access_qualifier_;
 };
 
 class ForwardPointer : public Type {
  public:
-  ForwardPointer(uint32_t id, SpvStorageClass sc)
+  ForwardPointer(uint32_t id, spv::StorageClass sc)
       : Type(kForwardPointer),
         target_id_(id),
         storage_class_(sc),
@@ -570,7 +580,7 @@
 
   uint32_t target_id() const { return target_id_; }
   void SetTargetPointer(const Pointer* pointer) { pointer_ = pointer; }
-  SpvStorageClass storage_class() const { return storage_class_; }
+  spv::StorageClass storage_class() const { return storage_class_; }
   const Pointer* target_pointer() const { return pointer_; }
 
   std::string str() const override;
@@ -584,7 +594,7 @@
   bool IsSameImpl(const Type* that, IsSameCache*) const override;
 
   uint32_t target_id_;
-  SpvStorageClass storage_class_;
+  spv::StorageClass storage_class_;
   const Pointer* pointer_;
 };
 
@@ -617,6 +627,38 @@
   const uint32_t columns_id_;
 };
 
+class CooperativeMatrixKHR : public Type {
+ public:
+  CooperativeMatrixKHR(const Type* type, const uint32_t scope,
+                       const uint32_t rows, const uint32_t columns,
+                       const uint32_t use);
+  CooperativeMatrixKHR(const CooperativeMatrixKHR&) = default;
+
+  std::string str() const override;
+
+  CooperativeMatrixKHR* AsCooperativeMatrixKHR() override { return this; }
+  const CooperativeMatrixKHR* AsCooperativeMatrixKHR() const override {
+    return this;
+  }
+
+  size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override;
+
+  const Type* component_type() const { return component_type_; }
+  uint32_t scope_id() const { return scope_id_; }
+  uint32_t rows_id() const { return rows_id_; }
+  uint32_t columns_id() const { return columns_id_; }
+  uint32_t use_id() const { return use_id_; }
+
+ private:
+  bool IsSameImpl(const Type* that, IsSameCache*) const override;
+
+  const Type* component_type_;
+  const uint32_t scope_id_;
+  const uint32_t rows_id_;
+  const uint32_t columns_id_;
+  const uint32_t use_id_;
+};
+
 #define DefineParameterlessType(type, name)                                \
   class type : public Type {                                               \
    public:                                                                 \
@@ -648,6 +690,7 @@
 DefineParameterlessType(NamedBarrier, named_barrier);
 DefineParameterlessType(AccelerationStructureNV, accelerationStructureNV);
 DefineParameterlessType(RayQueryKHR, rayQueryKHR);
+DefineParameterlessType(HitObjectNV, hitObjectNV);
 #undef DefineParameterlessType
 
 }  // namespace analysis
diff --git a/source/opt/unify_const_pass.cpp b/source/opt/unify_const_pass.cpp
index 6bfa11a..83dd438 100644
--- a/source/opt/unify_const_pass.cpp
+++ b/source/opt/unify_const_pass.cpp
@@ -20,12 +20,10 @@
 #include <vector>
 
 #include "source/opt/def_use_manager.h"
-#include "source/opt/ir_context.h"
 #include "source/util/make_unique.h"
 
 namespace spvtools {
 namespace opt {
-
 namespace {
 
 // The trie that stores a bunch of result ids and, for a given instruction,
@@ -103,7 +101,7 @@
 
   std::unique_ptr<Node> root_;  // The root node of the trie.
 };
-}  // anonymous namespace
+}  // namespace
 
 Pass::Status UnifyConstantPass::Process() {
   bool modified = false;
@@ -139,12 +137,12 @@
     // processing is up to a descendant. This makes comparing the key array
     // always valid for judging duplication.
     switch (inst->opcode()) {
-      case SpvOp::SpvOpConstantTrue:
-      case SpvOp::SpvOpConstantFalse:
-      case SpvOp::SpvOpConstant:
-      case SpvOp::SpvOpConstantNull:
-      case SpvOp::SpvOpConstantSampler:
-      case SpvOp::SpvOpConstantComposite:
+      case spv::Op::OpConstantTrue:
+      case spv::Op::OpConstantFalse:
+      case spv::Op::OpConstant:
+      case spv::Op::OpConstantNull:
+      case spv::Op::OpConstantSampler:
+      case spv::Op::OpConstantComposite:
       // Only spec constants defined with OpSpecConstantOp and
       // OpSpecConstantComposite should be processed in this pass. Spec
       // constants defined with OpSpecConstant{|True|False} are decorated with
@@ -154,8 +152,8 @@
       // unique. When all the operands/components are the same between two
       // OpSpecConstant{Op|Composite} results, their result values must be the
       // same so are unifiable.
-      case SpvOp::SpvOpSpecConstantOp:
-      case SpvOp::SpvOpSpecConstantComposite: {
+      case spv::Op::OpSpecConstantOp:
+      case spv::Op::OpSpecConstantComposite: {
         uint32_t id = defined_constants.LookupEquivalentResultFor(*inst);
         if (id != inst->result_id()) {
           // The constant is a duplicated one, use the cached constant to
diff --git a/source/opt/upgrade_memory_model.cpp b/source/opt/upgrade_memory_model.cpp
index 9d6a5bc..1b439a6 100644
--- a/source/opt/upgrade_memory_model.cpp
+++ b/source/opt/upgrade_memory_model.cpp
@@ -28,14 +28,16 @@
 Pass::Status UpgradeMemoryModel::Process() {
   // TODO: This pass needs changes to support cooperative matrices.
   if (context()->get_feature_mgr()->HasCapability(
-          SpvCapabilityCooperativeMatrixNV)) {
+          spv::Capability::CooperativeMatrixNV)) {
     return Pass::Status::SuccessWithoutChange;
   }
 
   // Only update Logical GLSL450 to Logical VulkanKHR.
   Instruction* memory_model = get_module()->GetMemoryModel();
-  if (memory_model->GetSingleWordInOperand(0u) != SpvAddressingModelLogical ||
-      memory_model->GetSingleWordInOperand(1u) != SpvMemoryModelGLSL450) {
+  if (memory_model->GetSingleWordInOperand(0u) !=
+          uint32_t(spv::AddressingModel::Logical) ||
+      memory_model->GetSingleWordInOperand(1u) !=
+          uint32_t(spv::MemoryModel::GLSL450)) {
     return Pass::Status::SuccessWithoutChange;
   }
 
@@ -55,16 +57,17 @@
   // 3. Modify the memory model.
   Instruction* memory_model = get_module()->GetMemoryModel();
   context()->AddCapability(MakeUnique<Instruction>(
-      context(), SpvOpCapability, 0, 0,
+      context(), spv::Op::OpCapability, 0, 0,
       std::initializer_list<Operand>{
-          {SPV_OPERAND_TYPE_CAPABILITY, {SpvCapabilityVulkanMemoryModelKHR}}}));
+          {SPV_OPERAND_TYPE_CAPABILITY,
+           {uint32_t(spv::Capability::VulkanMemoryModelKHR)}}}));
   const std::string extension = "SPV_KHR_vulkan_memory_model";
   std::vector<uint32_t> words = spvtools::utils::MakeVector(extension);
   context()->AddExtension(
-      MakeUnique<Instruction>(context(), SpvOpExtension, 0, 0,
+      MakeUnique<Instruction>(context(), spv::Op::OpExtension, 0, 0,
                               std::initializer_list<Operand>{
                                   {SPV_OPERAND_TYPE_LITERAL_STRING, words}}));
-  memory_model->SetInOperand(1u, {SpvMemoryModelVulkanKHR});
+  memory_model->SetInOperand(1u, {uint32_t(spv::MemoryModel::VulkanKHR)});
 }
 
 void UpgradeMemoryModel::UpgradeInstructions() {
@@ -79,7 +82,7 @@
   // In SPIR-V 1.4 or later, normalize OpCopyMemory* access operands.
   for (auto& func : *get_module()) {
     func.ForEachInst([this](Instruction* inst) {
-      if (inst->opcode() == SpvOpExtInst) {
+      if (inst->opcode() == spv::Op::OpExtInst) {
         auto ext_inst = inst->GetSingleWordInOperand(1u);
         if (ext_inst == GLSLstd450Modf || ext_inst == GLSLstd450Frexp) {
           auto import =
@@ -89,9 +92,10 @@
           }
         }
       } else if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4)) {
-        if (inst->opcode() == SpvOpCopyMemory ||
-            inst->opcode() == SpvOpCopyMemorySized) {
-          uint32_t start_operand = inst->opcode() == SpvOpCopyMemory ? 2u : 3u;
+        if (inst->opcode() == spv::Op::OpCopyMemory ||
+            inst->opcode() == spv::Op::OpCopyMemorySized) {
+          uint32_t start_operand =
+              inst->opcode() == spv::Op::OpCopyMemory ? 2u : 3u;
           if (inst->NumInOperands() > start_operand) {
             auto num_access_words = MemoryAccessNumWords(
                 inst->GetSingleWordInOperand(start_operand));
@@ -105,10 +109,10 @@
             }
           } else {
             // Add two memory access operands.
-            inst->AddOperand(
-                {SPV_OPERAND_TYPE_MEMORY_ACCESS, {SpvMemoryAccessMaskNone}});
-            inst->AddOperand(
-                {SPV_OPERAND_TYPE_MEMORY_ACCESS, {SpvMemoryAccessMaskNone}});
+            inst->AddOperand({SPV_OPERAND_TYPE_MEMORY_ACCESS,
+                              {uint32_t(spv::MemoryAccessMask::MaskNone)}});
+            inst->AddOperand({SPV_OPERAND_TYPE_MEMORY_ACCESS,
+                              {uint32_t(spv::MemoryAccessMask::MaskNone)}});
           }
         }
       }
@@ -129,23 +133,23 @@
       bool dst_coherent = false;
       bool dst_volatile = false;
       uint32_t start_operand = 0u;
-      SpvScope scope = SpvScopeQueueFamilyKHR;
-      SpvScope src_scope = SpvScopeQueueFamilyKHR;
-      SpvScope dst_scope = SpvScopeQueueFamilyKHR;
+      spv::Scope scope = spv::Scope::QueueFamilyKHR;
+      spv::Scope src_scope = spv::Scope::QueueFamilyKHR;
+      spv::Scope dst_scope = spv::Scope::QueueFamilyKHR;
       switch (inst->opcode()) {
-        case SpvOpLoad:
-        case SpvOpStore:
+        case spv::Op::OpLoad:
+        case spv::Op::OpStore:
           std::tie(is_coherent, is_volatile, scope) =
               GetInstructionAttributes(inst->GetSingleWordInOperand(0u));
           break;
-        case SpvOpImageRead:
-        case SpvOpImageSparseRead:
-        case SpvOpImageWrite:
+        case spv::Op::OpImageRead:
+        case spv::Op::OpImageSparseRead:
+        case spv::Op::OpImageWrite:
           std::tie(is_coherent, is_volatile, scope) =
               GetInstructionAttributes(inst->GetSingleWordInOperand(0u));
           break;
-        case SpvOpCopyMemory:
-        case SpvOpCopyMemorySized:
+        case spv::Op::OpCopyMemory:
+        case spv::Op::OpCopyMemorySized:
           std::tie(dst_coherent, dst_volatile, dst_scope) =
               GetInstructionAttributes(inst->GetSingleWordInOperand(0u));
           std::tie(src_coherent, src_volatile, src_scope) =
@@ -156,17 +160,17 @@
       }
 
       switch (inst->opcode()) {
-        case SpvOpLoad:
+        case spv::Op::OpLoad:
           UpgradeFlags(inst, 1u, is_coherent, is_volatile, kVisibility,
                        kMemory);
           break;
-        case SpvOpStore:
+        case spv::Op::OpStore:
           UpgradeFlags(inst, 2u, is_coherent, is_volatile, kAvailability,
                        kMemory);
           break;
-        case SpvOpCopyMemory:
-        case SpvOpCopyMemorySized:
-          start_operand = inst->opcode() == SpvOpCopyMemory ? 2u : 3u;
+        case spv::Op::OpCopyMemory:
+        case spv::Op::OpCopyMemorySized:
+          start_operand = inst->opcode() == spv::Op::OpCopyMemory ? 2u : 3u;
           if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4)) {
             // There are guaranteed to be two memory access operands at this
             // point so treat source and target separately.
@@ -183,11 +187,11 @@
                          kVisibility, kMemory);
           }
           break;
-        case SpvOpImageRead:
-        case SpvOpImageSparseRead:
+        case spv::Op::OpImageRead:
+        case spv::Op::OpImageSparseRead:
           UpgradeFlags(inst, 2u, is_coherent, is_volatile, kVisibility, kImage);
           break;
-        case SpvOpImageWrite:
+        case spv::Op::OpImageWrite:
           UpgradeFlags(inst, 3u, is_coherent, is_volatile, kAvailability,
                        kImage);
           break;
@@ -205,7 +209,7 @@
         // There are two memory access operands. The first is for the target and
         // the second is for the source.
         if (dst_coherent || src_coherent) {
-          start_operand = inst->opcode() == SpvOpCopyMemory ? 2u : 3u;
+          start_operand = inst->opcode() == spv::Op::OpCopyMemory ? 2u : 3u;
           std::vector<Operand> new_operands;
           uint32_t num_access_words =
               MemoryAccessNumWords(inst->GetSingleWordInOperand(start_operand));
@@ -255,13 +259,13 @@
       if (spvOpcodeIsAtomicOp(inst->opcode())) {
         bool unused_coherent = false;
         bool is_volatile = false;
-        SpvScope unused_scope = SpvScopeQueueFamilyKHR;
+        spv::Scope unused_scope = spv::Scope::QueueFamilyKHR;
         std::tie(unused_coherent, is_volatile, unused_scope) =
             GetInstructionAttributes(inst->GetSingleWordInOperand(0));
 
         UpgradeSemantics(inst, 2u, is_volatile);
-        if (inst->opcode() == SpvOpAtomicCompareExchange ||
-            inst->opcode() == SpvOpAtomicCompareExchangeWeak) {
+        if (inst->opcode() == spv::Op::OpAtomicCompareExchange ||
+            inst->opcode() == spv::Op::OpAtomicCompareExchangeWeak) {
           UpgradeSemantics(inst, 3u, is_volatile);
         }
       }
@@ -286,14 +290,14 @@
     value = constant->GetU32();
   }
 
-  value |= SpvMemorySemanticsVolatileMask;
+  value |= uint32_t(spv::MemorySemanticsMask::Volatile);
   auto new_constant = context()->get_constant_mgr()->GetConstant(type, {value});
   auto new_semantics =
       context()->get_constant_mgr()->GetDefiningInstruction(new_constant);
   inst->SetInOperand(in_operand, {new_semantics->result_id()});
 }
 
-std::tuple<bool, bool, SpvScope> UpgradeMemoryModel::GetInstructionAttributes(
+std::tuple<bool, bool, spv::Scope> UpgradeMemoryModel::GetInstructionAttributes(
     uint32_t id) {
   // |id| is a pointer used in a memory/image instruction. Need to determine if
   // that pointer points to volatile or coherent memory. Workgroup storage
@@ -302,8 +306,8 @@
   Instruction* inst = context()->get_def_use_mgr()->GetDef(id);
   analysis::Type* type = context()->get_type_mgr()->GetType(inst->type_id());
   if (type->AsPointer() &&
-      type->AsPointer()->storage_class() == SpvStorageClassWorkgroup) {
-    return std::make_tuple(true, false, SpvScopeWorkgroup);
+      type->AsPointer()->storage_class() == spv::StorageClass::Workgroup) {
+    return std::make_tuple(true, false, spv::Scope::Workgroup);
   }
 
   bool is_coherent = false;
@@ -313,7 +317,7 @@
       TraceInstruction(context()->get_def_use_mgr()->GetDef(id),
                        std::vector<uint32_t>(), &visited);
 
-  return std::make_tuple(is_coherent, is_volatile, SpvScopeQueueFamilyKHR);
+  return std::make_tuple(is_coherent, is_volatile, spv::Scope::QueueFamilyKHR);
 }
 
 std::pair<bool, bool> UpgradeMemoryModel::TraceInstruction(
@@ -336,10 +340,10 @@
   bool is_coherent = false;
   bool is_volatile = false;
   switch (inst->opcode()) {
-    case SpvOpVariable:
-    case SpvOpFunctionParameter:
-      is_coherent |= HasDecoration(inst, 0, SpvDecorationCoherent);
-      is_volatile |= HasDecoration(inst, 0, SpvDecorationVolatile);
+    case spv::Op::OpVariable:
+    case spv::Op::OpFunctionParameter:
+      is_coherent |= HasDecoration(inst, 0, spv::Decoration::Coherent);
+      is_volatile |= HasDecoration(inst, 0, spv::Decoration::Volatile);
       if (!is_coherent || !is_volatile) {
         bool type_coherent = false;
         bool type_volatile = false;
@@ -349,14 +353,14 @@
         is_volatile |= type_volatile;
       }
       break;
-    case SpvOpAccessChain:
-    case SpvOpInBoundsAccessChain:
+    case spv::Op::OpAccessChain:
+    case spv::Op::OpInBoundsAccessChain:
       // Store indices in reverse order.
       for (uint32_t i = inst->NumInOperands() - 1; i > 0; --i) {
         indices.push_back(inst->GetSingleWordInOperand(i));
       }
       break;
-    case SpvOpPtrAccessChain:
+    case spv::Op::OpPtrAccessChain:
       // Store indices in reverse order. Skip the |Element| operand.
       for (uint32_t i = inst->NumInOperands() - 1; i > 1; --i) {
         indices.push_back(inst->GetSingleWordInOperand(i));
@@ -375,8 +379,8 @@
 
   // Variables and function parameters are sources. Continue searching until we
   // reach them.
-  if (inst->opcode() != SpvOpVariable &&
-      inst->opcode() != SpvOpFunctionParameter) {
+  if (inst->opcode() != spv::Op::OpVariable &&
+      inst->opcode() != spv::Op::OpFunctionParameter) {
     inst->ForEachInId([this, &is_coherent, &is_volatile, &indices,
                        &visited](const uint32_t* id_ptr) {
       Instruction* op_inst = context()->get_def_use_mgr()->GetDef(*id_ptr);
@@ -404,24 +408,24 @@
   bool is_coherent = false;
   bool is_volatile = false;
   Instruction* type_inst = context()->get_def_use_mgr()->GetDef(type_id);
-  assert(type_inst->opcode() == SpvOpTypePointer);
+  assert(type_inst->opcode() == spv::Op::OpTypePointer);
   Instruction* element_inst = context()->get_def_use_mgr()->GetDef(
       type_inst->GetSingleWordInOperand(1u));
   for (int i = (int)indices.size() - 1; i >= 0; --i) {
     if (is_coherent && is_volatile) break;
 
-    if (element_inst->opcode() == SpvOpTypePointer) {
+    if (element_inst->opcode() == spv::Op::OpTypePointer) {
       element_inst = context()->get_def_use_mgr()->GetDef(
           element_inst->GetSingleWordInOperand(1u));
-    } else if (element_inst->opcode() == SpvOpTypeStruct) {
+    } else if (element_inst->opcode() == spv::Op::OpTypeStruct) {
       uint32_t index = indices.at(i);
       Instruction* index_inst = context()->get_def_use_mgr()->GetDef(index);
-      assert(index_inst->opcode() == SpvOpConstant);
+      assert(index_inst->opcode() == spv::Op::OpConstant);
       uint64_t value = GetIndexValue(index_inst);
       is_coherent |= HasDecoration(element_inst, static_cast<uint32_t>(value),
-                                   SpvDecorationCoherent);
+                                   spv::Decoration::Coherent);
       is_volatile |= HasDecoration(element_inst, static_cast<uint32_t>(value),
-                                   SpvDecorationVolatile);
+                                   spv::Decoration::Volatile);
       element_inst = context()->get_def_use_mgr()->GetDef(
           element_inst->GetSingleWordInOperand(static_cast<uint32_t>(value)));
     } else {
@@ -457,13 +461,13 @@
 
     if (!visited.insert(def).second) continue;
 
-    if (def->opcode() == SpvOpTypeStruct) {
+    if (def->opcode() == spv::Op::OpTypeStruct) {
       // Any member decorated with coherent and/or volatile is enough to have
       // the related operation be flagged as coherent and/or volatile.
       is_coherent |= HasDecoration(def, std::numeric_limits<uint32_t>::max(),
-                                   SpvDecorationCoherent);
+                                   spv::Decoration::Coherent);
       is_volatile |= HasDecoration(def, std::numeric_limits<uint32_t>::max(),
-                                   SpvDecorationVolatile);
+                                   spv::Decoration::Volatile);
       if (is_coherent && is_volatile)
         return std::make_pair(is_coherent, is_volatile);
 
@@ -475,7 +479,7 @@
     } else if (spvOpcodeIsComposite(def->opcode())) {
       stack.push_back(context()->get_def_use_mgr()->GetDef(
           def->GetSingleWordInOperand(0u)));
-    } else if (def->opcode() == SpvOpTypePointer) {
+    } else if (def->opcode() == spv::Op::OpTypePointer) {
       stack.push_back(context()->get_def_use_mgr()->GetDef(
           def->GetSingleWordInOperand(1u)));
     }
@@ -504,14 +508,15 @@
 }
 
 bool UpgradeMemoryModel::HasDecoration(const Instruction* inst, uint32_t value,
-                                       SpvDecoration decoration) {
+                                       spv::Decoration decoration) {
   // If the iteration was terminated early then an appropriate decoration was
   // found.
   return !context()->get_decoration_mgr()->WhileEachDecoration(
-      inst->result_id(), decoration, [value](const Instruction& i) {
-        if (i.opcode() == SpvOpDecorate || i.opcode() == SpvOpDecorateId) {
+      inst->result_id(), (uint32_t)decoration, [value](const Instruction& i) {
+        if (i.opcode() == spv::Op::OpDecorate ||
+            i.opcode() == spv::Op::OpDecorateId) {
           return false;
-        } else if (i.opcode() == SpvOpMemberDecorate) {
+        } else if (i.opcode() == spv::Op::OpMemberDecorate) {
           if (value == i.GetSingleWordInOperand(1u) ||
               value == std::numeric_limits<uint32_t>::max())
             return false;
@@ -533,27 +538,27 @@
   }
   if (is_coherent) {
     if (inst_type == kMemory) {
-      flags |= SpvMemoryAccessNonPrivatePointerKHRMask;
+      flags |= uint32_t(spv::MemoryAccessMask::NonPrivatePointerKHR);
       if (operation_type == kVisibility) {
-        flags |= SpvMemoryAccessMakePointerVisibleKHRMask;
+        flags |= uint32_t(spv::MemoryAccessMask::MakePointerVisibleKHR);
       } else {
-        flags |= SpvMemoryAccessMakePointerAvailableKHRMask;
+        flags |= uint32_t(spv::MemoryAccessMask::MakePointerAvailableKHR);
       }
     } else {
-      flags |= SpvImageOperandsNonPrivateTexelKHRMask;
+      flags |= uint32_t(spv::ImageOperandsMask::NonPrivateTexelKHR);
       if (operation_type == kVisibility) {
-        flags |= SpvImageOperandsMakeTexelVisibleKHRMask;
+        flags |= uint32_t(spv::ImageOperandsMask::MakeTexelVisibleKHR);
       } else {
-        flags |= SpvImageOperandsMakeTexelAvailableKHRMask;
+        flags |= uint32_t(spv::ImageOperandsMask::MakeTexelAvailableKHR);
       }
     }
   }
 
   if (is_volatile) {
     if (inst_type == kMemory) {
-      flags |= SpvMemoryAccessVolatileMask;
+      flags |= uint32_t(spv::MemoryAccessMask::Volatile);
     } else {
-      flags |= SpvImageOperandsVolatileTexelKHRMask;
+      flags |= uint32_t(spv::ImageOperandsMask::VolatileTexelKHR);
     }
   }
 
@@ -566,7 +571,7 @@
   }
 }
 
-uint32_t UpgradeMemoryModel::GetScopeConstant(SpvScope scope) {
+uint32_t UpgradeMemoryModel::GetScopeConstant(spv::Scope scope) {
   analysis::Integer int_ty(32, false);
   uint32_t int_id = context()->get_type_mgr()->GetTypeInstruction(&int_ty);
   const analysis::Constant* constant =
@@ -587,15 +592,19 @@
       context()->get_decoration_mgr()->RemoveDecorationsFrom(
           inst->result_id(), [](const Instruction& dec) {
             switch (dec.opcode()) {
-              case SpvOpDecorate:
-              case SpvOpDecorateId:
-                if (dec.GetSingleWordInOperand(1u) == SpvDecorationCoherent ||
-                    dec.GetSingleWordInOperand(1u) == SpvDecorationVolatile)
+              case spv::Op::OpDecorate:
+              case spv::Op::OpDecorateId:
+                if (spv::Decoration(dec.GetSingleWordInOperand(1u)) ==
+                        spv::Decoration::Coherent ||
+                    spv::Decoration(dec.GetSingleWordInOperand(1u)) ==
+                        spv::Decoration::Volatile)
                   return true;
                 break;
-              case SpvOpMemberDecorate:
-                if (dec.GetSingleWordInOperand(2u) == SpvDecorationCoherent ||
-                    dec.GetSingleWordInOperand(2u) == SpvDecorationVolatile)
+              case spv::Op::OpMemberDecorate:
+                if (spv::Decoration(dec.GetSingleWordInOperand(2u)) ==
+                        spv::Decoration::Coherent ||
+                    spv::Decoration(dec.GetSingleWordInOperand(2u)) ==
+                        spv::Decoration::Volatile)
                   return true;
                 break;
               default:
@@ -616,7 +625,7 @@
     for (auto& block : *function) {
       block.ForEachInst([this, &barriers,
                          &operates_on_output](Instruction* inst) {
-        if (inst->opcode() == SpvOpControlBarrier) {
+        if (inst->opcode() == spv::Op::OpControlBarrier) {
           barriers.push_back(inst);
         } else if (!operates_on_output) {
           // This instruction operates on output storage class if it is a
@@ -625,7 +634,7 @@
           analysis::Type* type =
               context()->get_type_mgr()->GetType(inst->type_id());
           if (type && type->AsPointer() &&
-              type->AsPointer()->storage_class() == SpvStorageClassOutput) {
+              type->AsPointer()->storage_class() == spv::StorageClass::Output) {
             operates_on_output = true;
             return;
           }
@@ -635,7 +644,8 @@
             analysis::Type* op_type =
                 context()->get_type_mgr()->GetType(op_inst->type_id());
             if (op_type && op_type->AsPointer() &&
-                op_type->AsPointer()->storage_class() == SpvStorageClassOutput)
+                op_type->AsPointer()->storage_class() ==
+                    spv::StorageClass::Output)
               operates_on_output = true;
           });
         }
@@ -646,7 +656,8 @@
 
   std::queue<uint32_t> roots;
   for (auto& e : get_module()->entry_points())
-    if (e.GetSingleWordInOperand(0u) == SpvExecutionModelTessellationControl) {
+    if (spv::ExecutionModel(e.GetSingleWordInOperand(0u)) ==
+        spv::ExecutionModel::TessellationControl) {
       roots.push(e.GetSingleWordInOperand(1u));
       if (context()->ProcessCallTreeFromRoots(CollectBarriers, &roots)) {
         for (auto barrier : barriers) {
@@ -659,8 +670,9 @@
           uint64_t semantics_value = GetIndexValue(semantics_inst);
           const analysis::Constant* constant =
               context()->get_constant_mgr()->GetConstant(
-                  semantics_type, {static_cast<uint32_t>(semantics_value) |
-                                   SpvMemorySemanticsOutputMemoryKHRMask});
+                  semantics_type,
+                  {static_cast<uint32_t>(semantics_value) |
+                   uint32_t(spv::MemorySemanticsMask::OutputMemoryKHR)});
           barrier->SetInOperand(2u, {context()
                                          ->get_constant_mgr()
                                          ->GetDefiningInstruction(constant)
@@ -680,15 +692,15 @@
     // * Workgroup ops (e.g. async_copy) have at most workgroup scope.
     if (spvOpcodeIsAtomicOp(inst->opcode())) {
       if (IsDeviceScope(inst->GetSingleWordInOperand(1))) {
-        inst->SetInOperand(1, {GetScopeConstant(SpvScopeQueueFamilyKHR)});
+        inst->SetInOperand(1, {GetScopeConstant(spv::Scope::QueueFamilyKHR)});
       }
-    } else if (inst->opcode() == SpvOpControlBarrier) {
+    } else if (inst->opcode() == spv::Op::OpControlBarrier) {
       if (IsDeviceScope(inst->GetSingleWordInOperand(1))) {
-        inst->SetInOperand(1, {GetScopeConstant(SpvScopeQueueFamilyKHR)});
+        inst->SetInOperand(1, {GetScopeConstant(spv::Scope::QueueFamilyKHR)});
       }
-    } else if (inst->opcode() == SpvOpMemoryBarrier) {
+    } else if (inst->opcode() == spv::Op::OpMemoryBarrier) {
       if (IsDeviceScope(inst->GetSingleWordInOperand(0))) {
-        inst->SetInOperand(0, {GetScopeConstant(SpvScopeQueueFamilyKHR)});
+        inst->SetInOperand(0, {GetScopeConstant(spv::Scope::QueueFamilyKHR)});
       }
     }
   });
@@ -704,14 +716,14 @@
   assert(type->width() == 32 || type->width() == 64);
   if (type->width() == 32) {
     if (type->IsSigned())
-      return static_cast<uint32_t>(constant->GetS32()) == SpvScopeDevice;
+      return static_cast<spv::Scope>(constant->GetS32()) == spv::Scope::Device;
     else
-      return static_cast<uint32_t>(constant->GetU32()) == SpvScopeDevice;
+      return static_cast<spv::Scope>(constant->GetU32()) == spv::Scope::Device;
   } else {
     if (type->IsSigned())
-      return static_cast<uint32_t>(constant->GetS64()) == SpvScopeDevice;
+      return static_cast<spv::Scope>(constant->GetS64()) == spv::Scope::Device;
     else
-      return static_cast<uint32_t>(constant->GetU64()) == SpvScopeDevice;
+      return static_cast<spv::Scope>(constant->GetU64()) == spv::Scope::Device;
   }
 
   assert(false);
@@ -758,9 +770,9 @@
 
 uint32_t UpgradeMemoryModel::MemoryAccessNumWords(uint32_t mask) {
   uint32_t result = 1;
-  if (mask & SpvMemoryAccessAlignedMask) ++result;
-  if (mask & SpvMemoryAccessMakePointerAvailableKHRMask) ++result;
-  if (mask & SpvMemoryAccessMakePointerVisibleKHRMask) ++result;
+  if (mask & uint32_t(spv::MemoryAccessMask::Aligned)) ++result;
+  if (mask & uint32_t(spv::MemoryAccessMask::MakePointerAvailableKHR)) ++result;
+  if (mask & uint32_t(spv::MemoryAccessMask::MakePointerVisibleKHR)) ++result;
   return result;
 }
 
diff --git a/source/opt/upgrade_memory_model.h b/source/opt/upgrade_memory_model.h
index f75304e..489436b 100644
--- a/source/opt/upgrade_memory_model.h
+++ b/source/opt/upgrade_memory_model.h
@@ -71,7 +71,7 @@
   void UpgradeAtomics();
 
   // Returns whether |id| is coherent and/or volatile.
-  std::tuple<bool, bool, SpvScope> GetInstructionAttributes(uint32_t id);
+  std::tuple<bool, bool, spv::Scope> GetInstructionAttributes(uint32_t id);
 
   // Traces |inst| to determine if it is coherent and/or volatile.
   // |indices| tracks the access chain indices seen so far.
@@ -84,7 +84,7 @@
   // match the index or |value| must be a maximum allowable value. The max
   // value allows any element to match.
   bool HasDecoration(const Instruction* inst, uint32_t value,
-                     SpvDecoration decoration);
+                     spv::Decoration decoration);
 
   // Returns whether |type_id| indexed via |indices| is coherent and/or
   // volatile.
@@ -108,7 +108,7 @@
                         bool is_volatile);
 
   // Returns the result id for a constant for |scope|.
-  uint32_t GetScopeConstant(SpvScope scope);
+  uint32_t GetScopeConstant(spv::Scope scope);
 
   // Returns the value of |index_inst|. |index_inst| must be an OpConstant of
   // integer type.g
@@ -127,7 +127,7 @@
   // scope.
   void UpgradeMemoryScope();
 
-  // Returns true if |scope_id| is SpvScopeDevice.
+  // Returns true if |scope_id| is spv::Scope::Device.
   bool IsDeviceScope(uint32_t scope_id);
 
   // Upgrades GLSL.std.450 modf and frexp. Both instructions are replaced with
diff --git a/source/opt/value_number_table.cpp b/source/opt/value_number_table.cpp
index 5271e3f..743dc52 100644
--- a/source/opt/value_number_table.cpp
+++ b/source/opt/value_number_table.cpp
@@ -57,9 +57,9 @@
   }
 
   switch (inst->opcode()) {
-    case SpvOpSampledImage:
-    case SpvOpImage:
-    case SpvOpVariable:
+    case spv::Op::OpSampledImage:
+    case spv::Op::OpImage:
+    case spv::Op::OpVariable:
       value = TakeNextValueNumber();
       id_to_value_[inst->result_id()] = value;
       return value;
@@ -82,7 +82,7 @@
   analysis::DecorationManager* dec_mgr = context()->get_decoration_mgr();
 
   // When we copy an object, the value numbers should be the same.
-  if (inst->opcode() == SpvOpCopyObject &&
+  if (inst->opcode() == spv::Op::OpCopyObject &&
       dec_mgr->HaveTheSameDecorations(inst->result_id(),
                                       inst->GetSingleWordInOperand(0))) {
     value = GetValueNumber(inst->GetSingleWordInOperand(0));
@@ -94,7 +94,7 @@
 
   // Phi nodes are a type of copy.  If all of the inputs have the same value
   // number, then we can assign the result of the phi the same value number.
-  if (inst->opcode() == SpvOpPhi && inst->NumInOperands() > 0 &&
+  if (inst->opcode() == spv::Op::OpPhi && inst->NumInOperands() > 0 &&
       dec_mgr->HaveTheSameDecorations(inst->result_id(),
                                       inst->GetSingleWordInOperand(0))) {
     value = GetValueNumber(inst->GetSingleWordInOperand(0));
@@ -226,7 +226,7 @@
   // instructions that are the same except for the result to hash to the
   // same value.
   std::u32string h;
-  h.push_back(inst.opcode());
+  h.push_back(uint32_t(inst.opcode()));
   h.push_back(inst.type_id());
   for (uint32_t i = 0; i < inst.NumInOperands(); ++i) {
     const auto& opnd = inst.GetInOperand(i);
diff --git a/source/opt/vector_dce.cpp b/source/opt/vector_dce.cpp
index 28d94a0..1e8d255 100644
--- a/source/opt/vector_dce.cpp
+++ b/source/opt/vector_dce.cpp
@@ -19,11 +19,9 @@
 namespace spvtools {
 namespace opt {
 namespace {
-
-const uint32_t kExtractCompositeIdInIdx = 0;
-const uint32_t kInsertObjectIdInIdx = 0;
-const uint32_t kInsertCompositeIdInIdx = 1;
-
+constexpr uint32_t kExtractCompositeIdInIdx = 0;
+constexpr uint32_t kInsertObjectIdInIdx = 0;
+constexpr uint32_t kInsertCompositeIdInIdx = 1;
 }  // namespace
 
 Pass::Status VectorDCE::Process() {
@@ -68,17 +66,17 @@
     Instruction* current_inst = current_item.instruction;
 
     switch (current_inst->opcode()) {
-      case SpvOpCompositeExtract:
+      case spv::Op::OpCompositeExtract:
         MarkExtractUseAsLive(current_inst, current_item.components,
                              live_components, &work_list);
         break;
-      case SpvOpCompositeInsert:
+      case spv::Op::OpCompositeInsert:
         MarkInsertUsesAsLive(current_item, live_components, &work_list);
         break;
-      case SpvOpVectorShuffle:
+      case spv::Op::OpVectorShuffle:
         MarkVectorShuffleUsesAsLive(current_item, live_components, &work_list);
         break;
-      case SpvOpCompositeConstruct:
+      case spv::Op::OpCompositeConstruct:
         MarkCompositeContructUsesAsLive(current_item, live_components,
                                         &work_list);
         break;
@@ -347,11 +345,11 @@
     }
 
     switch (current_inst->opcode()) {
-      case SpvOpCompositeInsert:
+      case spv::Op::OpCompositeInsert:
         modified |= RewriteInsertInstruction(
             current_inst, live_component->second, &dead_dbg_value);
         break;
-      case SpvOpCompositeConstruct:
+      case spv::Op::OpCompositeConstruct:
         // TODO: The members that are not live can be replaced by an undef
         // or constant. This will remove uses of those values, and possibly
         // create opportunities for ADCE.
diff --git a/source/opt/workaround1209.cpp b/source/opt/workaround1209.cpp
index d6e9d2c..0cf954a 100644
--- a/source/opt/workaround1209.cpp
+++ b/source/opt/workaround1209.cpp
@@ -43,13 +43,13 @@
         loop_merges.pop();
       }
 
-      if (bb->tail()->opcode() == SpvOpUnreachable) {
+      if (bb->tail()->opcode() == spv::Op::OpUnreachable) {
         if (!loop_merges.empty()) {
           // We found an OpUnreachable inside a loop.
           // Replace it with an unconditional branch to the loop merge.
           context()->KillInst(&*bb->tail());
           std::unique_ptr<Instruction> new_branch(
-              new Instruction(context(), SpvOpBranch, 0, 0,
+              new Instruction(context(), spv::Op::OpBranch, 0, 0,
                               {{spv_operand_type_t::SPV_OPERAND_TYPE_ID,
                                 {loop_merges.top()}}}));
           context()->AnalyzeDefUse(&*new_branch);
diff --git a/source/opt/wrap_opkill.cpp b/source/opt/wrap_opkill.cpp
index 51432a7..c0c6d62 100644
--- a/source/opt/wrap_opkill.cpp
+++ b/source/opt/wrap_opkill.cpp
@@ -28,7 +28,8 @@
     Function* func = context()->GetFunction(func_id);
     bool successful = func->WhileEachInst([this, &modified](Instruction* inst) {
       const auto opcode = inst->opcode();
-      if ((opcode == SpvOpKill) || (opcode == SpvOpTerminateInvocation)) {
+      if ((opcode == spv::Op::OpKill) ||
+          (opcode == spv::Op::OpTerminateInvocation)) {
         modified = true;
         if (!ReplaceWithFunctionCall(inst)) {
           return false;
@@ -56,8 +57,8 @@
 }
 
 bool WrapOpKill::ReplaceWithFunctionCall(Instruction* inst) {
-  assert((inst->opcode() == SpvOpKill ||
-          inst->opcode() == SpvOpTerminateInvocation) &&
+  assert((inst->opcode() == spv::Op::OpKill ||
+          inst->opcode() == spv::Op::OpTerminateInvocation) &&
          "|inst| must be an OpKill or OpTerminateInvocation instruction.");
   InstructionBuilder ir_builder(
       context(), inst,
@@ -76,14 +77,15 @@
   Instruction* return_inst = nullptr;
   uint32_t return_type_id = GetOwningFunctionsReturnType(inst);
   if (return_type_id != GetVoidTypeId()) {
-    Instruction* undef = ir_builder.AddNullaryOp(return_type_id, SpvOpUndef);
+    Instruction* undef =
+        ir_builder.AddNullaryOp(return_type_id, spv::Op::OpUndef);
     if (undef == nullptr) {
       return false;
     }
     return_inst =
-        ir_builder.AddUnaryOp(0, SpvOpReturnValue, undef->result_id());
+        ir_builder.AddUnaryOp(0, spv::Op::OpReturnValue, undef->result_id());
   } else {
-    return_inst = ir_builder.AddNullaryOp(0, SpvOpReturn);
+    return_inst = ir_builder.AddNullaryOp(0, spv::Op::OpReturn);
   }
 
   if (return_inst == nullptr) {
@@ -115,13 +117,13 @@
   return type_mgr->GetTypeInstruction(&func_type);
 }
 
-uint32_t WrapOpKill::GetKillingFuncId(SpvOp opcode) {
+uint32_t WrapOpKill::GetKillingFuncId(spv::Op opcode) {
   //  Parameterize by opcode
-  assert(opcode == SpvOpKill || opcode == SpvOpTerminateInvocation);
+  assert(opcode == spv::Op::OpKill || opcode == spv::Op::OpTerminateInvocation);
 
   std::unique_ptr<Function>* const killing_func =
-      (opcode == SpvOpKill) ? &opkill_function_
-                            : &opterminateinvocation_function_;
+      (opcode == spv::Op::OpKill) ? &opkill_function_
+                                  : &opterminateinvocation_function_;
 
   if (*killing_func != nullptr) {
     return (*killing_func)->result_id();
@@ -139,14 +141,14 @@
 
   // Generate the function start instruction
   std::unique_ptr<Instruction> func_start(new Instruction(
-      context(), SpvOpFunction, void_type_id, killing_func_id, {}));
+      context(), spv::Op::OpFunction, void_type_id, killing_func_id, {}));
   func_start->AddOperand({SPV_OPERAND_TYPE_FUNCTION_CONTROL, {0}});
   func_start->AddOperand({SPV_OPERAND_TYPE_ID, {GetVoidFunctionTypeId()}});
   (*killing_func).reset(new Function(std::move(func_start)));
 
   // Generate the function end instruction
   std::unique_ptr<Instruction> func_end(
-      new Instruction(context(), SpvOpFunctionEnd, 0, 0, {}));
+      new Instruction(context(), spv::Op::OpFunctionEnd, 0, 0, {}));
   (*killing_func)->SetFunctionEnd(std::move(func_end));
 
   // Create the one basic block for the function.
@@ -155,7 +157,7 @@
     return 0;
   }
   std::unique_ptr<Instruction> label_inst(
-      new Instruction(context(), SpvOpLabel, 0, lab_id, {}));
+      new Instruction(context(), spv::Op::OpLabel, 0, lab_id, {}));
   std::unique_ptr<BasicBlock> bb(new BasicBlock(std::move(label_inst)));
 
   // Add the OpKill to the basic block
diff --git a/source/opt/wrap_opkill.h b/source/opt/wrap_opkill.h
index 7e43ca6..c9eb888 100644
--- a/source/opt/wrap_opkill.h
+++ b/source/opt/wrap_opkill.h
@@ -53,7 +53,7 @@
   // Return the id of a function that has return type void, has no parameters,
   // and contains a single instruction, which is |opcode|, either OpKill or
   // OpTerminateInvocation.  Returns 0 if the function could not be generated.
-  uint32_t GetKillingFuncId(SpvOp opcode);
+  uint32_t GetKillingFuncId(spv::Op opcode);
 
   // Returns the id of the return type for the function that contains |inst|.
   // Returns 0 if |inst| is not in a function.
diff --git a/source/parsed_operand.cpp b/source/parsed_operand.cpp
index 5f8e94d..cc33f8b 100644
--- a/source/parsed_operand.cpp
+++ b/source/parsed_operand.cpp
@@ -24,6 +24,7 @@
 void EmitNumericLiteral(std::ostream* out, const spv_parsed_instruction_t& inst,
                         const spv_parsed_operand_t& operand) {
   if (operand.type != SPV_OPERAND_TYPE_LITERAL_INTEGER &&
+      operand.type != SPV_OPERAND_TYPE_LITERAL_FLOAT &&
       operand.type != SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER &&
       operand.type != SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER &&
       operand.type != SPV_OPERAND_TYPE_OPTIONAL_TYPED_LITERAL_INTEGER)
diff --git a/source/print.cpp b/source/print.cpp
index 6c94e2b..f36812e 100644
--- a/source/print.cpp
+++ b/source/print.cpp
@@ -17,7 +17,7 @@
 #if defined(SPIRV_ANDROID) || defined(SPIRV_LINUX) || defined(SPIRV_MAC) || \
     defined(SPIRV_IOS) || defined(SPIRV_TVOS) || defined(SPIRV_FREEBSD) ||  \
     defined(SPIRV_OPENBSD) || defined(SPIRV_EMSCRIPTEN) ||                  \
-    defined(SPIRV_FUCHSIA) || defined(SPIRV_GNU)
+    defined(SPIRV_FUCHSIA) || defined(SPIRV_GNU) || defined(SPIRV_QNX)
 namespace spvtools {
 
 clr::reset::operator const char*() { return "\x1b[0m"; }
diff --git a/source/reduce/CMakeLists.txt b/source/reduce/CMakeLists.txt
index 6fd8409..9ebe418 100644
--- a/source/reduce/CMakeLists.txt
+++ b/source/reduce/CMakeLists.txt
@@ -101,10 +101,7 @@
 spvtools_check_symbol_exports(SPIRV-Tools-reduce)
 
 if(ENABLE_SPIRV_TOOLS_INSTALL)
-  install(TARGETS SPIRV-Tools-reduce EXPORT SPIRV-Tools-reduceTargets
-    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
-    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
-    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
+  install(TARGETS SPIRV-Tools-reduce EXPORT SPIRV-Tools-reduceTargets)
   export(EXPORT SPIRV-Tools-reduceTargets FILE SPIRV-Tools-reduceTarget.cmake)
 
   spvtools_config_package_dir(SPIRV-Tools-reduce PACKAGE_DIR)
diff --git a/source/reduce/conditional_branch_to_simple_conditional_branch_opportunity_finder.cpp b/source/reduce/conditional_branch_to_simple_conditional_branch_opportunity_finder.cpp
index 2cd779a..93b51a1 100644
--- a/source/reduce/conditional_branch_to_simple_conditional_branch_opportunity_finder.cpp
+++ b/source/reduce/conditional_branch_to_simple_conditional_branch_opportunity_finder.cpp
@@ -36,9 +36,9 @@
     for (auto* function : GetTargetFunctions(context, target_function)) {
       // Consider every block in the function.
       for (auto& block : *function) {
-        // The terminator must be SpvOpBranchConditional.
+        // The terminator must be spv::Op::OpBranchConditional.
         opt::Instruction* terminator = block.terminator();
-        if (terminator->opcode() != SpvOpBranchConditional) {
+        if (terminator->opcode() != spv::Op::OpBranchConditional) {
           continue;
         }
 
diff --git a/source/reduce/merge_blocks_reduction_opportunity.cpp b/source/reduce/merge_blocks_reduction_opportunity.cpp
index a2c3b40..e626d60 100644
--- a/source/reduce/merge_blocks_reduction_opportunity.cpp
+++ b/source/reduce/merge_blocks_reduction_opportunity.cpp
@@ -23,7 +23,7 @@
 MergeBlocksReductionOpportunity::MergeBlocksReductionOpportunity(
     opt::IRContext* context, opt::Function* function, opt::BasicBlock* block) {
   // Precondition: the terminator has to be OpBranch.
-  assert(block->terminator()->opcode() == SpvOpBranch);
+  assert(block->terminator()->opcode() == spv::Op::OpBranch);
   context_ = context;
   function_ = function;
   // Get the successor block associated with the OpBranch.
diff --git a/source/reduce/operand_to_const_reduction_opportunity_finder.cpp b/source/reduce/operand_to_const_reduction_opportunity_finder.cpp
index eb7498a..c6196f3 100644
--- a/source/reduce/operand_to_const_reduction_opportunity_finder.cpp
+++ b/source/reduce/operand_to_const_reduction_opportunity_finder.cpp
@@ -50,7 +50,7 @@
                 // The argument is already a constant.
                 continue;
               }
-              if (def->opcode() == SpvOpFunction) {
+              if (def->opcode() == spv::Op::OpFunction) {
                 // The argument refers to a function, e.g. the function called
                 // by OpFunctionCall; avoid replacing this with a constant of
                 // the function's return type.
diff --git a/source/reduce/operand_to_undef_reduction_opportunity_finder.cpp b/source/reduce/operand_to_undef_reduction_opportunity_finder.cpp
index 06bf955..c7bc121 100644
--- a/source/reduce/operand_to_undef_reduction_opportunity_finder.cpp
+++ b/source/reduce/operand_to_undef_reduction_opportunity_finder.cpp
@@ -32,7 +32,7 @@
         auto type_id = inst.type_id();
         if (type_id) {
           auto type_id_def = context->get_def_use_mgr()->GetDef(type_id);
-          if (type_id_def->opcode() == SpvOpTypePointer) {
+          if (type_id_def->opcode() == spv::Op::OpTypePointer) {
             continue;
           }
         }
@@ -57,7 +57,7 @@
             }
 
             // Don't replace function operands with undef.
-            if (operand_id_def->opcode() == SpvOpFunction) {
+            if (operand_id_def->opcode() == spv::Op::OpFunction) {
               continue;
             }
 
@@ -68,7 +68,7 @@
                   context->get_def_use_mgr()->GetDef(operand_type_id);
 
               // Skip pointer operands.
-              if (operand_type_id_def->opcode() == SpvOpTypePointer) {
+              if (operand_type_id_def->opcode() == spv::Op::OpTypePointer) {
                 continue;
               }
 
diff --git a/source/reduce/reduction_util.cpp b/source/reduce/reduction_util.cpp
index 511f432..c9882d5 100644
--- a/source/reduce/reduction_util.cpp
+++ b/source/reduce/reduction_util.cpp
@@ -26,7 +26,7 @@
 uint32_t FindOrCreateGlobalVariable(opt::IRContext* context,
                                     uint32_t pointer_type_id) {
   for (auto& inst : context->module()->types_values()) {
-    if (inst.opcode() != SpvOpVariable) {
+    if (inst.opcode() != spv::Op::OpVariable) {
       continue;
     }
     if (inst.type_id() == pointer_type_id) {
@@ -35,7 +35,7 @@
   }
   const uint32_t variable_id = context->TakeNextId();
   auto variable_inst = MakeUnique<opt::Instruction>(
-      context, SpvOpVariable, pointer_type_id, variable_id,
+      context, spv::Op::OpVariable, pointer_type_id, variable_id,
       opt::Instruction::OperandList(
           {{SPV_OPERAND_TYPE_STORAGE_CLASS,
             {static_cast<uint32_t>(context->get_type_mgr()
@@ -53,7 +53,7 @@
   assert(context->get_type_mgr()
              ->GetType(pointer_type_id)
              ->AsPointer()
-             ->storage_class() == SpvStorageClassFunction);
+             ->storage_class() == spv::StorageClass::Function);
 
   // Go through the instructions in the function's first block until we find a
   // suitable variable, or go past all the variables.
@@ -62,7 +62,7 @@
     // We will either find a suitable variable, or find a non-variable
     // instruction; we won't exhaust all instructions.
     assert(iter != function->begin()->end());
-    if (iter->opcode() != SpvOpVariable) {
+    if (iter->opcode() != spv::Op::OpVariable) {
       // If we see a non-variable, we have gone through all the variables.
       break;
     }
@@ -74,16 +74,17 @@
   // function's entry block.
   const uint32_t variable_id = context->TakeNextId();
   auto variable_inst = MakeUnique<opt::Instruction>(
-      context, SpvOpVariable, pointer_type_id, variable_id,
+      context, spv::Op::OpVariable, pointer_type_id, variable_id,
       opt::Instruction::OperandList(
-          {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}));
+          {{SPV_OPERAND_TYPE_STORAGE_CLASS,
+            {uint32_t(spv::StorageClass::Function)}}}));
   iter->InsertBefore(std::move(variable_inst));
   return variable_id;
 }
 
 uint32_t FindOrCreateGlobalUndef(opt::IRContext* context, uint32_t type_id) {
   for (auto& inst : context->module()->types_values()) {
-    if (inst.opcode() != SpvOpUndef) {
+    if (inst.opcode() != spv::Op::OpUndef) {
       continue;
     }
     if (inst.type_id() == type_id) {
@@ -91,8 +92,9 @@
     }
   }
   const uint32_t undef_id = context->TakeNextId();
-  auto undef_inst = MakeUnique<opt::Instruction>(
-      context, SpvOpUndef, type_id, undef_id, opt::Instruction::OperandList());
+  auto undef_inst =
+      MakeUnique<opt::Instruction>(context, spv::Op::OpUndef, type_id, undef_id,
+                                   opt::Instruction::OperandList());
   assert(undef_id == undef_inst->result_id());
   context->module()->AddGlobalValue(std::move(undef_inst));
   return undef_id;
diff --git a/source/reduce/remove_selection_reduction_opportunity_finder.cpp b/source/reduce/remove_selection_reduction_opportunity_finder.cpp
index 74df1b8..6abadf2 100644
--- a/source/reduce/remove_selection_reduction_opportunity_finder.cpp
+++ b/source/reduce/remove_selection_reduction_opportunity_finder.cpp
@@ -36,7 +36,7 @@
   for (auto* function : GetTargetFunctions(context, target_function)) {
     for (auto& block : *function) {
       if (auto merge_instruction = block.GetMergeInst()) {
-        if (merge_instruction->opcode() == SpvOpLoopMerge) {
+        if (merge_instruction->opcode() == spv::Op::OpLoopMerge) {
           uint32_t merge_block_id =
               merge_instruction->GetSingleWordOperand(kMergeNodeIndex);
           uint32_t continue_block_id =
@@ -54,7 +54,7 @@
   for (auto& function : *context->module()) {
     for (auto& block : function) {
       if (auto merge_instruction = block.GetMergeInst()) {
-        if (merge_instruction->opcode() == SpvOpSelectionMerge) {
+        if (merge_instruction->opcode() == spv::Op::OpSelectionMerge) {
           if (CanOpSelectionMergeBeRemoved(
                   context, block, merge_instruction,
                   merge_and_continue_blocks_from_loops)) {
diff --git a/source/reduce/remove_struct_member_reduction_opportunity.cpp b/source/reduce/remove_struct_member_reduction_opportunity.cpp
index e72ed35..3309fd0 100644
--- a/source/reduce/remove_struct_member_reduction_opportunity.cpp
+++ b/source/reduce/remove_struct_member_reduction_opportunity.cpp
@@ -36,14 +36,14 @@
       struct_type_, [this, &decorations_to_kill](opt::Instruction* user,
                                                  uint32_t /*operand_index*/) {
         switch (user->opcode()) {
-          case SpvOpCompositeConstruct:
-          case SpvOpConstantComposite:
+          case spv::Op::OpCompositeConstruct:
+          case spv::Op::OpConstantComposite:
             // This use is constructing a composite of the struct type, so we
             // must remove the id that was provided for the member we are
             // removing.
             user->RemoveInOperand(member_index_);
             break;
-          case SpvOpMemberDecorate:
+          case spv::Op::OpMemberDecorate:
             // This use is decorating a member of the struct.
             if (user->GetSingleWordInOperand(1) == member_index_) {
               // The member we are removing is being decorated, so we record
@@ -78,8 +78,8 @@
     for (auto& block : function) {
       for (auto& inst : block) {
         switch (inst.opcode()) {
-          case SpvOpAccessChain:
-          case SpvOpInBoundsAccessChain: {
+          case spv::Op::OpAccessChain:
+          case spv::Op::OpInBoundsAccessChain: {
             // These access chain instructions take sequences of ids for
             // indexing, starting from input operand 1.
             auto composite_type_id =
@@ -90,8 +90,8 @@
                     ->GetSingleWordInOperand(1);
             AdjustAccessedIndices(composite_type_id, 1, false, context, &inst);
           } break;
-          case SpvOpPtrAccessChain:
-          case SpvOpInBoundsPtrAccessChain: {
+          case spv::Op::OpPtrAccessChain:
+          case spv::Op::OpInBoundsPtrAccessChain: {
             // These access chain instructions take sequences of ids for
             // indexing, starting from input operand 2.
             auto composite_type_id =
@@ -102,7 +102,7 @@
                     ->GetSingleWordInOperand(1);
             AdjustAccessedIndices(composite_type_id, 2, false, context, &inst);
           } break;
-          case SpvOpCompositeExtract: {
+          case spv::Op::OpCompositeExtract: {
             // OpCompositeExtract uses literals for indexing, starting at input
             // operand 1.
             auto composite_type_id =
@@ -111,7 +111,7 @@
                     ->type_id();
             AdjustAccessedIndices(composite_type_id, 1, true, context, &inst);
           } break;
-          case SpvOpCompositeInsert: {
+          case spv::Op::OpCompositeInsert: {
             // OpCompositeInsert uses literals for indexing, starting at input
             // operand 2.
             auto composite_type_id =
@@ -146,13 +146,13 @@
        i < composite_access_instruction->NumInOperands(); i++) {
     auto type_inst = context->get_def_use_mgr()->GetDef(next_type);
     switch (type_inst->opcode()) {
-      case SpvOpTypeArray:
-      case SpvOpTypeMatrix:
-      case SpvOpTypeRuntimeArray:
-      case SpvOpTypeVector:
+      case spv::Op::OpTypeArray:
+      case spv::Op::OpTypeMatrix:
+      case spv::Op::OpTypeRuntimeArray:
+      case spv::Op::OpTypeVector:
         next_type = type_inst->GetSingleWordInOperand(0);
         break;
-      case SpvOpTypeStruct: {
+      case spv::Op::OpTypeStruct: {
         // Struct types are special because (a) we may need to adjust the index
         // being used, if the struct type is the one from which we are removing
         // a member, and (b) the type encountered by following the current index
diff --git a/source/reduce/remove_unused_instruction_reduction_opportunity_finder.cpp b/source/reduce/remove_unused_instruction_reduction_opportunity_finder.cpp
index d7bb3a8..fbbeb34 100644
--- a/source/reduce/remove_unused_instruction_reduction_opportunity_finder.cpp
+++ b/source/reduce/remove_unused_instruction_reduction_opportunity_finder.cpp
@@ -103,8 +103,8 @@
           continue;
         }
         if (spvOpcodeIsBlockTerminator(inst.opcode()) ||
-            inst.opcode() == SpvOpSelectionMerge ||
-            inst.opcode() == SpvOpLoopMerge) {
+            inst.opcode() == spv::Op::OpSelectionMerge ||
+            inst.opcode() == spv::Op::OpLoopMerge) {
           // In this reduction pass we do not want to affect static
           // control flow.
           continue;
@@ -133,7 +133,7 @@
       &inst, [this](opt::Instruction* user, uint32_t use_index) -> bool {
         return (user->IsDecoration() &&
                 !IsIndependentlyRemovableDecoration(*user)) ||
-               (user->opcode() == SpvOpEntryPoint && use_index > 2);
+               (user->opcode() == spv::Op::OpEntryPoint && use_index > 2);
       });
 }
 
@@ -141,13 +141,13 @@
     IsIndependentlyRemovableDecoration(const opt::Instruction& inst) const {
   uint32_t decoration;
   switch (inst.opcode()) {
-    case SpvOpDecorate:
-    case SpvOpDecorateId:
-    case SpvOpDecorateString:
+    case spv::Op::OpDecorate:
+    case spv::Op::OpDecorateId:
+    case spv::Op::OpDecorateString:
       decoration = inst.GetSingleWordInOperand(1u);
       break;
-    case SpvOpMemberDecorate:
-    case SpvOpMemberDecorateString:
+    case spv::Op::OpMemberDecorate:
+    case spv::Op::OpMemberDecorateString:
       decoration = inst.GetSingleWordInOperand(2u);
       break;
     default:
@@ -160,12 +160,12 @@
   // not change the shader interface, will not make the shader invalid, will
   // actually be found in practice, etc.
 
-  switch (decoration) {
-    case SpvDecorationRelaxedPrecision:
-    case SpvDecorationNoSignedWrap:
-    case SpvDecorationNoContraction:
-    case SpvDecorationNoUnsignedWrap:
-    case SpvDecorationUserSemantic:
+  switch (spv::Decoration(decoration)) {
+    case spv::Decoration::RelaxedPrecision:
+    case spv::Decoration::NoSignedWrap:
+    case spv::Decoration::NoContraction:
+    case spv::Decoration::NoUnsignedWrap:
+    case spv::Decoration::UserSemantic:
       return true;
     default:
       return false;
diff --git a/source/reduce/remove_unused_struct_member_reduction_opportunity_finder.cpp b/source/reduce/remove_unused_struct_member_reduction_opportunity_finder.cpp
index cd0c4e4..db381e0 100644
--- a/source/reduce/remove_unused_struct_member_reduction_opportunity_finder.cpp
+++ b/source/reduce/remove_unused_struct_member_reduction_opportunity_finder.cpp
@@ -43,7 +43,7 @@
 
   // Consider every struct type in the module.
   for (auto& type_or_value : context->types_values()) {
-    if (type_or_value.opcode() != SpvOpTypeStruct) {
+    if (type_or_value.opcode() != spv::Op::OpTypeStruct) {
       continue;
     }
 
@@ -60,7 +60,7 @@
         &type_or_value,
         [&unused_members](opt::Instruction* user, uint32_t /*operand_index*/) {
           switch (user->opcode()) {
-            case SpvOpMemberName:
+            case spv::Op::OpMemberName:
               unused_members.erase(user->GetSingleWordInOperand(1));
               break;
             default:
@@ -91,8 +91,8 @@
           // The way the helper is invoked depends on whether the instruction
           // uses literal or id indices, and the offset into the instruction's
           // input operands from which index operands are provided.
-          case SpvOpAccessChain:
-          case SpvOpInBoundsAccessChain: {
+          case spv::Op::OpAccessChain:
+          case spv::Op::OpInBoundsAccessChain: {
             auto composite_type_id =
                 context->get_def_use_mgr()
                     ->GetDef(context->get_def_use_mgr()
@@ -102,8 +102,8 @@
             MarkAccessedMembersAsUsed(context, composite_type_id, 1, false,
                                       inst, &unused_member_to_structs);
           } break;
-          case SpvOpPtrAccessChain:
-          case SpvOpInBoundsPtrAccessChain: {
+          case spv::Op::OpPtrAccessChain:
+          case spv::Op::OpInBoundsPtrAccessChain: {
             auto composite_type_id =
                 context->get_def_use_mgr()
                     ->GetDef(context->get_def_use_mgr()
@@ -113,7 +113,7 @@
             MarkAccessedMembersAsUsed(context, composite_type_id, 2, false,
                                       inst, &unused_member_to_structs);
           } break;
-          case SpvOpCompositeExtract: {
+          case spv::Op::OpCompositeExtract: {
             auto composite_type_id =
                 context->get_def_use_mgr()
                     ->GetDef(inst.GetSingleWordInOperand(0))
@@ -121,7 +121,7 @@
             MarkAccessedMembersAsUsed(context, composite_type_id, 1, true, inst,
                                       &unused_member_to_structs);
           } break;
-          case SpvOpCompositeInsert: {
+          case spv::Op::OpCompositeInsert: {
             auto composite_type_id =
                 context->get_def_use_mgr()
                     ->GetDef(inst.GetSingleWordInOperand(1))
@@ -163,13 +163,13 @@
        i < composite_access_instruction.NumInOperands(); i++) {
     auto type_inst = context->get_def_use_mgr()->GetDef(next_type);
     switch (type_inst->opcode()) {
-      case SpvOpTypeArray:
-      case SpvOpTypeMatrix:
-      case SpvOpTypeRuntimeArray:
-      case SpvOpTypeVector:
+      case spv::Op::OpTypeArray:
+      case spv::Op::OpTypeMatrix:
+      case spv::Op::OpTypeRuntimeArray:
+      case spv::Op::OpTypeVector:
         next_type = type_inst->GetSingleWordInOperand(0);
         break;
-      case SpvOpTypeStruct: {
+      case spv::Op::OpTypeStruct: {
         uint32_t index_operand =
             composite_access_instruction.GetSingleWordInOperand(i);
         uint32_t member = literal_indices ? index_operand
diff --git a/source/reduce/simple_conditional_branch_to_branch_opportunity_finder.cpp b/source/reduce/simple_conditional_branch_to_branch_opportunity_finder.cpp
index d867c3a..9637f0f 100644
--- a/source/reduce/simple_conditional_branch_to_branch_opportunity_finder.cpp
+++ b/source/reduce/simple_conditional_branch_to_branch_opportunity_finder.cpp
@@ -29,15 +29,15 @@
   for (auto* function : GetTargetFunctions(context, target_function)) {
     // Consider every block in the function.
     for (auto& block : *function) {
-      // The terminator must be SpvOpBranchConditional.
+      // The terminator must be spv::Op::OpBranchConditional.
       opt::Instruction* terminator = block.terminator();
-      if (terminator->opcode() != SpvOpBranchConditional) {
+      if (terminator->opcode() != spv::Op::OpBranchConditional) {
         continue;
       }
       // It must not be a selection header, as these cannot be followed by
       // OpBranch.
       if (block.GetMergeInst() &&
-          block.GetMergeInst()->opcode() == SpvOpSelectionMerge) {
+          block.GetMergeInst()->opcode() == spv::Op::OpSelectionMerge) {
         continue;
       }
       // The conditional branch must be simplified.
diff --git a/source/reduce/simple_conditional_branch_to_branch_reduction_opportunity.cpp b/source/reduce/simple_conditional_branch_to_branch_reduction_opportunity.cpp
index a2be0c4..6d772b5 100644
--- a/source/reduce/simple_conditional_branch_to_branch_reduction_opportunity.cpp
+++ b/source/reduce/simple_conditional_branch_to_branch_reduction_opportunity.cpp
@@ -31,7 +31,8 @@
 }
 
 void SimpleConditionalBranchToBranchReductionOpportunity::Apply() {
-  assert(conditional_branch_instruction_->opcode() == SpvOpBranchConditional &&
+  assert(conditional_branch_instruction_->opcode() ==
+             spv::Op::OpBranchConditional &&
          "SimpleConditionalBranchToBranchReductionOpportunity: branch was not "
          "a conditional branch");
 
@@ -46,7 +47,7 @@
   // ->
   // OpBranch %block_id
 
-  conditional_branch_instruction_->SetOpcode(SpvOpBranch);
+  conditional_branch_instruction_->SetOpcode(spv::Op::OpBranch);
   conditional_branch_instruction_->ReplaceOperands(
       {{SPV_OPERAND_TYPE_ID,
         {conditional_branch_instruction_->GetSingleWordInOperand(
diff --git a/source/reduce/structured_construct_to_block_reduction_opportunity.cpp b/source/reduce/structured_construct_to_block_reduction_opportunity.cpp
index ed73841..cc5ffe3 100644
--- a/source/reduce/structured_construct_to_block_reduction_opportunity.cpp
+++ b/source/reduce/structured_construct_to_block_reduction_opportunity.cpp
@@ -55,7 +55,7 @@
 
   // The terminator for the header block is changed to be an unconditional
   // branch to the merge block.
-  header_block->terminator()->SetOpcode(SpvOpBranch);
+  header_block->terminator()->SetOpcode(spv::Op::OpBranch);
   header_block->terminator()->SetInOperands(
       {{SPV_OPERAND_TYPE_ID, {merge_block->id()}}});
 
diff --git a/source/reduce/structured_loop_to_selection_reduction_opportunity.cpp b/source/reduce/structured_loop_to_selection_reduction_opportunity.cpp
index 850af45..45b9528 100644
--- a/source/reduce/structured_loop_to_selection_reduction_opportunity.cpp
+++ b/source/reduce/structured_loop_to_selection_reduction_opportunity.cpp
@@ -129,12 +129,12 @@
   // Figure out which operands of the terminator need to be considered for
   // redirection.
   std::vector<uint32_t> operand_indices;
-  if (terminator->opcode() == SpvOpBranch) {
+  if (terminator->opcode() == spv::Op::OpBranch) {
     operand_indices = {0};
-  } else if (terminator->opcode() == SpvOpBranchConditional) {
+  } else if (terminator->opcode() == spv::Op::OpBranchConditional) {
     operand_indices = {1, 2};
   } else {
-    assert(terminator->opcode() == SpvOpSwitch);
+    assert(terminator->opcode() == spv::Op::OpSwitch);
     for (uint32_t label_index = 1; label_index < terminator->NumOperands();
          label_index += 2) {
       operand_indices.push_back(label_index);
@@ -179,18 +179,19 @@
   auto loop_merge_inst = loop_construct_header_->GetLoopMergeInst();
   auto const loop_merge_block_id =
       loop_merge_inst->GetSingleWordOperand(kMergeNodeIndex);
-  loop_merge_inst->SetOpcode(SpvOpSelectionMerge);
+  loop_merge_inst->SetOpcode(spv::Op::OpSelectionMerge);
   loop_merge_inst->ReplaceOperands(
       {{loop_merge_inst->GetOperand(kMergeNodeIndex).type,
         {loop_merge_block_id}},
-       {SPV_OPERAND_TYPE_SELECTION_CONTROL, {SpvSelectionControlMaskNone}}});
+       {SPV_OPERAND_TYPE_SELECTION_CONTROL,
+        {uint32_t(spv::SelectionControlMask::MaskNone)}}});
 
   // The loop header either finishes with OpBranch or OpBranchConditional.
   // The latter is fine for a selection.  In the former case we need to turn
   // it into OpBranchConditional.  We use "true" as the condition, and make
   // the "else" branch be the merge block.
   auto terminator = loop_construct_header_->terminator();
-  if (terminator->opcode() == SpvOpBranch) {
+  if (terminator->opcode() == spv::Op::OpBranch) {
     opt::analysis::Bool temp;
     const opt::analysis::Bool* bool_type =
         context_->get_type_mgr()->GetRegisteredType(&temp)->AsBool();
@@ -199,7 +200,7 @@
     auto true_const_result_id =
         const_mgr->GetDefiningInstruction(true_const)->result_id();
     auto original_branch_id = terminator->GetSingleWordOperand(0);
-    terminator->SetOpcode(SpvOpBranchConditional);
+    terminator->SetOpcode(spv::Op::OpBranchConditional);
     terminator->ReplaceOperands({{SPV_OPERAND_TYPE_ID, {true_const_result_id}},
                                  {SPV_OPERAND_TYPE_ID, {original_branch_id}},
                                  {SPV_OPERAND_TYPE_ID, {loop_merge_block_id}}});
@@ -215,7 +216,7 @@
   // Consider each instruction in the function.
   for (auto& block : *loop_construct_header_->GetParent()) {
     for (auto& def : block) {
-      if (def.opcode() == SpvOpVariable) {
+      if (def.opcode() == spv::Op::OpVariable) {
         // Variables are defined at the start of the function, and can be
         // accessed by all blocks, even by unreachable blocks that have no
         // dominators, so we do not need to worry about them.
@@ -233,11 +234,11 @@
         // access chain, in which case replace it with some (possibly fresh)
         // variable (as we cannot load from / store to OpUndef).
         if (!DefinitionSufficientlyDominatesUse(&def, use, index, block)) {
-          if (def.opcode() == SpvOpAccessChain) {
+          if (def.opcode() == spv::Op::OpAccessChain) {
             auto pointer_type =
                 context_->get_type_mgr()->GetType(def.type_id())->AsPointer();
             switch (pointer_type->storage_class()) {
-              case SpvStorageClassFunction:
+              case spv::StorageClass::Function:
                 use->SetOperand(
                     index, {FindOrCreateFunctionVariable(
                                context_, loop_construct_header_->GetParent(),
@@ -270,7 +271,7 @@
                                        opt::Instruction* use,
                                        uint32_t use_index,
                                        opt::BasicBlock& def_block) {
-  if (use->opcode() == SpvOpPhi) {
+  if (use->opcode() == spv::Op::OpPhi) {
     // A use in a phi doesn't need to be dominated by its definition, but the
     // associated parent block does need to be dominated by the definition.
     return context_->GetDominatorAnalysis(loop_construct_header_->GetParent())
diff --git a/source/spirv_target_env.cpp b/source/spirv_target_env.cpp
index 9a03817..585f8b6 100644
--- a/source/spirv_target_env.cpp
+++ b/source/spirv_target_env.cpp
@@ -17,6 +17,7 @@
 #include <cassert>
 #include <cstring>
 #include <string>
+#include <utility>
 
 #include "source/spirv_constant.h"
 #include "spirv-tools/libspirv.h"
diff --git a/source/table.h b/source/table.h
index 5adf04a..8097f13 100644
--- a/source/table.h
+++ b/source/table.h
@@ -21,9 +21,9 @@
 
 typedef struct spv_opcode_desc_t {
   const char* name;
-  const SpvOp opcode;
+  const spv::Op opcode;
   const uint32_t numCapabilities;
-  const SpvCapability* capabilities;
+  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.
@@ -48,7 +48,7 @@
   const char* name;
   const uint32_t value;
   const uint32_t numCapabilities;
-  const SpvCapability* capabilities;
+  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
@@ -73,7 +73,7 @@
   const char* name;
   const uint32_t ext_inst;
   const uint32_t numCapabilities;
-  const SpvCapability* capabilities;
+  const spv::Capability* capabilities;
   const spv_operand_type_t operandTypes[16];  // TODO: Smaller/larger?
 } spv_ext_inst_desc_t;
 
diff --git a/source/text.cpp b/source/text.cpp
index 90f69c5..737c223 100644
--- a/source/text.cpp
+++ b/source/text.cpp
@@ -227,7 +227,8 @@
 
       // Set the extended instruction type.
       // The import set id is the 3rd operand of OpExtInst.
-      if (pInst->opcode == SpvOpExtInst && pInst->words.size() == 4) {
+      if (spv::Op(pInst->opcode) == spv::Op::OpExtInst &&
+          pInst->words.size() == 4) {
         auto ext_inst_type = context->getExtInstTypeForId(pInst->words[3]);
         if (ext_inst_type == SPV_EXT_INST_TYPE_NONE) {
           return context->diagnostic()
@@ -279,7 +280,7 @@
       // The assembler accepts the symbolic name for the opcode, but without
       // the "Op" prefix.  For example, "IAdd" is accepted.  The number
       // of the opcode is emitted.
-      SpvOp opcode;
+      spv::Op opcode;
       if (grammar.lookupSpecConstantOpcode(textValue, &opcode)) {
         return context->diagnostic() << "Invalid " << spvOperandTypeStr(type)
                                      << " '" << textValue << "'.";
@@ -311,6 +312,17 @@
       }
     } break;
 
+    case SPV_OPERAND_TYPE_LITERAL_FLOAT: {
+      // The current operand is a 32-bit float.
+      // That's just how the grammar works.
+      spvtools::IdType expected_type = {
+          32, false, spvtools::IdTypeClass::kScalarFloatType};
+      if (auto error = context->binaryEncodeNumericLiteral(
+              textValue, error_code_for_literals, expected_type, pInst)) {
+        return error;
+      }
+    } break;
+
     case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_NUMBER:
       // This is a context-independent literal number which can be a 32-bit
       // number of floating point value.
@@ -327,8 +339,8 @@
       // The encoding for OpConstant, OpSpecConstant and OpSwitch all
       // depend on either their own result-id or the result-id of
       // one of their parameters.
-      if (SpvOpConstant == pInst->opcode ||
-          SpvOpSpecConstant == pInst->opcode) {
+      if (spv::Op::OpConstant == pInst->opcode ||
+          spv::Op::OpSpecConstant == pInst->opcode) {
         // The type of the literal is determined by the type Id of the
         // instruction.
         expected_type =
@@ -344,7 +356,7 @@
                  << "Type for " << opcode_name
                  << " must be a scalar floating point or integer type";
         }
-      } else if (pInst->opcode == SpvOpSwitch) {
+      } else if (pInst->opcode == spv::Op::OpSwitch) {
         // The type of the literal is the same as the type of the selector.
         expected_type = context->getTypeOfValueInstruction(pInst->words[1]);
         if (!spvtools::isScalarIntegral(expected_type)) {
@@ -375,7 +387,7 @@
       }
 
       // NOTE: Special case for extended instruction library import
-      if (SpvOpExtInstImport == pInst->opcode) {
+      if (spv::Op::OpExtInstImport == pInst->opcode) {
         const spv_ext_inst_type_t ext_inst_type =
             spvExtInstImportTypeGet(literal.str.c_str());
         if (SPV_EXT_INST_TYPE_NONE == ext_inst_type) {
@@ -401,7 +413,8 @@
     case SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS:
     case SPV_OPERAND_TYPE_SELECTION_CONTROL:
     case SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS:
-    case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS: {
+    case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS:
+    case SPV_OPERAND_TYPE_OPTIONAL_COOPERATIVE_MATRIX_OPERANDS: {
       uint32_t value;
       if (auto error = grammar.parseMaskOperand(type, textValue, &value)) {
         return context->diagnostic(error)
@@ -543,7 +556,8 @@
     std::string equal_sign;
     error = context->getWord(&equal_sign, &nextPosition);
     if ("=" != equal_sign)
-      return context->diagnostic() << "'=' expected after result id.";
+      return context->diagnostic() << "'=' expected after result id but found '"
+                                   << equal_sign << "'.";
 
     // The <opcode> after the '=' sign.
     context->setPosition(nextPosition);
@@ -688,7 +702,7 @@
                        uint32_t* header) {
   if (!header) return SPV_ERROR_INVALID_BINARY;
 
-  header[SPV_INDEX_MAGIC_NUMBER] = SpvMagicNumber;
+  header[SPV_INDEX_MAGIC_NUMBER] = spv::MagicNumber;
   header[SPV_INDEX_VERSION_NUMBER] = spvVersionForTargetEnv(env);
   header[SPV_INDEX_GENERATOR_NUMBER] =
       SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS_ASSEMBLER, kAssemblerVersion);
@@ -722,7 +736,7 @@
     // being parsed. A malformed input might feature such an operand *before*
     // the opcode is known. To guard against accessing an uninitialized opcode,
     // the instruction's opcode is initialized to a default value.
-    inst.opcode = SpvOpMax;
+    inst.opcode = spv::Op::Max;
 
     if (spvTextEncodeOpcode(grammar, &context, &inst)) {
       return SPV_ERROR_INVALID_TEXT;
diff --git a/source/text_handler.cpp b/source/text_handler.cpp
index 15c1741..35c4b83 100644
--- a/source/text_handler.cpp
+++ b/source/text_handler.cpp
@@ -323,12 +323,12 @@
                         << " has already been used to generate a type";
   }
 
-  if (pInst->opcode == SpvOpTypeInt) {
+  if (pInst->opcode == spv::Op::OpTypeInt) {
     if (pInst->words.size() != 4)
       return diagnostic() << "Invalid OpTypeInt instruction";
     types_[value] = {pInst->words[2], pInst->words[3] != 0,
                      IdTypeClass::kScalarIntegerType};
-  } else if (pInst->opcode == SpvOpTypeFloat) {
+  } else if (pInst->opcode == spv::Op::OpTypeFloat) {
     if (pInst->words.size() != 3)
       return diagnostic() << "Invalid OpTypeFloat instruction";
     types_[value] = {pInst->words[2], false, IdTypeClass::kScalarFloatType};
diff --git a/source/util/hex_float.h b/source/util/hex_float.h
index 06e3c57..98353a4 100644
--- a/source/util/hex_float.h
+++ b/source/util/hex_float.h
@@ -896,6 +896,47 @@
   return is;
 }
 
+namespace detail {
+
+// Returns a new value formed from 'value' by setting 'bit' that is the
+// 'n'th most significant bit (where 0 is the most significant bit).
+// If 'bit' is zero or 'n' is more than the number of bits in the integer
+// type, then return the original value.
+template <typename UINT_TYPE>
+UINT_TYPE set_nth_most_significant_bit(UINT_TYPE value, UINT_TYPE bit,
+                                       UINT_TYPE n) {
+  constexpr UINT_TYPE max_position = std::numeric_limits<UINT_TYPE>::digits - 1;
+  if ((bit != 0) && (n <= max_position)) {
+    return static_cast<UINT_TYPE>(value | (bit << (max_position - n)));
+  }
+  return value;
+}
+
+// Attempts to increment the argument.
+// If it does not overflow, then increments the argument and returns true.
+// If it would overflow, returns false.
+template <typename INT_TYPE>
+bool saturated_inc(INT_TYPE& value) {
+  if (value == std::numeric_limits<INT_TYPE>::max()) {
+    return false;
+  }
+  value++;
+  return true;
+}
+
+// Attempts to decrement the argument.
+// If it does not underflow, then decrements the argument and returns true.
+// If it would overflow, returns false.
+template <typename INT_TYPE>
+bool saturated_dec(INT_TYPE& value) {
+  if (value == std::numeric_limits<INT_TYPE>::min()) {
+    return false;
+  }
+  value--;
+  return true;
+}
+}  // namespace detail
+
 // Reads a HexFloat from the given stream.
 // If the float is not encoded as a hex-float then it will be parsed
 // as a regular float.
@@ -997,13 +1038,16 @@
         if (bits_written) {
           // If we are here the bits represented belong in the fractional
           // part of the float, and we have to adjust the exponent accordingly.
-          fraction = static_cast<uint_type>(
-              fraction |
-              static_cast<uint_type>(
-                  write_bit << (HF::top_bit_left_shift - fraction_index++)));
-          // TODO(dneto): Avoid overflow. Testing would require
-          // parameterization.
-          exponent = static_cast<int_type>(exponent + 1);
+          fraction = detail::set_nth_most_significant_bit(fraction, write_bit,
+                                                          fraction_index);
+          // Increment the fraction index. If the input has bizarrely many
+          // significant digits, then silently drop them.
+          detail::saturated_inc(fraction_index);
+          if (!detail::saturated_inc(exponent)) {
+            // Overflow failure
+            is.setstate(std::ios::failbit);
+            return is;
+          }
         }
         // Since this updated after setting fraction bits, this effectively
         // drops the leading 1 bit.
@@ -1034,14 +1078,17 @@
           // Handle modifying the exponent here this way we can handle
           // an arbitrary number of hex values without overflowing our
           // integer.
-          // TODO(dneto): Handle underflow. Testing would require extra
-          // parameterization.
-          exponent = static_cast<int_type>(exponent - 1);
+          if (!detail::saturated_dec(exponent)) {
+            // Overflow failure
+            is.setstate(std::ios::failbit);
+            return is;
+          }
         } else {
-          fraction = static_cast<uint_type>(
-              fraction |
-              static_cast<uint_type>(
-                  write_bit << (HF::top_bit_left_shift - fraction_index++)));
+          fraction = detail::set_nth_most_significant_bit(fraction, write_bit,
+                                                          fraction_index);
+          // Increment the fraction index. If the input has bizarrely many
+          // significant digits, then silently drop them.
+          detail::saturated_inc(fraction_index);
         }
       }
     } else {
diff --git a/source/util/small_vector.h b/source/util/small_vector.h
index 648a348..1351475 100644
--- a/source/util/small_vector.h
+++ b/source/util/small_vector.h
@@ -15,7 +15,9 @@
 #ifndef SOURCE_UTIL_SMALL_VECTOR_H_
 #define SOURCE_UTIL_SMALL_VECTOR_H_
 
+#include <array>
 #include <cassert>
+#include <cstddef>
 #include <iostream>
 #include <memory>
 #include <utility>
@@ -461,14 +463,18 @@
   // The number of elements in |small_data_| that have been constructed.
   size_t size_;
 
-  // The pointed used to access the array of elements when the number of
-  // elements is small.
-  T* small_data_;
+  // A type with the same alignment and size as T, but will is POD.
+  struct alignas(T) PodType {
+    std::array<int8_t, sizeof(T)> data;
+  };
 
   // The actual data used to store the array elements.  It must never be used
   // directly, but must only be accessed through |small_data_|.
-  typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type
-      buffer[small_size];
+  PodType buffer[small_size];
+
+  // The pointed used to access the array of elements when the number of
+  // elements is small.
+  T* small_data_;
 
   // A pointer to a vector that is used to store the elements of the vector when
   // this size exceeds |small_size|.  If |large_data_| is nullptr, then the data
diff --git a/source/val/basic_block.cpp b/source/val/basic_block.cpp
index da05db3..9a358fc 100644
--- a/source/val/basic_block.cpp
+++ b/source/val/basic_block.cpp
@@ -15,7 +15,6 @@
 #include "source/val/basic_block.h"
 
 #include <algorithm>
-#include <utility>
 #include <vector>
 
 namespace spvtools {
diff --git a/source/val/construct.cpp b/source/val/construct.cpp
index 52e61d5..10af155 100644
--- a/source/val/construct.cpp
+++ b/source/val/construct.cpp
@@ -16,7 +16,6 @@
 
 #include <cassert>
 #include <cstddef>
-#include <unordered_set>
 
 #include "source/val/function.h"
 #include "source/val/validation_state.h"
@@ -164,10 +163,12 @@
     //  ii. The immediate dominator of |block|.
     auto NextBlock = [](const BasicBlock* block) -> const BasicBlock* {
       for (auto& use : block->label()->uses()) {
-        if ((use.first->opcode() == SpvOpLoopMerge ||
-             use.first->opcode() == SpvOpSelectionMerge) &&
+        if ((use.first->opcode() == spv::Op::OpLoopMerge ||
+             use.first->opcode() == spv::Op::OpSelectionMerge) &&
             use.second == 1 &&
-            use.first->block()->structurally_dominates(*block)) {
+            use.first->block()->structurally_dominates(*block) &&
+            // A header likely declared itself as its merge.
+            use.first->block() != block) {
           return use.first->block();
         }
       }
@@ -181,10 +182,10 @@
       auto terminator = block->terminator();
       auto index = terminator - &_.ordered_instructions()[0];
       auto merge_inst = &_.ordered_instructions()[index - 1];
-      if (merge_inst->opcode() == SpvOpLoopMerge ||
-          (header->terminator()->opcode() != SpvOpSwitch &&
-           merge_inst->opcode() == SpvOpSelectionMerge &&
-           terminator->opcode() == SpvOpSwitch)) {
+      if (merge_inst->opcode() == spv::Op::OpLoopMerge ||
+          (header->terminator()->opcode() != spv::Op::OpSwitch &&
+           merge_inst->opcode() == spv::Op::OpSelectionMerge &&
+           terminator->opcode() == spv::Op::OpSwitch)) {
         auto merge_target = merge_inst->GetOperandAs<uint32_t>(0u);
         auto merge_block = merge_inst->function()->GetBlock(merge_target).first;
         if (merge_block->structurally_dominates(*header)) {
@@ -192,22 +193,22 @@
           continue;
         }
 
-        if ((!seen_switch || merge_inst->opcode() == SpvOpLoopMerge) &&
+        if ((!seen_switch || merge_inst->opcode() == spv::Op::OpLoopMerge) &&
             dest->id() == merge_target) {
           return true;
-        } else if (merge_inst->opcode() == SpvOpLoopMerge) {
+        } else if (merge_inst->opcode() == spv::Op::OpLoopMerge) {
           auto continue_target = merge_inst->GetOperandAs<uint32_t>(1u);
           if (dest->id() == continue_target) {
             return true;
           }
         }
 
-        if (terminator->opcode() == SpvOpSwitch) {
+        if (terminator->opcode() == spv::Op::OpSwitch) {
           seen_switch = true;
         }
 
         // Hit an enclosing loop and didn't break or continue.
-        if (merge_inst->opcode() == SpvOpLoopMerge) return false;
+        if (merge_inst->opcode() == spv::Op::OpLoopMerge) return false;
       }
 
       block = NextBlock(block);
diff --git a/source/val/decoration.h b/source/val/decoration.h
index 4f53f20..384cc57 100644
--- a/source/val/decoration.h
+++ b/source/val/decoration.h
@@ -39,33 +39,33 @@
 //
 // Example 1: Decoration for an object<id> with no parameters:
 // OpDecorate %obj Flat
-//            dec_type_ = SpvDecorationFlat
+//            dec_type_ = spv::Decoration::Flat
 //              params_ = empty vector
 // struct_member_index_ = kInvalidMember
 //
 // Example 2: Decoration for an object<id> with two parameters:
 // OpDecorate %obj LinkageAttributes "link" Import
-//            dec_type_ = SpvDecorationLinkageAttributes
+//            dec_type_ = spv::Decoration::LinkageAttributes
 //              params_ = vector { link, Import }
 // struct_member_index_ = kInvalidMember
 //
 // Example 3: Decoration for a member of a structure with one parameter:
 // OpMemberDecorate %struct 2 Offset 2
-//            dec_type_ = SpvDecorationOffset
+//            dec_type_ = spv::Decoration::Offset
 //              params_ = vector { 2 }
 // struct_member_index_ = 2
 //
 class Decoration {
  public:
   enum { kInvalidMember = -1 };
-  Decoration(SpvDecoration t,
+  Decoration(spv::Decoration t,
              const std::vector<uint32_t>& parameters = std::vector<uint32_t>(),
              uint32_t member_index = kInvalidMember)
       : dec_type_(t), params_(parameters), struct_member_index_(member_index) {}
 
   void set_struct_member_index(uint32_t index) { struct_member_index_ = index; }
   int struct_member_index() const { return struct_member_index_; }
-  SpvDecoration dec_type() const { return dec_type_; }
+  spv::Decoration dec_type() const { return dec_type_; }
   std::vector<uint32_t>& params() { return params_; }
   const std::vector<uint32_t>& params() const { return params_; }
 
@@ -84,7 +84,7 @@
   }
 
  private:
-  SpvDecoration dec_type_;
+  spv::Decoration dec_type_;
   std::vector<uint32_t> params_;
 
   // If the decoration applies to a member of a structure type, then the index
diff --git a/source/val/function.cpp b/source/val/function.cpp
index fc7ccd0..290574b 100644
--- a/source/val/function.cpp
+++ b/source/val/function.cpp
@@ -18,7 +18,6 @@
 #include <cassert>
 #include <sstream>
 #include <unordered_map>
-#include <unordered_set>
 #include <utility>
 
 #include "source/cfa.h"
@@ -33,7 +32,7 @@
 static const uint32_t kInvalidId = 0x400000;
 
 Function::Function(uint32_t function_id, uint32_t result_type_id,
-                   SpvFunctionControlMask function_control,
+                   spv::FunctionControlMask function_control,
                    uint32_t function_type_id)
     : id_(function_id),
       function_type_id_(function_type_id),
@@ -371,10 +370,10 @@
   return block_depth_[bb];
 }
 
-void Function::RegisterExecutionModelLimitation(SpvExecutionModel model,
+void Function::RegisterExecutionModelLimitation(spv::ExecutionModel model,
                                                 const std::string& message) {
   execution_model_limitations_.push_back(
-      [model, message](SpvExecutionModel in_model, std::string* out_message) {
+      [model, message](spv::ExecutionModel in_model, std::string* out_message) {
         if (model != in_model) {
           if (out_message) {
             *out_message = message;
@@ -385,7 +384,7 @@
       });
 }
 
-bool Function::IsCompatibleWithExecutionModel(SpvExecutionModel model,
+bool Function::IsCompatibleWithExecutionModel(spv::ExecutionModel model,
                                               std::string* reason) const {
   bool return_value = true;
   std::stringstream ss_reason;
diff --git a/source/val/function.h b/source/val/function.h
index 126b1dc..4811794 100644
--- a/source/val/function.h
+++ b/source/val/function.h
@@ -55,7 +55,8 @@
 class Function {
  public:
   Function(uint32_t id, uint32_t result_type_id,
-           SpvFunctionControlMask function_control, uint32_t function_type_id);
+           spv::FunctionControlMask function_control,
+           uint32_t function_type_id);
 
   /// Registers a function parameter in the current function
   /// @return Returns SPV_SUCCESS if the call was successful
@@ -80,7 +81,8 @@
   ///
   /// @return Returns SPV_SUCCESS if the call was successful
   spv_result_t RegisterBlockVariable(uint32_t type_id, uint32_t id,
-                                     SpvStorageClass storage, uint32_t init_id);
+                                     spv::StorageClass storage,
+                                     uint32_t init_id);
 
   /// Registers a loop merge construct in the function
   ///
@@ -205,12 +207,12 @@
 
   /// Registers execution model limitation such as "Feature X is only available
   /// with Execution Model Y".
-  void RegisterExecutionModelLimitation(SpvExecutionModel model,
+  void RegisterExecutionModelLimitation(spv::ExecutionModel model,
                                         const std::string& message);
 
   /// Registers execution model limitation with an |is_compatible| functor.
   void RegisterExecutionModelLimitation(
-      std::function<bool(SpvExecutionModel, std::string*)> is_compatible) {
+      std::function<bool(spv::ExecutionModel, std::string*)> is_compatible) {
     execution_model_limitations_.push_back(is_compatible);
   }
 
@@ -227,7 +229,7 @@
   /// Returns true if the given execution model passes the limitations stored in
   /// execution_model_limitations_. Returns false otherwise and fills optional
   /// |reason| parameter.
-  bool IsCompatibleWithExecutionModel(SpvExecutionModel model,
+  bool IsCompatibleWithExecutionModel(spv::ExecutionModel model,
                                       std::string* reason = nullptr) const;
 
   // Inserts id to the set of functions called from this function.
@@ -286,7 +288,7 @@
   uint32_t result_type_id_;
 
   /// The control fo the function
-  SpvFunctionControlMask function_control_;
+  spv::FunctionControlMask function_control_;
 
   /// The type of declaration of each function
   FunctionDecl declaration_type_;
@@ -381,7 +383,7 @@
   /// function. The functor stored in the list return true if execution model
   /// is compatible, false otherwise. If the functor returns false, it can also
   /// optionally fill the string parameter with the reason for incompatibility.
-  std::list<std::function<bool(SpvExecutionModel, std::string*)>>
+  std::list<std::function<bool(spv::ExecutionModel, std::string*)>>
       execution_model_limitations_;
 
   /// Stores limitations imposed by instructions used within the function.
diff --git a/source/val/instruction.h b/source/val/instruction.h
index 6d1f9f4..c524bd3 100644
--- a/source/val/instruction.h
+++ b/source/val/instruction.h
@@ -42,7 +42,7 @@
 
   uint32_t id() const { return inst_.result_id; }
   uint32_t type_id() const { return inst_.type_id; }
-  SpvOp opcode() const { return static_cast<SpvOp>(inst_.opcode); }
+  spv::Op opcode() const { return static_cast<spv::Op>(inst_.opcode); }
 
   /// Returns the Function where the instruction was defined. nullptr if it was
   /// defined outside of a Function
@@ -87,13 +87,13 @@
   }
 
   bool IsNonSemantic() const {
-    return opcode() == SpvOp::SpvOpExtInst &&
+    return opcode() == spv::Op::OpExtInst &&
            spvExtInstIsNonSemantic(inst_.ext_inst_type);
   }
 
   /// True if this is an OpExtInst for debug info extension.
   bool IsDebugInfo() const {
-    return opcode() == SpvOp::SpvOpExtInst &&
+    return opcode() == spv::Op::OpExtInst &&
            spvExtInstIsDebugInfo(inst_.ext_inst_type);
   }
 
diff --git a/source/val/validate.cpp b/source/val/validate.cpp
index efb9225..a5f320b 100644
--- a/source/val/validate.cpp
+++ b/source/val/validate.cpp
@@ -14,13 +14,9 @@
 
 #include "source/val/validate.h"
 
-#include <algorithm>
-#include <cassert>
-#include <cstdio>
 #include <functional>
 #include <iterator>
 #include <memory>
-#include <sstream>
 #include <string>
 #include <vector>
 
@@ -28,15 +24,11 @@
 #include "source/diagnostic.h"
 #include "source/enum_string_mapping.h"
 #include "source/extensions.h"
-#include "source/instruction.h"
 #include "source/opcode.h"
-#include "source/operand.h"
 #include "source/spirv_constant.h"
 #include "source/spirv_endian.h"
 #include "source/spirv_target_env.h"
-#include "source/spirv_validator_options.h"
 #include "source/val/construct.h"
-#include "source/val/function.h"
 #include "source/val/instruction.h"
 #include "source/val/validation_state.h"
 #include "spirv-tools/libspirv.h"
@@ -66,15 +58,15 @@
 
 // Parses the beginning of the module searching for OpExtension instructions.
 // Registers extensions if recognized. Returns SPV_REQUESTED_TERMINATION
-// once an instruction which is not SpvOpCapability and SpvOpExtension is
-// encountered. According to the SPIR-V spec extensions are declared after
-// capabilities and before everything else.
+// once an instruction which is not spv::Op::OpCapability and
+// spv::Op::OpExtension is encountered. According to the SPIR-V spec extensions
+// are declared after capabilities and before everything else.
 spv_result_t ProcessExtensions(void* user_data,
                                const spv_parsed_instruction_t* inst) {
-  const SpvOp opcode = static_cast<SpvOp>(inst->opcode);
-  if (opcode == SpvOpCapability) return SPV_SUCCESS;
+  const spv::Op opcode = static_cast<spv::Op>(inst->opcode);
+  if (opcode == spv::Op::OpCapability) return SPV_SUCCESS;
 
-  if (opcode == SpvOpExtension) {
+  if (opcode == spv::Op::OpExtension) {
     ValidationState_t& _ = *(reinterpret_cast<ValidationState_t*>(user_data));
     RegisterExtension(_, inst);
     return SPV_SUCCESS;
@@ -123,7 +115,7 @@
   _.ComputeFunctionToEntryPointMapping();
   _.ComputeRecursiveEntryPoints();
 
-  if (_.entry_points().empty() && !_.HasCapability(SpvCapabilityLinkage)) {
+  if (_.entry_points().empty() && !_.HasCapability(spv::Capability::Linkage)) {
     return _.diag(SPV_ERROR_INVALID_BINARY, nullptr)
            << "No OpEntryPoint instruction was found. This is only allowed if "
               "the Linkage capability is being used.";
@@ -218,9 +210,9 @@
       // able to, briefly, de-const the instruction.
       Instruction* inst = const_cast<Instruction*>(&instruction);
 
-      if (inst->opcode() == SpvOpEntryPoint) {
+      if (inst->opcode() == spv::Op::OpEntryPoint) {
         const auto entry_point = inst->GetOperandAs<uint32_t>(1);
-        const auto execution_model = inst->GetOperandAs<SpvExecutionModel>(0);
+        const auto execution_model = inst->GetOperandAs<spv::ExecutionModel>(0);
         const std::string desc_name = inst->GetOperandAs<std::string>(2);
 
         ValidationState_t::EntryPointDescription desc;
@@ -236,7 +228,7 @@
         if (visited_entry_points.size() > 0) {
           for (const Instruction* check_inst : visited_entry_points) {
             const auto check_execution_model =
-                check_inst->GetOperandAs<SpvExecutionModel>(0);
+                check_inst->GetOperandAs<spv::ExecutionModel>(0);
             const std::string check_name =
                 check_inst->GetOperandAs<std::string>(2);
 
@@ -250,12 +242,12 @@
         }
         visited_entry_points.push_back(inst);
 
-        has_mask_task_nv |= (execution_model == SpvExecutionModelTaskNV ||
-                             execution_model == SpvExecutionModelMeshNV);
-        has_mask_task_ext |= (execution_model == SpvExecutionModelTaskEXT ||
-                              execution_model == SpvExecutionModelMeshEXT);
+        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() == SpvOpFunctionCall) {
+      if (inst->opcode() == spv::Op::OpFunctionCall) {
         if (!vstate->in_function_body()) {
           return vstate->diag(SPV_ERROR_INVALID_LAYOUT, &instruction)
                  << "A FunctionCall must happen within a function body.";
@@ -286,7 +278,7 @@
     {
       Instruction* inst = const_cast<Instruction*>(&instruction);
       vstate->RegisterInstruction(inst);
-      if (inst->opcode() == SpvOpTypeForwardPointer) {
+      if (inst->opcode() == spv::Op::OpTypeForwardPointer) {
         vstate->RegisterForwardPointer(inst->GetOperandAs<uint32_t>(0));
       }
     }
@@ -300,7 +292,7 @@
     return vstate->diag(SPV_ERROR_INVALID_LAYOUT, nullptr)
            << "Missing OpFunctionEnd at end of module.";
 
-  if (vstate->HasCapability(SpvCapabilityBindlessTextureNV) &&
+  if (vstate->HasCapability(spv::Capability::BindlessTextureNV) &&
       !vstate->has_samplerimage_variable_address_mode_specified())
     return vstate->diag(SPV_ERROR_INVALID_LAYOUT, nullptr)
            << "Missing required OpSamplerImageAddressingModeNV instruction.";
@@ -365,11 +357,13 @@
     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 = MeshShadingPass(*vstate, &instruction)) return error;
   }
 
-  // Validate the preconditions involving adjacent instructions. e.g. SpvOpPhi
-  // must only be preceded by SpvOpLabel, SpvOpPhi, or SpvOpLine.
+  // Validate the preconditions involving adjacent instructions. e.g.
+  // spv::Op::OpPhi must only be preceded by spv::Op::OpLabel, spv::Op::OpPhi,
+  // or spv::Op::OpLine.
   if (auto error = ValidateAdjacency(*vstate)) return error;
 
   if (auto error = ValidateEntryPoints(*vstate)) return error;
@@ -387,6 +381,8 @@
   for (const auto& inst : vstate->ordered_instructions()) {
     if (auto error = ValidateExecutionLimitations(*vstate, &inst)) return error;
     if (auto error = ValidateSmallTypeUses(*vstate, &inst)) return error;
+    if (auto error = ValidateQCOMImageProcessingTextureUsages(*vstate, &inst))
+      return error;
   }
 
   return SPV_SUCCESS;
diff --git a/source/val/validate.h b/source/val/validate.h
index 4b953ba..6b7d7cd 100644
--- a/source/val/validate.h
+++ b/source/val/validate.h
@@ -31,11 +31,6 @@
 class BasicBlock;
 class Instruction;
 
-/// A function that returns a vector of BasicBlocks given a BasicBlock. Used to
-/// get the successor and predecessor nodes of a CFG block
-using get_blocks_func =
-    std::function<const std::vector<BasicBlock*>*(const BasicBlock*)>;
-
 /// @brief Performs the Control Flow Graph checks
 ///
 /// @param[in] _ the validation state of the module
@@ -69,8 +64,8 @@
 /// instructions.
 ///
 /// This function will iterate over all instructions and check for any required
-/// predecessor and/or successor instructions. e.g. SpvOpPhi must only be
-/// preceded by SpvOpLabel, SpvOpPhi, or SpvOpLine.
+/// predecessor and/or successor instructions. e.g. spv::Op::OpPhi must only be
+/// preceded by spv::Op::OpLabel, spv::Op::OpPhi, or spv::Op::OpLine.
 ///
 /// @param[in] _ the validation state of the module
 ///
@@ -203,6 +198,9 @@
 /// Validates correctness of ray tracing instructions.
 spv_result_t RayTracingPass(ValidationState_t& _, const Instruction* inst);
 
+/// Validates correctness of shader execution reorder instructions.
+spv_result_t RayReorderNVPass(ValidationState_t& _, const Instruction* inst);
+
 /// Validates correctness of mesh shading instructions.
 spv_result_t MeshShadingPass(ValidationState_t& _, const Instruction* inst);
 
@@ -222,6 +220,14 @@
 spv_result_t ValidateSmallTypeUses(ValidationState_t& _,
                                    const Instruction* inst);
 
+/// Validates restricted uses of QCOM decorated textures
+///
+/// The textures that are decorated with some of QCOM image processing
+/// decorations must be used in the specified QCOM image processing built-in
+/// functions and not used in any other image functions.
+spv_result_t ValidateQCOMImageProcessingTextureUsages(ValidationState_t& _,
+                                                      const Instruction* inst);
+
 /// @brief Validate the ID's within a SPIR-V binary
 ///
 /// @param[in] pInstructions array of instructions
diff --git a/source/val/validate_adjacency.cpp b/source/val/validate_adjacency.cpp
index 8e6c373..7e371c2 100644
--- a/source/val/validate_adjacency.cpp
+++ b/source/val/validate_adjacency.cpp
@@ -15,13 +15,10 @@
 // Validates correctness of the intra-block preconditions of SPIR-V
 // instructions.
 
-#include "source/val/validate.h"
-
 #include <string>
 
-#include "source/diagnostic.h"
-#include "source/opcode.h"
 #include "source/val/instruction.h"
+#include "source/val/validate.h"
 #include "source/val/validation_state.h"
 
 namespace spvtools {
@@ -46,15 +43,15 @@
   for (size_t i = 0; i < instructions.size(); ++i) {
     const auto& inst = instructions[i];
     switch (inst.opcode()) {
-      case SpvOpFunction:
-      case SpvOpFunctionParameter:
+      case spv::Op::OpFunction:
+      case spv::Op::OpFunctionParameter:
         adjacency_status = IN_NEW_FUNCTION;
         break;
-      case SpvOpLabel:
+      case spv::Op::OpLabel:
         adjacency_status =
             adjacency_status == IN_NEW_FUNCTION ? IN_ENTRY_BLOCK : PHI_VALID;
         break;
-      case SpvOpExtInst:
+      case spv::Op::OpExtInst:
         // If it is a debug info instruction, we do not change the status to
         // allow debug info instructions before OpVariable in a function.
         // TODO(https://gitlab.khronos.org/spirv/SPIR-V/issues/533): We need
@@ -67,7 +64,7 @@
           adjacency_status = PHI_AND_VAR_INVALID;
         }
         break;
-      case SpvOpPhi:
+      case spv::Op::OpPhi:
         if (adjacency_status != PHI_VALID) {
           return _.diag(SPV_ERROR_INVALID_DATA, &inst)
                  << "OpPhi must appear within a non-entry block before all "
@@ -75,15 +72,15 @@
                  << "(except for OpLine, which can be mixed with OpPhi).";
         }
         break;
-      case SpvOpLine:
-      case SpvOpNoLine:
+      case spv::Op::OpLine:
+      case spv::Op::OpNoLine:
         break;
-      case SpvOpLoopMerge:
+      case spv::Op::OpLoopMerge:
         adjacency_status = PHI_AND_VAR_INVALID;
         if (i != (instructions.size() - 1)) {
           switch (instructions[i + 1].opcode()) {
-            case SpvOpBranch:
-            case SpvOpBranchConditional:
+            case spv::Op::OpBranch:
+            case spv::Op::OpBranchConditional:
               break;
             default:
               return _.diag(SPV_ERROR_INVALID_DATA, &inst)
@@ -94,12 +91,12 @@
           }
         }
         break;
-      case SpvOpSelectionMerge:
+      case spv::Op::OpSelectionMerge:
         adjacency_status = PHI_AND_VAR_INVALID;
         if (i != (instructions.size() - 1)) {
           switch (instructions[i + 1].opcode()) {
-            case SpvOpBranchConditional:
-            case SpvOpSwitch:
+            case spv::Op::OpBranchConditional:
+            case spv::Op::OpSwitch:
               break;
             default:
               return _.diag(SPV_ERROR_INVALID_DATA, &inst)
@@ -110,8 +107,9 @@
           }
         }
         break;
-      case SpvOpVariable:
-        if (inst.GetOperandAs<SpvStorageClass>(2) == SpvStorageClassFunction &&
+      case spv::Op::OpVariable:
+        if (inst.GetOperandAs<spv::StorageClass>(2) ==
+                spv::StorageClass::Function &&
             adjacency_status != IN_ENTRY_BLOCK) {
           return _.diag(SPV_ERROR_INVALID_DATA, &inst)
                  << "All OpVariable instructions in a function must be the "
diff --git a/source/val/validate_annotation.cpp b/source/val/validate_annotation.cpp
index 21f999b..73d0285 100644
--- a/source/val/validate_annotation.cpp
+++ b/source/val/validate_annotation.cpp
@@ -24,12 +24,12 @@
 
 // Returns true if the decoration takes ID parameters.
 // TODO(dneto): This can be generated from the grammar.
-bool DecorationTakesIdParameters(SpvDecoration type) {
+bool DecorationTakesIdParameters(spv::Decoration type) {
   switch (type) {
-    case SpvDecorationUniformId:
-    case SpvDecorationAlignmentId:
-    case SpvDecorationMaxByteOffsetId:
-    case SpvDecorationHlslCounterBufferGOOGLE:
+    case spv::Decoration::UniformId:
+    case spv::Decoration::AlignmentId:
+    case spv::Decoration::MaxByteOffsetId:
+    case spv::Decoration::HlslCounterBufferGOOGLE:
       return true;
     default:
       break;
@@ -37,14 +37,14 @@
   return false;
 }
 
-bool IsMemberDecorationOnly(SpvDecoration dec) {
+bool IsMemberDecorationOnly(spv::Decoration dec) {
   switch (dec) {
-    case SpvDecorationRowMajor:
-    case SpvDecorationColMajor:
-    case SpvDecorationMatrixStride:
+    case spv::Decoration::RowMajor:
+    case spv::Decoration::ColMajor:
+    case spv::Decoration::MatrixStride:
       // SPIR-V spec bug? Offset is generated on variables when dealing with
       // transform feedback.
-      // case SpvDecorationOffset:
+      // case spv::Decoration::Offset:
       return true;
     default:
       break;
@@ -52,42 +52,42 @@
   return false;
 }
 
-bool IsNotMemberDecoration(SpvDecoration dec) {
+bool IsNotMemberDecoration(spv::Decoration dec) {
   switch (dec) {
-    case SpvDecorationSpecId:
-    case SpvDecorationBlock:
-    case SpvDecorationBufferBlock:
-    case SpvDecorationArrayStride:
-    case SpvDecorationGLSLShared:
-    case SpvDecorationGLSLPacked:
-    case SpvDecorationCPacked:
+    case spv::Decoration::SpecId:
+    case spv::Decoration::Block:
+    case spv::Decoration::BufferBlock:
+    case spv::Decoration::ArrayStride:
+    case spv::Decoration::GLSLShared:
+    case spv::Decoration::GLSLPacked:
+    case spv::Decoration::CPacked:
     // TODO: https://github.com/KhronosGroup/glslang/issues/703:
     // glslang applies Restrict to structure members.
-    // case SpvDecorationRestrict:
-    case SpvDecorationAliased:
-    case SpvDecorationConstant:
-    case SpvDecorationUniform:
-    case SpvDecorationUniformId:
-    case SpvDecorationSaturatedConversion:
-    case SpvDecorationIndex:
-    case SpvDecorationBinding:
-    case SpvDecorationDescriptorSet:
-    case SpvDecorationFuncParamAttr:
-    case SpvDecorationFPRoundingMode:
-    case SpvDecorationFPFastMathMode:
-    case SpvDecorationLinkageAttributes:
-    case SpvDecorationNoContraction:
-    case SpvDecorationInputAttachmentIndex:
-    case SpvDecorationAlignment:
-    case SpvDecorationMaxByteOffset:
-    case SpvDecorationAlignmentId:
-    case SpvDecorationMaxByteOffsetId:
-    case SpvDecorationNoSignedWrap:
-    case SpvDecorationNoUnsignedWrap:
-    case SpvDecorationNonUniform:
-    case SpvDecorationRestrictPointer:
-    case SpvDecorationAliasedPointer:
-    case SpvDecorationCounterBuffer:
+    // case spv::Decoration::Restrict:
+    case spv::Decoration::Aliased:
+    case spv::Decoration::Constant:
+    case spv::Decoration::Uniform:
+    case spv::Decoration::UniformId:
+    case spv::Decoration::SaturatedConversion:
+    case spv::Decoration::Index:
+    case spv::Decoration::Binding:
+    case spv::Decoration::DescriptorSet:
+    case spv::Decoration::FuncParamAttr:
+    case spv::Decoration::FPRoundingMode:
+    case spv::Decoration::FPFastMathMode:
+    case spv::Decoration::LinkageAttributes:
+    case spv::Decoration::NoContraction:
+    case spv::Decoration::InputAttachmentIndex:
+    case spv::Decoration::Alignment:
+    case spv::Decoration::MaxByteOffset:
+    case spv::Decoration::AlignmentId:
+    case spv::Decoration::MaxByteOffsetId:
+    case spv::Decoration::NoSignedWrap:
+    case spv::Decoration::NoUnsignedWrap:
+    case spv::Decoration::NonUniform:
+    case spv::Decoration::RestrictPointer:
+    case spv::Decoration::AliasedPointer:
+    case spv::Decoration::CounterBuffer:
       return true;
     default:
       break;
@@ -95,7 +95,7 @@
   return false;
 }
 
-spv_result_t ValidateDecorationTarget(ValidationState_t& _, SpvDecoration dec,
+spv_result_t ValidateDecorationTarget(ValidationState_t& _, spv::Decoration dec,
                                       const Instruction* inst,
                                       const Instruction* target) {
   auto fail = [&_, dec, inst, target](uint32_t vuid) -> DiagnosticStream {
@@ -106,76 +106,76 @@
     return ds;
   };
   switch (dec) {
-    case SpvDecorationSpecId:
+    case spv::Decoration::SpecId:
       if (!spvOpcodeIsScalarSpecConstant(target->opcode())) {
         return fail(0) << "must be a scalar specialization constant";
       }
       break;
-    case SpvDecorationBlock:
-    case SpvDecorationBufferBlock:
-    case SpvDecorationGLSLShared:
-    case SpvDecorationGLSLPacked:
-    case SpvDecorationCPacked:
-      if (target->opcode() != SpvOpTypeStruct) {
+    case spv::Decoration::Block:
+    case spv::Decoration::BufferBlock:
+    case spv::Decoration::GLSLShared:
+    case spv::Decoration::GLSLPacked:
+    case spv::Decoration::CPacked:
+      if (target->opcode() != spv::Op::OpTypeStruct) {
         return fail(0) << "must be a structure type";
       }
       break;
-    case SpvDecorationArrayStride:
-      if (target->opcode() != SpvOpTypeArray &&
-          target->opcode() != SpvOpTypeRuntimeArray &&
-          target->opcode() != SpvOpTypePointer) {
+    case spv::Decoration::ArrayStride:
+      if (target->opcode() != spv::Op::OpTypeArray &&
+          target->opcode() != spv::Op::OpTypeRuntimeArray &&
+          target->opcode() != spv::Op::OpTypePointer) {
         return fail(0) << "must be an array or pointer type";
       }
       break;
-    case SpvDecorationBuiltIn:
-      if (target->opcode() != SpvOpVariable &&
+    case spv::Decoration::BuiltIn:
+      if (target->opcode() != spv::Op::OpVariable &&
           !spvOpcodeIsConstant(target->opcode())) {
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
                << "BuiltIns can only target variables, structure members or "
                   "constants";
       }
-      if (_.HasCapability(SpvCapabilityShader) &&
-          inst->GetOperandAs<SpvBuiltIn>(2) == SpvBuiltInWorkgroupSize) {
+      if (_.HasCapability(spv::Capability::Shader) &&
+          inst->GetOperandAs<spv::BuiltIn>(2) == spv::BuiltIn::WorkgroupSize) {
         if (!spvOpcodeIsConstant(target->opcode())) {
           return fail(0) << "must be a constant for WorkgroupSize";
         }
-      } else if (target->opcode() != SpvOpVariable) {
+      } else if (target->opcode() != spv::Op::OpVariable) {
         return fail(0) << "must be a variable";
       }
       break;
-    case SpvDecorationNoPerspective:
-    case SpvDecorationFlat:
-    case SpvDecorationPatch:
-    case SpvDecorationCentroid:
-    case SpvDecorationSample:
-    case SpvDecorationRestrict:
-    case SpvDecorationAliased:
-    case SpvDecorationVolatile:
-    case SpvDecorationCoherent:
-    case SpvDecorationNonWritable:
-    case SpvDecorationNonReadable:
-    case SpvDecorationXfbBuffer:
-    case SpvDecorationXfbStride:
-    case SpvDecorationComponent:
-    case SpvDecorationStream:
-    case SpvDecorationRestrictPointer:
-    case SpvDecorationAliasedPointer:
-      if (target->opcode() != SpvOpVariable &&
-          target->opcode() != SpvOpFunctionParameter) {
+    case spv::Decoration::NoPerspective:
+    case spv::Decoration::Flat:
+    case spv::Decoration::Patch:
+    case spv::Decoration::Centroid:
+    case spv::Decoration::Sample:
+    case spv::Decoration::Restrict:
+    case spv::Decoration::Aliased:
+    case spv::Decoration::Volatile:
+    case spv::Decoration::Coherent:
+    case spv::Decoration::NonWritable:
+    case spv::Decoration::NonReadable:
+    case spv::Decoration::XfbBuffer:
+    case spv::Decoration::XfbStride:
+    case spv::Decoration::Component:
+    case spv::Decoration::Stream:
+    case spv::Decoration::RestrictPointer:
+    case spv::Decoration::AliasedPointer:
+      if (target->opcode() != spv::Op::OpVariable &&
+          target->opcode() != spv::Op::OpFunctionParameter) {
         return fail(0) << "must be a memory object declaration";
       }
-      if (_.GetIdOpcode(target->type_id()) != SpvOpTypePointer) {
+      if (_.GetIdOpcode(target->type_id()) != spv::Op::OpTypePointer) {
         return fail(0) << "must be a pointer type";
       }
       break;
-    case SpvDecorationInvariant:
-    case SpvDecorationConstant:
-    case SpvDecorationLocation:
-    case SpvDecorationIndex:
-    case SpvDecorationBinding:
-    case SpvDecorationDescriptorSet:
-    case SpvDecorationInputAttachmentIndex:
-      if (target->opcode() != SpvOpVariable) {
+    case spv::Decoration::Invariant:
+    case spv::Decoration::Constant:
+    case spv::Decoration::Location:
+    case spv::Decoration::Index:
+    case spv::Decoration::Binding:
+    case spv::Decoration::DescriptorSet:
+    case spv::Decoration::InputAttachmentIndex:
+      if (target->opcode() != spv::Op::OpVariable) {
         return fail(0) << "must be a variable";
       }
       break;
@@ -185,57 +185,60 @@
 
   if (spvIsVulkanEnv(_.context()->target_env)) {
     // The following were all checked as pointer types above.
-    SpvStorageClass sc = SpvStorageClassUniform;
+    spv::StorageClass sc = spv::StorageClass::Uniform;
     const auto type = _.FindDef(target->type_id());
     if (type && type->operands().size() > 2) {
-      sc = type->GetOperandAs<SpvStorageClass>(1);
+      sc = type->GetOperandAs<spv::StorageClass>(1);
     }
     switch (dec) {
-      case SpvDecorationLocation:
-      case SpvDecorationComponent:
-        // Location is used for input, output and ray tracing stages.
-        if (sc != SpvStorageClassInput && sc != SpvStorageClassOutput &&
-            sc != SpvStorageClassRayPayloadKHR &&
-            sc != SpvStorageClassIncomingRayPayloadKHR &&
-            sc != SpvStorageClassHitAttributeKHR &&
-            sc != SpvStorageClassCallableDataKHR &&
-            sc != SpvStorageClassIncomingCallableDataKHR &&
-            sc != SpvStorageClassShaderRecordBufferKHR) {
+      case spv::Decoration::Location:
+      case spv::Decoration::Component:
+        // Location is used for input, output, tile image, and ray tracing
+        // stages.
+        if (sc != spv::StorageClass::Input && sc != spv::StorageClass::Output &&
+            sc != spv::StorageClass::RayPayloadKHR &&
+            sc != spv::StorageClass::IncomingRayPayloadKHR &&
+            sc != spv::StorageClass::HitAttributeKHR &&
+            sc != spv::StorageClass::CallableDataKHR &&
+            sc != spv::StorageClass::IncomingCallableDataKHR &&
+            sc != spv::StorageClass::ShaderRecordBufferKHR &&
+            sc != spv::StorageClass::HitObjectAttributeNV &&
+            sc != spv::StorageClass::TileImageEXT) {
           return _.diag(SPV_ERROR_INVALID_ID, target)
                  << _.VkErrorID(6672) << _.SpvDecorationString(dec)
                  << " decoration must not be applied to this storage class";
         }
         break;
-      case SpvDecorationIndex:
+      case spv::Decoration::Index:
         // Langauge from SPIR-V definition of Index
-        if (sc != SpvStorageClassOutput) {
+        if (sc != spv::StorageClass::Output) {
           return fail(0) << "must be in the Output storage class";
         }
         break;
-      case SpvDecorationBinding:
-      case SpvDecorationDescriptorSet:
-        if (sc != SpvStorageClassStorageBuffer &&
-            sc != SpvStorageClassUniform &&
-            sc != SpvStorageClassUniformConstant) {
+      case spv::Decoration::Binding:
+      case spv::Decoration::DescriptorSet:
+        if (sc != spv::StorageClass::StorageBuffer &&
+            sc != spv::StorageClass::Uniform &&
+            sc != spv::StorageClass::UniformConstant) {
           return fail(6491) << "must be in the StorageBuffer, Uniform, or "
                                "UniformConstant storage class";
         }
         break;
-      case SpvDecorationInputAttachmentIndex:
-        if (sc != SpvStorageClassUniformConstant) {
+      case spv::Decoration::InputAttachmentIndex:
+        if (sc != spv::StorageClass::UniformConstant) {
           return fail(6678) << "must be in the UniformConstant storage class";
         }
         break;
-      case SpvDecorationFlat:
-      case SpvDecorationNoPerspective:
-      case SpvDecorationCentroid:
-      case SpvDecorationSample:
-        if (sc != SpvStorageClassInput && sc != SpvStorageClassOutput) {
+      case spv::Decoration::Flat:
+      case spv::Decoration::NoPerspective:
+      case spv::Decoration::Centroid:
+      case spv::Decoration::Sample:
+        if (sc != spv::StorageClass::Input && sc != spv::StorageClass::Output) {
           return fail(4670) << "storage class must be Input or Output";
         }
         break;
-      case SpvDecorationPerVertexKHR:
-        if (sc != SpvStorageClassInput) {
+      case spv::Decoration::PerVertexKHR:
+        if (sc != spv::StorageClass::Input) {
           return fail(6777) << "storage class must be Input";
         }
         break;
@@ -247,7 +250,7 @@
 }
 
 spv_result_t ValidateDecorate(ValidationState_t& _, const Instruction* inst) {
-  const auto decoration = inst->GetOperandAs<SpvDecoration>(1);
+  const auto decoration = inst->GetOperandAs<spv::Decoration>(1);
   const auto target_id = inst->GetOperandAs<uint32_t>(0);
   const auto target = _.FindDef(target_id);
   if (!target) {
@@ -255,8 +258,8 @@
   }
 
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    if ((decoration == SpvDecorationGLSLShared) ||
-        (decoration == SpvDecorationGLSLPacked)) {
+    if ((decoration == spv::Decoration::GLSLShared) ||
+        (decoration == spv::Decoration::GLSLPacked)) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
              << _.VkErrorID(4669) << "OpDecorate decoration '"
              << _.SpvDecorationString(decoration)
@@ -270,7 +273,7 @@
               "OpDecorateId";
   }
 
-  if (target->opcode() != SpvOpDecorationGroup) {
+  if (target->opcode() != spv::Op::OpDecorationGroup) {
     if (IsMemberDecorationOnly(decoration)) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
              << _.SpvDecorationString(decoration)
@@ -287,7 +290,7 @@
 }
 
 spv_result_t ValidateDecorateId(ValidationState_t& _, const Instruction* inst) {
-  const auto decoration = inst->GetOperandAs<SpvDecoration>(1);
+  const auto decoration = inst->GetOperandAs<spv::Decoration>(1);
   if (!DecorationTakesIdParameters(decoration)) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "Decorations that don't take ID parameters may not be used with "
@@ -306,7 +309,7 @@
                                     const Instruction* inst) {
   const auto struct_type_id = inst->GetOperandAs<uint32_t>(0);
   const auto struct_type = _.FindDef(struct_type_id);
-  if (!struct_type || SpvOpTypeStruct != struct_type->opcode()) {
+  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.";
@@ -323,7 +326,7 @@
            << " members. Largest valid index is " << member_count - 1 << ".";
   }
 
-  const auto decoration = inst->GetOperandAs<SpvDecoration>(2);
+  const auto decoration = inst->GetOperandAs<spv::Decoration>(2);
   if (IsNotMemberDecoration(decoration)) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << _.SpvDecorationString(decoration)
@@ -339,10 +342,11 @@
   const auto decoration_group = _.FindDef(decoration_group_id);
   for (auto pair : decoration_group->uses()) {
     auto use = pair.first;
-    if (use->opcode() != SpvOpDecorate && use->opcode() != SpvOpGroupDecorate &&
-        use->opcode() != SpvOpGroupMemberDecorate &&
-        use->opcode() != SpvOpName && use->opcode() != SpvOpDecorateId &&
-        !use->IsNonSemantic()) {
+    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()) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
              << "Result id of OpDecorationGroup can only "
              << "be targeted by OpName, OpGroupDecorate, "
@@ -356,7 +360,8 @@
                                    const Instruction* inst) {
   const auto decoration_group_id = inst->GetOperandAs<uint32_t>(0);
   auto decoration_group = _.FindDef(decoration_group_id);
-  if (!decoration_group || SpvOpDecorationGroup != decoration_group->opcode()) {
+  if (!decoration_group ||
+      spv::Op::OpDecorationGroup != decoration_group->opcode()) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "OpGroupDecorate Decoration group <id> "
            << _.getIdName(decoration_group_id) << " is not a decoration group.";
@@ -364,7 +369,7 @@
   for (unsigned i = 1; i < inst->operands().size(); ++i) {
     auto target_id = inst->GetOperandAs<uint32_t>(i);
     auto target = _.FindDef(target_id);
-    if (!target || target->opcode() == SpvOpDecorationGroup) {
+    if (!target || target->opcode() == spv::Op::OpDecorationGroup) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
              << "OpGroupDecorate may not target OpDecorationGroup <id> "
              << _.getIdName(target_id);
@@ -377,7 +382,8 @@
                                          const Instruction* inst) {
   const auto decoration_group_id = inst->GetOperandAs<uint32_t>(0);
   const auto decoration_group = _.FindDef(decoration_group_id);
-  if (!decoration_group || SpvOpDecorationGroup != decoration_group->opcode()) {
+  if (!decoration_group ||
+      spv::Op::OpDecorationGroup != decoration_group->opcode()) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "OpGroupMemberDecorate Decoration group <id> "
            << _.getIdName(decoration_group_id) << " is not a decoration group.";
@@ -388,7 +394,7 @@
     const uint32_t struct_id = inst->GetOperandAs<uint32_t>(i);
     const uint32_t index = inst->GetOperandAs<uint32_t>(i + 1);
     auto struct_instr = _.FindDef(struct_id);
-    if (!struct_instr || SpvOpTypeStruct != struct_instr->opcode()) {
+    if (!struct_instr || spv::Op::OpTypeStruct != struct_instr->opcode()) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
              << "OpGroupMemberDecorate Structure type <id> "
              << _.getIdName(struct_id) << " is not a struct type.";
@@ -413,10 +419,11 @@
 spv_result_t RegisterDecorations(ValidationState_t& _,
                                  const Instruction* inst) {
   switch (inst->opcode()) {
-    case SpvOpDecorate:
-    case SpvOpDecorateId: {
+    case spv::Op::OpDecorate:
+    case spv::Op::OpDecorateId: {
       const uint32_t target_id = inst->word(1);
-      const SpvDecoration dec_type = static_cast<SpvDecoration>(inst->word(2));
+      const spv::Decoration dec_type =
+          static_cast<spv::Decoration>(inst->word(2));
       std::vector<uint32_t> dec_params;
       if (inst->words().size() > 3) {
         dec_params.insert(dec_params.end(), inst->words().begin() + 3,
@@ -425,10 +432,11 @@
       _.RegisterDecorationForId(target_id, Decoration(dec_type, dec_params));
       break;
     }
-    case SpvOpMemberDecorate: {
+    case spv::Op::OpMemberDecorate: {
       const uint32_t struct_id = inst->word(1);
       const uint32_t index = inst->word(2);
-      const SpvDecoration dec_type = static_cast<SpvDecoration>(inst->word(3));
+      const spv::Decoration dec_type =
+          static_cast<spv::Decoration>(inst->word(3));
       std::vector<uint32_t> dec_params;
       if (inst->words().size() > 4) {
         dec_params.insert(dec_params.end(), inst->words().begin() + 4,
@@ -438,12 +446,12 @@
                                 Decoration(dec_type, dec_params, index));
       break;
     }
-    case SpvOpDecorationGroup: {
+    case spv::Op::OpDecorationGroup: {
       // We don't need to do anything right now. Assigning decorations to groups
       // will be taken care of via OpGroupDecorate.
       break;
     }
-    case SpvOpGroupDecorate: {
+    case spv::Op::OpGroupDecorate: {
       // Word 1 is the group <id>. All subsequent words are target <id>s that
       // are going to be decorated with the decorations.
       const uint32_t decoration_group_id = inst->word(1);
@@ -456,7 +464,7 @@
       }
       break;
     }
-    case SpvOpGroupMemberDecorate: {
+    case spv::Op::OpGroupMemberDecorate: {
       // Word 1 is the Decoration Group <id> followed by (struct<id>,literal)
       // pairs. All decorations of the group should be applied to all the struct
       // members that are specified in the instructions.
@@ -486,24 +494,24 @@
 
 spv_result_t AnnotationPass(ValidationState_t& _, const Instruction* inst) {
   switch (inst->opcode()) {
-    case SpvOpDecorate:
+    case spv::Op::OpDecorate:
       if (auto error = ValidateDecorate(_, inst)) return error;
       break;
-    case SpvOpDecorateId:
+    case spv::Op::OpDecorateId:
       if (auto error = ValidateDecorateId(_, inst)) return error;
       break;
-    // TODO(dneto): SpvOpDecorateStringGOOGLE
+    // TODO(dneto): spv::Op::OpDecorateStringGOOGLE
     // See https://github.com/KhronosGroup/SPIRV-Tools/issues/2253
-    case SpvOpMemberDecorate:
+    case spv::Op::OpMemberDecorate:
       if (auto error = ValidateMemberDecorate(_, inst)) return error;
       break;
-    case SpvOpDecorationGroup:
+    case spv::Op::OpDecorationGroup:
       if (auto error = ValidateDecorationGroup(_, inst)) return error;
       break;
-    case SpvOpGroupDecorate:
+    case spv::Op::OpGroupDecorate:
       if (auto error = ValidateGroupDecorate(_, inst)) return error;
       break;
-    case SpvOpGroupMemberDecorate:
+    case spv::Op::OpGroupMemberDecorate:
       if (auto error = ValidateGroupMemberDecorate(_, inst)) return error;
       break;
     default:
diff --git a/source/val/validate_arithmetics.cpp b/source/val/validate_arithmetics.cpp
index bae9b5d..b608a85 100644
--- a/source/val/validate_arithmetics.cpp
+++ b/source/val/validate_arithmetics.cpp
@@ -14,13 +14,11 @@
 
 // Performs validation of arithmetic instructions.
 
-#include "source/val/validate.h"
-
 #include <vector>
 
-#include "source/diagnostic.h"
 #include "source/opcode.h"
 #include "source/val/instruction.h"
+#include "source/val/validate.h"
 #include "source/val/validation_state.h"
 
 namespace spvtools {
@@ -28,29 +26,45 @@
 
 // Validates correctness of arithmetic instructions.
 spv_result_t ArithmeticsPass(ValidationState_t& _, const Instruction* inst) {
-  const SpvOp opcode = inst->opcode();
+  const spv::Op opcode = inst->opcode();
   const uint32_t result_type = inst->type_id();
 
   switch (opcode) {
-    case SpvOpFAdd:
-    case SpvOpFSub:
-    case SpvOpFMul:
-    case SpvOpFDiv:
-    case SpvOpFRem:
-    case SpvOpFMod:
-    case SpvOpFNegate: {
+    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 != SpvOpFMul && opcode != SpvOpFRem && opcode != SpvOpFMod);
+          (opcode != spv::Op::OpFMul && opcode != spv::Op::OpFRem &&
+           opcode != spv::Op::OpFMod);
       if (!_.IsFloatScalarType(result_type) &&
           !_.IsFloatVectorType(result_type) &&
-          !(supportsCoopMat && _.IsFloatCooperativeMatrixType(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 (_.GetOperandTypeId(inst, operand_index) != result_type)
+        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, type_id, result_type);
+          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 "
@@ -59,9 +73,9 @@
       break;
     }
 
-    case SpvOpUDiv:
-    case SpvOpUMod: {
-      bool supportsCoopMat = (opcode == SpvOpUDiv);
+    case spv::Op::OpUDiv:
+    case spv::Op::OpUMod: {
+      bool supportsCoopMat = (opcode == spv::Op::OpUDiv);
       if (!_.IsUnsignedIntScalarType(result_type) &&
           !_.IsUnsignedIntVectorType(result_type) &&
           !(supportsCoopMat &&
@@ -72,7 +86,19 @@
 
       for (size_t operand_index = 2; operand_index < inst->operands().size();
            ++operand_index) {
-        if (_.GetOperandTypeId(inst, operand_index) != result_type)
+        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, type_id, result_type);
+          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 "
@@ -81,17 +107,21 @@
       break;
     }
 
-    case SpvOpISub:
-    case SpvOpIAdd:
-    case SpvOpIMul:
-    case SpvOpSDiv:
-    case SpvOpSMod:
-    case SpvOpSRem:
-    case SpvOpSNegate: {
+    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 != SpvOpIMul && opcode != SpvOpSRem && opcode != SpvOpSMod);
+          (opcode != spv::Op::OpIMul && opcode != spv::Op::OpSRem &&
+           opcode != spv::Op::OpSMod);
       if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type) &&
-          !(supportsCoopMat && _.IsIntCooperativeMatrixType(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);
@@ -102,9 +132,26 @@
       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, type_id, result_type);
+          if (ret != SPV_SUCCESS) return ret;
+        }
+
         if (!type_id ||
             (!_.IsIntScalarType(type_id) && !_.IsIntVectorType(type_id) &&
-             !(supportsCoopMat && _.IsIntCooperativeMatrixType(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 operand: "
                  << spvOpcodeString(opcode) << " operand index "
@@ -125,7 +172,7 @@
       break;
     }
 
-    case SpvOpDot: {
+    case spv::Op::OpDot: {
       if (!_.IsFloatScalarType(result_type))
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
                << "Expected float scalar type as Result Type: "
@@ -162,7 +209,7 @@
       break;
     }
 
-    case SpvOpVectorTimesScalar: {
+    case spv::Op::OpVectorTimesScalar: {
       if (!_.IsFloatVectorType(result_type))
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
                << "Expected float vector type as Result Type: "
@@ -185,9 +232,9 @@
       break;
     }
 
-    case SpvOpMatrixTimesScalar: {
+    case spv::Op::OpMatrixTimesScalar: {
       if (!_.IsFloatMatrixType(result_type) &&
-          !_.IsCooperativeMatrixType(result_type))
+          !(_.IsCooperativeMatrixType(result_type)))
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
                << "Expected float matrix type as Result Type: "
                << spvOpcodeString(opcode);
@@ -209,7 +256,7 @@
       break;
     }
 
-    case SpvOpVectorTimesMatrix: {
+    case spv::Op::OpVectorTimesMatrix: {
       const uint32_t vector_type_id = _.GetOperandTypeId(inst, 2);
       const uint32_t matrix_type_id = _.GetOperandTypeId(inst, 3);
 
@@ -259,7 +306,7 @@
       break;
     }
 
-    case SpvOpMatrixTimesVector: {
+    case spv::Op::OpMatrixTimesVector: {
       const uint32_t matrix_type_id = _.GetOperandTypeId(inst, 2);
       const uint32_t vector_type_id = _.GetOperandTypeId(inst, 3);
 
@@ -303,7 +350,7 @@
       break;
     }
 
-    case SpvOpMatrixTimesMatrix: {
+    case spv::Op::OpMatrixTimesMatrix: {
       const uint32_t left_type_id = _.GetOperandTypeId(inst, 2);
       const uint32_t right_type_id = _.GetOperandTypeId(inst, 3);
 
@@ -369,7 +416,7 @@
       break;
     }
 
-    case SpvOpOuterProduct: {
+    case spv::Op::OpOuterProduct: {
       const uint32_t left_type_id = _.GetOperandTypeId(inst, 2);
       const uint32_t right_type_id = _.GetOperandTypeId(inst, 3);
 
@@ -407,10 +454,10 @@
       break;
     }
 
-    case SpvOpIAddCarry:
-    case SpvOpISubBorrow:
-    case SpvOpUMulExtended:
-    case SpvOpSMulExtended: {
+    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)
@@ -422,7 +469,7 @@
                << "Expected Result Type struct to have two members: "
                << spvOpcodeString(opcode);
 
-      if (opcode == SpvOpSMulExtended) {
+      if (opcode == spv::Op::OpSMulExtended) {
         if (!_.IsIntScalarType(result_types[0]) &&
             !_.IsIntVectorType(result_types[0]))
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
@@ -453,28 +500,114 @@
       break;
     }
 
-    case SpvOpCooperativeMatrixMulAddNV: {
+    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 (!_.IsCooperativeMatrixType(A_type_id)) {
+      if (!_.IsCooperativeMatrixNVType(A_type_id)) {
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
                << "Expected cooperative matrix type as A Type: "
                << spvOpcodeString(opcode);
       }
-      if (!_.IsCooperativeMatrixType(B_type_id)) {
+      if (!_.IsCooperativeMatrixNVType(B_type_id)) {
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
                << "Expected cooperative matrix type as B Type: "
                << spvOpcodeString(opcode);
       }
-      if (!_.IsCooperativeMatrixType(C_type_id)) {
+      if (!_.IsCooperativeMatrixNVType(C_type_id)) {
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
                << "Expected cooperative matrix type as C Type: "
                << spvOpcodeString(opcode);
       }
-      if (!_.IsCooperativeMatrixType(D_type_id)) {
+      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);
+      }
+      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);
diff --git a/source/val/validate_atomics.cpp b/source/val/validate_atomics.cpp
index bf565c3..b745a9e 100644
--- a/source/val/validate_atomics.cpp
+++ b/source/val/validate_atomics.cpp
@@ -16,31 +16,29 @@
 
 // Validates correctness of atomic SPIR-V instructions.
 
-#include "source/val/validate.h"
-
-#include "source/diagnostic.h"
 #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/validate_memory_semantics.h"
 #include "source/val/validate_scopes.h"
 #include "source/val/validation_state.h"
 
 namespace {
 
-bool IsStorageClassAllowedByUniversalRules(uint32_t storage_class) {
+bool IsStorageClassAllowedByUniversalRules(spv::StorageClass storage_class) {
   switch (storage_class) {
-    case SpvStorageClassUniform:
-    case SpvStorageClassStorageBuffer:
-    case SpvStorageClassWorkgroup:
-    case SpvStorageClassCrossWorkgroup:
-    case SpvStorageClassGeneric:
-    case SpvStorageClassAtomicCounter:
-    case SpvStorageClassImage:
-    case SpvStorageClassFunction:
-    case SpvStorageClassPhysicalStorageBuffer:
-    case SpvStorageClassTaskPayloadWorkgroupEXT:
+    case spv::StorageClass::Uniform:
+    case spv::StorageClass::StorageBuffer:
+    case spv::StorageClass::Workgroup:
+    case spv::StorageClass::CrossWorkgroup:
+    case spv::StorageClass::Generic:
+    case spv::StorageClass::AtomicCounter:
+    case spv::StorageClass::Image:
+    case spv::StorageClass::Function:
+    case spv::StorageClass::PhysicalStorageBuffer:
+    case spv::StorageClass::TaskPayloadWorkgroupEXT:
       return true;
       break;
     default:
@@ -48,10 +46,10 @@
   }
 }
 
-bool HasReturnType(uint32_t opcode) {
+bool HasReturnType(spv::Op opcode) {
   switch (opcode) {
-    case SpvOpAtomicStore:
-    case SpvOpAtomicFlagClear:
+    case spv::Op::OpAtomicStore:
+    case spv::Op::OpAtomicFlagClear:
       return false;
       break;
     default:
@@ -59,11 +57,11 @@
   }
 }
 
-bool HasOnlyFloatReturnType(uint32_t opcode) {
+bool HasOnlyFloatReturnType(spv::Op opcode) {
   switch (opcode) {
-    case SpvOpAtomicFAddEXT:
-    case SpvOpAtomicFMinEXT:
-    case SpvOpAtomicFMaxEXT:
+    case spv::Op::OpAtomicFAddEXT:
+    case spv::Op::OpAtomicFMinEXT:
+    case spv::Op::OpAtomicFMaxEXT:
       return true;
       break;
     default:
@@ -71,21 +69,21 @@
   }
 }
 
-bool HasOnlyIntReturnType(uint32_t opcode) {
+bool HasOnlyIntReturnType(spv::Op opcode) {
   switch (opcode) {
-    case SpvOpAtomicCompareExchange:
-    case SpvOpAtomicCompareExchangeWeak:
-    case SpvOpAtomicIIncrement:
-    case SpvOpAtomicIDecrement:
-    case SpvOpAtomicIAdd:
-    case SpvOpAtomicISub:
-    case SpvOpAtomicSMin:
-    case SpvOpAtomicUMin:
-    case SpvOpAtomicSMax:
-    case SpvOpAtomicUMax:
-    case SpvOpAtomicAnd:
-    case SpvOpAtomicOr:
-    case SpvOpAtomicXor:
+    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:
       return true;
       break;
     default:
@@ -93,10 +91,10 @@
   }
 }
 
-bool HasIntOrFloatReturnType(uint32_t opcode) {
+bool HasIntOrFloatReturnType(spv::Op opcode) {
   switch (opcode) {
-    case SpvOpAtomicLoad:
-    case SpvOpAtomicExchange:
+    case spv::Op::OpAtomicLoad:
+    case spv::Op::OpAtomicExchange:
       return true;
       break;
     default:
@@ -104,9 +102,9 @@
   }
 }
 
-bool HasOnlyBoolReturnType(uint32_t opcode) {
+bool HasOnlyBoolReturnType(spv::Op opcode) {
   switch (opcode) {
-    case SpvOpAtomicFlagTestAndSet:
+    case spv::Op::OpAtomicFlagTestAndSet:
       return true;
       break;
     default:
@@ -121,29 +119,29 @@
 
 // Validates correctness of atomic instructions.
 spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) {
-  const SpvOp opcode = inst->opcode();
+  const spv::Op opcode = inst->opcode();
   switch (opcode) {
-    case SpvOpAtomicLoad:
-    case SpvOpAtomicStore:
-    case SpvOpAtomicExchange:
-    case SpvOpAtomicFAddEXT:
-    case SpvOpAtomicCompareExchange:
-    case SpvOpAtomicCompareExchangeWeak:
-    case SpvOpAtomicIIncrement:
-    case SpvOpAtomicIDecrement:
-    case SpvOpAtomicIAdd:
-    case SpvOpAtomicISub:
-    case SpvOpAtomicSMin:
-    case SpvOpAtomicUMin:
-    case SpvOpAtomicFMinEXT:
-    case SpvOpAtomicSMax:
-    case SpvOpAtomicUMax:
-    case SpvOpAtomicFMaxEXT:
-    case SpvOpAtomicAnd:
-    case SpvOpAtomicOr:
-    case SpvOpAtomicXor:
-    case SpvOpAtomicFlagTestAndSet:
-    case SpvOpAtomicFlagClear: {
+    case spv::Op::OpAtomicLoad:
+    case spv::Op::OpAtomicStore:
+    case spv::Op::OpAtomicExchange:
+    case spv::Op::OpAtomicFAddEXT:
+    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::OpAtomicFMinEXT:
+    case spv::Op::OpAtomicSMax:
+    case spv::Op::OpAtomicUMax:
+    case spv::Op::OpAtomicFMaxEXT:
+    case spv::Op::OpAtomicAnd:
+    case spv::Op::OpAtomicOr:
+    case spv::Op::OpAtomicXor:
+    case spv::Op::OpAtomicFlagTestAndSet:
+    case spv::Op::OpAtomicFlagClear: {
       const uint32_t result_type = inst->type_id();
 
       // All current atomics only are scalar result
@@ -177,7 +175,7 @@
       uint32_t operand_index = HasReturnType(opcode) ? 2 : 0;
       const uint32_t pointer_type = _.GetOperandTypeId(inst, operand_index++);
       uint32_t data_type = 0;
-      uint32_t storage_class = 0;
+      spv::StorageClass storage_class;
       if (!_.GetPointerTypeInfo(pointer_type, &data_type, &storage_class)) {
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
                << spvOpcodeString(opcode)
@@ -185,8 +183,8 @@
       }
 
       // Can't use result_type because OpAtomicStore doesn't have a result
-      if ( _.IsIntScalarType(data_type) &&_.GetBitWidth(data_type) == 64 &&
-          !_.HasCapability(SpvCapabilityInt64Atomics)) {
+      if (_.IsIntScalarType(data_type) && _.GetBitWidth(data_type) == 64 &&
+          !_.HasCapability(spv::Capability::Int64Atomics)) {
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
                << spvOpcodeString(opcode)
                << ": 64-bit atomics require the Int64Atomics capability";
@@ -200,69 +198,69 @@
       }
 
       // Then Shader rules
-      if (_.HasCapability(SpvCapabilityShader)) {
+      if (_.HasCapability(spv::Capability::Shader)) {
         // Vulkan environment rule
         if (spvIsVulkanEnv(_.context()->target_env)) {
-          if ((storage_class != SpvStorageClassUniform) &&
-              (storage_class != SpvStorageClassStorageBuffer) &&
-              (storage_class != SpvStorageClassWorkgroup) &&
-              (storage_class != SpvStorageClassImage) &&
-              (storage_class != SpvStorageClassPhysicalStorageBuffer) &&
-              (storage_class != SpvStorageClassTaskPayloadWorkgroupEXT)) {
+          if ((storage_class != spv::StorageClass::Uniform) &&
+              (storage_class != spv::StorageClass::StorageBuffer) &&
+              (storage_class != spv::StorageClass::Workgroup) &&
+              (storage_class != spv::StorageClass::Image) &&
+              (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 "
                       "be: Uniform, Workgroup, Image, StorageBuffer, "
                       "PhysicalStorageBuffer or TaskPayloadWorkgroupEXT.";
           }
-        } else if (storage_class == SpvStorageClassFunction) {
+        } else if (storage_class == spv::StorageClass::Function) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
                  << spvOpcodeString(opcode)
                  << ": Function storage class forbidden when the Shader "
                     "capability is declared.";
         }
 
-        if (opcode == SpvOpAtomicFAddEXT) {
+        if (opcode == spv::Op::OpAtomicFAddEXT) {
           // result type being float checked already
           if ((_.GetBitWidth(result_type) == 16) &&
-              (!_.HasCapability(SpvCapabilityAtomicFloat16AddEXT))) {
+              (!_.HasCapability(spv::Capability::AtomicFloat16AddEXT))) {
             return _.diag(SPV_ERROR_INVALID_DATA, inst)
                    << spvOpcodeString(opcode)
                    << ": float add atomics require the AtomicFloat32AddEXT "
                       "capability";
           }
           if ((_.GetBitWidth(result_type) == 32) &&
-              (!_.HasCapability(SpvCapabilityAtomicFloat32AddEXT))) {
+              (!_.HasCapability(spv::Capability::AtomicFloat32AddEXT))) {
             return _.diag(SPV_ERROR_INVALID_DATA, inst)
                    << spvOpcodeString(opcode)
                    << ": float add atomics require the AtomicFloat32AddEXT "
                       "capability";
           }
           if ((_.GetBitWidth(result_type) == 64) &&
-              (!_.HasCapability(SpvCapabilityAtomicFloat64AddEXT))) {
+              (!_.HasCapability(spv::Capability::AtomicFloat64AddEXT))) {
             return _.diag(SPV_ERROR_INVALID_DATA, inst)
                    << spvOpcodeString(opcode)
                    << ": float add atomics require the AtomicFloat64AddEXT "
                       "capability";
           }
-        } else if (opcode == SpvOpAtomicFMinEXT ||
-                   opcode == SpvOpAtomicFMaxEXT) {
+        } else if (opcode == spv::Op::OpAtomicFMinEXT ||
+                   opcode == spv::Op::OpAtomicFMaxEXT) {
           if ((_.GetBitWidth(result_type) == 16) &&
-              (!_.HasCapability(SpvCapabilityAtomicFloat16MinMaxEXT))) {
+              (!_.HasCapability(spv::Capability::AtomicFloat16MinMaxEXT))) {
             return _.diag(SPV_ERROR_INVALID_DATA, inst)
                    << spvOpcodeString(opcode)
                    << ": float min/max atomics require the "
                       "AtomicFloat16MinMaxEXT capability";
           }
           if ((_.GetBitWidth(result_type) == 32) &&
-              (!_.HasCapability(SpvCapabilityAtomicFloat32MinMaxEXT))) {
+              (!_.HasCapability(spv::Capability::AtomicFloat32MinMaxEXT))) {
             return _.diag(SPV_ERROR_INVALID_DATA, inst)
                    << spvOpcodeString(opcode)
                    << ": float min/max atomics require the "
                       "AtomicFloat32MinMaxEXT capability";
           }
           if ((_.GetBitWidth(result_type) == 64) &&
-              (!_.HasCapability(SpvCapabilityAtomicFloat64MinMaxEXT))) {
+              (!_.HasCapability(spv::Capability::AtomicFloat64MinMaxEXT))) {
             return _.diag(SPV_ERROR_INVALID_DATA, inst)
                    << spvOpcodeString(opcode)
                    << ": float min/max atomics require the "
@@ -273,10 +271,10 @@
 
       // And finally OpenCL environment rules
       if (spvIsOpenCLEnv(_.context()->target_env)) {
-        if ((storage_class != SpvStorageClassFunction) &&
-            (storage_class != SpvStorageClassWorkgroup) &&
-            (storage_class != SpvStorageClassCrossWorkgroup) &&
-            (storage_class != SpvStorageClassGeneric)) {
+        if ((storage_class != spv::StorageClass::Function) &&
+            (storage_class != spv::StorageClass::Workgroup) &&
+            (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, "
@@ -284,7 +282,7 @@
         }
 
         if (_.context()->target_env == SPV_ENV_OPENCL_1_2) {
-          if (storage_class == SpvStorageClassGeneric) {
+          if (storage_class == spv::StorageClass::Generic) {
             return _.diag(SPV_ERROR_INVALID_DATA, inst)
                    << "Storage class cannot be Generic in OpenCL 1.2 "
                       "environment";
@@ -293,15 +291,15 @@
       }
 
       // If result and pointer type are different, need to do special check here
-      if (opcode == SpvOpAtomicFlagTestAndSet ||
-          opcode == SpvOpAtomicFlagClear) {
+      if (opcode == spv::Op::OpAtomicFlagTestAndSet ||
+          opcode == spv::Op::OpAtomicFlagClear) {
         if (!_.IsIntScalarType(data_type) || _.GetBitWidth(data_type) != 32) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
                  << spvOpcodeString(opcode)
                  << ": expected Pointer to point to a value of 32-bit integer "
                     "type";
         }
-      } else if (opcode == SpvOpAtomicStore) {
+      } else if (opcode == spv::Op::OpAtomicStore) {
         if (!_.IsFloatScalarType(data_type) && !_.IsIntScalarType(data_type)) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
                  << spvOpcodeString(opcode)
@@ -325,8 +323,8 @@
                                                memory_scope))
         return error;
 
-      if (opcode == SpvOpAtomicCompareExchange ||
-          opcode == SpvOpAtomicCompareExchangeWeak) {
+      if (opcode == spv::Op::OpAtomicCompareExchange ||
+          opcode == spv::Op::OpAtomicCompareExchangeWeak) {
         const auto unequal_semantics_index = operand_index++;
         if (auto error = ValidateMemorySemantics(
                 _, inst, unequal_semantics_index, memory_scope))
@@ -346,15 +344,15 @@
             _.EvalInt32IfConst(
                 inst->GetOperandAs<uint32_t>(unequal_semantics_index));
         if (is_equal_const && is_unequal_const &&
-            ((equal_value & SpvMemorySemanticsVolatileMask) ^
-             (unequal_value & SpvMemorySemanticsVolatileMask))) {
+            ((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 == SpvOpAtomicStore) {
+      if (opcode == spv::Op::OpAtomicStore) {
         const uint32_t value_type = _.GetOperandTypeId(inst, 3);
         if (value_type != data_type) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
@@ -362,10 +360,11 @@
                  << ": expected Value type and the type pointed to by "
                     "Pointer to be the same";
         }
-      } else if (opcode != SpvOpAtomicLoad && opcode != SpvOpAtomicIIncrement &&
-                 opcode != SpvOpAtomicIDecrement &&
-                 opcode != SpvOpAtomicFlagTestAndSet &&
-                 opcode != SpvOpAtomicFlagClear) {
+      } else if (opcode != spv::Op::OpAtomicLoad &&
+                 opcode != spv::Op::OpAtomicIIncrement &&
+                 opcode != spv::Op::OpAtomicIDecrement &&
+                 opcode != spv::Op::OpAtomicFlagTestAndSet &&
+                 opcode != spv::Op::OpAtomicFlagClear) {
         const uint32_t value_type = _.GetOperandTypeId(inst, operand_index++);
         if (value_type != result_type) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
@@ -374,8 +373,8 @@
         }
       }
 
-      if (opcode == SpvOpAtomicCompareExchange ||
-          opcode == SpvOpAtomicCompareExchangeWeak) {
+      if (opcode == spv::Op::OpAtomicCompareExchange ||
+          opcode == spv::Op::OpAtomicCompareExchangeWeak) {
         const uint32_t comparator_type =
             _.GetOperandTypeId(inst, operand_index++);
         if (comparator_type != result_type) {
diff --git a/source/val/validate_barriers.cpp b/source/val/validate_barriers.cpp
index 03225d8..0abd5c8 100644
--- a/source/val/validate_barriers.cpp
+++ b/source/val/validate_barriers.cpp
@@ -16,11 +16,8 @@
 
 #include <string>
 
-#include "source/diagnostic.h"
 #include "source/opcode.h"
 #include "source/spirv_constant.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/validate_memory_semantics.h"
@@ -32,20 +29,20 @@
 
 // Validates correctness of barrier instructions.
 spv_result_t BarriersPass(ValidationState_t& _, const Instruction* inst) {
-  const SpvOp opcode = inst->opcode();
+  const spv::Op opcode = inst->opcode();
   const uint32_t result_type = inst->type_id();
 
   switch (opcode) {
-    case SpvOpControlBarrier: {
+    case spv::Op::OpControlBarrier: {
       if (_.version() < SPV_SPIRV_VERSION_WORD(1, 3)) {
         _.function(inst->function()->id())
             ->RegisterExecutionModelLimitation(
-                [](SpvExecutionModel model, std::string* message) {
-                  if (model != SpvExecutionModelTessellationControl &&
-                      model != SpvExecutionModelGLCompute &&
-                      model != SpvExecutionModelKernel &&
-                      model != SpvExecutionModelTaskNV &&
-                      model != SpvExecutionModelMeshNV) {
+                [](spv::ExecutionModel model, std::string* message) {
+                  if (model != spv::ExecutionModel::TessellationControl &&
+                      model != spv::ExecutionModel::GLCompute &&
+                      model != spv::ExecutionModel::Kernel &&
+                      model != spv::ExecutionModel::TaskNV &&
+                      model != spv::ExecutionModel::MeshNV) {
                     if (message) {
                       *message =
                           "OpControlBarrier requires one of the following "
@@ -76,7 +73,7 @@
       break;
     }
 
-    case SpvOpMemoryBarrier: {
+    case spv::Op::OpMemoryBarrier: {
       const uint32_t memory_scope = inst->word(1);
 
       if (auto error = ValidateMemoryScope(_, inst, memory_scope)) {
@@ -89,8 +86,8 @@
       break;
     }
 
-    case SpvOpNamedBarrierInitialize: {
-      if (_.GetIdOpcode(result_type) != SpvOpTypeNamedBarrier) {
+    case spv::Op::OpNamedBarrierInitialize: {
+      if (_.GetIdOpcode(result_type) != spv::Op::OpTypeNamedBarrier) {
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
                << spvOpcodeString(opcode)
                << ": expected Result Type to be OpTypeNamedBarrier";
@@ -106,9 +103,9 @@
       break;
     }
 
-    case SpvOpMemoryNamedBarrier: {
+    case spv::Op::OpMemoryNamedBarrier: {
       const uint32_t named_barrier_type = _.GetOperandTypeId(inst, 0);
-      if (_.GetIdOpcode(named_barrier_type) != SpvOpTypeNamedBarrier) {
+      if (_.GetIdOpcode(named_barrier_type) != spv::Op::OpTypeNamedBarrier) {
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
                << spvOpcodeString(opcode)
                << ": expected Named Barrier to be of type OpTypeNamedBarrier";
diff --git a/source/val/validate_bitwise.cpp b/source/val/validate_bitwise.cpp
index e6e97c4..d8d9958 100644
--- a/source/val/validate_bitwise.cpp
+++ b/source/val/validate_bitwise.cpp
@@ -14,7 +14,6 @@
 
 // Validates correctness of bitwise instructions.
 
-#include "source/diagnostic.h"
 #include "source/opcode.h"
 #include "source/spirv_target_env.h"
 #include "source/val/instruction.h"
@@ -27,7 +26,7 @@
 // Validates when base and result need to be the same type
 spv_result_t ValidateBaseType(ValidationState_t& _, const Instruction* inst,
                               const uint32_t base_type) {
-  const SpvOp opcode = inst->opcode();
+  const spv::Op opcode = inst->opcode();
 
   if (!_.IsIntScalarType(base_type) && !_.IsIntVectorType(base_type)) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
@@ -47,7 +46,7 @@
   }
 
   // OpBitCount just needs same number of components
-  if (base_type != inst->type_id() && opcode != SpvOpBitCount) {
+  if (base_type != inst->type_id() && opcode != spv::Op::OpBitCount) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "Expected Base Type to be equal to Result Type: "
            << spvOpcodeString(opcode);
@@ -58,13 +57,13 @@
 
 // Validates correctness of bitwise instructions.
 spv_result_t BitwisePass(ValidationState_t& _, const Instruction* inst) {
-  const SpvOp opcode = inst->opcode();
+  const spv::Op opcode = inst->opcode();
   const uint32_t result_type = inst->type_id();
 
   switch (opcode) {
-    case SpvOpShiftRightLogical:
-    case SpvOpShiftRightArithmetic:
-    case SpvOpShiftLeftLogical: {
+    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: "
@@ -103,10 +102,10 @@
       break;
     }
 
-    case SpvOpBitwiseOr:
-    case SpvOpBitwiseXor:
-    case SpvOpBitwiseAnd:
-    case SpvOpNot: {
+    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: "
@@ -140,7 +139,7 @@
       break;
     }
 
-    case SpvOpBitFieldInsert: {
+    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);
@@ -167,8 +166,8 @@
       break;
     }
 
-    case SpvOpBitFieldSExtract:
-    case SpvOpBitFieldUExtract: {
+    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);
@@ -189,7 +188,7 @@
       break;
     }
 
-    case SpvOpBitReverse: {
+    case spv::Op::OpBitReverse: {
       const uint32_t base_type = _.GetOperandTypeId(inst, 2);
 
       if (spv_result_t error = ValidateBaseType(_, inst, base_type)) {
@@ -199,20 +198,21 @@
       break;
     }
 
-    case SpvOpBitCount: {
+    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);
-      const uint32_t base_dimension = _.GetDimension(base_type);
-      const uint32_t result_dimension = _.GetDimension(result_type);
 
       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 "
diff --git a/source/val/validate_builtins.cpp b/source/val/validate_builtins.cpp
index 6f4b0f9..3e81712 100644
--- a/source/val/validate_builtins.cpp
+++ b/source/val/validate_builtins.cpp
@@ -24,10 +24,8 @@
 #include <sstream>
 #include <stack>
 #include <string>
-#include <unordered_map>
 #include <vector>
 
-#include "source/diagnostic.h"
 #include "source/opcode.h"
 #include "source/spirv_target_env.h"
 #include "source/util/bitutils.h"
@@ -62,7 +60,7 @@
                                const Instruction& inst,
                                uint32_t* underlying_type) {
   if (decoration.struct_member_index() != Decoration::kInvalidMember) {
-    if (inst.opcode() != SpvOpTypeStruct) {
+    if (inst.opcode() != spv::Op::OpTypeStruct) {
       return _.diag(SPV_ERROR_INVALID_DATA, &inst)
              << GetIdDesc(inst)
              << "Attempted to get underlying data type via member index for "
@@ -72,7 +70,7 @@
     return SPV_SUCCESS;
   }
 
-  if (inst.opcode() == SpvOpTypeStruct) {
+  if (inst.opcode() == spv::Op::OpTypeStruct) {
     return _.diag(SPV_ERROR_INVALID_DATA, &inst)
            << GetIdDesc(inst)
            << " did not find an member index to get underlying data type for "
@@ -84,7 +82,7 @@
     return SPV_SUCCESS;
   }
 
-  uint32_t storage_class = 0;
+  spv::StorageClass storage_class;
   if (!_.GetPointerTypeInfo(inst.type_id(), underlying_type, &storage_class)) {
     return _.diag(SPV_ERROR_INVALID_DATA, &inst)
            << GetIdDesc(inst)
@@ -95,22 +93,22 @@
 }
 
 // Returns Storage Class used by the instruction if applicable.
-// Returns SpvStorageClassMax if not.
-SpvStorageClass GetStorageClass(const Instruction& inst) {
+// Returns spv::StorageClass::Max if not.
+spv::StorageClass GetStorageClass(const Instruction& inst) {
   switch (inst.opcode()) {
-    case SpvOpTypePointer:
-    case SpvOpTypeForwardPointer: {
-      return SpvStorageClass(inst.word(2));
+    case spv::Op::OpTypePointer:
+    case spv::Op::OpTypeForwardPointer: {
+      return spv::StorageClass(inst.word(2));
     }
-    case SpvOpVariable: {
-      return SpvStorageClass(inst.word(3));
+    case spv::Op::OpVariable: {
+      return spv::StorageClass(inst.word(3));
     }
-    case SpvOpGenericCastToPtrExplicit: {
-      return SpvStorageClass(inst.word(4));
+    case spv::Op::OpGenericCastToPtrExplicit: {
+      return spv::StorageClass(inst.word(4));
     }
     default: { break; }
   }
-  return SpvStorageClassMax;
+  return spv::StorageClass::Max;
 }
 
 typedef enum VUIDError_ {
@@ -123,52 +121,52 @@
 const static uint32_t NumVUIDBuiltins = 36;
 
 typedef struct {
-  SpvBuiltIn builtIn;
+  spv::BuiltIn builtIn;
   uint32_t vuid[VUIDErrorMax];  // execution mode, storage class, type VUIDs
 } BuiltinVUIDMapping;
 
 std::array<BuiltinVUIDMapping, NumVUIDBuiltins> builtinVUIDInfo = {{
     // clang-format off
-    {SpvBuiltInSubgroupEqMask,            {0,    4370, 4371}},
-    {SpvBuiltInSubgroupGeMask,            {0,    4372, 4373}},
-    {SpvBuiltInSubgroupGtMask,            {0,    4374, 4375}},
-    {SpvBuiltInSubgroupLeMask,            {0,    4376, 4377}},
-    {SpvBuiltInSubgroupLtMask,            {0,    4378, 4379}},
-    {SpvBuiltInSubgroupLocalInvocationId, {0,    4380, 4381}},
-    {SpvBuiltInSubgroupSize,              {0,    4382, 4383}},
-    {SpvBuiltInGlobalInvocationId,        {4236, 4237, 4238}},
-    {SpvBuiltInLocalInvocationId,         {4281, 4282, 4283}},
-    {SpvBuiltInNumWorkgroups,             {4296, 4297, 4298}},
-    {SpvBuiltInNumSubgroups,              {4293, 4294, 4295}},
-    {SpvBuiltInSubgroupId,                {4367, 4368, 4369}},
-    {SpvBuiltInWorkgroupId,               {4422, 4423, 4424}},
-    {SpvBuiltInHitKindKHR,                {4242, 4243, 4244}},
-    {SpvBuiltInHitTNV,                    {4245, 4246, 4247}},
-    {SpvBuiltInInstanceCustomIndexKHR,    {4251, 4252, 4253}},
-    {SpvBuiltInInstanceId,                {4254, 4255, 4256}},
-    {SpvBuiltInRayGeometryIndexKHR,       {4345, 4346, 4347}},
-    {SpvBuiltInObjectRayDirectionKHR,     {4299, 4300, 4301}},
-    {SpvBuiltInObjectRayOriginKHR,        {4302, 4303, 4304}},
-    {SpvBuiltInObjectToWorldKHR,          {4305, 4306, 4307}},
-    {SpvBuiltInWorldToObjectKHR,          {4434, 4435, 4436}},
-    {SpvBuiltInIncomingRayFlagsKHR,       {4248, 4249, 4250}},
-    {SpvBuiltInRayTminKHR,                {4351, 4352, 4353}},
-    {SpvBuiltInRayTmaxKHR,                {4348, 4349, 4350}},
-    {SpvBuiltInWorldRayDirectionKHR,      {4428, 4429, 4430}},
-    {SpvBuiltInWorldRayOriginKHR,         {4431, 4432, 4433}},
-    {SpvBuiltInLaunchIdKHR,               {4266, 4267, 4268}},
-    {SpvBuiltInLaunchSizeKHR,             {4269, 4270, 4271}},
-    {SpvBuiltInFragInvocationCountEXT,    {4217, 4218, 4219}},
-    {SpvBuiltInFragSizeEXT,               {4220, 4221, 4222}},
-    {SpvBuiltInFragStencilRefEXT,         {4223, 4224, 4225}},
-    {SpvBuiltInFullyCoveredEXT,           {4232, 4233, 4234}},
-    {SpvBuiltInCullMaskKHR,               {6735, 6736, 6737}},
-    {SpvBuiltInBaryCoordKHR,              {4154, 4155, 4156}},
-    {SpvBuiltInBaryCoordNoPerspKHR,       {4160, 4161, 4162}},
+    {spv::BuiltIn::SubgroupEqMask,            {0,    4370, 4371}},
+    {spv::BuiltIn::SubgroupGeMask,            {0,    4372, 4373}},
+    {spv::BuiltIn::SubgroupGtMask,            {0,    4374, 4375}},
+    {spv::BuiltIn::SubgroupLeMask,            {0,    4376, 4377}},
+    {spv::BuiltIn::SubgroupLtMask,            {0,    4378, 4379}},
+    {spv::BuiltIn::SubgroupLocalInvocationId, {0,    4380, 4381}},
+    {spv::BuiltIn::SubgroupSize,              {0,    4382, 4383}},
+    {spv::BuiltIn::GlobalInvocationId,        {4236, 4237, 4238}},
+    {spv::BuiltIn::LocalInvocationId,         {4281, 4282, 4283}},
+    {spv::BuiltIn::NumWorkgroups,             {4296, 4297, 4298}},
+    {spv::BuiltIn::NumSubgroups,              {4293, 4294, 4295}},
+    {spv::BuiltIn::SubgroupId,                {4367, 4368, 4369}},
+    {spv::BuiltIn::WorkgroupId,               {4422, 4423, 4424}},
+    {spv::BuiltIn::HitKindKHR,                {4242, 4243, 4244}},
+    {spv::BuiltIn::HitTNV,                    {4245, 4246, 4247}},
+    {spv::BuiltIn::InstanceCustomIndexKHR,    {4251, 4252, 4253}},
+    {spv::BuiltIn::InstanceId,                {4254, 4255, 4256}},
+    {spv::BuiltIn::RayGeometryIndexKHR,       {4345, 4346, 4347}},
+    {spv::BuiltIn::ObjectRayDirectionKHR,     {4299, 4300, 4301}},
+    {spv::BuiltIn::ObjectRayOriginKHR,        {4302, 4303, 4304}},
+    {spv::BuiltIn::ObjectToWorldKHR,          {4305, 4306, 4307}},
+    {spv::BuiltIn::WorldToObjectKHR,          {4434, 4435, 4436}},
+    {spv::BuiltIn::IncomingRayFlagsKHR,       {4248, 4249, 4250}},
+    {spv::BuiltIn::RayTminKHR,                {4351, 4352, 4353}},
+    {spv::BuiltIn::RayTmaxKHR,                {4348, 4349, 4350}},
+    {spv::BuiltIn::WorldRayDirectionKHR,      {4428, 4429, 4430}},
+    {spv::BuiltIn::WorldRayOriginKHR,         {4431, 4432, 4433}},
+    {spv::BuiltIn::LaunchIdKHR,               {4266, 4267, 4268}},
+    {spv::BuiltIn::LaunchSizeKHR,             {4269, 4270, 4271}},
+    {spv::BuiltIn::FragInvocationCountEXT,    {4217, 4218, 4219}},
+    {spv::BuiltIn::FragSizeEXT,               {4220, 4221, 4222}},
+    {spv::BuiltIn::FragStencilRefEXT,         {4223, 4224, 4225}},
+    {spv::BuiltIn::FullyCoveredEXT,           {4232, 4233, 4234}},
+    {spv::BuiltIn::CullMaskKHR,               {6735, 6736, 6737}},
+    {spv::BuiltIn::BaryCoordKHR,              {4154, 4155, 4156}},
+    {spv::BuiltIn::BaryCoordNoPerspKHR,       {4160, 4161, 4162}},
     // clang-format off
 } };
 
-uint32_t GetVUIDForBuiltin(SpvBuiltIn builtIn, VUIDError type) {
+uint32_t GetVUIDForBuiltin(spv::BuiltIn builtIn, VUIDError type) {
   uint32_t vuid = 0;
   for (const auto& iter: builtinVUIDInfo) {
     if (iter.builtIn == builtIn) {
@@ -180,57 +178,57 @@
   return vuid;
 }
 
-bool IsExecutionModelValidForRtBuiltIn(SpvBuiltIn builtin,
-                                       SpvExecutionModel stage) {
+bool IsExecutionModelValidForRtBuiltIn(spv::BuiltIn builtin,
+                                       spv::ExecutionModel stage) {
   switch (builtin) {
-    case SpvBuiltInHitKindKHR:
-    case SpvBuiltInHitTNV:
-      if (stage == SpvExecutionModelAnyHitKHR ||
-          stage == SpvExecutionModelClosestHitKHR) {
+    case spv::BuiltIn::HitKindKHR:
+    case spv::BuiltIn::HitTNV:
+      if (stage == spv::ExecutionModel::AnyHitKHR ||
+          stage == spv::ExecutionModel::ClosestHitKHR) {
         return true;
       }
       break;
-    case SpvBuiltInInstanceCustomIndexKHR:
-    case SpvBuiltInInstanceId:
-    case SpvBuiltInRayGeometryIndexKHR:
-    case SpvBuiltInObjectRayDirectionKHR:
-    case SpvBuiltInObjectRayOriginKHR:
-    case SpvBuiltInObjectToWorldKHR:
-    case SpvBuiltInWorldToObjectKHR:
+    case spv::BuiltIn::InstanceCustomIndexKHR:
+    case spv::BuiltIn::InstanceId:
+    case spv::BuiltIn::RayGeometryIndexKHR:
+    case spv::BuiltIn::ObjectRayDirectionKHR:
+    case spv::BuiltIn::ObjectRayOriginKHR:
+    case spv::BuiltIn::ObjectToWorldKHR:
+    case spv::BuiltIn::WorldToObjectKHR:
       switch (stage) {
-        case SpvExecutionModelIntersectionKHR:
-        case SpvExecutionModelAnyHitKHR:
-        case SpvExecutionModelClosestHitKHR:
+        case spv::ExecutionModel::IntersectionKHR:
+        case spv::ExecutionModel::AnyHitKHR:
+        case spv::ExecutionModel::ClosestHitKHR:
           return true;
         default:
           return false;
       }
       break;
-    case SpvBuiltInIncomingRayFlagsKHR:
-    case SpvBuiltInRayTminKHR:
-    case SpvBuiltInRayTmaxKHR:
-    case SpvBuiltInWorldRayDirectionKHR:
-    case SpvBuiltInWorldRayOriginKHR:
-    case SpvBuiltInCullMaskKHR:
+    case spv::BuiltIn::IncomingRayFlagsKHR:
+    case spv::BuiltIn::RayTminKHR:
+    case spv::BuiltIn::RayTmaxKHR:
+    case spv::BuiltIn::WorldRayDirectionKHR:
+    case spv::BuiltIn::WorldRayOriginKHR:
+    case spv::BuiltIn::CullMaskKHR:
       switch (stage) {
-        case SpvExecutionModelIntersectionKHR:
-        case SpvExecutionModelAnyHitKHR:
-        case SpvExecutionModelClosestHitKHR:
-        case SpvExecutionModelMissKHR:
+        case spv::ExecutionModel::IntersectionKHR:
+        case spv::ExecutionModel::AnyHitKHR:
+        case spv::ExecutionModel::ClosestHitKHR:
+        case spv::ExecutionModel::MissKHR:
           return true;
         default:
           return false;
       }
       break;
-    case SpvBuiltInLaunchIdKHR:
-    case SpvBuiltInLaunchSizeKHR:
+    case spv::BuiltIn::LaunchIdKHR:
+    case spv::BuiltIn::LaunchSizeKHR:
       switch (stage) {
-        case SpvExecutionModelRayGenerationKHR:
-        case SpvExecutionModelIntersectionKHR:
-        case SpvExecutionModelAnyHitKHR:
-        case SpvExecutionModelClosestHitKHR:
-        case SpvExecutionModelMissKHR:
-        case SpvExecutionModelCallableKHR:
+        case spv::ExecutionModel::RayGenerationKHR:
+        case spv::ExecutionModel::IntersectionKHR:
+        case spv::ExecutionModel::AnyHitKHR:
+        case spv::ExecutionModel::ClosestHitKHR:
+        case spv::ExecutionModel::MissKHR:
+        case spv::ExecutionModel::CallableKHR:
           return true;
         default:
           return false;
@@ -559,7 +557,7 @@
   // |referenced_from_inst| - instruction which references id defined by
   //                          |referenced_inst| from within a function.
   spv_result_t ValidateNotCalledWithExecutionModel(
-      int vuid, const char* comment, SpvExecutionModel execution_model,
+      int vuid, const char* comment, spv::ExecutionModel execution_model,
       const Decoration& decoration, const Instruction& built_in_inst,
       const Instruction& referenced_inst,
       const Instruction& referenced_from_inst);
@@ -642,7 +640,7 @@
       const Decoration& decoration, const Instruction& built_in_inst,
       const Instruction& referenced_inst,
       const Instruction& referenced_from_inst,
-      SpvExecutionModel execution_model = SpvExecutionModelMax) const;
+      spv::ExecutionModel execution_model = spv::ExecutionModel::Max) const;
 
   // Generates strings like "ID <51> (OpTypePointer) uses storage class
   // UniformConstant".
@@ -671,12 +669,12 @@
   const std::vector<uint32_t>* entry_points_ = &no_entry_points;
 
   // Execution models with which the current function can be called.
-  std::set<SpvExecutionModel> execution_models_;
+  std::set<spv::ExecutionModel> execution_models_;
 };
 
 void BuiltInsValidator::Update(const Instruction& inst) {
-  const SpvOp opcode = inst.opcode();
-  if (opcode == SpvOpFunction) {
+  const spv::Op opcode = inst.opcode();
+  if (opcode == spv::Op::OpFunction) {
     // Entering a function.
     assert(function_id_ == 0);
     function_id_ = inst.id();
@@ -691,7 +689,7 @@
     }
   }
 
-  if (opcode == SpvOpFunctionEnd) {
+  if (opcode == spv::Op::OpFunctionEnd) {
     // Exiting a function.
     assert(function_id_ != 0);
     function_id_ = 0;
@@ -704,7 +702,7 @@
     const Decoration& decoration, const Instruction& inst) const {
   std::ostringstream ss;
   if (decoration.struct_member_index() != Decoration::kInvalidMember) {
-    assert(inst.opcode() == SpvOpTypeStruct);
+    assert(inst.opcode() == spv::Op::OpTypeStruct);
     ss << "Member #" << decoration.struct_member_index();
     ss << " of struct ID <" << inst.id() << ">";
   } else {
@@ -716,7 +714,7 @@
 std::string BuiltInsValidator::GetReferenceDesc(
     const Decoration& decoration, const Instruction& built_in_inst,
     const Instruction& referenced_inst, const Instruction& referenced_from_inst,
-    SpvExecutionModel execution_model) const {
+    spv::ExecutionModel execution_model) const {
   std::ostringstream ss;
   ss << GetIdDesc(referenced_from_inst) << " is referencing "
      << GetIdDesc(referenced_inst);
@@ -729,10 +727,10 @@
                                       decoration.params()[0]);
   if (function_id_) {
     ss << " in function <" << function_id_ << ">";
-    if (execution_model != SpvExecutionModelMax) {
+    if (execution_model != spv::ExecutionModel::Max) {
       ss << " called with execution model ";
       ss << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_EXECUTION_MODEL,
-                                          execution_model);
+                                          uint32_t(execution_model));
     }
   }
   ss << ".";
@@ -744,7 +742,7 @@
   std::ostringstream ss;
   ss << GetIdDesc(inst) << " uses storage class ";
   ss << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_STORAGE_CLASS,
-                                      GetStorageClass(inst));
+                                      uint32_t(GetStorageClass(inst)));
   ss << ".";
   return ss.str();
 }
@@ -803,7 +801,7 @@
   }
 
   // Strip the array, if present.
-  if (_.GetIdOpcode(underlying_type) == SpvOpTypeArray) {
+  if (_.GetIdOpcode(underlying_type) == spv::Op::OpTypeArray) {
     underlying_type = _.FindDef(underlying_type)->word(2u);
   }
 
@@ -839,7 +837,7 @@
   }
 
   // Strip the array, if present.
-  if (_.GetIdOpcode(underlying_type) == SpvOpTypeArray) {
+  if (_.GetIdOpcode(underlying_type) == spv::Op::OpTypeArray) {
     underlying_type = _.FindDef(underlying_type)->word(2u);
   }
 
@@ -922,7 +920,7 @@
   }
 
   // Strip the array, if present.
-  if (_.GetIdOpcode(underlying_type) == SpvOpTypeArray) {
+  if (_.GetIdOpcode(underlying_type) == spv::Op::OpTypeArray) {
     underlying_type = _.FindDef(underlying_type)->word(2u);
   }
 
@@ -983,7 +981,7 @@
   }
 
   const Instruction* const type_inst = _.FindDef(underlying_type);
-  if (type_inst->opcode() != SpvOpTypeArray) {
+  if (type_inst->opcode() != spv::Op::OpTypeArray) {
     return diag(GetDefinitionDesc(decoration, inst) + " is not an array.");
   }
 
@@ -1029,9 +1027,9 @@
   }
 
   // Strip an extra layer of arraying if present.
-  if (_.GetIdOpcode(underlying_type) == SpvOpTypeArray) {
+  if (_.GetIdOpcode(underlying_type) == spv::Op::OpTypeArray) {
     uint32_t subtype = _.FindDef(underlying_type)->word(2u);
-    if (_.GetIdOpcode(subtype) == SpvOpTypeArray) {
+    if (_.GetIdOpcode(subtype) == spv::Op::OpTypeArray) {
       underlying_type = subtype;
     }
   }
@@ -1046,7 +1044,7 @@
     const std::function<spv_result_t(const std::string& message)>& diag,
     uint32_t underlying_type) {
   const Instruction* const type_inst = _.FindDef(underlying_type);
-  if (type_inst->opcode() != SpvOpTypeArray) {
+  if (type_inst->opcode() != spv::Op::OpTypeArray) {
     return diag(GetDefinitionDesc(decoration, inst) + " is not an array.");
   }
 
@@ -1107,14 +1105,14 @@
 }
 
 spv_result_t BuiltInsValidator::ValidateNotCalledWithExecutionModel(
-    int vuid, const char* comment, SpvExecutionModel execution_model,
+    int vuid, const char* comment, spv::ExecutionModel execution_model,
     const Decoration& decoration, const Instruction& built_in_inst,
     const Instruction& referenced_inst,
     const Instruction& referenced_from_inst) {
   if (function_id_) {
     if (execution_models_.count(execution_model)) {
       const char* execution_model_str = _.grammar().lookupOperandName(
-          SPV_OPERAND_TYPE_EXECUTION_MODEL, execution_model);
+          SPV_OPERAND_TYPE_EXECUTION_MODEL, uint32_t(execution_model));
       const char* built_in_str = _.grammar().lookupOperandName(
           SPV_OPERAND_TYPE_BUILT_IN, decoration.params()[0]);
       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
@@ -1149,11 +1147,11 @@
     const Instruction& referenced_from_inst) {
   uint32_t operand = decoration.params()[0];
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
-    if (storage_class != SpvStorageClassMax &&
-        storage_class != SpvStorageClassInput &&
-        storage_class != SpvStorageClassOutput) {
-      uint32_t vuid = (decoration.params()[0] == SpvBuiltInClipDistance) ? 4190 : 4199;
+    const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
+    if (storage_class != spv::StorageClass::Max &&
+        storage_class != spv::StorageClass::Input &&
+        storage_class != spv::StorageClass::Output) {
+      uint32_t vuid = (spv::BuiltIn(decoration.params()[0]) == spv::BuiltIn::ClipDistance) ? 4190 : 4199;
       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
              << _.VkErrorID(vuid) << "Vulkan spec allows BuiltIn "
              << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
@@ -1165,54 +1163,54 @@
              << " " << GetStorageClassDesc(referenced_from_inst);
     }
 
-    if (storage_class == SpvStorageClassInput) {
+    if (storage_class == spv::StorageClass::Input) {
       assert(function_id_ == 0);
-      uint32_t vuid = (decoration.params()[0] == SpvBuiltInClipDistance) ? 4188 : 4197;
+      uint32_t vuid = (spv::BuiltIn(decoration.params()[0]) == spv::BuiltIn::ClipDistance) ? 4188 : 4197;
       id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
           &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, vuid,
           "Vulkan spec doesn't allow BuiltIn ClipDistance/CullDistance to be "
           "used for variables with Input storage class if execution model is "
           "Vertex.",
-          SpvExecutionModelVertex, decoration, built_in_inst,
+          spv::ExecutionModel::Vertex, decoration, built_in_inst,
           referenced_from_inst, std::placeholders::_1));
       id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
           &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, vuid,
           "Vulkan spec doesn't allow BuiltIn ClipDistance/CullDistance to be "
           "used for variables with Input storage class if execution model is "
           "MeshNV.",
-          SpvExecutionModelMeshNV, decoration, built_in_inst,
+          spv::ExecutionModel::MeshNV, decoration, built_in_inst,
           referenced_from_inst, std::placeholders::_1));
       id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
           &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, vuid,
           "Vulkan spec doesn't allow BuiltIn ClipDistance/CullDistance to be "
           "used for variables with Input storage class if execution model is "
           "MeshEXT.",
-          SpvExecutionModelMeshEXT, decoration, built_in_inst,
+          spv::ExecutionModel::MeshEXT, decoration, built_in_inst,
           referenced_from_inst, std::placeholders::_1));
     }
 
-    if (storage_class == SpvStorageClassOutput) {
+    if (storage_class == spv::StorageClass::Output) {
       assert(function_id_ == 0);
-      uint32_t vuid = (decoration.params()[0] == SpvBuiltInClipDistance) ? 4189 : 4198;
+      uint32_t vuid = (spv::BuiltIn(decoration.params()[0]) == spv::BuiltIn::ClipDistance) ? 4189 : 4198;
       id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
           &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, vuid,
           "Vulkan spec doesn't allow BuiltIn ClipDistance/CullDistance to be "
           "used for variables with Output storage class if execution model is "
           "Fragment.",
-          SpvExecutionModelFragment, decoration, built_in_inst,
+          spv::ExecutionModel::Fragment, decoration, built_in_inst,
           referenced_from_inst, std::placeholders::_1));
     }
 
-    for (const SpvExecutionModel execution_model : execution_models_) {
+    for (const spv::ExecutionModel execution_model : execution_models_) {
       switch (execution_model) {
-        case SpvExecutionModelFragment:
-        case SpvExecutionModelVertex: {
+        case spv::ExecutionModel::Fragment:
+        case spv::ExecutionModel::Vertex: {
           if (spv_result_t error = ValidateF32Arr(
                   decoration, built_in_inst, /* Any number of components */ 0,
                   [this, &decoration, &referenced_from_inst](
                       const std::string& message) -> spv_result_t {
                     uint32_t vuid =
-                        (decoration.params()[0] == SpvBuiltInClipDistance)
+                        (spv::BuiltIn(decoration.params()[0]) == spv::BuiltIn::ClipDistance)
                             ? 4191
                             : 4200;
                     return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
@@ -1228,11 +1226,11 @@
           }
           break;
         }
-        case SpvExecutionModelTessellationControl:
-        case SpvExecutionModelTessellationEvaluation:
-        case SpvExecutionModelGeometry:
-        case SpvExecutionModelMeshNV:
-        case SpvExecutionModelMeshEXT: {
+        case spv::ExecutionModel::TessellationControl:
+        case spv::ExecutionModel::TessellationEvaluation:
+        case spv::ExecutionModel::Geometry:
+        case spv::ExecutionModel::MeshNV:
+        case spv::ExecutionModel::MeshEXT: {
           if (decoration.struct_member_index() != Decoration::kInvalidMember) {
             // The outer level of array is applied on the variable.
             if (spv_result_t error = ValidateF32Arr(
@@ -1240,7 +1238,7 @@
                     [this, &decoration, &referenced_from_inst](
                         const std::string& message) -> spv_result_t {
                       uint32_t vuid =
-                          (decoration.params()[0] == SpvBuiltInClipDistance)
+                          (spv::BuiltIn(decoration.params()[0]) == spv::BuiltIn::ClipDistance)
                               ? 4191
                               : 4200;
                       return _.diag(SPV_ERROR_INVALID_DATA,
@@ -1261,7 +1259,7 @@
                     [this, &decoration, &referenced_from_inst](
                         const std::string& message) -> spv_result_t {
                       uint32_t vuid =
-                          (decoration.params()[0] == SpvBuiltInClipDistance)
+                          (spv::BuiltIn(decoration.params()[0]) == spv::BuiltIn::ClipDistance)
                               ? 4191
                               : 4200;
                       return _.diag(SPV_ERROR_INVALID_DATA,
@@ -1282,7 +1280,7 @@
 
         default: {
           uint32_t vuid =
-              (decoration.params()[0] == SpvBuiltInClipDistance) ? 4187 : 4196;
+              (spv::BuiltIn(decoration.params()[0]) == spv::BuiltIn::ClipDistance) ? 4187 : 4196;
           return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
                  << _.VkErrorID(vuid) << "Vulkan spec allows BuiltIn "
                  << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
@@ -1335,9 +1333,9 @@
     const Instruction& referenced_inst,
     const Instruction& referenced_from_inst) {
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
-    if (storage_class != SpvStorageClassMax &&
-        storage_class != SpvStorageClassInput) {
+    const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
+    if (storage_class != spv::StorageClass::Max &&
+        storage_class != spv::StorageClass::Input) {
       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
              << _.VkErrorID(4211) << spvLogStringForEnv(_.context()->target_env)
              << " spec allows BuiltIn FragCoord to be only used for "
@@ -1347,8 +1345,8 @@
              << " " << GetStorageClassDesc(referenced_from_inst);
     }
 
-    for (const SpvExecutionModel execution_model : execution_models_) {
-      if (execution_model != SpvExecutionModelFragment) {
+    for (const spv::ExecutionModel execution_model : execution_models_) {
+      if (execution_model != spv::ExecutionModel::Fragment) {
         return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
                << _.VkErrorID(4210)
                << spvLogStringForEnv(_.context()->target_env)
@@ -1396,9 +1394,9 @@
     const Instruction& referenced_inst,
     const Instruction& referenced_from_inst) {
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
-    if (storage_class != SpvStorageClassMax &&
-        storage_class != SpvStorageClassOutput) {
+    const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
+    if (storage_class != spv::StorageClass::Max &&
+        storage_class != spv::StorageClass::Output) {
       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
              << _.VkErrorID(4214) << spvLogStringForEnv(_.context()->target_env)
              << " spec allows BuiltIn FragDepth to be only used for "
@@ -1408,8 +1406,8 @@
              << " " << GetStorageClassDesc(referenced_from_inst);
     }
 
-    for (const SpvExecutionModel execution_model : execution_models_) {
-      if (execution_model != SpvExecutionModelFragment) {
+    for (const spv::ExecutionModel execution_model : execution_models_) {
+      if (execution_model != spv::ExecutionModel::Fragment) {
         return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
                << _.VkErrorID(4213)
                << spvLogStringForEnv(_.context()->target_env)
@@ -1424,7 +1422,7 @@
       // Every entry point from which this function is called needs to have
       // Execution Mode DepthReplacing.
       const auto* modes = _.GetExecutionModes(entry_point);
-      if (!modes || !modes->count(SpvExecutionModeDepthReplacing)) {
+      if (!modes || !modes->count(spv::ExecutionMode::DepthReplacing)) {
         return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
                << _.VkErrorID(4216)
                << spvLogStringForEnv(_.context()->target_env)
@@ -1472,9 +1470,9 @@
     const Instruction& referenced_inst,
     const Instruction& referenced_from_inst) {
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
-    if (storage_class != SpvStorageClassMax &&
-        storage_class != SpvStorageClassInput) {
+    const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
+    if (storage_class != spv::StorageClass::Max &&
+        storage_class != spv::StorageClass::Input) {
       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
              << _.VkErrorID(4230) << spvLogStringForEnv(_.context()->target_env)
              << " spec allows BuiltIn FrontFacing to be only used for "
@@ -1484,8 +1482,8 @@
              << " " << GetStorageClassDesc(referenced_from_inst);
     }
 
-    for (const SpvExecutionModel execution_model : execution_models_) {
-      if (execution_model != SpvExecutionModelFragment) {
+    for (const spv::ExecutionModel execution_model : execution_models_) {
+      if (execution_model != spv::ExecutionModel::Fragment) {
         return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
                << _.VkErrorID(4229)
                << spvLogStringForEnv(_.context()->target_env)
@@ -1532,9 +1530,9 @@
     const Instruction& referenced_inst,
     const Instruction& referenced_from_inst) {
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
-    if (storage_class != SpvStorageClassMax &&
-        storage_class != SpvStorageClassInput) {
+    const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
+    if (storage_class != spv::StorageClass::Max &&
+        storage_class != spv::StorageClass::Input) {
       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
              << _.VkErrorID(4240)
              << "Vulkan spec allows BuiltIn HelperInvocation to be only used "
@@ -1544,8 +1542,8 @@
              << " " << GetStorageClassDesc(referenced_from_inst);
     }
 
-    for (const SpvExecutionModel execution_model : execution_models_) {
-      if (execution_model != SpvExecutionModelFragment) {
+    for (const spv::ExecutionModel execution_model : execution_models_) {
+      if (execution_model != spv::ExecutionModel::Fragment) {
         return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
                << _.VkErrorID(4239)
                << "Vulkan spec allows BuiltIn HelperInvocation to be used only "
@@ -1592,9 +1590,9 @@
     const Instruction& referenced_inst,
     const Instruction& referenced_from_inst) {
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
-    if (storage_class != SpvStorageClassMax &&
-        storage_class != SpvStorageClassInput) {
+    const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
+    if (storage_class != spv::StorageClass::Max &&
+        storage_class != spv::StorageClass::Input) {
       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
              << _.VkErrorID(4258)
              << "Vulkan spec allows BuiltIn InvocationId to be only used for "
@@ -1604,9 +1602,9 @@
              << " " << GetStorageClassDesc(referenced_from_inst);
     }
 
-    for (const SpvExecutionModel execution_model : execution_models_) {
-      if (execution_model != SpvExecutionModelTessellationControl &&
-          execution_model != SpvExecutionModelGeometry) {
+    for (const spv::ExecutionModel execution_model : execution_models_) {
+      if (execution_model != spv::ExecutionModel::TessellationControl &&
+          execution_model != spv::ExecutionModel::Geometry) {
         return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
                << _.VkErrorID(4257)
                << "Vulkan spec allows BuiltIn InvocationId to be used only "
@@ -1653,9 +1651,9 @@
     const Instruction& referenced_inst,
     const Instruction& referenced_from_inst) {
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
-    if (storage_class != SpvStorageClassMax &&
-        storage_class != SpvStorageClassInput) {
+    const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
+    if (storage_class != spv::StorageClass::Max &&
+        storage_class != spv::StorageClass::Input) {
       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
              << _.VkErrorID(4264) << spvLogStringForEnv(_.context()->target_env)
              << " spec allows BuiltIn InstanceIndex to be only used for "
@@ -1665,8 +1663,8 @@
              << " " << GetStorageClassDesc(referenced_from_inst);
     }
 
-    for (const SpvExecutionModel execution_model : execution_models_) {
-      if (execution_model != SpvExecutionModelVertex) {
+    for (const spv::ExecutionModel execution_model : execution_models_) {
+      if (execution_model != spv::ExecutionModel::Vertex) {
         return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
                << _.VkErrorID(4263)
                << spvLogStringForEnv(_.context()->target_env)
@@ -1713,9 +1711,9 @@
     const Instruction& referenced_inst,
     const Instruction& referenced_from_inst) {
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
-    if (storage_class != SpvStorageClassMax &&
-        storage_class != SpvStorageClassInput) {
+    const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
+    if (storage_class != spv::StorageClass::Max &&
+        storage_class != spv::StorageClass::Input) {
       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
              << _.VkErrorID(4309)
              << "Vulkan spec allows BuiltIn PatchVertices to be only used for "
@@ -1725,9 +1723,9 @@
              << " " << GetStorageClassDesc(referenced_from_inst);
     }
 
-    for (const SpvExecutionModel execution_model : execution_models_) {
-      if (execution_model != SpvExecutionModelTessellationControl &&
-          execution_model != SpvExecutionModelTessellationEvaluation) {
+    for (const spv::ExecutionModel execution_model : execution_models_) {
+      if (execution_model != spv::ExecutionModel::TessellationControl &&
+          execution_model != spv::ExecutionModel::TessellationEvaluation) {
         return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
                << _.VkErrorID(4308)
                << "Vulkan spec allows BuiltIn PatchVertices to be used only "
@@ -1775,9 +1773,9 @@
     const Instruction& referenced_inst,
     const Instruction& referenced_from_inst) {
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
-    if (storage_class != SpvStorageClassMax &&
-        storage_class != SpvStorageClassInput) {
+    const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
+    if (storage_class != spv::StorageClass::Max &&
+        storage_class != spv::StorageClass::Input) {
       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
              << _.VkErrorID(4312)
              << "Vulkan spec allows BuiltIn PointCoord to be only used for "
@@ -1787,8 +1785,8 @@
              << " " << GetStorageClassDesc(referenced_from_inst);
     }
 
-    for (const SpvExecutionModel execution_model : execution_models_) {
-      if (execution_model != SpvExecutionModelFragment) {
+    for (const spv::ExecutionModel execution_model : execution_models_) {
+      if (execution_model != spv::ExecutionModel::Fragment) {
         return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
                << _.VkErrorID(4311)
                << "Vulkan spec allows BuiltIn PointCoord to be used only with "
@@ -1820,10 +1818,10 @@
     const Instruction& referenced_inst,
     const Instruction& referenced_from_inst) {
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
-    if (storage_class != SpvStorageClassMax &&
-        storage_class != SpvStorageClassInput &&
-        storage_class != SpvStorageClassOutput) {
+    const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
+    if (storage_class != spv::StorageClass::Max &&
+        storage_class != spv::StorageClass::Input &&
+        storage_class != spv::StorageClass::Output) {
       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
              << _.VkErrorID(4316)
              << "Vulkan spec allows BuiltIn PointSize to be only used for "
@@ -1833,20 +1831,20 @@
              << " " << GetStorageClassDesc(referenced_from_inst);
     }
 
-    if (storage_class == SpvStorageClassInput) {
+    if (storage_class == spv::StorageClass::Input) {
       assert(function_id_ == 0);
       id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
           &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, 4315,
           "Vulkan spec doesn't allow BuiltIn PointSize to be used for "
           "variables with Input storage class if execution model is "
           "Vertex.",
-          SpvExecutionModelVertex, decoration, built_in_inst,
+          spv::ExecutionModel::Vertex, decoration, built_in_inst,
           referenced_from_inst, std::placeholders::_1));
     }
 
-    for (const SpvExecutionModel execution_model : execution_models_) {
+    for (const spv::ExecutionModel execution_model : execution_models_) {
       switch (execution_model) {
-        case SpvExecutionModelVertex: {
+        case spv::ExecutionModel::Vertex: {
           if (spv_result_t error = ValidateF32(
                   decoration, built_in_inst,
                   [this, &referenced_from_inst](
@@ -1861,11 +1859,11 @@
           }
           break;
         }
-        case SpvExecutionModelTessellationControl:
-        case SpvExecutionModelTessellationEvaluation:
-        case SpvExecutionModelGeometry:
-        case SpvExecutionModelMeshNV:
-        case SpvExecutionModelMeshEXT: {
+        case spv::ExecutionModel::TessellationControl:
+        case spv::ExecutionModel::TessellationEvaluation:
+        case spv::ExecutionModel::Geometry:
+        case spv::ExecutionModel::MeshNV:
+        case spv::ExecutionModel::MeshEXT: {
           // PointSize can be a per-vertex variable for tessellation control,
           // tessellation evaluation and geometry shader stages. In such cases
           // variables will have an array of 32-bit floats.
@@ -1938,10 +1936,10 @@
     const Instruction& referenced_inst,
     const Instruction& referenced_from_inst) {
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
-    if (storage_class != SpvStorageClassMax &&
-        storage_class != SpvStorageClassInput &&
-        storage_class != SpvStorageClassOutput) {
+    const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
+    if (storage_class != spv::StorageClass::Max &&
+        storage_class != spv::StorageClass::Input &&
+        storage_class != spv::StorageClass::Output) {
       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
              << _.VkErrorID(4320) << "Vulkan spec allows BuiltIn Position to be only used for "
                 "variables with Input or Output storage class. "
@@ -1950,34 +1948,34 @@
              << " " << GetStorageClassDesc(referenced_from_inst);
     }
 
-    if (storage_class == SpvStorageClassInput) {
+    if (storage_class == spv::StorageClass::Input) {
       assert(function_id_ == 0);
       id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
           &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, 4319,
           "Vulkan spec doesn't allow BuiltIn Position to be used "
           "for variables "
           "with Input storage class if execution model is Vertex.",
-          SpvExecutionModelVertex, decoration, built_in_inst,
+          spv::ExecutionModel::Vertex, decoration, built_in_inst,
           referenced_from_inst, std::placeholders::_1));
       id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
           &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, 4319,
           "Vulkan spec doesn't allow BuiltIn Position to be used "
           "for variables "
           "with Input storage class if execution model is MeshNV.",
-          SpvExecutionModelMeshNV, decoration, built_in_inst,
+          spv::ExecutionModel::MeshNV, decoration, built_in_inst,
           referenced_from_inst, std::placeholders::_1));
       id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
           &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, 4319,
           "Vulkan spec doesn't allow BuiltIn Position to be used "
           "for variables "
           "with Input storage class if execution model is MeshEXT.",
-          SpvExecutionModelMeshEXT, decoration, built_in_inst,
+          spv::ExecutionModel::MeshEXT, decoration, built_in_inst,
           referenced_from_inst, std::placeholders::_1));
     }
 
-    for (const SpvExecutionModel execution_model : execution_models_) {
+    for (const spv::ExecutionModel execution_model : execution_models_) {
       switch (execution_model) {
-        case SpvExecutionModelVertex: {
+        case spv::ExecutionModel::Vertex: {
           if (spv_result_t error = ValidateF32Vec(
                   decoration, built_in_inst, 4,
                   [this, &referenced_from_inst](
@@ -1993,11 +1991,11 @@
           }
           break;
         }
-        case SpvExecutionModelGeometry:
-        case SpvExecutionModelTessellationControl:
-        case SpvExecutionModelTessellationEvaluation:
-        case SpvExecutionModelMeshNV:
-        case SpvExecutionModelMeshEXT: {
+        case spv::ExecutionModel::Geometry:
+        case spv::ExecutionModel::TessellationControl:
+        case spv::ExecutionModel::TessellationEvaluation:
+        case spv::ExecutionModel::MeshNV:
+        case spv::ExecutionModel::MeshEXT: {
           // Position can be a per-vertex variable for tessellation control,
           // tessellation evaluation, geometry and mesh shader stages. In such
           // cases variables will have an array of 4-component 32-bit float
@@ -2103,10 +2101,10 @@
     const Instruction& referenced_inst,
     const Instruction& referenced_from_inst) {
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
-    if (storage_class != SpvStorageClassMax &&
-        storage_class != SpvStorageClassInput &&
-        storage_class != SpvStorageClassOutput) {
+    const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
+    if (storage_class != spv::StorageClass::Max &&
+        storage_class != spv::StorageClass::Input &&
+        storage_class != spv::StorageClass::Output) {
       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
              << "Vulkan spec allows BuiltIn PrimitiveId to be only used for "
                 "variables with Input or Output storage class. "
@@ -2115,63 +2113,63 @@
              << " " << GetStorageClassDesc(referenced_from_inst);
     }
 
-    if (storage_class == SpvStorageClassOutput) {
+    if (storage_class == spv::StorageClass::Output) {
       assert(function_id_ == 0);
       id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
           &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, 4334,
           "Vulkan spec doesn't allow BuiltIn PrimitiveId to be used for "
           "variables with Output storage class if execution model is "
           "TessellationControl.",
-          SpvExecutionModelTessellationControl, decoration, built_in_inst,
+          spv::ExecutionModel::TessellationControl, decoration, built_in_inst,
           referenced_from_inst, std::placeholders::_1));
       id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
           &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, 4334,
           "Vulkan spec doesn't allow BuiltIn PrimitiveId to be used for "
           "variables with Output storage class if execution model is "
           "TessellationEvaluation.",
-          SpvExecutionModelTessellationEvaluation, decoration, built_in_inst,
+          spv::ExecutionModel::TessellationEvaluation, decoration, built_in_inst,
           referenced_from_inst, std::placeholders::_1));
       id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
           &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, 4334,
           "Vulkan spec doesn't allow BuiltIn PrimitiveId to be used for "
           "variables with Output storage class if execution model is "
           "Fragment.",
-          SpvExecutionModelFragment, decoration, built_in_inst,
+          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::ValidateNotCalledWithExecutionModel, this, 4334,
           "Vulkan spec doesn't allow BuiltIn PrimitiveId to be used for "
           "variables with Output storage class if execution model is "
           "IntersectionKHR.",
-          SpvExecutionModelIntersectionKHR, decoration, built_in_inst,
+          spv::ExecutionModel::IntersectionKHR, decoration, built_in_inst,
           referenced_from_inst, std::placeholders::_1));
       id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
           &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, 4334,
           "Vulkan spec doesn't allow BuiltIn PrimitiveId to be used for "
           "variables with Output storage class if execution model is "
           "AnyHitKHR.",
-          SpvExecutionModelAnyHitKHR, decoration, built_in_inst,
+          spv::ExecutionModel::AnyHitKHR, decoration, built_in_inst,
           referenced_from_inst, std::placeholders::_1));
       id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
           &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, 4334,
           "Vulkan spec doesn't allow BuiltIn PrimitiveId to be used for "
           "variables with Output storage class if execution model is "
           "ClosestHitKHR.",
-          SpvExecutionModelClosestHitKHR, decoration, built_in_inst,
+          spv::ExecutionModel::ClosestHitKHR, decoration, built_in_inst,
           referenced_from_inst, std::placeholders::_1));
     }
 
-    for (const SpvExecutionModel execution_model : execution_models_) {
+    for (const spv::ExecutionModel execution_model : execution_models_) {
       switch (execution_model) {
-        case SpvExecutionModelFragment:
-        case SpvExecutionModelTessellationControl:
-        case SpvExecutionModelTessellationEvaluation:
-        case SpvExecutionModelGeometry:
-        case SpvExecutionModelMeshNV:
-        case SpvExecutionModelMeshEXT:
-        case SpvExecutionModelIntersectionKHR:
-        case SpvExecutionModelAnyHitKHR:
-        case SpvExecutionModelClosestHitKHR: {
+        case spv::ExecutionModel::Fragment:
+        case spv::ExecutionModel::TessellationControl:
+        case spv::ExecutionModel::TessellationEvaluation:
+        case spv::ExecutionModel::Geometry:
+        case spv::ExecutionModel::MeshNV:
+        case spv::ExecutionModel::MeshEXT:
+        case spv::ExecutionModel::IntersectionKHR:
+        case spv::ExecutionModel::AnyHitKHR:
+        case spv::ExecutionModel::ClosestHitKHR: {
           // Ok.
           break;
         }
@@ -2225,9 +2223,9 @@
     const Instruction& referenced_inst,
     const Instruction& referenced_from_inst) {
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
-    if (storage_class != SpvStorageClassMax &&
-        storage_class != SpvStorageClassInput) {
+    const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
+    if (storage_class != spv::StorageClass::Max &&
+        storage_class != spv::StorageClass::Input) {
       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
              << _.VkErrorID(4355)
              << "Vulkan spec allows BuiltIn SampleId to be only used for "
@@ -2237,8 +2235,8 @@
              << " " << GetStorageClassDesc(referenced_from_inst);
     }
 
-    for (const SpvExecutionModel execution_model : execution_models_) {
-      if (execution_model != SpvExecutionModelFragment) {
+    for (const spv::ExecutionModel execution_model : execution_models_) {
+      if (execution_model != spv::ExecutionModel::Fragment) {
         return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
                << _.VkErrorID(4354)
                << "Vulkan spec allows BuiltIn SampleId to be used only with "
@@ -2284,10 +2282,10 @@
     const Instruction& referenced_inst,
     const Instruction& referenced_from_inst) {
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
-    if (storage_class != SpvStorageClassMax &&
-        storage_class != SpvStorageClassInput &&
-        storage_class != SpvStorageClassOutput) {
+    const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
+    if (storage_class != spv::StorageClass::Max &&
+        storage_class != spv::StorageClass::Input &&
+        storage_class != spv::StorageClass::Output) {
       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
              << _.VkErrorID(4358)
              << "Vulkan spec allows BuiltIn SampleMask to be only used for "
@@ -2297,8 +2295,8 @@
              << " " << GetStorageClassDesc(referenced_from_inst);
     }
 
-    for (const SpvExecutionModel execution_model : execution_models_) {
-      if (execution_model != SpvExecutionModelFragment) {
+    for (const spv::ExecutionModel execution_model : execution_models_) {
+      if (execution_model != spv::ExecutionModel::Fragment) {
         return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
                << _.VkErrorID(4357)
                << "Vulkan spec allows BuiltIn SampleMask to be used only "
@@ -2346,9 +2344,9 @@
     const Instruction& referenced_inst,
     const Instruction& referenced_from_inst) {
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
-    if (storage_class != SpvStorageClassMax &&
-        storage_class != SpvStorageClassInput) {
+    const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
+    if (storage_class != spv::StorageClass::Max &&
+        storage_class != spv::StorageClass::Input) {
       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
              << _.VkErrorID(4361)
              << "Vulkan spec allows BuiltIn SamplePosition to be only used "
@@ -2359,8 +2357,8 @@
              << " " << GetStorageClassDesc(referenced_from_inst);
     }
 
-    for (const SpvExecutionModel execution_model : execution_models_) {
-      if (execution_model != SpvExecutionModelFragment) {
+    for (const spv::ExecutionModel execution_model : execution_models_) {
+      if (execution_model != spv::ExecutionModel::Fragment) {
         return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
                << _.VkErrorID(4360)
                << "Vulkan spec allows BuiltIn SamplePosition to be used only "
@@ -2408,9 +2406,9 @@
     const Instruction& referenced_inst,
     const Instruction& referenced_from_inst) {
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
-    if (storage_class != SpvStorageClassMax &&
-        storage_class != SpvStorageClassInput) {
+    const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
+    if (storage_class != spv::StorageClass::Max &&
+        storage_class != spv::StorageClass::Input) {
       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
              << _.VkErrorID(4388)
              << "Vulkan spec allows BuiltIn TessCoord to be only used for "
@@ -2420,8 +2418,8 @@
              << " " << GetStorageClassDesc(referenced_from_inst);
     }
 
-    for (const SpvExecutionModel execution_model : execution_models_) {
-      if (execution_model != SpvExecutionModelTessellationEvaluation) {
+    for (const spv::ExecutionModel execution_model : execution_models_) {
+      if (execution_model != spv::ExecutionModel::TessellationEvaluation) {
         return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
                << _.VkErrorID(4387)
                << "Vulkan spec allows BuiltIn TessCoord to be used only with "
@@ -2490,10 +2488,10 @@
     const Instruction& referenced_from_inst) {
   uint32_t operand = decoration.params()[0];
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
-    if (storage_class != SpvStorageClassMax &&
-        storage_class != SpvStorageClassInput &&
-        storage_class != SpvStorageClassOutput) {
+    const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
+    if (storage_class != spv::StorageClass::Max &&
+        storage_class != spv::StorageClass::Input &&
+        storage_class != spv::StorageClass::Output) {
       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
              << "Vulkan spec allows BuiltIn "
              << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
@@ -2505,42 +2503,42 @@
              << " " << GetStorageClassDesc(referenced_from_inst);
     }
 
-    if (storage_class == SpvStorageClassInput) {
+    if (storage_class == spv::StorageClass::Input) {
       assert(function_id_ == 0);
-      uint32_t vuid = (decoration.params()[0] == SpvBuiltInTessLevelOuter) ? 4391 : 4395;
+      uint32_t vuid = (spv::BuiltIn(decoration.params()[0]) == spv::BuiltIn::TessLevelOuter) ? 4391 : 4395;
       id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
           &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, vuid,
           "Vulkan spec doesn't allow TessLevelOuter/TessLevelInner to be "
           "used "
           "for variables with Input storage class if execution model is "
           "TessellationControl.",
-          SpvExecutionModelTessellationControl, decoration, built_in_inst,
+          spv::ExecutionModel::TessellationControl, decoration, built_in_inst,
           referenced_from_inst, std::placeholders::_1));
     }
 
-    if (storage_class == SpvStorageClassOutput) {
+    if (storage_class == spv::StorageClass::Output) {
       assert(function_id_ == 0);
-      uint32_t vuid = (decoration.params()[0] == SpvBuiltInTessLevelOuter) ? 4392 : 4396;
+      uint32_t vuid = (spv::BuiltIn(decoration.params()[0]) == spv::BuiltIn::TessLevelOuter) ? 4392 : 4396;
       id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
           &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, vuid,
           "Vulkan spec doesn't allow TessLevelOuter/TessLevelInner to be "
           "used "
           "for variables with Output storage class if execution model is "
           "TessellationEvaluation.",
-          SpvExecutionModelTessellationEvaluation, decoration, built_in_inst,
+          spv::ExecutionModel::TessellationEvaluation, decoration, built_in_inst,
           referenced_from_inst, std::placeholders::_1));
     }
 
-    for (const SpvExecutionModel execution_model : execution_models_) {
+    for (const spv::ExecutionModel execution_model : execution_models_) {
       switch (execution_model) {
-        case SpvExecutionModelTessellationControl:
-        case SpvExecutionModelTessellationEvaluation: {
+        case spv::ExecutionModel::TessellationControl:
+        case spv::ExecutionModel::TessellationEvaluation: {
           // Ok.
           break;
         }
 
         default: {
-          uint32_t vuid = (operand == SpvBuiltInTessLevelOuter) ? 4390 : 4394;
+          uint32_t vuid = (spv::BuiltIn(operand) == spv::BuiltIn::TessLevelOuter) ? 4390 : 4394;
           return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
                  << _.VkErrorID(vuid) << "Vulkan spec allows BuiltIn "
                  << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
@@ -2623,9 +2621,9 @@
     const Instruction& referenced_inst,
     const Instruction& referenced_from_inst) {
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
-    if (storage_class != SpvStorageClassMax &&
-        storage_class != SpvStorageClassInput) {
+    const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
+    if (storage_class != spv::StorageClass::Max &&
+        storage_class != spv::StorageClass::Input) {
       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
              << _.VkErrorID(4399) << spvLogStringForEnv(_.context()->target_env)
              << " spec allows BuiltIn VertexIndex to be only used for "
@@ -2635,8 +2633,8 @@
              << " " << GetStorageClassDesc(referenced_from_inst);
     }
 
-    for (const SpvExecutionModel execution_model : execution_models_) {
-      if (execution_model != SpvExecutionModelVertex) {
+    for (const spv::ExecutionModel execution_model : execution_models_) {
+      if (execution_model != spv::ExecutionModel::Vertex) {
         return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
                << _.VkErrorID(4398)
                << spvLogStringForEnv(_.context()->target_env)
@@ -2670,7 +2668,7 @@
               [this, &decoration,
                &inst](const std::string& message) -> spv_result_t {
                 uint32_t vuid =
-                    (decoration.params()[0] == SpvBuiltInLayer) ? 4276 : 4408;
+                    (spv::BuiltIn(decoration.params()[0]) == spv::BuiltIn::Layer) ? 4276 : 4408;
                 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
                        << _.VkErrorID(vuid)
                        << "According to the Vulkan spec BuiltIn "
@@ -2687,7 +2685,7 @@
               [this, &decoration,
                &inst](const std::string& message) -> spv_result_t {
                 uint32_t vuid =
-                    (decoration.params()[0] == SpvBuiltInLayer) ? 4276 : 4408;
+                    (spv::BuiltIn(decoration.params()[0]) == spv::BuiltIn::Layer) ? 4276 : 4408;
                 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
                        << _.VkErrorID(vuid)
                        << "According to the Vulkan spec BuiltIn "
@@ -2711,10 +2709,10 @@
     const Instruction& referenced_from_inst) {
   uint32_t operand = decoration.params()[0];
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
-    if (storage_class != SpvStorageClassMax &&
-        storage_class != SpvStorageClassInput &&
-        storage_class != SpvStorageClassOutput) {
+    const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
+    if (storage_class != spv::StorageClass::Max &&
+        storage_class != spv::StorageClass::Input &&
+        storage_class != spv::StorageClass::Output) {
       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
              << "Vulkan spec allows BuiltIn "
              << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
@@ -2726,15 +2724,15 @@
              << " " << GetStorageClassDesc(referenced_from_inst);
     }
 
-    if (storage_class == SpvStorageClassInput) {
+    if (storage_class == spv::StorageClass::Input) {
       assert(function_id_ == 0);
       for (const auto em :
-           {SpvExecutionModelVertex, SpvExecutionModelTessellationEvaluation,
-            SpvExecutionModelGeometry, SpvExecutionModelMeshNV,
-            SpvExecutionModelMeshEXT}) {
+           {spv::ExecutionModel::Vertex, spv::ExecutionModel::TessellationEvaluation,
+            spv::ExecutionModel::Geometry, spv::ExecutionModel::MeshNV,
+            spv::ExecutionModel::MeshEXT}) {
         id_to_at_reference_checks_[referenced_from_inst.id()].push_back(
             std::bind(&BuiltInsValidator::ValidateNotCalledWithExecutionModel,
-                      this, ((operand == SpvBuiltInLayer) ? 4274 : 4406),
+                      this, ((spv::BuiltIn(operand) == spv::BuiltIn::Layer) ? 4274 : 4406),
                       "Vulkan spec doesn't allow BuiltIn Layer and "
                       "ViewportIndex to be "
                       "used for variables with Input storage class if "
@@ -2745,46 +2743,46 @@
       }
     }
 
-    if (storage_class == SpvStorageClassOutput) {
+    if (storage_class == spv::StorageClass::Output) {
       assert(function_id_ == 0);
       id_to_at_reference_checks_[referenced_from_inst.id()].push_back(
           std::bind(&BuiltInsValidator::ValidateNotCalledWithExecutionModel,
-                    this, ((operand == SpvBuiltInLayer) ? 4275 : 4407),
+                    this, ((spv::BuiltIn(operand) == spv::BuiltIn::Layer) ? 4275 : 4407),
                     "Vulkan spec doesn't allow BuiltIn Layer and "
                     "ViewportIndex to be "
                     "used for variables with Output storage class if "
                     "execution model is "
                     "Fragment.",
-                    SpvExecutionModelFragment, decoration, built_in_inst,
+                    spv::ExecutionModel::Fragment, decoration, built_in_inst,
                     referenced_from_inst, std::placeholders::_1));
     }
 
-    for (const SpvExecutionModel execution_model : execution_models_) {
+    for (const spv::ExecutionModel execution_model : execution_models_) {
       switch (execution_model) {
-        case SpvExecutionModelGeometry:
-        case SpvExecutionModelFragment:
-        case SpvExecutionModelMeshNV:
-        case SpvExecutionModelMeshEXT:
+        case spv::ExecutionModel::Geometry:
+        case spv::ExecutionModel::Fragment:
+        case spv::ExecutionModel::MeshNV:
+        case spv::ExecutionModel::MeshEXT:
           // Ok.
           break;
-        case SpvExecutionModelVertex:
-        case SpvExecutionModelTessellationEvaluation: {
-          if (!_.HasCapability(SpvCapabilityShaderViewportIndexLayerEXT)) {
-            if (operand == SpvBuiltInViewportIndex &&
-                _.HasCapability(SpvCapabilityShaderViewportIndex))
+        case spv::ExecutionModel::Vertex:
+        case spv::ExecutionModel::TessellationEvaluation: {
+          if (!_.HasCapability(spv::Capability::ShaderViewportIndexLayerEXT)) {
+            if (spv::BuiltIn(operand) == spv::BuiltIn::ViewportIndex &&
+                _.HasCapability(spv::Capability::ShaderViewportIndex))
               break;  // Ok
-            if (operand == SpvBuiltInLayer &&
-                _.HasCapability(SpvCapabilityShaderLayer))
+            if (spv::BuiltIn(operand) == spv::BuiltIn::Layer &&
+                _.HasCapability(spv::Capability::ShaderLayer))
               break;  // Ok
 
             const char* capability = "ShaderViewportIndexLayerEXT";
 
-            if (operand == SpvBuiltInViewportIndex)
+            if (spv::BuiltIn(operand) == spv::BuiltIn::ViewportIndex)
               capability = "ShaderViewportIndexLayerEXT or ShaderViewportIndex";
-            if (operand == SpvBuiltInLayer)
+            if (spv::BuiltIn(operand) == spv::BuiltIn::Layer)
               capability = "ShaderViewportIndexLayerEXT or ShaderLayer";
 
-            uint32_t vuid = (operand == SpvBuiltInLayer) ? 4273 : 4405;
+            uint32_t vuid = (spv::BuiltIn(operand) == spv::BuiltIn::Layer) ? 4273 : 4405;
             return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
                    << _.VkErrorID(vuid) << "Using BuiltIn "
                    << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
@@ -2795,7 +2793,7 @@
           break;
         }
         default: {
-          uint32_t vuid = (operand == SpvBuiltInLayer) ? 4272 : 4404;
+          uint32_t vuid = (spv::BuiltIn(operand) == spv::BuiltIn::Layer) ? 4272 : 4404;
           return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
                  << _.VkErrorID(vuid) << "Vulkan spec allows BuiltIn "
                  << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
@@ -2823,7 +2821,7 @@
 spv_result_t BuiltInsValidator::ValidateFragmentShaderF32Vec3InputAtDefinition(
     const Decoration& decoration, const Instruction& inst) {
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]);
+    const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]);
     if (spv_result_t error = ValidateF32Vec(
             decoration, inst, 3,
             [this, &inst, builtin](const std::string& message) -> spv_result_t {
@@ -2833,7 +2831,7 @@
                      << spvLogStringForEnv(_.context()->target_env)
                      << " spec BuiltIn "
                      << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
-                                                      builtin)
+                                                      uint32_t(builtin))
                      << " variable needs to be a 3-component 32-bit float "
                         "vector. "
                      << message;
@@ -2853,29 +2851,29 @@
     const Instruction& referenced_from_inst) {
 
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]);
-    const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
-    if (storage_class != SpvStorageClassMax &&
-        storage_class != SpvStorageClassInput) {
+    const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]);
+    const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
+    if (storage_class != spv::StorageClass::Max &&
+        storage_class != spv::StorageClass::Input) {
       uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass);
       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
              << _.VkErrorID(vuid) << spvLogStringForEnv(_.context()->target_env)
              << " spec allows BuiltIn "
-             << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin)
+             << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
              << " 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 SpvExecutionModel execution_model : execution_models_) {
-      if (execution_model != SpvExecutionModelFragment) {
+    for (const spv::ExecutionModel execution_model : execution_models_) {
+      if (execution_model != spv::ExecutionModel::Fragment) {
         uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorExecutionModel);
         return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
                << _.VkErrorID(vuid)
                << spvLogStringForEnv(_.context()->target_env)
                << " spec allows BuiltIn "
-               << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin)
+               << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
                << " to be used only with Fragment execution model. "
                << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
                                    referenced_from_inst, execution_model);
@@ -2897,7 +2895,7 @@
 spv_result_t BuiltInsValidator::ValidateComputeShaderI32Vec3InputAtDefinition(
     const Decoration& decoration, const Instruction& inst) {
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]);
+    const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]);
     if (spv_result_t error = ValidateI32Vec(
             decoration, inst, 3,
             [this, &inst, builtin](const std::string& message) -> spv_result_t {
@@ -2907,7 +2905,7 @@
                      << spvLogStringForEnv(_.context()->target_env)
                      << " spec BuiltIn "
                      << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
-                                                      builtin)
+                                                      uint32_t(builtin))
                      << " variable needs to be a 3-component 32-bit int "
                         "vector. "
                      << message;
@@ -2926,27 +2924,27 @@
     const Instruction& referenced_inst,
     const Instruction& referenced_from_inst) {
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]);
-    const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
-    if (storage_class != SpvStorageClassMax &&
-        storage_class != SpvStorageClassInput) {
+    const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]);
+    const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
+    if (storage_class != spv::StorageClass::Max &&
+        storage_class != spv::StorageClass::Input) {
       uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass);
       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
              << _.VkErrorID(vuid) << spvLogStringForEnv(_.context()->target_env)
              << " spec allows BuiltIn "
-             << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin)
+             << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
              << " 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 SpvExecutionModel execution_model : execution_models_) {
-      bool has_vulkan_model = execution_model == SpvExecutionModelGLCompute ||
-                              execution_model == SpvExecutionModelTaskNV ||
-                              execution_model == SpvExecutionModelMeshNV ||
-                              execution_model == SpvExecutionModelTaskEXT ||
-                              execution_model == SpvExecutionModelMeshEXT;
+    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(builtin, VUIDErrorExecutionModel);
@@ -2954,7 +2952,7 @@
                << _.VkErrorID(vuid)
                << spvLogStringForEnv(_.context()->target_env)
                << " spec allows BuiltIn "
-               << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin)
+               << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
                << " to be used only with GLCompute, MeshNV, TaskNV, MeshEXT or"
                << " TaskEXT execution model. "
                << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
@@ -2977,11 +2975,11 @@
 spv_result_t BuiltInsValidator::ValidateComputeI32InputAtDefinition(
     const Decoration& decoration, const Instruction& inst) {
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]);
+    const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]);
     if (decoration.struct_member_index() != Decoration::kInvalidMember) {
       return _.diag(SPV_ERROR_INVALID_DATA, &inst)
              << "BuiltIn "
-             << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin)
+             << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
              << " cannot be used as a member decoration ";
     }
     if (spv_result_t error = ValidateI32(
@@ -2993,7 +2991,7 @@
                      << "According to the "
                      << spvLogStringForEnv(_.context()->target_env)
                      << " spec BuiltIn "
-                     << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin)
+                     << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
                      << " variable needs to be a 32-bit int "
                         "vector. "
                      << message;
@@ -3011,35 +3009,35 @@
     const Instruction& referenced_inst,
     const Instruction& referenced_from_inst) {
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]);
-    const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
-    if (storage_class != SpvStorageClassMax &&
-        storage_class != SpvStorageClassInput) {
+    const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]);
+    const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
+    if (storage_class != spv::StorageClass::Max &&
+        storage_class != spv::StorageClass::Input) {
       uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass);
       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
              << _.VkErrorID(vuid)
              << spvLogStringForEnv(_.context()->target_env)
              << " spec allows BuiltIn "
-             << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin)
+             << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
              << " 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 SpvExecutionModel execution_model : execution_models_) {
-      bool has_vulkan_model = execution_model == SpvExecutionModelGLCompute ||
-                              execution_model == SpvExecutionModelTaskNV ||
-                              execution_model == SpvExecutionModelMeshNV ||
-                              execution_model == SpvExecutionModelTaskEXT ||
-                              execution_model == SpvExecutionModelMeshEXT;
+    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(builtin, VUIDErrorExecutionModel);
         return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
                << _.VkErrorID(vuid)
                << spvLogStringForEnv(_.context()->target_env)
                << " spec allows BuiltIn "
-               << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin)
+               << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
                << " to be used only with GLCompute, MeshNV, TaskNV, MeshEXT or "
                << "TaskEXT execution model. "
                << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
@@ -3062,11 +3060,11 @@
 spv_result_t BuiltInsValidator::ValidateI32InputAtDefinition(
     const Decoration& decoration, const Instruction& inst) {
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]);
+    const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]);
     if (decoration.struct_member_index() != Decoration::kInvalidMember) {
       return _.diag(SPV_ERROR_INVALID_DATA, &inst)
              << "BuiltIn "
-             << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin)
+             << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
              << " cannot be used as a member decoration ";
     }
     if (spv_result_t error = ValidateI32(
@@ -3078,21 +3076,21 @@
                      << "According to the "
                      << spvLogStringForEnv(_.context()->target_env)
                      << " spec BuiltIn "
-                     << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin)
+                     << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
                      << " variable needs to be a 32-bit int. " << message;
             })) {
       return error;
     }
 
-    const SpvStorageClass storage_class = GetStorageClass(inst);
-    if (storage_class != SpvStorageClassMax &&
-        storage_class != SpvStorageClassInput) {
+    const spv::StorageClass storage_class = GetStorageClass(inst);
+    if (storage_class != spv::StorageClass::Max &&
+        storage_class != spv::StorageClass::Input) {
       uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass);
       return _.diag(SPV_ERROR_INVALID_DATA, &inst)
              << _.VkErrorID(vuid)
              << spvLogStringForEnv(_.context()->target_env)
              << " spec allows BuiltIn "
-             << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin)
+             << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
              << " to be only used for variables with Input storage class. "
              << GetReferenceDesc(decoration, inst, inst, inst) << " "
              << GetStorageClassDesc(inst);
@@ -3105,11 +3103,11 @@
 spv_result_t BuiltInsValidator::ValidateI32Vec4InputAtDefinition(
     const Decoration& decoration, const Instruction& inst) {
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]);
+    const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]);
     if (decoration.struct_member_index() != Decoration::kInvalidMember) {
       return _.diag(SPV_ERROR_INVALID_DATA, &inst)
              << "BuiltIn "
-             << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin)
+             << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
              << " cannot be used as a member decoration ";
     }
     if (spv_result_t error = ValidateI32Vec(
@@ -3121,7 +3119,7 @@
                      << "According to the "
                      << spvLogStringForEnv(_.context()->target_env)
                      << " spec BuiltIn "
-                     << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin)
+                     << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
                      << " variable needs to be a 4-component 32-bit int "
                         "vector. "
                      << message;
@@ -3129,15 +3127,15 @@
       return error;
     }
 
-    const SpvStorageClass storage_class = GetStorageClass(inst);
-    if (storage_class != SpvStorageClassMax &&
-        storage_class != SpvStorageClassInput) {
+    const spv::StorageClass storage_class = GetStorageClass(inst);
+    if (storage_class != spv::StorageClass::Max &&
+        storage_class != spv::StorageClass::Input) {
       uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass);
       return _.diag(SPV_ERROR_INVALID_DATA, &inst)
              << _.VkErrorID(vuid)
              << spvLogStringForEnv(_.context()->target_env)
              << " spec allows BuiltIn "
-             << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin)
+             << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
              << " to be only used for variables with Input storage class. "
              << GetReferenceDesc(decoration, inst, inst, inst) << " "
              << GetStorageClassDesc(inst);
@@ -3182,12 +3180,12 @@
     const Instruction& referenced_inst,
     const Instruction& referenced_from_inst) {
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    for (const SpvExecutionModel execution_model : execution_models_) {
-      if (execution_model != SpvExecutionModelGLCompute &&
-          execution_model != SpvExecutionModelTaskNV &&
-          execution_model != SpvExecutionModelMeshNV &&
-          execution_model != SpvExecutionModelTaskEXT &&
-          execution_model != SpvExecutionModelMeshEXT) {
+    for (const spv::ExecutionModel execution_model : execution_models_) {
+      if (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) {
         return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
                << _.VkErrorID(4425)
                << spvLogStringForEnv(_.context()->target_env)
@@ -3219,7 +3217,7 @@
             decoration, inst,
             [this, &inst,
              &decoration](const std::string& message) -> spv_result_t {
-              uint32_t vuid = (decoration.params()[0] == SpvBuiltInBaseInstance)
+              uint32_t vuid = (spv::BuiltIn(decoration.params()[0]) == spv::BuiltIn::BaseInstance)
                                   ? 4183
                                   : 4186;
               return _.diag(SPV_ERROR_INVALID_DATA, &inst)
@@ -3243,10 +3241,10 @@
     const Instruction& referenced_from_inst) {
   uint32_t operand = decoration.params()[0];
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
-    if (storage_class != SpvStorageClassMax &&
-        storage_class != SpvStorageClassInput) {
-      uint32_t vuid = (operand == SpvBuiltInBaseInstance) ? 4182 : 4185;
+    const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
+    if (storage_class != spv::StorageClass::Max &&
+        storage_class != spv::StorageClass::Input) {
+      uint32_t vuid = (spv::BuiltIn(operand) == spv::BuiltIn::BaseInstance) ? 4182 : 4185;
       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
              << _.VkErrorID(vuid) << "Vulkan spec allows BuiltIn "
              << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
@@ -3257,9 +3255,9 @@
              << " " << GetStorageClassDesc(referenced_from_inst);
     }
 
-    for (const SpvExecutionModel execution_model : execution_models_) {
-      if (execution_model != SpvExecutionModelVertex) {
-        uint32_t vuid = (operand == SpvBuiltInBaseInstance) ? 4181 : 4184;
+    for (const spv::ExecutionModel execution_model : execution_models_) {
+      if (execution_model != spv::ExecutionModel::Vertex) {
+        uint32_t vuid = (spv::BuiltIn(operand) == spv::BuiltIn::BaseInstance) ? 4181 : 4184;
         return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
                << _.VkErrorID(vuid) << "Vulkan spec allows BuiltIn "
                << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
@@ -3310,9 +3308,9 @@
     const Instruction& referenced_from_inst) {
   uint32_t operand = decoration.params()[0];
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
-    if (storage_class != SpvStorageClassMax &&
-        storage_class != SpvStorageClassInput) {
+    const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
+    if (storage_class != spv::StorageClass::Max &&
+        storage_class != spv::StorageClass::Input) {
       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
              << _.VkErrorID(4208) << "Vulkan spec allows BuiltIn "
              << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
@@ -3323,12 +3321,12 @@
              << " " << GetStorageClassDesc(referenced_from_inst);
     }
 
-    for (const SpvExecutionModel execution_model : execution_models_) {
-      if (execution_model != SpvExecutionModelVertex &&
-          execution_model != SpvExecutionModelMeshNV &&
-          execution_model != SpvExecutionModelTaskNV &&
-          execution_model != SpvExecutionModelMeshEXT &&
-          execution_model != SpvExecutionModelTaskEXT) {
+    for (const spv::ExecutionModel execution_model : execution_models_) {
+      if (execution_model != spv::ExecutionModel::Vertex &&
+          execution_model != spv::ExecutionModel::MeshNV &&
+          execution_model != spv::ExecutionModel::TaskNV &&
+          execution_model != spv::ExecutionModel::MeshEXT &&
+          execution_model != spv::ExecutionModel::TaskEXT) {
         return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
                << _.VkErrorID(4207) << "Vulkan spec allows BuiltIn "
                << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
@@ -3380,9 +3378,9 @@
     const Instruction& referenced_from_inst) {
   uint32_t operand = decoration.params()[0];
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
-    if (storage_class != SpvStorageClassMax &&
-        storage_class != SpvStorageClassInput) {
+    const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
+    if (storage_class != spv::StorageClass::Max &&
+        storage_class != spv::StorageClass::Input) {
       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
              << _.VkErrorID(4402) << "Vulkan spec allows BuiltIn "
              << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
@@ -3393,8 +3391,8 @@
              << " " << GetStorageClassDesc(referenced_from_inst);
     }
 
-    for (const SpvExecutionModel execution_model : execution_models_) {
-      if (execution_model == SpvExecutionModelGLCompute) {
+    for (const spv::ExecutionModel execution_model : execution_models_) {
+      if (execution_model == spv::ExecutionModel::GLCompute) {
         return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
                << _.VkErrorID(4401) << "Vulkan spec allows BuiltIn "
                << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
@@ -3444,9 +3442,9 @@
     const Instruction& referenced_from_inst) {
   uint32_t operand = decoration.params()[0];
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
-    if (storage_class != SpvStorageClassMax &&
-        storage_class != SpvStorageClassInput) {
+    const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
+    if (storage_class != spv::StorageClass::Max &&
+        storage_class != spv::StorageClass::Input) {
       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
              << _.VkErrorID(4205) << "Vulkan spec allows BuiltIn "
              << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
@@ -3472,7 +3470,7 @@
                                             const Instruction& inst) {
 
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]);
+    const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]);
     if (spv_result_t error = ValidateI32(
             decoration, inst,
             [this, &inst, &builtin](const std::string& message) -> spv_result_t {
@@ -3482,7 +3480,7 @@
                      << spvLogStringForEnv(_.context()->target_env)
                      << " spec BuiltIn "
                      << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
-                                                      builtin)
+                                                      uint32_t(builtin))
                      << " variable needs to be a 32-bit int scalar. "
                      << message;
             })) {
@@ -3499,29 +3497,29 @@
     const Instruction& referenced_from_inst) {
 
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]);
-    const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
-    if (storage_class != SpvStorageClassMax &&
-        storage_class != SpvStorageClassInput) {
+    const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]);
+    const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
+    if (storage_class != spv::StorageClass::Max &&
+        storage_class != spv::StorageClass::Input) {
       uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass);
       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
              << _.VkErrorID(vuid) << spvLogStringForEnv(_.context()->target_env)
              << " spec allows BuiltIn "
-             << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin)
+             << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
              << " 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 SpvExecutionModel execution_model : execution_models_) {
-      if (execution_model != SpvExecutionModelFragment) {
+    for (const spv::ExecutionModel execution_model : execution_models_) {
+      if (execution_model != spv::ExecutionModel::Fragment) {
         uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorExecutionModel);
         return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
                << _.VkErrorID(vuid)
                << spvLogStringForEnv(_.context()->target_env)
                << " spec allows BuiltIn "
-               << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin)
+               << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
                << " to be used only with Fragment execution model. "
                << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
                                    referenced_from_inst, execution_model);
@@ -3542,7 +3540,7 @@
 spv_result_t BuiltInsValidator::ValidateFragSizeAtDefinition(const Decoration& decoration,
                                             const Instruction& inst) {
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]);
+    const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]);
     if (spv_result_t error = ValidateI32Vec(
             decoration, inst, 2,
             [this, &inst, &builtin](const std::string& message) -> spv_result_t {
@@ -3552,7 +3550,7 @@
                      << spvLogStringForEnv(_.context()->target_env)
                      << " spec BuiltIn "
                      << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
-                                                      builtin)
+                                                      uint32_t(builtin))
                      << " variable needs to be a 2-component 32-bit int vector. "
                      << message;
             })) {
@@ -3569,29 +3567,29 @@
     const Instruction& referenced_from_inst) {
 
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]);
-    const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
-    if (storage_class != SpvStorageClassMax &&
-        storage_class != SpvStorageClassInput) {
+    const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]);
+    const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
+    if (storage_class != spv::StorageClass::Max &&
+        storage_class != spv::StorageClass::Input) {
       uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass);
       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
              << _.VkErrorID(vuid) << spvLogStringForEnv(_.context()->target_env)
              << " spec allows BuiltIn "
-             << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin)
+             << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
              << " 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 SpvExecutionModel execution_model : execution_models_) {
-      if (execution_model != SpvExecutionModelFragment) {
+    for (const spv::ExecutionModel execution_model : execution_models_) {
+      if (execution_model != spv::ExecutionModel::Fragment) {
         uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorExecutionModel);
         return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
                << _.VkErrorID(vuid)
                << spvLogStringForEnv(_.context()->target_env)
                << " spec allows BuiltIn "
-               << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin)
+               << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
                << " to be used only with Fragment execution model. "
                << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
                                    referenced_from_inst, execution_model);
@@ -3612,7 +3610,7 @@
 spv_result_t BuiltInsValidator::ValidateFragStencilRefAtDefinition(const Decoration& decoration,
                                             const Instruction& inst) {
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]);
+    const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]);
     if (spv_result_t error = ValidateI(
             decoration, inst,
             [this, &inst, &builtin](const std::string& message) -> spv_result_t {
@@ -3622,7 +3620,7 @@
                      << spvLogStringForEnv(_.context()->target_env)
                      << " spec BuiltIn "
                      << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
-                                                      builtin)
+                                                      uint32_t(builtin))
                      << " variable needs to be a int scalar. "
                      << message;
             })) {
@@ -3639,29 +3637,29 @@
     const Instruction& referenced_from_inst) {
 
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]);
-    const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
-    if (storage_class != SpvStorageClassMax &&
-        storage_class != SpvStorageClassOutput) {
+    const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]);
+    const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
+    if (storage_class != spv::StorageClass::Max &&
+        storage_class != spv::StorageClass::Output) {
       uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass);
       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
              << _.VkErrorID(vuid) << spvLogStringForEnv(_.context()->target_env)
              << " spec allows BuiltIn "
-             << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin)
+             << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
              << " to be only used for variables with Output storage class. "
              << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
                                  referenced_from_inst)
              << " " << GetStorageClassDesc(referenced_from_inst);
     }
 
-    for (const SpvExecutionModel execution_model : execution_models_) {
-      if (execution_model != SpvExecutionModelFragment) {
+    for (const spv::ExecutionModel execution_model : execution_models_) {
+      if (execution_model != spv::ExecutionModel::Fragment) {
         uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorExecutionModel);
         return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
                << _.VkErrorID(vuid)
                << spvLogStringForEnv(_.context()->target_env)
                << " spec allows BuiltIn "
-               << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin)
+               << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
                << " to be used only with Fragment execution model. "
                << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
                                    referenced_from_inst, execution_model);
@@ -3682,7 +3680,7 @@
 spv_result_t BuiltInsValidator::ValidateFullyCoveredAtDefinition(const Decoration& decoration,
                                                const Instruction& inst) {
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]);
+    const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]);
     if (spv_result_t error = ValidateBool(
             decoration, inst,
             [this, &inst, &builtin](const std::string& message) -> spv_result_t {
@@ -3692,7 +3690,7 @@
                      << spvLogStringForEnv(_.context()->target_env)
                      << " spec BuiltIn "
                      << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
-                                                      builtin)
+                                                      uint32_t(builtin))
                      << " variable needs to be a bool scalar. "
                      << message;
             })) {
@@ -3709,29 +3707,29 @@
     const Instruction& referenced_from_inst) {
 
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]);
-    const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
-    if (storage_class != SpvStorageClassMax &&
-        storage_class != SpvStorageClassInput) {
+    const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]);
+    const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
+    if (storage_class != spv::StorageClass::Max &&
+        storage_class != spv::StorageClass::Input) {
       uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass);
       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
              << _.VkErrorID(vuid) << spvLogStringForEnv(_.context()->target_env)
              << " spec allows BuiltIn "
-             << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin)
+             << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
              << " 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 SpvExecutionModel execution_model : execution_models_) {
-      if (execution_model != SpvExecutionModelFragment) {
+    for (const spv::ExecutionModel execution_model : execution_models_) {
+      if (execution_model != spv::ExecutionModel::Fragment) {
         uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorExecutionModel);
         return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
                << _.VkErrorID(vuid)
                << spvLogStringForEnv(_.context()->target_env)
                << " spec allows BuiltIn "
-               << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin)
+               << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
                << " to be used only with Fragment execution model. "
                << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
                                    referenced_from_inst, execution_model);
@@ -3778,9 +3776,9 @@
     const Instruction& referenced_inst,
     const Instruction& referenced_from_inst) {
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
-    if (storage_class != SpvStorageClassMax &&
-        storage_class != SpvStorageClassInput) {
+    const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
+    if (storage_class != spv::StorageClass::Max &&
+        storage_class != spv::StorageClass::Input) {
       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
              << spvLogStringForEnv(_.context()->target_env)
              << " spec allows BuiltIn "
@@ -3832,9 +3830,9 @@
     const Instruction& referenced_inst,
     const Instruction& referenced_from_inst) {
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
-    if (storage_class != SpvStorageClassMax &&
-        storage_class != SpvStorageClassOutput) {
+    const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
+    if (storage_class != spv::StorageClass::Max &&
+        storage_class != spv::StorageClass::Output) {
       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
              << _.VkErrorID(4485) << "Vulkan spec allows BuiltIn "
              << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
@@ -3845,12 +3843,12 @@
              << " " << GetStorageClassDesc(referenced_from_inst);
     }
 
-    for (const SpvExecutionModel execution_model : execution_models_) {
+    for (const spv::ExecutionModel execution_model : execution_models_) {
       switch (execution_model) {
-        case SpvExecutionModelVertex:
-        case SpvExecutionModelGeometry:
-        case SpvExecutionModelMeshNV:
-        case SpvExecutionModelMeshEXT:
+        case spv::ExecutionModel::Vertex:
+        case spv::ExecutionModel::Geometry:
+        case spv::ExecutionModel::MeshNV:
+        case spv::ExecutionModel::MeshEXT:
           break;
         default: {
           return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
@@ -3905,9 +3903,9 @@
     const Instruction& referenced_inst,
     const Instruction& referenced_from_inst) {
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
-    if (storage_class != SpvStorageClassMax &&
-        storage_class != SpvStorageClassInput) {
+    const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
+    if (storage_class != spv::StorageClass::Max &&
+        storage_class != spv::StorageClass::Input) {
       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
              << _.VkErrorID(4491) << "Vulkan spec allows BuiltIn "
              << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
@@ -3918,8 +3916,8 @@
              << " " << GetStorageClassDesc(referenced_from_inst);
     }
 
-    for (const SpvExecutionModel execution_model : execution_models_) {
-      if (execution_model != SpvExecutionModelFragment) {
+    for (const spv::ExecutionModel execution_model : execution_models_) {
+      if (execution_model != spv::ExecutionModel::Fragment) {
         return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
                << _.VkErrorID(4490) << "Vulkan spec allows BuiltIn "
                << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
@@ -3944,11 +3942,11 @@
 spv_result_t BuiltInsValidator::ValidateRayTracingBuiltinsAtDefinition(
     const Decoration& decoration, const Instruction& inst) {
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]);
+    const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]);
     switch (builtin) {
-      case SpvBuiltInHitTNV:
-      case SpvBuiltInRayTminKHR:
-      case SpvBuiltInRayTmaxKHR:
+      case spv::BuiltIn::HitTNV:
+      case spv::BuiltIn::RayTminKHR:
+      case spv::BuiltIn::RayTmaxKHR:
         // f32 scalar
         if (spv_result_t error = ValidateF32(
                 decoration, inst,
@@ -3959,19 +3957,19 @@
                          << _.VkErrorID(vuid)
                          << "According to the Vulkan spec BuiltIn "
                          << _.grammar().lookupOperandName(
-                                SPV_OPERAND_TYPE_BUILT_IN, builtin)
+                                SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
                          << " variable needs to be a 32-bit float scalar. "
                          << message;
                 })) {
           return error;
         }
         break;
-      case SpvBuiltInHitKindKHR:
-      case SpvBuiltInInstanceCustomIndexKHR:
-      case SpvBuiltInInstanceId:
-      case SpvBuiltInRayGeometryIndexKHR:
-      case SpvBuiltInIncomingRayFlagsKHR:
-      case SpvBuiltInCullMaskKHR:
+      case spv::BuiltIn::HitKindKHR:
+      case spv::BuiltIn::InstanceCustomIndexKHR:
+      case spv::BuiltIn::InstanceId:
+      case spv::BuiltIn::RayGeometryIndexKHR:
+      case spv::BuiltIn::IncomingRayFlagsKHR:
+      case spv::BuiltIn::CullMaskKHR:
         // i32 scalar
         if (spv_result_t error = ValidateI32(
                 decoration, inst,
@@ -3982,17 +3980,17 @@
                          << _.VkErrorID(vuid)
                          << "According to the Vulkan spec BuiltIn "
                          << _.grammar().lookupOperandName(
-                                SPV_OPERAND_TYPE_BUILT_IN, builtin)
+                                SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
                          << " variable needs to be a 32-bit int scalar. "
                          << message;
                 })) {
           return error;
         }
         break;
-      case SpvBuiltInObjectRayDirectionKHR:
-      case SpvBuiltInObjectRayOriginKHR:
-      case SpvBuiltInWorldRayDirectionKHR:
-      case SpvBuiltInWorldRayOriginKHR:
+      case spv::BuiltIn::ObjectRayDirectionKHR:
+      case spv::BuiltIn::ObjectRayOriginKHR:
+      case spv::BuiltIn::WorldRayDirectionKHR:
+      case spv::BuiltIn::WorldRayOriginKHR:
         // f32 vec3
         if (spv_result_t error = ValidateF32Vec(
                 decoration, inst, 3,
@@ -4003,7 +4001,7 @@
                          << _.VkErrorID(vuid)
                          << "According to the Vulkan spec BuiltIn "
                          << _.grammar().lookupOperandName(
-                                SPV_OPERAND_TYPE_BUILT_IN, builtin)
+                                SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
                          << " variable needs to be a 3-component 32-bit float "
                             "vector. "
                          << message;
@@ -4011,8 +4009,8 @@
           return error;
         }
         break;
-      case SpvBuiltInLaunchIdKHR:
-      case SpvBuiltInLaunchSizeKHR:
+      case spv::BuiltIn::LaunchIdKHR:
+      case spv::BuiltIn::LaunchSizeKHR:
         // i32 vec3
         if (spv_result_t error = ValidateI32Vec(
                 decoration, inst, 3,
@@ -4023,7 +4021,7 @@
                          << _.VkErrorID(vuid)
                          << "According to the Vulkan spec BuiltIn "
                          << _.grammar().lookupOperandName(
-                                SPV_OPERAND_TYPE_BUILT_IN, builtin)
+                                SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
                          << " variable needs to be a 3-component 32-bit int "
                             "vector. "
                          << message;
@@ -4031,8 +4029,8 @@
           return error;
         }
         break;
-      case SpvBuiltInObjectToWorldKHR:
-      case SpvBuiltInWorldToObjectKHR:
+      case spv::BuiltIn::ObjectToWorldKHR:
+      case spv::BuiltIn::WorldToObjectKHR:
         // f32 mat4x3
         if (spv_result_t error = ValidateF32Mat(
                 decoration, inst, 3, 4,
@@ -4043,7 +4041,7 @@
                          << _.VkErrorID(vuid)
                          << "According to the Vulkan spec BuiltIn "
                          << _.grammar().lookupOperandName(
-                                SPV_OPERAND_TYPE_BUILT_IN, builtin)
+                                SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
                          << " variable needs to be a matrix with"
                          << " 4 columns of 3-component vectors of 32-bit "
                             "floats. "
@@ -4067,10 +4065,10 @@
     const Instruction& referenced_inst,
     const Instruction& referenced_from_inst) {
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]);
-    const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
-    if (storage_class != SpvStorageClassMax &&
-        storage_class != SpvStorageClassInput) {
+    const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]);
+    const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
+    if (storage_class != spv::StorageClass::Max &&
+        storage_class != spv::StorageClass::Input) {
       uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass);
       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
              << _.VkErrorID(vuid) << "Vulkan spec allows BuiltIn "
@@ -4082,7 +4080,7 @@
              << " " << GetStorageClassDesc(referenced_from_inst);
     }
 
-    for (const SpvExecutionModel execution_model : execution_models_) {
+    for (const spv::ExecutionModel execution_model : execution_models_) {
       if (!IsExecutionModelValidForRtBuiltIn(builtin, execution_model)) {
         uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorExecutionModel);
         return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
@@ -4091,7 +4089,7 @@
                                                 decoration.params()[0])
                << " to be used with the execution model "
                << _.grammar().lookupOperandName(
-                      SPV_OPERAND_TYPE_EXECUTION_MODEL, execution_model)
+                      SPV_OPERAND_TYPE_EXECUTION_MODEL, uint32_t(execution_model))
                << ".\n"
                << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
                                    referenced_from_inst, execution_model);
@@ -4112,7 +4110,7 @@
 
 spv_result_t BuiltInsValidator::ValidateSingleBuiltInAtDefinition(
     const Decoration& decoration, const Instruction& inst) {
-  const SpvBuiltIn label = SpvBuiltIn(decoration.params()[0]);
+  const spv::BuiltIn label = spv::BuiltIn(decoration.params()[0]);
 
   if (!spvIsVulkanEnv(_.context()->target_env)) {
     // Early return. All currently implemented rules are based on Vulkan spec.
@@ -4129,166 +4127,166 @@
   // If the newly added enum has validation rules associated with it
   // consider leaving a TODO and/or creating an issue.
   switch (label) {
-    case SpvBuiltInClipDistance:
-    case SpvBuiltInCullDistance: {
+    case spv::BuiltIn::ClipDistance:
+    case spv::BuiltIn::CullDistance: {
       return ValidateClipOrCullDistanceAtDefinition(decoration, inst);
     }
-    case SpvBuiltInFragCoord: {
+    case spv::BuiltIn::FragCoord: {
       return ValidateFragCoordAtDefinition(decoration, inst);
     }
-    case SpvBuiltInFragDepth: {
+    case spv::BuiltIn::FragDepth: {
       return ValidateFragDepthAtDefinition(decoration, inst);
     }
-    case SpvBuiltInFrontFacing: {
+    case spv::BuiltIn::FrontFacing: {
       return ValidateFrontFacingAtDefinition(decoration, inst);
     }
-    case SpvBuiltInGlobalInvocationId:
-    case SpvBuiltInLocalInvocationId:
-    case SpvBuiltInNumWorkgroups:
-    case SpvBuiltInWorkgroupId: {
+    case spv::BuiltIn::GlobalInvocationId:
+    case spv::BuiltIn::LocalInvocationId:
+    case spv::BuiltIn::NumWorkgroups:
+    case spv::BuiltIn::WorkgroupId: {
       return ValidateComputeShaderI32Vec3InputAtDefinition(decoration, inst);
     }
-    case SpvBuiltInBaryCoordKHR:
-    case SpvBuiltInBaryCoordNoPerspKHR: {
+    case spv::BuiltIn::BaryCoordKHR:
+    case spv::BuiltIn::BaryCoordNoPerspKHR: {
       return ValidateFragmentShaderF32Vec3InputAtDefinition(decoration, inst);
     }
-    case SpvBuiltInHelperInvocation: {
+    case spv::BuiltIn::HelperInvocation: {
       return ValidateHelperInvocationAtDefinition(decoration, inst);
     }
-    case SpvBuiltInInvocationId: {
+    case spv::BuiltIn::InvocationId: {
       return ValidateInvocationIdAtDefinition(decoration, inst);
     }
-    case SpvBuiltInInstanceIndex: {
+    case spv::BuiltIn::InstanceIndex: {
       return ValidateInstanceIndexAtDefinition(decoration, inst);
     }
-    case SpvBuiltInLayer:
-    case SpvBuiltInViewportIndex: {
+    case spv::BuiltIn::Layer:
+    case spv::BuiltIn::ViewportIndex: {
       return ValidateLayerOrViewportIndexAtDefinition(decoration, inst);
     }
-    case SpvBuiltInPatchVertices: {
+    case spv::BuiltIn::PatchVertices: {
       return ValidatePatchVerticesAtDefinition(decoration, inst);
     }
-    case SpvBuiltInPointCoord: {
+    case spv::BuiltIn::PointCoord: {
       return ValidatePointCoordAtDefinition(decoration, inst);
     }
-    case SpvBuiltInPointSize: {
+    case spv::BuiltIn::PointSize: {
       return ValidatePointSizeAtDefinition(decoration, inst);
     }
-    case SpvBuiltInPosition: {
+    case spv::BuiltIn::Position: {
       return ValidatePositionAtDefinition(decoration, inst);
     }
-    case SpvBuiltInPrimitiveId: {
+    case spv::BuiltIn::PrimitiveId: {
       return ValidatePrimitiveIdAtDefinition(decoration, inst);
     }
-    case SpvBuiltInSampleId: {
+    case spv::BuiltIn::SampleId: {
       return ValidateSampleIdAtDefinition(decoration, inst);
     }
-    case SpvBuiltInSampleMask: {
+    case spv::BuiltIn::SampleMask: {
       return ValidateSampleMaskAtDefinition(decoration, inst);
     }
-    case SpvBuiltInSamplePosition: {
+    case spv::BuiltIn::SamplePosition: {
       return ValidateSamplePositionAtDefinition(decoration, inst);
     }
-    case SpvBuiltInSubgroupId:
-    case SpvBuiltInNumSubgroups: {
+    case spv::BuiltIn::SubgroupId:
+    case spv::BuiltIn::NumSubgroups: {
       return ValidateComputeI32InputAtDefinition(decoration, inst);
     }
-    case SpvBuiltInSubgroupLocalInvocationId:
-    case SpvBuiltInSubgroupSize: {
+    case spv::BuiltIn::SubgroupLocalInvocationId:
+    case spv::BuiltIn::SubgroupSize: {
       return ValidateI32InputAtDefinition(decoration, inst);
     }
-    case SpvBuiltInSubgroupEqMask:
-    case SpvBuiltInSubgroupGeMask:
-    case SpvBuiltInSubgroupGtMask:
-    case SpvBuiltInSubgroupLeMask:
-    case SpvBuiltInSubgroupLtMask: {
+    case spv::BuiltIn::SubgroupEqMask:
+    case spv::BuiltIn::SubgroupGeMask:
+    case spv::BuiltIn::SubgroupGtMask:
+    case spv::BuiltIn::SubgroupLeMask:
+    case spv::BuiltIn::SubgroupLtMask: {
       return ValidateI32Vec4InputAtDefinition(decoration, inst);
     }
-    case SpvBuiltInTessCoord: {
+    case spv::BuiltIn::TessCoord: {
       return ValidateTessCoordAtDefinition(decoration, inst);
     }
-    case SpvBuiltInTessLevelOuter: {
+    case spv::BuiltIn::TessLevelOuter: {
       return ValidateTessLevelOuterAtDefinition(decoration, inst);
     }
-    case SpvBuiltInTessLevelInner: {
+    case spv::BuiltIn::TessLevelInner: {
       return ValidateTessLevelInnerAtDefinition(decoration, inst);
     }
-    case SpvBuiltInVertexIndex: {
+    case spv::BuiltIn::VertexIndex: {
       return ValidateVertexIndexAtDefinition(decoration, inst);
     }
-    case SpvBuiltInWorkgroupSize: {
+    case spv::BuiltIn::WorkgroupSize: {
       return ValidateWorkgroupSizeAtDefinition(decoration, inst);
     }
-    case SpvBuiltInVertexId: {
+    case spv::BuiltIn::VertexId: {
       return ValidateVertexIdAtDefinition(decoration, inst);
     }
-    case SpvBuiltInLocalInvocationIndex: {
+    case spv::BuiltIn::LocalInvocationIndex: {
       return ValidateLocalInvocationIndexAtDefinition(decoration, inst);
     }
-    case SpvBuiltInCoreIDARM:
-    case SpvBuiltInCoreCountARM:
-    case SpvBuiltInCoreMaxIDARM:
-    case SpvBuiltInWarpIDARM:
-    case SpvBuiltInWarpMaxIDARM:
-    case SpvBuiltInWarpsPerSMNV:
-    case SpvBuiltInSMCountNV:
-    case SpvBuiltInWarpIDNV:
-    case SpvBuiltInSMIDNV: {
+    case spv::BuiltIn::CoreIDARM:
+    case spv::BuiltIn::CoreCountARM:
+    case spv::BuiltIn::CoreMaxIDARM:
+    case spv::BuiltIn::WarpIDARM:
+    case spv::BuiltIn::WarpMaxIDARM:
+    case spv::BuiltIn::WarpsPerSMNV:
+    case spv::BuiltIn::SMCountNV:
+    case spv::BuiltIn::WarpIDNV:
+    case spv::BuiltIn::SMIDNV: {
       return ValidateNVSMOrARMCoreBuiltinsAtDefinition(decoration, inst);
     }
-    case SpvBuiltInBaseInstance:
-    case SpvBuiltInBaseVertex: {
+    case spv::BuiltIn::BaseInstance:
+    case spv::BuiltIn::BaseVertex: {
       return ValidateBaseInstanceOrVertexAtDefinition(decoration, inst);
     }
-    case SpvBuiltInDrawIndex: {
+    case spv::BuiltIn::DrawIndex: {
       return ValidateDrawIndexAtDefinition(decoration, inst);
     }
-    case SpvBuiltInViewIndex: {
+    case spv::BuiltIn::ViewIndex: {
       return ValidateViewIndexAtDefinition(decoration, inst);
     }
-    case SpvBuiltInDeviceIndex: {
+    case spv::BuiltIn::DeviceIndex: {
       return ValidateDeviceIndexAtDefinition(decoration, inst);
     }
-    case SpvBuiltInFragInvocationCountEXT: {
-      // alias SpvBuiltInInvocationsPerPixelNV
+    case spv::BuiltIn::FragInvocationCountEXT: {
+      // alias spv::BuiltIn::InvocationsPerPixelNV
       return ValidateFragInvocationCountAtDefinition(decoration, inst);
     }
-    case SpvBuiltInFragSizeEXT: {
-      // alias SpvBuiltInFragmentSizeNV
+    case spv::BuiltIn::FragSizeEXT: {
+      // alias spv::BuiltIn::FragmentSizeNV
       return ValidateFragSizeAtDefinition(decoration, inst);
     }
-    case SpvBuiltInFragStencilRefEXT: {
+    case spv::BuiltIn::FragStencilRefEXT: {
       return ValidateFragStencilRefAtDefinition(decoration, inst);
     }
-    case SpvBuiltInFullyCoveredEXT:{
+    case spv::BuiltIn::FullyCoveredEXT:{
       return ValidateFullyCoveredAtDefinition(decoration, inst);
     }
     // Ray tracing builtins
-    case SpvBuiltInHitKindKHR:  // alias SpvBuiltInHitKindNV
-    case SpvBuiltInHitTNV:      // NOT present in KHR
-    case SpvBuiltInInstanceId:
-    case SpvBuiltInLaunchIdKHR:           // alias SpvBuiltInLaunchIdNV
-    case SpvBuiltInLaunchSizeKHR:         // alias SpvBuiltInLaunchSizeNV
-    case SpvBuiltInWorldRayOriginKHR:     // alias SpvBuiltInWorldRayOriginNV
-    case SpvBuiltInWorldRayDirectionKHR:  // alias SpvBuiltInWorldRayDirectionNV
-    case SpvBuiltInObjectRayOriginKHR:    // alias SpvBuiltInObjectRayOriginNV
-    case SpvBuiltInObjectRayDirectionKHR:   // alias
-                                            // SpvBuiltInObjectRayDirectionNV
-    case SpvBuiltInRayTminKHR:              // alias SpvBuiltInRayTminNV
-    case SpvBuiltInRayTmaxKHR:              // alias SpvBuiltInRayTmaxNV
-    case SpvBuiltInInstanceCustomIndexKHR:  // alias
-                                            // SpvBuiltInInstanceCustomIndexNV
-    case SpvBuiltInObjectToWorldKHR:        // alias SpvBuiltInObjectToWorldNV
-    case SpvBuiltInWorldToObjectKHR:        // alias SpvBuiltInWorldToObjectNV
-    case SpvBuiltInIncomingRayFlagsKHR:    // alias SpvBuiltInIncomingRayFlagsNV
-    case SpvBuiltInRayGeometryIndexKHR:    // NOT present in NV
-    case SpvBuiltInCullMaskKHR: {
+    case spv::BuiltIn::HitKindKHR:  // alias spv::BuiltIn::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::RayGeometryIndexKHR:    // NOT present in NV
+    case spv::BuiltIn::CullMaskKHR: {
       return ValidateRayTracingBuiltinsAtDefinition(decoration, inst);
     }
-    case SpvBuiltInPrimitiveShadingRateKHR: {
+    case spv::BuiltIn::PrimitiveShadingRateKHR: {
       return ValidatePrimitiveShadingRateAtDefinition(decoration, inst);
     }
-    case SpvBuiltInShadingRateKHR: {
+    case spv::BuiltIn::ShadingRateKHR: {
       return ValidateShadingRateAtDefinition(decoration, inst);
     }
     default:
@@ -4310,7 +4308,7 @@
     assert(inst);
 
     for (const auto& decoration : kv.second) {
-      if (decoration.dec_type() != SpvDecorationBuiltIn) {
+      if (decoration.dec_type() != spv::Decoration::BuiltIn) {
         continue;
       }
 
diff --git a/source/val/validate_capability.cpp b/source/val/validate_capability.cpp
index 8efd554..81d2ad5 100644
--- a/source/val/validate_capability.cpp
+++ b/source/val/validate_capability.cpp
@@ -16,9 +16,7 @@
 
 #include <cassert>
 #include <string>
-#include <unordered_set>
 
-#include "source/diagnostic.h"
 #include "source/opcode.h"
 #include "source/val/instruction.h"
 #include "source/val/validate.h"
@@ -29,74 +27,82 @@
 namespace {
 
 bool IsSupportGuaranteedVulkan_1_0(uint32_t capability) {
-  switch (capability) {
-    case SpvCapabilityMatrix:
-    case SpvCapabilityShader:
-    case SpvCapabilityInputAttachment:
-    case SpvCapabilitySampled1D:
-    case SpvCapabilityImage1D:
-    case SpvCapabilitySampledBuffer:
-    case SpvCapabilityImageBuffer:
-    case SpvCapabilityImageQuery:
-    case SpvCapabilityDerivativeControl:
+  switch (spv::Capability(capability)) {
+    case spv::Capability::Matrix:
+    case spv::Capability::Shader:
+    case spv::Capability::InputAttachment:
+    case spv::Capability::Sampled1D:
+    case spv::Capability::Image1D:
+    case spv::Capability::SampledBuffer:
+    case spv::Capability::ImageBuffer:
+    case spv::Capability::ImageQuery:
+    case spv::Capability::DerivativeControl:
       return true;
+    default:
+      break;
   }
   return false;
 }
 
 bool IsSupportGuaranteedVulkan_1_1(uint32_t capability) {
   if (IsSupportGuaranteedVulkan_1_0(capability)) return true;
-  switch (capability) {
-    case SpvCapabilityDeviceGroup:
-    case SpvCapabilityMultiView:
+  switch (spv::Capability(capability)) {
+    case spv::Capability::DeviceGroup:
+    case spv::Capability::MultiView:
       return true;
+    default:
+      break;
   }
   return false;
 }
 
 bool IsSupportGuaranteedVulkan_1_2(uint32_t capability) {
   if (IsSupportGuaranteedVulkan_1_1(capability)) return true;
-  switch (capability) {
-    case SpvCapabilityShaderNonUniform:
+  switch (spv::Capability(capability)) {
+    case spv::Capability::ShaderNonUniform:
       return true;
+    default:
+      break;
   }
   return false;
 }
 
 bool IsSupportOptionalVulkan_1_0(uint32_t capability) {
-  switch (capability) {
-    case SpvCapabilityGeometry:
-    case SpvCapabilityTessellation:
-    case SpvCapabilityFloat64:
-    case SpvCapabilityInt64:
-    case SpvCapabilityInt16:
-    case SpvCapabilityTessellationPointSize:
-    case SpvCapabilityGeometryPointSize:
-    case SpvCapabilityImageGatherExtended:
-    case SpvCapabilityStorageImageMultisample:
-    case SpvCapabilityUniformBufferArrayDynamicIndexing:
-    case SpvCapabilitySampledImageArrayDynamicIndexing:
-    case SpvCapabilityStorageBufferArrayDynamicIndexing:
-    case SpvCapabilityStorageImageArrayDynamicIndexing:
-    case SpvCapabilityClipDistance:
-    case SpvCapabilityCullDistance:
-    case SpvCapabilityImageCubeArray:
-    case SpvCapabilitySampleRateShading:
-    case SpvCapabilitySparseResidency:
-    case SpvCapabilityMinLod:
-    case SpvCapabilitySampledCubeArray:
-    case SpvCapabilityImageMSArray:
-    case SpvCapabilityStorageImageExtendedFormats:
-    case SpvCapabilityInterpolationFunction:
-    case SpvCapabilityStorageImageReadWithoutFormat:
-    case SpvCapabilityStorageImageWriteWithoutFormat:
-    case SpvCapabilityMultiViewport:
-    case SpvCapabilityInt64Atomics:
-    case SpvCapabilityTransformFeedback:
-    case SpvCapabilityGeometryStreams:
-    case SpvCapabilityFloat16:
-    case SpvCapabilityInt8:
+  switch (spv::Capability(capability)) {
+    case spv::Capability::Geometry:
+    case spv::Capability::Tessellation:
+    case spv::Capability::Float64:
+    case spv::Capability::Int64:
+    case spv::Capability::Int16:
+    case spv::Capability::TessellationPointSize:
+    case spv::Capability::GeometryPointSize:
+    case spv::Capability::ImageGatherExtended:
+    case spv::Capability::StorageImageMultisample:
+    case spv::Capability::UniformBufferArrayDynamicIndexing:
+    case spv::Capability::SampledImageArrayDynamicIndexing:
+    case spv::Capability::StorageBufferArrayDynamicIndexing:
+    case spv::Capability::StorageImageArrayDynamicIndexing:
+    case spv::Capability::ClipDistance:
+    case spv::Capability::CullDistance:
+    case spv::Capability::ImageCubeArray:
+    case spv::Capability::SampleRateShading:
+    case spv::Capability::SparseResidency:
+    case spv::Capability::MinLod:
+    case spv::Capability::SampledCubeArray:
+    case spv::Capability::ImageMSArray:
+    case spv::Capability::StorageImageExtendedFormats:
+    case spv::Capability::InterpolationFunction:
+    case spv::Capability::StorageImageReadWithoutFormat:
+    case spv::Capability::StorageImageWriteWithoutFormat:
+    case spv::Capability::MultiViewport:
+    case spv::Capability::Int64Atomics:
+    case spv::Capability::TransformFeedback:
+    case spv::Capability::GeometryStreams:
+    case spv::Capability::Float16:
+    case spv::Capability::Int8:
       return true;
+    default:
+      break;
   }
   return false;
 }
@@ -104,27 +110,29 @@
 bool IsSupportOptionalVulkan_1_1(uint32_t capability) {
   if (IsSupportOptionalVulkan_1_0(capability)) return true;
 
-  switch (capability) {
-    case SpvCapabilityGroupNonUniform:
-    case SpvCapabilityGroupNonUniformVote:
-    case SpvCapabilityGroupNonUniformArithmetic:
-    case SpvCapabilityGroupNonUniformBallot:
-    case SpvCapabilityGroupNonUniformShuffle:
-    case SpvCapabilityGroupNonUniformShuffleRelative:
-    case SpvCapabilityGroupNonUniformClustered:
-    case SpvCapabilityGroupNonUniformQuad:
-    case SpvCapabilityDrawParameters:
-    // Alias SpvCapabilityStorageBuffer16BitAccess.
-    case SpvCapabilityStorageUniformBufferBlock16:
-    // Alias SpvCapabilityUniformAndStorageBuffer16BitAccess.
-    case SpvCapabilityStorageUniform16:
-    case SpvCapabilityStoragePushConstant16:
-    case SpvCapabilityStorageInputOutput16:
-    case SpvCapabilityDeviceGroup:
-    case SpvCapabilityMultiView:
-    case SpvCapabilityVariablePointersStorageBuffer:
-    case SpvCapabilityVariablePointers:
+  switch (spv::Capability(capability)) {
+    case spv::Capability::GroupNonUniform:
+    case spv::Capability::GroupNonUniformVote:
+    case spv::Capability::GroupNonUniformArithmetic:
+    case spv::Capability::GroupNonUniformBallot:
+    case spv::Capability::GroupNonUniformShuffle:
+    case spv::Capability::GroupNonUniformShuffleRelative:
+    case spv::Capability::GroupNonUniformClustered:
+    case spv::Capability::GroupNonUniformQuad:
+    case spv::Capability::DrawParameters:
+    // Alias spv::Capability::StorageBuffer16BitAccess.
+    case spv::Capability::StorageUniformBufferBlock16:
+    // Alias spv::Capability::UniformAndStorageBuffer16BitAccess.
+    case spv::Capability::StorageUniform16:
+    case spv::Capability::StoragePushConstant16:
+    case spv::Capability::StorageInputOutput16:
+    case spv::Capability::DeviceGroup:
+    case spv::Capability::MultiView:
+    case spv::Capability::VariablePointersStorageBuffer:
+    case spv::Capability::VariablePointers:
       return true;
+    default:
+      break;
   }
   return false;
 }
@@ -132,47 +140,51 @@
 bool IsSupportOptionalVulkan_1_2(uint32_t capability) {
   if (IsSupportOptionalVulkan_1_1(capability)) return true;
 
-  switch (capability) {
-    case SpvCapabilityDenormPreserve:
-    case SpvCapabilityDenormFlushToZero:
-    case SpvCapabilitySignedZeroInfNanPreserve:
-    case SpvCapabilityRoundingModeRTE:
-    case SpvCapabilityRoundingModeRTZ:
-    case SpvCapabilityVulkanMemoryModel:
-    case SpvCapabilityVulkanMemoryModelDeviceScope:
-    case SpvCapabilityStorageBuffer8BitAccess:
-    case SpvCapabilityUniformAndStorageBuffer8BitAccess:
-    case SpvCapabilityStoragePushConstant8:
-    case SpvCapabilityShaderViewportIndex:
-    case SpvCapabilityShaderLayer:
-    case SpvCapabilityPhysicalStorageBufferAddresses:
-    case SpvCapabilityRuntimeDescriptorArray:
-    case SpvCapabilityUniformTexelBufferArrayDynamicIndexing:
-    case SpvCapabilityStorageTexelBufferArrayDynamicIndexing:
-    case SpvCapabilityUniformBufferArrayNonUniformIndexing:
-    case SpvCapabilitySampledImageArrayNonUniformIndexing:
-    case SpvCapabilityStorageBufferArrayNonUniformIndexing:
-    case SpvCapabilityStorageImageArrayNonUniformIndexing:
-    case SpvCapabilityInputAttachmentArrayNonUniformIndexing:
-    case SpvCapabilityUniformTexelBufferArrayNonUniformIndexing:
-    case SpvCapabilityStorageTexelBufferArrayNonUniformIndexing:
+  switch (spv::Capability(capability)) {
+    case spv::Capability::DenormPreserve:
+    case spv::Capability::DenormFlushToZero:
+    case spv::Capability::SignedZeroInfNanPreserve:
+    case spv::Capability::RoundingModeRTE:
+    case spv::Capability::RoundingModeRTZ:
+    case spv::Capability::VulkanMemoryModel:
+    case spv::Capability::VulkanMemoryModelDeviceScope:
+    case spv::Capability::StorageBuffer8BitAccess:
+    case spv::Capability::UniformAndStorageBuffer8BitAccess:
+    case spv::Capability::StoragePushConstant8:
+    case spv::Capability::ShaderViewportIndex:
+    case spv::Capability::ShaderLayer:
+    case spv::Capability::PhysicalStorageBufferAddresses:
+    case spv::Capability::RuntimeDescriptorArray:
+    case spv::Capability::UniformTexelBufferArrayDynamicIndexing:
+    case spv::Capability::StorageTexelBufferArrayDynamicIndexing:
+    case spv::Capability::UniformBufferArrayNonUniformIndexing:
+    case spv::Capability::SampledImageArrayNonUniformIndexing:
+    case spv::Capability::StorageBufferArrayNonUniformIndexing:
+    case spv::Capability::StorageImageArrayNonUniformIndexing:
+    case spv::Capability::InputAttachmentArrayNonUniformIndexing:
+    case spv::Capability::UniformTexelBufferArrayNonUniformIndexing:
+    case spv::Capability::StorageTexelBufferArrayNonUniformIndexing:
       return true;
+    default:
+      break;
   }
   return false;
 }
 
 bool IsSupportGuaranteedOpenCL_1_2(uint32_t capability, bool embedded_profile) {
-  switch (capability) {
-    case SpvCapabilityAddresses:
-    case SpvCapabilityFloat16Buffer:
-    case SpvCapabilityInt16:
-    case SpvCapabilityInt8:
-    case SpvCapabilityKernel:
-    case SpvCapabilityLinkage:
-    case SpvCapabilityVector16:
+  switch (spv::Capability(capability)) {
+    case spv::Capability::Addresses:
+    case spv::Capability::Float16Buffer:
+    case spv::Capability::Int16:
+    case spv::Capability::Int8:
+    case spv::Capability::Kernel:
+    case spv::Capability::Linkage:
+    case spv::Capability::Vector16:
       return true;
-    case SpvCapabilityInt64:
+    case spv::Capability::Int64:
       return !embedded_profile;
+    default:
+      break;
   }
   return false;
 }
@@ -180,12 +192,14 @@
 bool IsSupportGuaranteedOpenCL_2_0(uint32_t capability, bool embedded_profile) {
   if (IsSupportGuaranteedOpenCL_1_2(capability, embedded_profile)) return true;
 
-  switch (capability) {
-    case SpvCapabilityDeviceEnqueue:
-    case SpvCapabilityGenericPointer:
-    case SpvCapabilityGroups:
-    case SpvCapabilityPipes:
+  switch (spv::Capability(capability)) {
+    case spv::Capability::DeviceEnqueue:
+    case spv::Capability::GenericPointer:
+    case spv::Capability::Groups:
+    case spv::Capability::Pipes:
       return true;
+    default:
+      break;
   }
   return false;
 }
@@ -193,19 +207,23 @@
 bool IsSupportGuaranteedOpenCL_2_2(uint32_t capability, bool embedded_profile) {
   if (IsSupportGuaranteedOpenCL_2_0(capability, embedded_profile)) return true;
 
-  switch (capability) {
-    case SpvCapabilitySubgroupDispatch:
-    case SpvCapabilityPipeStorage:
+  switch (spv::Capability(capability)) {
+    case spv::Capability::SubgroupDispatch:
+    case spv::Capability::PipeStorage:
       return true;
+    default:
+      break;
   }
   return false;
 }
 
 bool IsSupportOptionalOpenCL_1_2(uint32_t capability) {
-  switch (capability) {
-    case SpvCapabilityImageBasic:
-    case SpvCapabilityFloat64:
+  switch (spv::Capability(capability)) {
+    case spv::Capability::ImageBasic:
+    case spv::Capability::Float64:
       return true;
+    default:
+      break;
   }
   return false;
 }
@@ -222,21 +240,23 @@
 
   ExtensionSet operand_exts(operand_desc->numExtensions,
                             operand_desc->extensions);
-  if (operand_exts.IsEmpty()) return false;
+  if (operand_exts.empty()) return false;
 
   return _.HasAnyOfExtensions(operand_exts);
 }
 
 bool IsEnabledByCapabilityOpenCL_1_2(ValidationState_t& _,
                                      uint32_t capability) {
-  if (_.HasCapability(SpvCapabilityImageBasic)) {
-    switch (capability) {
-      case SpvCapabilityLiteralSampler:
-      case SpvCapabilitySampled1D:
-      case SpvCapabilityImage1D:
-      case SpvCapabilitySampledBuffer:
-      case SpvCapabilityImageBuffer:
+  if (_.HasCapability(spv::Capability::ImageBasic)) {
+    switch (spv::Capability(capability)) {
+      case spv::Capability::LiteralSampler:
+      case spv::Capability::Sampled1D:
+      case spv::Capability::Image1D:
+      case spv::Capability::SampledBuffer:
+      case spv::Capability::ImageBuffer:
         return true;
+      default:
+        break;
     }
     return false;
   }
@@ -245,15 +265,17 @@
 
 bool IsEnabledByCapabilityOpenCL_2_0(ValidationState_t& _,
                                      uint32_t capability) {
-  if (_.HasCapability(SpvCapabilityImageBasic)) {
-    switch (capability) {
-      case SpvCapabilityImageReadWrite:
-      case SpvCapabilityLiteralSampler:
-      case SpvCapabilitySampled1D:
-      case SpvCapabilityImage1D:
-      case SpvCapabilitySampledBuffer:
-      case SpvCapabilityImageBuffer:
+  if (_.HasCapability(spv::Capability::ImageBasic)) {
+    switch (spv::Capability(capability)) {
+      case spv::Capability::ImageReadWrite:
+      case spv::Capability::LiteralSampler:
+      case spv::Capability::Sampled1D:
+      case spv::Capability::Image1D:
+      case spv::Capability::SampledBuffer:
+      case spv::Capability::ImageBuffer:
         return true;
+      default:
+        break;
     }
     return false;
   }
@@ -265,7 +287,7 @@
 // Validates that capability declarations use operands allowed in the current
 // context.
 spv_result_t CapabilityPass(ValidationState_t& _, const Instruction* inst) {
-  if (inst->opcode() != SpvOpCapability) return SPV_SUCCESS;
+  if (inst->opcode() != spv::Op::OpCapability) return SPV_SUCCESS;
 
   assert(inst->operands().size() == 1);
 
diff --git a/source/val/validate_cfg.cpp b/source/val/validate_cfg.cpp
index cc0b999..7b3f748 100644
--- a/source/val/validate_cfg.cpp
+++ b/source/val/validate_cfg.cpp
@@ -12,11 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <algorithm>
 #include <cassert>
 #include <functional>
 #include <iostream>
-#include <iterator>
 #include <map>
 #include <string>
 #include <tuple>
@@ -28,7 +26,6 @@
 #include "source/cfa.h"
 #include "source/opcode.h"
 #include "source/spirv_constant.h"
-#include "source/spirv_target_env.h"
 #include "source/spirv_validator_options.h"
 #include "source/val/basic_block.h"
 #include "source/val/construct.h"
@@ -54,7 +51,7 @@
            << "OpPhi must not have void result type";
   }
   if (_.IsPointerType(inst->type_id()) &&
-      _.addressing_model() == SpvAddressingModelLogical) {
+      _.addressing_model() == spv::AddressingModel::Logical) {
     if (!_.features().variable_pointers) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Using pointers with OpPhi requires capability "
@@ -64,13 +61,14 @@
 
   const Instruction* type_inst = _.FindDef(inst->type_id());
   assert(type_inst);
-  const SpvOp type_opcode = type_inst->opcode();
+  const spv::Op type_opcode = type_inst->opcode();
 
   if (!_.options()->before_hlsl_legalization &&
-      !_.HasCapability(SpvCapabilityBindlessTextureNV)) {
-    if (type_opcode == SpvOpTypeSampledImage ||
-        (_.HasCapability(SpvCapabilityShader) &&
-         (type_opcode == SpvOpTypeImage || type_opcode == SpvOpTypeSampler))) {
+      !_.HasCapability(spv::Capability::BindlessTextureNV)) {
+    if (type_opcode == spv::Op::OpTypeSampledImage ||
+        (_.HasCapability(spv::Capability::Shader) &&
+         (type_opcode == spv::Op::OpTypeImage ||
+          type_opcode == spv::Op::OpTypeSampler))) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
              << "Result type cannot be Op" << spvOpcodeString(type_opcode);
     }
@@ -108,7 +106,7 @@
                << " type <id> " << _.getIdName(inc_type_id) << ".";
       }
     } else {
-      if (_.GetIdOpcode(inc_id) != SpvOpLabel) {
+      if (_.GetIdOpcode(inc_id) != spv::Op::OpLabel) {
         return _.diag(SPV_ERROR_INVALID_ID, inst)
                << "OpPhi's incoming basic block <id> " << _.getIdName(inc_id)
                << " is not an OpLabel.";
@@ -143,7 +141,7 @@
   // target operands must be OpLabel
   const auto id = inst->GetOperandAs<uint32_t>(0);
   const auto target = _.FindDef(id);
-  if (!target || SpvOpLabel != target->opcode()) {
+  if (!target || spv::Op::OpLabel != target->opcode()) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "'Target Label' operands for OpBranch must be the ID "
               "of an OpLabel instruction";
@@ -178,7 +176,7 @@
   // PerformCfgChecks already checks for that
   const auto true_id = inst->GetOperandAs<uint32_t>(1);
   const auto true_target = _.FindDef(true_id);
-  if (!true_target || SpvOpLabel != true_target->opcode()) {
+  if (!true_target || spv::Op::OpLabel != true_target->opcode()) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "The 'True Label' operand for OpBranchConditional must be the "
               "ID of an OpLabel instruction";
@@ -186,7 +184,7 @@
 
   const auto false_id = inst->GetOperandAs<uint32_t>(2);
   const auto false_target = _.FindDef(false_id);
-  if (!false_target || SpvOpLabel != false_target->opcode()) {
+  if (!false_target || spv::Op::OpLabel != false_target->opcode()) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "The 'False Label' operand for OpBranchConditional must be the "
               "ID of an OpLabel instruction";
@@ -213,7 +211,7 @@
   }
 
   const auto default_label = _.FindDef(inst->GetOperandAs<uint32_t>(1));
-  if (default_label->opcode() != SpvOpLabel) {
+  if (default_label->opcode() != spv::Op::OpLabel) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "Default must be an OpLabel instruction";
   }
@@ -223,7 +221,7 @@
     // literal, id
     const auto id = inst->GetOperandAs<uint32_t>(i + 1);
     const auto target = _.FindDef(id);
-    if (!target || SpvOpLabel != target->opcode()) {
+    if (!target || spv::Op::OpLabel != target->opcode()) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
              << "'Target Label' operands for OpSwitch must be IDs of an "
                 "OpLabel instruction";
@@ -243,14 +241,14 @@
            << " does not represent a value.";
   }
   auto value_type = _.FindDef(value->type_id());
-  if (!value_type || SpvOpTypeVoid == value_type->opcode()) {
+  if (!value_type || spv::Op::OpTypeVoid == value_type->opcode()) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "OpReturnValue value's type <id> "
            << _.getIdName(value->type_id()) << " is missing or void.";
   }
 
-  if (_.addressing_model() == SpvAddressingModelLogical &&
-      SpvOpTypePointer == value_type->opcode() &&
+  if (_.addressing_model() == spv::AddressingModel::Logical &&
+      spv::Op::OpTypePointer == value_type->opcode() &&
       !_.features().variable_pointers && !_.options()->relax_logical_pointer) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "OpReturnValue value's type <id> "
@@ -270,10 +268,15 @@
   return SPV_SUCCESS;
 }
 
+uint32_t operator>>(const spv::LoopControlShift& lhs,
+                    const spv::LoopControlShift& rhs) {
+  return uint32_t(lhs) >> uint32_t(rhs);
+}
+
 spv_result_t ValidateLoopMerge(ValidationState_t& _, const Instruction* inst) {
   const auto merge_id = inst->GetOperandAs<uint32_t>(0);
   const auto merge = _.FindDef(merge_id);
-  if (!merge || merge->opcode() != SpvOpLabel) {
+  if (!merge || merge->opcode() != spv::Op::OpLabel) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "Merge Block " << _.getIdName(merge_id) << " must be an OpLabel";
   }
@@ -284,7 +287,7 @@
 
   const auto continue_id = inst->GetOperandAs<uint32_t>(1);
   const auto continue_target = _.FindDef(continue_id);
-  if (!continue_target || continue_target->opcode() != SpvOpLabel) {
+  if (!continue_target || continue_target->opcode() != spv::Op::OpLabel) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "Continue Target " << _.getIdName(continue_id)
            << " must be an OpLabel";
@@ -295,36 +298,36 @@
            << "Merge Block and Continue Target must be different ids";
   }
 
-  const auto loop_control = inst->GetOperandAs<uint32_t>(2);
-  if ((loop_control >> SpvLoopControlUnrollShift) & 0x1 &&
-      (loop_control >> SpvLoopControlDontUnrollShift) & 0x1) {
+  const auto loop_control = inst->GetOperandAs<spv::LoopControlShift>(2);
+  if ((loop_control >> spv::LoopControlShift::Unroll) & 0x1 &&
+      (loop_control >> spv::LoopControlShift::DontUnroll) & 0x1) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "Unroll and DontUnroll loop controls must not both be specified";
   }
-  if ((loop_control >> SpvLoopControlDontUnrollShift) & 0x1 &&
-      (loop_control >> SpvLoopControlPeelCountShift) & 0x1) {
+  if ((loop_control >> spv::LoopControlShift::DontUnroll) & 0x1 &&
+      (loop_control >> spv::LoopControlShift::PeelCount) & 0x1) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst) << "PeelCount and DontUnroll "
                                                    "loop controls must not "
                                                    "both be specified";
   }
-  if ((loop_control >> SpvLoopControlDontUnrollShift) & 0x1 &&
-      (loop_control >> SpvLoopControlPartialCountShift) & 0x1) {
+  if ((loop_control >> spv::LoopControlShift::DontUnroll) & 0x1 &&
+      (loop_control >> spv::LoopControlShift::PartialCount) & 0x1) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst) << "PartialCount and "
                                                    "DontUnroll loop controls "
                                                    "must not both be specified";
   }
 
   uint32_t operand = 3;
-  if ((loop_control >> SpvLoopControlDependencyLengthShift) & 0x1) {
+  if ((loop_control >> spv::LoopControlShift::DependencyLength) & 0x1) {
     ++operand;
   }
-  if ((loop_control >> SpvLoopControlMinIterationsShift) & 0x1) {
+  if ((loop_control >> spv::LoopControlShift::MinIterations) & 0x1) {
     ++operand;
   }
-  if ((loop_control >> SpvLoopControlMaxIterationsShift) & 0x1) {
+  if ((loop_control >> spv::LoopControlShift::MaxIterations) & 0x1) {
     ++operand;
   }
-  if ((loop_control >> SpvLoopControlIterationMultipleShift) & 0x1) {
+  if ((loop_control >> spv::LoopControlShift::IterationMultiple) & 0x1) {
     if (inst->operands().size() < operand ||
         inst->GetOperandAs<uint32_t>(operand) == 0) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst) << "IterationMultiple loop "
@@ -333,10 +336,10 @@
     }
     ++operand;
   }
-  if ((loop_control >> SpvLoopControlPeelCountShift) & 0x1) {
+  if ((loop_control >> spv::LoopControlShift::PeelCount) & 0x1) {
     ++operand;
   }
-  if ((loop_control >> SpvLoopControlPartialCountShift) & 0x1) {
+  if ((loop_control >> spv::LoopControlShift::PartialCount) & 0x1) {
     ++operand;
   }
 
@@ -554,7 +557,7 @@
           target_block->structurally_reachable() &&
           !header->structurally_dominates(*target_block)) {
         return _.diag(SPV_ERROR_INVALID_CFG, header->label())
-               << "Selection header " << _.getIdName(header->id())
+               << "Switch header " << _.getIdName(header->id())
                << " does not structurally dominate its case construct "
                << _.getIdName(target);
       }
@@ -644,9 +647,9 @@
     const auto index = terminator - &_.ordered_instructions()[0];
     auto* merge = &_.ordered_instructions()[index - 1];
     // Marks merges and continues as seen.
-    if (merge->opcode() == SpvOpSelectionMerge) {
+    if (merge->opcode() == spv::Op::OpSelectionMerge) {
       seen.insert(merge->GetOperandAs<uint32_t>(0));
-    } else if (merge->opcode() == SpvOpLoopMerge) {
+    } else if (merge->opcode() == spv::Op::OpLoopMerge) {
       seen.insert(merge->GetOperandAs<uint32_t>(0));
       seen.insert(merge->GetOperandAs<uint32_t>(1));
     } else {
@@ -657,7 +660,7 @@
     // Skip unreachable blocks.
     if (!block->structurally_reachable()) continue;
 
-    if (terminator->opcode() == SpvOpBranchConditional) {
+    if (terminator->opcode() == spv::Op::OpBranchConditional) {
       const auto true_label = terminator->GetOperandAs<uint32_t>(1);
       const auto false_label = terminator->GetOperandAs<uint32_t>(2);
       // Mark the upcoming blocks as seen now, but only error out if this block
@@ -665,11 +668,12 @@
       // previously.
       const bool true_label_unseen = seen.insert(true_label).second;
       const bool false_label_unseen = seen.insert(false_label).second;
-      if (!merge && true_label_unseen && false_label_unseen) {
+      if ((!merge || merge->opcode() == spv::Op::OpLoopMerge) &&
+          true_label_unseen && false_label_unseen) {
         return _.diag(SPV_ERROR_INVALID_CFG, terminator)
                << "Selection must be structured";
       }
-    } else if (terminator->opcode() == SpvOpSwitch) {
+    } else if (terminator->opcode() == spv::Op::OpSwitch) {
       if (!merge) {
         return _.diag(SPV_ERROR_INVALID_CFG, terminator)
                << "OpSwitch must be preceded by an OpSelectionMerge "
@@ -746,6 +750,7 @@
                                      _.getIdName(merge->id()),
                                      "does not structurally dominate");
     }
+
     // If it's really a merge block for a selection or loop, then it must be
     // *strictly* structrually dominated by the header.
     if (construct.ExitBlockIsMergeBlock() && (header == merge)) {
@@ -798,8 +803,8 @@
           block->is_type(BlockType::kBlockTypeLoop)) {
         size_t index = (block->terminator() - &_.ordered_instructions()[0]) - 1;
         const auto& merge_inst = _.ordered_instructions()[index];
-        if (merge_inst.opcode() == SpvOpSelectionMerge ||
-            merge_inst.opcode() == SpvOpLoopMerge) {
+        if (merge_inst.opcode() == spv::Op::OpSelectionMerge ||
+            merge_inst.opcode() == spv::Op::OpLoopMerge) {
           uint32_t merge_id = merge_inst.GetOperandAs<uint32_t>(0);
           auto merge_block = function->GetBlock(merge_id).first;
           if (merge_block->structurally_reachable() &&
@@ -854,7 +859,7 @@
 
     // Checks rules for case constructs.
     if (construct.type() == ConstructType::kSelection &&
-        header->terminator()->opcode() == SpvOpSwitch) {
+        header->terminator()->opcode() == spv::Op::OpSwitch) {
       const auto terminator = header->terminator();
       if (auto error =
               StructuredSwitchChecks(_, function, terminator, header, merge)) {
@@ -928,7 +933,7 @@
       }
       // If we have structured control flow, check that no block has a control
       // flow nesting depth larger than the limit.
-      if (_.HasCapability(SpvCapabilityShader)) {
+      if (_.HasCapability(spv::Capability::Shader)) {
         const int control_flow_nesting_depth_limit =
             _.options()->universal_limits_.max_control_flow_nesting_depth;
         for (auto block = begin(blocks); block != end(blocks); ++block) {
@@ -942,7 +947,7 @@
     }
 
     /// Structured control flow checks are only required for shader capabilities
-    if (_.HasCapability(SpvCapabilityShader)) {
+    if (_.HasCapability(spv::Capability::Shader)) {
       // Calculate structural dominance.
       postorder.clear();
       std::vector<const BasicBlock*> postdom_postorder;
@@ -998,9 +1003,9 @@
 }
 
 spv_result_t CfgPass(ValidationState_t& _, const Instruction* inst) {
-  SpvOp opcode = inst->opcode();
+  spv::Op opcode = inst->opcode();
   switch (opcode) {
-    case SpvOpLabel:
+    case spv::Op::OpLabel:
       if (auto error = _.current_function().RegisterBlock(inst->id()))
         return error;
 
@@ -1009,7 +1014,7 @@
       // passes the OpLabel ends up not being part of the basic block it starts.
       _.current_function().current_block()->set_label(inst);
       break;
-    case SpvOpLoopMerge: {
+    case spv::Op::OpLoopMerge: {
       uint32_t merge_block = inst->GetOperandAs<uint32_t>(0);
       uint32_t continue_block = inst->GetOperandAs<uint32_t>(1);
       CFG_ASSERT(MergeBlockAssert, merge_block);
@@ -1018,20 +1023,20 @@
                                                               continue_block))
         return error;
     } break;
-    case SpvOpSelectionMerge: {
+    case spv::Op::OpSelectionMerge: {
       uint32_t merge_block = inst->GetOperandAs<uint32_t>(0);
       CFG_ASSERT(MergeBlockAssert, merge_block);
 
       if (auto error = _.current_function().RegisterSelectionMerge(merge_block))
         return error;
     } break;
-    case SpvOpBranch: {
+    case spv::Op::OpBranch: {
       uint32_t target = inst->GetOperandAs<uint32_t>(0);
       CFG_ASSERT(FirstBlockAssert, target);
 
       _.current_function().RegisterBlockEnd({target});
     } break;
-    case SpvOpBranchConditional: {
+    case spv::Op::OpBranchConditional: {
       uint32_t tlabel = inst->GetOperandAs<uint32_t>(1);
       uint32_t flabel = inst->GetOperandAs<uint32_t>(2);
       CFG_ASSERT(FirstBlockAssert, tlabel);
@@ -1040,7 +1045,7 @@
       _.current_function().RegisterBlockEnd({tlabel, flabel});
     } break;
 
-    case SpvOpSwitch: {
+    case spv::Op::OpSwitch: {
       std::vector<uint32_t> cases;
       for (size_t i = 1; i < inst->operands().size(); i += 2) {
         uint32_t target = inst->GetOperandAs<uint32_t>(i);
@@ -1049,44 +1054,44 @@
       }
       _.current_function().RegisterBlockEnd({cases});
     } break;
-    case SpvOpReturn: {
+    case spv::Op::OpReturn: {
       const uint32_t return_type = _.current_function().GetResultTypeId();
       const Instruction* return_type_inst = _.FindDef(return_type);
       assert(return_type_inst);
-      if (return_type_inst->opcode() != SpvOpTypeVoid)
+      if (return_type_inst->opcode() != spv::Op::OpTypeVoid)
         return _.diag(SPV_ERROR_INVALID_CFG, inst)
                << "OpReturn can only be called from a function with void "
                << "return type.";
       _.current_function().RegisterBlockEnd(std::vector<uint32_t>());
       break;
     }
-    case SpvOpKill:
-    case SpvOpReturnValue:
-    case SpvOpUnreachable:
-    case SpvOpTerminateInvocation:
-    case SpvOpIgnoreIntersectionKHR:
-    case SpvOpTerminateRayKHR:
-    case SpvOpEmitMeshTasksEXT:
+    case spv::Op::OpKill:
+    case spv::Op::OpReturnValue:
+    case spv::Op::OpUnreachable:
+    case spv::Op::OpTerminateInvocation:
+    case spv::Op::OpIgnoreIntersectionKHR:
+    case spv::Op::OpTerminateRayKHR:
+    case spv::Op::OpEmitMeshTasksEXT:
       _.current_function().RegisterBlockEnd(std::vector<uint32_t>());
       // Ops with dedicated passes check for the Execution Model there
-      if (opcode == SpvOpKill) {
+      if (opcode == spv::Op::OpKill) {
         _.current_function().RegisterExecutionModelLimitation(
-            SpvExecutionModelFragment,
+            spv::ExecutionModel::Fragment,
             "OpKill requires Fragment execution model");
       }
-      if (opcode == SpvOpTerminateInvocation) {
+      if (opcode == spv::Op::OpTerminateInvocation) {
         _.current_function().RegisterExecutionModelLimitation(
-            SpvExecutionModelFragment,
+            spv::ExecutionModel::Fragment,
             "OpTerminateInvocation requires Fragment execution model");
       }
-      if (opcode == SpvOpIgnoreIntersectionKHR) {
+      if (opcode == spv::Op::OpIgnoreIntersectionKHR) {
         _.current_function().RegisterExecutionModelLimitation(
-            SpvExecutionModelAnyHitKHR,
+            spv::ExecutionModel::AnyHitKHR,
             "OpIgnoreIntersectionKHR requires AnyHitKHR execution model");
       }
-      if (opcode == SpvOpTerminateRayKHR) {
+      if (opcode == spv::Op::OpTerminateRayKHR) {
         _.current_function().RegisterExecutionModelLimitation(
-            SpvExecutionModelAnyHitKHR,
+            spv::ExecutionModel::AnyHitKHR,
             "OpTerminateRayKHR requires AnyHitKHR execution model");
       }
 
@@ -1140,22 +1145,22 @@
 
 spv_result_t ControlFlowPass(ValidationState_t& _, const Instruction* inst) {
   switch (inst->opcode()) {
-    case SpvOpPhi:
+    case spv::Op::OpPhi:
       if (auto error = ValidatePhi(_, inst)) return error;
       break;
-    case SpvOpBranch:
+    case spv::Op::OpBranch:
       if (auto error = ValidateBranch(_, inst)) return error;
       break;
-    case SpvOpBranchConditional:
+    case spv::Op::OpBranchConditional:
       if (auto error = ValidateBranchConditional(_, inst)) return error;
       break;
-    case SpvOpReturnValue:
+    case spv::Op::OpReturnValue:
       if (auto error = ValidateReturnValue(_, inst)) return error;
       break;
-    case SpvOpSwitch:
+    case spv::Op::OpSwitch:
       if (auto error = ValidateSwitch(_, inst)) return error;
       break;
-    case SpvOpLoopMerge:
+    case spv::Op::OpLoopMerge:
       if (auto error = ValidateLoopMerge(_, inst)) return error;
       break;
     default:
diff --git a/source/val/validate_composites.cpp b/source/val/validate_composites.cpp
index c3d948d..ed043b6 100644
--- a/source/val/validate_composites.cpp
+++ b/source/val/validate_composites.cpp
@@ -14,12 +14,10 @@
 
 // Validates correctness of composite SPIR-V instructions.
 
-#include "source/val/validate.h"
-
-#include "source/diagnostic.h"
 #include "source/opcode.h"
 #include "source/spirv_target_env.h"
 #include "source/val/instruction.h"
+#include "source/val/validate.h"
 #include "source/val/validation_state.h"
 
 namespace spvtools {
@@ -35,9 +33,10 @@
 spv_result_t GetExtractInsertValueType(ValidationState_t& _,
                                        const Instruction* inst,
                                        uint32_t* member_type) {
-  const SpvOp opcode = inst->opcode();
-  assert(opcode == SpvOpCompositeExtract || opcode == SpvOpCompositeInsert);
-  uint32_t word_index = opcode == SpvOpCompositeExtract ? 4 : 5;
+  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;
@@ -66,7 +65,7 @@
     const Instruction* const type_inst = _.FindDef(*member_type);
     assert(type_inst);
     switch (type_inst->opcode()) {
-      case SpvOpTypeVector: {
+      case spv::Op::OpTypeVector: {
         *member_type = type_inst->word(2);
         const uint32_t vector_size = type_inst->word(3);
         if (component_index >= vector_size) {
@@ -76,7 +75,7 @@
         }
         break;
       }
-      case SpvOpTypeMatrix: {
+      case spv::Op::OpTypeMatrix: {
         *member_type = type_inst->word(2);
         const uint32_t num_cols = type_inst->word(3);
         if (component_index >= num_cols) {
@@ -86,7 +85,7 @@
         }
         break;
       }
-      case SpvOpTypeArray: {
+      case spv::Op::OpTypeArray: {
         uint64_t array_size = 0;
         auto size = _.FindDef(type_inst->word(3));
         *member_type = type_inst->word(2);
@@ -105,12 +104,12 @@
         }
         break;
       }
-      case SpvOpTypeRuntimeArray: {
+      case spv::Op::OpTypeRuntimeArray: {
         *member_type = type_inst->word(2);
         // Array size is unknown.
         break;
       }
-      case SpvOpTypeStruct: {
+      case spv::Op::OpTypeStruct: {
         const size_t num_struct_members = type_inst->words().size() - 2;
         if (component_index >= num_struct_members) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
@@ -123,7 +122,8 @@
         *member_type = type_inst->word(component_index + 2);
         break;
       }
-      case SpvOpTypeCooperativeMatrixNV: {
+      case spv::Op::OpTypeCooperativeMatrixKHR:
+      case spv::Op::OpTypeCooperativeMatrixNV: {
         *member_type = type_inst->word(2);
         break;
       }
@@ -140,15 +140,15 @@
 spv_result_t ValidateVectorExtractDynamic(ValidationState_t& _,
                                           const Instruction* inst) {
   const uint32_t result_type = inst->type_id();
-  const SpvOp result_opcode = _.GetIdOpcode(result_type);
+  const spv::Op result_opcode = _.GetIdOpcode(result_type);
   if (!spvOpcodeIsScalarType(result_opcode)) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "Expected Result Type to be a scalar type";
   }
 
   const uint32_t vector_type = _.GetOperandTypeId(inst, 2);
-  const SpvOp vector_opcode = _.GetIdOpcode(vector_type);
-  if (vector_opcode != SpvOpTypeVector) {
+  const spv::Op vector_opcode = _.GetIdOpcode(vector_type);
+  if (vector_opcode != spv::Op::OpTypeVector) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "Expected Vector type to be OpTypeVector";
   }
@@ -164,7 +164,7 @@
            << "Expected Index to be int scalar";
   }
 
-  if (_.HasCapability(SpvCapabilityShader) &&
+  if (_.HasCapability(spv::Capability::Shader) &&
       _.ContainsLimitedUseIntOrFloatType(inst->type_id())) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "Cannot extract from a vector of 8- or 16-bit types";
@@ -175,8 +175,8 @@
 spv_result_t ValidateVectorInsertDyanmic(ValidationState_t& _,
                                          const Instruction* inst) {
   const uint32_t result_type = inst->type_id();
-  const SpvOp result_opcode = _.GetIdOpcode(result_type);
-  if (result_opcode != SpvOpTypeVector) {
+  const spv::Op result_opcode = _.GetIdOpcode(result_type);
+  if (result_opcode != spv::Op::OpTypeVector) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "Expected Result Type to be OpTypeVector";
   }
@@ -200,7 +200,7 @@
            << "Expected Index to be int scalar";
   }
 
-  if (_.HasCapability(SpvCapabilityShader) &&
+  if (_.HasCapability(spv::Capability::Shader) &&
       _.ContainsLimitedUseIntOrFloatType(inst->type_id())) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "Cannot insert into a vector of 8- or 16-bit types";
@@ -212,9 +212,9 @@
                                         const Instruction* inst) {
   const uint32_t num_operands = static_cast<uint32_t>(inst->operands().size());
   const uint32_t result_type = inst->type_id();
-  const SpvOp result_opcode = _.GetIdOpcode(result_type);
+  const spv::Op result_opcode = _.GetIdOpcode(result_type);
   switch (result_opcode) {
-    case SpvOpTypeVector: {
+    case spv::Op::OpTypeVector: {
       const uint32_t num_result_components = _.GetDimension(result_type);
       const uint32_t result_component_type = _.GetComponentType(result_type);
       uint32_t given_component_count = 0;
@@ -230,7 +230,7 @@
         if (operand_type == result_component_type) {
           ++given_component_count;
         } else {
-          if (_.GetIdOpcode(operand_type) != SpvOpTypeVector ||
+          if (_.GetIdOpcode(operand_type) != spv::Op::OpTypeVector ||
               _.GetComponentType(operand_type) != result_component_type) {
             return _.diag(SPV_ERROR_INVALID_DATA, inst)
                    << "Expected Constituents to be scalars or vectors of"
@@ -249,7 +249,7 @@
 
       break;
     }
-    case SpvOpTypeMatrix: {
+    case spv::Op::OpTypeMatrix: {
       uint32_t result_num_rows = 0;
       uint32_t result_num_cols = 0;
       uint32_t result_col_type = 0;
@@ -277,10 +277,10 @@
 
       break;
     }
-    case SpvOpTypeArray: {
+    case spv::Op::OpTypeArray: {
       const Instruction* const array_inst = _.FindDef(result_type);
       assert(array_inst);
-      assert(array_inst->opcode() == SpvOpTypeArray);
+      assert(array_inst->opcode() == spv::Op::OpTypeArray);
 
       auto size = _.FindDef(array_inst->word(3));
       if (spvOpcodeIsSpecConstant(size->opcode())) {
@@ -312,10 +312,10 @@
 
       break;
     }
-    case SpvOpTypeStruct: {
+    case spv::Op::OpTypeStruct: {
       const Instruction* const struct_inst = _.FindDef(result_type);
       assert(struct_inst);
-      assert(struct_inst->opcode() == SpvOpTypeStruct);
+      assert(struct_inst->opcode() == spv::Op::OpTypeStruct);
 
       if (struct_inst->operands().size() + 1 != num_operands) {
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
@@ -336,7 +336,26 @@
 
       break;
     }
-    case SpvOpTypeCooperativeMatrixNV: {
+    case spv::Op::OpTypeCooperativeMatrixKHR: {
+      const auto result_type_inst = _.FindDef(result_type);
+      assert(result_type_inst);
+      const auto component_type_id =
+          result_type_inst->GetOperandAs<uint32_t>(1);
+
+      if (3 != num_operands) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Must be only one constituent";
+      }
+
+      const uint32_t operand_type_id = _.GetOperandTypeId(inst, 2);
+
+      if (operand_type_id != component_type_id) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Expected Constituent type to be equal to the component type";
+      }
+      break;
+    }
+    case spv::Op::OpTypeCooperativeMatrixNV: {
       const auto result_type_inst = _.FindDef(result_type);
       assert(result_type_inst);
       const auto component_type_id =
@@ -362,7 +381,7 @@
     }
   }
 
-  if (_.HasCapability(SpvCapabilityShader) &&
+  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";
@@ -386,7 +405,7 @@
            << spvOpcodeString(_.GetIdOpcode(member_type)) << ").";
   }
 
-  if (_.HasCapability(SpvCapabilityShader) &&
+  if (_.HasCapability(spv::Capability::Shader) &&
       _.ContainsLimitedUseIntOrFloatType(inst->type_id())) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "Cannot extract from a composite of 8- or 16-bit types";
@@ -421,7 +440,7 @@
            << spvOpcodeString(_.GetIdOpcode(member_type)) << ").";
   }
 
-  if (_.HasCapability(SpvCapabilityShader) &&
+  if (_.HasCapability(spv::Capability::Shader) &&
       _.ContainsLimitedUseIntOrFloatType(inst->type_id())) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "Cannot insert into a composite of 8- or 16-bit types";
@@ -480,7 +499,7 @@
            << "to be the reverse of those of Result Type";
   }
 
-  if (_.HasCapability(SpvCapabilityShader) &&
+  if (_.HasCapability(spv::Capability::Shader) &&
       _.ContainsLimitedUseIntOrFloatType(inst->type_id())) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "Cannot transpose matrices of 16-bit floats";
@@ -491,11 +510,12 @@
 spv_result_t ValidateVectorShuffle(ValidationState_t& _,
                                    const Instruction* inst) {
   auto resultType = _.FindDef(inst->type_id());
-  if (!resultType || resultType->opcode() != SpvOpTypeVector) {
+  if (!resultType || resultType->opcode() != spv::Op::OpTypeVector) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "The Result Type of OpVectorShuffle must be"
            << " OpTypeVector. Found Op"
-           << spvOpcodeString(static_cast<SpvOp>(resultType->opcode())) << ".";
+           << spvOpcodeString(static_cast<spv::Op>(resultType->opcode()))
+           << ".";
   }
 
   // The number of components in Result Type must be the same as the number of
@@ -515,11 +535,11 @@
   auto vector1Type = _.FindDef(vector1Object->type_id());
   auto vector2Object = _.FindDef(inst->GetOperandAs<uint32_t>(3));
   auto vector2Type = _.FindDef(vector2Object->type_id());
-  if (!vector1Type || vector1Type->opcode() != SpvOpTypeVector) {
+  if (!vector1Type || vector1Type->opcode() != spv::Op::OpTypeVector) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "The type of Vector 1 must be OpTypeVector.";
   }
-  if (!vector2Type || vector2Type->opcode() != SpvOpTypeVector) {
+  if (!vector2Type || vector2Type->opcode() != spv::Op::OpTypeVector) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "The type of Vector 2 must be OpTypeVector.";
   }
@@ -548,7 +568,7 @@
     }
   }
 
-  if (_.HasCapability(SpvCapabilityShader) &&
+  if (_.HasCapability(spv::Capability::Shader) &&
       _.ContainsLimitedUseIntOrFloatType(inst->type_id())) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "Cannot shuffle a vector of 8- or 16-bit types";
@@ -572,7 +592,7 @@
            << "Result Type does not logically match the Operand type";
   }
 
-  if (_.HasCapability(SpvCapabilityShader) &&
+  if (_.HasCapability(spv::Capability::Shader) &&
       _.ContainsLimitedUseIntOrFloatType(inst->type_id())) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "Cannot copy composites of 8- or 16-bit types";
@@ -586,23 +606,23 @@
 // Validates correctness of composite instructions.
 spv_result_t CompositesPass(ValidationState_t& _, const Instruction* inst) {
   switch (inst->opcode()) {
-    case SpvOpVectorExtractDynamic:
+    case spv::Op::OpVectorExtractDynamic:
       return ValidateVectorExtractDynamic(_, inst);
-    case SpvOpVectorInsertDynamic:
+    case spv::Op::OpVectorInsertDynamic:
       return ValidateVectorInsertDyanmic(_, inst);
-    case SpvOpVectorShuffle:
+    case spv::Op::OpVectorShuffle:
       return ValidateVectorShuffle(_, inst);
-    case SpvOpCompositeConstruct:
+    case spv::Op::OpCompositeConstruct:
       return ValidateCompositeConstruct(_, inst);
-    case SpvOpCompositeExtract:
+    case spv::Op::OpCompositeExtract:
       return ValidateCompositeExtract(_, inst);
-    case SpvOpCompositeInsert:
+    case spv::Op::OpCompositeInsert:
       return ValidateCompositeInsert(_, inst);
-    case SpvOpCopyObject:
+    case spv::Op::OpCopyObject:
       return ValidateCopyObject(_, inst);
-    case SpvOpTranspose:
+    case spv::Op::OpTranspose:
       return ValidateTranspose(_, inst);
-    case SpvOpCopyLogical:
+    case spv::Op::OpCopyLogical:
       return ValidateCopyLogical(_, inst);
     default:
       break;
diff --git a/source/val/validate_constants.cpp b/source/val/validate_constants.cpp
index fdfaea5..4deaa49 100644
--- a/source/val/validate_constants.cpp
+++ b/source/val/validate_constants.cpp
@@ -24,7 +24,7 @@
 spv_result_t ValidateConstantBool(ValidationState_t& _,
                                   const Instruction* inst) {
   auto type = _.FindDef(inst->type_id());
-  if (!type || type->opcode() != SpvOpTypeBool) {
+  if (!type || type->opcode() != spv::Op::OpTypeBool) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "Op" << spvOpcodeString(inst->opcode()) << " Result Type <id> "
            << _.getIdName(inst->type_id()) << " is not a boolean type.";
@@ -46,7 +46,7 @@
 
   const auto constituent_count = inst->words().size() - 3;
   switch (result_type->opcode()) {
-    case SpvOpTypeVector: {
+    case spv::Op::OpTypeVector: {
       const auto component_count = result_type->GetOperandAs<uint32_t>(2);
       if (component_count != constituent_count) {
         // TODO: Output ID's on diagnostic
@@ -76,7 +76,7 @@
         }
         const auto constituent_result_type = _.FindDef(constituent->type_id());
         if (!constituent_result_type ||
-            component_type->opcode() != constituent_result_type->opcode()) {
+            component_type->id() != constituent_result_type->id()) {
           return _.diag(SPV_ERROR_INVALID_ID, inst)
                  << opcode_name << " Constituent <id> "
                  << _.getIdName(constituent_id)
@@ -85,7 +85,7 @@
         }
       }
     } break;
-    case SpvOpTypeMatrix: {
+    case spv::Op::OpTypeMatrix: {
       const auto column_count = result_type->GetOperandAs<uint32_t>(2);
       if (column_count != constituent_count) {
         // TODO: Output ID's on diagnostic
@@ -155,7 +155,7 @@
         }
       }
     } break;
-    case SpvOpTypeArray: {
+    case spv::Op::OpTypeArray: {
       auto element_type = _.FindDef(result_type->GetOperandAs<uint32_t>(1));
       if (!element_type) {
         return _.diag(SPV_ERROR_INVALID_ID, result_type)
@@ -203,7 +203,7 @@
         }
       }
     } break;
-    case SpvOpTypeStruct: {
+    case spv::Op::OpTypeStruct: {
       const auto member_count = result_type->words().size() - 2;
       if (member_count != constituent_count) {
         return _.diag(SPV_ERROR_INVALID_ID, inst)
@@ -243,7 +243,8 @@
         }
       }
     } break;
-    case SpvOpTypeCooperativeMatrixNV: {
+    case spv::Op::OpTypeCooperativeMatrixKHR:
+    case spv::Op::OpTypeCooperativeMatrixNV: {
       if (1 != constituent_count) {
         return _.diag(SPV_ERROR_INVALID_ID, inst)
                << opcode_name << " Constituent <id> "
@@ -281,7 +282,7 @@
 spv_result_t ValidateConstantSampler(ValidationState_t& _,
                                      const Instruction* inst) {
   const auto result_type = _.FindDef(inst->type_id());
-  if (!result_type || result_type->opcode() != SpvOpTypeSampler) {
+  if (!result_type || result_type->opcode() != spv::Op::OpTypeSampler) {
     return _.diag(SPV_ERROR_INVALID_ID, result_type)
            << "OpConstantSampler Result Type <id> "
            << _.getIdName(inst->type_id()) << " is not a sampler type.";
@@ -298,23 +299,24 @@
   uint16_t opcode;
   uint16_t word_count;
   spvOpcodeSplit(instruction[0], &word_count, &opcode);
-  switch (static_cast<SpvOp>(opcode)) {
-    case SpvOpTypeBool:
-    case SpvOpTypeInt:
-    case SpvOpTypeFloat:
-    case SpvOpTypeEvent:
-    case SpvOpTypeDeviceEvent:
-    case SpvOpTypeReserveId:
-    case SpvOpTypeQueue:
+  switch (static_cast<spv::Op>(opcode)) {
+    case spv::Op::OpTypeBool:
+    case spv::Op::OpTypeInt:
+    case spv::Op::OpTypeFloat:
+    case spv::Op::OpTypeEvent:
+    case spv::Op::OpTypeDeviceEvent:
+    case spv::Op::OpTypeReserveId:
+    case spv::Op::OpTypeQueue:
       return true;
-    case SpvOpTypeArray:
-    case SpvOpTypeMatrix:
-    case SpvOpTypeCooperativeMatrixNV:
-    case SpvOpTypeVector: {
+    case spv::Op::OpTypeArray:
+    case spv::Op::OpTypeMatrix:
+    case spv::Op::OpTypeCooperativeMatrixNV:
+    case spv::Op::OpTypeCooperativeMatrixKHR:
+    case spv::Op::OpTypeVector: {
       auto base_type = _.FindDef(instruction[2]);
       return base_type && IsTypeNullable(base_type->words(), _);
     }
-    case SpvOpTypeStruct: {
+    case spv::Op::OpTypeStruct: {
       for (size_t elementIndex = 2; elementIndex < instruction.size();
            ++elementIndex) {
         auto element = _.FindDef(instruction[elementIndex]);
@@ -322,8 +324,9 @@
       }
       return true;
     }
-    case SpvOpTypePointer:
-      if (instruction[2] == SpvStorageClassPhysicalStorageBuffer) {
+    case spv::Op::OpTypePointer:
+      if (spv::StorageClass(instruction[2]) ==
+          spv::StorageClass::PhysicalStorageBuffer) {
         return false;
       }
       return true;
@@ -351,7 +354,8 @@
   auto type_id = inst->GetOperandAs<const uint32_t>(0);
   auto type_instruction = _.FindDef(type_id);
   auto type_opcode = type_instruction->opcode();
-  if (type_opcode != SpvOpTypeInt && type_opcode != SpvOpTypeFloat) {
+  if (type_opcode != spv::Op::OpTypeInt &&
+      type_opcode != spv::Op::OpTypeFloat) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Specialization constant "
                                                    "must be an integer or "
                                                    "floating-point number.";
@@ -361,22 +365,22 @@
 
 spv_result_t ValidateSpecConstantOp(ValidationState_t& _,
                                     const Instruction* inst) {
-  const auto op = inst->GetOperandAs<SpvOp>(2);
+  const auto op = inst->GetOperandAs<spv::Op>(2);
 
   // The binary parser already ensures that the op is valid for *some*
   // environment.  Here we check restrictions.
   switch (op) {
-    case SpvOpQuantizeToF16:
-      if (!_.HasCapability(SpvCapabilityShader)) {
+    case spv::Op::OpQuantizeToF16:
+      if (!_.HasCapability(spv::Capability::Shader)) {
         return _.diag(SPV_ERROR_INVALID_ID, inst)
                << "Specialization constant operation " << spvOpcodeString(op)
                << " requires Shader capability";
       }
       break;
 
-    case SpvOpUConvert:
+    case spv::Op::OpUConvert:
       if (!_.features().uconvert_spec_constant_op &&
-          !_.HasCapability(SpvCapabilityKernel)) {
+          !_.HasCapability(spv::Capability::Kernel)) {
         return _.diag(SPV_ERROR_INVALID_ID, inst)
                << "Prior to SPIR-V 1.4, specialization constant operation "
                   "UConvert requires Kernel capability or extension "
@@ -384,27 +388,27 @@
       }
       break;
 
-    case SpvOpConvertFToS:
-    case SpvOpConvertSToF:
-    case SpvOpConvertFToU:
-    case SpvOpConvertUToF:
-    case SpvOpConvertPtrToU:
-    case SpvOpConvertUToPtr:
-    case SpvOpGenericCastToPtr:
-    case SpvOpPtrCastToGeneric:
-    case SpvOpBitcast:
-    case SpvOpFNegate:
-    case SpvOpFAdd:
-    case SpvOpFSub:
-    case SpvOpFMul:
-    case SpvOpFDiv:
-    case SpvOpFRem:
-    case SpvOpFMod:
-    case SpvOpAccessChain:
-    case SpvOpInBoundsAccessChain:
-    case SpvOpPtrAccessChain:
-    case SpvOpInBoundsPtrAccessChain:
-      if (!_.HasCapability(SpvCapabilityKernel)) {
+    case spv::Op::OpConvertFToS:
+    case spv::Op::OpConvertSToF:
+    case spv::Op::OpConvertFToU:
+    case spv::Op::OpConvertUToF:
+    case spv::Op::OpConvertPtrToU:
+    case spv::Op::OpConvertUToPtr:
+    case spv::Op::OpGenericCastToPtr:
+    case spv::Op::OpPtrCastToGeneric:
+    case spv::Op::OpBitcast:
+    case spv::Op::OpFNegate:
+    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::OpAccessChain:
+    case spv::Op::OpInBoundsAccessChain:
+    case spv::Op::OpPtrAccessChain:
+    case spv::Op::OpInBoundsPtrAccessChain:
+      if (!_.HasCapability(spv::Capability::Kernel)) {
         return _.diag(SPV_ERROR_INVALID_ID, inst)
                << "Specialization constant operation " << spvOpcodeString(op)
                << " requires Kernel capability";
@@ -423,26 +427,26 @@
 
 spv_result_t ConstantPass(ValidationState_t& _, const Instruction* inst) {
   switch (inst->opcode()) {
-    case SpvOpConstantTrue:
-    case SpvOpConstantFalse:
-    case SpvOpSpecConstantTrue:
-    case SpvOpSpecConstantFalse:
+    case spv::Op::OpConstantTrue:
+    case spv::Op::OpConstantFalse:
+    case spv::Op::OpSpecConstantTrue:
+    case spv::Op::OpSpecConstantFalse:
       if (auto error = ValidateConstantBool(_, inst)) return error;
       break;
-    case SpvOpConstantComposite:
-    case SpvOpSpecConstantComposite:
+    case spv::Op::OpConstantComposite:
+    case spv::Op::OpSpecConstantComposite:
       if (auto error = ValidateConstantComposite(_, inst)) return error;
       break;
-    case SpvOpConstantSampler:
+    case spv::Op::OpConstantSampler:
       if (auto error = ValidateConstantSampler(_, inst)) return error;
       break;
-    case SpvOpConstantNull:
+    case spv::Op::OpConstantNull:
       if (auto error = ValidateConstantNull(_, inst)) return error;
       break;
-    case SpvOpSpecConstant:
+    case spv::Op::OpSpecConstant:
       if (auto error = ValidateSpecConstant(_, inst)) return error;
       break;
-    case SpvOpSpecConstantOp:
+    case spv::Op::OpSpecConstantOp:
       if (auto error = ValidateSpecConstantOp(_, inst)) return error;
       break;
     default:
@@ -452,7 +456,7 @@
   // Generally disallow creating 8- or 16-bit constants unless the full
   // capabilities are present.
   if (spvOpcodeIsConstant(inst->opcode()) &&
-      _.HasCapability(SpvCapabilityShader) &&
+      _.HasCapability(spv::Capability::Shader) &&
       !_.IsPointerType(inst->type_id()) &&
       _.ContainsLimitedUseIntOrFloatType(inst->type_id())) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
diff --git a/source/val/validate_conversion.cpp b/source/val/validate_conversion.cpp
index dc6b151..b2892a8 100644
--- a/source/val/validate_conversion.cpp
+++ b/source/val/validate_conversion.cpp
@@ -14,7 +14,6 @@
 
 // Validates correctness of conversion instructions.
 
-#include "source/diagnostic.h"
 #include "source/opcode.h"
 #include "source/spirv_constant.h"
 #include "source/spirv_target_env.h"
@@ -27,11 +26,11 @@
 
 // Validates correctness of conversion instructions.
 spv_result_t ConversionPass(ValidationState_t& _, const Instruction* inst) {
-  const SpvOp opcode = inst->opcode();
+  const spv::Op opcode = inst->opcode();
   const uint32_t result_type = inst->type_id();
 
   switch (opcode) {
-    case SpvOpConvertFToU: {
+    case spv::Op::OpConvertFToU: {
       if (!_.IsUnsignedIntScalarType(result_type) &&
           !_.IsUnsignedIntVectorType(result_type) &&
           !_.IsUnsignedIntCooperativeMatrixType(result_type))
@@ -62,7 +61,7 @@
       break;
     }
 
-    case SpvOpConvertFToS: {
+    case spv::Op::OpConvertFToS: {
       if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type) &&
           !_.IsIntCooperativeMatrixType(result_type))
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
@@ -92,8 +91,8 @@
       break;
     }
 
-    case SpvOpConvertSToF:
-    case SpvOpConvertUToF: {
+    case spv::Op::OpConvertSToF:
+    case spv::Op::OpConvertUToF: {
       if (!_.IsFloatScalarType(result_type) &&
           !_.IsFloatVectorType(result_type) &&
           !_.IsFloatCooperativeMatrixType(result_type))
@@ -124,7 +123,7 @@
       break;
     }
 
-    case SpvOpUConvert: {
+    case spv::Op::OpUConvert: {
       if (!_.IsUnsignedIntScalarType(result_type) &&
           !_.IsUnsignedIntVectorType(result_type) &&
           !_.IsUnsignedIntCooperativeMatrixType(result_type))
@@ -160,7 +159,7 @@
       break;
     }
 
-    case SpvOpSConvert: {
+    case spv::Op::OpSConvert: {
       if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type) &&
           !_.IsIntCooperativeMatrixType(result_type))
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
@@ -195,7 +194,7 @@
       break;
     }
 
-    case SpvOpFConvert: {
+    case spv::Op::OpFConvert: {
       if (!_.IsFloatScalarType(result_type) &&
           !_.IsFloatVectorType(result_type) &&
           !_.IsFloatCooperativeMatrixType(result_type))
@@ -231,7 +230,7 @@
       break;
     }
 
-    case SpvOpQuantizeToF16: {
+    case spv::Op::OpQuantizeToF16: {
       if ((!_.IsFloatScalarType(result_type) &&
            !_.IsFloatVectorType(result_type)) ||
           _.GetBitWidth(result_type) != 32)
@@ -247,7 +246,7 @@
       break;
     }
 
-    case SpvOpConvertPtrToU: {
+    case spv::Op::OpConvertPtrToU: {
       if (!_.IsUnsignedIntScalarType(result_type))
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
                << "Expected unsigned int scalar type as Result Type: "
@@ -258,17 +257,18 @@
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
                << "Expected input to be a pointer: " << spvOpcodeString(opcode);
 
-      if (_.addressing_model() == SpvAddressingModelLogical)
+      if (_.addressing_model() == spv::AddressingModel::Logical)
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
                << "Logical addressing not supported: "
                << spvOpcodeString(opcode);
 
-      if (_.addressing_model() == SpvAddressingModelPhysicalStorageBuffer64) {
-        uint32_t input_storage_class = 0;
+      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 != SpvStorageClassPhysicalStorageBuffer)
+        if (input_storage_class != spv::StorageClass::PhysicalStorageBuffer)
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
                  << "Pointer storage class must be PhysicalStorageBuffer: "
                  << spvOpcodeString(opcode);
@@ -286,8 +286,8 @@
       break;
     }
 
-    case SpvOpSatConvertSToU:
-    case SpvOpSatConvertUToS: {
+    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: "
@@ -307,7 +307,7 @@
       break;
     }
 
-    case SpvOpConvertUToPtr: {
+    case spv::Op::OpConvertUToPtr: {
       if (!_.IsPointerType(result_type))
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
                << "Expected Result Type to be a pointer: "
@@ -318,17 +318,18 @@
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
                << "Expected int scalar as input: " << spvOpcodeString(opcode);
 
-      if (_.addressing_model() == SpvAddressingModelLogical)
+      if (_.addressing_model() == spv::AddressingModel::Logical)
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
                << "Logical addressing not supported: "
                << spvOpcodeString(opcode);
 
-      if (_.addressing_model() == SpvAddressingModelPhysicalStorageBuffer64) {
-        uint32_t result_storage_class = 0;
+      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 != SpvStorageClassPhysicalStorageBuffer)
+        if (result_storage_class != spv::StorageClass::PhysicalStorageBuffer)
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
                  << "Pointer storage class must be PhysicalStorageBuffer: "
                  << spvOpcodeString(opcode);
@@ -346,8 +347,8 @@
       break;
     }
 
-    case SpvOpPtrCastToGeneric: {
-      uint32_t result_storage_class = 0;
+    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))
@@ -355,22 +356,22 @@
                << "Expected Result Type to be a pointer: "
                << spvOpcodeString(opcode);
 
-      if (result_storage_class != SpvStorageClassGeneric)
+      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);
-      uint32_t input_storage_class = 0;
+      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 != SpvStorageClassWorkgroup &&
-          input_storage_class != SpvStorageClassCrossWorkgroup &&
-          input_storage_class != SpvStorageClassFunction)
+      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);
@@ -382,8 +383,8 @@
       break;
     }
 
-    case SpvOpGenericCastToPtr: {
-      uint32_t result_storage_class = 0;
+    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))
@@ -391,22 +392,22 @@
                << "Expected Result Type to be a pointer: "
                << spvOpcodeString(opcode);
 
-      if (result_storage_class != SpvStorageClassWorkgroup &&
-          result_storage_class != SpvStorageClassCrossWorkgroup &&
-          result_storage_class != SpvStorageClassFunction)
+      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);
-      uint32_t input_storage_class = 0;
+      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 != SpvStorageClassGeneric)
+      if (input_storage_class != spv::StorageClass::Generic)
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
                << "Expected input to have storage class Generic: "
                << spvOpcodeString(opcode);
@@ -418,8 +419,8 @@
       break;
     }
 
-    case SpvOpGenericCastToPtrExplicit: {
-      uint32_t result_storage_class = 0;
+    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))
@@ -427,21 +428,22 @@
                << "Expected Result Type to be a pointer: "
                << spvOpcodeString(opcode);
 
-      const uint32_t target_storage_class = inst->word(4);
+      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);
-      uint32_t input_storage_class = 0;
+      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 != SpvStorageClassGeneric)
+      if (input_storage_class != spv::StorageClass::Generic)
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
                << "Expected input to have storage class Generic: "
                << spvOpcodeString(opcode);
@@ -451,16 +453,16 @@
                << "Expected input and Result Type to point to the same type: "
                << spvOpcodeString(opcode);
 
-      if (target_storage_class != SpvStorageClassWorkgroup &&
-          target_storage_class != SpvStorageClassCrossWorkgroup &&
-          target_storage_class != SpvStorageClassFunction)
+      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 SpvOpBitcast: {
+    case spv::Op::OpBitcast: {
       const uint32_t input_type = _.GetOperandTypeId(inst, 2);
       if (!input_type)
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
@@ -471,7 +473,10 @@
       const bool input_is_pointer = _.IsPointerType(input_type);
       const bool input_is_int_scalar = _.IsIntScalarType(input_type);
 
-      if (!result_is_pointer && !result_is_int_scalar &&
+      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))
@@ -479,21 +484,32 @@
                << "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 &&
+      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);
+        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, SpvOpTypeInt, 32);
+            _.ContainsSizedIntOrFloatType(result_type, spv::Op::OpTypeInt, 32);
         const bool input_is_int_vector = _.IsIntVectorType(input_type);
         const bool input_has_int32 =
-            _.ContainsSizedIntOrFloatType(input_type, SpvOpTypeInt, 32);
+            _.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)
@@ -534,7 +550,7 @@
       break;
     }
 
-    case SpvOpConvertUToAccelerationStructureKHR: {
+    case spv::Op::OpConvertUToAccelerationStructureKHR: {
       if (!_.IsAccelerationStructureType(result_type)) {
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
                << "Expected Result Type to be a Acceleration Structure: "
@@ -556,13 +572,13 @@
       break;
   }
 
-  if (_.HasCapability(SpvCapabilityShader)) {
+  if (_.HasCapability(spv::Capability::Shader)) {
     switch (inst->opcode()) {
-      case SpvOpConvertFToU:
-      case SpvOpConvertFToS:
-      case SpvOpConvertSToF:
-      case SpvOpConvertUToF:
-      case SpvOpBitcast:
+      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)
diff --git a/source/val/validate_debug.cpp b/source/val/validate_debug.cpp
index 7ab597a..ef537ea 100644
--- a/source/val/validate_debug.cpp
+++ b/source/val/validate_debug.cpp
@@ -12,11 +12,9 @@
 // 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/validation_state.h"
 
 namespace spvtools {
@@ -26,7 +24,7 @@
 spv_result_t ValidateMemberName(ValidationState_t& _, const Instruction* inst) {
   const auto type_id = inst->GetOperandAs<uint32_t>(0);
   const auto type = _.FindDef(type_id);
-  if (!type || SpvOpTypeStruct != type->opcode()) {
+  if (!type || spv::Op::OpTypeStruct != type->opcode()) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "OpMemberName Type <id> " << _.getIdName(type_id)
            << " is not a struct type.";
@@ -45,7 +43,7 @@
 spv_result_t ValidateLine(ValidationState_t& _, const Instruction* inst) {
   const auto file_id = inst->GetOperandAs<uint32_t>(0);
   const auto file = _.FindDef(file_id);
-  if (!file || SpvOpString != file->opcode()) {
+  if (!file || spv::Op::OpString != file->opcode()) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "OpLine Target <id> " << _.getIdName(file_id)
            << " is not an OpString.";
@@ -57,10 +55,10 @@
 
 spv_result_t DebugPass(ValidationState_t& _, const Instruction* inst) {
   switch (inst->opcode()) {
-    case SpvOpMemberName:
+    case spv::Op::OpMemberName:
       if (auto error = ValidateMemberName(_, inst)) return error;
       break;
-    case SpvOpLine:
+    case spv::Op::OpLine:
       if (auto error = ValidateLine(_, inst)) return error;
       break;
     default:
diff --git a/source/val/validate_decorations.cpp b/source/val/validate_decorations.cpp
index 7505850..605b79e 100644
--- a/source/val/validate_decorations.cpp
+++ b/source/val/validate_decorations.cpp
@@ -21,7 +21,6 @@
 #include <utility>
 #include <vector>
 
-#include "source/binary.h"
 #include "source/diagnostic.h"
 #include "source/opcode.h"
 #include "source/spirv_constant.h"
@@ -48,13 +47,6 @@
   }
 };
 
-// A functor for hashing decoration types.
-struct SpvDecorationHash {
-  std::size_t operator()(SpvDecoration dec) const {
-    return static_cast<std::size_t>(dec);
-  }
-};
-
 // Struct member layout attributes that are inherited through arrays.
 struct LayoutConstraints {
   explicit LayoutConstraints(
@@ -72,7 +64,7 @@
 // Returns the array stride of the given array type.
 uint32_t GetArrayStride(uint32_t array_id, ValidationState_t& vstate) {
   for (auto& decoration : vstate.id_decorations(array_id)) {
-    if (SpvDecorationArrayStride == decoration.dec_type()) {
+    if (spv::Decoration::ArrayStride == decoration.dec_type()) {
       return decoration.params()[0];
     }
   }
@@ -82,9 +74,10 @@
 // Returns true if the given variable has a BuiltIn decoration.
 bool isBuiltInVar(uint32_t var_id, ValidationState_t& vstate) {
   const auto& decorations = vstate.id_decorations(var_id);
-  return std::any_of(
-      decorations.begin(), decorations.end(),
-      [](const Decoration& d) { return SpvDecorationBuiltIn == d.dec_type(); });
+  return std::any_of(decorations.begin(), decorations.end(),
+                     [](const Decoration& d) {
+                       return spv::Decoration::BuiltIn == d.dec_type();
+                     });
 }
 
 // Returns true if the given structure type has any members with BuiltIn
@@ -93,7 +86,7 @@
   const auto& decorations = vstate.id_decorations(struct_id);
   return std::any_of(
       decorations.begin(), decorations.end(), [](const Decoration& d) {
-        return SpvDecorationBuiltIn == d.dec_type() &&
+        return spv::Decoration::BuiltIn == d.dec_type() &&
                Decoration::kInvalidMember != d.struct_member_index();
       });
 }
@@ -101,20 +94,21 @@
 // Returns true if the given structure type has a Block decoration.
 bool isBlock(uint32_t struct_id, ValidationState_t& vstate) {
   const auto& decorations = vstate.id_decorations(struct_id);
-  return std::any_of(
-      decorations.begin(), decorations.end(),
-      [](const Decoration& d) { return SpvDecorationBlock == d.dec_type(); });
+  return std::any_of(decorations.begin(), decorations.end(),
+                     [](const Decoration& d) {
+                       return spv::Decoration::Block == d.dec_type();
+                     });
 }
 
 // Returns true if the given ID has the Import LinkageAttributes decoration.
 bool hasImportLinkageAttribute(uint32_t id, ValidationState_t& vstate) {
   const auto& decorations = vstate.id_decorations(id);
-  return std::any_of(decorations.begin(), decorations.end(),
-                     [](const Decoration& d) {
-                       return SpvDecorationLinkageAttributes == d.dec_type() &&
-                              d.params().size() >= 2u &&
-                              d.params().back() == SpvLinkageTypeImport;
-                     });
+  return std::any_of(
+      decorations.begin(), decorations.end(), [](const Decoration& d) {
+        return spv::Decoration::LinkageAttributes == d.dec_type() &&
+               d.params().size() >= 2u &&
+               spv::LinkageType(d.params().back()) == spv::LinkageType::Import;
+      });
 }
 
 // Returns a vector of all members of a structure.
@@ -125,7 +119,7 @@
 }
 
 // Returns a vector of all members of a structure that have specific type.
-std::vector<uint32_t> getStructMembers(uint32_t struct_id, SpvOp type,
+std::vector<uint32_t> getStructMembers(uint32_t struct_id, spv::Op type,
                                        ValidationState_t& vstate) {
   std::vector<uint32_t> members;
   for (auto id : getStructMembers(struct_id, vstate)) {
@@ -142,21 +136,21 @@
   const auto* inst = vstate.FindDef(struct_id);
   std::vector<bool> hasOffset;
   std::vector<uint32_t> struct_members;
-  if (inst->opcode() == SpvOpTypeStruct) {
+  if (inst->opcode() == spv::Op::OpTypeStruct) {
     // Check offsets of member decorations.
     struct_members = getStructMembers(struct_id, vstate);
     hasOffset.resize(struct_members.size(), false);
 
     for (auto& decoration : vstate.id_decorations(struct_id)) {
-      if (SpvDecorationOffset == decoration.dec_type() &&
+      if (spv::Decoration::Offset == 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;
         hasOffset[decoration.struct_member_index()] = true;
       }
     }
-  } else if (inst->opcode() == SpvOpTypeArray ||
-             inst->opcode() == SpvOpTypeRuntimeArray) {
+  } else if (inst->opcode() == spv::Op::OpTypeArray ||
+             inst->opcode() == spv::Op::OpTypeRuntimeArray) {
     hasOffset.resize(1, true);
     struct_members.push_back(inst->GetOperandAs<uint32_t>(1u));
   }
@@ -191,18 +185,18 @@
   // Minimal alignment is byte-aligned.
   uint32_t baseAlignment = 1;
   switch (inst->opcode()) {
-    case SpvOpTypeSampledImage:
-    case SpvOpTypeSampler:
-    case SpvOpTypeImage:
-      if (vstate.HasCapability(SpvCapabilityBindlessTextureNV))
+    case spv::Op::OpTypeSampledImage:
+    case spv::Op::OpTypeSampler:
+    case spv::Op::OpTypeImage:
+      if (vstate.HasCapability(spv::Capability::BindlessTextureNV))
         return baseAlignment = vstate.samplerimage_variable_address_mode() / 8;
       assert(0);
       return 0;
-    case SpvOpTypeInt:
-    case SpvOpTypeFloat:
+    case spv::Op::OpTypeInt:
+    case spv::Op::OpTypeFloat:
       baseAlignment = words[2] / 8;
       break;
-    case SpvOpTypeVector: {
+    case spv::Op::OpTypeVector: {
       const auto componentId = words[2];
       const auto numComponents = words[3];
       const auto componentAlignment = getBaseAlignment(
@@ -211,7 +205,7 @@
           componentAlignment * (numComponents == 3 ? 4 : numComponents);
       break;
     }
-    case SpvOpTypeMatrix: {
+    case spv::Op::OpTypeMatrix: {
       const auto column_type = words[2];
       if (inherited.majorness == kColumnMajor) {
         baseAlignment = getBaseAlignment(column_type, roundUp, inherited,
@@ -229,13 +223,13 @@
       }
       if (roundUp) baseAlignment = align(baseAlignment, 16u);
     } break;
-    case SpvOpTypeArray:
-    case SpvOpTypeRuntimeArray:
+    case spv::Op::OpTypeArray:
+    case spv::Op::OpTypeRuntimeArray:
       baseAlignment =
           getBaseAlignment(words[2], roundUp, inherited, constraints, vstate);
       if (roundUp) baseAlignment = align(baseAlignment, 16u);
       break;
-    case SpvOpTypeStruct: {
+    case spv::Op::OpTypeStruct: {
       const auto members = getStructMembers(member_id, vstate);
       for (uint32_t memberIdx = 0, numMembers = uint32_t(members.size());
            memberIdx < numMembers; ++memberIdx) {
@@ -249,7 +243,7 @@
       if (roundUp) baseAlignment = align(baseAlignment, 16u);
       break;
     }
-    case SpvOpTypePointer:
+    case spv::Op::OpTypePointer:
       baseAlignment = vstate.pointer_size_and_alignment();
       break;
     default:
@@ -265,24 +259,24 @@
   const auto inst = vstate.FindDef(type_id);
   const auto& words = inst->words();
   switch (inst->opcode()) {
-    case SpvOpTypeSampledImage:
-    case SpvOpTypeSampler:
-    case SpvOpTypeImage:
-      if (vstate.HasCapability(SpvCapabilityBindlessTextureNV))
+    case spv::Op::OpTypeSampledImage:
+    case spv::Op::OpTypeSampler:
+    case spv::Op::OpTypeImage:
+      if (vstate.HasCapability(spv::Capability::BindlessTextureNV))
         return vstate.samplerimage_variable_address_mode() / 8;
       assert(0);
       return 0;
-    case SpvOpTypeInt:
-    case SpvOpTypeFloat:
+    case spv::Op::OpTypeInt:
+    case spv::Op::OpTypeFloat:
       return words[2] / 8;
-    case SpvOpTypeVector:
-    case SpvOpTypeMatrix:
-    case SpvOpTypeArray:
-    case SpvOpTypeRuntimeArray: {
+    case spv::Op::OpTypeVector:
+    case spv::Op::OpTypeMatrix:
+    case spv::Op::OpTypeArray:
+    case spv::Op::OpTypeRuntimeArray: {
       const auto compositeMemberTypeId = words[2];
       return getScalarAlignment(compositeMemberTypeId, vstate);
     }
-    case SpvOpTypeStruct: {
+    case spv::Op::OpTypeStruct: {
       const auto members = getStructMembers(type_id, vstate);
       uint32_t max_member_alignment = 1;
       for (uint32_t memberIdx = 0, numMembers = uint32_t(members.size());
@@ -295,7 +289,7 @@
       }
       return max_member_alignment;
     } break;
-    case SpvOpTypePointer:
+    case spv::Op::OpTypePointer:
       return vstate.pointer_size_and_alignment();
     default:
       assert(0);
@@ -312,17 +306,17 @@
   const auto inst = vstate.FindDef(member_id);
   const auto& words = inst->words();
   switch (inst->opcode()) {
-    case SpvOpTypeSampledImage:
-    case SpvOpTypeSampler:
-    case SpvOpTypeImage:
-      if (vstate.HasCapability(SpvCapabilityBindlessTextureNV))
+    case spv::Op::OpTypeSampledImage:
+    case spv::Op::OpTypeSampler:
+    case spv::Op::OpTypeImage:
+      if (vstate.HasCapability(spv::Capability::BindlessTextureNV))
         return vstate.samplerimage_variable_address_mode() / 8;
       assert(0);
       return 0;
-    case SpvOpTypeInt:
-    case SpvOpTypeFloat:
+    case spv::Op::OpTypeInt:
+    case spv::Op::OpTypeFloat:
       return words[2] / 8;
-    case SpvOpTypeVector: {
+    case spv::Op::OpTypeVector: {
       const auto componentId = words[2];
       const auto numComponents = words[3];
       const auto componentSize =
@@ -330,10 +324,10 @@
       const auto size = componentSize * numComponents;
       return size;
     }
-    case SpvOpTypeArray: {
+    case spv::Op::OpTypeArray: {
       const auto sizeInst = vstate.FindDef(words[3]);
       if (spvOpcodeIsSpecConstant(sizeInst->opcode())) return 0;
-      assert(SpvOpConstant == sizeInst->opcode());
+      assert(spv::Op::OpConstant == sizeInst->opcode());
       const uint32_t num_elem = sizeInst->words()[3];
       const uint32_t elem_type = words[2];
       const uint32_t elem_size =
@@ -344,9 +338,9 @@
           (num_elem - 1) * GetArrayStride(member_id, vstate) + elem_size;
       return size;
     }
-    case SpvOpTypeRuntimeArray:
+    case spv::Op::OpTypeRuntimeArray:
       return 0;
-    case SpvOpTypeMatrix: {
+    case spv::Op::OpTypeMatrix: {
       const auto num_columns = words[3];
       if (inherited.majorness == kColumnMajor) {
         return num_columns * inherited.matrix_stride;
@@ -362,7 +356,7 @@
                num_columns * scalar_elem_size;
       }
     }
-    case SpvOpTypeStruct: {
+    case spv::Op::OpTypeStruct: {
       const auto& members = getStructMembers(member_id, vstate);
       if (members.empty()) return 0;
       const auto lastIdx = uint32_t(members.size() - 1);
@@ -374,7 +368,7 @@
       for (auto decoration = member_decorations.begin;
            decoration != member_decorations.end; ++decoration) {
         assert(decoration->struct_member_index() == (int)lastIdx);
-        if (SpvDecorationOffset == decoration->dec_type()) {
+        if (spv::Decoration::Offset == decoration->dec_type()) {
           offset = decoration->params()[0];
         }
       }
@@ -384,7 +378,7 @@
       const auto& constraint = constraints[std::make_pair(lastMember, lastIdx)];
       return offset + getSize(lastMember, constraint, constraints, vstate);
     }
-    case SpvOpTypePointer:
+    case spv::Op::OpTypePointer:
       return vstate.pointer_size_and_alignment();
     default:
       assert(0);
@@ -458,7 +452,16 @@
     return ds;
   };
 
-  const auto& members = getStructMembers(struct_id, vstate);
+  // If we are checking 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.
+  const auto& struct_type = vstate.FindDef(struct_id);
+  std::vector<uint32_t> members;
+  if (struct_type->opcode() == spv::Op::OpTypeStruct) {
+    members = getStructMembers(struct_id, vstate);
+  } else {
+    members.push_back(struct_id);
+  }
 
   // To check for member overlaps, we want to traverse the members in
   // offset order.
@@ -467,31 +470,38 @@
     uint32_t offset;
   };
   std::vector<MemberOffsetPair> member_offsets;
-  member_offsets.reserve(members.size());
-  for (uint32_t memberIdx = 0, numMembers = uint32_t(members.size());
-       memberIdx < numMembers; memberIdx++) {
-    uint32_t offset = 0xffffffff;
-    auto member_decorations =
-        vstate.id_member_decorations(struct_id, memberIdx);
-    for (auto decoration = member_decorations.begin;
-         decoration != member_decorations.end; ++decoration) {
-      assert(decoration->struct_member_index() == (int)memberIdx);
-      switch (decoration->dec_type()) {
-        case SpvDecorationOffset:
-          offset = decoration->params()[0];
-          break;
-        default:
-          break;
+
+  // With physical storage buffers, we might be checking layouts that do not
+  // originate from a structure.
+  if (struct_type->opcode() == spv::Op::OpTypeStruct) {
+    member_offsets.reserve(members.size());
+    for (uint32_t memberIdx = 0, numMembers = uint32_t(members.size());
+         memberIdx < numMembers; memberIdx++) {
+      uint32_t offset = 0xffffffff;
+      auto member_decorations =
+          vstate.id_member_decorations(struct_id, memberIdx);
+      for (auto decoration = member_decorations.begin;
+           decoration != member_decorations.end; ++decoration) {
+        assert(decoration->struct_member_index() == (int)memberIdx);
+        switch (decoration->dec_type()) {
+          case spv::Decoration::Offset:
+            offset = decoration->params()[0];
+            break;
+          default:
+            break;
+        }
       }
+      member_offsets.push_back(
+          MemberOffsetPair{memberIdx, incoming_offset + offset});
     }
-    member_offsets.push_back(
-        MemberOffsetPair{memberIdx, incoming_offset + offset});
+    std::stable_sort(
+        member_offsets.begin(), member_offsets.end(),
+        [](const MemberOffsetPair& lhs, const MemberOffsetPair& rhs) {
+          return lhs.offset < rhs.offset;
+        });
+  } else {
+    member_offsets.push_back({0, 0});
   }
-  std::stable_sort(
-      member_offsets.begin(), member_offsets.end(),
-      [](const MemberOffsetPair& lhs, const MemberOffsetPair& rhs) {
-        return lhs.offset < rhs.offset;
-      });
 
   // Now scan from lowest offset to highest offset.
   uint32_t nextValidOffset = 0;
@@ -517,7 +527,7 @@
     if (offset == 0xffffffff)
       return fail(memberIdx) << "is missing an Offset decoration";
     if (!scalar_block_layout && relaxed_block_layout &&
-        opcode == SpvOpTypeVector) {
+        opcode == spv::Op::OpTypeVector) {
       // In relaxed block layout, the vector offset must be aligned to the
       // vector's scalar element type.
       const auto componentId = inst->words()[2];
@@ -541,21 +551,20 @@
                              << nextValidOffset - 1;
     if (!scalar_block_layout && relaxed_block_layout) {
       // Check improper straddle of vectors.
-      if (SpvOpTypeVector == opcode &&
+      if (spv::Op::OpTypeVector == opcode &&
           hasImproperStraddle(id, offset, constraint, constraints, vstate))
         return fail(memberIdx)
                << "is an improperly straddling vector at offset " << offset;
     }
     // Check struct members recursively.
     spv_result_t recursive_status = SPV_SUCCESS;
-    if (SpvOpTypeStruct == opcode &&
+    if (spv::Op::OpTypeStruct == opcode &&
         SPV_SUCCESS != (recursive_status = checkLayout(
                             id, storage_class_str, decoration_str, blockRules,
-                            scalar_block_layout,
-                            offset, constraints, vstate)))
+                            scalar_block_layout, offset, constraints, vstate)))
       return recursive_status;
     // Check matrix stride.
-    if (SpvOpTypeMatrix == opcode) {
+    if (spv::Op::OpTypeMatrix == opcode) {
       const auto stride = constraint.matrix_stride;
       if (!IsAlignedTo(stride, alignment)) {
         return fail(memberIdx) << "is a matrix with stride " << stride
@@ -566,14 +575,14 @@
     // Check arrays and runtime arrays recursively.
     auto array_inst = inst;
     auto array_alignment = alignment;
-    while (array_inst->opcode() == SpvOpTypeArray ||
-           array_inst->opcode() == SpvOpTypeRuntimeArray) {
+    while (array_inst->opcode() == spv::Op::OpTypeArray ||
+           array_inst->opcode() == spv::Op::OpTypeRuntimeArray) {
       const auto typeId = array_inst->word(2);
       const auto element_inst = vstate.FindDef(typeId);
       // Check array stride.
       uint32_t array_stride = 0;
       for (auto& decoration : vstate.id_decorations(array_inst->id())) {
-        if (SpvDecorationArrayStride == decoration.dec_type()) {
+        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";
@@ -588,7 +597,7 @@
       bool is_int32 = false;
       bool is_const = false;
       uint32_t num_elements = 0;
-      if (array_inst->opcode() == SpvOpTypeArray) {
+      if (array_inst->opcode() == spv::Op::OpTypeArray) {
         std::tie(is_int32, is_const, num_elements) =
             vstate.EvalInt32IfConst(array_inst->word(3));
       }
@@ -597,7 +606,7 @@
       // limitation to this check if the array size is a spec constant or is a
       // runtime array then we will only check a single element. This means
       // some improper straddles might be missed.
-      if (SpvOpTypeStruct == element_inst->opcode()) {
+      if (spv::Op::OpTypeStruct == element_inst->opcode()) {
         std::vector<bool> seen(16, false);
         for (uint32_t i = 0; i < num_elements; ++i) {
           uint32_t next_offset = i * array_stride + offset;
@@ -632,10 +641,10 @@
       }
     }
     nextValidOffset = offset + size;
-    if (!scalar_block_layout && blockRules &&
-        (SpvOpTypeArray == opcode || SpvOpTypeStruct == opcode)) {
-      // Uniform block rules don't permit anything in the padding of a struct
-      // or array.
+    if (!scalar_block_layout &&
+        (spv::Op::OpTypeArray == opcode || spv::Op::OpTypeStruct == opcode)) {
+      // Non-scalar block layout rules don't permit anything in the padding of
+      // a struct or array.
       nextValidOffset = align(nextValidOffset, alignment);
     }
   }
@@ -644,15 +653,15 @@
 
 // Returns true if variable or structure id has given decoration. Handles also
 // nested structures.
-bool hasDecoration(uint32_t id, SpvDecoration decoration,
+bool hasDecoration(uint32_t id, spv::Decoration decoration,
                    ValidationState_t& vstate) {
   for (auto& dec : vstate.id_decorations(id)) {
     if (decoration == dec.dec_type()) return true;
   }
-  if (SpvOpTypeStruct != vstate.FindDef(id)->opcode()) {
+  if (spv::Op::OpTypeStruct != vstate.FindDef(id)->opcode()) {
     return false;
   }
-  for (auto member_id : getStructMembers(id, SpvOpTypeStruct, vstate)) {
+  for (auto member_id : getStructMembers(id, spv::Op::OpTypeStruct, vstate)) {
     if (hasDecoration(member_id, decoration, vstate)) {
       return true;
     }
@@ -662,8 +671,8 @@
 
 // Returns true if all ids of given type have a specified decoration.
 bool checkForRequiredDecoration(uint32_t struct_id,
-                                std::function<bool(SpvDecoration)> checker,
-                                SpvOp type, ValidationState_t& vstate) {
+                                std::function<bool(spv::Decoration)> checker,
+                                spv::Op type, ValidationState_t& vstate) {
   const auto& members = getStructMembers(struct_id, vstate);
   for (size_t memberIdx = 0; memberIdx < members.size(); memberIdx++) {
     const auto id = members[memberIdx];
@@ -682,7 +691,7 @@
       return false;
     }
   }
-  for (auto id : getStructMembers(struct_id, SpvOpTypeStruct, vstate)) {
+  for (auto id : getStructMembers(struct_id, spv::Op::OpTypeStruct, vstate)) {
     if (!checkForRequiredDecoration(id, checker, type, vstate)) {
       return false;
     }
@@ -738,8 +747,8 @@
   const auto& decorations = vstate.id_decorations(var_id);
   for (const auto& d : decorations) {
     if (spvIsVulkanEnv(vstate.context()->target_env)) {
-      if (d.dec_type() == SpvDecorationLocation ||
-          d.dec_type() == SpvDecorationComponent) {
+      if (d.dec_type() == spv::Decoration::Location ||
+          d.dec_type() == spv::Decoration::Component) {
         return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id))
                << vstate.VkErrorID(4915) << "A BuiltIn variable (id " << var_id
                << ") cannot have any Location or Component decorations";
@@ -762,18 +771,18 @@
       std::unordered_set<Instruction*> seen_vars;
       for (auto interface : desc.interfaces) {
         Instruction* var_instr = vstate.FindDef(interface);
-        if (!var_instr || SpvOpVariable != var_instr->opcode()) {
+        if (!var_instr || spv::Op::OpVariable != var_instr->opcode()) {
           return vstate.diag(SPV_ERROR_INVALID_ID, var_instr)
                  << "Interfaces passed to OpEntryPoint must be of type "
                     "OpTypeVariable. Found Op"
                  << spvOpcodeString(var_instr->opcode()) << ".";
         }
-        const SpvStorageClass storage_class =
-            var_instr->GetOperandAs<SpvStorageClass>(2);
+        const spv::StorageClass storage_class =
+            var_instr->GetOperandAs<spv::StorageClass>(2);
         if (vstate.version() >= SPV_SPIRV_VERSION_WORD(1, 4)) {
           // Starting in 1.4, OpEntryPoint must list all global variables
           // it statically uses and those interfaces must be unique.
-          if (storage_class == SpvStorageClassFunction) {
+          if (storage_class == spv::StorageClass::Function) {
             return vstate.diag(SPV_ERROR_INVALID_ID, var_instr)
                    << "OpEntryPoint interfaces should only list global "
                       "variables";
@@ -785,14 +794,14 @@
                    << vstate.getIdName(interface) << " is disallowed";
           }
         } else {
-          if (storage_class != SpvStorageClassInput &&
-              storage_class != SpvStorageClassOutput) {
+          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 "
                       "Storage Class of Input(1) or Output(3). Found Storage "
                       "Class "
-                   << storage_class << " for Entry Point id " << entry_point
-                   << ".";
+                   << uint32_t(storage_class) << " for Entry Point id "
+                   << entry_point << ".";
           }
         }
 
@@ -803,7 +812,7 @@
         // to.
         const uint32_t type_id = ptr_instr->word(3);
         Instruction* type_instr = vstate.FindDef(type_id);
-        if (type_instr && SpvOpTypeStruct == type_instr->opcode() &&
+        if (type_instr && spv::Op::OpTypeStruct == type_instr->opcode() &&
             isBuiltInStruct(type_id, vstate)) {
           if (!isBlock(type_id, vstate)) {
             return vstate.diag(SPV_ERROR_INVALID_DATA, vstate.FindDef(type_id))
@@ -814,8 +823,9 @@
                       "OpVariable with a structure type that is a block not "
                       "decorated with Location.";
           }
-          if (storage_class == SpvStorageClassInput) ++num_builtin_block_inputs;
-          if (storage_class == SpvStorageClassOutput)
+          if (storage_class == spv::StorageClass::Input)
+            ++num_builtin_block_inputs;
+          if (storage_class == spv::StorageClass::Output)
             ++num_builtin_block_outputs;
           if (num_builtin_block_inputs > 1 || num_builtin_block_outputs > 1)
             break;
@@ -826,12 +836,13 @@
             return error;
         }
 
-        if (storage_class == SpvStorageClassWorkgroup) {
+        if (storage_class == spv::StorageClass::Workgroup) {
           ++num_workgroup_variables;
-          if (type_instr && SpvOpTypeStruct == type_instr->opcode()) {
-            if (hasDecoration(type_id, SpvDecorationBlock, vstate))
+          if (type_instr && spv::Op::OpTypeStruct == type_instr->opcode()) {
+            if (hasDecoration(type_id, spv::Decoration::Block, vstate))
               ++num_workgroup_variables_with_block;
-            if (hasDecoration(var_instr->id(), SpvDecorationAliased, vstate))
+            if (hasDecoration(var_instr->id(), spv::Decoration::Aliased,
+                              vstate))
               ++num_workgroup_variables_with_aliased;
           }
         }
@@ -839,31 +850,32 @@
         if (spvIsVulkanEnv(vstate.context()->target_env)) {
           const auto* models = vstate.GetExecutionModels(entry_point);
           const bool has_frag =
-              models->find(SpvExecutionModelFragment) != models->end();
+              models->find(spv::ExecutionModel::Fragment) != models->end();
           const bool has_vert =
-              models->find(SpvExecutionModelVertex) != models->end();
+              models->find(spv::ExecutionModel::Vertex) != models->end();
           for (const auto& decoration :
                vstate.id_decorations(var_instr->id())) {
-            if (decoration == SpvDecorationFlat ||
-                decoration == SpvDecorationNoPerspective ||
-                decoration == SpvDecorationSample ||
-                decoration == SpvDecorationCentroid) {
+            if (decoration == spv::Decoration::Flat ||
+                decoration == spv::Decoration::NoPerspective ||
+                decoration == spv::Decoration::Sample ||
+                decoration == spv::Decoration::Centroid) {
               // VUID 04670 already validates these decorations are input/output
-              if (storage_class == SpvStorageClassInput &&
+              if (storage_class == spv::StorageClass::Input &&
                   (models->size() > 1 || has_vert)) {
                 return vstate.diag(SPV_ERROR_INVALID_ID, var_instr)
                        << vstate.VkErrorID(6202)
-                       << "OpEntryPoint interfaces variable must not be vertex "
-                          "execution model with an input storage class for "
-                          "Entry Point id "
+                       << vstate.SpvDecorationString(decoration.dec_type())
+                       << " decorated variable must not be used in vertex "
+                          "execution model as an Input storage class for Entry "
+                          "Point id "
                        << entry_point << ".";
-              } else if (storage_class == SpvStorageClassOutput &&
+              } else if (storage_class == spv::StorageClass::Output &&
                          (models->size() > 1 || has_frag)) {
                 return vstate.diag(SPV_ERROR_INVALID_ID, var_instr)
                        << vstate.VkErrorID(6201)
-                       << "OpEntryPoint interfaces variable must not be "
-                          "fragment "
-                          "execution model with an output storage class for "
+                       << vstate.SpvDecorationString(decoration.dec_type())
+                       << " decorated variable must not be used in fragment "
+                          "execution model as an Output storage class for "
                           "Entry Point id "
                        << entry_point << ".";
               }
@@ -871,8 +883,9 @@
           }
 
           const bool has_flat =
-              hasDecoration(var_instr->id(), SpvDecorationFlat, vstate);
-          if (has_frag && storage_class == SpvStorageClassInput && !has_flat &&
+              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.IsIntScalarOrVectorType(type_id))) {
@@ -897,7 +910,7 @@
       // The LinkageAttributes Decoration cannot be applied to functions
       // targeted by an OpEntryPoint instruction
       for (auto& decoration : vstate.id_decorations(entry_point)) {
-        if (SpvDecorationLinkageAttributes == decoration.dec_type()) {
+        if (spv::Decoration::LinkageAttributes == decoration.dec_type()) {
           const std::string linkage_name =
               spvtools::utils::MakeString(decoration.params());
           return vstate.diag(SPV_ERROR_INVALID_BINARY,
@@ -909,7 +922,8 @@
         }
       }
 
-      if (vstate.HasCapability(SpvCapabilityWorkgroupMemoryExplicitLayoutKHR) &&
+      if (vstate.HasCapability(
+              spv::Capability::WorkgroupMemoryExplicitLayoutKHR) &&
           num_workgroup_variables > 0 &&
           num_workgroup_variables_with_block > 0) {
         if (num_workgroup_variables != num_workgroup_variables_with_block) {
@@ -963,13 +977,13 @@
          decoration != member_decorations.end; ++decoration) {
       assert(decoration->struct_member_index() == (int)memberIdx);
       switch (decoration->dec_type()) {
-        case SpvDecorationRowMajor:
+        case spv::Decoration::RowMajor:
           constraint.majorness = kRowMajor;
           break;
-        case SpvDecorationColMajor:
+        case spv::Decoration::ColMajor:
           constraint.majorness = kColumnMajor;
           break;
-        case SpvDecorationMatrixStride:
+        case spv::Decoration::MatrixStride:
           constraint.matrix_stride = decoration->params()[0];
           break;
         default:
@@ -982,12 +996,12 @@
     const auto member_type_inst = vstate.FindDef(member_type_id);
     const auto opcode = member_type_inst->opcode();
     switch (opcode) {
-      case SpvOpTypeArray:
-      case SpvOpTypeRuntimeArray:
+      case spv::Op::OpTypeArray:
+      case spv::Op::OpTypeRuntimeArray:
         ComputeMemberConstraintsForArray(constraints, member_type_id, inherited,
                                          vstate);
         break;
-      case SpvOpTypeStruct:
+      case spv::Op::OpTypeStruct:
         ComputeMemberConstraintsForStruct(constraints, member_type_id,
                                           inherited, vstate);
         break;
@@ -1006,12 +1020,12 @@
   const auto elem_type_inst = vstate.FindDef(elem_type_id);
   const auto opcode = elem_type_inst->opcode();
   switch (opcode) {
-    case SpvOpTypeArray:
-    case SpvOpTypeRuntimeArray:
+    case spv::Op::OpTypeArray:
+    case spv::Op::OpTypeRuntimeArray:
       ComputeMemberConstraintsForArray(constraints, elem_type_id, inherited,
                                        vstate);
       break;
-    case SpvOpTypeStruct:
+    case spv::Op::OpTypeStruct:
       ComputeMemberConstraintsForStruct(constraints, elem_type_id, inherited,
                                         vstate);
       break;
@@ -1025,16 +1039,20 @@
   std::unordered_set<uint32_t> uses_push_constant;
   for (const auto& inst : vstate.ordered_instructions()) {
     const auto& words = inst.words();
-    if (SpvOpVariable == inst.opcode()) {
+    auto type_id = inst.type_id();
+    const Instruction* type_inst = vstate.FindDef(type_id);
+    if (spv::Op::OpVariable == inst.opcode()) {
       const auto var_id = inst.id();
       // For storage class / decoration combinations, see Vulkan 14.5.4 "Offset
       // and Stride Assignment".
-      const auto storageClass = words[3];
-      const bool uniform = storageClass == SpvStorageClassUniform;
+      const auto storageClass = inst.GetOperandAs<spv::StorageClass>(2);
+      const bool uniform = storageClass == spv::StorageClass::Uniform;
       const bool uniform_constant =
-          storageClass == SpvStorageClassUniformConstant;
-      const bool push_constant = storageClass == SpvStorageClassPushConstant;
-      const bool storage_buffer = storageClass == SpvStorageClassStorageBuffer;
+          storageClass == spv::StorageClass::UniformConstant;
+      const bool push_constant =
+          storageClass == spv::StorageClass::PushConstant;
+      const bool storage_buffer =
+          storageClass == spv::StorageClass::StorageBuffer;
 
       if (spvIsVulkanEnv(vstate.context()->target_env)) {
         // Vulkan: There must be no more than one PushConstant block per entry
@@ -1058,7 +1076,7 @@
         if (uniform_constant) {
           auto entry_points = vstate.EntryPointReferences(var_id);
           if (!entry_points.empty() &&
-              !hasDecoration(var_id, SpvDecorationDescriptorSet, vstate)) {
+              !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"
@@ -1067,7 +1085,7 @@
                       "decorations specified";
           }
           if (!entry_points.empty() &&
-              !hasDecoration(var_id, SpvDecorationBinding, vstate)) {
+              !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"
@@ -1079,14 +1097,14 @@
       }
 
       if (spvIsOpenGLEnv(vstate.context()->target_env)) {
-        bool has_block = hasDecoration(var_id, SpvDecorationBlock, vstate);
+        bool has_block = hasDecoration(var_id, spv::Decoration::Block, vstate);
         bool has_buffer_block =
-            hasDecoration(var_id, SpvDecorationBufferBlock, vstate);
+            hasDecoration(var_id, spv::Decoration::BufferBlock, vstate);
         if ((uniform && (has_block || has_buffer_block)) ||
             (storage_buffer && has_block)) {
           auto entry_points = vstate.EntryPointReferences(var_id);
           if (!entry_points.empty() &&
-              !hasDecoration(var_id, SpvDecorationBinding, vstate)) {
+              !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"
@@ -1098,24 +1116,25 @@
       }
 
       const bool phys_storage_buffer =
-          storageClass == SpvStorageClassPhysicalStorageBuffer;
+          storageClass == spv::StorageClass::PhysicalStorageBuffer;
       const bool workgroup =
-          storageClass == SpvStorageClassWorkgroup &&
-          vstate.HasCapability(SpvCapabilityWorkgroupMemoryExplicitLayoutKHR);
+          storageClass == spv::StorageClass::Workgroup &&
+          vstate.HasCapability(
+              spv::Capability::WorkgroupMemoryExplicitLayoutKHR);
       if (uniform || push_constant || storage_buffer || phys_storage_buffer ||
           workgroup) {
         const auto ptrInst = vstate.FindDef(words[1]);
-        assert(SpvOpTypePointer == ptrInst->opcode());
+        assert(spv::Op::OpTypePointer == ptrInst->opcode());
         auto id = ptrInst->words()[3];
         auto id_inst = vstate.FindDef(id);
         // Jump through one level of arraying.
-        if (!workgroup && (id_inst->opcode() == SpvOpTypeArray ||
-                           id_inst->opcode() == SpvOpTypeRuntimeArray)) {
+        if (!workgroup && (id_inst->opcode() == spv::Op::OpTypeArray ||
+                           id_inst->opcode() == spv::Op::OpTypeRuntimeArray)) {
           id = id_inst->GetOperandAs<uint32_t>(1u);
           id_inst = vstate.FindDef(id);
         }
         // Struct requirement is checked on variables so just move on here.
-        if (SpvOpTypeStruct != id_inst->opcode()) continue;
+        if (spv::Op::OpTypeStruct != id_inst->opcode()) continue;
         MemberConstraints constraints;
         ComputeMemberConstraintsForStruct(&constraints, id, LayoutConstraints(),
                                           vstate);
@@ -1127,9 +1146,9 @@
                                                   : "StorageBuffer"));
 
         if (spvIsVulkanEnv(vstate.context()->target_env)) {
-          const bool block = hasDecoration(id, SpvDecorationBlock, vstate);
+          const bool block = hasDecoration(id, spv::Decoration::Block, vstate);
           const bool buffer_block =
-              hasDecoration(id, SpvDecorationBufferBlock, vstate);
+              hasDecoration(id, spv::Decoration::BufferBlock, vstate);
           if (storage_buffer && buffer_block) {
             return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id))
                    << vstate.VkErrorID(6675) << "Storage buffer id '" << var_id
@@ -1167,7 +1186,8 @@
           if (uniform || storage_buffer) {
             auto entry_points = vstate.EntryPointReferences(var_id);
             if (!entry_points.empty() &&
-                !hasDecoration(var_id, SpvDecorationDescriptorSet, vstate)) {
+                !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"
@@ -1176,7 +1196,7 @@
                         "decorations specified";
             }
             if (!entry_points.empty() &&
-                !hasDecoration(var_id, SpvDecorationBinding, vstate)) {
+                !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"
@@ -1188,8 +1208,9 @@
         }
 
         for (const auto& dec : vstate.id_decorations(id)) {
-          const bool blockDeco = SpvDecorationBlock == dec.dec_type();
-          const bool bufferDeco = SpvDecorationBufferBlock == dec.dec_type();
+          const bool blockDeco = spv::Decoration::Block == dec.dec_type();
+          const bool bufferDeco =
+              spv::Decoration::BufferBlock == dec.dec_type();
           const bool blockRules = uniform && blockDeco;
           const bool bufferRules =
               (uniform && bufferDeco) ||
@@ -1217,79 +1238,97 @@
                      << "Structure id " << id << " decorated as " << deco_str
                      << " must be explicitly laid out with Offset "
                         "decorations.";
-            } else if (hasDecoration(id, SpvDecorationGLSLShared, vstate)) {
-              return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
-                     << "Structure id " << id << " decorated as " << deco_str
-                     << " must not use GLSLShared decoration.";
-            } else if (hasDecoration(id, SpvDecorationGLSLPacked, vstate)) {
-              return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
-                     << "Structure id " << id << " decorated as " << deco_str
-                     << " must not use GLSLPacked decoration.";
-            } else if (!checkForRequiredDecoration(
-                           id,
-                           [](SpvDecoration d) {
-                             return d == SpvDecorationArrayStride;
-                           },
-                           SpvOpTypeArray, vstate)) {
+            }
+
+            if (!checkForRequiredDecoration(
+                    id,
+                    [](spv::Decoration d) {
+                      return d == spv::Decoration::ArrayStride;
+                    },
+                    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 "
                         "decorations.";
-            } else if (!checkForRequiredDecoration(
-                           id,
-                           [](SpvDecoration d) {
-                             return d == SpvDecorationMatrixStride;
-                           },
-                           SpvOpTypeMatrix, vstate)) {
+            }
+
+            if (!checkForRequiredDecoration(
+                    id,
+                    [](spv::Decoration d) {
+                      return d == spv::Decoration::MatrixStride;
+                    },
+                    spv::Op::OpTypeMatrix, vstate)) {
               return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
                      << "Structure id " << id << " decorated as " << deco_str
                      << " must be explicitly laid out with MatrixStride "
                         "decorations.";
-            } else if (!checkForRequiredDecoration(
-                           id,
-                           [](SpvDecoration d) {
-                             return d == SpvDecorationRowMajor ||
-                                    d == SpvDecorationColMajor;
-                           },
-                           SpvOpTypeMatrix, vstate)) {
+            }
+
+            if (!checkForRequiredDecoration(
+                    id,
+                    [](spv::Decoration d) {
+                      return d == spv::Decoration::RowMajor ||
+                             d == spv::Decoration::ColMajor;
+                    },
+                    spv::Op::OpTypeMatrix, vstate)) {
               return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
                      << "Structure id " << id << " decorated as " << deco_str
                      << " must be explicitly laid out with RowMajor or "
                         "ColMajor decorations.";
-            } else if (blockRules &&
-                       (SPV_SUCCESS !=
-                        (recursive_status = checkLayout(
-                             id, sc_str, 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, constraints, vstate)))) {
-              return recursive_status;
+            }
+
+            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)))) {
+                return recursive_status;
+              } else if (bufferRules &&
+                         (SPV_SUCCESS !=
+                          (recursive_status = checkLayout(
+                               id, sc_str, deco_str, false, scalar_block_layout,
+                               0, constraints, vstate)))) {
+                return recursive_status;
+              }
             }
           }
         }
       }
+    } else if (type_inst && type_inst->opcode() == spv::Op::OpTypePointer &&
+               type_inst->GetOperandAs<spv::StorageClass>(1u) ==
+                   spv::StorageClass::PhysicalStorageBuffer) {
+      const bool scalar_block_layout = vstate.options()->scalar_block_layout;
+      MemberConstraints constraints;
+      const bool buffer = true;
+      const auto data_type_id = type_inst->GetOperandAs<uint32_t>(2u);
+      const auto* data_type_inst = vstate.FindDef(data_type_id);
+      if (data_type_inst->opcode() == spv::Op::OpTypeStruct) {
+        ComputeMemberConstraintsForStruct(&constraints, data_type_id,
+                                          LayoutConstraints(), vstate);
+      }
+      if (auto res = checkLayout(data_type_id, "PhysicalStorageBuffer", "Block",
+                                 !buffer, scalar_block_layout, 0, constraints,
+                                 vstate)) {
+        return res;
+      }
     }
   }
   return SPV_SUCCESS;
 }
 
 // Returns true if |decoration| cannot be applied to the same id more than once.
-bool AtMostOncePerId(SpvDecoration decoration) {
-  return decoration == SpvDecorationArrayStride;
+bool AtMostOncePerId(spv::Decoration decoration) {
+  return decoration == spv::Decoration::ArrayStride;
 }
 
 // Returns true if |decoration| cannot be applied to the same member more than
 // once.
-bool AtMostOncePerMember(SpvDecoration decoration) {
+bool AtMostOncePerMember(spv::Decoration decoration) {
   switch (decoration) {
-    case SpvDecorationOffset:
-    case SpvDecorationMatrixStride:
-    case SpvDecorationRowMajor:
-    case SpvDecorationColMajor:
+    case spv::Decoration::Offset:
+    case spv::Decoration::MatrixStride:
+    case spv::Decoration::RowMajor:
+    case spv::Decoration::ColMajor:
       return true;
     default:
       return false;
@@ -1297,32 +1336,32 @@
 }
 
 spv_result_t CheckDecorationsCompatibility(ValidationState_t& vstate) {
-  using PerIDKey = std::tuple<SpvDecoration, uint32_t>;
-  using PerMemberKey = std::tuple<SpvDecoration, uint32_t, uint32_t>;
+  using PerIDKey = std::tuple<spv::Decoration, uint32_t>;
+  using PerMemberKey = std::tuple<spv::Decoration, uint32_t, uint32_t>;
 
   // An Array of pairs where the decorations in the pair cannot both be applied
   // to the same id.
-  static const SpvDecoration mutually_exclusive_per_id[][2] = {
-      {SpvDecorationBlock, SpvDecorationBufferBlock},
-      {SpvDecorationRestrict, SpvDecorationAliased}};
+  static const spv::Decoration mutually_exclusive_per_id[][2] = {
+      {spv::Decoration::Block, spv::Decoration::BufferBlock},
+      {spv::Decoration::Restrict, spv::Decoration::Aliased}};
   static const auto num_mutually_exclusive_per_id_pairs =
-      sizeof(mutually_exclusive_per_id) / (2 * sizeof(SpvDecoration));
+      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 SpvDecoration mutually_exclusive_per_member[][2] = {
-      {SpvDecorationRowMajor, SpvDecorationColMajor}};
+  static const spv::Decoration mutually_exclusive_per_member[][2] = {
+      {spv::Decoration::RowMajor, spv::Decoration::ColMajor}};
   static const auto num_mutually_exclusive_per_mem_pairs =
-      sizeof(mutually_exclusive_per_member) / (2 * sizeof(SpvDecoration));
+      sizeof(mutually_exclusive_per_member) / (2 * sizeof(spv::Decoration));
 
   std::set<PerIDKey> seen_per_id;
   std::set<PerMemberKey> seen_per_member;
 
   for (const auto& inst : vstate.ordered_instructions()) {
     const auto& words = inst.words();
-    if (SpvOpDecorate == inst.opcode()) {
+    if (spv::Op::OpDecorate == inst.opcode()) {
       const auto id = words[1];
-      const auto dec_type = static_cast<SpvDecoration>(words[2]);
+      const auto dec_type = static_cast<spv::Decoration>(words[2]);
       const auto k = PerIDKey(dec_type, id);
       const auto already_used = !seen_per_id.insert(k).second;
       if (already_used && AtMostOncePerId(dec_type)) {
@@ -1335,7 +1374,7 @@
       // an ID.
       for (uint32_t pair_idx = 0;
            pair_idx < num_mutually_exclusive_per_id_pairs; ++pair_idx) {
-        SpvDecoration excl_dec_type = SpvDecorationMax;
+        spv::Decoration excl_dec_type = spv::Decoration::Max;
         if (mutually_exclusive_per_id[pair_idx][0] == dec_type) {
           excl_dec_type = mutually_exclusive_per_id[pair_idx][1];
         } else if (mutually_exclusive_per_id[pair_idx][1] == dec_type) {
@@ -1353,10 +1392,10 @@
                  << " is not allowed.";
         }
       }
-    } else if (SpvOpMemberDecorate == inst.opcode()) {
+    } else if (spv::Op::OpMemberDecorate == inst.opcode()) {
       const auto id = words[1];
       const auto member_id = words[2];
-      const auto dec_type = static_cast<SpvDecoration>(words[3]);
+      const auto dec_type = static_cast<spv::Decoration>(words[3]);
       const auto k = PerMemberKey(dec_type, id, member_id);
       const auto already_used = !seen_per_member.insert(k).second;
       if (already_used && AtMostOncePerMember(dec_type)) {
@@ -1369,7 +1408,7 @@
       // a (ID, member) tuple.
       for (uint32_t pair_idx = 0;
            pair_idx < num_mutually_exclusive_per_mem_pairs; ++pair_idx) {
-        SpvDecoration excl_dec_type = SpvDecorationMax;
+        spv::Decoration excl_dec_type = spv::Decoration::Max;
         if (mutually_exclusive_per_member[pair_idx][0] == dec_type) {
           excl_dec_type = mutually_exclusive_per_member[pair_idx][1];
         } else if (mutually_exclusive_per_member[pair_idx][1] == dec_type) {
@@ -1395,7 +1434,7 @@
 
 spv_result_t CheckVulkanMemoryModelDeprecatedDecorations(
     ValidationState_t& vstate) {
-  if (vstate.memory_model() != SpvMemoryModelVulkanKHR) return SPV_SUCCESS;
+  if (vstate.memory_model() != spv::MemoryModel::VulkanKHR) return SPV_SUCCESS;
 
   std::string msg;
   std::ostringstream str(msg);
@@ -1404,10 +1443,10 @@
     const auto id = inst->id();
     for (const auto& dec : vstate.id_decorations(id)) {
       const auto member = dec.struct_member_index();
-      if (dec.dec_type() == SpvDecorationCoherent ||
-          dec.dec_type() == SpvDecorationVolatile) {
-        str << (dec.dec_type() == SpvDecorationCoherent ? "Coherent"
-                                                        : "Volatile");
+      if (dec.dec_type() == spv::Decoration::Coherent ||
+          dec.dec_type() == spv::Decoration::Volatile) {
+        str << (dec.dec_type() == spv::Decoration::Coherent ? "Coherent"
+                                                            : "Volatile");
         str << " decoration targeting " << vstate.getIdName(id);
         if (member != Decoration::kInvalidMember) {
           str << " (member index " << member << ")";
@@ -1428,7 +1467,7 @@
                                            const Decoration& decoration) {
   // Validates width-only conversion instruction for floating-point object
   // i.e., OpFConvert
-  if (inst.opcode() != SpvOpFConvert) {
+  if (inst.opcode() != spv::Op::OpFConvert) {
     return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
            << "FPRoundingMode decoration can be applied only to a "
               "width-only conversion instruction for floating-point "
@@ -1436,8 +1475,9 @@
   }
 
   if (spvIsVulkanEnv(vstate.context()->target_env)) {
-    const auto mode = decoration.params()[0];
-    if ((mode != SpvFPRoundingModeRTE) && (mode != SpvFPRoundingModeRTZ)) {
+    const auto mode = spv::FPRoundingMode(decoration.params()[0]);
+    if ((mode != spv::FPRoundingMode::RTE) &&
+        (mode != spv::FPRoundingMode::RTZ)) {
       return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
              << vstate.VkErrorID(4675)
              << "In Vulkan, the FPRoundingMode mode must only by RTE or RTZ.";
@@ -1447,11 +1487,11 @@
   // Validates Object operand of an OpStore
   for (const auto& use : inst.uses()) {
     const auto store = use.first;
-    if (store->opcode() == SpvOpFConvert) continue;
+    if (store->opcode() == spv::Op::OpFConvert) continue;
     if (spvOpcodeIsDebug(store->opcode())) continue;
     if (store->IsNonSemantic()) continue;
     if (spvOpcodeIsDecoration(store->opcode())) continue;
-    if (store->opcode() != SpvOpStore) {
+    if (store->opcode() != spv::Op::OpStore) {
       return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
              << "FPRoundingMode decoration can be applied only to the "
                 "Object operand of an OpStore.";
@@ -1477,12 +1517,13 @@
     }
 
     // Validates storage class of the pointer to the OpStore
-    const auto storage = ptr_type->GetOperandAs<uint32_t>(1);
-    if (storage != SpvStorageClassStorageBuffer &&
-        storage != SpvStorageClassUniform &&
-        storage != SpvStorageClassPushConstant &&
-        storage != SpvStorageClassInput && storage != SpvStorageClassOutput &&
-        storage != SpvStorageClassPhysicalStorageBuffer) {
+    const auto storage = ptr_type->GetOperandAs<spv::StorageClass>(1);
+    if (storage != spv::StorageClass::StorageBuffer &&
+        storage != spv::StorageClass::Uniform &&
+        storage != spv::StorageClass::PushConstant &&
+        storage != spv::StorageClass::Input &&
+        storage != spv::StorageClass::Output &&
+        storage != spv::StorageClass::PhysicalStorageBuffer) {
       return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
              << "FPRoundingMode decoration can be applied only to the "
                 "Object operand of an OpStore in the StorageBuffer, "
@@ -1507,16 +1548,17 @@
     // First, it must be a variable or function parameter.
     const auto opcode = inst.opcode();
     const auto type_id = inst.type_id();
-    if (opcode != SpvOpVariable && opcode != SpvOpFunctionParameter) {
+    if (opcode != spv::Op::OpVariable &&
+        opcode != spv::Op::OpFunctionParameter) {
       return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
              << "Target of NonWritable decoration must be a memory object "
                 "declaration (a variable or a function parameter)";
     }
-    const auto var_storage_class = opcode == SpvOpVariable
-                                       ? inst.GetOperandAs<SpvStorageClass>(2)
-                                       : SpvStorageClassMax;
-    if ((var_storage_class == SpvStorageClassFunction ||
-         var_storage_class == SpvStorageClassPrivate) &&
+    const auto var_storage_class = opcode == spv::Op::OpVariable
+                                       ? inst.GetOperandAs<spv::StorageClass>(2)
+                                       : spv::StorageClass::Max;
+    if ((var_storage_class == spv::StorageClass::Function ||
+         var_storage_class == spv::StorageClass::Private) &&
         vstate.features().nonwritable_var_in_function_or_private) {
       // New permitted feature in SPIR-V 1.4.
     } else if (
@@ -1546,8 +1588,9 @@
 spv_result_t CheckUniformDecoration(ValidationState_t& vstate,
                                     const Instruction& inst,
                                     const Decoration& decoration) {
-  const char* const dec_name =
-      decoration.dec_type() == SpvDecorationUniform ? "Uniform" : "UniformId";
+  const char* const dec_name = decoration.dec_type() == spv::Decoration::Uniform
+                                   ? "Uniform"
+                                   : "UniformId";
 
   // Uniform or UniformId must decorate an "object"
   //  - has a result ID
@@ -1561,7 +1604,7 @@
            << dec_name << " decoration applied to a non-object";
   }
   if (Instruction* type_inst = vstate.FindDef(inst.type_id())) {
-    if (type_inst->opcode() == SpvOpTypeVoid) {
+    if (type_inst->opcode() == spv::Op::OpTypeVoid) {
       return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
              << dec_name << " decoration applied to a value with void type";
     }
@@ -1575,7 +1618,7 @@
   // Use of Uniform with OpDecorate is checked elsewhere.
   // Use of UniformId with OpDecorateId is checked elsewhere.
 
-  if (decoration.dec_type() == SpvDecorationUniformId) {
+  if (decoration.dec_type() == spv::Decoration::UniformId) {
     assert(decoration.params().size() == 1 &&
            "Grammar ensures UniformId has one parameter");
 
@@ -1596,13 +1639,13 @@
                                         const Instruction& inst,
                                         const Decoration& decoration) {
   switch (inst.opcode()) {
-    case SpvOpIAdd:
-    case SpvOpISub:
-    case SpvOpIMul:
-    case SpvOpShiftLeftLogical:
-    case SpvOpSNegate:
+    case spv::Op::OpIAdd:
+    case spv::Op::OpISub:
+    case spv::Op::OpIMul:
+    case spv::Op::OpShiftLeftLogical:
+    case spv::Op::OpSNegate:
       return SPV_SUCCESS;
-    case SpvOpExtInst:
+    case spv::Op::OpExtInst:
       // TODO(dneto): Only certain extended instructions allow these
       // decorations.  For now allow anything.
       return SPV_SUCCESS;
@@ -1611,7 +1654,7 @@
   }
 
   return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
-         << (decoration.dec_type() == SpvDecorationNoSignedWrap
+         << (decoration.dec_type() == spv::Decoration::NoSignedWrap
                  ? "NoSignedWrap"
                  : "NoUnsignedWrap")
          << " decoration may not be applied to "
@@ -1625,29 +1668,32 @@
                                       const Instruction& inst,
                                       const Decoration& decoration) {
   assert(inst.id() && "Parser ensures the target of the decoration has an ID");
+  assert(decoration.params().size() == 1 &&
+         "Grammar ensures Component has one parameter");
 
   uint32_t type_id;
   if (decoration.struct_member_index() == Decoration::kInvalidMember) {
     // The target must be a memory object declaration.
     const auto opcode = inst.opcode();
-    if (opcode != SpvOpVariable && opcode != SpvOpFunctionParameter) {
+    if (opcode != spv::Op::OpVariable &&
+        opcode != spv::Op::OpFunctionParameter) {
       return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
              << "Target of Component decoration must be a memory object "
                 "declaration (a variable or a function parameter)";
     }
 
     // Only valid for the Input and Output Storage Classes.
-    const auto storage_class = opcode == SpvOpVariable
-                                   ? inst.GetOperandAs<SpvStorageClass>(2)
-                                   : SpvStorageClassMax;
-    if (storage_class != SpvStorageClassInput &&
-        storage_class != SpvStorageClassOutput &&
-        storage_class != SpvStorageClassMax) {
+    const auto storage_class = opcode == spv::Op::OpVariable
+                                   ? inst.GetOperandAs<spv::StorageClass>(2)
+                                   : spv::StorageClass::Max;
+    if (storage_class != spv::StorageClass::Input &&
+        storage_class != spv::StorageClass::Output &&
+        storage_class != spv::StorageClass::Max) {
       return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
              << "Target of Component decoration is invalid: must point to a "
                 "Storage Class of Input(1) or Output(3). Found Storage "
                 "Class "
-             << storage_class;
+             << uint32_t(storage_class);
     }
 
     type_id = inst.type_id();
@@ -1656,7 +1702,7 @@
       type_id = pointer->GetOperandAs<uint32_t>(2);
     }
   } else {
-    if (inst.opcode() != SpvOpTypeStruct) {
+    if (inst.opcode() != spv::Op::OpTypeStruct) {
       return vstate.diag(SPV_ERROR_INVALID_DATA, &inst)
              << "Attempted to get underlying data type via member index for "
                 "non-struct type.";
@@ -1666,30 +1712,56 @@
 
   if (spvIsVulkanEnv(vstate.context()->target_env)) {
     // Strip the array, if present.
-    if (vstate.GetIdOpcode(type_id) == SpvOpTypeArray) {
+    if (vstate.GetIdOpcode(type_id) == spv::Op::OpTypeArray) {
       type_id = vstate.FindDef(type_id)->word(2u);
     }
 
     if (!vstate.IsIntScalarOrVectorType(type_id) &&
         !vstate.IsFloatScalarOrVectorType(type_id)) {
       return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
+             << vstate.VkErrorID(4924)
              << "Component decoration specified for type "
              << vstate.getIdName(type_id) << " that is not a scalar or vector";
     }
 
-    // For 16-, and 32-bit types, it is invalid if this sequence of components
-    // gets larger than 3.
+    const auto component = decoration.params()[0];
+    if (component > 3) {
+      return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
+             << vstate.VkErrorID(4920)
+             << "Component decoration value must not be greater than 3";
+    }
+
+    const auto dimension = vstate.GetDimension(type_id);
     const auto bit_width = vstate.GetBitWidth(type_id);
     if (bit_width == 16 || bit_width == 32) {
-      assert(decoration.params().size() == 1 &&
-             "Grammar ensures Component has one parameter");
-
-      const auto component = decoration.params()[0];
-      const auto last_component = component + vstate.GetDimension(type_id) - 1;
-      if (last_component > 3) {
+      const auto sum_component = component + dimension;
+      if (sum_component > 4) {
         return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
+               << vstate.VkErrorID(4921)
                << "Sequence of components starting with " << component
-               << " and ending with " << last_component
+               << " and ending with " << (sum_component - 1)
+               << " gets larger than 3";
+      }
+    } else if (bit_width == 64) {
+      if (dimension > 2) {
+        return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
+               << vstate.VkErrorID(7703)
+               << "Component decoration only allowed on 64-bit scalar and "
+                  "2-component vector";
+      }
+      if (component == 1 || component == 3) {
+        return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
+               << vstate.VkErrorID(4923)
+               << "Component decoration value must not be 1 or 3 for 64-bit "
+                  "data types";
+      }
+      // 64-bit is double per component dimension
+      const auto sum_component = component + (2 * dimension);
+      if (sum_component > 4) {
+        return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
+               << vstate.VkErrorID(4922)
+               << "Sequence of components starting with " << component
+               << " and ending with " << (sum_component - 1)
                << " gets larger than 3";
       }
     }
@@ -1705,9 +1777,10 @@
                                   const Instruction& inst,
                                   const Decoration& decoration) {
   assert(inst.id() && "Parser ensures the target of the decoration has an ID");
-  if (inst.opcode() != SpvOpTypeStruct) {
-    const char* const dec_name =
-        decoration.dec_type() == SpvDecorationBlock ? "Block" : "BufferBlock";
+  if (inst.opcode() != spv::Op::OpTypeStruct) {
+    const char* const dec_name = decoration.dec_type() == spv::Decoration::Block
+                                     ? "Block"
+                                     : "BufferBlock";
     return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
            << dec_name << " decoration on a non-struct type.";
   }
@@ -1717,10 +1790,10 @@
 spv_result_t CheckLocationDecoration(ValidationState_t& vstate,
                                      const Instruction& inst,
                                      const Decoration& decoration) {
-  if (inst.opcode() == SpvOpVariable) return SPV_SUCCESS;
+  if (inst.opcode() == spv::Op::OpVariable) return SPV_SUCCESS;
 
   if (decoration.struct_member_index() != Decoration::kInvalidMember &&
-      inst.opcode() == SpvOpTypeStruct) {
+      inst.opcode() == spv::Op::OpTypeStruct) {
     return SPV_SUCCESS;
   }
 
@@ -1740,7 +1813,7 @@
   }
 
   if (decoration.struct_member_index() != Decoration::kInvalidMember &&
-      inst.opcode() == SpvOpTypeStruct) {
+      inst.opcode() == spv::Op::OpTypeStruct) {
     return SPV_SUCCESS;
   }
   return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
@@ -1759,7 +1832,7 @@
 // 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(SpvCapabilityShader);
+  const bool is_shader = vstate.HasCapability(spv::Capability::Shader);
 
   for (const auto& kv : vstate.id_decorations()) {
     const uint32_t id = kv.first;
@@ -1771,37 +1844,37 @@
 
     // We assume the decorations applied to a decoration group have already
     // been propagated down to the group members.
-    if (inst->opcode() == SpvOpDecorationGroup) continue;
+    if (inst->opcode() == spv::Op::OpDecorationGroup) continue;
 
     for (const auto& decoration : decorations) {
       switch (decoration.dec_type()) {
-        case SpvDecorationComponent:
+        case spv::Decoration::Component:
           PASS_OR_BAIL(CheckComponentDecoration(vstate, *inst, decoration));
           break;
-        case SpvDecorationFPRoundingMode:
+        case spv::Decoration::FPRoundingMode:
           if (is_shader)
             PASS_OR_BAIL(
                 CheckFPRoundingModeForShaders(vstate, *inst, decoration));
           break;
-        case SpvDecorationNonWritable:
+        case spv::Decoration::NonWritable:
           PASS_OR_BAIL(CheckNonWritableDecoration(vstate, *inst, decoration));
           break;
-        case SpvDecorationUniform:
-        case SpvDecorationUniformId:
+        case spv::Decoration::Uniform:
+        case spv::Decoration::UniformId:
           PASS_OR_BAIL(CheckUniformDecoration(vstate, *inst, decoration));
           break;
-        case SpvDecorationNoSignedWrap:
-        case SpvDecorationNoUnsignedWrap:
+        case spv::Decoration::NoSignedWrap:
+        case spv::Decoration::NoUnsignedWrap:
           PASS_OR_BAIL(CheckIntegerWrapDecoration(vstate, *inst, decoration));
           break;
-        case SpvDecorationBlock:
-        case SpvDecorationBufferBlock:
+        case spv::Decoration::Block:
+        case spv::Decoration::BufferBlock:
           PASS_OR_BAIL(CheckBlockDecoration(vstate, *inst, decoration));
           break;
-        case SpvDecorationLocation:
+        case spv::Decoration::Location:
           PASS_OR_BAIL(CheckLocationDecoration(vstate, *inst, decoration));
           break;
-        case SpvDecorationRelaxedPrecision:
+        case spv::Decoration::RelaxedPrecision:
           PASS_OR_BAIL(
               CheckRelaxPrecisionDecoration(vstate, *inst, decoration));
           break;
diff --git a/source/val/validate_derivatives.cpp b/source/val/validate_derivatives.cpp
index 25b941a..90cf664 100644
--- a/source/val/validate_derivatives.cpp
+++ b/source/val/validate_derivatives.cpp
@@ -14,13 +14,11 @@
 
 // Validates correctness of derivative SPIR-V instructions.
 
-#include "source/val/validate.h"
-
 #include <string>
 
-#include "source/diagnostic.h"
 #include "source/opcode.h"
 #include "source/val/instruction.h"
+#include "source/val/validate.h"
 #include "source/val/validation_state.h"
 
 namespace spvtools {
@@ -28,25 +26,26 @@
 
 // Validates correctness of derivative instructions.
 spv_result_t DerivativesPass(ValidationState_t& _, const Instruction* inst) {
-  const SpvOp opcode = inst->opcode();
+  const spv::Op opcode = inst->opcode();
   const uint32_t result_type = inst->type_id();
 
   switch (opcode) {
-    case SpvOpDPdx:
-    case SpvOpDPdy:
-    case SpvOpFwidth:
-    case SpvOpDPdxFine:
-    case SpvOpDPdyFine:
-    case SpvOpFwidthFine:
-    case SpvOpDPdxCoarse:
-    case SpvOpDPdyCoarse:
-    case SpvOpFwidthCoarse: {
+    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: {
       if (!_.IsFloatScalarOrVectorType(result_type)) {
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
                << "Expected Result Type to be float scalar or vector type: "
                << spvOpcodeString(opcode);
       }
-      if (!_.ContainsSizedIntOrFloatType(result_type, SpvOpTypeFloat, 32)) {
+      if (!_.ContainsSizedIntOrFloatType(result_type, spv::Op::OpTypeFloat,
+                                         32)) {
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
                << "Result type component width must be 32 bits";
       }
@@ -58,10 +57,10 @@
                << spvOpcodeString(opcode);
       }
       _.function(inst->function()->id())
-          ->RegisterExecutionModelLimitation([opcode](SpvExecutionModel model,
+          ->RegisterExecutionModelLimitation([opcode](spv::ExecutionModel model,
                                                       std::string* message) {
-            if (model != SpvExecutionModelFragment &&
-                model != SpvExecutionModelGLCompute) {
+            if (model != spv::ExecutionModel::Fragment &&
+                model != spv::ExecutionModel::GLCompute) {
               if (message) {
                 *message =
                     std::string(
@@ -80,11 +79,11 @@
             const auto* models = state.GetExecutionModels(entry_point->id());
             const auto* modes = state.GetExecutionModes(entry_point->id());
             if (models &&
-                models->find(SpvExecutionModelGLCompute) != models->end() &&
+                models->find(spv::ExecutionModel::GLCompute) != models->end() &&
                 (!modes ||
-                 (modes->find(SpvExecutionModeDerivativeGroupLinearNV) ==
+                 (modes->find(spv::ExecutionMode::DerivativeGroupLinearNV) ==
                       modes->end() &&
-                  modes->find(SpvExecutionModeDerivativeGroupQuadsNV) ==
+                  modes->find(spv::ExecutionMode::DerivativeGroupQuadsNV) ==
                       modes->end()))) {
               if (message) {
                 *message = std::string(
diff --git a/source/val/validate_execution_limitations.cpp b/source/val/validate_execution_limitations.cpp
index e1f4d7b..0221d7e 100644
--- a/source/val/validate_execution_limitations.cpp
+++ b/source/val/validate_execution_limitations.cpp
@@ -13,8 +13,6 @@
 // limitations under the License.
 
 #include "source/val/validate.h"
-
-#include "source/val/function.h"
 #include "source/val/validation_state.h"
 
 namespace spvtools {
@@ -22,7 +20,7 @@
 
 spv_result_t ValidateExecutionLimitations(ValidationState_t& _,
                                           const Instruction* inst) {
-  if (inst->opcode() != SpvOpFunction) {
+  if (inst->opcode() != spv::Op::OpFunction) {
     return SPV_SUCCESS;
   }
 
diff --git a/source/val/validate_extensions.cpp b/source/val/validate_extensions.cpp
index 1e69cb3..0ac62bf 100644
--- a/source/val/validate_extensions.cpp
+++ b/source/val/validate_extensions.cpp
@@ -18,31 +18,41 @@
 #include <string>
 #include <vector>
 
-#include "spirv/unified1/NonSemanticClspvReflection.h"
-
 #include "NonSemanticShaderDebugInfo100.h"
 #include "OpenCLDebugInfo100.h"
 #include "source/common_debug_info.h"
-#include "source/diagnostic.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/spirv_target_env.h"
 #include "source/val/instruction.h"
 #include "source/val/validate.h"
 #include "source/val/validation_state.h"
+#include "spirv/unified1/NonSemanticClspvReflection.h"
 
 namespace spvtools {
 namespace val {
 namespace {
 
-uint32_t GetSizeTBitWidth(const ValidationState_t& _) {
-  if (_.addressing_model() == SpvAddressingModelPhysical32) return 32;
+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 ||
+      !desc) {
+    return std::string("Unknown ExtInst");
+  }
+  std::ostringstream ss;
+  ss << desc->name;
 
-  if (_.addressing_model() == SpvAddressingModelPhysical64) return 64;
+  return ss.str();
+}
+
+uint32_t GetSizeTBitWidth(const ValidationState_t& _) {
+  if (_.addressing_model() == spv::AddressingModel::Physical32) return 32;
+
+  if (_.addressing_model() == spv::AddressingModel::Physical64) return 64;
 
   return 0;
 }
@@ -50,7 +60,7 @@
 bool IsIntScalar(ValidationState_t& _, uint32_t id, bool must_len32,
                  bool must_unsigned) {
   auto type = _.FindDef(id);
-  if (!type || type->opcode() != SpvOpTypeInt) {
+  if (!type || type->opcode() != spv::Op::OpTypeInt) {
     return false;
   }
 
@@ -63,7 +73,7 @@
 
 bool IsUint32Constant(ValidationState_t& _, uint32_t id) {
   auto inst = _.FindDef(id);
-  if (!inst || inst->opcode() != SpvOpConstant) {
+  if (!inst || inst->opcode() != spv::Op::OpConstant) {
     return false;
   }
 
@@ -79,7 +89,7 @@
 // is a result id of an instruction with |expected_opcode|.
 spv_result_t ValidateOperandForDebugInfo(
     ValidationState_t& _, const std::string& operand_name,
-    SpvOp expected_opcode, const Instruction* inst, uint32_t word_index,
+    spv::Op expected_opcode, const Instruction* inst, uint32_t word_index,
     const std::function<std::string()>& ext_inst_name) {
   auto* operand = _.FindDef(inst->word(word_index));
   if (operand->opcode() != expected_opcode) {
@@ -137,7 +147,7 @@
     const Instruction* inst, uint32_t word_index) {
   if (inst->words().size() <= word_index) return false;
   auto* debug_inst = _.FindDef(inst->word(word_index));
-  if (debug_inst->opcode() != SpvOpExtInst ||
+  if (debug_inst->opcode() != spv::Op::OpExtInst ||
       (debug_inst->ext_inst_type() != SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 &&
        debug_inst->ext_inst_type() !=
            SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) ||
@@ -155,7 +165,7 @@
     const Instruction* inst, uint32_t word_index) {
   if (inst->words().size() <= word_index) return false;
   auto* debug_inst = _.FindDef(inst->word(word_index));
-  if (debug_inst->opcode() != SpvOpExtInst ||
+  if (debug_inst->opcode() != spv::Op::OpExtInst ||
       (debug_inst->ext_inst_type() !=
        SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) ||
       !expectation(
@@ -273,12 +283,14 @@
 }
 
 spv_result_t ValidateClspvReflectionKernel(ValidationState_t& _,
-                                           const Instruction* inst) {
+                                           const Instruction* inst,
+                                           uint32_t version) {
+  const auto inst_name = ReflectionInstructionName(_, inst);
   const auto kernel_id = inst->GetOperandAs<uint32_t>(4);
   const auto kernel = _.FindDef(kernel_id);
-  if (kernel->opcode() != SpvOpFunction) {
+  if (kernel->opcode() != spv::Op::OpFunction) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
-           << "Kernel does not reference a function";
+           << inst_name << " does not reference a function";
   }
 
   bool found_kernel = false;
@@ -290,23 +302,23 @@
   }
   if (!found_kernel) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
-           << "Kernel does not reference an entry-point";
+           << inst_name << " does not reference an entry-point";
   }
 
   const auto* exec_models = _.GetExecutionModels(kernel_id);
   if (!exec_models || exec_models->empty()) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
-           << "Kernel does not reference an entry-point";
+           << inst_name << " does not reference an entry-point";
   }
   for (auto exec_model : *exec_models) {
-    if (exec_model != SpvExecutionModelGLCompute) {
+    if (exec_model != spv::ExecutionModel::GLCompute) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
-             << "Kernel must refer only to GLCompute entry-points";
+             << inst_name << " must refer only to GLCompute entry-points";
     }
   }
 
   auto name = _.FindDef(inst->GetOperandAs<uint32_t>(5));
-  if (!name || name->opcode() != SpvOpString) {
+  if (!name || name->opcode() != spv::Op::OpString) {
     return _.diag(SPV_ERROR_INVALID_ID, inst) << "Name must be an OpString";
   }
 
@@ -323,17 +335,48 @@
            << "Name must match an entry-point for Kernel";
   }
 
+  const auto num_operands = inst->operands().size();
+  if (version < 5 && num_operands > 6) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "Version " << version << " of the " << inst_name
+           << " instruction can only have 2 additional operands";
+  }
+
+  if (num_operands > 6) {
+    const auto num_args_id = inst->GetOperandAs<uint32_t>(6);
+    if (!IsUint32Constant(_, num_args_id)) {
+      return _.diag(SPV_ERROR_INVALID_ID, inst)
+             << "NumArguments must be a 32-bit unsigned integer OpConstant";
+    }
+  }
+
+  if (num_operands > 7) {
+    const auto flags_id = inst->GetOperandAs<uint32_t>(7);
+    if (!IsUint32Constant(_, flags_id)) {
+      return _.diag(SPV_ERROR_INVALID_ID, inst)
+             << "Flags must be a 32-bit unsigned integer OpConstant";
+    }
+  }
+
+  if (num_operands > 8) {
+    const auto atts_id = inst->GetOperandAs<uint32_t>(8);
+    if (_.GetIdOpcode(atts_id) != spv::Op::OpString) {
+      return _.diag(SPV_ERROR_INVALID_ID, inst)
+             << "Attributes must be an OpString";
+    }
+  }
+
   return SPV_SUCCESS;
 }
 
 spv_result_t ValidateClspvReflectionArgumentInfo(ValidationState_t& _,
                                                  const Instruction* inst) {
   const auto num_operands = inst->operands().size();
-  if (_.GetIdOpcode(inst->GetOperandAs<uint32_t>(4)) != SpvOpString) {
+  if (_.GetIdOpcode(inst->GetOperandAs<uint32_t>(4)) != spv::Op::OpString) {
     return _.diag(SPV_ERROR_INVALID_ID, inst) << "Name must be an OpString";
   }
   if (num_operands > 5) {
-    if (_.GetIdOpcode(inst->GetOperandAs<uint32_t>(5)) != SpvOpString) {
+    if (_.GetIdOpcode(inst->GetOperandAs<uint32_t>(5)) != spv::Op::OpString) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
              << "TypeName must be an OpString";
     }
@@ -366,7 +409,7 @@
 spv_result_t ValidateKernelDecl(ValidationState_t& _, const Instruction* inst) {
   const auto decl_id = inst->GetOperandAs<uint32_t>(4);
   const auto decl = _.FindDef(decl_id);
-  if (!decl || decl->opcode() != SpvOpExtInst) {
+  if (!decl || decl->opcode() != spv::Op::OpExtInst) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "Kernel must be a Kernel extended instruction";
   }
@@ -389,7 +432,7 @@
 spv_result_t ValidateArgInfo(ValidationState_t& _, const Instruction* inst,
                              uint32_t info_index) {
   auto info = _.FindDef(inst->GetOperandAs<uint32_t>(info_index));
-  if (!info || info->opcode() != SpvOpExtInst) {
+  if (!info || info->opcode() != spv::Op::OpExtInst) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "ArgInfo must be an ArgumentInfo extended instruction";
   }
@@ -439,8 +482,8 @@
   return SPV_SUCCESS;
 }
 
-spv_result_t ValidateClspvReflectionArgumentPodBuffer(ValidationState_t& _,
-                                                      const Instruction* inst) {
+spv_result_t ValidateClspvReflectionArgumentOffsetBuffer(ValidationState_t& _,
+                                                         const Instruction* inst) {
   const auto num_operands = inst->operands().size();
   if (auto error = ValidateKernelDecl(_, inst)) {
     return error;
@@ -480,7 +523,7 @@
   return SPV_SUCCESS;
 }
 
-spv_result_t ValidateClspvReflectionArgumentPodPushConstant(
+spv_result_t ValidateClspvReflectionArgumentPushConstant(
     ValidationState_t& _, const Instruction* inst) {
   const auto num_operands = inst->operands().size();
   if (auto error = ValidateKernelDecl(_, inst)) {
@@ -587,8 +630,8 @@
   return SPV_SUCCESS;
 }
 
-spv_result_t ValidateClspvReflectionConstantData(ValidationState_t& _,
-                                                 const Instruction* inst) {
+spv_result_t ValidateClspvReflectionInitializedData(ValidationState_t& _,
+                                                    const Instruction* inst) {
   if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(4))) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "DescriptorSet must be a 32-bit unsigned integer OpConstant";
@@ -599,7 +642,7 @@
            << "Binding must be a 32-bit unsigned integer OpConstant";
   }
 
-  if (_.GetIdOpcode(inst->GetOperandAs<uint32_t>(6)) != SpvOpString) {
+  if (_.GetIdOpcode(inst->GetOperandAs<uint32_t>(6)) != spv::Op::OpString) {
     return _.diag(SPV_ERROR_INVALID_ID, inst) << "Data must be an OpString";
   }
 
@@ -650,18 +693,250 @@
   return SPV_SUCCESS;
 }
 
+spv_result_t ValidateClspvReflectionSubgroupMaxSize(ValidationState_t& _,
+                                                    const Instruction* inst) {
+  const auto size_id = inst->GetOperandAs<uint32_t>(4);
+  if (!IsUint32Constant(_, size_id)) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "Size must be a 32-bit unsigned integer OpConstant";
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateClspvReflectionPointerRelocation(ValidationState_t& _,
+                                                      const Instruction* inst) {
+  if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(4))) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "ObjectOffset must be a 32-bit unsigned integer OpConstant";
+  }
+
+  if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(5))) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "PointerOffset must be a 32-bit unsigned integer OpConstant";
+  }
+
+  if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(6))) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "PointerSize must be a 32-bit unsigned integer OpConstant";
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateClspvReflectionImageMetadataPushConstant(
+    ValidationState_t& _, const Instruction* inst) {
+  if (auto error = ValidateKernelDecl(_, inst)) {
+    return error;
+  }
+
+  if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(5))) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "Ordinal must be a 32-bit unsigned integer OpConstant";
+  }
+
+  if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(6))) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "Offset must be a 32-bit unsigned integer OpConstant";
+  }
+
+  if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(7))) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "Size must be a 32-bit unsigned integer OpConstant";
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateClspvReflectionImageMetadataUniform(
+    ValidationState_t& _, const Instruction* inst) {
+  if (auto error = ValidateKernelDecl(_, inst)) {
+    return error;
+  }
+
+  if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(5))) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "Ordinal must be a 32-bit unsigned integer OpConstant";
+  }
+
+  if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(6))) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "DescriptorSet must be a 32-bit unsigned integer OpConstant";
+  }
+
+  if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(7))) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "Binding must be a 32-bit unsigned integer OpConstant";
+  }
+
+  if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(8))) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "Offset must be a 32-bit unsigned integer OpConstant";
+  }
+
+  if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(9))) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "Size must be a 32-bit unsigned integer OpConstant";
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateClspvReflectionPushConstantData(ValidationState_t& _,
+                                                     const Instruction* inst) {
+  if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(4))) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "Offset must be a 32-bit unsigned integer OpConstant";
+  }
+
+  if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(5))) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "Size must be a 32-bit unsigned integer OpConstant";
+  }
+
+  if (_.GetIdOpcode(inst->GetOperandAs<uint32_t>(6)) != spv::Op::OpString) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst) << "Data must be an OpString";
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateClspvReflectionPrintfInfo(ValidationState_t& _,
+                                              const Instruction* inst) {
+  if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(4))) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "PrintfID must be a 32-bit unsigned integer OpConstant";
+  }
+
+  if (_.GetIdOpcode(inst->GetOperandAs<uint32_t>(5)) != spv::Op::OpString) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "FormatString must be an OpString";
+  }
+
+  for (size_t i = 6; i < inst->operands().size(); ++i) {
+    if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(i))) {
+      return _.diag(SPV_ERROR_INVALID_ID, inst)
+             << "ArgumentSizes must be a 32-bit unsigned integer OpConstant";
+    }
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateClspvReflectionPrintfStorageBuffer(ValidationState_t& _,
+                                                        const Instruction* inst) {
+  if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(4))) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "DescriptorSet must be a 32-bit unsigned integer OpConstant";
+  }
+
+  if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(5))) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "Binding must be a 32-bit unsigned integer OpConstant";
+  }
+
+  if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(6))) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "Size must be a 32-bit unsigned integer OpConstant";
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateClspvReflectionPrintfPushConstant(ValidationState_t& _,
+                                                       const Instruction* inst) {
+  if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(4))) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "Offset must be a 32-bit unsigned integer OpConstant";
+  }
+
+  if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(5))) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "Size must be a 32-bit unsigned integer OpConstant";
+  }
+
+  if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(6))) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "BufferSize must be a 32-bit unsigned integer OpConstant";
+  }
+
+  return SPV_SUCCESS;
+}
+
 spv_result_t ValidateClspvReflectionInstruction(ValidationState_t& _,
                                                 const Instruction* inst,
-                                                uint32_t /*version*/) {
+                                                uint32_t version) {
   if (!_.IsVoidType(inst->type_id())) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "Return Type must be OpTypeVoid";
   }
 
-  auto ext_inst = inst->GetOperandAs<NonSemanticClspvReflectionInstructions>(3);
+  uint32_t required_version = 0;
+  const auto ext_inst =
+      inst->GetOperandAs<NonSemanticClspvReflectionInstructions>(3);
   switch (ext_inst) {
     case NonSemanticClspvReflectionKernel:
-      return ValidateClspvReflectionKernel(_, inst);
+    case NonSemanticClspvReflectionArgumentInfo:
+    case NonSemanticClspvReflectionArgumentStorageBuffer:
+    case NonSemanticClspvReflectionArgumentUniform:
+    case NonSemanticClspvReflectionArgumentPodStorageBuffer:
+    case NonSemanticClspvReflectionArgumentPodUniform:
+    case NonSemanticClspvReflectionArgumentPodPushConstant:
+    case NonSemanticClspvReflectionArgumentSampledImage:
+    case NonSemanticClspvReflectionArgumentStorageImage:
+    case NonSemanticClspvReflectionArgumentSampler:
+    case NonSemanticClspvReflectionArgumentWorkgroup:
+    case NonSemanticClspvReflectionSpecConstantWorkgroupSize:
+    case NonSemanticClspvReflectionSpecConstantGlobalOffset:
+    case NonSemanticClspvReflectionSpecConstantWorkDim:
+    case NonSemanticClspvReflectionPushConstantGlobalOffset:
+    case NonSemanticClspvReflectionPushConstantEnqueuedLocalSize:
+    case NonSemanticClspvReflectionPushConstantGlobalSize:
+    case NonSemanticClspvReflectionPushConstantRegionOffset:
+    case NonSemanticClspvReflectionPushConstantNumWorkgroups:
+    case NonSemanticClspvReflectionPushConstantRegionGroupOffset:
+    case NonSemanticClspvReflectionConstantDataStorageBuffer:
+    case NonSemanticClspvReflectionConstantDataUniform:
+    case NonSemanticClspvReflectionLiteralSampler:
+    case NonSemanticClspvReflectionPropertyRequiredWorkgroupSize:
+      required_version = 1;
+      break;
+    case NonSemanticClspvReflectionSpecConstantSubgroupMaxSize:
+      required_version = 2;
+      break;
+    case NonSemanticClspvReflectionArgumentPointerPushConstant:
+    case NonSemanticClspvReflectionArgumentPointerUniform:
+    case NonSemanticClspvReflectionProgramScopeVariablesStorageBuffer:
+    case NonSemanticClspvReflectionProgramScopeVariablePointerRelocation:
+    case NonSemanticClspvReflectionImageArgumentInfoChannelOrderPushConstant:
+    case NonSemanticClspvReflectionImageArgumentInfoChannelDataTypePushConstant:
+    case NonSemanticClspvReflectionImageArgumentInfoChannelOrderUniform:
+    case NonSemanticClspvReflectionImageArgumentInfoChannelDataTypeUniform:
+      required_version = 3;
+      break;
+    case NonSemanticClspvReflectionArgumentStorageTexelBuffer:
+    case NonSemanticClspvReflectionArgumentUniformTexelBuffer:
+      required_version = 4;
+      break;
+    case NonSemanticClspvReflectionConstantDataPointerPushConstant:
+    case NonSemanticClspvReflectionProgramScopeVariablePointerPushConstant:
+    case NonSemanticClspvReflectionPrintfInfo:
+    case NonSemanticClspvReflectionPrintfBufferStorageBuffer:
+    case NonSemanticClspvReflectionPrintfBufferPointerPushConstant:
+      required_version = 5;
+      break;
+    default:
+      break;
+  }
+  if (version < required_version) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << ReflectionInstructionName(_, inst) << " requires version "
+           << required_version << ", but parsed version is " << version;
+  }
+
+  switch (ext_inst) {
+    case NonSemanticClspvReflectionKernel:
+      return ValidateClspvReflectionKernel(_, inst, version);
     case NonSemanticClspvReflectionArgumentInfo:
       return ValidateClspvReflectionArgumentInfo(_, inst);
     case NonSemanticClspvReflectionArgumentStorageBuffer:
@@ -669,12 +944,16 @@
     case NonSemanticClspvReflectionArgumentSampledImage:
     case NonSemanticClspvReflectionArgumentStorageImage:
     case NonSemanticClspvReflectionArgumentSampler:
+    case NonSemanticClspvReflectionArgumentStorageTexelBuffer:
+    case NonSemanticClspvReflectionArgumentUniformTexelBuffer:
       return ValidateClspvReflectionArgumentBuffer(_, inst);
     case NonSemanticClspvReflectionArgumentPodStorageBuffer:
     case NonSemanticClspvReflectionArgumentPodUniform:
-      return ValidateClspvReflectionArgumentPodBuffer(_, inst);
+    case NonSemanticClspvReflectionArgumentPointerUniform:
+      return ValidateClspvReflectionArgumentOffsetBuffer(_, inst);
     case NonSemanticClspvReflectionArgumentPodPushConstant:
-      return ValidateClspvReflectionArgumentPodPushConstant(_, inst);
+    case NonSemanticClspvReflectionArgumentPointerPushConstant:
+      return ValidateClspvReflectionArgumentPushConstant(_, inst);
     case NonSemanticClspvReflectionArgumentWorkgroup:
       return ValidateClspvReflectionArgumentWorkgroup(_, inst);
     case NonSemanticClspvReflectionSpecConstantWorkgroupSize:
@@ -691,11 +970,31 @@
       return ValidateClspvReflectionPushConstant(_, inst);
     case NonSemanticClspvReflectionConstantDataStorageBuffer:
     case NonSemanticClspvReflectionConstantDataUniform:
-      return ValidateClspvReflectionConstantData(_, inst);
+    case NonSemanticClspvReflectionProgramScopeVariablesStorageBuffer:
+      return ValidateClspvReflectionInitializedData(_, inst);
     case NonSemanticClspvReflectionLiteralSampler:
       return ValidateClspvReflectionSampler(_, inst);
     case NonSemanticClspvReflectionPropertyRequiredWorkgroupSize:
       return ValidateClspvReflectionPropertyRequiredWorkgroupSize(_, inst);
+    case NonSemanticClspvReflectionSpecConstantSubgroupMaxSize:
+      return ValidateClspvReflectionSubgroupMaxSize(_, inst);
+    case NonSemanticClspvReflectionProgramScopeVariablePointerRelocation:
+      return ValidateClspvReflectionPointerRelocation(_, inst);
+    case NonSemanticClspvReflectionImageArgumentInfoChannelOrderPushConstant:
+    case NonSemanticClspvReflectionImageArgumentInfoChannelDataTypePushConstant:
+      return ValidateClspvReflectionImageMetadataPushConstant(_, inst);
+    case NonSemanticClspvReflectionImageArgumentInfoChannelOrderUniform:
+    case NonSemanticClspvReflectionImageArgumentInfoChannelDataTypeUniform:
+      return ValidateClspvReflectionImageMetadataUniform(_, inst);
+    case NonSemanticClspvReflectionConstantDataPointerPushConstant:
+    case NonSemanticClspvReflectionProgramScopeVariablePointerPushConstant:
+      return ValidateClspvReflectionPushConstantData(_, inst);
+    case NonSemanticClspvReflectionPrintfInfo:
+      return ValidateClspvReflectionPrintfInfo(_, inst);
+    case NonSemanticClspvReflectionPrintfBufferStorageBuffer:
+      return ValidateClspvReflectionPrintfStorageBuffer(_, inst);
+    case NonSemanticClspvReflectionPrintfBufferPointerPushConstant:
+      return ValidateClspvReflectionPrintfPushConstant(_, inst);
     default:
       break;
   }
@@ -705,7 +1004,7 @@
 
 bool IsConstIntScalarTypeWith32Or64Bits(ValidationState_t& _,
                                         Instruction* instr) {
-  if (instr->opcode() != SpvOpConstant) return false;
+  if (instr->opcode() != spv::Op::OpConstant) 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;
@@ -714,7 +1013,7 @@
 bool IsConstWithIntScalarType(ValidationState_t& _, const Instruction* inst,
                               uint32_t word_index) {
   auto* int_scalar_const = _.FindDef(inst->word(word_index));
-  if (int_scalar_const->opcode() == SpvOpConstant &&
+  if (int_scalar_const->opcode() == spv::Op::OpConstant &&
       _.IsIntScalarType(int_scalar_const->type_id())) {
     return true;
   }
@@ -757,7 +1056,8 @@
     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_EXT_mesh_shader) ||
+        extension == ExtensionToString(kSPV_NV_shader_invocation_reorder)) {
       return _.diag(SPV_ERROR_WRONG_VERSION, inst)
              << extension << " extension requires SPIR-V version 1.4 or later.";
     }
@@ -1020,7 +1320,7 @@
                  << "expected operand X type to be equal to Result Type";
         }
 
-        uint32_t i_storage_class = 0;
+        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)
@@ -1075,7 +1375,7 @@
                  << "expected operand X type to be equal to Result Type";
         }
 
-        uint32_t exp_storage_class = 0;
+        spv::StorageClass exp_storage_class;
         uint32_t exp_data_type = 0;
         if (!_.GetPointerTypeInfo(exp_type, &exp_data_type,
                                   &exp_storage_class)) {
@@ -1424,7 +1724,7 @@
       case GLSLstd450InterpolateAtCentroid:
       case GLSLstd450InterpolateAtSample:
       case GLSLstd450InterpolateAtOffset: {
-        if (!_.HasCapability(SpvCapabilityInterpolationFunction)) {
+        if (!_.HasCapability(spv::Capability::InterpolationFunction)) {
           return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst)
                  << ext_inst_name()
                  << " requires capability InterpolationFunction";
@@ -1444,11 +1744,11 @@
         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() == SpvOpLoad)
+                                     interp_inst->opcode() == spv::Op::OpLoad)
                                         ? _.GetOperandTypeId(interp_inst, 2)
                                         : _.GetOperandTypeId(inst, 4);
 
-        uint32_t interpolant_storage_class = 0;
+        spv::StorageClass interpolant_storage_class;
         uint32_t interpolant_data_type = 0;
         if (!_.GetPointerTypeInfo(interpolant_type, &interpolant_data_type,
                                   &interpolant_storage_class)) {
@@ -1463,7 +1763,7 @@
                  << "expected Interpolant data type to be equal to Result Type";
         }
 
-        if (interpolant_storage_class != SpvStorageClassInput) {
+        if (interpolant_storage_class != spv::StorageClass::Input) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
                  << ext_inst_name() << ": "
                  << "expected Interpolant storage class to be Input";
@@ -1492,7 +1792,7 @@
 
         _.function(inst->function()->id())
             ->RegisterExecutionModelLimitation(
-                SpvExecutionModelFragment,
+                spv::ExecutionModel::Fragment,
                 ext_inst_name() +
                     std::string(" requires Fragment execution model"));
         break;
@@ -1662,7 +1962,7 @@
         }
 
         const uint32_t p_type = _.GetOperandTypeId(inst, 5);
-        uint32_t p_storage_class = 0;
+        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)
@@ -1670,10 +1970,10 @@
                  << "expected the last operand to be a pointer";
         }
 
-        if (p_storage_class != SpvStorageClassGeneric &&
-            p_storage_class != SpvStorageClassCrossWorkgroup &&
-            p_storage_class != SpvStorageClassWorkgroup &&
-            p_storage_class != SpvStorageClassFunction) {
+        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, "
@@ -1724,7 +2024,7 @@
         }
 
         const uint32_t p_type = _.GetOperandTypeId(inst, operand_index++);
-        uint32_t p_storage_class = 0;
+        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)
@@ -1732,10 +2032,10 @@
                  << "expected the last operand to be a pointer";
         }
 
-        if (p_storage_class != SpvStorageClassGeneric &&
-            p_storage_class != SpvStorageClassCrossWorkgroup &&
-            p_storage_class != SpvStorageClassWorkgroup &&
-            p_storage_class != SpvStorageClassFunction) {
+        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, "
@@ -2254,7 +2554,7 @@
                  << "-bit integer for the addressing model used in the module)";
         }
 
-        uint32_t p_storage_class = 0;
+        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)
@@ -2262,11 +2562,11 @@
                  << "expected operand P to be a pointer";
         }
 
-        if (p_storage_class != SpvStorageClassUniformConstant &&
-            p_storage_class != SpvStorageClassGeneric &&
-            p_storage_class != SpvStorageClassCrossWorkgroup &&
-            p_storage_class != SpvStorageClassWorkgroup &&
-            p_storage_class != SpvStorageClassFunction) {
+        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, "
@@ -2291,7 +2591,7 @@
       }
 
       case OpenCLLIB::Vstoren: {
-        if (_.GetIdOpcode(result_type) != SpvOpTypeVoid) {
+        if (_.GetIdOpcode(result_type) != spv::Op::OpTypeVoid) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
                  << ext_inst_name() << ": expected Result Type to be void";
         }
@@ -2329,7 +2629,7 @@
                  << "-bit integer for the addressing model used in the module)";
         }
 
-        uint32_t p_storage_class = 0;
+        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)
@@ -2337,10 +2637,10 @@
                  << "expected operand P to be a pointer";
         }
 
-        if (p_storage_class != SpvStorageClassGeneric &&
-            p_storage_class != SpvStorageClassCrossWorkgroup &&
-            p_storage_class != SpvStorageClassWorkgroup &&
-            p_storage_class != SpvStorageClassFunction) {
+        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, "
@@ -2382,7 +2682,7 @@
                  << "-bit integer for the addressing model used in the module)";
         }
 
-        uint32_t p_storage_class = 0;
+        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)
@@ -2390,11 +2690,11 @@
                  << "expected operand P to be a pointer";
         }
 
-        if (p_storage_class != SpvStorageClassUniformConstant &&
-            p_storage_class != SpvStorageClassGeneric &&
-            p_storage_class != SpvStorageClassCrossWorkgroup &&
-            p_storage_class != SpvStorageClassWorkgroup &&
-            p_storage_class != SpvStorageClassFunction) {
+        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, "
@@ -2444,7 +2744,7 @@
                  << "-bit integer for the addressing model used in the module)";
         }
 
-        uint32_t p_storage_class = 0;
+        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)
@@ -2452,11 +2752,11 @@
                  << "expected operand P to be a pointer";
         }
 
-        if (p_storage_class != SpvStorageClassUniformConstant &&
-            p_storage_class != SpvStorageClassGeneric &&
-            p_storage_class != SpvStorageClassCrossWorkgroup &&
-            p_storage_class != SpvStorageClassWorkgroup &&
-            p_storage_class != SpvStorageClassFunction) {
+        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, "
@@ -2486,7 +2786,7 @@
       case OpenCLLIB::Vstore_halfn_r:
       case OpenCLLIB::Vstorea_halfn:
       case OpenCLLIB::Vstorea_halfn_r: {
-        if (_.GetIdOpcode(result_type) != SpvOpTypeVoid) {
+        if (_.GetIdOpcode(result_type) != spv::Op::OpTypeVoid) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
                  << ext_inst_name() << ": expected Result Type to be void";
         }
@@ -2537,7 +2837,7 @@
                  << "-bit integer for the addressing model used in the module)";
         }
 
-        uint32_t p_storage_class = 0;
+        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)
@@ -2545,10 +2845,10 @@
                  << "expected operand P to be a pointer";
         }
 
-        if (p_storage_class != SpvStorageClassGeneric &&
-            p_storage_class != SpvStorageClassCrossWorkgroup &&
-            p_storage_class != SpvStorageClassWorkgroup &&
-            p_storage_class != SpvStorageClassFunction) {
+        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, "
@@ -2653,7 +2953,7 @@
         }
 
         const uint32_t format_type = _.GetOperandTypeId(inst, 4);
-        uint32_t format_storage_class = 0;
+        spv::StorageClass format_storage_class;
         uint32_t format_data_type = 0;
         if (!_.GetPointerTypeInfo(format_type, &format_data_type,
                                   &format_storage_class)) {
@@ -2662,7 +2962,7 @@
                  << "expected operand Format to be a pointer";
         }
 
-        if (format_storage_class != SpvStorageClassUniformConstant) {
+        if (format_storage_class != spv::StorageClass::UniformConstant) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
                  << ext_inst_name() << ": "
                  << "expected Format storage class to be UniformConstant";
@@ -2678,7 +2978,7 @@
       }
 
       case OpenCLLIB::Prefetch: {
-        if (_.GetIdOpcode(result_type) != SpvOpTypeVoid) {
+        if (_.GetIdOpcode(result_type) != spv::Op::OpTypeVoid) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
                  << ext_inst_name() << ": expected Result Type to be void";
         }
@@ -2686,7 +2986,7 @@
         const uint32_t p_type = _.GetOperandTypeId(inst, 4);
         const uint32_t num_elements_type = _.GetOperandTypeId(inst, 5);
 
-        uint32_t p_storage_class = 0;
+        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)
@@ -2694,7 +2994,7 @@
                  << "expected operand Ptr to be a pointer";
         }
 
-        if (p_storage_class != SpvStorageClassCrossWorkgroup) {
+        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";
@@ -2857,13 +3157,13 @@
           break;
         }
         case CommonDebugInfoDebugSource: {
-          CHECK_OPERAND("File", SpvOpString, 5);
-          if (num_words == 7) CHECK_OPERAND("Text", SpvOpString, 6);
+          CHECK_OPERAND("File", spv::Op::OpString, 5);
+          if (num_words == 7) CHECK_OPERAND("Text", spv::Op::OpString, 6);
           break;
         }
         case CommonDebugInfoDebugTypeBasic: {
-          CHECK_OPERAND("Name", SpvOpString, 5);
-          CHECK_OPERAND("Size", SpvOpConstant, 6);
+          CHECK_OPERAND("Name", spv::Op::OpString, 5);
+          CHECK_OPERAND("Size", spv::Op::OpConstant, 6);
           CHECK_CONST_UINT_OPERAND("Encoding", 7);
           break;
         }
@@ -2966,7 +3266,7 @@
           break;
         }
         case CommonDebugInfoDebugTypedef: {
-          CHECK_OPERAND("Name", SpvOpString, 5);
+          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;
@@ -2983,7 +3283,7 @@
           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() != SpvOpTypeVoid) {
+          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;
@@ -2996,7 +3296,7 @@
           break;
         }
         case CommonDebugInfoDebugTypeEnum: {
-          CHECK_OPERAND("Name", SpvOpString, 5);
+          CHECK_OPERAND("Name", spv::Op::OpString, 5);
           if (!DoesDebugInfoOperandMatchExpectation(
                   _,
                   [](CommonDebugInfoInstructions dbg_inst) {
@@ -3014,7 +3314,7 @@
           auto validate_parent =
               ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name);
           if (validate_parent != SPV_SUCCESS) return validate_parent;
-          CHECK_OPERAND("Size", SpvOpConstant, 11);
+          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)
@@ -3024,27 +3324,27 @@
           CHECK_CONST_UINT_OPERAND("Flags", 12);
           for (uint32_t word_index = 13; word_index + 1 < num_words;
                word_index += 2) {
-            CHECK_OPERAND("Value", SpvOpConstant, word_index);
-            CHECK_OPERAND("Name", SpvOpString, word_index + 1);
+            CHECK_OPERAND("Value", spv::Op::OpConstant, word_index);
+            CHECK_OPERAND("Name", spv::Op::OpString, word_index + 1);
           }
           break;
         }
         case CommonDebugInfoDebugTypeComposite: {
-          CHECK_OPERAND("Name", SpvOpString, 5);
+          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", SpvOpString, 11);
+          CHECK_OPERAND("Linkage Name", spv::Op::OpString, 11);
           if (!DoesDebugInfoOperandMatchExpectation(
                   _,
                   [](CommonDebugInfoInstructions dbg_inst) {
                     return dbg_inst == CommonDebugInfoDebugInfoNone;
                   },
                   inst, 12)) {
-            CHECK_OPERAND("Size", SpvOpConstant, 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) {
@@ -3066,7 +3366,7 @@
           break;
         }
         case CommonDebugInfoDebugTypeMember: {
-          CHECK_OPERAND("Name", SpvOpString, 5);
+          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 =
@@ -3077,17 +3377,19 @@
           CHECK_CONST_UINT_OPERAND("Column", 9);
           // NonSemantic.Shader.DebugInfo doesn't have the Parent operand
           if (vulkanDebugInfo) {
-            CHECK_OPERAND("Offset", SpvOpConstant, 10);
-            CHECK_OPERAND("Size", SpvOpConstant, 11);
+            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", SpvOpConstant, 13);
+            if (num_words == 14)
+              CHECK_OPERAND("Value", spv::Op::OpConstant, 13);
           } else {
             CHECK_DEBUG_OPERAND("Parent", CommonDebugInfoDebugTypeComposite,
                                 10);
-            CHECK_OPERAND("Offset", SpvOpConstant, 11);
-            CHECK_OPERAND("Size", SpvOpConstant, 12);
+            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", SpvOpConstant, 14);
+            if (num_words == 15)
+              CHECK_OPERAND("Value", spv::Op::OpConstant, 14);
           }
           break;
         }
@@ -3114,13 +3416,13 @@
                    << "expected operand Parent must be class or struct debug "
                       "type";
           }
-          CHECK_OPERAND("Offset", SpvOpConstant, 7);
-          CHECK_OPERAND("Size", SpvOpConstant, 8);
+          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", SpvOpString, 5);
+          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;
@@ -3130,7 +3432,7 @@
           auto validate_parent =
               ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name);
           if (validate_parent != SPV_SUCCESS) return validate_parent;
-          CHECK_OPERAND("Linkage Name", SpvOpString, 11);
+          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
@@ -3147,7 +3449,7 @@
                       return dbg_inst == CommonDebugInfoDebugInfoNone;
                     },
                     inst, 14)) {
-              CHECK_OPERAND("Function", SpvOpFunction, 14);
+              CHECK_OPERAND("Function", spv::Op::OpFunction, 14);
             }
             if (num_words == 16) {
               CHECK_DEBUG_OPERAND("Declaration",
@@ -3157,7 +3459,7 @@
           break;
         }
         case CommonDebugInfoDebugFunctionDeclaration: {
-          CHECK_OPERAND("Name", SpvOpString, 5);
+          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;
@@ -3167,7 +3469,7 @@
           auto validate_parent =
               ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name);
           if (validate_parent != SPV_SUCCESS) return validate_parent;
-          CHECK_OPERAND("Linkage Name", SpvOpString, 11);
+          CHECK_OPERAND("Linkage Name", spv::Op::OpString, 11);
           CHECK_CONST_UINT_OPERAND("Flags", 12);
           break;
         }
@@ -3178,7 +3480,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", SpvOpString, 9);
+          if (num_words == 10) CHECK_OPERAND("Name", spv::Op::OpString, 9);
           break;
         }
         case CommonDebugInfoDebugScope: {
@@ -3191,7 +3493,7 @@
           break;
         }
         case CommonDebugInfoDebugLocalVariable: {
-          CHECK_OPERAND("Name", SpvOpString, 5);
+          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 =
@@ -3213,8 +3515,8 @@
           CHECK_DEBUG_OPERAND("Local Variable",
                               CommonDebugInfoDebugLocalVariable, 5);
           auto* operand = _.FindDef(inst->word(6));
-          if (operand->opcode() != SpvOpVariable &&
-              operand->opcode() != SpvOpFunctionParameter) {
+          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 "
@@ -3276,7 +3578,7 @@
           break;
         }
         case CommonDebugInfoDebugTypeTemplateParameter: {
-          CHECK_OPERAND("Name", SpvOpString, 5);
+          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;
@@ -3286,7 +3588,7 @@
                     return dbg_inst == CommonDebugInfoDebugInfoNone;
                   },
                   inst, 7)) {
-            CHECK_OPERAND("Value", SpvOpConstant, 7);
+            CHECK_OPERAND("Value", spv::Op::OpConstant, 7);
           }
           CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 8);
           CHECK_CONST_UINT_OPERAND("Line", 9);
@@ -3294,7 +3596,7 @@
           break;
         }
         case CommonDebugInfoDebugGlobalVariable: {
-          CHECK_OPERAND("Name", SpvOpString, 5);
+          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;
@@ -3304,7 +3606,7 @@
           auto validate_scope =
               ValidateOperandLexicalScope(_, "Scope", inst, 10, ext_inst_name);
           if (validate_scope != SPV_SUCCESS) return validate_scope;
-          CHECK_OPERAND("Linkage Name", SpvOpString, 11);
+          CHECK_OPERAND("Linkage Name", spv::Op::OpString, 11);
           if (!DoesDebugInfoOperandMatchExpectation(
                   _,
                   [](CommonDebugInfoInstructions dbg_inst) {
@@ -3312,8 +3614,8 @@
                   },
                   inst, 12)) {
             auto* operand = _.FindDef(inst->word(12));
-            if (operand->opcode() != SpvOpVariable &&
-                operand->opcode() != SpvOpConstant) {
+            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 "
@@ -3401,10 +3703,10 @@
 }
 
 spv_result_t ExtensionPass(ValidationState_t& _, const Instruction* inst) {
-  const SpvOp opcode = inst->opcode();
-  if (opcode == SpvOpExtension) return ValidateExtension(_, inst);
-  if (opcode == SpvOpExtInstImport) return ValidateExtInstImport(_, inst);
-  if (opcode == SpvOpExtInst) return ValidateExtInst(_, inst);
+  const spv::Op opcode = inst->opcode();
+  if (opcode == spv::Op::OpExtension) return ValidateExtension(_, inst);
+  if (opcode == spv::Op::OpExtInstImport) return ValidateExtInstImport(_, inst);
+  if (opcode == spv::Op::OpExtInst) return ValidateExtInst(_, inst);
 
   return SPV_SUCCESS;
 }
diff --git a/source/val/validate_function.cpp b/source/val/validate_function.cpp
index 0ccf5a9..639817f 100644
--- a/source/val/validate_function.cpp
+++ b/source/val/validate_function.cpp
@@ -14,6 +14,7 @@
 
 #include <algorithm>
 
+#include "source/enum_string_mapping.h"
 #include "source/opcode.h"
 #include "source/val/instruction.h"
 #include "source/val/validate.h"
@@ -28,7 +29,8 @@
 // of the decorations that apply to |a|.
 bool DoPointeesLogicallyMatch(val::Instruction* a, val::Instruction* b,
                               ValidationState_t& _) {
-  if (a->opcode() != SpvOpTypePointer || b->opcode() != SpvOpTypePointer) {
+  if (a->opcode() != spv::Op::OpTypePointer ||
+      b->opcode() != spv::Op::OpTypePointer) {
     return false;
   }
 
@@ -56,7 +58,7 @@
 spv_result_t ValidateFunction(ValidationState_t& _, const Instruction* inst) {
   const auto function_type_id = inst->GetOperandAs<uint32_t>(3);
   const auto function_type = _.FindDef(function_type_id);
-  if (!function_type || SpvOpTypeFunction != function_type->opcode()) {
+  if (!function_type || spv::Op::OpTypeFunction != function_type->opcode()) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "OpFunction Function Type <id> " << _.getIdName(function_type_id)
            << " is not a function type.";
@@ -70,21 +72,21 @@
            << _.getIdName(return_id) << ".";
   }
 
-  const std::vector<SpvOp> acceptable = {
-      SpvOpGroupDecorate,
-      SpvOpDecorate,
-      SpvOpEnqueueKernel,
-      SpvOpEntryPoint,
-      SpvOpExecutionMode,
-      SpvOpExecutionModeId,
-      SpvOpFunctionCall,
-      SpvOpGetKernelNDrangeSubGroupCount,
-      SpvOpGetKernelNDrangeMaxSubGroupSize,
-      SpvOpGetKernelWorkGroupSize,
-      SpvOpGetKernelPreferredWorkGroupSizeMultiple,
-      SpvOpGetKernelLocalSizeForSubgroupCount,
-      SpvOpGetKernelMaxNumSubgroups,
-      SpvOpName};
+  const std::vector<spv::Op> acceptable = {
+      spv::Op::OpGroupDecorate,
+      spv::Op::OpDecorate,
+      spv::Op::OpEnqueueKernel,
+      spv::Op::OpEntryPoint,
+      spv::Op::OpExecutionMode,
+      spv::Op::OpExecutionModeId,
+      spv::Op::OpFunctionCall,
+      spv::Op::OpGetKernelNDrangeSubGroupCount,
+      spv::Op::OpGetKernelNDrangeMaxSubGroupSize,
+      spv::Op::OpGetKernelWorkGroupSize,
+      spv::Op::OpGetKernelPreferredWorkGroupSizeMultiple,
+      spv::Op::OpGetKernelLocalSizeForSubgroupCount,
+      spv::Op::OpGetKernelMaxNumSubgroups,
+      spv::Op::OpName};
   for (auto& pair : inst->uses()) {
     const auto* use = pair.first;
     if (std::find(acceptable.begin(), acceptable.end(), use->opcode()) ==
@@ -112,14 +114,14 @@
   auto func_inst = &_.ordered_instructions()[inst_num];
   while (--inst_num) {
     func_inst = &_.ordered_instructions()[inst_num];
-    if (func_inst->opcode() == SpvOpFunction) {
+    if (func_inst->opcode() == spv::Op::OpFunction) {
       break;
-    } else if (func_inst->opcode() == SpvOpFunctionParameter) {
+    } else if (func_inst->opcode() == spv::Op::OpFunctionParameter) {
       ++param_index;
     }
   }
 
-  if (func_inst->opcode() != SpvOpFunction) {
+  if (func_inst->opcode() != spv::Op::OpFunction) {
     return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
            << "Function parameter must be preceded by a function.";
   }
@@ -150,25 +152,25 @@
   // Validate that PhysicalStorageBuffer have one of Restrict, Aliased,
   // RestrictPointer, or AliasedPointer.
   auto param_nonarray_type_id = param_type->id();
-  while (_.GetIdOpcode(param_nonarray_type_id) == SpvOpTypeArray) {
+  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) == SpvOpTypePointer) {
+  if (_.GetIdOpcode(param_nonarray_type_id) == spv::Op::OpTypePointer) {
     auto param_nonarray_type = _.FindDef(param_nonarray_type_id);
-    if (param_nonarray_type->GetOperandAs<uint32_t>(1u) ==
-        SpvStorageClassPhysicalStorageBuffer) {
+    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 SpvDecorationAliased == d.dec_type();
+            return spv::Decoration::Aliased == d.dec_type();
           });
 
       bool foundRestrict = std::any_of(
           decorations.begin(), decorations.end(), [](const Decoration& d) {
-            return SpvDecorationRestrict == d.dec_type();
+            return spv::Decoration::Restrict == d.dec_type();
           });
 
       if (!foundAliased && !foundRestrict) {
@@ -187,20 +189,20 @@
       const auto pointee_type_id =
           param_nonarray_type->GetOperandAs<uint32_t>(2);
       const auto pointee_type = _.FindDef(pointee_type_id);
-      if (SpvOpTypePointer == pointee_type->opcode() &&
-          pointee_type->GetOperandAs<uint32_t>(1u) ==
-              SpvStorageClassPhysicalStorageBuffer) {
+      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 SpvDecorationAliasedPointer == d.dec_type();
+              return spv::Decoration::AliasedPointer == d.dec_type();
             });
 
         bool foundRestrict = std::any_of(
             decorations.begin(), decorations.end(), [](const Decoration& d) {
-              return SpvDecorationRestrictPointer == d.dec_type();
+              return spv::Decoration::RestrictPointer == d.dec_type();
             });
 
         if (!foundAliased && !foundRestrict) {
@@ -226,7 +228,7 @@
                                   const Instruction* inst) {
   const auto function_id = inst->GetOperandAs<uint32_t>(2);
   const auto function = _.FindDef(function_id);
-  if (!function || SpvOpFunction != function->opcode()) {
+  if (!function || spv::Op::OpFunction != function->opcode()) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "OpFunctionCall Function <id> " << _.getIdName(function_id)
            << " is not a function.";
@@ -242,7 +244,7 @@
 
   const auto function_type_id = function->GetOperandAs<uint32_t>(3);
   const auto function_type = _.FindDef(function_type_id);
-  if (!function_type || function_type->opcode() != SpvOpTypeFunction) {
+  if (!function_type || function_type->opcode() != spv::Op::OpTypeFunction) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "Missing function type definition.";
   }
@@ -285,20 +287,21 @@
       }
     }
 
-    if (_.addressing_model() == SpvAddressingModelLogical) {
-      if (parameter_type->opcode() == SpvOpTypePointer &&
+    if (_.addressing_model() == spv::AddressingModel::Logical) {
+      if (parameter_type->opcode() == spv::Op::OpTypePointer &&
           !_.options()->relax_logical_pointer) {
-        SpvStorageClass sc = parameter_type->GetOperandAs<SpvStorageClass>(1u);
+        spv::StorageClass sc =
+            parameter_type->GetOperandAs<spv::StorageClass>(1u);
         // Validate which storage classes can be pointer operands.
         switch (sc) {
-          case SpvStorageClassUniformConstant:
-          case SpvStorageClassFunction:
-          case SpvStorageClassPrivate:
-          case SpvStorageClassWorkgroup:
-          case SpvStorageClassAtomicCounter:
+          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 SpvStorageClassStorageBuffer:
+          case spv::StorageClass::StorageBuffer:
             if (!_.features().variable_pointers) {
               return _.diag(SPV_ERROR_INVALID_ID, inst)
                      << "StorageBuffer pointer operand "
@@ -313,13 +316,14 @@
         }
 
         // Validate memory object declaration requirements.
-        if (argument->opcode() != SpvOpVariable &&
-            argument->opcode() != SpvOpFunctionParameter) {
+        if (argument->opcode() != spv::Op::OpVariable &&
+            argument->opcode() != spv::Op::OpFunctionParameter) {
           const bool ssbo_vptr = _.features().variable_pointers &&
-                                 sc == SpvStorageClassStorageBuffer;
-          const bool wg_vptr = _.HasCapability(SpvCapabilityVariablePointers) &&
-                               sc == SpvStorageClassWorkgroup;
-          const bool uc_ptr = sc == SpvStorageClassUniformConstant;
+                                 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)
@@ -336,13 +340,13 @@
 
 spv_result_t FunctionPass(ValidationState_t& _, const Instruction* inst) {
   switch (inst->opcode()) {
-    case SpvOpFunction:
+    case spv::Op::OpFunction:
       if (auto error = ValidateFunction(_, inst)) return error;
       break;
-    case SpvOpFunctionParameter:
+    case spv::Op::OpFunctionParameter:
       if (auto error = ValidateFunctionParameter(_, inst)) return error;
       break;
-    case SpvOpFunctionCall:
+    case spv::Op::OpFunctionCall:
       if (auto error = ValidateFunctionCall(_, inst)) return error;
       break;
     default:
diff --git a/source/val/validate_id.cpp b/source/val/validate_id.cpp
index 2bab203..bcfeb59 100644
--- a/source/val/validate_id.cpp
+++ b/source/val/validate_id.cpp
@@ -12,25 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "source/val/validate.h"
-
-#include <cassert>
-
-#include <algorithm>
-#include <iostream>
-#include <iterator>
-#include <stack>
-#include <string>
 #include <unordered_set>
-#include <utility>
 #include <vector>
 
-#include "source/diagnostic.h"
 #include "source/instruction.h"
 #include "source/opcode.h"
 #include "source/operand.h"
-#include "source/spirv_validator_options.h"
 #include "source/val/function.h"
+#include "source/val/validate.h"
 #include "source/val/validation_state.h"
 #include "spirv-tools/libspirv.h"
 
@@ -71,7 +60,7 @@
           const Instruction* use = use_index_pair.first;
           if (const BasicBlock* use_block = use->block()) {
             if (use_block->reachable() == false) continue;
-            if (use->opcode() == SpvOpPhi) {
+            if (use->opcode() == spv::Op::OpPhi) {
               if (phi_ids.insert(use->id()).second) {
                 phi_instructions.push_back(use);
               }
@@ -131,7 +120,7 @@
 // instruction operand's ID can be forward referenced.
 spv_result_t IdPass(ValidationState_t& _, Instruction* inst) {
   auto can_have_forward_declared_ids =
-      inst->opcode() == SpvOpExtInst &&
+      inst->opcode() == spv::Op::OpExtInst &&
               spvExtInstIsDebugInfo(inst->ext_inst_type())
           ? spvDbgInfoExtOperandCanBeForwardDeclaredFunction(
                 inst->ext_inst_type(), inst->word(4))
@@ -172,23 +161,33 @@
           if (spvOpcodeGeneratesType(def->opcode()) &&
               !spvOpcodeGeneratesType(opcode) && !spvOpcodeIsDebug(opcode) &&
               !inst->IsDebugInfo() && !inst->IsNonSemantic() &&
-              !spvOpcodeIsDecoration(opcode) && opcode != SpvOpFunction &&
-              opcode != SpvOpCooperativeMatrixLengthNV &&
-              !(opcode == SpvOpSpecConstantOp &&
-                inst->word(3) == SpvOpCooperativeMatrixLengthNV)) {
+              !spvOpcodeIsDecoration(opcode) && opcode != spv::Op::OpFunction &&
+              opcode != spv::Op::OpCooperativeMatrixLengthNV &&
+              opcode != spv::Op::OpCooperativeMatrixLengthKHR &&
+              !(opcode == spv::Op::OpSpecConstantOp &&
+                (spv::Op(inst->word(3)) ==
+                     spv::Op::OpCooperativeMatrixLengthNV ||
+                 spv::Op(inst->word(3)) ==
+                     spv::Op::OpCooperativeMatrixLengthKHR))) {
             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 != SpvOpPhi &&
-                     opcode != SpvOpExtInst && opcode != SpvOpExtInstImport &&
-                     opcode != SpvOpSelectionMerge &&
-                     opcode != SpvOpLoopMerge && opcode != SpvOpFunction &&
-                     opcode != SpvOpCooperativeMatrixLengthNV &&
-                     !(opcode == SpvOpSpecConstantOp &&
-                       inst->word(3) == SpvOpCooperativeMatrixLengthNV)) {
+                     !spvOpcodeIsBranch(opcode) && opcode != spv::Op::OpPhi &&
+                     opcode != spv::Op::OpExtInst &&
+                     opcode != spv::Op::OpExtInstImport &&
+                     opcode != spv::Op::OpSelectionMerge &&
+                     opcode != spv::Op::OpLoopMerge &&
+                     opcode != spv::Op::OpFunction &&
+                     opcode != spv::Op::OpCooperativeMatrixLengthNV &&
+                     opcode != spv::Op::OpCooperativeMatrixLengthKHR &&
+                     !(opcode == spv::Op::OpSpecConstantOp &&
+                       (spv::Op(inst->word(3)) ==
+                            spv::Op::OpCooperativeMatrixLengthNV ||
+                        spv::Op(inst->word(3)) ==
+                            spv::Op::OpCooperativeMatrixLengthKHR))) {
             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 9c7c8c1..39eeb4b 100644
--- a/source/val/validate_image.cpp
+++ b/source/val/validate_image.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017 Google Inc.
+// Copyright (c) 2017 Google Inc.
 // Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights
 // reserved.
 //
@@ -18,7 +18,6 @@
 
 #include <string>
 
-#include "source/diagnostic.h"
 #include "source/opcode.h"
 #include "source/spirv_constant.h"
 #include "source/spirv_target_env.h"
@@ -32,47 +31,47 @@
 namespace val {
 namespace {
 
-// Performs compile time check that all SpvImageOperandsXXX cases are handled in
-// this module. If SpvImageOperandsXXX list changes, this function will fail the
-// build.
-// For all other purposes this is a placeholder function.
+// Performs compile time check that all spv::ImageOperandsMask::XXX cases are
+// handled in this module. If spv::ImageOperandsMask::XXX list changes, this
+// function will fail the build. For all other purposes this is a placeholder
+// function.
 bool CheckAllImageOperandsHandled() {
-  SpvImageOperandsMask enum_val = SpvImageOperandsBiasMask;
+  spv::ImageOperandsMask enum_val = spv::ImageOperandsMask::Bias;
 
   // Some improvised code to prevent the compiler from considering enum_val
   // constant and optimizing the switch away.
   uint32_t stack_var = 0;
   if (reinterpret_cast<uintptr_t>(&stack_var) % 256)
-    enum_val = SpvImageOperandsLodMask;
+    enum_val = spv::ImageOperandsMask::Lod;
 
   switch (enum_val) {
     // Please update the validation rules in this module if you are changing
     // the list of image operands, and add new enum values to this switch.
-    case SpvImageOperandsMaskNone:
+    case spv::ImageOperandsMask::MaskNone:
       return false;
-    case SpvImageOperandsBiasMask:
-    case SpvImageOperandsLodMask:
-    case SpvImageOperandsGradMask:
-    case SpvImageOperandsConstOffsetMask:
-    case SpvImageOperandsOffsetMask:
-    case SpvImageOperandsConstOffsetsMask:
-    case SpvImageOperandsSampleMask:
-    case SpvImageOperandsMinLodMask:
+    case spv::ImageOperandsMask::Bias:
+    case spv::ImageOperandsMask::Lod:
+    case spv::ImageOperandsMask::Grad:
+    case spv::ImageOperandsMask::ConstOffset:
+    case spv::ImageOperandsMask::Offset:
+    case spv::ImageOperandsMask::ConstOffsets:
+    case spv::ImageOperandsMask::Sample:
+    case spv::ImageOperandsMask::MinLod:
 
     // TODO(dneto): Support image operands related to the Vulkan memory model.
     // https://gitlab.khronos.org/spirv/spirv-tools/issues/32
-    case SpvImageOperandsMakeTexelAvailableKHRMask:
-    case SpvImageOperandsMakeTexelVisibleKHRMask:
-    case SpvImageOperandsNonPrivateTexelKHRMask:
-    case SpvImageOperandsVolatileTexelKHRMask:
-    case SpvImageOperandsSignExtendMask:
-    case SpvImageOperandsZeroExtendMask:
+    case spv::ImageOperandsMask::MakeTexelAvailableKHR:
+    case spv::ImageOperandsMask::MakeTexelVisibleKHR:
+    case spv::ImageOperandsMask::NonPrivateTexelKHR:
+    case spv::ImageOperandsMask::VolatileTexelKHR:
+    case spv::ImageOperandsMask::SignExtend:
+    case spv::ImageOperandsMask::ZeroExtend:
     // TODO(jaebaek): Move this line properly after handling image offsets
     //                operand. This line temporarily fixes CI failure that
     //                blocks other PRs.
     // https://github.com/KhronosGroup/SPIRV-Tools/issues/4565
-    case SpvImageOperandsOffsetsMask:
-    case SpvImageOperandsNontemporalMask:
+    case spv::ImageOperandsMask::Offsets:
+    case spv::ImageOperandsMask::Nontemporal:
       return true;
   }
   return false;
@@ -81,13 +80,13 @@
 // Used by GetImageTypeInfo. See OpTypeImage spec for more information.
 struct ImageTypeInfo {
   uint32_t sampled_type = 0;
-  SpvDim dim = SpvDimMax;
+  spv::Dim dim = spv::Dim::Max;
   uint32_t depth = 0;
   uint32_t arrayed = 0;
   uint32_t multisampled = 0;
   uint32_t sampled = 0;
-  SpvImageFormat format = SpvImageFormatMax;
-  SpvAccessQualifier access_qualifier = SpvAccessQualifierMax;
+  spv::ImageFormat format = spv::ImageFormat::Max;
+  spv::AccessQualifier access_qualifier = spv::AccessQualifier::Max;
 };
 
 // Provides information on image type. |id| should be object of either
@@ -100,39 +99,39 @@
   const Instruction* inst = _.FindDef(id);
   assert(inst);
 
-  if (inst->opcode() == SpvOpTypeSampledImage) {
+  if (inst->opcode() == spv::Op::OpTypeSampledImage) {
     inst = _.FindDef(inst->word(2));
     assert(inst);
   }
 
-  if (inst->opcode() != SpvOpTypeImage) return false;
+  if (inst->opcode() != spv::Op::OpTypeImage) return false;
 
   const size_t num_words = inst->words().size();
   if (num_words != 9 && num_words != 10) return false;
 
   info->sampled_type = inst->word(2);
-  info->dim = static_cast<SpvDim>(inst->word(3));
+  info->dim = static_cast<spv::Dim>(inst->word(3));
   info->depth = inst->word(4);
   info->arrayed = inst->word(5);
   info->multisampled = inst->word(6);
   info->sampled = inst->word(7);
-  info->format = static_cast<SpvImageFormat>(inst->word(8));
-  info->access_qualifier = num_words < 10
-                               ? SpvAccessQualifierMax
-                               : static_cast<SpvAccessQualifier>(inst->word(9));
+  info->format = static_cast<spv::ImageFormat>(inst->word(8));
+  info->access_qualifier =
+      num_words < 10 ? spv::AccessQualifier::Max
+                     : static_cast<spv::AccessQualifier>(inst->word(9));
   return true;
 }
 
-bool IsImplicitLod(SpvOp opcode) {
+bool IsImplicitLod(spv::Op opcode) {
   switch (opcode) {
-    case SpvOpImageSampleImplicitLod:
-    case SpvOpImageSampleDrefImplicitLod:
-    case SpvOpImageSampleProjImplicitLod:
-    case SpvOpImageSampleProjDrefImplicitLod:
-    case SpvOpImageSparseSampleImplicitLod:
-    case SpvOpImageSparseSampleDrefImplicitLod:
-    case SpvOpImageSparseSampleProjImplicitLod:
-    case SpvOpImageSparseSampleProjDrefImplicitLod:
+    case spv::Op::OpImageSampleImplicitLod:
+    case spv::Op::OpImageSampleDrefImplicitLod:
+    case spv::Op::OpImageSampleProjImplicitLod:
+    case spv::Op::OpImageSampleProjDrefImplicitLod:
+    case spv::Op::OpImageSparseSampleImplicitLod:
+    case spv::Op::OpImageSparseSampleDrefImplicitLod:
+    case spv::Op::OpImageSparseSampleProjImplicitLod:
+    case spv::Op::OpImageSparseSampleProjDrefImplicitLod:
       return true;
     default:
       break;
@@ -140,16 +139,16 @@
   return false;
 }
 
-bool IsExplicitLod(SpvOp opcode) {
+bool IsExplicitLod(spv::Op opcode) {
   switch (opcode) {
-    case SpvOpImageSampleExplicitLod:
-    case SpvOpImageSampleDrefExplicitLod:
-    case SpvOpImageSampleProjExplicitLod:
-    case SpvOpImageSampleProjDrefExplicitLod:
-    case SpvOpImageSparseSampleExplicitLod:
-    case SpvOpImageSparseSampleDrefExplicitLod:
-    case SpvOpImageSparseSampleProjExplicitLod:
-    case SpvOpImageSparseSampleProjDrefExplicitLod:
+    case spv::Op::OpImageSampleExplicitLod:
+    case spv::Op::OpImageSampleDrefExplicitLod:
+    case spv::Op::OpImageSampleProjExplicitLod:
+    case spv::Op::OpImageSampleProjDrefExplicitLod:
+    case spv::Op::OpImageSparseSampleExplicitLod:
+    case spv::Op::OpImageSparseSampleDrefExplicitLod:
+    case spv::Op::OpImageSparseSampleProjExplicitLod:
+    case spv::Op::OpImageSparseSampleProjDrefExplicitLod:
       return true;
     default:
       break;
@@ -157,22 +156,22 @@
   return false;
 }
 
-bool IsValidLodOperand(const ValidationState_t& _, SpvOp opcode) {
+bool IsValidLodOperand(const ValidationState_t& _, spv::Op opcode) {
   switch (opcode) {
-    case SpvOpImageRead:
-    case SpvOpImageWrite:
-    case SpvOpImageSparseRead:
-      return _.HasCapability(SpvCapabilityImageReadWriteLodAMD);
+    case spv::Op::OpImageRead:
+    case spv::Op::OpImageWrite:
+    case spv::Op::OpImageSparseRead:
+      return _.HasCapability(spv::Capability::ImageReadWriteLodAMD);
     default:
       return IsExplicitLod(opcode);
   }
 }
 
-bool IsValidGatherLodBiasAMD(const ValidationState_t& _, SpvOp opcode) {
+bool IsValidGatherLodBiasAMD(const ValidationState_t& _, spv::Op opcode) {
   switch (opcode) {
-    case SpvOpImageGather:
-    case SpvOpImageSparseGather:
-      return _.HasCapability(SpvCapabilityImageGatherBiasLodAMD);
+    case spv::Op::OpImageGather:
+    case spv::Op::OpImageSparseGather:
+      return _.HasCapability(spv::Capability::ImageGatherBiasLodAMD);
     default:
       break;
   }
@@ -181,16 +180,16 @@
 
 // Returns true if the opcode is a Image instruction which applies
 // homogenous projection to the coordinates.
-bool IsProj(SpvOp opcode) {
+bool IsProj(spv::Op opcode) {
   switch (opcode) {
-    case SpvOpImageSampleProjImplicitLod:
-    case SpvOpImageSampleProjDrefImplicitLod:
-    case SpvOpImageSparseSampleProjImplicitLod:
-    case SpvOpImageSparseSampleProjDrefImplicitLod:
-    case SpvOpImageSampleProjExplicitLod:
-    case SpvOpImageSampleProjDrefExplicitLod:
-    case SpvOpImageSparseSampleProjExplicitLod:
-    case SpvOpImageSparseSampleProjDrefExplicitLod:
+    case spv::Op::OpImageSampleProjImplicitLod:
+    case spv::Op::OpImageSampleProjDrefImplicitLod:
+    case spv::Op::OpImageSparseSampleProjImplicitLod:
+    case spv::Op::OpImageSparseSampleProjDrefImplicitLod:
+    case spv::Op::OpImageSampleProjExplicitLod:
+    case spv::Op::OpImageSampleProjDrefExplicitLod:
+    case spv::Op::OpImageSparseSampleProjExplicitLod:
+    case spv::Op::OpImageSparseSampleProjDrefExplicitLod:
       return true;
     default:
       break;
@@ -204,21 +203,23 @@
   uint32_t plane_size = 0;
   // If this switch breaks your build, please add new values below.
   switch (info.dim) {
-    case SpvDim1D:
-    case SpvDimBuffer:
+    case spv::Dim::Dim1D:
+    case spv::Dim::Buffer:
       plane_size = 1;
       break;
-    case SpvDim2D:
-    case SpvDimRect:
-    case SpvDimSubpassData:
+    case spv::Dim::Dim2D:
+    case spv::Dim::Rect:
+    case spv::Dim::SubpassData:
+    case spv::Dim::TileImageDataEXT:
       plane_size = 2;
       break;
-    case SpvDim3D:
-    case SpvDimCube:
+    case spv::Dim::Dim3D:
+    case spv::Dim::Cube:
       // For Cube direction vector is used instead of UV.
       plane_size = 3;
       break;
-    case SpvDimMax:
+    case spv::Dim::Max:
+    default:
       assert(0);
       break;
   }
@@ -228,10 +229,10 @@
 
 // Returns minimal number of coordinates based on image dim, arrayed and whether
 // the instruction uses projection coordinates.
-uint32_t GetMinCoordSize(SpvOp opcode, const ImageTypeInfo& info) {
-  if (info.dim == SpvDimCube &&
-      (opcode == SpvOpImageRead || opcode == SpvOpImageWrite ||
-       opcode == SpvOpImageSparseRead)) {
+uint32_t GetMinCoordSize(spv::Op opcode, const ImageTypeInfo& info) {
+  if (info.dim == spv::Dim::Cube &&
+      (opcode == spv::Op::OpImageRead || opcode == spv::Op::OpImageWrite ||
+       opcode == spv::Op::OpImageSparseRead)) {
     // These opcodes use UV for Cube, not direction vector.
     return 3;
   }
@@ -248,7 +249,7 @@
   static const bool kAllImageOperandsHandled = CheckAllImageOperandsHandled();
   (void)kAllImageOperandsHandled;
 
-  const SpvOp opcode = inst->opcode();
+  const spv::Op opcode = inst->opcode();
   const size_t num_words = inst->words().size();
 
   const bool have_explicit_mask = (word_index - 1 < num_words);
@@ -257,14 +258,14 @@
   if (have_explicit_mask) {
     // NonPrivate, Volatile, SignExtend, ZeroExtend take no operand words.
     const uint32_t mask_bits_having_operands =
-        mask & ~uint32_t(SpvImageOperandsNonPrivateTexelKHRMask |
-                         SpvImageOperandsVolatileTexelKHRMask |
-                         SpvImageOperandsSignExtendMask |
-                         SpvImageOperandsZeroExtendMask |
-                         SpvImageOperandsNontemporalMask);
+        mask & ~uint32_t(spv::ImageOperandsMask::NonPrivateTexelKHR |
+                         spv::ImageOperandsMask::VolatileTexelKHR |
+                         spv::ImageOperandsMask::SignExtend |
+                         spv::ImageOperandsMask::ZeroExtend |
+                         spv::ImageOperandsMask::Nontemporal);
     size_t expected_num_image_operand_words =
         spvtools::utils::CountSetBits(mask_bits_having_operands);
-    if (mask & SpvImageOperandsGradMask) {
+    if (mask & uint32_t(spv::ImageOperandsMask::Grad)) {
       // Grad uses two words.
       ++expected_num_image_operand_words;
     }
@@ -279,7 +280,8 @@
            << "Number of image operand ids doesn't correspond to the bit mask";
   }
 
-  if (info.multisampled & (0 == (mask & SpvImageOperandsSampleMask))) {
+  if (info.multisampled &
+      (0 == (mask & uint32_t(spv::ImageOperandsMask::Sample)))) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "Image Operand Sample is required for operation on "
               "multi-sampled image";
@@ -289,12 +291,12 @@
   // the module to be invalid.
   if (mask == 0) return SPV_SUCCESS;
 
-  if (spvtools::utils::CountSetBits(mask & (SpvImageOperandsOffsetMask |
-                                            SpvImageOperandsConstOffsetMask |
-                                            SpvImageOperandsConstOffsetsMask |
-                                            SpvImageOperandsOffsetsMask)) > 1) {
+  if (spvtools::utils::CountSetBits(
+          mask & uint32_t(spv::ImageOperandsMask::Offset |
+                          spv::ImageOperandsMask::ConstOffset |
+                          spv::ImageOperandsMask::ConstOffsets |
+                          spv::ImageOperandsMask::Offsets)) > 1) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << _.VkErrorID(4662)
            << "Image Operands Offset, ConstOffset, ConstOffsets, Offsets "
               "cannot be used together";
   }
@@ -306,7 +308,7 @@
 
   // The checks should be done in the order of definition of OperandImage.
 
-  if (mask & SpvImageOperandsBiasMask) {
+  if (mask & uint32_t(spv::ImageOperandsMask::Bias)) {
     if (!is_implicit_lod && !is_valid_gather_lod_bias_amd) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Image Operand Bias can only be used with ImplicitLod opcodes";
@@ -318,8 +320,8 @@
              << "Expected Image Operand Bias to be float scalar";
     }
 
-    if (info.dim != SpvDim1D && info.dim != SpvDim2D && info.dim != SpvDim3D &&
-        info.dim != SpvDimCube) {
+    if (info.dim != spv::Dim::Dim1D && info.dim != spv::Dim::Dim2D &&
+        info.dim != spv::Dim::Dim3D && info.dim != spv::Dim::Cube) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Image Operand Bias requires 'Dim' parameter to be 1D, 2D, 3D "
                 "or Cube";
@@ -328,15 +330,16 @@
     // Multisampled is already checked.
   }
 
-  if (mask & SpvImageOperandsLodMask) {
-    if (!is_valid_lod_operand && opcode != SpvOpImageFetch &&
-        opcode != SpvOpImageSparseFetch && !is_valid_gather_lod_bias_amd) {
+  if (mask & uint32_t(spv::ImageOperandsMask::Lod)) {
+    if (!is_valid_lod_operand && opcode != spv::Op::OpImageFetch &&
+        opcode != spv::Op::OpImageSparseFetch &&
+        !is_valid_gather_lod_bias_amd) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Image Operand Lod can only be used with ExplicitLod opcodes "
              << "and OpImageFetch";
     }
 
-    if (mask & SpvImageOperandsGradMask) {
+    if (mask & uint32_t(spv::ImageOperandsMask::Grad)) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Image Operand bits Lod and Grad cannot be set at the same "
                 "time";
@@ -357,8 +360,8 @@
       }
     }
 
-    if (info.dim != SpvDim1D && info.dim != SpvDim2D && info.dim != SpvDim3D &&
-        info.dim != SpvDimCube) {
+    if (info.dim != spv::Dim::Dim1D && info.dim != spv::Dim::Dim2D &&
+        info.dim != spv::Dim::Dim3D && info.dim != spv::Dim::Cube) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Image Operand Lod requires 'Dim' parameter to be 1D, 2D, 3D "
                 "or Cube";
@@ -367,7 +370,7 @@
     // Multisampled is already checked.
   }
 
-  if (mask & SpvImageOperandsGradMask) {
+  if (mask & uint32_t(spv::ImageOperandsMask::Grad)) {
     if (!is_explicit_lod) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Image Operand Grad can only be used with ExplicitLod opcodes";
@@ -400,8 +403,8 @@
     // Multisampled is already checked.
   }
 
-  if (mask & SpvImageOperandsConstOffsetMask) {
-    if (info.dim == SpvDimCube) {
+  if (mask & uint32_t(spv::ImageOperandsMask::ConstOffset)) {
+    if (info.dim == spv::Dim::Cube) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Image Operand ConstOffset cannot be used with Cube Image "
                 "'Dim'";
@@ -429,8 +432,8 @@
     }
   }
 
-  if (mask & SpvImageOperandsOffsetMask) {
-    if (info.dim == SpvDimCube) {
+  if (mask & uint32_t(spv::ImageOperandsMask::Offset)) {
+    if (info.dim == spv::Dim::Cube) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Image Operand Offset cannot be used with Cube Image 'Dim'";
     }
@@ -453,9 +456,10 @@
 
     if (!_.options()->before_hlsl_legalization &&
         spvIsVulkanEnv(_.context()->target_env)) {
-      if (opcode != SpvOpImageGather && opcode != SpvOpImageDrefGather &&
-          opcode != SpvOpImageSparseGather &&
-          opcode != SpvOpImageSparseDrefGather) {
+      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)
                << "Image Operand Offset can only be used with "
@@ -464,16 +468,17 @@
     }
   }
 
-  if (mask & SpvImageOperandsConstOffsetsMask) {
-    if (opcode != SpvOpImageGather && opcode != SpvOpImageDrefGather &&
-        opcode != SpvOpImageSparseGather &&
-        opcode != SpvOpImageSparseDrefGather) {
+  if (mask & uint32_t(spv::ImageOperandsMask::ConstOffsets)) {
+    if (opcode != spv::Op::OpImageGather &&
+        opcode != spv::Op::OpImageDrefGather &&
+        opcode != spv::Op::OpImageSparseGather &&
+        opcode != spv::Op::OpImageSparseDrefGather) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Image Operand ConstOffsets can only be used with "
                 "OpImageGather and OpImageDrefGather";
     }
 
-    if (info.dim == SpvDimCube) {
+    if (info.dim == spv::Dim::Cube) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Image Operand ConstOffsets cannot be used with Cube Image "
                 "'Dim'";
@@ -484,7 +489,7 @@
     const Instruction* type_inst = _.FindDef(type_id);
     assert(type_inst);
 
-    if (type_inst->opcode() != SpvOpTypeArray) {
+    if (type_inst->opcode() != spv::Op::OpTypeArray) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Expected Image Operand ConstOffsets to be an array of size 4";
     }
@@ -513,10 +518,11 @@
     }
   }
 
-  if (mask & SpvImageOperandsSampleMask) {
-    if (opcode != SpvOpImageFetch && opcode != SpvOpImageRead &&
-        opcode != SpvOpImageWrite && opcode != SpvOpImageSparseFetch &&
-        opcode != SpvOpImageSparseRead) {
+  if (mask & uint32_t(spv::ImageOperandsMask::Sample)) {
+    if (opcode != spv::Op::OpImageFetch && opcode != spv::Op::OpImageRead &&
+        opcode != spv::Op::OpImageWrite &&
+        opcode != spv::Op::OpImageSparseFetch &&
+        opcode != spv::Op::OpImageSparseRead) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Image Operand Sample can only be used with OpImageFetch, "
              << "OpImageRead, OpImageWrite, OpImageSparseFetch and "
@@ -535,8 +541,8 @@
     }
   }
 
-  if (mask & SpvImageOperandsMinLodMask) {
-    if (!is_implicit_lod && !(mask & SpvImageOperandsGradMask)) {
+  if (mask & uint32_t(spv::ImageOperandsMask::MinLod)) {
+    if (!is_implicit_lod && !(mask & uint32_t(spv::ImageOperandsMask::Grad))) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Image Operand MinLod can only be used with ImplicitLod "
              << "opcodes or together with Image Operand Grad";
@@ -548,8 +554,8 @@
              << "Expected Image Operand MinLod to be float scalar";
     }
 
-    if (info.dim != SpvDim1D && info.dim != SpvDim2D && info.dim != SpvDim3D &&
-        info.dim != SpvDimCube) {
+    if (info.dim != spv::Dim::Dim1D && info.dim != spv::Dim::Dim2D &&
+        info.dim != spv::Dim::Dim3D && info.dim != spv::Dim::Cube) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Image Operand MinLod requires 'Dim' parameter to be 1D, 2D, "
                 "3D or Cube";
@@ -561,16 +567,16 @@
     }
   }
 
-  if (mask & SpvImageOperandsMakeTexelAvailableKHRMask) {
+  if (mask & uint32_t(spv::ImageOperandsMask::MakeTexelAvailableKHR)) {
     // Checked elsewhere: capability and memory model are correct.
-    if (opcode != SpvOpImageWrite) {
+    if (opcode != spv::Op::OpImageWrite) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Image Operand MakeTexelAvailableKHR can only be used with Op"
-             << spvOpcodeString(SpvOpImageWrite) << ": Op"
+             << spvOpcodeString(spv::Op::OpImageWrite) << ": Op"
              << spvOpcodeString(opcode);
     }
 
-    if (!(mask & SpvImageOperandsNonPrivateTexelKHRMask)) {
+    if (!(mask & uint32_t(spv::ImageOperandsMask::NonPrivateTexelKHR))) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Image Operand MakeTexelAvailableKHR requires "
                 "NonPrivateTexelKHR is also specified: Op"
@@ -582,17 +588,18 @@
       return error;
   }
 
-  if (mask & SpvImageOperandsMakeTexelVisibleKHRMask) {
+  if (mask & uint32_t(spv::ImageOperandsMask::MakeTexelVisibleKHR)) {
     // Checked elsewhere: capability and memory model are correct.
-    if (opcode != SpvOpImageRead && opcode != SpvOpImageSparseRead) {
+    if (opcode != spv::Op::OpImageRead &&
+        opcode != spv::Op::OpImageSparseRead) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Image Operand MakeTexelVisibleKHR can only be used with Op"
-             << spvOpcodeString(SpvOpImageRead) << " or Op"
-             << spvOpcodeString(SpvOpImageSparseRead) << ": Op"
+             << spvOpcodeString(spv::Op::OpImageRead) << " or Op"
+             << spvOpcodeString(spv::Op::OpImageSparseRead) << ": Op"
              << spvOpcodeString(opcode);
     }
 
-    if (!(mask & SpvImageOperandsNonPrivateTexelKHRMask)) {
+    if (!(mask & uint32_t(spv::ImageOperandsMask::NonPrivateTexelKHR))) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Image Operand MakeTexelVisibleKHR requires NonPrivateTexelKHR "
                 "is also specified: Op"
@@ -603,7 +610,7 @@
     if (auto error = ValidateMemoryScope(_, inst, visible_scope)) return error;
   }
 
-  if (mask & SpvImageOperandsSignExtendMask) {
+  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.
@@ -616,7 +623,7 @@
     // setup.
   }
 
-  if (mask & SpvImageOperandsZeroExtendMask) {
+  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.
@@ -629,11 +636,11 @@
     // setup.
   }
 
-  if (mask & SpvImageOperandsOffsetsMask) {
+  if (mask & uint32_t(spv::ImageOperandsMask::Offsets)) {
     // TODO: add validation
   }
 
-  if (mask & SpvImageOperandsNontemporalMask) {
+  if (mask & uint32_t(spv::ImageOperandsMask::Nontemporal)) {
     // Checked elsewhere: SPIR-V 1.6 version or later.
   }
 
@@ -643,8 +650,8 @@
 // Validate OpImage*Proj* instructions
 spv_result_t ValidateImageProj(ValidationState_t& _, const Instruction* inst,
                                const ImageTypeInfo& info) {
-  if (info.dim != SpvDim1D && info.dim != SpvDim2D && info.dim != SpvDim3D &&
-      info.dim != SpvDimRect) {
+  if (info.dim != spv::Dim::Dim1D && info.dim != spv::Dim::Dim2D &&
+      info.dim != spv::Dim::Dim3D && info.dim != spv::Dim::Rect) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "Expected Image 'Dim' parameter to be 1D, 2D, 3D or Rect";
   }
@@ -667,33 +674,30 @@
                                     const Instruction* inst,
                                     const ImageTypeInfo& info) {
   if (info.sampled == 2) {
-    if (info.dim == SpvDim1D && !_.HasCapability(SpvCapabilityImage1D)) {
+    if (info.dim == spv::Dim::Dim1D &&
+        !_.HasCapability(spv::Capability::Image1D)) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Capability Image1D is required to access storage image";
-    } else if (info.dim == SpvDimRect &&
-               !_.HasCapability(SpvCapabilityImageRect)) {
+    } else if (info.dim == spv::Dim::Rect &&
+               !_.HasCapability(spv::Capability::ImageRect)) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Capability ImageRect is required to access storage image";
-    } else if (info.dim == SpvDimBuffer &&
-               !_.HasCapability(SpvCapabilityImageBuffer)) {
+    } else if (info.dim == spv::Dim::Buffer &&
+               !_.HasCapability(spv::Capability::ImageBuffer)) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Capability ImageBuffer is required to access storage image";
-    } else if (info.dim == SpvDimCube && info.arrayed == 1 &&
-               !_.HasCapability(SpvCapabilityImageCubeArray)) {
+    } else if (info.dim == spv::Dim::Cube && info.arrayed == 1 &&
+               !_.HasCapability(spv::Capability::ImageCubeArray)) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Capability ImageCubeArray is required to access "
              << "storage image";
     }
 
-    if (info.multisampled == 1 && !_.HasCapability(SpvCapabilityImageMSArray)) {
-#if 0
-      // TODO(atgoo@github.com) The description of this rule in the spec
-      // is unclear and Glslang doesn't declare ImageMSArray. Need to clarify
-      // and reenable.
+    if (info.multisampled == 1 && info.arrayed == 1 && info.sampled == 2 &&
+        !_.HasCapability(spv::Capability::ImageMSArray)) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
-          << "Capability ImageMSArray is required to access storage "
-          << "image";
-#endif
+             << "Capability ImageMSArray is required to access storage "
+             << "image";
     }
   } else if (info.sampled != 0) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
@@ -704,21 +708,21 @@
 }
 
 // Returns true if opcode is *ImageSparse*, false otherwise.
-bool IsSparse(SpvOp opcode) {
+bool IsSparse(spv::Op opcode) {
   switch (opcode) {
-    case SpvOpImageSparseSampleImplicitLod:
-    case SpvOpImageSparseSampleExplicitLod:
-    case SpvOpImageSparseSampleDrefImplicitLod:
-    case SpvOpImageSparseSampleDrefExplicitLod:
-    case SpvOpImageSparseSampleProjImplicitLod:
-    case SpvOpImageSparseSampleProjExplicitLod:
-    case SpvOpImageSparseSampleProjDrefImplicitLod:
-    case SpvOpImageSparseSampleProjDrefExplicitLod:
-    case SpvOpImageSparseFetch:
-    case SpvOpImageSparseGather:
-    case SpvOpImageSparseDrefGather:
-    case SpvOpImageSparseTexelsResident:
-    case SpvOpImageSparseRead: {
+    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;
     }
 
@@ -733,13 +737,13 @@
 // Not valid for sparse image opcodes which do not return a struct.
 spv_result_t GetActualResultType(ValidationState_t& _, const Instruction* inst,
                                  uint32_t* actual_result_type) {
-  const SpvOp opcode = inst->opcode();
+  const spv::Op opcode = inst->opcode();
 
   if (IsSparse(opcode)) {
     const Instruction* const type_inst = _.FindDef(inst->type_id());
     assert(type_inst);
 
-    if (!type_inst || type_inst->opcode() != SpvOpTypeStruct) {
+    if (!type_inst || type_inst->opcode() != spv::Op::OpTypeStruct) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Expected Result Type to be OpTypeStruct";
     }
@@ -761,7 +765,7 @@
 
 // Returns a string describing actual result type of an opcode.
 // Not valid for sparse image opcodes which do not return a struct.
-const char* GetActualResultTypeStr(SpvOp opcode) {
+const char* GetActualResultTypeStr(spv::Op opcode) {
   if (IsSparse(opcode)) return "Result Type's second member";
   return "Result Type";
 }
@@ -777,7 +781,7 @@
 
   if (_.IsIntScalarType(info.sampled_type) &&
       (64 == _.GetBitWidth(info.sampled_type)) &&
-      !_.HasCapability(SpvCapabilityInt64ImageEXT)) {
+      !_.HasCapability(spv::Capability::Int64ImageEXT)) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "Capability Int64ImageEXT is required when using Sampled Type of "
               "64-bit int";
@@ -802,10 +806,10 @@
              << "Sampled Type must be OpTypeVoid in the OpenCL environment.";
     }
   } else {
-    const SpvOp sampled_type_opcode = _.GetIdOpcode(info.sampled_type);
-    if (sampled_type_opcode != SpvOpTypeVoid &&
-        sampled_type_opcode != SpvOpTypeInt &&
-        sampled_type_opcode != SpvOpTypeFloat) {
+    const spv::Op sampled_type_opcode = _.GetIdOpcode(info.sampled_type);
+    if (sampled_type_opcode != spv::Op::OpTypeVoid &&
+        sampled_type_opcode != spv::Op::OpTypeInt &&
+        sampled_type_opcode != spv::Op::OpTypeFloat) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Expected Sampled Type to be either void or"
              << " numerical scalar type";
@@ -835,19 +839,41 @@
            << "Invalid Sampled " << info.sampled << " (must be 0, 1 or 2)";
   }
 
-  if (info.dim == SpvDimSubpassData) {
+  if (info.dim == spv::Dim::SubpassData) {
     if (info.sampled != 2) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << _.VkErrorID(6214) << "Dim SubpassData requires Sampled to be 2";
     }
 
-    if (info.format != SpvImageFormatUnknown) {
+    if (info.format != spv::ImageFormat::Unknown) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Dim SubpassData requires format Unknown";
     }
+  } else if (info.dim == spv::Dim::TileImageDataEXT) {
+    if (_.IsVoidType(info.sampled_type)) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Dim TileImageDataEXT requires Sampled Type to be not "
+                "OpTypeVoid";
+    }
+    if (info.sampled != 2) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Dim TileImageDataEXT requires Sampled to be 2";
+    }
+    if (info.format != spv::ImageFormat::Unknown) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Dim TileImageDataEXT requires format Unknown";
+    }
+    if (info.depth != 0) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Dim TileImageDataEXT requires Depth to be 0";
+    }
+    if (info.arrayed != 0) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Dim TileImageDataEXT requires Arrayed to be 0";
+    }
   } else {
     if (info.multisampled && (info.sampled == 2) &&
-        !_.HasCapability(SpvCapabilityStorageImageMultisample)) {
+        !_.HasCapability(spv::Capability::StorageImageMultisample)) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Capability StorageImageMultisample is required when using "
                 "multisampled storage image";
@@ -855,8 +881,8 @@
   }
 
   if (spvIsOpenCLEnv(target_env)) {
-    if ((info.arrayed == 1) && (info.dim != SpvDim1D) &&
-        (info.dim != SpvDim2D)) {
+    if ((info.arrayed == 1) && (info.dim != spv::Dim::Dim1D) &&
+        (info.dim != spv::Dim::Dim2D)) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "In the OpenCL environment, Arrayed may only be set to 1 "
              << "when Dim is either 1D or 2D.";
@@ -872,7 +898,7 @@
              << "Sampled must be 0 in the OpenCL environment.";
     }
 
-    if (info.access_qualifier == SpvAccessQualifierMax) {
+    if (info.access_qualifier == spv::AccessQualifier::Max) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "In the OpenCL environment, the optional Access Qualifier"
              << " must be present.";
@@ -886,7 +912,7 @@
              << "Sampled must be 1 or 2 in the Vulkan environment.";
     }
 
-    if (info.dim == SpvDimSubpassData && info.arrayed != 0) {
+    if (info.dim == spv::Dim::SubpassData && info.arrayed != 0) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << _.VkErrorID(6214) << "Dim SubpassData requires Arrayed to be 0";
     }
@@ -898,7 +924,7 @@
 spv_result_t ValidateTypeSampledImage(ValidationState_t& _,
                                       const Instruction* inst) {
   const uint32_t image_type = inst->word(2);
-  if (_.GetIdOpcode(image_type) != SpvOpTypeImage) {
+  if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "Expected Image to be of type OpTypeImage";
   }
@@ -910,6 +936,8 @@
   }
   // OpenCL requires Sampled=0, checked elsewhere.
   // Vulkan uses the Sampled=1 case.
+  // If Dim is TileImageDataEXT, Sampled must be 2 and this is validated
+  // elsewhere.
   if ((info.sampled != 0) && (info.sampled != 1)) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << _.VkErrorID(4657)
@@ -918,7 +946,8 @@
   }
 
   // This covers both OpTypeSampledImage and OpSampledImage.
-  if (_.version() >= SPV_SPIRV_VERSION_WORD(1, 6) && info.dim == SpvDimBuffer) {
+  if (_.version() >= SPV_SPIRV_VERSION_WORD(1, 6) &&
+      info.dim == spv::Dim::Buffer) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "In SPIR-V 1.6 or later, sampled image dimension must not be "
               "Buffer";
@@ -927,31 +956,35 @@
   return SPV_SUCCESS;
 }
 
-bool IsAllowedSampledImageOperand(SpvOp opcode, ValidationState_t& _) {
+bool IsAllowedSampledImageOperand(spv::Op opcode, ValidationState_t& _) {
   switch (opcode) {
-    case SpvOpSampledImage:
-    case SpvOpImageSampleImplicitLod:
-    case SpvOpImageSampleExplicitLod:
-    case SpvOpImageSampleDrefImplicitLod:
-    case SpvOpImageSampleDrefExplicitLod:
-    case SpvOpImageSampleProjImplicitLod:
-    case SpvOpImageSampleProjExplicitLod:
-    case SpvOpImageSampleProjDrefImplicitLod:
-    case SpvOpImageSampleProjDrefExplicitLod:
-    case SpvOpImageGather:
-    case SpvOpImageDrefGather:
-    case SpvOpImage:
-    case SpvOpImageQueryLod:
-    case SpvOpImageSparseSampleImplicitLod:
-    case SpvOpImageSparseSampleExplicitLod:
-    case SpvOpImageSparseSampleDrefImplicitLod:
-    case SpvOpImageSparseSampleDrefExplicitLod:
-    case SpvOpImageSparseGather:
-    case SpvOpImageSparseDrefGather:
-    case SpvOpCopyObject:
+    case spv::Op::OpSampledImage:
+    case spv::Op::OpImageSampleImplicitLod:
+    case spv::Op::OpImageSampleExplicitLod:
+    case spv::Op::OpImageSampleDrefImplicitLod:
+    case spv::Op::OpImageSampleDrefExplicitLod:
+    case spv::Op::OpImageSampleProjImplicitLod:
+    case spv::Op::OpImageSampleProjExplicitLod:
+    case spv::Op::OpImageSampleProjDrefImplicitLod:
+    case spv::Op::OpImageSampleProjDrefExplicitLod:
+    case spv::Op::OpImageGather:
+    case spv::Op::OpImageDrefGather:
+    case spv::Op::OpImage:
+    case spv::Op::OpImageQueryLod:
+    case spv::Op::OpImageSparseSampleImplicitLod:
+    case spv::Op::OpImageSparseSampleExplicitLod:
+    case spv::Op::OpImageSparseSampleDrefImplicitLod:
+    case spv::Op::OpImageSparseSampleDrefExplicitLod:
+    case spv::Op::OpImageSparseGather:
+    case spv::Op::OpImageSparseDrefGather:
+    case spv::Op::OpCopyObject:
+    case spv::Op::OpImageSampleWeightedQCOM:
+    case spv::Op::OpImageBoxFilterQCOM:
+    case spv::Op::OpImageBlockMatchSSDQCOM:
+    case spv::Op::OpImageBlockMatchSADQCOM:
       return true;
-    case SpvOpStore:
-      if (_.HasCapability(SpvCapabilityBindlessTextureNV)) return true;
+    case spv::Op::OpStore:
+      if (_.HasCapability(spv::Capability::BindlessTextureNV)) return true;
       return false;
     default:
       return false;
@@ -960,13 +993,13 @@
 
 spv_result_t ValidateSampledImage(ValidationState_t& _,
                                   const Instruction* inst) {
-  if (_.GetIdOpcode(inst->type_id()) != SpvOpTypeSampledImage) {
+  if (_.GetIdOpcode(inst->type_id()) != spv::Op::OpTypeSampledImage) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "Expected Result Type to be OpTypeSampledImage.";
   }
 
   const uint32_t image_type = _.GetOperandTypeId(inst, 2);
-  if (_.GetIdOpcode(image_type) != SpvOpTypeImage) {
+  if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "Expected Image to be of type OpTypeImage.";
   }
@@ -994,12 +1027,12 @@
     }
   }
 
-  if (info.dim == SpvDimSubpassData) {
+  if (info.dim == spv::Dim::SubpassData) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "Expected Image 'Dim' parameter to be not SubpassData.";
   }
 
-  if (_.GetIdOpcode(_.GetOperandTypeId(inst, 3)) != SpvOpTypeSampler) {
+  if (_.GetIdOpcode(_.GetOperandTypeId(inst, 3)) != spv::Op::OpTypeSampler) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "Expected Sampler to be of type OpTypeSampler";
   }
@@ -1027,12 +1060,13 @@
                << _.getIdName(consumer_instr->id()) << ".";
       }
 
-      if (consumer_opcode == SpvOpPhi || consumer_opcode == SpvOpSelect) {
+      if (consumer_opcode == spv::Op::OpPhi ||
+          consumer_opcode == spv::Op::OpSelect) {
         return _.diag(SPV_ERROR_INVALID_ID, inst)
                << "Result <id> from OpSampledImage instruction must not appear "
                   "as "
                   "operands of Op"
-               << spvOpcodeString(static_cast<SpvOp>(consumer_opcode)) << "."
+               << spvOpcodeString(static_cast<spv::Op>(consumer_opcode)) << "."
                << " Found result <id> " << _.getIdName(inst->id())
                << " as an operand of <id> " << _.getIdName(consumer_instr->id())
                << ".";
@@ -1042,7 +1076,7 @@
         return _.diag(SPV_ERROR_INVALID_ID, inst)
                << "Result <id> from OpSampledImage instruction must not appear "
                   "as operand for Op"
-               << spvOpcodeString(static_cast<SpvOp>(consumer_opcode))
+               << spvOpcodeString(static_cast<spv::Op>(consumer_opcode))
                << ", since it is not specified as taking an "
                << "OpTypeSampledImage."
                << " Found result <id> " << _.getIdName(inst->id())
@@ -1051,19 +1085,31 @@
       }
     }
   }
+
+  const Instruction* ld_inst;
+  {
+    int t_idx = inst->GetOperandAs<int>(2);
+    ld_inst = _.FindDef(t_idx);
+  }
+
+  if (ld_inst->opcode() == spv::Op::OpLoad) {
+    int texture_id = ld_inst->GetOperandAs<int>(2);  // variable to load
+    _.RegisterQCOMImageProcessingTextureConsumer(texture_id, ld_inst, inst);
+  }
+
   return SPV_SUCCESS;
 }
 
 spv_result_t ValidateImageTexelPointer(ValidationState_t& _,
                                        const Instruction* inst) {
   const auto result_type = _.FindDef(inst->type_id());
-  if (result_type->opcode() != SpvOpTypePointer) {
+  if (result_type->opcode() != spv::Op::OpTypePointer) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "Expected Result Type to be OpTypePointer";
   }
 
-  const auto storage_class = result_type->GetOperandAs<uint32_t>(1);
-  if (storage_class != SpvStorageClassImage) {
+  const auto storage_class = result_type->GetOperandAs<spv::StorageClass>(1);
+  if (storage_class != spv::StorageClass::Image) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "Expected Result Type to be OpTypePointer whose Storage Class "
               "operand is Image";
@@ -1071,21 +1117,21 @@
 
   const auto ptr_type = result_type->GetOperandAs<uint32_t>(2);
   const auto ptr_opcode = _.GetIdOpcode(ptr_type);
-  if (ptr_opcode != SpvOpTypeInt && ptr_opcode != SpvOpTypeFloat &&
-      ptr_opcode != SpvOpTypeVoid) {
+  if (ptr_opcode != spv::Op::OpTypeInt && ptr_opcode != spv::Op::OpTypeFloat &&
+      ptr_opcode != spv::Op::OpTypeVoid) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "Expected Result Type to be OpTypePointer whose Type operand "
               "must be a scalar numerical type or OpTypeVoid";
   }
 
   const auto image_ptr = _.FindDef(_.GetOperandTypeId(inst, 2));
-  if (!image_ptr || image_ptr->opcode() != SpvOpTypePointer) {
+  if (!image_ptr || image_ptr->opcode() != spv::Op::OpTypePointer) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "Expected Image to be OpTypePointer";
   }
 
   const auto image_type = image_ptr->GetOperandAs<uint32_t>(2);
-  if (_.GetIdOpcode(image_type) != SpvOpTypeImage) {
+  if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "Expected Image to be OpTypePointer with Type OpTypeImage";
   }
@@ -1102,11 +1148,17 @@
               "pointed to by Result Type";
   }
 
-  if (info.dim == SpvDimSubpassData) {
+  if (info.dim == spv::Dim::SubpassData) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "Image Dim SubpassData cannot be used with OpImageTexelPointer";
   }
 
+  if (info.dim == spv::Dim::TileImageDataEXT) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Image Dim TileImageDataEXT cannot be used with "
+              "OpImageTexelPointer";
+  }
+
   const uint32_t coord_type = _.GetOperandTypeId(inst, 3);
   if (!coord_type || !_.IsIntScalarOrVectorType(coord_type)) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
@@ -1118,11 +1170,11 @@
     expected_coord_size = GetPlaneCoordSize(info);
   } else if (info.arrayed == 1) {
     switch (info.dim) {
-      case SpvDim1D:
+      case spv::Dim::Dim1D:
         expected_coord_size = 2;
         break;
-      case SpvDimCube:
-      case SpvDim2D:
+      case spv::Dim::Cube:
+      case spv::Dim::Dim2D:
         expected_coord_size = 3;
         break;
       default:
@@ -1157,11 +1209,11 @@
   }
 
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    if ((info.format != SpvImageFormatR64i) &&
-        (info.format != SpvImageFormatR64ui) &&
-        (info.format != SpvImageFormatR32f) &&
-        (info.format != SpvImageFormatR32i) &&
-        (info.format != SpvImageFormatR32ui)) {
+    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)) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << _.VkErrorID(4658)
              << "Expected the Image Format in Image to be R64i, R64ui, R32f, "
@@ -1173,7 +1225,7 @@
 }
 
 spv_result_t ValidateImageLod(ValidationState_t& _, const Instruction* inst) {
-  const SpvOp opcode = inst->opcode();
+  const spv::Op opcode = inst->opcode();
   uint32_t actual_result_type = 0;
   if (spv_result_t error = GetActualResultType(_, inst, &actual_result_type)) {
     return error;
@@ -1193,7 +1245,7 @@
   }
 
   const uint32_t image_type = _.GetOperandTypeId(inst, 2);
-  if (_.GetIdOpcode(image_type) != SpvOpTypeSampledImage) {
+  if (_.GetIdOpcode(image_type) != spv::Op::OpTypeSampledImage) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "Expected Sampled Image to be of type OpTypeSampledImage";
   }
@@ -1216,7 +1268,7 @@
            << "Sampling operation is invalid for multisample image";
   }
 
-  if (_.GetIdOpcode(info.sampled_type) != SpvOpTypeVoid) {
+  if (_.GetIdOpcode(info.sampled_type) != spv::Op::OpTypeVoid) {
     const uint32_t texel_component_type =
         _.GetComponentType(actual_result_type);
     if (texel_component_type != info.sampled_type) {
@@ -1227,9 +1279,9 @@
   }
 
   const uint32_t coord_type = _.GetOperandTypeId(inst, 3);
-  if ((opcode == SpvOpImageSampleExplicitLod ||
-       opcode == SpvOpImageSparseSampleExplicitLod) &&
-      _.HasCapability(SpvCapabilityKernel)) {
+  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)
@@ -1252,9 +1304,9 @@
 
   const uint32_t mask = inst->words().size() <= 5 ? 0 : inst->word(5);
 
-  if (mask & SpvImageOperandsConstOffsetMask) {
+  if (mask & uint32_t(spv::ImageOperandsMask::ConstOffset)) {
     if (spvIsOpenCLEnv(_.context()->target_env)) {
-      if (opcode == SpvOpImageSampleExplicitLod) {
+      if (opcode == spv::Op::OpImageSampleExplicitLod) {
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
                << "ConstOffset image operand not allowed "
                << "in the OpenCL environment.";
@@ -1279,7 +1331,7 @@
   }
 
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    if (info.dim == SpvDim3D) {
+    if (info.dim == spv::Dim::Dim3D) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << _.VkErrorID(4777)
              << "In Vulkan, OpImage*Dref* instructions must not use images "
@@ -1292,7 +1344,7 @@
 
 spv_result_t ValidateImageDrefLod(ValidationState_t& _,
                                   const Instruction* inst) {
-  const SpvOp opcode = inst->opcode();
+  const spv::Op opcode = inst->opcode();
   uint32_t actual_result_type = 0;
   if (spv_result_t error = GetActualResultType(_, inst, &actual_result_type)) {
     return error;
@@ -1306,7 +1358,7 @@
   }
 
   const uint32_t image_type = _.GetOperandTypeId(inst, 2);
-  if (_.GetIdOpcode(image_type) != SpvOpTypeSampledImage) {
+  if (_.GetIdOpcode(image_type) != spv::Op::OpTypeSampledImage) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "Expected Sampled Image to be of type OpTypeSampledImage";
   }
@@ -1364,7 +1416,7 @@
     return error;
   }
 
-  const SpvOp opcode = inst->opcode();
+  const spv::Op opcode = inst->opcode();
   if (!_.IsIntVectorType(actual_result_type) &&
       !_.IsFloatVectorType(actual_result_type)) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
@@ -1379,7 +1431,7 @@
   }
 
   const uint32_t image_type = _.GetOperandTypeId(inst, 2);
-  if (_.GetIdOpcode(image_type) != SpvOpTypeImage) {
+  if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "Expected Image to be of type OpTypeImage";
   }
@@ -1390,7 +1442,7 @@
            << "Corrupt image type definition";
   }
 
-  if (_.GetIdOpcode(info.sampled_type) != SpvOpTypeVoid) {
+  if (_.GetIdOpcode(info.sampled_type) != spv::Op::OpTypeVoid) {
     const uint32_t result_component_type =
         _.GetComponentType(actual_result_type);
     if (result_component_type != info.sampled_type) {
@@ -1400,7 +1452,7 @@
     }
   }
 
-  if (info.dim == SpvDimCube) {
+  if (info.dim == spv::Dim::Cube) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Image 'Dim' cannot be Cube";
   }
 
@@ -1436,7 +1488,7 @@
   if (spv_result_t error = GetActualResultType(_, inst, &actual_result_type))
     return error;
 
-  const SpvOp opcode = inst->opcode();
+  const spv::Op opcode = inst->opcode();
   if (!_.IsIntVectorType(actual_result_type) &&
       !_.IsFloatVectorType(actual_result_type)) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
@@ -1451,7 +1503,7 @@
   }
 
   const uint32_t image_type = _.GetOperandTypeId(inst, 2);
-  if (_.GetIdOpcode(image_type) != SpvOpTypeSampledImage) {
+  if (_.GetIdOpcode(image_type) != spv::Op::OpTypeSampledImage) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "Expected Sampled Image to be of type OpTypeSampledImage";
   }
@@ -1470,8 +1522,9 @@
            << "Gather operation is invalid for multisample image";
   }
 
-  if (opcode == SpvOpImageDrefGather || opcode == SpvOpImageSparseDrefGather ||
-      _.GetIdOpcode(info.sampled_type) != SpvOpTypeVoid) {
+  if (opcode == spv::Op::OpImageDrefGather ||
+      opcode == spv::Op::OpImageSparseDrefGather ||
+      _.GetIdOpcode(info.sampled_type) != spv::Op::OpTypeVoid) {
     const uint32_t result_component_type =
         _.GetComponentType(actual_result_type);
     if (result_component_type != info.sampled_type) {
@@ -1481,8 +1534,8 @@
     }
   }
 
-  if (info.dim != SpvDim2D && info.dim != SpvDimCube &&
-      info.dim != SpvDimRect) {
+  if (info.dim != spv::Dim::Dim2D && info.dim != spv::Dim::Cube &&
+      info.dim != spv::Dim::Rect) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << _.VkErrorID(4777)
            << "Expected Image 'Dim' to be 2D, Cube, or Rect";
@@ -1502,7 +1555,8 @@
            << " components, but given only " << actual_coord_size;
   }
 
-  if (opcode == SpvOpImageGather || opcode == SpvOpImageSparseGather) {
+  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) ||
@@ -1519,8 +1573,8 @@
       }
     }
   } else {
-    assert(opcode == SpvOpImageDrefGather ||
-           opcode == SpvOpImageSparseDrefGather);
+    assert(opcode == spv::Op::OpImageDrefGather ||
+           opcode == spv::Op::OpImageSparseDrefGather);
     if (spv_result_t result = ValidateImageDref(_, inst, info)) return result;
   }
 
@@ -1532,7 +1586,7 @@
 }
 
 spv_result_t ValidateImageRead(ValidationState_t& _, const Instruction* inst) {
-  const SpvOp opcode = inst->opcode();
+  const spv::Op opcode = inst->opcode();
   uint32_t actual_result_type = 0;
   if (spv_result_t error = GetActualResultType(_, inst, &actual_result_type)) {
     return error;
@@ -1557,7 +1611,7 @@
   }  // Check OpenCL below, after we get the image info.
 
   const uint32_t image_type = _.GetOperandTypeId(inst, 2);
-  if (_.GetIdOpcode(image_type) != SpvOpTypeImage) {
+  if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "Expected Image to be of type OpTypeImage";
   }
@@ -1591,27 +1645,33 @@
     }
 
     const uint32_t mask = inst->words().size() <= 5 ? 0 : inst->word(5);
-    if (mask & SpvImageOperandsConstOffsetMask) {
+    if (mask & uint32_t(spv::ImageOperandsMask::ConstOffset)) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "ConstOffset image operand not allowed "
              << "in the OpenCL environment.";
     }
   }
 
-  if (info.dim == SpvDimSubpassData) {
-    if (opcode == SpvOpImageSparseRead) {
+  if (info.dim == spv::Dim::SubpassData) {
+    if (opcode == spv::Op::OpImageSparseRead) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Image Dim SubpassData cannot be used with ImageSparseRead";
     }
 
     _.function(inst->function()->id())
         ->RegisterExecutionModelLimitation(
-            SpvExecutionModelFragment,
+            spv::ExecutionModel::Fragment,
             std::string("Dim SubpassData requires Fragment execution model: ") +
                 spvOpcodeString(opcode));
   }
 
-  if (_.GetIdOpcode(info.sampled_type) != SpvOpTypeVoid) {
+  if (info.dim == spv::Dim::TileImageDataEXT) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Image Dim TileImageDataEXT cannot be used with "
+           << spvOpcodeString(opcode);
+  }
+
+  if (_.GetIdOpcode(info.sampled_type) != spv::Op::OpTypeVoid) {
     const uint32_t result_component_type =
         _.GetComponentType(actual_result_type);
     if (result_component_type != info.sampled_type) {
@@ -1639,8 +1699,9 @@
   }
 
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    if (info.format == SpvImageFormatUnknown && info.dim != SpvDimSubpassData &&
-        !_.HasCapability(SpvCapabilityStorageImageReadWithoutFormat)) {
+    if (info.format == spv::ImageFormat::Unknown &&
+        info.dim != spv::Dim::SubpassData &&
+        !_.HasCapability(spv::Capability::StorageImageReadWithoutFormat)) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Capability StorageImageReadWithoutFormat is required to "
              << "read storage image";
@@ -1656,7 +1717,7 @@
 
 spv_result_t ValidateImageWrite(ValidationState_t& _, const Instruction* inst) {
   const uint32_t image_type = _.GetOperandTypeId(inst, 0);
-  if (_.GetIdOpcode(image_type) != SpvOpTypeImage) {
+  if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "Expected Image to be of type OpTypeImage";
   }
@@ -1667,11 +1728,16 @@
            << "Corrupt image type definition";
   }
 
-  if (info.dim == SpvDimSubpassData) {
+  if (info.dim == spv::Dim::SubpassData) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "Image 'Dim' cannot be SubpassData";
   }
 
+  if (info.dim == spv::Dim::TileImageDataEXT) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Image 'Dim' cannot be TileImageDataEXT";
+  }
+
   if (spv_result_t result = ValidateImageReadWrite(_, inst, info))
     return result;
 
@@ -1697,7 +1763,7 @@
            << "Expected Texel to be int or float vector or scalar";
   }
 
-  if (_.GetIdOpcode(info.sampled_type) != SpvOpTypeVoid) {
+  if (_.GetIdOpcode(info.sampled_type) != spv::Op::OpTypeVoid) {
     const uint32_t texel_component_type = _.GetComponentType(texel_type);
     if (texel_component_type != info.sampled_type) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
@@ -1707,8 +1773,9 @@
   }
 
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    if (info.format == SpvImageFormatUnknown && info.dim != SpvDimSubpassData &&
-        !_.HasCapability(SpvCapabilityStorageImageWriteWithoutFormat)) {
+    if (info.format == spv::ImageFormat::Unknown &&
+        info.dim != spv::Dim::SubpassData &&
+        !_.HasCapability(spv::Capability::StorageImageWriteWithoutFormat)) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Capability StorageImageWriteWithoutFormat is required to "
                 "write "
@@ -1733,7 +1800,7 @@
 
 spv_result_t ValidateImage(ValidationState_t& _, const Instruction* inst) {
   const uint32_t result_type = inst->type_id();
-  if (_.GetIdOpcode(result_type) != SpvOpTypeImage) {
+  if (_.GetIdOpcode(result_type) != spv::Op::OpTypeImage) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "Expected Result Type to be OpTypeImage";
   }
@@ -1742,7 +1809,7 @@
   const Instruction* sampled_image_type_inst = _.FindDef(sampled_image_type);
   assert(sampled_image_type_inst);
 
-  if (sampled_image_type_inst->opcode() != SpvOpTypeSampledImage) {
+  if (sampled_image_type_inst->opcode() != spv::Op::OpTypeSampledImage) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "Expected Sample Image to be of type OpTypeSampleImage";
   }
@@ -1764,7 +1831,7 @@
   }
 
   const uint32_t image_type = _.GetOperandTypeId(inst, 2);
-  if (_.GetIdOpcode(image_type) != SpvOpTypeImage) {
+  if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "Expected Image to be of type OpTypeImage";
   }
@@ -1777,14 +1844,14 @@
 
   uint32_t expected_num_components = info.arrayed;
   switch (info.dim) {
-    case SpvDim1D:
+    case spv::Dim::Dim1D:
       expected_num_components += 1;
       break;
-    case SpvDim2D:
-    case SpvDimCube:
+    case spv::Dim::Dim2D:
+    case spv::Dim::Cube:
       expected_num_components += 2;
       break;
-    case SpvDim3D:
+    case spv::Dim::Dim3D:
       expected_num_components += 3;
       break;
     default:
@@ -1830,7 +1897,7 @@
   }
 
   const uint32_t image_type = _.GetOperandTypeId(inst, 2);
-  if (_.GetIdOpcode(image_type) != SpvOpTypeImage) {
+  if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "Expected Image to be of type OpTypeImage";
   }
@@ -1843,16 +1910,16 @@
 
   uint32_t expected_num_components = info.arrayed;
   switch (info.dim) {
-    case SpvDim1D:
-    case SpvDimBuffer:
+    case spv::Dim::Dim1D:
+    case spv::Dim::Buffer:
       expected_num_components += 1;
       break;
-    case SpvDim2D:
-    case SpvDimCube:
-    case SpvDimRect:
+    case spv::Dim::Dim2D:
+    case spv::Dim::Cube:
+    case spv::Dim::Rect:
       expected_num_components += 2;
       break;
-    case SpvDim3D:
+    case spv::Dim::Dim3D:
       expected_num_components += 3;
       break;
     default:
@@ -1860,8 +1927,8 @@
              << "Image 'Dim' must be 1D, Buffer, 2D, Cube, 3D or Rect";
   }
 
-  if (info.dim == SpvDim1D || info.dim == SpvDim2D || info.dim == SpvDim3D ||
-      info.dim == SpvDimCube) {
+  if (info.dim == spv::Dim::Dim1D || info.dim == spv::Dim::Dim2D ||
+      info.dim == spv::Dim::Dim3D || info.dim == spv::Dim::Cube) {
     if (info.multisampled != 1 && info.sampled != 0 && info.sampled != 2) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Image must have either 'MS'=1 or 'Sampled'=0 or 'Sampled'=2";
@@ -1885,10 +1952,22 @@
            << "Expected Result Type to be int scalar type";
   }
 
-  if (_.GetIdOpcode(_.GetOperandTypeId(inst, 2)) != SpvOpTypeImage) {
+  const uint32_t image_type = _.GetOperandTypeId(inst, 2);
+  if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "Expected operand to be of type OpTypeImage";
   }
+
+  ImageTypeInfo info;
+  if (!GetImageTypeInfo(_, image_type, &info)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Corrupt image type definition";
+  }
+
+  if (info.dim == spv::Dim::TileImageDataEXT) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Image 'Dim' cannot be TileImageDataEXT";
+  }
   return SPV_SUCCESS;
 }
 
@@ -1896,9 +1975,9 @@
                                    const Instruction* inst) {
   _.function(inst->function()->id())
       ->RegisterExecutionModelLimitation(
-          [&](SpvExecutionModel model, std::string* message) {
-            if (model != SpvExecutionModelFragment &&
-                model != SpvExecutionModelGLCompute) {
+          [&](spv::ExecutionModel model, std::string* message) {
+            if (model != spv::ExecutionModel::Fragment &&
+                model != spv::ExecutionModel::GLCompute) {
               if (message) {
                 *message = std::string(
                     "OpImageQueryLod requires Fragment or GLCompute execution "
@@ -1914,10 +1993,10 @@
                               std::string* message) {
         const auto* models = state.GetExecutionModels(entry_point->id());
         const auto* modes = state.GetExecutionModes(entry_point->id());
-        if (models->find(SpvExecutionModelGLCompute) != models->end() &&
-            modes->find(SpvExecutionModeDerivativeGroupLinearNV) ==
+        if (models->find(spv::ExecutionModel::GLCompute) != models->end() &&
+            modes->find(spv::ExecutionMode::DerivativeGroupLinearNV) ==
                 modes->end() &&
-            modes->find(SpvExecutionModeDerivativeGroupQuadsNV) ==
+            modes->find(spv::ExecutionMode::DerivativeGroupQuadsNV) ==
                 modes->end()) {
           if (message) {
             *message = std::string(
@@ -1942,7 +2021,7 @@
   }
 
   const uint32_t image_type = _.GetOperandTypeId(inst, 2);
-  if (_.GetIdOpcode(image_type) != SpvOpTypeSampledImage) {
+  if (_.GetIdOpcode(image_type) != spv::Op::OpTypeSampledImage) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "Expected Image operand to be of type OpTypeSampledImage";
   }
@@ -1953,14 +2032,14 @@
            << "Corrupt image type definition";
   }
 
-  if (info.dim != SpvDim1D && info.dim != SpvDim2D && info.dim != SpvDim3D &&
-      info.dim != SpvDimCube) {
+  if (info.dim != spv::Dim::Dim1D && info.dim != spv::Dim::Dim2D &&
+      info.dim != spv::Dim::Dim3D && info.dim != spv::Dim::Cube) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "Image 'Dim' must be 1D, 2D, 3D or Cube";
   }
 
   const uint32_t coord_type = _.GetOperandTypeId(inst, 3);
-  if (_.HasCapability(SpvCapabilityKernel)) {
+  if (_.HasCapability(spv::Capability::Kernel)) {
     if (!_.IsFloatScalarOrVectorType(coord_type) &&
         !_.IsIntScalarOrVectorType(coord_type)) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
@@ -1981,11 +2060,11 @@
            << " components, but given only " << actual_coord_size;
   }
 
-  // The operad is a sampled image.
+  // The operand is a sampled image.
   // The sampled image type is already checked to be parameterized by an image
   // type with Sampled=0 or Sampled=1.  Vulkan bans Sampled=0, and so we have
   // Sampled=1.  So the validator already enforces Vulkan VUID 4659:
-  //   OpImageQuerySizeLod must only consume an “Image” operand whose type has
+  //   OpImageQuerySizeLod must only consume an "Image" operand whose type has
   //   its "Sampled" operand set to 1
   return SPV_SUCCESS;
 }
@@ -2005,7 +2084,7 @@
   }
 
   const uint32_t image_type = _.GetOperandTypeId(inst, 2);
-  if (_.GetIdOpcode(image_type) != SpvOpTypeImage) {
+  if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "Expected Image to be of type OpTypeImage";
   }
@@ -2016,10 +2095,10 @@
            << "Corrupt image type definition";
   }
 
-  const SpvOp opcode = inst->opcode();
-  if (opcode == SpvOpImageQueryLevels) {
-    if (info.dim != SpvDim1D && info.dim != SpvDim2D && info.dim != SpvDim3D &&
-        info.dim != SpvDimCube) {
+  const spv::Op opcode = inst->opcode();
+  if (opcode == spv::Op::OpImageQueryLevels) {
+    if (info.dim != spv::Dim::Dim1D && info.dim != spv::Dim::Dim2D &&
+        info.dim != spv::Dim::Dim3D && info.dim != spv::Dim::Cube) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Image 'Dim' must be 1D, 2D, 3D or Cube";
     }
@@ -2033,8 +2112,8 @@
       }
     }
   } else {
-    assert(opcode == SpvOpImageQuerySamples);
-    if (info.dim != SpvDim2D) {
+    assert(opcode == spv::Op::OpImageQuerySamples);
+    if (info.dim != spv::Dim::Dim2D) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Image 'Dim' must be 2D";
     }
 
@@ -2061,17 +2140,67 @@
   return SPV_SUCCESS;
 }
 
+spv_result_t ValidateImageProcessingQCOMDecoration(ValidationState_t& _, int id,
+                                                   spv::Decoration decor) {
+  const Instruction* si_inst = nullptr;
+  const Instruction* ld_inst = _.FindDef(id);
+  if (ld_inst->opcode() == spv::Op::OpSampledImage) {
+    si_inst = ld_inst;
+    int t_idx = si_inst->GetOperandAs<int>(2);  // texture
+    ld_inst = _.FindDef(t_idx);
+  }
+  if (ld_inst->opcode() != spv::Op::OpLoad) {
+    return _.diag(SPV_ERROR_INVALID_DATA, ld_inst) << "Expect to see OpLoad";
+  }
+  int texture_id = ld_inst->GetOperandAs<int>(2);  // variable to load
+  if (!_.HasDecoration(texture_id, decor)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, ld_inst)
+           << "Missing decoration WeightTextureQCOM/BlockMatchTextureQCOM";
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateImageProcessingQCOM(ValidationState_t& _,
+                                         const Instruction* inst) {
+  spv_result_t res = SPV_SUCCESS;
+  const spv::Op opcode = inst->opcode();
+  switch (opcode) {
+    case spv::Op::OpImageSampleWeightedQCOM: {
+      int wi_idx = inst->GetOperandAs<int>(4);  // weight
+      res = ValidateImageProcessingQCOMDecoration(
+          _, wi_idx, spv::Decoration::WeightTextureQCOM);
+      break;
+    }
+    case spv::Op::OpImageBlockMatchSSDQCOM:
+    case spv::Op::OpImageBlockMatchSADQCOM: {
+      int tgt_idx = inst->GetOperandAs<int>(2);  // target
+      res = ValidateImageProcessingQCOMDecoration(
+          _, tgt_idx, spv::Decoration::BlockMatchTextureQCOM);
+      if (res != SPV_SUCCESS) break;
+      int ref_idx = inst->GetOperandAs<int>(4);  // reference
+      res = ValidateImageProcessingQCOMDecoration(
+          _, ref_idx, spv::Decoration::BlockMatchTextureQCOM);
+      break;
+    }
+    default:
+      break;
+  }
+
+  return res;
+}
+
 }  // namespace
 
 // Validates correctness of image instructions.
 spv_result_t ImagePass(ValidationState_t& _, const Instruction* inst) {
-  const SpvOp opcode = inst->opcode();
+  const spv::Op opcode = inst->opcode();
   if (IsImplicitLod(opcode)) {
     _.function(inst->function()->id())
-        ->RegisterExecutionModelLimitation([opcode](SpvExecutionModel model,
+        ->RegisterExecutionModelLimitation([opcode](spv::ExecutionModel model,
                                                     std::string* message) {
-          if (model != SpvExecutionModelFragment &&
-              model != SpvExecutionModelGLCompute) {
+          if (model != spv::ExecutionModel::Fragment &&
+              model != spv::ExecutionModel::GLCompute) {
             if (message) {
               *message =
                   std::string(
@@ -2090,11 +2219,11 @@
           const auto* models = state.GetExecutionModels(entry_point->id());
           const auto* modes = state.GetExecutionModes(entry_point->id());
           if (models &&
-              models->find(SpvExecutionModelGLCompute) != models->end() &&
+              models->find(spv::ExecutionModel::GLCompute) != models->end() &&
               (!modes ||
-               (modes->find(SpvExecutionModeDerivativeGroupLinearNV) ==
+               (modes->find(spv::ExecutionMode::DerivativeGroupLinearNV) ==
                     modes->end() &&
-                modes->find(SpvExecutionModeDerivativeGroupQuadsNV) ==
+                modes->find(spv::ExecutionMode::DerivativeGroupQuadsNV) ==
                     modes->end()))) {
             if (message) {
               *message =
@@ -2111,75 +2240,81 @@
   }
 
   switch (opcode) {
-    case SpvOpTypeImage:
+    case spv::Op::OpTypeImage:
       return ValidateTypeImage(_, inst);
-    case SpvOpTypeSampledImage:
+    case spv::Op::OpTypeSampledImage:
       return ValidateTypeSampledImage(_, inst);
-    case SpvOpSampledImage:
+    case spv::Op::OpSampledImage:
       return ValidateSampledImage(_, inst);
-    case SpvOpImageTexelPointer:
+    case spv::Op::OpImageTexelPointer:
       return ValidateImageTexelPointer(_, inst);
 
-    case SpvOpImageSampleImplicitLod:
-    case SpvOpImageSampleExplicitLod:
-    case SpvOpImageSampleProjImplicitLod:
-    case SpvOpImageSampleProjExplicitLod:
-    case SpvOpImageSparseSampleImplicitLod:
-    case SpvOpImageSparseSampleExplicitLod:
+    case spv::Op::OpImageSampleImplicitLod:
+    case spv::Op::OpImageSampleExplicitLod:
+    case spv::Op::OpImageSampleProjImplicitLod:
+    case spv::Op::OpImageSampleProjExplicitLod:
+    case spv::Op::OpImageSparseSampleImplicitLod:
+    case spv::Op::OpImageSparseSampleExplicitLod:
       return ValidateImageLod(_, inst);
 
-    case SpvOpImageSampleDrefImplicitLod:
-    case SpvOpImageSampleDrefExplicitLod:
-    case SpvOpImageSampleProjDrefImplicitLod:
-    case SpvOpImageSampleProjDrefExplicitLod:
-    case SpvOpImageSparseSampleDrefImplicitLod:
-    case SpvOpImageSparseSampleDrefExplicitLod:
+    case spv::Op::OpImageSampleDrefImplicitLod:
+    case spv::Op::OpImageSampleDrefExplicitLod:
+    case spv::Op::OpImageSampleProjDrefImplicitLod:
+    case spv::Op::OpImageSampleProjDrefExplicitLod:
+    case spv::Op::OpImageSparseSampleDrefImplicitLod:
+    case spv::Op::OpImageSparseSampleDrefExplicitLod:
       return ValidateImageDrefLod(_, inst);
 
-    case SpvOpImageFetch:
-    case SpvOpImageSparseFetch:
+    case spv::Op::OpImageFetch:
+    case spv::Op::OpImageSparseFetch:
       return ValidateImageFetch(_, inst);
 
-    case SpvOpImageGather:
-    case SpvOpImageDrefGather:
-    case SpvOpImageSparseGather:
-    case SpvOpImageSparseDrefGather:
+    case spv::Op::OpImageGather:
+    case spv::Op::OpImageDrefGather:
+    case spv::Op::OpImageSparseGather:
+    case spv::Op::OpImageSparseDrefGather:
       return ValidateImageGather(_, inst);
 
-    case SpvOpImageRead:
-    case SpvOpImageSparseRead:
+    case spv::Op::OpImageRead:
+    case spv::Op::OpImageSparseRead:
       return ValidateImageRead(_, inst);
 
-    case SpvOpImageWrite:
+    case spv::Op::OpImageWrite:
       return ValidateImageWrite(_, inst);
 
-    case SpvOpImage:
+    case spv::Op::OpImage:
       return ValidateImage(_, inst);
 
-    case SpvOpImageQueryFormat:
-    case SpvOpImageQueryOrder:
+    case spv::Op::OpImageQueryFormat:
+    case spv::Op::OpImageQueryOrder:
       return ValidateImageQueryFormatOrOrder(_, inst);
 
-    case SpvOpImageQuerySizeLod:
+    case spv::Op::OpImageQuerySizeLod:
       return ValidateImageQuerySizeLod(_, inst);
-    case SpvOpImageQuerySize:
+    case spv::Op::OpImageQuerySize:
       return ValidateImageQuerySize(_, inst);
-    case SpvOpImageQueryLod:
+    case spv::Op::OpImageQueryLod:
       return ValidateImageQueryLod(_, inst);
 
-    case SpvOpImageQueryLevels:
-    case SpvOpImageQuerySamples:
+    case spv::Op::OpImageQueryLevels:
+    case spv::Op::OpImageQuerySamples:
       return ValidateImageQueryLevelsOrSamples(_, inst);
 
-    case SpvOpImageSparseSampleProjImplicitLod:
-    case SpvOpImageSparseSampleProjExplicitLod:
-    case SpvOpImageSparseSampleProjDrefImplicitLod:
-    case SpvOpImageSparseSampleProjDrefExplicitLod:
+    case spv::Op::OpImageSparseSampleProjImplicitLod:
+    case spv::Op::OpImageSparseSampleProjExplicitLod:
+    case spv::Op::OpImageSparseSampleProjDrefImplicitLod:
+    case spv::Op::OpImageSparseSampleProjDrefExplicitLod:
       return ValidateImageSparseLod(_, inst);
 
-    case SpvOpImageSparseTexelsResident:
+    case spv::Op::OpImageSparseTexelsResident:
       return ValidateImageSparseTexelsResident(_, inst);
 
+    case spv::Op::OpImageSampleWeightedQCOM:
+    case spv::Op::OpImageBoxFilterQCOM:
+    case spv::Op::OpImageBlockMatchSSDQCOM:
+    case spv::Op::OpImageBlockMatchSADQCOM:
+      return ValidateImageProcessingQCOM(_, inst);
+
     default:
       break;
   }
@@ -2187,5 +2322,89 @@
   return SPV_SUCCESS;
 }
 
+bool IsImageInstruction(const spv::Op opcode) {
+  switch (opcode) {
+    case spv::Op::OpImageSampleImplicitLod:
+    case spv::Op::OpImageSampleDrefImplicitLod:
+    case spv::Op::OpImageSampleProjImplicitLod:
+    case spv::Op::OpImageSampleProjDrefImplicitLod:
+    case spv::Op::OpImageSparseSampleImplicitLod:
+    case spv::Op::OpImageSparseSampleDrefImplicitLod:
+    case spv::Op::OpImageSparseSampleProjImplicitLod:
+    case spv::Op::OpImageSparseSampleProjDrefImplicitLod:
+
+    case spv::Op::OpImageSampleExplicitLod:
+    case spv::Op::OpImageSampleDrefExplicitLod:
+    case spv::Op::OpImageSampleProjExplicitLod:
+    case spv::Op::OpImageSampleProjDrefExplicitLod:
+    case spv::Op::OpImageSparseSampleExplicitLod:
+    case spv::Op::OpImageSparseSampleDrefExplicitLod:
+    case spv::Op::OpImageSparseSampleProjExplicitLod:
+    case spv::Op::OpImageSparseSampleProjDrefExplicitLod:
+
+    case spv::Op::OpImage:
+    case spv::Op::OpImageFetch:
+    case spv::Op::OpImageSparseFetch:
+    case spv::Op::OpImageGather:
+    case spv::Op::OpImageDrefGather:
+    case spv::Op::OpImageSparseGather:
+    case spv::Op::OpImageSparseDrefGather:
+    case spv::Op::OpImageRead:
+    case spv::Op::OpImageSparseRead:
+    case spv::Op::OpImageWrite:
+
+    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:
+
+    case spv::Op::OpImageSampleWeightedQCOM:
+    case spv::Op::OpImageBoxFilterQCOM:
+    case spv::Op::OpImageBlockMatchSSDQCOM:
+    case spv::Op::OpImageBlockMatchSADQCOM:
+      return true;
+    default:
+      break;
+  }
+  return false;
+}
+
+spv_result_t ValidateQCOMImageProcessingTextureUsages(ValidationState_t& _,
+                                                      const Instruction* inst) {
+  const spv::Op opcode = inst->opcode();
+  if (!IsImageInstruction(opcode)) return SPV_SUCCESS;
+
+  switch (opcode) {
+    case spv::Op::OpImageSampleWeightedQCOM:
+    case spv::Op::OpImageBoxFilterQCOM:
+    case spv::Op::OpImageBlockMatchSSDQCOM:
+    case spv::Op::OpImageBlockMatchSADQCOM:
+      break;
+    default:
+      for (size_t i = 0; i < inst->operands().size(); ++i) {
+        int id = inst->GetOperandAs<int>(i);
+        const Instruction* operand_inst = _.FindDef(id);
+        if (operand_inst == nullptr) continue;
+        if (operand_inst->opcode() == spv::Op::OpLoad) {
+          if (_.IsQCOMImageProcessingTextureConsumer(id)) {
+            return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                   << "Illegal use of QCOM image processing decorated texture";
+          }
+        }
+        if (operand_inst->opcode() == spv::Op::OpSampledImage) {
+          if (_.IsQCOMImageProcessingTextureConsumer(id)) {
+            return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                   << "Illegal use of QCOM image processing decorated texture";
+          }
+        }
+      }
+      break;
+  }
+  return SPV_SUCCESS;
+}
+
 }  // namespace val
 }  // namespace spvtools
diff --git a/source/val/validate_instruction.cpp b/source/val/validate_instruction.cpp
index 767c0ce..8710ffa 100644
--- a/source/val/validate_instruction.cpp
+++ b/source/val/validate_instruction.cpp
@@ -14,26 +14,20 @@
 
 // Performs validation on instructions that appear inside of a SPIR-V block.
 
-#include <algorithm>
 #include <cassert>
-#include <iomanip>
 #include <sstream>
 #include <string>
 #include <vector>
 
-#include "source/binary.h"
-#include "source/diagnostic.h"
 #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_definition.h"
 #include "source/spirv_target_env.h"
 #include "source/spirv_validator_options.h"
 #include "source/util/string_utils.h"
-#include "source/val/function.h"
 #include "source/val/validate.h"
 #include "source/val/validation_state.h"
 
@@ -44,14 +38,14 @@
 std::string ToString(const CapabilitySet& capabilities,
                      const AssemblyGrammar& grammar) {
   std::stringstream ss;
-  capabilities.ForEach([&grammar, &ss](SpvCapability cap) {
+  for (auto capability : capabilities) {
     spv_operand_desc desc;
-    if (SPV_SUCCESS ==
-        grammar.lookupOperand(SPV_OPERAND_TYPE_CAPABILITY, cap, &desc))
+    if (SPV_SUCCESS == grammar.lookupOperand(SPV_OPERAND_TYPE_CAPABILITY,
+                                             uint32_t(capability), &desc))
       ss << desc->name << " ";
     else
-      ss << cap << " ";
-  });
+      ss << uint32_t(capability) << " ";
+  }
   return ss.str();
 }
 
@@ -60,18 +54,18 @@
 // the opcode may only be used if at least one of the capabilities is specified
 // by the module.
 CapabilitySet EnablingCapabilitiesForOp(const ValidationState_t& state,
-                                        SpvOp opcode) {
+                                        spv::Op opcode) {
   // Exceptions for SPV_AMD_shader_ballot
   switch (opcode) {
     // Normally these would require Group capability
-    case SpvOpGroupIAddNonUniformAMD:
-    case SpvOpGroupFAddNonUniformAMD:
-    case SpvOpGroupFMinNonUniformAMD:
-    case SpvOpGroupUMinNonUniformAMD:
-    case SpvOpGroupSMinNonUniformAMD:
-    case SpvOpGroupFMaxNonUniformAMD:
-    case SpvOpGroupUMaxNonUniformAMD:
-    case SpvOpGroupSMaxNonUniformAMD:
+    case spv::Op::OpGroupIAddNonUniformAMD:
+    case spv::Op::OpGroupFAddNonUniformAMD:
+    case spv::Op::OpGroupFMinNonUniformAMD:
+    case spv::Op::OpGroupUMinNonUniformAMD:
+    case spv::Op::OpGroupSMinNonUniformAMD:
+    case spv::Op::OpGroupFMaxNonUniformAMD:
+    case spv::Op::OpGroupUMaxNonUniformAMD:
+    case spv::Op::OpGroupSMaxNonUniformAMD:
       if (state.HasExtension(kSPV_AMD_shader_ballot)) return CapabilitySet();
       break;
     default:
@@ -151,10 +145,10 @@
   // not implemented yet.  This rule is independent of target environment.
   // See https://github.com/KhronosGroup/SPIRV-Tools/issues/365
   if (operand.type == SPV_OPERAND_TYPE_BUILT_IN) {
-    switch (word) {
-      case SpvBuiltInPointSize:
-      case SpvBuiltInClipDistance:
-      case SpvBuiltInCullDistance:
+    switch (spv::BuiltIn(word)) {
+      case spv::BuiltIn::PointSize:
+      case spv::BuiltIn::ClipDistance:
+      case spv::BuiltIn::CullDistance:
         return SPV_SUCCESS;
       default:
         break;
@@ -166,7 +160,7 @@
     }
   } else if (operand.type == SPV_OPERAND_TYPE_GROUP_OPERATION &&
              state.features().group_ops_reduce_and_scans &&
-             (word <= uint32_t(SpvGroupOperationExclusiveScan))) {
+             (word <= uint32_t(spv::GroupOperation::ExclusiveScan))) {
     // Allow certain group operations if requested.
     return SPV_SUCCESS;
   }
@@ -178,15 +172,17 @@
   if (lookup_result == SPV_SUCCESS) {
     // Allow FPRoundingMode decoration if requested.
     if (operand.type == SPV_OPERAND_TYPE_DECORATION &&
-        operand_desc->value == SpvDecorationFPRoundingMode) {
+        spv::Decoration(operand_desc->value) ==
+            spv::Decoration::FPRoundingMode) {
       if (state.features().free_fp_rounding_mode) return SPV_SUCCESS;
 
       // Vulkan API requires more capabilities on rounding mode.
       if (spvIsVulkanEnv(state.context()->target_env)) {
-        enabling_capabilities.Add(SpvCapabilityStorageUniformBufferBlock16);
-        enabling_capabilities.Add(SpvCapabilityStorageUniform16);
-        enabling_capabilities.Add(SpvCapabilityStoragePushConstant16);
-        enabling_capabilities.Add(SpvCapabilityStorageInputOutput16);
+        enabling_capabilities.insert(
+            spv::Capability::StorageUniformBufferBlock16);
+        enabling_capabilities.insert(spv::Capability::StorageUniform16);
+        enabling_capabilities.insert(spv::Capability::StoragePushConstant16);
+        enabling_capabilities.insert(spv::Capability::StorageInputOutput16);
       }
     } else {
       enabling_capabilities = state.grammar().filterCapsAgainstTargetEnv(
@@ -197,10 +193,10 @@
     // 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() != SpvOpCapability) {
+    if (inst->opcode() != spv::Op::OpCapability) {
       const bool enabled_by_cap =
           state.HasAnyOfCapabilities(enabling_capabilities);
-      if (!enabling_capabilities.IsEmpty() && !enabled_by_cap) {
+      if (!enabling_capabilities.empty() && !enabled_by_cap) {
         return state.diag(SPV_ERROR_INVALID_CAPABILITY, inst)
                << "Operand " << which_operand << " of "
                << spvOpcodeString(inst->opcode())
@@ -218,14 +214,14 @@
 // is explicitly reserved in the SPIR-V core spec.  Otherwise return
 // SPV_SUCCESS.
 spv_result_t ReservedCheck(ValidationState_t& _, const Instruction* inst) {
-  const SpvOp opcode = inst->opcode();
+  const spv::Op opcode = inst->opcode();
   switch (opcode) {
     // These instructions are enabled by a capability, but should never
     // be used anyway.
-    case SpvOpImageSparseSampleProjImplicitLod:
-    case SpvOpImageSparseSampleProjExplicitLod:
-    case SpvOpImageSparseSampleProjDrefImplicitLod:
-    case SpvOpImageSparseSampleProjDrefExplicitLod: {
+    case spv::Op::OpImageSparseSampleProjImplicitLod:
+    case spv::Op::OpImageSparseSampleProjExplicitLod:
+    case spv::Op::OpImageSparseSampleProjDrefImplicitLod:
+    case spv::Op::OpImageSparseSampleProjDrefExplicitLod: {
       spv_opcode_desc inst_desc;
       _.grammar().lookupOpcode(opcode, &inst_desc);
       return _.diag(SPV_ERROR_INVALID_BINARY, inst)
@@ -241,7 +237,7 @@
 // instruction is invalid because the required capability isn't declared
 // in the module.
 spv_result_t CapabilityCheck(ValidationState_t& _, const Instruction* inst) {
-  const SpvOp opcode = inst->opcode();
+  const spv::Op opcode = inst->opcode();
   CapabilitySet opcode_caps = EnablingCapabilitiesForOp(_, opcode);
   if (!_.HasAnyOfCapabilities(opcode_caps)) {
     return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst)
@@ -299,7 +295,7 @@
   // OpTerminateInvocation is special because it is enabled by Shader
   // capability, but also requires an extension and/or version check.
   const bool capability_check_is_sufficient =
-      inst->opcode() != SpvOpTerminateInvocation;
+      inst->opcode() != spv::Op::OpTerminateInvocation;
 
   if (capability_check_is_sufficient && (inst_desc->numCapabilities > 0u)) {
     // We already checked that the direct capability dependency has been
@@ -308,7 +304,7 @@
   }
 
   ExtensionSet exts(inst_desc->numExtensions, inst_desc->extensions);
-  if (exts.IsEmpty()) {
+  if (exts.empty()) {
     // If no extensions can enable this instruction, then emit error
     // messages only concerning core SPIR-V versions if errors happen.
     if (min_version == ~0u) {
@@ -357,7 +353,7 @@
 
 // Checks that the number of OpTypeStruct members is within the limit.
 spv_result_t LimitCheckStruct(ValidationState_t& _, const Instruction* inst) {
-  if (SpvOpTypeStruct != inst->opcode()) {
+  if (spv::Op::OpTypeStruct != inst->opcode()) {
     return SPV_SUCCESS;
   }
 
@@ -382,7 +378,7 @@
   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 && SpvOpTypeStruct == memberTypeInstr->opcode()) {
+    if (memberTypeInstr && spv::Op::OpTypeStruct == memberTypeInstr->opcode()) {
       max_member_depth = std::max(
           max_member_depth, _.struct_nesting_depth(memberTypeInstr->id()));
     }
@@ -402,7 +398,7 @@
 // Checks that the number of (literal, label) pairs in OpSwitch is within
 // the limit.
 spv_result_t LimitCheckSwitch(ValidationState_t& _, const Instruction* inst) {
-  if (SpvOpSwitch == inst->opcode()) {
+  if (spv::Op::OpSwitch == inst->opcode()) {
     // The instruction syntax is as follows:
     // OpSwitch <selector ID> <Default ID> literal label literal label ...
     // literal,label pairs come after the first 2 operands.
@@ -422,8 +418,8 @@
 // Ensure the number of variables of the given class does not exceed the
 // limit.
 spv_result_t LimitCheckNumVars(ValidationState_t& _, const uint32_t var_id,
-                               const SpvStorageClass storage_class) {
-  if (SpvStorageClassFunction == storage_class) {
+                               const spv::StorageClass storage_class) {
+  if (spv::StorageClass::Function == storage_class) {
     _.registerLocalVariable(var_id);
     const uint32_t num_local_vars_limit =
         _.options()->universal_limits_.max_local_variables;
@@ -462,29 +458,29 @@
 }  // namespace
 
 spv_result_t InstructionPass(ValidationState_t& _, const Instruction* inst) {
-  const SpvOp opcode = inst->opcode();
-  if (opcode == SpvOpExtension) {
+  const spv::Op opcode = inst->opcode();
+  if (opcode == spv::Op::OpExtension) {
     CheckIfKnownExtension(_, inst);
-  } else if (opcode == SpvOpCapability) {
-    _.RegisterCapability(inst->GetOperandAs<SpvCapability>(0));
-  } else if (opcode == SpvOpMemoryModel) {
+  } else if (opcode == spv::Op::OpCapability) {
+    _.RegisterCapability(inst->GetOperandAs<spv::Capability>(0));
+  } else if (opcode == spv::Op::OpMemoryModel) {
     if (_.has_memory_model_specified()) {
       return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
              << "OpMemoryModel should only be provided once.";
     }
-    _.set_addressing_model(inst->GetOperandAs<SpvAddressingModel>(0));
-    _.set_memory_model(inst->GetOperandAs<SpvMemoryModel>(1));
-  } else if (opcode == SpvOpExecutionMode) {
+    _.set_addressing_model(inst->GetOperandAs<spv::AddressingModel>(0));
+    _.set_memory_model(inst->GetOperandAs<spv::MemoryModel>(1));
+  } else if (opcode == spv::Op::OpExecutionMode) {
     const uint32_t entry_point = inst->word(1);
     _.RegisterExecutionModeForEntryPoint(entry_point,
-                                         SpvExecutionMode(inst->word(2)));
-  } else if (opcode == SpvOpVariable) {
-    const auto storage_class = inst->GetOperandAs<SpvStorageClass>(2);
+                                         spv::ExecutionMode(inst->word(2)));
+  } else if (opcode == spv::Op::OpVariable) {
+    const auto storage_class = inst->GetOperandAs<spv::StorageClass>(2);
     if (auto error = LimitCheckNumVars(_, inst->id(), storage_class)) {
       return error;
     }
-  } else if (opcode == SpvOpSamplerImageAddressingModeNV) {
-    if (!_.HasCapability(SpvCapabilityBindlessTextureNV)) {
+  } else if (opcode == spv::Op::OpSamplerImageAddressingModeNV) {
+    if (!_.HasCapability(spv::Capability::BindlessTextureNV)) {
       return _.diag(SPV_ERROR_MISSING_EXTENSION, inst)
              << "OpSamplerImageAddressingModeNV supported only with extension "
                 "SPV_NV_bindless_texture";
diff --git a/source/val/validate_interfaces.cpp b/source/val/validate_interfaces.cpp
index 7f2d648..291b4b8 100644
--- a/source/val/validate_interfaces.cpp
+++ b/source/val/validate_interfaces.cpp
@@ -15,7 +15,6 @@
 #include <algorithm>
 #include <vector>
 
-#include "source/diagnostic.h"
 #include "source/spirv_constant.h"
 #include "source/spirv_target_env.h"
 #include "source/val/function.h"
@@ -35,12 +34,15 @@
 bool is_interface_variable(const Instruction* inst, bool is_spv_1_4) {
   if (is_spv_1_4) {
     // Starting in SPIR-V 1.4, all global variables are interface variables.
-    return inst->opcode() == SpvOpVariable &&
-           inst->word(3u) != SpvStorageClassFunction;
+    return inst->opcode() == spv::Op::OpVariable &&
+           inst->GetOperandAs<spv::StorageClass>(2u) !=
+               spv::StorageClass::Function;
   } else {
-    return inst->opcode() == SpvOpVariable &&
-           (inst->word(3u) == SpvStorageClassInput ||
-            inst->word(3u) == SpvStorageClassOutput);
+    return inst->opcode() == spv::Op::OpVariable &&
+           (inst->GetOperandAs<spv::StorageClass>(2u) ==
+                spv::StorageClass::Input ||
+            inst->GetOperandAs<spv::StorageClass>(2u) ==
+                spv::StorageClass::Output);
   }
 }
 
@@ -114,29 +116,30 @@
                                   uint32_t* num_locations) {
   *num_locations = 0;
   switch (type->opcode()) {
-    case SpvOpTypeInt:
-    case SpvOpTypeFloat:
+    case spv::Op::OpTypeInt:
+    case spv::Op::OpTypeFloat:
       // Scalars always consume a single location.
       *num_locations = 1;
       break;
-    case SpvOpTypeVector:
+    case spv::Op::OpTypeVector:
       // 3- and 4-component 64-bit vectors consume two locations.
-      if ((_.ContainsSizedIntOrFloatType(type->id(), SpvOpTypeInt, 64) ||
-           _.ContainsSizedIntOrFloatType(type->id(), SpvOpTypeFloat, 64)) &&
+      if ((_.ContainsSizedIntOrFloatType(type->id(), spv::Op::OpTypeInt, 64) ||
+           _.ContainsSizedIntOrFloatType(type->id(), spv::Op::OpTypeFloat,
+                                         64)) &&
           (type->GetOperandAs<uint32_t>(2) > 2)) {
         *num_locations = 2;
       } else {
         *num_locations = 1;
       }
       break;
-    case SpvOpTypeMatrix:
+    case spv::Op::OpTypeMatrix:
       // Matrices consume locations equal to the underlying vector type for
       // each column.
       NumConsumedLocations(_, _.FindDef(type->GetOperandAs<uint32_t>(1)),
                            num_locations);
       *num_locations *= type->GetOperandAs<uint32_t>(2);
       break;
-    case SpvOpTypeArray: {
+    case spv::Op::OpTypeArray: {
       // Arrays consume locations equal to the underlying type times the number
       // of elements in the vector.
       NumConsumedLocations(_, _.FindDef(type->GetOperandAs<uint32_t>(1)),
@@ -150,9 +153,9 @@
       if (is_int && is_const) *num_locations *= value;
       break;
     }
-    case SpvOpTypeStruct: {
+    case spv::Op::OpTypeStruct: {
       // Members cannot have location decorations at this point.
-      if (_.HasDecoration(type->id(), SpvDecorationLocation)) {
+      if (_.HasDecoration(type->id(), spv::Decoration::Location)) {
         return _.diag(SPV_ERROR_INVALID_DATA, type)
                << _.VkErrorID(4918) << "Members cannot be assigned a location";
       }
@@ -170,8 +173,19 @@
       }
       break;
     }
+    case spv::Op::OpTypePointer: {
+      if (_.addressing_model() ==
+              spv::AddressingModel::PhysicalStorageBuffer64 &&
+          type->GetOperandAs<spv::StorageClass>(1) ==
+              spv::StorageClass::PhysicalStorageBuffer) {
+        *num_locations = 1;
+        break;
+      }
+      [[fallthrough]];
+    }
     default:
-      break;
+      return _.diag(SPV_ERROR_INVALID_DATA, type)
+             << "Invalid type to assign a location";
   }
 
   return SPV_SUCCESS;
@@ -182,8 +196,8 @@
 uint32_t NumConsumedComponents(ValidationState_t& _, const Instruction* type) {
   uint32_t num_components = 0;
   switch (type->opcode()) {
-    case SpvOpTypeInt:
-    case SpvOpTypeFloat:
+    case spv::Op::OpTypeInt:
+    case spv::Op::OpTypeFloat:
       // 64-bit types consume two components.
       if (type->GetOperandAs<uint32_t>(1) == 64) {
         num_components = 2;
@@ -191,7 +205,7 @@
         num_components = 1;
       }
       break;
-    case SpvOpTypeVector:
+    case spv::Op::OpTypeVector:
       // 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).
@@ -199,10 +213,18 @@
           NumConsumedComponents(_, _.FindDef(type->GetOperandAs<uint32_t>(1)));
       num_components *= type->GetOperandAs<uint32_t>(2);
       break;
-    case SpvOpTypeArray:
+    case spv::Op::OpTypeArray:
       // Skip the array.
       return NumConsumedComponents(_,
                                    _.FindDef(type->GetOperandAs<uint32_t>(1)));
+    case spv::Op::OpTypePointer:
+      if (_.addressing_model() ==
+              spv::AddressingModel::PhysicalStorageBuffer64 &&
+          type->GetOperandAs<spv::StorageClass>(1) ==
+              spv::StorageClass::PhysicalStorageBuffer) {
+        return 2;
+      }
+      break;
     default:
       // This is an error that is validated elsewhere.
       break;
@@ -218,10 +240,10 @@
     ValidationState_t& _, const Instruction* entry_point,
     const Instruction* variable, std::unordered_set<uint32_t>* locations,
     std::unordered_set<uint32_t>* output_index1_locations) {
-  const bool is_fragment = entry_point->GetOperandAs<SpvExecutionModel>(0) ==
-                           SpvExecutionModelFragment;
+  const bool is_fragment = entry_point->GetOperandAs<spv::ExecutionModel>(0) ==
+                           spv::ExecutionModel::Fragment;
   const bool is_output =
-      variable->GetOperandAs<SpvStorageClass>(2) == SpvStorageClassOutput;
+      variable->GetOperandAs<spv::StorageClass>(2) == spv::StorageClass::Output;
   auto ptr_type_id = variable->GetOperandAs<uint32_t>(0);
   auto ptr_type = _.FindDef(ptr_type_id);
   auto type_id = ptr_type->GetOperandAs<uint32_t>(2);
@@ -240,21 +262,21 @@
   bool has_per_task_nv = false;
   bool has_per_vertex_khr = false;
   for (auto& dec : _.id_decorations(variable->id())) {
-    if (dec.dec_type() == SpvDecorationLocation) {
+    if (dec.dec_type() == spv::Decoration::Location) {
       if (has_location && dec.params()[0] != location) {
         return _.diag(SPV_ERROR_INVALID_DATA, variable)
                << "Variable has conflicting location decorations";
       }
       has_location = true;
       location = dec.params()[0];
-    } else if (dec.dec_type() == SpvDecorationComponent) {
+    } else if (dec.dec_type() == spv::Decoration::Component) {
       if (has_component && dec.params()[0] != component) {
         return _.diag(SPV_ERROR_INVALID_DATA, variable)
                << "Variable has conflicting component decorations";
       }
       has_component = true;
       component = dec.params()[0];
-    } else if (dec.dec_type() == SpvDecorationIndex) {
+    } else if (dec.dec_type() == spv::Decoration::Index) {
       if (!is_output || !is_fragment) {
         return _.diag(SPV_ERROR_INVALID_DATA, variable)
                << "Index can only be applied to Fragment output variables";
@@ -265,22 +287,22 @@
       }
       has_index = true;
       index = dec.params()[0];
-    } else if (dec.dec_type() == SpvDecorationBuiltIn) {
+    } else if (dec.dec_type() == spv::Decoration::BuiltIn) {
       // Don't check built-ins.
       return SPV_SUCCESS;
-    } else if (dec.dec_type() == SpvDecorationPatch) {
+    } else if (dec.dec_type() == spv::Decoration::Patch) {
       has_patch = true;
-    } else if (dec.dec_type() == SpvDecorationPerTaskNV) {
+    } else if (dec.dec_type() == spv::Decoration::PerTaskNV) {
       has_per_task_nv = true;
-    } else if (dec.dec_type() == SpvDecorationPerVertexKHR) {
+    } else if (dec.dec_type() == spv::Decoration::PerVertexKHR) {
       if (!is_fragment) {
         return _.diag(SPV_ERROR_INVALID_DATA, variable)
                << _.VkErrorID(6777)
                << "PerVertexKHR can only be applied to Fragment Execution "
                   "Models";
       }
-      if (type->opcode() != SpvOpTypeArray &&
-          type->opcode() != SpvOpTypeRuntimeArray) {
+      if (type->opcode() != spv::Op::OpTypeArray &&
+          type->opcode() != spv::Op::OpTypeRuntimeArray) {
         return _.diag(SPV_ERROR_INVALID_DATA, variable)
                << _.VkErrorID(6778)
                << "PerVertexKHR must be declared as arrays";
@@ -293,28 +315,28 @@
   // tessellation control, evaluation and geometry per-vertex inputs have a
   // layer of arraying that is not included in interface matching.
   bool is_arrayed = false;
-  switch (entry_point->GetOperandAs<SpvExecutionModel>(0)) {
-    case SpvExecutionModelTessellationControl:
+  switch (entry_point->GetOperandAs<spv::ExecutionModel>(0)) {
+    case spv::ExecutionModel::TessellationControl:
       if (!has_patch) {
         is_arrayed = true;
       }
       break;
-    case SpvExecutionModelTessellationEvaluation:
+    case spv::ExecutionModel::TessellationEvaluation:
       if (!is_output && !has_patch) {
         is_arrayed = true;
       }
       break;
-    case SpvExecutionModelGeometry:
+    case spv::ExecutionModel::Geometry:
       if (!is_output) {
         is_arrayed = true;
       }
       break;
-    case SpvExecutionModelFragment:
+    case spv::ExecutionModel::Fragment:
       if (!is_output && has_per_vertex_khr) {
         is_arrayed = true;
       }
       break;
-    case SpvExecutionModelMeshNV:
+    case spv::ExecutionModel::MeshNV:
       if (is_output && !has_per_task_nv) {
         is_arrayed = true;
       }
@@ -324,21 +346,21 @@
   }
 
   // Unpack arrayness.
-  if (is_arrayed && (type->opcode() == SpvOpTypeArray ||
-                     type->opcode() == SpvOpTypeRuntimeArray)) {
+  if (is_arrayed && (type->opcode() == spv::Op::OpTypeArray ||
+                     type->opcode() == spv::Op::OpTypeRuntimeArray)) {
     type_id = type->GetOperandAs<uint32_t>(1);
     type = _.FindDef(type_id);
   }
 
-  if (type->opcode() == SpvOpTypeStruct) {
+  if (type->opcode() == spv::Op::OpTypeStruct) {
     // Don't check built-ins.
-    if (_.HasDecoration(type_id, SpvDecorationBuiltIn)) return SPV_SUCCESS;
+    if (_.HasDecoration(type_id, spv::Decoration::BuiltIn)) return SPV_SUCCESS;
   }
 
   // Only block-decorated structs don't need a location on the variable.
-  const bool is_block = _.HasDecoration(type_id, SpvDecorationBlock);
+  const bool is_block = _.HasDecoration(type_id, spv::Decoration::Block);
   if (!has_location && !is_block) {
-    const auto vuid = (type->opcode() == SpvOpTypeStruct) ? 4917 : 4916;
+    const auto vuid = (type->opcode() == spv::Op::OpTypeStruct) ? 4917 : 4916;
     return _.diag(SPV_ERROR_INVALID_DATA, variable)
            << _.VkErrorID(vuid) << "Variable must be decorated with a location";
   }
@@ -351,7 +373,7 @@
     uint32_t array_size = 1;
     // If the variable is still arrayed, mark the locations/components per
     // index.
-    if (type->opcode() == SpvOpTypeArray) {
+    if (type->opcode() == spv::Op::OpTypeArray) {
       // Determine the array size if possible and get the element type.
       std::tie(is_int, is_const, array_size) =
           _.EvalInt32IfConst(type->GetOperandAs<uint32_t>(2));
@@ -360,12 +382,12 @@
       sub_type = _.FindDef(sub_type_id);
     }
 
-    for (uint32_t array_idx = 0; array_idx < array_size; ++array_idx) {
-      uint32_t num_locations = 0;
-      if (auto error = NumConsumedLocations(_, sub_type, &num_locations))
-        return error;
+    uint32_t num_locations = 0;
+    if (auto error = NumConsumedLocations(_, sub_type, &num_locations))
+      return error;
+    uint32_t num_components = NumConsumedComponents(_, sub_type);
 
-      uint32_t num_components = NumConsumedComponents(_, sub_type);
+    for (uint32_t array_idx = 0; array_idx < array_size; ++array_idx) {
       uint32_t array_location = location + (num_locations * array_idx);
       uint32_t start = array_location * 4;
       if (kMaxLocations <= start) {
@@ -385,6 +407,7 @@
       for (uint32_t i = start; i < end; ++i) {
         if (!locs->insert(i).second) {
           return _.diag(SPV_ERROR_INVALID_DATA, entry_point)
+                 << (is_output ? _.VkErrorID(8722) : _.VkErrorID(8721))
                  << "Entry-point has conflicting " << storage_class
                  << " location assignment at location " << i / 4
                  << ", component " << i % 4;
@@ -399,7 +422,7 @@
     std::unordered_map<uint32_t, uint32_t> member_locations;
     std::unordered_map<uint32_t, uint32_t> member_components;
     for (auto& dec : _.id_decorations(type_id)) {
-      if (dec.dec_type() == SpvDecorationLocation) {
+      if (dec.dec_type() == spv::Decoration::Location) {
         auto where = member_locations.find(dec.struct_member_index());
         if (where == member_locations.end()) {
           member_locations[dec.struct_member_index()] = dec.params()[0];
@@ -408,7 +431,7 @@
                  << "Member index " << dec.struct_member_index()
                  << " has conflicting location assignments";
         }
-      } else if (dec.dec_type() == SpvDecorationComponent) {
+      } else if (dec.dec_type() == spv::Decoration::Component) {
         auto where = member_components.find(dec.struct_member_index());
         if (where == member_components.end()) {
           member_components[dec.struct_member_index()] = dec.params()[0];
@@ -447,7 +470,7 @@
         continue;
       }
 
-      if (member->opcode() == SpvOpTypeArray && num_components >= 1 &&
+      if (member->opcode() == spv::Op::OpTypeArray && num_components >= 1 &&
           num_components < 4) {
         // When an array has an element that takes less than a location in
         // size, calculate the used locations in a strided manner.
@@ -456,6 +479,7 @@
             uint32_t check = 4 * l + c;
             if (!locations->insert(check).second) {
               return _.diag(SPV_ERROR_INVALID_DATA, entry_point)
+                     << (is_output ? _.VkErrorID(8722) : _.VkErrorID(8721))
                      << "Entry-point has conflicting " << storage_class
                      << " location assignment at location " << l
                      << ", component " << c;
@@ -473,6 +497,7 @@
         for (uint32_t l = start; l < end; ++l) {
           if (!locations->insert(l).second) {
             return _.diag(SPV_ERROR_INVALID_DATA, entry_point)
+                   << (is_output ? _.VkErrorID(8722) : _.VkErrorID(8721))
                    << "Entry-point has conflicting " << storage_class
                    << " location assignment at location " << l / 4
                    << ", component " << l % 4;
@@ -492,12 +517,12 @@
   // TODO(dneto): SPV_NV_ray_tracing also uses locations on interface variables,
   // in other shader stages. Similarly, the *provisional* version of
   // SPV_KHR_ray_tracing did as well, but not the final version.
-  switch (entry_point->GetOperandAs<SpvExecutionModel>(0)) {
-    case SpvExecutionModelVertex:
-    case SpvExecutionModelTessellationControl:
-    case SpvExecutionModelTessellationEvaluation:
-    case SpvExecutionModelGeometry:
-    case SpvExecutionModelFragment:
+  switch (entry_point->GetOperandAs<spv::ExecutionModel>(0)) {
+    case spv::ExecutionModel::Vertex:
+    case spv::ExecutionModel::TessellationControl:
+    case spv::ExecutionModel::TessellationEvaluation:
+    case spv::ExecutionModel::Geometry:
+    case spv::ExecutionModel::Fragment:
       break;
     default:
       return SPV_SUCCESS;
@@ -511,9 +536,9 @@
   for (uint32_t i = 3; i < entry_point->operands().size(); ++i) {
     auto interface_id = entry_point->GetOperandAs<uint32_t>(i);
     auto interface_var = _.FindDef(interface_id);
-    auto storage_class = interface_var->GetOperandAs<SpvStorageClass>(2);
-    if (storage_class != SpvStorageClassInput &&
-        storage_class != SpvStorageClassOutput) {
+    auto storage_class = interface_var->GetOperandAs<spv::StorageClass>(2);
+    if (storage_class != spv::StorageClass::Input &&
+        storage_class != spv::StorageClass::Output) {
       continue;
     }
     if (!seen.insert(interface_id).second) {
@@ -522,7 +547,7 @@
       continue;
     }
 
-    auto locations = (storage_class == SpvStorageClassInput)
+    auto locations = (storage_class == spv::StorageClass::Input)
                          ? &input_locations
                          : &output_locations_index0;
     if (auto error = GetLocationsForVariable(
@@ -547,12 +572,12 @@
 
   if (spvIsVulkanEnv(_.context()->target_env)) {
     for (auto& inst : _.ordered_instructions()) {
-      if (inst.opcode() == SpvOpEntryPoint) {
+      if (inst.opcode() == spv::Op::OpEntryPoint) {
         if (auto error = ValidateLocations(_, &inst)) {
           return error;
         }
       }
-      if (inst.opcode() == SpvOpTypeVoid) break;
+      if (inst.opcode() == spv::Op::OpTypeVoid) break;
     }
   }
 
diff --git a/source/val/validate_layout.cpp b/source/val/validate_layout.cpp
index 6f95135..dbc1f1e 100644
--- a/source/val/validate_layout.cpp
+++ b/source/val/validate_layout.cpp
@@ -14,12 +14,9 @@
 
 // Source code for logical layout validation as described in section 2.4
 
-#include <cassert>
-
 #include "DebugInfo.h"
 #include "NonSemanticShaderDebugInfo100.h"
 #include "OpenCLDebugInfo100.h"
-#include "source/diagnostic.h"
 #include "source/opcode.h"
 #include "source/operand.h"
 #include "source/val/function.h"
@@ -35,9 +32,9 @@
 // is part of the current layout section. If it is not then the next sections is
 // checked.
 spv_result_t ModuleScopedInstructions(ValidationState_t& _,
-                                      const Instruction* inst, SpvOp opcode) {
+                                      const Instruction* inst, spv::Op opcode) {
   switch (opcode) {
-    case SpvOpExtInst:
+    case spv::Op::OpExtInst:
       if (spvExtInstIsDebugInfo(inst->ext_inst_type())) {
         const uint32_t ext_inst_index = inst->word(4);
         bool local_debug_info = false;
@@ -131,7 +128,7 @@
 
     switch (_.current_layout_section()) {
       case kLayoutMemoryModel:
-        if (opcode != SpvOpMemoryModel) {
+        if (opcode != spv::Op::OpMemoryModel) {
           return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
                  << spvOpcodeString(opcode)
                  << " cannot appear before the memory model instruction";
@@ -154,7 +151,8 @@
 // inside of another function. This stage ends when the first label is
 // encountered inside of a function.
 spv_result_t FunctionScopedInstructions(ValidationState_t& _,
-                                        const Instruction* inst, SpvOp opcode) {
+                                        const Instruction* inst,
+                                        spv::Op opcode) {
   // Make sure we advance into the function definitions when we hit
   // non-function declaration instructions.
   if (_.current_layout_section() == kLayoutFunctionDeclarations &&
@@ -171,12 +169,12 @@
 
   if (_.IsOpcodeInCurrentLayoutSection(opcode)) {
     switch (opcode) {
-      case SpvOpFunction: {
+      case spv::Op::OpFunction: {
         if (_.in_function_body()) {
           return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
                  << "Cannot declare a function in a function body";
         }
-        auto control_mask = inst->GetOperandAs<SpvFunctionControlMask>(2);
+        auto control_mask = inst->GetOperandAs<spv::FunctionControlMask>(2);
         if (auto error =
                 _.RegisterFunction(inst->id(), inst->type_id(), control_mask,
                                    inst->GetOperandAs<uint32_t>(3)))
@@ -188,7 +186,7 @@
         }
       } break;
 
-      case SpvOpFunctionParameter:
+      case spv::Op::OpFunctionParameter:
         if (_.in_function_body() == false) {
           return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
                  << "Function parameter instructions must be in a "
@@ -204,7 +202,7 @@
           return error;
         break;
 
-      case SpvOpFunctionEnd:
+      case spv::Op::OpFunctionEnd:
         if (_.in_function_body() == false) {
           return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
                  << "Function end instructions must be in a function body";
@@ -227,10 +225,10 @@
         if (auto error = _.RegisterFunctionEnd()) return error;
         break;
 
-      case SpvOpLine:
-      case SpvOpNoLine:
+      case spv::Op::OpLine:
+      case spv::Op::OpNoLine:
         break;
-      case SpvOpLabel:
+      case spv::Op::OpLabel:
         // If the label is encountered then the current function is a
         // definition so set the function to a declaration and update the
         // module section
@@ -244,7 +242,7 @@
         }
         break;
 
-      case SpvOpExtInst:
+      case spv::Op::OpExtInst:
         if (spvExtInstIsDebugInfo(inst->ext_inst_type())) {
           const uint32_t ext_inst_index = inst->word(4);
           bool local_debug_info = false;
@@ -356,7 +354,7 @@
 // NOTE: This function does not handle CFG related validation
 // Performs logical layout validation. See Section 2.4
 spv_result_t ModuleLayoutPass(ValidationState_t& _, const Instruction* inst) {
-  const SpvOp opcode = inst->opcode();
+  const spv::Op opcode = inst->opcode();
 
   switch (_.current_layout_section()) {
     case kLayoutCapabilities:
diff --git a/source/val/validate_literals.cpp b/source/val/validate_literals.cpp
index 53aae07..15cc27a 100644
--- a/source/val/validate_literals.cpp
+++ b/source/val/validate_literals.cpp
@@ -14,13 +14,10 @@
 
 // Validates literal numbers.
 
-#include "source/val/validate.h"
-
 #include <cassert>
 
-#include "source/diagnostic.h"
-#include "source/opcode.h"
 #include "source/val/instruction.h"
+#include "source/val/validate.h"
 #include "source/val/validation_state.h"
 
 namespace spvtools {
diff --git a/source/val/validate_logicals.cpp b/source/val/validate_logicals.cpp
index ec1e207..4479e43 100644
--- a/source/val/validate_logicals.cpp
+++ b/source/val/validate_logicals.cpp
@@ -14,11 +14,9 @@
 
 // Validates correctness of logical SPIR-V instructions.
 
-#include "source/val/validate.h"
-
-#include "source/diagnostic.h"
 #include "source/opcode.h"
 #include "source/val/instruction.h"
+#include "source/val/validate.h"
 #include "source/val/validation_state.h"
 
 namespace spvtools {
@@ -26,12 +24,12 @@
 
 // Validates correctness of logical instructions.
 spv_result_t LogicalsPass(ValidationState_t& _, const Instruction* inst) {
-  const SpvOp opcode = inst->opcode();
+  const spv::Op opcode = inst->opcode();
   const uint32_t result_type = inst->type_id();
 
   switch (opcode) {
-    case SpvOpAny:
-    case SpvOpAll: {
+    case spv::Op::OpAny:
+    case spv::Op::OpAll: {
       if (!_.IsBoolScalarType(result_type))
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
                << "Expected bool scalar type as Result Type: "
@@ -46,11 +44,11 @@
       break;
     }
 
-    case SpvOpIsNan:
-    case SpvOpIsInf:
-    case SpvOpIsFinite:
-    case SpvOpIsNormal:
-    case SpvOpSignBitSet: {
+    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: "
@@ -72,21 +70,21 @@
       break;
     }
 
-    case SpvOpFOrdEqual:
-    case SpvOpFUnordEqual:
-    case SpvOpFOrdNotEqual:
-    case SpvOpFUnordNotEqual:
-    case SpvOpFOrdLessThan:
-    case SpvOpFUnordLessThan:
-    case SpvOpFOrdGreaterThan:
-    case SpvOpFUnordGreaterThan:
-    case SpvOpFOrdLessThanEqual:
-    case SpvOpFUnordLessThanEqual:
-    case SpvOpFOrdGreaterThanEqual:
-    case SpvOpFUnordGreaterThanEqual:
-    case SpvOpLessOrGreater:
-    case SpvOpOrdered:
-    case SpvOpUnordered: {
+    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: {
       if (!_.IsBoolScalarType(result_type) && !_.IsBoolVectorType(result_type))
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
                << "Expected bool scalar or vector type as Result Type: "
@@ -113,10 +111,10 @@
       break;
     }
 
-    case SpvOpLogicalEqual:
-    case SpvOpLogicalNotEqual:
-    case SpvOpLogicalOr:
-    case SpvOpLogicalAnd: {
+    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: "
@@ -131,7 +129,7 @@
       break;
     }
 
-    case SpvOpLogicalNot: {
+    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: "
@@ -145,7 +143,7 @@
       break;
     }
 
-    case SpvOpSelect: {
+    case spv::Op::OpSelect: {
       uint32_t dimension = 1;
       {
         const Instruction* type_inst = _.FindDef(result_type);
@@ -159,10 +157,10 @@
                  << " type as Result Type: " << spvOpcodeString(opcode);
         };
 
-        const SpvOp type_opcode = type_inst->opcode();
+        const spv::Op type_opcode = type_inst->opcode();
         switch (type_opcode) {
-          case SpvOpTypePointer: {
-            if (_.addressing_model() == SpvAddressingModelLogical &&
+          case spv::Op::OpTypePointer: {
+            if (_.addressing_model() == spv::AddressingModel::Logical &&
                 !_.features().variable_pointers)
               return _.diag(SPV_ERROR_INVALID_DATA, inst)
                      << "Using pointers with OpSelect requires capability "
@@ -170,31 +168,31 @@
             break;
           }
 
-          case SpvOpTypeSampledImage:
-          case SpvOpTypeImage:
-          case SpvOpTypeSampler: {
-            if (!_.HasCapability(SpvCapabilityBindlessTextureNV))
+          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 SpvOpTypeVector: {
+          case spv::Op::OpTypeVector: {
             dimension = type_inst->word(3);
             break;
           }
 
-          case SpvOpTypeBool:
-          case SpvOpTypeInt:
-          case SpvOpTypeFloat: {
+          case spv::Op::OpTypeBool:
+          case spv::Op::OpTypeInt:
+          case spv::Op::OpTypeFloat: {
             break;
           }
 
           // Not RuntimeArray because of other rules.
-          case SpvOpTypeArray:
-          case SpvOpTypeMatrix:
-          case SpvOpTypeStruct: {
+          case spv::Op::OpTypeArray:
+          case spv::Op::OpTypeMatrix:
+          case spv::Op::OpTypeStruct: {
             if (!composites) return fail();
             break;
           }
@@ -235,16 +233,16 @@
       }
     }
 
-    case SpvOpIEqual:
-    case SpvOpINotEqual:
-    case SpvOpUGreaterThan:
-    case SpvOpUGreaterThanEqual:
-    case SpvOpULessThan:
-    case SpvOpULessThanEqual:
-    case SpvOpSGreaterThan:
-    case SpvOpSGreaterThanEqual:
-    case SpvOpSLessThan:
-    case SpvOpSLessThanEqual: {
+    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: {
       if (!_.IsBoolScalarType(result_type) && !_.IsBoolVectorType(result_type))
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
                << "Expected bool scalar or vector type as Result Type: "
diff --git a/source/val/validate_memory.cpp b/source/val/validate_memory.cpp
index 074bdb8..5b25eeb 100644
--- a/source/val/validate_memory.cpp
+++ b/source/val/validate_memory.cpp
@@ -39,13 +39,13 @@
                                  const std::set<Decoration>&);
 
 bool IsAllowedTypeOrArrayOfSame(ValidationState_t& _, const Instruction* type,
-                                std::initializer_list<uint32_t> allowed) {
+                                std::initializer_list<spv::Op> allowed) {
   if (std::find(allowed.begin(), allowed.end(), type->opcode()) !=
       allowed.end()) {
     return true;
   }
-  if (type->opcode() == SpvOpTypeArray ||
-      type->opcode() == SpvOpTypeRuntimeArray) {
+  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();
@@ -57,10 +57,10 @@
 // validator can tell, have the exact same data layout.
 bool AreLayoutCompatibleStructs(ValidationState_t& _, const Instruction* type1,
                                 const Instruction* type2) {
-  if (type1->opcode() != SpvOpTypeStruct) {
+  if (type1->opcode() != spv::Op::OpTypeStruct) {
     return false;
   }
-  if (type2->opcode() != SpvOpTypeStruct) {
+  if (type2->opcode() != spv::Op::OpTypeStruct) {
     return false;
   }
 
@@ -74,9 +74,9 @@
 // be OpTypeStruct instructions.
 bool HaveLayoutCompatibleMembers(ValidationState_t& _, const Instruction* type1,
                                  const Instruction* type2) {
-  assert(type1->opcode() == SpvOpTypeStruct &&
+  assert(type1->opcode() == spv::Op::OpTypeStruct &&
          "type1 must be an OpTypeStruct instruction.");
-  assert(type2->opcode() == SpvOpTypeStruct &&
+  assert(type2->opcode() == spv::Op::OpTypeStruct &&
          "type2 must be an OpTypeStruct instruction.");
   const auto& type1_operands = type1->operands();
   const auto& type2_operands = type2->operands();
@@ -101,9 +101,9 @@
 // OpTypeStruct instructions.
 bool HaveSameLayoutDecorations(ValidationState_t& _, const Instruction* type1,
                                const Instruction* type2) {
-  assert(type1->opcode() == SpvOpTypeStruct &&
+  assert(type1->opcode() == spv::Op::OpTypeStruct &&
          "type1 must be an OpTypeStruct instruction.");
-  assert(type2->opcode() == SpvOpTypeStruct &&
+  assert(type2->opcode() == spv::Op::OpTypeStruct &&
          "type2 must be an OpTypeStruct instruction.");
   const std::set<Decoration>& type1_decorations = _.id_decorations(type1->id());
   const std::set<Decoration>& type2_decorations = _.id_decorations(type2->id());
@@ -130,11 +130,11 @@
     // type1_decoration.  Therefore, it cannot lead to a conflict.
     for (const Decoration& decoration : type1_decorations) {
       switch (decoration.dec_type()) {
-        case SpvDecorationOffset: {
+        case spv::Decoration::Offset: {
           // Since these affect the layout of the struct, they must be present
           // in both structs.
           auto compare = [&decoration](const Decoration& rhs) {
-            if (rhs.dec_type() != SpvDecorationOffset) return false;
+            if (rhs.dec_type() != spv::Decoration::Offset) return false;
             return decoration.struct_member_index() ==
                    rhs.struct_member_index();
           };
@@ -163,7 +163,7 @@
                          bool skip_builtin) {
   if (skip_builtin) {
     for (const Decoration& decoration : _.id_decorations(storage->id())) {
-      if (decoration.dec_type() == SpvDecorationBuiltIn) return false;
+      if (decoration.dec_type() == spv::Decoration::BuiltIn) return false;
     }
   }
 
@@ -172,16 +172,16 @@
   Instruction* elem_type;
 
   switch (storage->opcode()) {
-    case SpvOpTypeBool:
+    case spv::Op::OpTypeBool:
       return true;
-    case SpvOpTypeVector:
-    case SpvOpTypeMatrix:
-    case SpvOpTypeArray:
-    case SpvOpTypeRuntimeArray:
+    case spv::Op::OpTypeVector:
+    case spv::Op::OpTypeMatrix:
+    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 ContainsInvalidBool(_, elem_type, skip_builtin);
-    case SpvOpTypeStruct:
+    case spv::Op::OpTypeStruct:
       for (size_t member_type_index = 1;
            member_type_index < storage->operands().size();
            ++member_type_index) {
@@ -203,14 +203,15 @@
   Instruction* elem_type;
 
   switch (storage->opcode()) {
-    case SpvOpTypeCooperativeMatrixNV:
+    case spv::Op::OpTypeCooperativeMatrixNV:
+    case spv::Op::OpTypeCooperativeMatrixKHR:
       return true;
-    case SpvOpTypeArray:
-    case SpvOpTypeRuntimeArray:
+    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 SpvOpTypeStruct:
+    case spv::Op::OpTypeStruct:
       for (size_t member_type_index = 1;
            member_type_index < storage->operands().size();
            ++member_type_index) {
@@ -226,33 +227,35 @@
   return false;
 }
 
-std::pair<SpvStorageClass, SpvStorageClass> GetStorageClass(
+std::pair<spv::StorageClass, spv::StorageClass> GetStorageClass(
     ValidationState_t& _, const Instruction* inst) {
-  SpvStorageClass dst_sc = SpvStorageClassMax;
-  SpvStorageClass src_sc = SpvStorageClassMax;
+  spv::StorageClass dst_sc = spv::StorageClass::Max;
+  spv::StorageClass src_sc = spv::StorageClass::Max;
   switch (inst->opcode()) {
-    case SpvOpCooperativeMatrixLoadNV:
-    case SpvOpLoad: {
+    case spv::Op::OpCooperativeMatrixLoadNV:
+    case spv::Op::OpCooperativeMatrixLoadKHR:
+    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<SpvStorageClass>(1);
+      dst_sc = load_pointer_type->GetOperandAs<spv::StorageClass>(1);
       break;
     }
-    case SpvOpCooperativeMatrixStoreNV:
-    case SpvOpStore: {
+    case spv::Op::OpCooperativeMatrixStoreNV:
+    case spv::Op::OpCooperativeMatrixStoreKHR:
+    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<SpvStorageClass>(1);
+      dst_sc = store_pointer_type->GetOperandAs<spv::StorageClass>(1);
       break;
     }
-    case SpvOpCopyMemory:
-    case SpvOpCopyMemorySized: {
+    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<SpvStorageClass>(1);
+      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<SpvStorageClass>(1);
+      src_sc = src_type->GetOperandAs<spv::StorageClass>(1);
       break;
     }
     default:
@@ -266,9 +269,9 @@
 // argument and its implied operands.
 int MemoryAccessNumWords(uint32_t mask) {
   int result = 1;  // Count the mask
-  if (mask & SpvMemoryAccessAlignedMask) ++result;
-  if (mask & SpvMemoryAccessMakePointerAvailableKHRMask) ++result;
-  if (mask & SpvMemoryAccessMakePointerVisibleKHRMask) ++result;
+  if (mask & uint32_t(spv::MemoryAccessMask::Aligned)) ++result;
+  if (mask & uint32_t(spv::MemoryAccessMask::MakePointerAvailableKHR)) ++result;
+  if (mask & uint32_t(spv::MemoryAccessMask::MakePointerVisibleKHR)) ++result;
   return result;
 }
 
@@ -279,8 +282,8 @@
 // OpCooperativeMatrixStoreNV.
 uint32_t GetMakeAvailableScope(const Instruction* inst, uint32_t mask,
                                uint32_t mask_index) {
-  assert(mask & SpvMemoryAccessMakePointerAvailableKHRMask);
-  uint32_t this_bit = uint32_t(SpvMemoryAccessMakePointerAvailableKHRMask);
+  assert(mask & uint32_t(spv::MemoryAccessMask::MakePointerAvailableKHR));
+  uint32_t this_bit = uint32_t(spv::MemoryAccessMask::MakePointerAvailableKHR);
   uint32_t index =
       mask_index - 1 + MemoryAccessNumWords(mask & (this_bit | (this_bit - 1)));
   return inst->GetOperandAs<uint32_t>(index);
@@ -291,8 +294,8 @@
 // OpCooperativeMatrixStoreNV.
 uint32_t GetMakeVisibleScope(const Instruction* inst, uint32_t mask,
                              uint32_t mask_index) {
-  assert(mask & SpvMemoryAccessMakePointerVisibleKHRMask);
-  uint32_t this_bit = uint32_t(SpvMemoryAccessMakePointerVisibleKHRMask);
+  assert(mask & uint32_t(spv::MemoryAccessMask::MakePointerVisibleKHR));
+  uint32_t this_bit = uint32_t(spv::MemoryAccessMask::MakePointerVisibleKHR);
   uint32_t index =
       mask_index - 1 + MemoryAccessNumWords(mask & (this_bit | (this_bit - 1)));
   return inst->GetOperandAs<uint32_t>(index);
@@ -303,19 +306,19 @@
        ++member_index) {
     const auto member_id = inst->GetOperandAs<uint32_t>(member_index);
     const auto member_type = _.FindDef(member_id);
-    if (member_type->opcode() == SpvOpTypeRuntimeArray) return true;
+    if (member_type->opcode() == spv::Op::OpTypeRuntimeArray) return true;
   }
   return false;
 }
 
 spv_result_t CheckMemoryAccess(ValidationState_t& _, const Instruction* inst,
                                uint32_t index) {
-  SpvStorageClass dst_sc, src_sc;
+  spv::StorageClass dst_sc, src_sc;
   std::tie(dst_sc, src_sc) = GetStorageClass(_, inst);
   if (inst->operands().size() <= index) {
     // Cases where lack of some operand is invalid
-    if (src_sc == SpvStorageClassPhysicalStorageBuffer ||
-        dst_sc == SpvStorageClassPhysicalStorageBuffer) {
+    if (src_sc == spv::StorageClass::PhysicalStorageBuffer ||
+        dst_sc == spv::StorageClass::PhysicalStorageBuffer) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
              << _.VkErrorID(4708)
              << "Memory accesses with PhysicalStorageBuffer must use Aligned.";
@@ -324,14 +327,15 @@
   }
 
   const uint32_t mask = inst->GetOperandAs<uint32_t>(index);
-  if (mask & SpvMemoryAccessMakePointerAvailableKHRMask) {
-    if (inst->opcode() == SpvOpLoad ||
-        inst->opcode() == SpvOpCooperativeMatrixLoadNV) {
+  if (mask & uint32_t(spv::MemoryAccessMask::MakePointerAvailableKHR)) {
+    if (inst->opcode() == spv::Op::OpLoad ||
+        inst->opcode() == spv::Op::OpCooperativeMatrixLoadNV ||
+        inst->opcode() == spv::Op::OpCooperativeMatrixLoadKHR) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
              << "MakePointerAvailableKHR cannot be used with OpLoad.";
     }
 
-    if (!(mask & SpvMemoryAccessNonPrivatePointerKHRMask)) {
+    if (!(mask & uint32_t(spv::MemoryAccessMask::NonPrivatePointerKHR))) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
              << "NonPrivatePointerKHR must be specified if "
                 "MakePointerAvailableKHR is specified.";
@@ -343,14 +347,14 @@
       return error;
   }
 
-  if (mask & SpvMemoryAccessMakePointerVisibleKHRMask) {
-    if (inst->opcode() == SpvOpStore ||
-        inst->opcode() == SpvOpCooperativeMatrixStoreNV) {
+  if (mask & uint32_t(spv::MemoryAccessMask::MakePointerVisibleKHR)) {
+    if (inst->opcode() == spv::Op::OpStore ||
+        inst->opcode() == spv::Op::OpCooperativeMatrixStoreNV) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
              << "MakePointerVisibleKHR cannot be used with OpStore.";
     }
 
-    if (!(mask & SpvMemoryAccessNonPrivatePointerKHRMask)) {
+    if (!(mask & uint32_t(spv::MemoryAccessMask::NonPrivatePointerKHR))) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
              << "NonPrivatePointerKHR must be specified if "
              << "MakePointerVisibleKHR is specified.";
@@ -361,24 +365,27 @@
     if (auto error = ValidateMemoryScope(_, inst, visible_scope)) return error;
   }
 
-  if (mask & SpvMemoryAccessNonPrivatePointerKHRMask) {
-    if (dst_sc != SpvStorageClassUniform &&
-        dst_sc != SpvStorageClassWorkgroup &&
-        dst_sc != SpvStorageClassCrossWorkgroup &&
-        dst_sc != SpvStorageClassGeneric && dst_sc != SpvStorageClassImage &&
-        dst_sc != SpvStorageClassStorageBuffer &&
-        dst_sc != SpvStorageClassPhysicalStorageBuffer) {
+  if (mask & uint32_t(spv::MemoryAccessMask::NonPrivatePointerKHR)) {
+    if (dst_sc != spv::StorageClass::Uniform &&
+        dst_sc != spv::StorageClass::Workgroup &&
+        dst_sc != spv::StorageClass::CrossWorkgroup &&
+        dst_sc != spv::StorageClass::Generic &&
+        dst_sc != spv::StorageClass::Image &&
+        dst_sc != spv::StorageClass::StorageBuffer &&
+        dst_sc != spv::StorageClass::PhysicalStorageBuffer) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
              << "NonPrivatePointerKHR requires a pointer in Uniform, "
              << "Workgroup, CrossWorkgroup, Generic, Image or StorageBuffer "
              << "storage classes.";
     }
-    if (src_sc != SpvStorageClassMax && src_sc != SpvStorageClassUniform &&
-        src_sc != SpvStorageClassWorkgroup &&
-        src_sc != SpvStorageClassCrossWorkgroup &&
-        src_sc != SpvStorageClassGeneric && src_sc != SpvStorageClassImage &&
-        src_sc != SpvStorageClassStorageBuffer &&
-        src_sc != SpvStorageClassPhysicalStorageBuffer) {
+    if (src_sc != spv::StorageClass::Max &&
+        src_sc != spv::StorageClass::Uniform &&
+        src_sc != spv::StorageClass::Workgroup &&
+        src_sc != spv::StorageClass::CrossWorkgroup &&
+        src_sc != spv::StorageClass::Generic &&
+        src_sc != spv::StorageClass::Image &&
+        src_sc != spv::StorageClass::StorageBuffer &&
+        src_sc != spv::StorageClass::PhysicalStorageBuffer) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
              << "NonPrivatePointerKHR requires a pointer in Uniform, "
              << "Workgroup, CrossWorkgroup, Generic, Image or StorageBuffer "
@@ -386,9 +393,9 @@
     }
   }
 
-  if (!(mask & SpvMemoryAccessAlignedMask)) {
-    if (src_sc == SpvStorageClassPhysicalStorageBuffer ||
-        dst_sc == SpvStorageClassPhysicalStorageBuffer) {
+  if (!(mask & uint32_t(spv::MemoryAccessMask::Aligned))) {
+    if (src_sc == spv::StorageClass::PhysicalStorageBuffer ||
+        dst_sc == spv::StorageClass::PhysicalStorageBuffer) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
              << _.VkErrorID(4708)
              << "Memory accesses with PhysicalStorageBuffer must use Aligned.";
@@ -400,7 +407,7 @@
 
 spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
   auto result_type = _.FindDef(inst->type_id());
-  if (!result_type || result_type->opcode() != SpvOpTypePointer) {
+  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.";
@@ -416,9 +423,9 @@
     const auto initializer_id = inst->GetOperandAs<uint32_t>(initializer_index);
     const auto initializer = _.FindDef(initializer_id);
     const auto is_module_scope_var =
-        initializer && (initializer->opcode() == SpvOpVariable) &&
-        (initializer->GetOperandAs<SpvStorageClass>(storage_class_index) !=
-         SpvStorageClassFunction);
+        initializer && (initializer->opcode() == spv::Op::OpVariable) &&
+        (initializer->GetOperandAs<spv::StorageClass>(storage_class_index) !=
+         spv::StorageClass::Function);
     const auto is_constant =
         initializer && spvOpcodeIsConstant(initializer->opcode());
     if (!initializer || !(is_constant || is_module_scope_var)) {
@@ -433,23 +440,26 @@
     }
   }
 
-  auto storage_class = inst->GetOperandAs<SpvStorageClass>(storage_class_index);
-  if (storage_class != SpvStorageClassWorkgroup &&
-      storage_class != SpvStorageClassCrossWorkgroup &&
-      storage_class != SpvStorageClassPrivate &&
-      storage_class != SpvStorageClassFunction &&
-      storage_class != SpvStorageClassRayPayloadKHR &&
-      storage_class != SpvStorageClassIncomingRayPayloadKHR &&
-      storage_class != SpvStorageClassHitAttributeKHR &&
-      storage_class != SpvStorageClassCallableDataKHR &&
-      storage_class != SpvStorageClassIncomingCallableDataKHR &&
-      storage_class != SpvStorageClassTaskPayloadWorkgroupEXT) {
-    bool storage_input_or_output = storage_class == SpvStorageClassInput ||
-                                   storage_class == SpvStorageClassOutput;
+  auto storage_class =
+      inst->GetOperandAs<spv::StorageClass>(storage_class_index);
+  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() == SpvDecorationBuiltIn) {
+        if (decoration.dec_type() == spv::Decoration::BuiltIn) {
           builtin = true;
           break;
         }
@@ -470,8 +480,8 @@
                   "can only be used with non-externally visible shader Storage "
                   "Classes: Workgroup, CrossWorkgroup, Private, Function, "
                   "Input, Output, RayPayloadKHR, IncomingRayPayloadKHR, "
-                  "HitAttributeKHR, CallableDataKHR, or "
-                  "IncomingCallableDataKHR";
+                  "HitAttributeKHR, CallableDataKHR, "
+                  "IncomingCallableDataKHR, or UniformConstant";
       }
     }
   }
@@ -482,18 +492,18 @@
            << "Invalid storage class for target environment";
   }
 
-  if (storage_class == SpvStorageClassGeneric) {
+  if (storage_class == spv::StorageClass::Generic) {
     return _.diag(SPV_ERROR_INVALID_BINARY, inst)
            << "OpVariable storage class cannot be Generic";
   }
 
-  if (inst->function() && storage_class != SpvStorageClassFunction) {
+  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 == SpvStorageClassFunction) {
+  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";
@@ -503,7 +513,7 @@
   // storage class.
   const auto result_storage_class_index = 1;
   const auto result_storage_class =
-      result_type->GetOperandAs<uint32_t>(result_storage_class_index);
+      result_type->GetOperandAs<spv::StorageClass>(result_storage_class_index);
   if (storage_class != result_storage_class) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "From SPIR-V spec, section 3.32.8 on OpVariable:\n"
@@ -513,16 +523,16 @@
 
   // Variable pointer related restrictions.
   const auto pointee = _.FindDef(result_type->word(3));
-  if (_.addressing_model() == SpvAddressingModelLogical &&
+  if (_.addressing_model() == spv::AddressingModel::Logical &&
       !_.options()->relax_logical_pointer) {
     // VariablePointersStorageBuffer is implied by VariablePointers.
-    if (pointee->opcode() == SpvOpTypePointer) {
-      if (!_.HasCapability(SpvCapabilityVariablePointersStorageBuffer)) {
+    if (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 != SpvStorageClassFunction &&
-                 storage_class != SpvStorageClassPrivate) {
+      } 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 "
@@ -534,8 +544,8 @@
   if (spvIsVulkanEnv(_.context()->target_env)) {
     // Vulkan Push Constant Interface section: Check type of PushConstant
     // variables.
-    if (storage_class == SpvStorageClassPushConstant) {
-      if (pointee->opcode() != SpvOpTypeStruct) {
+    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"
@@ -546,11 +556,12 @@
 
     // Vulkan Descriptor Set Interface: Check type of UniformConstant and
     // Uniform variables.
-    if (storage_class == SpvStorageClassUniformConstant) {
+    if (storage_class == spv::StorageClass::UniformConstant) {
       if (!IsAllowedTypeOrArrayOfSame(
               _, pointee,
-              {SpvOpTypeImage, SpvOpTypeSampler, SpvOpTypeSampledImage,
-               SpvOpTypeAccelerationStructureKHR})) {
+              {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"
@@ -562,8 +573,8 @@
       }
     }
 
-    if (storage_class == SpvStorageClassUniform) {
-      if (!IsAllowedTypeOrArrayOfSame(_, pointee, {SpvOpTypeStruct})) {
+    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"
@@ -575,8 +586,8 @@
       }
     }
 
-    if (storage_class == SpvStorageClassStorageBuffer) {
-      if (!IsAllowedTypeOrArrayOfSame(_, pointee, {SpvOpTypeStruct})) {
+    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"
@@ -589,9 +600,9 @@
     }
 
     // Check for invalid use of Invariant
-    if (storage_class != SpvStorageClassInput &&
-        storage_class != SpvStorageClassOutput) {
-      if (_.HasDecoration(inst->id(), SpvDecorationInvariant)) {
+    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 "
@@ -599,8 +610,8 @@
                   "environment.";
       }
       // Need to check if only the members in a struct are decorated
-      if (value_type && value_type->opcode() == SpvOpTypeStruct) {
-        if (_.HasDecoration(value_id, SpvDecorationInvariant)) {
+      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 "
@@ -612,10 +623,10 @@
 
     // Initializers in Vulkan are only allowed in some storage clases
     if (inst->operands().size() > 3) {
-      if (storage_class == SpvStorageClassWorkgroup) {
+      if (storage_class == spv::StorageClass::Workgroup) {
         auto init_id = inst->GetOperandAs<uint32_t>(3);
         auto init = _.FindDef(init_id);
-        if (init->opcode() != SpvOpConstantNull) {
+        if (init->opcode() != spv::Op::OpConstantNull) {
           return _.diag(SPV_ERROR_INVALID_ID, inst)
                  << _.VkErrorID(4734) << "OpVariable, <id> "
                  << _.getIdName(inst->id())
@@ -623,9 +634,9 @@
                     "Workgroup "
                     "storage class";
         }
-      } else if (storage_class != SpvStorageClassOutput &&
-                 storage_class != SpvStorageClassPrivate &&
-                 storage_class != SpvStorageClassFunction) {
+      } else if (storage_class != spv::StorageClass::Output &&
+                 storage_class != spv::StorageClass::Private &&
+                 storage_class != spv::StorageClass::Function) {
         return _.diag(SPV_ERROR_INVALID_ID, inst)
                << _.VkErrorID(4651) << "OpVariable, <id> "
                << _.getIdName(inst->id())
@@ -641,35 +652,40 @@
   }
 
   if (inst->operands().size() > 3) {
-    if (storage_class == SpvStorageClassTaskPayloadWorkgroupEXT) {
+    if (storage_class == spv::StorageClass::TaskPayloadWorkgroupEXT) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
              << "OpVariable, <id> " << _.getIdName(inst->id())
              << ", initializer are not allowed for TaskPayloadWorkgroupEXT";
     }
-    if (storage_class == SpvStorageClassInput) {
+    if (storage_class == spv::StorageClass::Input) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
              << "OpVariable, <id> " << _.getIdName(inst->id())
              << ", initializer are not allowed for Input";
     }
+    if (storage_class == spv::StorageClass::HitObjectAttributeNV) {
+      return _.diag(SPV_ERROR_INVALID_ID, inst)
+             << "OpVariable, <id> " << _.getIdName(inst->id())
+             << ", initializer are not allowed for HitObjectAttributeNV";
+    }
   }
 
-  if (storage_class == SpvStorageClassPhysicalStorageBuffer) {
+  if (storage_class == spv::StorageClass::PhysicalStorageBuffer) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "PhysicalStorageBuffer must not be used with OpVariable.";
   }
 
   auto pointee_base = pointee;
-  while (pointee_base->opcode() == SpvOpTypeArray) {
+  while (pointee_base->opcode() == spv::Op::OpTypeArray) {
     pointee_base = _.FindDef(pointee_base->GetOperandAs<uint32_t>(1u));
   }
-  if (pointee_base->opcode() == SpvOpTypePointer) {
-    if (pointee_base->GetOperandAs<uint32_t>(1u) ==
-        SpvStorageClassPhysicalStorageBuffer) {
+  if (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(), SpvDecorationAliasedPointer);
+          _.HasDecoration(inst->id(), spv::Decoration::AliasedPointer);
       bool foundRestrict =
-          _.HasDecoration(inst->id(), SpvDecorationRestrictPointer);
+          _.HasDecoration(inst->id(), spv::Decoration::RestrictPointer);
       if (!foundAliased && !foundRestrict) {
         return _.diag(SPV_ERROR_INVALID_ID, inst)
                << "OpVariable " << inst->id()
@@ -690,8 +706,8 @@
     // 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() == SpvOpTypeRuntimeArray) {
-      if (!_.HasCapability(SpvCapabilityRuntimeDescriptorArrayEXT)) {
+    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())
@@ -702,9 +718,9 @@
       } else {
         // A bare variable OpTypeRuntimeArray is allowed in this context, but
         // still need to check the storage class.
-        if (storage_class != SpvStorageClassStorageBuffer &&
-            storage_class != SpvStorageClassUniform &&
-            storage_class != SpvStorageClassUniformConstant) {
+        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 "
@@ -718,11 +734,11 @@
     // 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.
-    if (value_type && value_type->opcode() == SpvOpTypeStruct) {
+    if (value_type && value_type->opcode() == spv::Op::OpTypeStruct) {
       if (DoesStructContainRTA(_, value_type)) {
-        if (storage_class == SpvStorageClassStorageBuffer ||
-            storage_class == SpvStorageClassPhysicalStorageBuffer) {
-          if (!_.HasDecoration(value_id, SpvDecorationBlock)) {
+        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 "
@@ -730,8 +746,8 @@
                    << "has storage class StorageBuffer or "
                       "PhysicalStorageBuffer.";
           }
-        } else if (storage_class == SpvStorageClassUniform) {
-          if (!_.HasDecoration(value_id, SpvDecorationBufferBlock)) {
+        } 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 "
@@ -750,8 +766,8 @@
   }
 
   // Cooperative matrix types can only be allocated in Function or Private
-  if ((storage_class != SpvStorageClassFunction &&
-       storage_class != SpvStorageClassPrivate) &&
+  if ((storage_class != spv::StorageClass::Function &&
+       storage_class != spv::StorageClass::Private) &&
       ContainsCooperativeMatrix(_, pointee)) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "Cooperative matrix types (or types containing them) can only be "
@@ -760,57 +776,59 @@
               "parameters";
   }
 
-  if (_.HasCapability(SpvCapabilityShader)) {
+  if (_.HasCapability(spv::Capability::Shader)) {
     // Don't allow variables containing 16-bit elements without the appropriate
     // capabilities.
-    if ((!_.HasCapability(SpvCapabilityInt16) &&
-         _.ContainsSizedIntOrFloatType(value_id, SpvOpTypeInt, 16)) ||
-        (!_.HasCapability(SpvCapabilityFloat16) &&
-         _.ContainsSizedIntOrFloatType(value_id, SpvOpTypeFloat, 16))) {
+    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->opcode() == SpvOpTypePointer) {
-        storage_class = underlying_type->GetOperandAs<SpvStorageClass>(1u);
+      while (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, storage_class);
+          SPV_OPERAND_TYPE_STORAGE_CLASS, uint32_t(storage_class));
       switch (storage_class) {
-        case SpvStorageClassStorageBuffer:
-        case SpvStorageClassPhysicalStorageBuffer:
-          if (!_.HasCapability(SpvCapabilityStorageBuffer16BitAccess)) {
+        case spv::StorageClass::StorageBuffer:
+        case spv::StorageClass::PhysicalStorageBuffer:
+          if (!_.HasCapability(spv::Capability::StorageBuffer16BitAccess)) {
             storage_class_ok = false;
           }
           break;
-        case SpvStorageClassUniform:
+        case spv::StorageClass::Uniform:
           if (!_.HasCapability(
-                  SpvCapabilityUniformAndStorageBuffer16BitAccess)) {
-            if (underlying_type->opcode() == SpvOpTypeArray ||
-                underlying_type->opcode() == SpvOpTypeRuntimeArray) {
+                  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(SpvCapabilityStorageBuffer16BitAccess) ||
+            if (!_.HasCapability(spv::Capability::StorageBuffer16BitAccess) ||
                 !_.HasDecoration(underlying_type->id(),
-                                 SpvDecorationBufferBlock)) {
+                                 spv::Decoration::BufferBlock)) {
               storage_class_ok = false;
             }
           }
           break;
-        case SpvStorageClassPushConstant:
-          if (!_.HasCapability(SpvCapabilityStoragePushConstant16)) {
+        case spv::StorageClass::PushConstant:
+          if (!_.HasCapability(spv::Capability::StoragePushConstant16)) {
             storage_class_ok = false;
           }
           break;
-        case SpvStorageClassInput:
-        case SpvStorageClassOutput:
-          if (!_.HasCapability(SpvCapabilityStorageInputOutput16)) {
+        case spv::StorageClass::Input:
+        case spv::StorageClass::Output:
+          if (!_.HasCapability(spv::Capability::StorageInputOutput16)) {
             storage_class_ok = false;
           }
           break;
-        case SpvStorageClassWorkgroup:
-          if (!_.HasCapability(SpvCapabilityWorkgroupMemoryExplicitLayout16BitAccessKHR)) {
+        case spv::StorageClass::Workgroup:
+          if (!_.HasCapability(
+                  spv::Capability::
+                      WorkgroupMemoryExplicitLayout16BitAccessKHR)) {
             storage_class_ok = false;
           }
           break;
@@ -827,46 +845,48 @@
     }
     // Don't allow variables containing 8-bit elements without the appropriate
     // capabilities.
-    if (!_.HasCapability(SpvCapabilityInt8) &&
-        _.ContainsSizedIntOrFloatType(value_id, SpvOpTypeInt, 8)) {
+    if (!_.HasCapability(spv::Capability::Int8) &&
+        _.ContainsSizedIntOrFloatType(value_id, spv::Op::OpTypeInt, 8)) {
       auto underlying_type = value_type;
-      while (underlying_type->opcode() == SpvOpTypePointer) {
-        storage_class = underlying_type->GetOperandAs<SpvStorageClass>(1u);
+      while (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, storage_class);
+          SPV_OPERAND_TYPE_STORAGE_CLASS, uint32_t(storage_class));
       switch (storage_class) {
-        case SpvStorageClassStorageBuffer:
-        case SpvStorageClassPhysicalStorageBuffer:
-          if (!_.HasCapability(SpvCapabilityStorageBuffer8BitAccess)) {
+        case spv::StorageClass::StorageBuffer:
+        case spv::StorageClass::PhysicalStorageBuffer:
+          if (!_.HasCapability(spv::Capability::StorageBuffer8BitAccess)) {
             storage_class_ok = false;
           }
           break;
-        case SpvStorageClassUniform:
+        case spv::StorageClass::Uniform:
           if (!_.HasCapability(
-                  SpvCapabilityUniformAndStorageBuffer8BitAccess)) {
-            if (underlying_type->opcode() == SpvOpTypeArray ||
-                underlying_type->opcode() == SpvOpTypeRuntimeArray) {
+                  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(SpvCapabilityStorageBuffer8BitAccess) ||
+            if (!_.HasCapability(spv::Capability::StorageBuffer8BitAccess) ||
                 !_.HasDecoration(underlying_type->id(),
-                                 SpvDecorationBufferBlock)) {
+                                 spv::Decoration::BufferBlock)) {
               storage_class_ok = false;
             }
           }
           break;
-        case SpvStorageClassPushConstant:
-          if (!_.HasCapability(SpvCapabilityStoragePushConstant8)) {
+        case spv::StorageClass::PushConstant:
+          if (!_.HasCapability(spv::Capability::StoragePushConstant8)) {
             storage_class_ok = false;
           }
           break;
-        case SpvStorageClassWorkgroup:
-          if (!_.HasCapability(SpvCapabilityWorkgroupMemoryExplicitLayout8BitAccessKHR)) {
+        case spv::StorageClass::Workgroup:
+          if (!_.HasCapability(
+                  spv::Capability::
+                      WorkgroupMemoryExplicitLayout8BitAccessKHR)) {
             storage_class_ok = false;
           }
           break;
@@ -898,7 +918,7 @@
   const auto pointer_id = inst->GetOperandAs<uint32_t>(pointer_index);
   const auto pointer = _.FindDef(pointer_id);
   if (!pointer ||
-      ((_.addressing_model() == SpvAddressingModelLogical) &&
+      ((_.addressing_model() == spv::AddressingModel::Logical) &&
        ((!_.features().variable_pointers &&
          !spvOpcodeReturnsLogicalPointer(pointer->opcode())) ||
         (_.features().variable_pointers &&
@@ -909,14 +929,14 @@
   }
 
   const auto pointer_type = _.FindDef(pointer->type_id());
-  if (!pointer_type || pointer_type->opcode() != SpvOpTypePointer) {
+  if (!pointer_type || pointer_type->opcode() != spv::Op::OpTypePointer) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "OpLoad type for pointer <id> " << _.getIdName(pointer_id)
            << " is not a pointer type.";
   }
 
   uint32_t pointee_data_type;
-  uint32_t storage_class;
+  spv::StorageClass storage_class;
   if (!_.GetPointerTypeInfo(pointer_type->id(), &pointee_data_type,
                             &storage_class) ||
       result_type->id() != pointee_data_type) {
@@ -934,18 +954,20 @@
 
   if (auto error = CheckMemoryAccess(_, inst, 3)) return error;
 
-  if (_.HasCapability(SpvCapabilityShader) &&
+  if (_.HasCapability(spv::Capability::Shader) &&
       _.ContainsLimitedUseIntOrFloatType(inst->type_id()) &&
-      result_type->opcode() != SpvOpTypePointer) {
-    if (result_type->opcode() != SpvOpTypeInt &&
-        result_type->opcode() != SpvOpTypeFloat &&
-        result_type->opcode() != SpvOpTypeVector &&
-        result_type->opcode() != SpvOpTypeMatrix) {
+      result_type->opcode() != spv::Op::OpTypePointer) {
+    if (result_type->opcode() != spv::Op::OpTypeInt &&
+        result_type->opcode() != spv::Op::OpTypeFloat &&
+        result_type->opcode() != spv::Op::OpTypeVector &&
+        result_type->opcode() != spv::Op::OpTypeMatrix) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
              << "8- or 16-bit loads must be a scalar, vector or matrix type";
     }
   }
 
+  _.RegisterQCOMImageProcessingTextureConsumer(pointer_id, inst, nullptr);
+
   return SPV_SUCCESS;
 }
 
@@ -954,7 +976,7 @@
   const auto pointer_id = inst->GetOperandAs<uint32_t>(pointer_index);
   const auto pointer = _.FindDef(pointer_id);
   if (!pointer ||
-      (_.addressing_model() == SpvAddressingModelLogical &&
+      (_.addressing_model() == spv::AddressingModel::Logical &&
        ((!_.features().variable_pointers &&
          !spvOpcodeReturnsLogicalPointer(pointer->opcode())) ||
         (_.features().variable_pointers &&
@@ -964,14 +986,14 @@
            << " is not a logical pointer.";
   }
   const auto pointer_type = _.FindDef(pointer->type_id());
-  if (!pointer_type || pointer_type->opcode() != SpvOpTypePointer) {
+  if (!pointer_type || pointer_type->opcode() != spv::Op::OpTypePointer) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "OpStore type for pointer <id> " << _.getIdName(pointer_id)
            << " is not a pointer type.";
   }
   const auto type_id = pointer_type->GetOperandAs<uint32_t>(2);
   const auto type = _.FindDef(type_id);
-  if (!type || SpvOpTypeVoid == type->opcode()) {
+  if (!type || spv::Op::OpTypeVoid == type->opcode()) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "OpStore Pointer <id> " << _.getIdName(pointer_id)
            << "s type is void.";
@@ -980,29 +1002,29 @@
   // validate storage class
   {
     uint32_t data_type;
-    uint32_t storage_class;
+    spv::StorageClass storage_class;
     if (!_.GetPointerTypeInfo(pointer_type->id(), &data_type, &storage_class)) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
              << "OpStore Pointer <id> " << _.getIdName(pointer_id)
              << " is not pointer type";
     }
 
-    if (storage_class == SpvStorageClassUniformConstant ||
-        storage_class == SpvStorageClassInput ||
-        storage_class == SpvStorageClassPushConstant) {
+    if (storage_class == spv::StorageClass::UniformConstant ||
+        storage_class == spv::StorageClass::Input ||
+        storage_class == spv::StorageClass::PushConstant) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
              << "OpStore Pointer <id> " << _.getIdName(pointer_id)
              << " storage class is read-only";
-    } else if (storage_class == SpvStorageClassShaderRecordBufferKHR) {
+    } else if (storage_class == spv::StorageClass::ShaderRecordBufferKHR) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
              << "ShaderRecordBufferKHR Storage Class variables are read only";
-    } else if (storage_class == SpvStorageClassHitAttributeKHR) {
+    } else if (storage_class == spv::StorageClass::HitAttributeKHR) {
       std::string errorVUID = _.VkErrorID(4703);
       _.function(inst->function()->id())
           ->RegisterExecutionModelLimitation(
-              [errorVUID](SpvExecutionModel model, std::string* message) {
-                if (model == SpvExecutionModelAnyHitKHR ||
-                    model == SpvExecutionModelClosestHitKHR) {
+              [errorVUID](spv::ExecutionModel model, std::string* message) {
+                if (model == spv::ExecutionModel::AnyHitKHR ||
+                    model == spv::ExecutionModel::ClosestHitKHR) {
                   if (message) {
                     *message =
                         errorVUID +
@@ -1016,18 +1038,18 @@
     }
 
     if (spvIsVulkanEnv(_.context()->target_env) &&
-        storage_class == SpvStorageClassUniform) {
+        storage_class == spv::StorageClass::Uniform) {
       auto base_ptr = _.TracePointer(pointer);
-      if (base_ptr->opcode() == SpvOpVariable) {
+      if (base_ptr->opcode() == spv::Op::OpVariable) {
         // If it's not a variable a different check should catch the problem.
         auto base_type = _.FindDef(base_ptr->GetOperandAs<uint32_t>(0));
         // Get the pointed-to type.
         base_type = _.FindDef(base_type->GetOperandAs<uint32_t>(2u));
-        if (base_type->opcode() == SpvOpTypeArray ||
-            base_type->opcode() == SpvOpTypeRuntimeArray) {
+        if (base_type->opcode() == spv::Op::OpTypeArray ||
+            base_type->opcode() == spv::Op::OpTypeRuntimeArray) {
           base_type = _.FindDef(base_type->GetOperandAs<uint32_t>(1u));
         }
-        if (_.HasDecoration(base_type->id(), SpvDecorationBlock)) {
+        if (_.HasDecoration(base_type->id(), spv::Decoration::Block)) {
           return _.diag(SPV_ERROR_INVALID_ID, inst)
                  << _.VkErrorID(6925)
                  << "In the Vulkan environment, cannot store to Uniform Blocks";
@@ -1045,15 +1067,16 @@
            << " is not an object.";
   }
   const auto object_type = _.FindDef(object->type_id());
-  if (!object_type || SpvOpTypeVoid == object_type->opcode()) {
+  if (!object_type || spv::Op::OpTypeVoid == object_type->opcode()) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "OpStore Object <id> " << _.getIdName(object_id)
            << "s type is void.";
   }
 
   if (type->id() != object_type->id()) {
-    if (!_.options()->relax_struct_store || type->opcode() != SpvOpTypeStruct ||
-        object_type->opcode() != SpvOpTypeStruct) {
+    if (!_.options()->relax_struct_store ||
+        type->opcode() != spv::Op::OpTypeStruct ||
+        object_type->opcode() != spv::Op::OpTypeStruct) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
              << "OpStore Pointer <id> " << _.getIdName(pointer_id)
              << "s type does not match Object <id> "
@@ -1071,13 +1094,13 @@
 
   if (auto error = CheckMemoryAccess(_, inst, 2)) return error;
 
-  if (_.HasCapability(SpvCapabilityShader) &&
+  if (_.HasCapability(spv::Capability::Shader) &&
       _.ContainsLimitedUseIntOrFloatType(inst->type_id()) &&
-      object_type->opcode() != SpvOpTypePointer) {
-    if (object_type->opcode() != SpvOpTypeInt &&
-        object_type->opcode() != SpvOpTypeFloat &&
-        object_type->opcode() != SpvOpTypeVector &&
-        object_type->opcode() != SpvOpTypeMatrix) {
+      object_type->opcode() != spv::Op::OpTypePointer) {
+    if (object_type->opcode() != spv::Op::OpTypeInt &&
+        object_type->opcode() != spv::Op::OpTypeFloat &&
+        object_type->opcode() != spv::Op::OpTypeVector &&
+        object_type->opcode() != spv::Op::OpTypeMatrix) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
              << "8- or 16-bit stores must be a scalar, vector or matrix type";
     }
@@ -1088,9 +1111,10 @@
 
 spv_result_t ValidateCopyMemoryMemoryAccess(ValidationState_t& _,
                                             const Instruction* inst) {
-  assert(inst->opcode() == SpvOpCopyMemory ||
-         inst->opcode() == SpvOpCopyMemorySized);
-  const uint32_t first_access_index = inst->opcode() == SpvOpCopyMemory ? 2 : 3;
+  assert(inst->opcode() == spv::Op::OpCopyMemory ||
+         inst->opcode() == spv::Op::OpCopyMemorySized);
+  const uint32_t first_access_index =
+      inst->opcode() == spv::Op::OpCopyMemory ? 2 : 3;
   if (inst->operands().size() > first_access_index) {
     if (auto error = CheckMemoryAccess(_, inst, first_access_index))
       return error;
@@ -1108,21 +1132,23 @@
         //  make-visible.
         //  - the second is the source (read) access and it can't have
         //  make-available.
-        if (first_access & SpvMemoryAccessMakePointerVisibleKHRMask) {
+        if (first_access &
+            uint32_t(spv::MemoryAccessMask::MakePointerVisibleKHR)) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
                  << "Target memory access must not include "
                     "MakePointerVisibleKHR";
         }
         const auto second_access =
             inst->GetOperandAs<uint32_t>(second_access_index);
-        if (second_access & SpvMemoryAccessMakePointerAvailableKHRMask) {
+        if (second_access &
+            uint32_t(spv::MemoryAccessMask::MakePointerAvailableKHR)) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
                  << "Source memory access must not include "
                     "MakePointerAvailableKHR";
         }
       } else {
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << spvOpcodeString(static_cast<SpvOp>(inst->opcode()))
+               << spvOpcodeString(static_cast<spv::Op>(inst->opcode()))
                << " with two memory access operands requires SPIR-V 1.4 or "
                   "later";
       }
@@ -1152,7 +1178,7 @@
 
   const auto target_pointer_type = _.FindDef(target->type_id());
   if (!target_pointer_type ||
-      target_pointer_type->opcode() != SpvOpTypePointer) {
+      target_pointer_type->opcode() != spv::Op::OpTypePointer) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "Target operand <id> " << _.getIdName(target_id)
            << " is not a pointer.";
@@ -1160,16 +1186,16 @@
 
   const auto source_pointer_type = _.FindDef(source->type_id());
   if (!source_pointer_type ||
-      source_pointer_type->opcode() != SpvOpTypePointer) {
+      source_pointer_type->opcode() != spv::Op::OpTypePointer) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "Source operand <id> " << _.getIdName(source_id)
            << " is not a pointer.";
   }
 
-  if (inst->opcode() == SpvOpCopyMemory) {
+  if (inst->opcode() == spv::Op::OpCopyMemory) {
     const auto target_type =
         _.FindDef(target_pointer_type->GetOperandAs<uint32_t>(2));
-    if (!target_type || target_type->opcode() == SpvOpTypeVoid) {
+    if (!target_type || target_type->opcode() == spv::Op::OpTypeVoid) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
              << "Target operand <id> " << _.getIdName(target_id)
              << " cannot be a void pointer.";
@@ -1177,7 +1203,7 @@
 
     const auto source_type =
         _.FindDef(source_pointer_type->GetOperandAs<uint32_t>(2));
-    if (!source_type || source_type->opcode() == SpvOpTypeVoid) {
+    if (!source_type || source_type->opcode() == spv::Op::OpTypeVoid) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
              << "Source operand <id> " << _.getIdName(source_id)
              << " cannot be a void pointer.";
@@ -1207,11 +1233,11 @@
 
     bool is_zero = true;
     switch (size->opcode()) {
-      case SpvOpConstantNull:
+      case spv::Op::OpConstantNull:
         return _.diag(SPV_ERROR_INVALID_ID, inst)
                << "Size operand <id> " << _.getIdName(size_id)
                << " cannot be a constant zero.";
-      case SpvOpConstant:
+      case spv::Op::OpConstant:
         if (size_type->word(3) == 1 &&
             size->word(size->words().size() - 1) & 0x80000000) {
           return _.diag(SPV_ERROR_INVALID_ID, inst)
@@ -1236,10 +1262,10 @@
 
   // Get past the pointers to avoid checking a pointer copy.
   auto sub_type = _.FindDef(target_pointer_type->GetOperandAs<uint32_t>(2));
-  while (sub_type->opcode() == SpvOpTypePointer) {
+  while (sub_type->opcode() == spv::Op::OpTypePointer) {
     sub_type = _.FindDef(sub_type->GetOperandAs<uint32_t>(2));
   }
-  if (_.HasCapability(SpvCapabilityShader) &&
+  if (_.HasCapability(spv::Capability::Shader) &&
       _.ContainsLimitedUseIntOrFloatType(sub_type->id())) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "Cannot copy memory of objects containing 8- or 16-bit types";
@@ -1251,15 +1277,16 @@
 spv_result_t ValidateAccessChain(ValidationState_t& _,
                                  const Instruction* inst) {
   std::string instr_name =
-      "Op" + std::string(spvOpcodeString(static_cast<SpvOp>(inst->opcode())));
+      "Op" + std::string(spvOpcodeString(static_cast<spv::Op>(inst->opcode())));
 
   // The result type must be OpTypePointer.
   auto result_type = _.FindDef(inst->type_id());
-  if (SpvOpTypePointer != result_type->opcode()) {
+  if (spv::Op::OpTypePointer != result_type->opcode()) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "The Result Type of " << instr_name << " <id> "
            << _.getIdName(inst->id()) << " must be OpTypePointer. Found Op"
-           << spvOpcodeString(static_cast<SpvOp>(result_type->opcode())) << ".";
+           << spvOpcodeString(static_cast<spv::Op>(result_type->opcode()))
+           << ".";
   }
 
   // Result type is a pointer. Find out what it's pointing to.
@@ -1272,7 +1299,7 @@
   const auto base_id = inst->GetOperandAs<uint32_t>(base_index);
   const auto base = _.FindDef(base_id);
   const auto base_type = _.FindDef(base->type_id());
-  if (!base_type || SpvOpTypePointer != base_type->opcode()) {
+  if (!base_type || spv::Op::OpTypePointer != base_type->opcode()) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "The Base <id> " << _.getIdName(base_id) << " in " << instr_name
            << " instruction must be a pointer.";
@@ -1296,8 +1323,8 @@
   // The number of indexes passed to OpAccessChain may not exceed 255
   // The instruction includes 4 words + N words (for N indexes)
   size_t num_indexes = inst->words().size() - 4;
-  if (inst->opcode() == SpvOpPtrAccessChain ||
-      inst->opcode() == SpvOpInBoundsPtrAccessChain) {
+  if (inst->opcode() == spv::Op::OpPtrAccessChain ||
+      inst->opcode() == spv::Op::OpInBoundsPtrAccessChain) {
     // In pointer access chains, the element operand is required, but not
     // counted as an index.
     --num_indexes;
@@ -1317,8 +1344,8 @@
   // on. Once any non-composite type is reached, there must be no remaining
   // (unused) indexes.
   auto starting_index = 4;
-  if (inst->opcode() == SpvOpPtrAccessChain ||
-      inst->opcode() == SpvOpInBoundsPtrAccessChain) {
+  if (inst->opcode() == spv::Op::OpPtrAccessChain ||
+      inst->opcode() == spv::Op::OpInBoundsPtrAccessChain) {
     ++starting_index;
   }
   for (size_t i = starting_index; i < inst->words().size(); ++i) {
@@ -1327,26 +1354,27 @@
     auto cur_word_instr = _.FindDef(cur_word);
     // The index must be a scalar integer type (See OpAccessChain in the Spec.)
     auto index_type = _.FindDef(cur_word_instr->type_id());
-    if (!index_type || SpvOpTypeInt != index_type->opcode()) {
+    if (!index_type || spv::Op::OpTypeInt != index_type->opcode()) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
              << "Indexes passed to " << instr_name
              << " must be of type integer.";
     }
     switch (type_pointee->opcode()) {
-      case SpvOpTypeMatrix:
-      case SpvOpTypeVector:
-      case SpvOpTypeCooperativeMatrixNV:
-      case SpvOpTypeArray:
-      case SpvOpTypeRuntimeArray: {
-        // In OpTypeMatrix, OpTypeVector, SpvOpTypeCooperativeMatrixNV,
+      case spv::Op::OpTypeMatrix:
+      case spv::Op::OpTypeVector:
+      case spv::Op::OpTypeCooperativeMatrixNV:
+      case spv::Op::OpTypeCooperativeMatrixKHR:
+      case spv::Op::OpTypeArray:
+      case spv::Op::OpTypeRuntimeArray: {
+        // In OpTypeMatrix, OpTypeVector, spv::Op::OpTypeCooperativeMatrixNV,
         // OpTypeArray, and OpTypeRuntimeArray, word 2 is the Element Type.
         type_pointee = _.FindDef(type_pointee->word(2));
         break;
       }
-      case SpvOpTypeStruct: {
+      case spv::Op::OpTypeStruct: {
         // In case of structures, there is an additional constraint on the
         // index: the index must be an OpConstant.
-        if (SpvOpConstant != cur_word_instr->opcode()) {
+        if (spv::Op::OpConstant != cur_word_instr->opcode()) {
           return _.diag(SPV_ERROR_INVALID_ID, cur_word_instr)
                  << "The <id> passed to " << instr_name
                  << " to index into a "
@@ -1378,7 +1406,7 @@
       }
       default: {
         // Give an error. reached non-composite type while indexes still remain.
-        return _.diag(SPV_ERROR_INVALID_ID, cur_word_instr)
+        return _.diag(SPV_ERROR_INVALID_ID, inst)
                << instr_name
                << " reached non-composite type while indexes "
                   "still remain to be traversed.";
@@ -1390,11 +1418,12 @@
   if (type_pointee->id() != result_type_pointee->id()) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << instr_name << " result type (Op"
-           << spvOpcodeString(static_cast<SpvOp>(result_type_pointee->opcode()))
+           << spvOpcodeString(
+                  static_cast<spv::Op>(result_type_pointee->opcode()))
            << ") does not match the type that results from indexing into the "
               "base "
               "<id> (Op"
-           << spvOpcodeString(static_cast<SpvOp>(type_pointee->opcode()))
+           << spvOpcodeString(static_cast<spv::Op>(type_pointee->opcode()))
            << ").";
   }
 
@@ -1403,24 +1432,72 @@
 
 spv_result_t ValidatePtrAccessChain(ValidationState_t& _,
                                     const Instruction* inst) {
-  if (_.addressing_model() == SpvAddressingModelLogical) {
+  if (_.addressing_model() == spv::AddressingModel::Logical) {
     if (!_.features().variable_pointers) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Generating variable pointers requires capability "
              << "VariablePointers or VariablePointersStorageBuffer";
     }
   }
-  return ValidateAccessChain(_, inst);
+
+  // Need to call first, will make sure Base is a valid ID
+  if (auto error = ValidateAccessChain(_, inst)) return error;
+
+  const auto base_id = inst->GetOperandAs<uint32_t>(2);
+  const auto base = _.FindDef(base_id);
+  const auto base_type = _.FindDef(base->type_id());
+  const auto base_type_storage_class =
+      base_type->GetOperandAs<spv::StorageClass>(1);
+
+  if (_.HasCapability(spv::Capability::Shader) &&
+      (base_type_storage_class == spv::StorageClass::Uniform ||
+       base_type_storage_class == spv::StorageClass::StorageBuffer ||
+       base_type_storage_class == spv::StorageClass::PhysicalStorageBuffer ||
+       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)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "OpPtrAccessChain must have a Base whose type is decorated "
+              "with ArrayStride";
+  }
+
+  if (spvIsVulkanEnv(_.context()->target_env)) {
+    if (base_type_storage_class == spv::StorageClass::Workgroup) {
+      if (!_.HasCapability(spv::Capability::VariablePointers)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << _.VkErrorID(7651)
+               << "OpPtrAccessChain Base operand pointing to Workgroup "
+                  "storage class must use VariablePointers capability";
+      }
+    } else if (base_type_storage_class == spv::StorageClass::StorageBuffer) {
+      if (!_.features().variable_pointers) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << _.VkErrorID(7652)
+               << "OpPtrAccessChain Base operand pointing to StorageBuffer "
+                  "storage class must use VariablePointers or "
+                  "VariablePointersStorageBuffer capability";
+      }
+    } else if (base_type_storage_class !=
+               spv::StorageClass::PhysicalStorageBuffer) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << _.VkErrorID(7650)
+             << "OpPtrAccessChain Base operand must point to Workgroup, "
+                "StorageBuffer, or PhysicalStorageBuffer storage class";
+    }
+  }
+
+  return SPV_SUCCESS;
 }
 
 spv_result_t ValidateArrayLength(ValidationState_t& state,
                                  const Instruction* inst) {
   std::string instr_name =
-      "Op" + std::string(spvOpcodeString(static_cast<SpvOp>(inst->opcode())));
+      "Op" + std::string(spvOpcodeString(static_cast<spv::Op>(inst->opcode())));
 
   // Result type must be a 32-bit unsigned int.
   auto result_type = state.FindDef(inst->type_id());
-  if (result_type->opcode() != SpvOpTypeInt ||
+  if (result_type->opcode() != spv::Op::OpTypeInt ||
       result_type->GetOperandAs<uint32_t>(1) != 32 ||
       result_type->GetOperandAs<uint32_t>(2) != 0) {
     return state.diag(SPV_ERROR_INVALID_ID, inst)
@@ -1433,7 +1510,7 @@
   // last element is a runtime array.
   auto pointer = state.FindDef(inst->GetOperandAs<uint32_t>(2));
   auto pointer_type = state.FindDef(pointer->type_id());
-  if (pointer_type->opcode() != SpvOpTypePointer) {
+  if (pointer_type->opcode() != spv::Op::OpTypePointer) {
     return state.diag(SPV_ERROR_INVALID_ID, inst)
            << "The Structure's type in " << instr_name << " <id> "
            << state.getIdName(inst->id())
@@ -1441,7 +1518,7 @@
   }
 
   auto structure_type = state.FindDef(pointer_type->GetOperandAs<uint32_t>(2));
-  if (structure_type->opcode() != SpvOpTypeStruct) {
+  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())
@@ -1451,7 +1528,7 @@
   auto num_of_members = structure_type->operands().size() - 1;
   auto last_member =
       state.FindDef(structure_type->GetOperandAs<uint32_t>(num_of_members));
-  if (last_member->opcode() != SpvOpTypeRuntimeArray) {
+  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.";
@@ -1471,11 +1548,11 @@
 spv_result_t ValidateCooperativeMatrixLengthNV(ValidationState_t& state,
                                                const Instruction* inst) {
   std::string instr_name =
-      "Op" + std::string(spvOpcodeString(static_cast<SpvOp>(inst->opcode())));
+      "Op" + std::string(spvOpcodeString(static_cast<spv::Op>(inst->opcode())));
 
   // Result type must be a 32-bit unsigned int.
   auto result_type = state.FindDef(inst->type_id());
-  if (result_type->opcode() != SpvOpTypeInt ||
+  if (result_type->opcode() != spv::Op::OpTypeInt ||
       result_type->GetOperandAs<uint32_t>(1) != 32 ||
       result_type->GetOperandAs<uint32_t>(2) != 0) {
     return state.diag(SPV_ERROR_INVALID_ID, inst)
@@ -1484,9 +1561,15 @@
            << " 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 = state.FindDef(type_id);
-  if (type->opcode() != SpvOpTypeCooperativeMatrixNV) {
+  if (isKhr && type->opcode() != spv::Op::OpTypeCooperativeMatrixKHR) {
+    return state.diag(SPV_ERROR_INVALID_ID, inst)
+           << "The type in " << instr_name << " <id> "
+           << state.getIdName(type_id)
+           << " must be OpTypeCooperativeMatrixKHR.";
+  } else if (!isKhr && type->opcode() != spv::Op::OpTypeCooperativeMatrixNV) {
     return state.diag(SPV_ERROR_INVALID_ID, inst)
            << "The type in " << instr_name << " <id> "
            << state.getIdName(type_id) << " must be OpTypeCooperativeMatrixNV.";
@@ -1498,35 +1581,35 @@
                                                   const Instruction* inst) {
   uint32_t type_id;
   const char* opname;
-  if (inst->opcode() == SpvOpCooperativeMatrixLoadNV) {
+  if (inst->opcode() == spv::Op::OpCooperativeMatrixLoadNV) {
     type_id = inst->type_id();
-    opname = "SpvOpCooperativeMatrixLoadNV";
+    opname = "spv::Op::OpCooperativeMatrixLoadNV";
   } else {
     // get Object operand's type
     type_id = _.FindDef(inst->GetOperandAs<uint32_t>(1))->type_id();
-    opname = "SpvOpCooperativeMatrixStoreNV";
+    opname = "spv::Op::OpCooperativeMatrixStoreNV";
   }
 
   auto matrix_type = _.FindDef(type_id);
 
-  if (matrix_type->opcode() != SpvOpTypeCooperativeMatrixNV) {
-    if (inst->opcode() == SpvOpCooperativeMatrixLoadNV) {
+  if (matrix_type->opcode() != spv::Op::OpTypeCooperativeMatrixNV) {
+    if (inst->opcode() == spv::Op::OpCooperativeMatrixLoadNV) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
-             << "SpvOpCooperativeMatrixLoadNV Result Type <id> "
+             << "spv::Op::OpCooperativeMatrixLoadNV Result Type <id> "
              << _.getIdName(type_id) << " is not a cooperative matrix type.";
     } else {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
-             << "SpvOpCooperativeMatrixStoreNV Object type <id> "
+             << "spv::Op::OpCooperativeMatrixStoreNV Object type <id> "
              << _.getIdName(type_id) << " is not a cooperative matrix type.";
     }
   }
 
   const auto pointer_index =
-      (inst->opcode() == SpvOpCooperativeMatrixLoadNV) ? 2u : 0u;
+      (inst->opcode() == spv::Op::OpCooperativeMatrixLoadNV) ? 2u : 0u;
   const auto pointer_id = inst->GetOperandAs<uint32_t>(pointer_index);
   const auto pointer = _.FindDef(pointer_id);
   if (!pointer ||
-      ((_.addressing_model() == SpvAddressingModelLogical) &&
+      ((_.addressing_model() == spv::AddressingModel::Logical) &&
        ((!_.features().variable_pointers &&
          !spvOpcodeReturnsLogicalPointer(pointer->opcode())) ||
         (_.features().variable_pointers &&
@@ -1538,7 +1621,7 @@
 
   const auto pointer_type_id = pointer->type_id();
   const auto pointer_type = _.FindDef(pointer_type_id);
-  if (!pointer_type || pointer_type->opcode() != SpvOpTypePointer) {
+  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.";
@@ -1546,11 +1629,11 @@
 
   const auto storage_class_index = 1u;
   const auto storage_class =
-      pointer_type->GetOperandAs<uint32_t>(storage_class_index);
+      pointer_type->GetOperandAs<spv::StorageClass>(storage_class_index);
 
-  if (storage_class != SpvStorageClassWorkgroup &&
-      storage_class != SpvStorageClassStorageBuffer &&
-      storage_class != SpvStorageClassPhysicalStorageBuffer) {
+  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)
@@ -1567,7 +1650,7 @@
   }
 
   const auto stride_index =
-      (inst->opcode() == SpvOpCooperativeMatrixLoadNV) ? 3u : 2u;
+      (inst->opcode() == spv::Op::OpCooperativeMatrixLoadNV) ? 3u : 2u;
   const auto stride_id = inst->GetOperandAs<uint32_t>(stride_index);
   const auto stride = _.FindDef(stride_id);
   if (!stride || !_.IsIntScalarType(stride->type_id())) {
@@ -1577,7 +1660,7 @@
   }
 
   const auto colmajor_index =
-      (inst->opcode() == SpvOpCooperativeMatrixLoadNV) ? 4u : 3u;
+      (inst->opcode() == spv::Op::OpCooperativeMatrixLoadNV) ? 4u : 3u;
   const auto colmajor_id = inst->GetOperandAs<uint32_t>(colmajor_index);
   const auto colmajor = _.FindDef(colmajor_id);
   if (!colmajor || !_.IsBoolScalarType(colmajor->type_id()) ||
@@ -1589,7 +1672,114 @@
   }
 
   const auto memory_access_index =
-      (inst->opcode() == SpvOpCooperativeMatrixLoadNV) ? 5u : 4u;
+      (inst->opcode() == spv::Op::OpCooperativeMatrixLoadNV) ? 5u : 4u;
+  if (inst->operands().size() > memory_access_index) {
+    if (auto error = CheckMemoryAccess(_, inst, memory_access_index))
+      return error;
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateCooperativeMatrixLoadStoreKHR(ValidationState_t& _,
+                                                   const Instruction* inst) {
+  uint32_t type_id;
+  const char* opname;
+  if (inst->opcode() == spv::Op::OpCooperativeMatrixLoadKHR) {
+    type_id = inst->type_id();
+    opname = "spv::Op::OpCooperativeMatrixLoadKHR";
+  } else {
+    // get Object operand's type
+    type_id = _.FindDef(inst->GetOperandAs<uint32_t>(1))->type_id();
+    opname = "spv::Op::OpCooperativeMatrixStoreKHR";
+  }
+
+  auto matrix_type = _.FindDef(type_id);
+
+  if (matrix_type->opcode() != spv::Op::OpTypeCooperativeMatrixKHR) {
+    if (inst->opcode() == spv::Op::OpCooperativeMatrixLoadKHR) {
+      return _.diag(SPV_ERROR_INVALID_ID, inst)
+             << "spv::Op::OpCooperativeMatrixLoadKHR Result Type <id> "
+             << _.getIdName(type_id) << " is not a cooperative matrix type.";
+    } else {
+      return _.diag(SPV_ERROR_INVALID_ID, inst)
+             << "spv::Op::OpCooperativeMatrixStoreKHR Object type <id> "
+             << _.getIdName(type_id) << " is not a cooperative matrix type.";
+    }
+  }
+
+  const auto pointer_index =
+      (inst->opcode() == spv::Op::OpCooperativeMatrixLoadKHR) ? 2u : 0u;
+  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)
+           << _.VkErrorID(8973) << opname
+           << " storage class for pointer type <id> "
+           << _.getIdName(pointer_type_id)
+           << " is not Workgroup, StorageBuffer, or PhysicalStorageBuffer.";
+  }
+
+  const auto pointee_id = pointer_type->GetOperandAs<uint32_t>(2);
+  const auto pointee_type = _.FindDef(pointee_id);
+  if (!pointee_type || !(_.IsIntScalarOrVectorType(pointee_id) ||
+                         _.IsFloatScalarOrVectorType(pointee_id))) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << opname << " Pointer <id> " << _.getIdName(pointer->id())
+           << "s Type must be a scalar or vector type.";
+  }
+
+  const auto layout_index =
+      (inst->opcode() == spv::Op::OpCooperativeMatrixLoadKHR) ? 3u : 2u;
+  const auto colmajor_id = inst->GetOperandAs<uint32_t>(layout_index);
+  const auto colmajor = _.FindDef(colmajor_id);
+  if (!colmajor || !_.IsIntScalarType(colmajor->type_id()) ||
+      !(spvOpcodeIsConstant(colmajor->opcode()) ||
+        spvOpcodeIsSpecConstant(colmajor->opcode()))) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "MemoryLayout operand <id> " << _.getIdName(colmajor_id)
+           << " must be a 32-bit integer constant instruction.";
+  }
+
+  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())) {
+      return _.diag(SPV_ERROR_INVALID_ID, inst)
+             << "Stride operand <id> " << _.getIdName(stride_id)
+             << " must be a scalar integer type.";
+    }
+  }
+
+  const auto memory_access_index =
+      (inst->opcode() == spv::Op::OpCooperativeMatrixLoadKHR) ? 5u : 4u;
   if (inst->operands().size() > memory_access_index) {
     if (auto error = CheckMemoryAccess(_, inst, memory_access_index))
       return error;
@@ -1600,7 +1790,7 @@
 
 spv_result_t ValidatePtrComparison(ValidationState_t& _,
                                    const Instruction* inst) {
-  if (_.addressing_model() == SpvAddressingModelLogical &&
+  if (_.addressing_model() == spv::AddressingModel::Logical &&
       !_.features().variable_pointers) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "Instruction cannot for logical addressing model be used without "
@@ -1608,13 +1798,13 @@
   }
 
   const auto result_type = _.FindDef(inst->type_id());
-  if (inst->opcode() == SpvOpPtrDiff) {
-    if (!result_type || result_type->opcode() != SpvOpTypeInt) {
+  if (inst->opcode() == spv::Op::OpPtrDiff) {
+    if (!result_type || result_type->opcode() != spv::Op::OpTypeInt) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
              << "Result Type must be an integer scalar";
     }
   } else {
-    if (!result_type || result_type->opcode() != SpvOpTypeBool) {
+    if (!result_type || result_type->opcode() != spv::Op::OpTypeBool) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
              << "Result Type must be OpTypeBool";
     }
@@ -1627,25 +1817,26 @@
            << "The types of Operand 1 and Operand 2 must match";
   }
   const auto op1_type = _.FindDef(op1->type_id());
-  if (!op1_type || op1_type->opcode() != SpvOpTypePointer) {
+  if (!op1_type || op1_type->opcode() != spv::Op::OpTypePointer) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "Operand type must be a pointer";
   }
 
-  SpvStorageClass sc = op1_type->GetOperandAs<SpvStorageClass>(1u);
-  if (_.addressing_model() == SpvAddressingModelLogical) {
-    if (sc != SpvStorageClassWorkgroup && sc != SpvStorageClassStorageBuffer) {
+  spv::StorageClass sc = op1_type->GetOperandAs<spv::StorageClass>(1u);
+  if (_.addressing_model() == spv::AddressingModel::Logical) {
+    if (sc != spv::StorageClass::Workgroup &&
+        sc != spv::StorageClass::StorageBuffer) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
              << "Invalid pointer storage class";
     }
 
-    if (sc == SpvStorageClassWorkgroup &&
-        !_.HasCapability(SpvCapabilityVariablePointers)) {
+    if (sc == spv::StorageClass::Workgroup &&
+        !_.HasCapability(spv::Capability::VariablePointers)) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
              << "Workgroup storage class pointer requires VariablePointers "
                 "capability to be specified";
     }
-  } else if (sc == SpvStorageClassPhysicalStorageBuffer) {
+  } else if (sc == spv::StorageClass::PhysicalStorageBuffer) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "Cannot use a pointer in the PhysicalStorageBuffer storage class";
   }
@@ -1657,45 +1848,51 @@
 
 spv_result_t MemoryPass(ValidationState_t& _, const Instruction* inst) {
   switch (inst->opcode()) {
-    case SpvOpVariable:
+    case spv::Op::OpVariable:
       if (auto error = ValidateVariable(_, inst)) return error;
       break;
-    case SpvOpLoad:
+    case spv::Op::OpLoad:
       if (auto error = ValidateLoad(_, inst)) return error;
       break;
-    case SpvOpStore:
+    case spv::Op::OpStore:
       if (auto error = ValidateStore(_, inst)) return error;
       break;
-    case SpvOpCopyMemory:
-    case SpvOpCopyMemorySized:
+    case spv::Op::OpCopyMemory:
+    case spv::Op::OpCopyMemorySized:
       if (auto error = ValidateCopyMemory(_, inst)) return error;
       break;
-    case SpvOpPtrAccessChain:
+    case spv::Op::OpPtrAccessChain:
       if (auto error = ValidatePtrAccessChain(_, inst)) return error;
       break;
-    case SpvOpAccessChain:
-    case SpvOpInBoundsAccessChain:
-    case SpvOpInBoundsPtrAccessChain:
+    case spv::Op::OpAccessChain:
+    case spv::Op::OpInBoundsAccessChain:
+    case spv::Op::OpInBoundsPtrAccessChain:
       if (auto error = ValidateAccessChain(_, inst)) return error;
       break;
-    case SpvOpArrayLength:
+    case spv::Op::OpArrayLength:
       if (auto error = ValidateArrayLength(_, inst)) return error;
       break;
-    case SpvOpCooperativeMatrixLoadNV:
-    case SpvOpCooperativeMatrixStoreNV:
+    case spv::Op::OpCooperativeMatrixLoadNV:
+    case spv::Op::OpCooperativeMatrixStoreNV:
       if (auto error = ValidateCooperativeMatrixLoadStoreNV(_, inst))
         return error;
       break;
-    case SpvOpCooperativeMatrixLengthNV:
+    case spv::Op::OpCooperativeMatrixLengthKHR:
+    case spv::Op::OpCooperativeMatrixLengthNV:
       if (auto error = ValidateCooperativeMatrixLengthNV(_, inst)) return error;
       break;
-    case SpvOpPtrEqual:
-    case SpvOpPtrNotEqual:
-    case SpvOpPtrDiff:
+    case spv::Op::OpCooperativeMatrixLoadKHR:
+    case spv::Op::OpCooperativeMatrixStoreKHR:
+      if (auto error = ValidateCooperativeMatrixLoadStoreKHR(_, inst))
+        return error;
+      break;
+    case spv::Op::OpPtrEqual:
+    case spv::Op::OpPtrNotEqual:
+    case spv::Op::OpPtrDiff:
       if (auto error = ValidatePtrComparison(_, inst)) return error;
       break;
-    case SpvOpImageTexelPointer:
-    case SpvOpGenericPtrMemSemantics:
+    case spv::Op::OpImageTexelPointer:
+    case spv::Op::OpGenericPtrMemSemantics:
     default:
       break;
   }
diff --git a/source/val/validate_memory_semantics.cpp b/source/val/validate_memory_semantics.cpp
index d918931..c4f22a6 100644
--- a/source/val/validate_memory_semantics.cpp
+++ b/source/val/validate_memory_semantics.cpp
@@ -14,7 +14,6 @@
 
 #include "source/val/validate_memory_semantics.h"
 
-#include "source/diagnostic.h"
 #include "source/spirv_target_env.h"
 #include "source/util/bitutils.h"
 #include "source/val/instruction.h"
@@ -27,7 +26,7 @@
                                      const Instruction* inst,
                                      uint32_t operand_index,
                                      uint32_t memory_scope) {
-  const SpvOp opcode = inst->opcode();
+  const spv::Op opcode = inst->opcode();
   const auto id = inst->GetOperandAs<const uint32_t>(operand_index);
   bool is_int32 = false, is_const_int32 = false;
   uint32_t value = 0;
@@ -40,15 +39,15 @@
   }
 
   if (!is_const_int32) {
-    if (_.HasCapability(SpvCapabilityShader) &&
-        !_.HasCapability(SpvCapabilityCooperativeMatrixNV)) {
+    if (_.HasCapability(spv::Capability::Shader) &&
+        !_.HasCapability(spv::Capability::CooperativeMatrixNV)) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Memory Semantics ids must be OpConstant when Shader "
                 "capability is present";
     }
 
-    if (_.HasCapability(SpvCapabilityShader) &&
-        _.HasCapability(SpvCapabilityCooperativeMatrixNV) &&
+    if (_.HasCapability(spv::Capability::Shader) &&
+        _.HasCapability(spv::Capability::CooperativeMatrixNV) &&
         !spvOpcodeIsConstant(_.GetIdOpcode(id))) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Memory Semantics must be a constant instruction when "
@@ -58,9 +57,10 @@
   }
 
   const size_t num_memory_order_set_bits = spvtools::utils::CountSetBits(
-      value & (SpvMemorySemanticsAcquireMask | SpvMemorySemanticsReleaseMask |
-               SpvMemorySemanticsAcquireReleaseMask |
-               SpvMemorySemanticsSequentiallyConsistentMask));
+      value & uint32_t(spv::MemorySemanticsMask::Acquire |
+                       spv::MemorySemanticsMask::Release |
+                       spv::MemorySemanticsMask::AcquireRelease |
+                       spv::MemorySemanticsMask::SequentiallyConsistent));
 
   if (num_memory_order_set_bits > 1) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
@@ -71,40 +71,40 @@
               "SequentiallyConsistent";
   }
 
-  if (_.memory_model() == SpvMemoryModelVulkanKHR &&
-      value & SpvMemorySemanticsSequentiallyConsistentMask) {
+  if (_.memory_model() == spv::MemoryModel::VulkanKHR &&
+      value & uint32_t(spv::MemorySemanticsMask::SequentiallyConsistent)) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "SequentiallyConsistent memory "
               "semantics cannot be used with "
               "the VulkanKHR memory model.";
   }
 
-  if (value & SpvMemorySemanticsMakeAvailableKHRMask &&
-      !_.HasCapability(SpvCapabilityVulkanMemoryModelKHR)) {
+  if (value & uint32_t(spv::MemorySemanticsMask::MakeAvailableKHR) &&
+      !_.HasCapability(spv::Capability::VulkanMemoryModelKHR)) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << spvOpcodeString(opcode)
            << ": Memory Semantics MakeAvailableKHR requires capability "
            << "VulkanMemoryModelKHR";
   }
 
-  if (value & SpvMemorySemanticsMakeVisibleKHRMask &&
-      !_.HasCapability(SpvCapabilityVulkanMemoryModelKHR)) {
+  if (value & uint32_t(spv::MemorySemanticsMask::MakeVisibleKHR) &&
+      !_.HasCapability(spv::Capability::VulkanMemoryModelKHR)) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << spvOpcodeString(opcode)
            << ": Memory Semantics MakeVisibleKHR requires capability "
            << "VulkanMemoryModelKHR";
   }
 
-  if (value & SpvMemorySemanticsOutputMemoryKHRMask &&
-      !_.HasCapability(SpvCapabilityVulkanMemoryModelKHR)) {
+  if (value & uint32_t(spv::MemorySemanticsMask::OutputMemoryKHR) &&
+      !_.HasCapability(spv::Capability::VulkanMemoryModelKHR)) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << spvOpcodeString(opcode)
            << ": Memory Semantics OutputMemoryKHR requires capability "
            << "VulkanMemoryModelKHR";
   }
 
-  if (value & SpvMemorySemanticsVolatileMask) {
-    if (!_.HasCapability(SpvCapabilityVulkanMemoryModelKHR)) {
+  if (value & uint32_t(spv::MemorySemanticsMask::Volatile)) {
+    if (!_.HasCapability(spv::Capability::VulkanMemoryModelKHR)) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << spvOpcodeString(opcode)
              << ": Memory Semantics Volatile requires capability "
@@ -118,26 +118,27 @@
     }
   }
 
-  if (value & SpvMemorySemanticsUniformMemoryMask &&
-      !_.HasCapability(SpvCapabilityShader)) {
+  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 SpvCapabilityAtomicStorage is intentionally not done here. See
-  // https://github.com/KhronosGroup/glslang/issues/1618 for the reasoning why.
+  // Checking for spv::Capability::AtomicStorage is intentionally not done here.
+  // See https://github.com/KhronosGroup/glslang/issues/1618 for the reasoning
+  // why.
 
-  if (value & (SpvMemorySemanticsMakeAvailableKHRMask |
-               SpvMemorySemanticsMakeVisibleKHRMask)) {
+  if (value & uint32_t(spv::MemorySemanticsMask::MakeAvailableKHR |
+                       spv::MemorySemanticsMask::MakeVisibleKHR)) {
     const bool includes_storage_class =
-        value & (SpvMemorySemanticsUniformMemoryMask |
-                 SpvMemorySemanticsSubgroupMemoryMask |
-                 SpvMemorySemanticsWorkgroupMemoryMask |
-                 SpvMemorySemanticsCrossWorkgroupMemoryMask |
-                 SpvMemorySemanticsAtomicCounterMemoryMask |
-                 SpvMemorySemanticsImageMemoryMask |
-                 SpvMemorySemanticsOutputMemoryKHRMask);
+        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) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
@@ -146,18 +147,18 @@
     }
   }
 
-  if (value & SpvMemorySemanticsMakeVisibleKHRMask &&
-      !(value & (SpvMemorySemanticsAcquireMask |
-                 SpvMemorySemanticsAcquireReleaseMask))) {
+  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";
   }
 
-  if (value & SpvMemorySemanticsMakeAvailableKHRMask &&
-      !(value & (SpvMemorySemanticsReleaseMask |
-                 SpvMemorySemanticsAcquireReleaseMask))) {
+  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 "
@@ -166,12 +167,12 @@
 
   if (spvIsVulkanEnv(_.context()->target_env)) {
     const bool includes_storage_class =
-        value & (SpvMemorySemanticsUniformMemoryMask |
-                 SpvMemorySemanticsWorkgroupMemoryMask |
-                 SpvMemorySemanticsImageMemoryMask |
-                 SpvMemorySemanticsOutputMemoryKHRMask);
+        value & uint32_t(spv::MemorySemanticsMask::UniformMemory |
+                         spv::MemorySemanticsMask::WorkgroupMemory |
+                         spv::MemorySemanticsMask::ImageMemory |
+                         spv::MemorySemanticsMask::OutputMemoryKHR);
 
-    if (opcode == SpvOpMemoryBarrier && !num_memory_order_set_bits) {
+    if (opcode == spv::Op::OpMemoryBarrier && !num_memory_order_set_bits) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << _.VkErrorID(4732) << spvOpcodeString(opcode)
              << ": Vulkan specification requires Memory Semantics to have "
@@ -179,13 +180,15 @@
                 "of the following bits set: Acquire, Release, "
                 "AcquireRelease "
                 "or SequentiallyConsistent";
-    } else if (opcode != SpvOpMemoryBarrier && num_memory_order_set_bits) {
+    } 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 && memory_value == SpvScopeInvocation) {
+      if (memory_is_int32 &&
+          spv::Scope(memory_value) == spv::Scope::Invocation) {
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
                << _.VkErrorID(4641) << spvOpcodeString(opcode)
                << ": Vulkan specification requires Memory Semantics to be None "
@@ -193,7 +196,7 @@
       }
     }
 
-    if (opcode == SpvOpMemoryBarrier && !includes_storage_class) {
+    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 "
@@ -202,7 +205,7 @@
 
 #if 0
     // TODO(atgoo@github.com): this check fails Vulkan CTS, reenable once fixed.
-    if (opcode == SpvOpControlBarrier && value && !includes_storage_class) {
+    if (opcode == spv::Op::OpControlBarrier && value && !includes_storage_class) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << spvOpcodeString(opcode)
              << ": expected Memory Semantics to include a Vulkan-supported "
@@ -211,18 +214,18 @@
 #endif
   }
 
-  if (opcode == SpvOpAtomicFlagClear &&
-      (value & SpvMemorySemanticsAcquireMask ||
-       value & SpvMemorySemanticsAcquireReleaseMask)) {
+  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 == SpvOpAtomicCompareExchange && operand_index == 5 &&
-      (value & SpvMemorySemanticsReleaseMask ||
-       value & SpvMemorySemanticsAcquireReleaseMask)) {
+  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 "
@@ -231,20 +234,20 @@
   }
 
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    if (opcode == SpvOpAtomicLoad &&
-        (value & SpvMemorySemanticsReleaseMask ||
-         value & SpvMemorySemanticsAcquireReleaseMask ||
-         value & SpvMemorySemanticsSequentiallyConsistentMask)) {
+    if (opcode == spv::Op::OpAtomicLoad &&
+        (value & uint32_t(spv::MemorySemanticsMask::Release) ||
+         value & uint32_t(spv::MemorySemanticsMask::AcquireRelease) ||
+         value & uint32_t(spv::MemorySemanticsMask::SequentiallyConsistent))) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << _.VkErrorID(4731)
              << "Vulkan spec disallows OpAtomicLoad with Memory Semantics "
                 "Release, AcquireRelease and SequentiallyConsistent";
     }
 
-    if (opcode == SpvOpAtomicStore &&
-        (value & SpvMemorySemanticsAcquireMask ||
-         value & SpvMemorySemanticsAcquireReleaseMask ||
-         value & SpvMemorySemanticsSequentiallyConsistentMask)) {
+    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 "
diff --git a/source/val/validate_mesh_shading.cpp b/source/val/validate_mesh_shading.cpp
index a7f0726..e569e25 100644
--- a/source/val/validate_mesh_shading.cpp
+++ b/source/val/validate_mesh_shading.cpp
@@ -23,13 +23,13 @@
 namespace val {
 
 spv_result_t MeshShadingPass(ValidationState_t& _, const Instruction* inst) {
-  const SpvOp opcode = inst->opcode();
+  const spv::Op opcode = inst->opcode();
   switch (opcode) {
-    case SpvOpEmitMeshTasksEXT: {
+    case spv::Op::OpEmitMeshTasksEXT: {
       _.function(inst->function()->id())
           ->RegisterExecutionModelLimitation(
-              [](SpvExecutionModel model, std::string* message) {
-                if (model != SpvExecutionModelTaskEXT) {
+              [](spv::ExecutionModel model, std::string* message) {
+                if (model != spv::ExecutionModel::TaskEXT) {
                   if (message) {
                     *message =
                         "OpEmitMeshTasksEXT requires TaskEXT execution model";
@@ -62,12 +62,12 @@
 
       if (inst->operands().size() == 4) {
         const auto payload = _.FindDef(inst->GetOperandAs<uint32_t>(3));
-        if (payload->opcode() != SpvOpVariable) {
+        if (payload->opcode() != spv::Op::OpVariable) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
                  << "Payload must be the result of a OpVariable";
         }
-        if (SpvStorageClass(payload->GetOperandAs<uint32_t>(2)) !=
-            SpvStorageClassTaskPayloadWorkgroupEXT) {
+        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";
@@ -76,11 +76,11 @@
       break;
     }
 
-    case SpvOpSetMeshOutputsEXT: {
+    case spv::Op::OpSetMeshOutputsEXT: {
       _.function(inst->function()->id())
           ->RegisterExecutionModelLimitation(
-              [](SpvExecutionModel model, std::string* message) {
-                if (model != SpvExecutionModelMeshEXT) {
+              [](spv::ExecutionModel model, std::string* message) {
+                if (model != spv::ExecutionModel::MeshEXT) {
                   if (message) {
                     *message =
                         "OpSetMeshOutputsEXT requires MeshEXT execution model";
@@ -107,7 +107,7 @@
       break;
     }
 
-    case SpvOpWritePackedPrimitiveIndices4x8NV: {
+    case spv::Op::OpWritePackedPrimitiveIndices4x8NV: {
       // No validation rules (for the moment).
       break;
     }
diff --git a/source/val/validate_misc.cpp b/source/val/validate_misc.cpp
index 5acc21e..d71fd2d 100644
--- a/source/val/validate_misc.cpp
+++ b/source/val/validate_misc.cpp
@@ -30,7 +30,7 @@
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "Cannot create undefined values with void type";
   }
-  if (_.HasCapability(SpvCapabilityShader) &&
+  if (_.HasCapability(spv::Capability::Shader) &&
       _.ContainsLimitedUseIntOrFloatType(inst->type_id()) &&
       !_.IsPointerType(inst->type_id())) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
@@ -50,7 +50,8 @@
   bool is_int32 = false, is_const_int32 = false;
   uint32_t value = 0;
   std::tie(is_int32, is_const_int32, value) = _.EvalInt32IfConst(scope);
-  if (is_const_int32 && value != SpvScopeSubgroup && value != SpvScopeDevice) {
+  if (is_const_int32 && spv::Scope(value) != spv::Scope::Subgroup &&
+      spv::Scope(value) != spv::Scope::Device) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << _.VkErrorID(4652) << "Scope must be Subgroup or Device";
   }
@@ -104,18 +105,18 @@
 
 spv_result_t MiscPass(ValidationState_t& _, const Instruction* inst) {
   switch (inst->opcode()) {
-    case SpvOpUndef:
+    case spv::Op::OpUndef:
       if (auto error = ValidateUndef(_, inst)) return error;
       break;
     default:
       break;
   }
   switch (inst->opcode()) {
-    case SpvOpBeginInvocationInterlockEXT:
-    case SpvOpEndInvocationInterlockEXT:
+    case spv::Op::OpBeginInvocationInterlockEXT:
+    case spv::Op::OpEndInvocationInterlockEXT:
       _.function(inst->function()->id())
           ->RegisterExecutionModelLimitation(
-              SpvExecutionModelFragment,
+              spv::ExecutionModel::Fragment,
               "OpBeginInvocationInterlockEXT/OpEndInvocationInterlockEXT "
               "require Fragment execution model");
 
@@ -126,14 +127,14 @@
             const auto* execution_modes =
                 state.GetExecutionModes(entry_point->id());
 
-            auto find_interlock = [](const SpvExecutionMode& mode) {
+            auto find_interlock = [](const spv::ExecutionMode& mode) {
               switch (mode) {
-                case SpvExecutionModePixelInterlockOrderedEXT:
-                case SpvExecutionModePixelInterlockUnorderedEXT:
-                case SpvExecutionModeSampleInterlockOrderedEXT:
-                case SpvExecutionModeSampleInterlockUnorderedEXT:
-                case SpvExecutionModeShadingRateInterlockOrderedEXT:
-                case SpvExecutionModeShadingRateInterlockUnorderedEXT:
+                case spv::ExecutionMode::PixelInterlockOrderedEXT:
+                case spv::ExecutionMode::PixelInterlockUnorderedEXT:
+                case spv::ExecutionMode::SampleInterlockOrderedEXT:
+                case spv::ExecutionMode::SampleInterlockUnorderedEXT:
+                case spv::ExecutionMode::ShadingRateInterlockOrderedEXT:
+                case spv::ExecutionMode::ShadingRateInterlockUnorderedEXT:
                   return true;
                 default:
                   return false;
@@ -156,18 +157,18 @@
             return true;
           });
       break;
-    case SpvOpDemoteToHelperInvocationEXT:
+    case spv::Op::OpDemoteToHelperInvocationEXT:
       _.function(inst->function()->id())
           ->RegisterExecutionModelLimitation(
-              SpvExecutionModelFragment,
+              spv::ExecutionModel::Fragment,
               "OpDemoteToHelperInvocationEXT requires Fragment execution "
               "model");
       break;
-    case SpvOpIsHelperInvocationEXT: {
+    case spv::Op::OpIsHelperInvocationEXT: {
       const uint32_t result_type = inst->type_id();
       _.function(inst->function()->id())
           ->RegisterExecutionModelLimitation(
-              SpvExecutionModelFragment,
+              spv::ExecutionModel::Fragment,
               "OpIsHelperInvocationEXT requires Fragment execution model");
       if (!_.IsBoolScalarType(result_type))
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
@@ -175,17 +176,17 @@
                << spvOpcodeString(inst->opcode());
       break;
     }
-    case SpvOpReadClockKHR:
+    case spv::Op::OpReadClockKHR:
       if (auto error = ValidateShaderClock(_, inst)) {
         return error;
       }
       break;
-    case SpvOpAssumeTrueKHR:
+    case spv::Op::OpAssumeTrueKHR:
       if (auto error = ValidateAssumeTrue(_, inst)) {
         return error;
       }
       break;
-    case SpvOpExpectKHR:
+    case spv::Op::OpExpectKHR:
       if (auto error = ValidateExpect(_, inst)) {
         return error;
       }
diff --git a/source/val/validate_mode_setting.cpp b/source/val/validate_mode_setting.cpp
index 672192b..d757d4f 100644
--- a/source/val/validate_mode_setting.cpp
+++ b/source/val/validate_mode_setting.cpp
@@ -27,16 +27,16 @@
 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);
-  if (!entry_point || SpvOpFunction != entry_point->opcode()) {
+  if (!entry_point || spv::Op::OpFunction != entry_point->opcode()) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "OpEntryPoint Entry Point <id> " << _.getIdName(entry_point_id)
            << " is not a function.";
   }
 
   // Only check the shader execution models
-  const SpvExecutionModel execution_model =
-      inst->GetOperandAs<SpvExecutionModel>(0);
-  if (execution_model != SpvExecutionModelKernel) {
+  const spv::ExecutionModel execution_model =
+      inst->GetOperandAs<spv::ExecutionModel>(0);
+  if (execution_model != spv::ExecutionModel::Kernel) {
     const auto entry_point_type_id = entry_point->GetOperandAs<uint32_t>(3);
     const auto entry_point_type = _.FindDef(entry_point_type_id);
     if (!entry_point_type || 3 != entry_point_type->words().size()) {
@@ -48,7 +48,7 @@
   }
 
   auto return_type = _.FindDef(entry_point->type_id());
-  if (!return_type || SpvOpTypeVoid != return_type->opcode()) {
+  if (!return_type || spv::Op::OpTypeVoid != return_type->opcode()) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << _.VkErrorID(4633) << "OpEntryPoint Entry Point <id> "
            << _.getIdName(entry_point_id)
@@ -56,31 +56,31 @@
   }
 
   const auto* execution_modes = _.GetExecutionModes(entry_point_id);
-  if (_.HasCapability(SpvCapabilityShader)) {
+  if (_.HasCapability(spv::Capability::Shader)) {
     switch (execution_model) {
-      case SpvExecutionModelFragment:
+      case spv::ExecutionModel::Fragment:
         if (execution_modes &&
-            execution_modes->count(SpvExecutionModeOriginUpperLeft) &&
-            execution_modes->count(SpvExecutionModeOriginLowerLeft)) {
+            execution_modes->count(spv::ExecutionMode::OriginUpperLeft) &&
+            execution_modes->count(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(SpvExecutionModeOriginUpperLeft) &&
-             !execution_modes->count(SpvExecutionModeOriginLowerLeft))) {
+            (!execution_modes->count(spv::ExecutionMode::OriginUpperLeft) &&
+             !execution_modes->count(spv::ExecutionMode::OriginLowerLeft))) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
                  << "Fragment execution model entry points require either an "
                     "OriginUpperLeft or OriginLowerLeft execution mode.";
         }
         if (execution_modes &&
             1 < std::count_if(execution_modes->begin(), execution_modes->end(),
-                              [](const SpvExecutionMode& mode) {
+                              [](const spv::ExecutionMode& mode) {
                                 switch (mode) {
-                                  case SpvExecutionModeDepthGreater:
-                                  case SpvExecutionModeDepthLess:
-                                  case SpvExecutionModeDepthUnchanged:
+                                  case spv::ExecutionMode::DepthGreater:
+                                  case spv::ExecutionMode::DepthLess:
+                                  case spv::ExecutionMode::DepthUnchanged:
                                     return true;
                                   default:
                                     return false;
@@ -94,14 +94,15 @@
         if (execution_modes &&
             1 < std::count_if(
                     execution_modes->begin(), execution_modes->end(),
-                    [](const SpvExecutionMode& mode) {
+                    [](const spv::ExecutionMode& mode) {
                       switch (mode) {
-                        case SpvExecutionModePixelInterlockOrderedEXT:
-                        case SpvExecutionModePixelInterlockUnorderedEXT:
-                        case SpvExecutionModeSampleInterlockOrderedEXT:
-                        case SpvExecutionModeSampleInterlockUnorderedEXT:
-                        case SpvExecutionModeShadingRateInterlockOrderedEXT:
-                        case SpvExecutionModeShadingRateInterlockUnorderedEXT:
+                        case spv::ExecutionMode::PixelInterlockOrderedEXT:
+                        case spv::ExecutionMode::PixelInterlockUnorderedEXT:
+                        case spv::ExecutionMode::SampleInterlockOrderedEXT:
+                        case spv::ExecutionMode::SampleInterlockUnorderedEXT:
+                        case spv::ExecutionMode::ShadingRateInterlockOrderedEXT:
+                        case spv::ExecutionMode::
+                            ShadingRateInterlockUnorderedEXT:
                           return true;
                         default:
                           return false;
@@ -114,11 +115,11 @@
         if (execution_modes &&
             1 < std::count_if(
                     execution_modes->begin(), execution_modes->end(),
-                    [](const SpvExecutionMode& mode) {
+                    [](const spv::ExecutionMode& mode) {
                       switch (mode) {
-                        case SpvExecutionModeStencilRefUnchangedFrontAMD:
-                        case SpvExecutionModeStencilRefLessFrontAMD:
-                        case SpvExecutionModeStencilRefGreaterFrontAMD:
+                        case spv::ExecutionMode::StencilRefUnchangedFrontAMD:
+                        case spv::ExecutionMode::StencilRefLessFrontAMD:
+                        case spv::ExecutionMode::StencilRefGreaterFrontAMD:
                           return true;
                         default:
                           return false;
@@ -133,11 +134,11 @@
         if (execution_modes &&
             1 < std::count_if(
                     execution_modes->begin(), execution_modes->end(),
-                    [](const SpvExecutionMode& mode) {
+                    [](const spv::ExecutionMode& mode) {
                       switch (mode) {
-                        case SpvExecutionModeStencilRefUnchangedBackAMD:
-                        case SpvExecutionModeStencilRefLessBackAMD:
-                        case SpvExecutionModeStencilRefGreaterBackAMD:
+                        case spv::ExecutionMode::StencilRefUnchangedBackAMD:
+                        case spv::ExecutionMode::StencilRefLessBackAMD:
+                        case spv::ExecutionMode::StencilRefGreaterBackAMD:
                           return true;
                         default:
                           return false;
@@ -150,20 +151,21 @@
                     "execution modes.";
         }
         break;
-      case SpvExecutionModelTessellationControl:
-      case SpvExecutionModelTessellationEvaluation:
+      case spv::ExecutionModel::TessellationControl:
+      case spv::ExecutionModel::TessellationEvaluation:
         if (execution_modes &&
-            1 < std::count_if(execution_modes->begin(), execution_modes->end(),
-                              [](const SpvExecutionMode& mode) {
-                                switch (mode) {
-                                  case SpvExecutionModeSpacingEqual:
-                                  case SpvExecutionModeSpacingFractionalEven:
-                                  case SpvExecutionModeSpacingFractionalOdd:
-                                    return true;
-                                  default:
-                                    return false;
-                                }
-                              })) {
+            1 < std::count_if(
+                    execution_modes->begin(), execution_modes->end(),
+                    [](const spv::ExecutionMode& mode) {
+                      switch (mode) {
+                        case spv::ExecutionMode::SpacingEqual:
+                        case spv::ExecutionMode::SpacingFractionalEven:
+                        case spv::ExecutionMode::SpacingFractionalOdd:
+                          return true;
+                        default:
+                          return false;
+                      }
+                    })) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
                  << "Tessellation execution model entry points can specify at "
                     "most one of SpacingEqual, SpacingFractionalOdd or "
@@ -171,11 +173,11 @@
         }
         if (execution_modes &&
             1 < std::count_if(execution_modes->begin(), execution_modes->end(),
-                              [](const SpvExecutionMode& mode) {
+                              [](const spv::ExecutionMode& mode) {
                                 switch (mode) {
-                                  case SpvExecutionModeTriangles:
-                                  case SpvExecutionModeQuads:
-                                  case SpvExecutionModeIsolines:
+                                  case spv::ExecutionMode::Triangles:
+                                  case spv::ExecutionMode::Quads:
+                                  case spv::ExecutionMode::Isolines:
                                     return true;
                                   default:
                                     return false;
@@ -187,10 +189,10 @@
         }
         if (execution_modes &&
             1 < std::count_if(execution_modes->begin(), execution_modes->end(),
-                              [](const SpvExecutionMode& mode) {
+                              [](const spv::ExecutionMode& mode) {
                                 switch (mode) {
-                                  case SpvExecutionModeVertexOrderCw:
-                                  case SpvExecutionModeVertexOrderCcw:
+                                  case spv::ExecutionMode::VertexOrderCw:
+                                  case spv::ExecutionMode::VertexOrderCcw:
                                     return true;
                                   default:
                                     return false;
@@ -202,21 +204,22 @@
                     "modes.";
         }
         break;
-      case SpvExecutionModelGeometry:
+      case spv::ExecutionModel::Geometry:
         if (!execution_modes ||
-            1 != std::count_if(execution_modes->begin(), execution_modes->end(),
-                               [](const SpvExecutionMode& mode) {
-                                 switch (mode) {
-                                   case SpvExecutionModeInputPoints:
-                                   case SpvExecutionModeInputLines:
-                                   case SpvExecutionModeInputLinesAdjacency:
-                                   case SpvExecutionModeTriangles:
-                                   case SpvExecutionModeInputTrianglesAdjacency:
-                                     return true;
-                                   default:
-                                     return false;
-                                 }
-                               })) {
+            1 != std::count_if(
+                     execution_modes->begin(), execution_modes->end(),
+                     [](const spv::ExecutionMode& mode) {
+                       switch (mode) {
+                         case spv::ExecutionMode::InputPoints:
+                         case spv::ExecutionMode::InputLines:
+                         case spv::ExecutionMode::InputLinesAdjacency:
+                         case spv::ExecutionMode::Triangles:
+                         case spv::ExecutionMode::InputTrianglesAdjacency:
+                           return true;
+                         default:
+                           return false;
+                       }
+                     })) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
                  << "Geometry execution model entry points must specify "
                     "exactly one of InputPoints, InputLines, "
@@ -225,11 +228,11 @@
         }
         if (!execution_modes ||
             1 != std::count_if(execution_modes->begin(), execution_modes->end(),
-                               [](const SpvExecutionMode& mode) {
+                               [](const spv::ExecutionMode& mode) {
                                  switch (mode) {
-                                   case SpvExecutionModeOutputPoints:
-                                   case SpvExecutionModeOutputLineStrip:
-                                   case SpvExecutionModeOutputTriangleStrip:
+                                   case spv::ExecutionMode::OutputPoints:
+                                   case spv::ExecutionMode::OutputLineStrip:
+                                   case spv::ExecutionMode::OutputTriangleStrip:
                                      return true;
                                    default:
                                      return false;
@@ -241,14 +244,14 @@
                     "OutputTriangleStrip execution modes.";
         }
         break;
-      case SpvExecutionModelMeshEXT:
+      case spv::ExecutionModel::MeshEXT:
         if (!execution_modes ||
             1 != std::count_if(execution_modes->begin(), execution_modes->end(),
-                               [](const SpvExecutionMode& mode) {
+                               [](const spv::ExecutionMode& mode) {
                                  switch (mode) {
-                                   case SpvExecutionModeOutputPoints:
-                                   case SpvExecutionModeOutputLinesEXT:
-                                   case SpvExecutionModeOutputTrianglesEXT:
+                                   case spv::ExecutionMode::OutputPoints:
+                                   case spv::ExecutionMode::OutputLinesEXT:
+                                   case spv::ExecutionMode::OutputTrianglesEXT:
                                      return true;
                                    default:
                                      return false;
@@ -260,10 +263,10 @@
                     "OutputTrianglesEXT Execution Modes.";
         } else if (2 != std::count_if(
                             execution_modes->begin(), execution_modes->end(),
-                            [](const SpvExecutionMode& mode) {
+                            [](const spv::ExecutionMode& mode) {
                               switch (mode) {
-                                case SpvExecutionModeOutputPrimitivesEXT:
-                                case SpvExecutionModeOutputVertices:
+                                case spv::ExecutionMode::OutputPrimitivesEXT:
+                                case spv::ExecutionMode::OutputVertices:
                                   return true;
                                 default:
                                   return false;
@@ -281,23 +284,25 @@
 
   if (spvIsVulkanEnv(_.context()->target_env)) {
     switch (execution_model) {
-      case SpvExecutionModelGLCompute:
+      case spv::ExecutionModel::GLCompute:
         if (!execution_modes ||
-            !execution_modes->count(SpvExecutionModeLocalSize)) {
+            !execution_modes->count(spv::ExecutionMode::LocalSize)) {
           bool ok = false;
           for (auto& i : _.ordered_instructions()) {
-            if (i.opcode() == SpvOpDecorate) {
+            if (i.opcode() == spv::Op::OpDecorate) {
               if (i.operands().size() > 2) {
-                if (i.GetOperandAs<SpvDecoration>(1) == SpvDecorationBuiltIn &&
-                    i.GetOperandAs<SpvBuiltIn>(2) == SpvBuiltInWorkgroupSize) {
+                if (i.GetOperandAs<spv::Decoration>(1) ==
+                        spv::Decoration::BuiltIn &&
+                    i.GetOperandAs<spv::BuiltIn>(2) ==
+                        spv::BuiltIn::WorkgroupSize) {
                   ok = true;
                   break;
                 }
               }
             }
-            if (i.opcode() == SpvOpExecutionModeId) {
-              const auto mode = i.GetOperandAs<SpvExecutionMode>(1);
-              if (mode == SpvExecutionModeLocalSizeId) {
+            if (i.opcode() == spv::Op::OpExecutionModeId) {
+              const auto mode = i.GetOperandAs<spv::ExecutionMode>(1);
+              if (mode == spv::ExecutionMode::LocalSizeId) {
                 ok = true;
                 break;
               }
@@ -333,15 +338,15 @@
               "operand of an OpEntryPoint.";
   }
 
-  const auto mode = inst->GetOperandAs<SpvExecutionMode>(1);
-  if (inst->opcode() == SpvOpExecutionModeId) {
+  const auto mode = inst->GetOperandAs<spv::ExecutionMode>(1);
+  if (inst->opcode() == spv::Op::OpExecutionModeId) {
     size_t operand_count = inst->operands().size();
     for (size_t i = 2; i < operand_count; ++i) {
       const auto operand_id = inst->GetOperandAs<uint32_t>(2);
       const auto* operand_inst = _.FindDef(operand_id);
-      if (mode == SpvExecutionModeSubgroupsPerWorkgroupId ||
-          mode == SpvExecutionModeLocalSizeHintId ||
-          mode == SpvExecutionModeLocalSizeId) {
+      if (mode == spv::ExecutionMode::SubgroupsPerWorkgroupId ||
+          mode == spv::ExecutionMode::LocalSizeHintId ||
+          mode == spv::ExecutionMode::LocalSizeId) {
         if (!spvOpcodeIsConstant(operand_inst->opcode())) {
           return _.diag(SPV_ERROR_INVALID_ID, inst)
                  << "For OpExecutionModeId all Extra Operand ids must be "
@@ -355,9 +360,9 @@
                   "operands.";
       }
     }
-  } else if (mode == SpvExecutionModeSubgroupsPerWorkgroupId ||
-             mode == SpvExecutionModeLocalSizeHintId ||
-             mode == SpvExecutionModeLocalSizeId) {
+  } else if (mode == spv::ExecutionMode::SubgroupsPerWorkgroupId ||
+             mode == spv::ExecutionMode::LocalSizeHintId ||
+             mode == spv::ExecutionMode::LocalSizeId) {
     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 "
@@ -366,39 +371,39 @@
 
   const auto* models = _.GetExecutionModels(entry_point_id);
   switch (mode) {
-    case SpvExecutionModeInvocations:
-    case SpvExecutionModeInputPoints:
-    case SpvExecutionModeInputLines:
-    case SpvExecutionModeInputLinesAdjacency:
-    case SpvExecutionModeInputTrianglesAdjacency:
-    case SpvExecutionModeOutputLineStrip:
-    case SpvExecutionModeOutputTriangleStrip:
+    case spv::ExecutionMode::Invocations:
+    case spv::ExecutionMode::InputPoints:
+    case spv::ExecutionMode::InputLines:
+    case spv::ExecutionMode::InputLinesAdjacency:
+    case spv::ExecutionMode::InputTrianglesAdjacency:
+    case spv::ExecutionMode::OutputLineStrip:
+    case spv::ExecutionMode::OutputTriangleStrip:
       if (!std::all_of(models->begin(), models->end(),
-                       [](const SpvExecutionModel& model) {
-                         return model == SpvExecutionModelGeometry;
+                       [](const spv::ExecutionModel& model) {
+                         return model == spv::ExecutionModel::Geometry;
                        })) {
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
                << "Execution mode can only be used with the Geometry execution "
                   "model.";
       }
       break;
-    case SpvExecutionModeOutputPoints:
-      if (!std::all_of(models->begin(), models->end(),
-                       [&_](const SpvExecutionModel& model) {
-                         switch (model) {
-                           case SpvExecutionModelGeometry:
-                             return true;
-                           case SpvExecutionModelMeshNV:
-                             return _.HasCapability(SpvCapabilityMeshShadingNV);
-                           case SpvExecutionModelMeshEXT:
-                             return _.HasCapability(
-                                 SpvCapabilityMeshShadingEXT);
-                           default:
-                             return false;
-                         }
-                       })) {
-        if (_.HasCapability(SpvCapabilityMeshShadingNV) ||
-            _.HasCapability(SpvCapabilityMeshShadingEXT)) {
+    case spv::ExecutionMode::OutputPoints:
+      if (!std::all_of(
+              models->begin(), models->end(),
+              [&_](const spv::ExecutionModel& model) {
+                switch (model) {
+                  case spv::ExecutionModel::Geometry:
+                    return true;
+                  case spv::ExecutionModel::MeshNV:
+                    return _.HasCapability(spv::Capability::MeshShadingNV);
+                  case spv::ExecutionModel::MeshEXT:
+                    return _.HasCapability(spv::Capability::MeshShadingEXT);
+                  default:
+                    return false;
+                }
+              })) {
+        if (_.HasCapability(spv::Capability::MeshShadingNV) ||
+            _.HasCapability(spv::Capability::MeshShadingEXT)) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
                  << "Execution mode can only be used with the Geometry "
                     "MeshNV or MeshEXT execution model.";
@@ -410,32 +415,32 @@
         }
       }
       break;
-    case SpvExecutionModeSpacingEqual:
-    case SpvExecutionModeSpacingFractionalEven:
-    case SpvExecutionModeSpacingFractionalOdd:
-    case SpvExecutionModeVertexOrderCw:
-    case SpvExecutionModeVertexOrderCcw:
-    case SpvExecutionModePointMode:
-    case SpvExecutionModeQuads:
-    case SpvExecutionModeIsolines:
+    case spv::ExecutionMode::SpacingEqual:
+    case spv::ExecutionMode::SpacingFractionalEven:
+    case spv::ExecutionMode::SpacingFractionalOdd:
+    case spv::ExecutionMode::VertexOrderCw:
+    case spv::ExecutionMode::VertexOrderCcw:
+    case spv::ExecutionMode::PointMode:
+    case spv::ExecutionMode::Quads:
+    case spv::ExecutionMode::Isolines:
       if (!std::all_of(
               models->begin(), models->end(),
-              [](const SpvExecutionModel& model) {
-                return (model == SpvExecutionModelTessellationControl) ||
-                       (model == SpvExecutionModelTessellationEvaluation);
+              [](const spv::ExecutionModel& model) {
+                return (model == spv::ExecutionModel::TessellationControl) ||
+                       (model == spv::ExecutionModel::TessellationEvaluation);
               })) {
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
                << "Execution mode can only be used with a tessellation "
                   "execution model.";
       }
       break;
-    case SpvExecutionModeTriangles:
+    case spv::ExecutionMode::Triangles:
       if (!std::all_of(models->begin(), models->end(),
-                       [](const SpvExecutionModel& model) {
+                       [](const spv::ExecutionModel& model) {
                          switch (model) {
-                           case SpvExecutionModelGeometry:
-                           case SpvExecutionModelTessellationControl:
-                           case SpvExecutionModelTessellationEvaluation:
+                           case spv::ExecutionModel::Geometry:
+                           case spv::ExecutionModel::TessellationControl:
+                           case spv::ExecutionModel::TessellationEvaluation:
                              return true;
                            default:
                              return false;
@@ -446,25 +451,25 @@
                   "tessellation execution model.";
       }
       break;
-    case SpvExecutionModeOutputVertices:
-      if (!std::all_of(models->begin(), models->end(),
-                       [&_](const SpvExecutionModel& model) {
-                         switch (model) {
-                           case SpvExecutionModelGeometry:
-                           case SpvExecutionModelTessellationControl:
-                           case SpvExecutionModelTessellationEvaluation:
-                             return true;
-                           case SpvExecutionModelMeshNV:
-                             return _.HasCapability(SpvCapabilityMeshShadingNV);
-                           case SpvExecutionModelMeshEXT:
-                             return _.HasCapability(
-                                 SpvCapabilityMeshShadingEXT);
-                           default:
-                             return false;
-                         }
-                       })) {
-        if (_.HasCapability(SpvCapabilityMeshShadingNV) ||
-            _.HasCapability(SpvCapabilityMeshShadingEXT)) {
+    case spv::ExecutionMode::OutputVertices:
+      if (!std::all_of(
+              models->begin(), models->end(),
+              [&_](const spv::ExecutionModel& model) {
+                switch (model) {
+                  case spv::ExecutionModel::Geometry:
+                  case spv::ExecutionModel::TessellationControl:
+                  case spv::ExecutionModel::TessellationEvaluation:
+                    return true;
+                  case spv::ExecutionModel::MeshNV:
+                    return _.HasCapability(spv::Capability::MeshShadingNV);
+                  case spv::ExecutionModel::MeshEXT:
+                    return _.HasCapability(spv::Capability::MeshShadingEXT);
+                  default:
+                    return false;
+                }
+              })) {
+        if (_.HasCapability(spv::Capability::MeshShadingNV) ||
+            _.HasCapability(spv::Capability::MeshShadingEXT)) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
                  << "Execution mode can only be used with a Geometry, "
                     "tessellation, MeshNV or MeshEXT execution model.";
@@ -475,13 +480,13 @@
         }
       }
       break;
-    case SpvExecutionModeOutputLinesEXT:
-    case SpvExecutionModeOutputTrianglesEXT:
-    case SpvExecutionModeOutputPrimitivesEXT:
+    case spv::ExecutionMode::OutputLinesEXT:
+    case spv::ExecutionMode::OutputTrianglesEXT:
+    case spv::ExecutionMode::OutputPrimitivesEXT:
       if (!std::all_of(models->begin(), models->end(),
-                       [](const SpvExecutionModel& model) {
-                         return (model == SpvExecutionModelMeshEXT ||
-                                 model == SpvExecutionModelMeshNV);
+                       [](const spv::ExecutionModel& model) {
+                         return (model == spv::ExecutionModel::MeshEXT ||
+                                 model == spv::ExecutionModel::MeshNV);
                        })) {
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
                << "Execution mode can only be used with the MeshEXT or MeshNV "
@@ -489,74 +494,77 @@
                   "model.";
       }
       break;
-    case SpvExecutionModePixelCenterInteger:
-    case SpvExecutionModeOriginUpperLeft:
-    case SpvExecutionModeOriginLowerLeft:
-    case SpvExecutionModeEarlyFragmentTests:
-    case SpvExecutionModeDepthReplacing:
-    case SpvExecutionModeDepthGreater:
-    case SpvExecutionModeDepthLess:
-    case SpvExecutionModeDepthUnchanged:
-    case SpvExecutionModePixelInterlockOrderedEXT:
-    case SpvExecutionModePixelInterlockUnorderedEXT:
-    case SpvExecutionModeSampleInterlockOrderedEXT:
-    case SpvExecutionModeSampleInterlockUnorderedEXT:
-    case SpvExecutionModeShadingRateInterlockOrderedEXT:
-    case SpvExecutionModeShadingRateInterlockUnorderedEXT:
-    case SpvExecutionModeEarlyAndLateFragmentTestsAMD:
-    case SpvExecutionModeStencilRefUnchangedFrontAMD:
-    case SpvExecutionModeStencilRefGreaterFrontAMD:
-    case SpvExecutionModeStencilRefLessFrontAMD:
-    case SpvExecutionModeStencilRefUnchangedBackAMD:
-    case SpvExecutionModeStencilRefGreaterBackAMD:
-    case SpvExecutionModeStencilRefLessBackAMD:
+    case spv::ExecutionMode::PixelCenterInteger:
+    case spv::ExecutionMode::OriginUpperLeft:
+    case spv::ExecutionMode::OriginLowerLeft:
+    case spv::ExecutionMode::EarlyFragmentTests:
+    case spv::ExecutionMode::DepthReplacing:
+    case spv::ExecutionMode::DepthGreater:
+    case spv::ExecutionMode::DepthLess:
+    case spv::ExecutionMode::DepthUnchanged:
+    case spv::ExecutionMode::NonCoherentColorAttachmentReadEXT:
+    case spv::ExecutionMode::NonCoherentDepthAttachmentReadEXT:
+    case spv::ExecutionMode::NonCoherentStencilAttachmentReadEXT:
+    case spv::ExecutionMode::PixelInterlockOrderedEXT:
+    case spv::ExecutionMode::PixelInterlockUnorderedEXT:
+    case spv::ExecutionMode::SampleInterlockOrderedEXT:
+    case spv::ExecutionMode::SampleInterlockUnorderedEXT:
+    case spv::ExecutionMode::ShadingRateInterlockOrderedEXT:
+    case spv::ExecutionMode::ShadingRateInterlockUnorderedEXT:
+    case spv::ExecutionMode::EarlyAndLateFragmentTestsAMD:
+    case spv::ExecutionMode::StencilRefUnchangedFrontAMD:
+    case spv::ExecutionMode::StencilRefGreaterFrontAMD:
+    case spv::ExecutionMode::StencilRefLessFrontAMD:
+    case spv::ExecutionMode::StencilRefUnchangedBackAMD:
+    case spv::ExecutionMode::StencilRefGreaterBackAMD:
+    case spv::ExecutionMode::StencilRefLessBackAMD:
       if (!std::all_of(models->begin(), models->end(),
-                       [](const SpvExecutionModel& model) {
-                         return model == SpvExecutionModelFragment;
+                       [](const spv::ExecutionModel& model) {
+                         return model == spv::ExecutionModel::Fragment;
                        })) {
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
                << "Execution mode can only be used with the Fragment execution "
                   "model.";
       }
       break;
-    case SpvExecutionModeLocalSizeHint:
-    case SpvExecutionModeVecTypeHint:
-    case SpvExecutionModeContractionOff:
-    case SpvExecutionModeLocalSizeHintId:
+    case spv::ExecutionMode::LocalSizeHint:
+    case spv::ExecutionMode::VecTypeHint:
+    case spv::ExecutionMode::ContractionOff:
+    case spv::ExecutionMode::LocalSizeHintId:
       if (!std::all_of(models->begin(), models->end(),
-                       [](const SpvExecutionModel& model) {
-                         return model == SpvExecutionModelKernel;
+                       [](const spv::ExecutionModel& model) {
+                         return model == spv::ExecutionModel::Kernel;
                        })) {
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
                << "Execution mode can only be used with the Kernel execution "
                   "model.";
       }
       break;
-    case SpvExecutionModeLocalSize:
-    case SpvExecutionModeLocalSizeId:
-      if (mode == SpvExecutionModeLocalSizeId && !_.IsLocalSizeIdAllowed())
+    case spv::ExecutionMode::LocalSize:
+    case spv::ExecutionMode::LocalSizeId:
+      if (mode == spv::ExecutionMode::LocalSizeId && !_.IsLocalSizeIdAllowed())
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
                << "LocalSizeId mode is not allowed by the current environment.";
 
-      if (!std::all_of(models->begin(), models->end(),
-                       [&_](const SpvExecutionModel& model) {
-                         switch (model) {
-                           case SpvExecutionModelKernel:
-                           case SpvExecutionModelGLCompute:
-                             return true;
-                           case SpvExecutionModelTaskNV:
-                           case SpvExecutionModelMeshNV:
-                             return _.HasCapability(SpvCapabilityMeshShadingNV);
-                           case SpvExecutionModelTaskEXT:
-                           case SpvExecutionModelMeshEXT:
-                             return _.HasCapability(
-                                 SpvCapabilityMeshShadingEXT);
-                           default:
-                             return false;
-                         }
-                       })) {
-        if (_.HasCapability(SpvCapabilityMeshShadingNV) ||
-            _.HasCapability(SpvCapabilityMeshShadingEXT)) {
+      if (!std::all_of(
+              models->begin(), models->end(),
+              [&_](const spv::ExecutionModel& model) {
+                switch (model) {
+                  case spv::ExecutionModel::Kernel:
+                  case spv::ExecutionModel::GLCompute:
+                    return true;
+                  case spv::ExecutionModel::TaskNV:
+                  case spv::ExecutionModel::MeshNV:
+                    return _.HasCapability(spv::Capability::MeshShadingNV);
+                  case spv::ExecutionModel::TaskEXT:
+                  case spv::ExecutionModel::MeshEXT:
+                    return _.HasCapability(spv::Capability::MeshShadingEXT);
+                  default:
+                    return false;
+                }
+              })) {
+        if (_.HasCapability(spv::Capability::MeshShadingNV) ||
+            _.HasCapability(spv::Capability::MeshShadingEXT)) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
                  << "Execution mode can only be used with a Kernel, GLCompute, "
                     "MeshNV, MeshEXT, TaskNV or TaskEXT execution model.";
@@ -572,13 +580,13 @@
   }
 
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    if (mode == SpvExecutionModeOriginLowerLeft) {
+    if (mode == spv::ExecutionMode::OriginLowerLeft) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << _.VkErrorID(4653)
              << "In the Vulkan environment, the OriginLowerLeft execution mode "
                 "must not be used.";
     }
-    if (mode == SpvExecutionModePixelCenterInteger) {
+    if (mode == spv::ExecutionMode::PixelCenterInteger) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << _.VkErrorID(4654)
              << "In the Vulkan environment, the PixelCenterInteger execution "
@@ -593,29 +601,30 @@
                                  const Instruction* inst) {
   // Already produced an error if multiple memory model instructions are
   // present.
-  if (_.memory_model() != SpvMemoryModelVulkanKHR &&
-      _.HasCapability(SpvCapabilityVulkanMemoryModelKHR)) {
+  if (_.memory_model() != spv::MemoryModel::VulkanKHR &&
+      _.HasCapability(spv::Capability::VulkanMemoryModelKHR)) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "VulkanMemoryModelKHR capability must only be specified if "
               "the VulkanKHR memory model is used.";
   }
 
   if (spvIsOpenCLEnv(_.context()->target_env)) {
-    if ((_.addressing_model() != SpvAddressingModelPhysical32) &&
-        (_.addressing_model() != SpvAddressingModelPhysical64)) {
+    if ((_.addressing_model() != spv::AddressingModel::Physical32) &&
+        (_.addressing_model() != spv::AddressingModel::Physical64)) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Addressing model must be Physical32 or Physical64 "
              << "in the OpenCL environment.";
     }
-    if (_.memory_model() != SpvMemoryModelOpenCL) {
+    if (_.memory_model() != spv::MemoryModel::OpenCL) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Memory model must be OpenCL in the OpenCL environment.";
     }
   }
 
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    if ((_.addressing_model() != SpvAddressingModelLogical) &&
-        (_.addressing_model() != SpvAddressingModelPhysicalStorageBuffer64)) {
+    if ((_.addressing_model() != spv::AddressingModel::Logical) &&
+        (_.addressing_model() !=
+         spv::AddressingModel::PhysicalStorageBuffer64)) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << _.VkErrorID(4635)
              << "Addressing model must be Logical or PhysicalStorageBuffer64 "
@@ -629,14 +638,14 @@
 
 spv_result_t ModeSettingPass(ValidationState_t& _, const Instruction* inst) {
   switch (inst->opcode()) {
-    case SpvOpEntryPoint:
+    case spv::Op::OpEntryPoint:
       if (auto error = ValidateEntryPoint(_, inst)) return error;
       break;
-    case SpvOpExecutionMode:
-    case SpvOpExecutionModeId:
+    case spv::Op::OpExecutionMode:
+    case spv::Op::OpExecutionModeId:
       if (auto error = ValidateExecutionMode(_, inst)) return error;
       break;
-    case SpvOpMemoryModel:
+    case spv::Op::OpMemoryModel:
       if (auto error = ValidateMemoryModel(_, inst)) return error;
       break;
     default:
diff --git a/source/val/validate_non_uniform.cpp b/source/val/validate_non_uniform.cpp
index 6d4f8a2..2c36ce3 100644
--- a/source/val/validate_non_uniform.cpp
+++ b/source/val/validate_non_uniform.cpp
@@ -14,14 +14,11 @@
 
 // Validates correctness of barrier SPIR-V instructions.
 
-#include "source/val/validate.h"
-
-#include "source/diagnostic.h"
 #include "source/opcode.h"
 #include "source/spirv_constant.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/validate_scopes.h"
 #include "source/val/validation_state.h"
 
@@ -29,6 +26,207 @@
 namespace val {
 namespace {
 
+spv_result_t ValidateGroupNonUniformElect(ValidationState_t& _,
+                                          const Instruction* inst) {
+  if (!_.IsBoolScalarType(inst->type_id())) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Result must be a boolean scalar type";
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateGroupNonUniformAnyAll(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 ValidateGroupNonUniformAllEqual(ValidationState_t& _,
+                                             const Instruction* inst) {
+  if (!_.IsBoolScalarType(inst->type_id())) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Result must be a boolean scalar type";
+  }
+
+  const auto value_type = _.GetOperandTypeId(inst, 3);
+  if (!_.IsFloatScalarOrVectorType(value_type) &&
+      !_.IsIntScalarOrVectorType(value_type) &&
+      !_.IsBoolScalarOrVectorType(value_type)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Value must be a scalar or vector of integer, floating-point, or "
+              "boolean type";
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateGroupNonUniformBroadcastShuffle(ValidationState_t& _,
+                                                     const Instruction* inst) {
+  const auto 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 auto 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";
+  }
+
+  const auto GetOperandName = [](const spv::Op opcode) {
+    std::string operand;
+    switch (opcode) {
+      case spv::Op::OpGroupNonUniformBroadcast:
+      case spv::Op::OpGroupNonUniformShuffle:
+        operand = "Id";
+        break;
+      case spv::Op::OpGroupNonUniformShuffleXor:
+        operand = "Mask";
+        break;
+      case spv::Op::OpGroupNonUniformQuadBroadcast:
+        operand = "Index";
+        break;
+      case spv::Op::OpGroupNonUniformQuadSwap:
+        operand = "Direction";
+        break;
+      case spv::Op::OpGroupNonUniformShuffleUp:
+      case spv::Op::OpGroupNonUniformShuffleDown:
+      default:
+        operand = "Delta";
+        break;
+    }
+    return operand;
+  };
+
+  const auto id_type_id = _.GetOperandTypeId(inst, 4);
+  if (!_.IsUnsignedIntScalarType(id_type_id)) {
+    std::string operand = GetOperandName(inst->opcode());
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << operand << " must be an unsigned integer scalar";
+  }
+
+  const bool should_be_constant =
+      inst->opcode() == spv::Op::OpGroupNonUniformQuadSwap ||
+      ((inst->opcode() == spv::Op::OpGroupNonUniformBroadcast ||
+        inst->opcode() == spv::Op::OpGroupNonUniformQuadBroadcast) &&
+       _.version() < SPV_SPIRV_VERSION_WORD(1, 5));
+  if (should_be_constant) {
+    const auto id_id = inst->GetOperandAs<uint32_t>(4);
+    const auto id_op = _.GetIdOpcode(id_id);
+    if (!spvOpcodeIsConstant(id_op)) {
+      std::string operand = GetOperandName(inst->opcode());
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Before SPIR-V 1.5, " << operand
+             << " must be a constant instruction";
+    }
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateGroupNonUniformBroadcastFirst(ValidationState_t& _,
+                                                   const Instruction* inst) {
+  const auto 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 auto 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 ValidateGroupNonUniformBallot(ValidationState_t& _,
+                                           const Instruction* inst) {
+  if (!_.IsUnsignedIntVectorType(inst->type_id())) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Result must be a 4-component unsigned integer vector";
+  }
+
+  if (_.GetDimension(inst->type_id()) != 4) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Result must be a 4-component unsigned integer vector";
+  }
+
+  const auto pred_type_id = _.GetOperandTypeId(inst, 3);
+  if (!_.IsBoolScalarType(pred_type_id)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Predicate must be a boolean scalar";
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateGroupNonUniformInverseBallot(ValidationState_t& _,
+                                                  const Instruction* inst) {
+  if (!_.IsBoolScalarType(inst->type_id())) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Result must be a boolean scalar";
+  }
+
+  const auto value_type_id = _.GetOperandTypeId(inst, 3);
+  if (!_.IsUnsignedIntVectorType(value_type_id)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Value must be a 4-component unsigned integer vector";
+  }
+
+  if (_.GetDimension(value_type_id) != 4) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Value must be a 4-component unsigned integer vector";
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateGroupNonUniformBallotBitExtract(ValidationState_t& _,
+                                                     const Instruction* inst) {
+  if (!_.IsBoolScalarType(inst->type_id())) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Result must be a boolean scalar";
+  }
+
+  const auto value_type_id = _.GetOperandTypeId(inst, 3);
+  if (!_.IsUnsignedIntVectorType(value_type_id)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Value must be a 4-component unsigned integer vector";
+  }
+
+  if (_.GetDimension(value_type_id) != 4) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Value must be a 4-component unsigned integer vector";
+  }
+
+  const auto id_type_id = _.GetOperandTypeId(inst, 4);
+  if (!_.IsUnsignedIntScalarType(id_type_id)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Id must be an unsigned integer scalar";
+  }
+
+  return SPV_SUCCESS;
+}
+
 spv_result_t ValidateGroupNonUniformBallotBitCount(ValidationState_t& _,
                                                    const Instruction* inst) {
   // Scope is already checked by ValidateExecutionScope() above.
@@ -48,11 +246,11 @@
                                                    "of integer type scalar";
   }
 
-  const auto group = inst->GetOperandAs<uint32_t>(3);
+  const auto group = inst->GetOperandAs<spv::GroupOperation>(3);
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    if ((group != SpvGroupOperationReduce) &&
-        (group != SpvGroupOperationInclusiveScan) &&
-        (group != SpvGroupOperationExclusiveScan)) {
+    if ((group != spv::GroupOperation::Reduce) &&
+        (group != spv::GroupOperation::InclusiveScan) &&
+        (group != spv::GroupOperation::ExclusiveScan)) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << _.VkErrorID(4685)
              << "In Vulkan: The OpGroupNonUniformBallotBitCount group "
@@ -63,6 +261,107 @@
   return SPV_SUCCESS;
 }
 
+spv_result_t ValidateGroupNonUniformBallotFind(ValidationState_t& _,
+                                               const Instruction* inst) {
+  if (!_.IsUnsignedIntScalarType(inst->type_id())) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Result must be an unsigned integer scalar";
+  }
+
+  const auto value_type_id = _.GetOperandTypeId(inst, 3);
+  if (!_.IsUnsignedIntVectorType(value_type_id)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Value must be a 4-component unsigned integer vector";
+  }
+
+  if (_.GetDimension(value_type_id) != 4) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Value must be a 4-component unsigned integer vector";
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateGroupNonUniformArithmetic(ValidationState_t& _,
+                                               const Instruction* inst) {
+  const bool is_unsigned = inst->opcode() == spv::Op::OpGroupNonUniformUMin ||
+                           inst->opcode() == spv::Op::OpGroupNonUniformUMax;
+  const bool is_float = inst->opcode() == spv::Op::OpGroupNonUniformFAdd ||
+                        inst->opcode() == spv::Op::OpGroupNonUniformFMul ||
+                        inst->opcode() == spv::Op::OpGroupNonUniformFMin ||
+                        inst->opcode() == spv::Op::OpGroupNonUniformFMax;
+  const bool is_bool = inst->opcode() == spv::Op::OpGroupNonUniformLogicalAnd ||
+                       inst->opcode() == spv::Op::OpGroupNonUniformLogicalOr ||
+                       inst->opcode() == spv::Op::OpGroupNonUniformLogicalXor;
+  if (is_float) {
+    if (!_.IsFloatScalarOrVectorType(inst->type_id())) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Result must be a floating-point scalar or vector";
+    }
+  } else if (is_bool) {
+    if (!_.IsBoolScalarOrVectorType(inst->type_id())) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Result must be a boolean scalar or vector";
+    }
+  } else if (is_unsigned) {
+    if (!_.IsUnsignedIntScalarOrVectorType(inst->type_id())) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Result must be an unsigned integer scalar or vector";
+    }
+  } else if (!_.IsIntScalarOrVectorType(inst->type_id())) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Result must be an integer scalar or vector";
+  }
+
+  const auto value_type_id = _.GetOperandTypeId(inst, 4);
+  if (value_type_id != inst->type_id()) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "The type of Value must match the Result type";
+  }
+
+  const auto group_op = inst->GetOperandAs<spv::GroupOperation>(3);
+  bool is_clustered_reduce = group_op == spv::GroupOperation::ClusteredReduce;
+  bool is_partitioned_nv =
+      group_op == spv::GroupOperation::PartitionedReduceNV ||
+      group_op == spv::GroupOperation::PartitionedInclusiveScanNV ||
+      group_op == spv::GroupOperation::PartitionedExclusiveScanNV;
+  if (inst->operands().size() <= 5) {
+    if (is_clustered_reduce) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "ClusterSize must be present when Operation is ClusteredReduce";
+    } else if (is_partitioned_nv) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Ballot must be present when Operation is PartitionedReduceNV, "
+                "PartitionedInclusiveScanNV, or PartitionedExclusiveScanNV";
+    }
+  } else {
+    const auto operand_id = inst->GetOperandAs<uint32_t>(5);
+    const auto* operand = _.FindDef(operand_id);
+    if (is_partitioned_nv) {
+      if (!operand || !_.IsIntScalarOrVectorType(operand->type_id())) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Ballot must be a 4-component integer vector";
+      }
+
+      if (_.GetDimension(operand->type_id()) != 4) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Ballot must be a 4-component integer vector";
+      }
+    } else {
+      if (!operand || !_.IsUnsignedIntScalarType(operand->type_id())) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "ClusterSize must be an unsigned integer scalar";
+      }
+
+      if (!spvOpcodeIsConstant(operand->opcode())) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "ClusterSize must be a constant instruction";
+      }
+    }
+  }
+  return SPV_SUCCESS;
+}
+
 spv_result_t ValidateGroupNonUniformRotateKHR(ValidationState_t& _,
                                               const Instruction* inst) {
   // Scope is already checked by ValidateExecutionScope() above.
@@ -120,19 +419,62 @@
 
 // Validates correctness of non-uniform group instructions.
 spv_result_t NonUniformPass(ValidationState_t& _, const Instruction* inst) {
-  const SpvOp opcode = inst->opcode();
+  const spv::Op opcode = inst->opcode();
 
   if (spvOpcodeIsNonUniformGroupOperation(opcode)) {
-    const uint32_t execution_scope = inst->word(3);
+    const uint32_t execution_scope = inst->GetOperandAs<uint32_t>(2);
     if (auto error = ValidateExecutionScope(_, inst, execution_scope)) {
       return error;
     }
   }
 
   switch (opcode) {
-    case SpvOpGroupNonUniformBallotBitCount:
+    case spv::Op::OpGroupNonUniformElect:
+      return ValidateGroupNonUniformElect(_, inst);
+    case spv::Op::OpGroupNonUniformAny:
+    case spv::Op::OpGroupNonUniformAll:
+      return ValidateGroupNonUniformAnyAll(_, inst);
+    case spv::Op::OpGroupNonUniformAllEqual:
+      return ValidateGroupNonUniformAllEqual(_, inst);
+    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:
+      return ValidateGroupNonUniformBroadcastShuffle(_, inst);
+    case spv::Op::OpGroupNonUniformBroadcastFirst:
+      return ValidateGroupNonUniformBroadcastFirst(_, inst);
+    case spv::Op::OpGroupNonUniformBallot:
+      return ValidateGroupNonUniformBallot(_, inst);
+    case spv::Op::OpGroupNonUniformInverseBallot:
+      return ValidateGroupNonUniformInverseBallot(_, inst);
+    case spv::Op::OpGroupNonUniformBallotBitExtract:
+      return ValidateGroupNonUniformBallotBitExtract(_, inst);
+    case spv::Op::OpGroupNonUniformBallotBitCount:
       return ValidateGroupNonUniformBallotBitCount(_, inst);
-    case SpvOpGroupNonUniformRotateKHR:
+    case spv::Op::OpGroupNonUniformBallotFindLSB:
+    case spv::Op::OpGroupNonUniformBallotFindMSB:
+      return ValidateGroupNonUniformBallotFind(_, inst);
+    case spv::Op::OpGroupNonUniformIAdd:
+    case spv::Op::OpGroupNonUniformFAdd:
+    case spv::Op::OpGroupNonUniformIMul:
+    case spv::Op::OpGroupNonUniformFMul:
+    case spv::Op::OpGroupNonUniformSMin:
+    case spv::Op::OpGroupNonUniformUMin:
+    case spv::Op::OpGroupNonUniformFMin:
+    case spv::Op::OpGroupNonUniformSMax:
+    case spv::Op::OpGroupNonUniformUMax:
+    case spv::Op::OpGroupNonUniformFMax:
+    case spv::Op::OpGroupNonUniformBitwiseAnd:
+    case spv::Op::OpGroupNonUniformBitwiseOr:
+    case spv::Op::OpGroupNonUniformBitwiseXor:
+    case spv::Op::OpGroupNonUniformLogicalAnd:
+    case spv::Op::OpGroupNonUniformLogicalOr:
+    case spv::Op::OpGroupNonUniformLogicalXor:
+      return ValidateGroupNonUniformArithmetic(_, inst);
+    case spv::Op::OpGroupNonUniformRotateKHR:
       return ValidateGroupNonUniformRotateKHR(_, inst);
     default:
       break;
diff --git a/source/val/validate_primitives.cpp b/source/val/validate_primitives.cpp
index 7d11f2e..6769090 100644
--- a/source/val/validate_primitives.cpp
+++ b/source/val/validate_primitives.cpp
@@ -14,13 +14,11 @@
 
 // Validates correctness of primitive SPIR-V instructions.
 
-#include "source/val/validate.h"
-
 #include <string>
 
-#include "source/diagnostic.h"
 #include "source/opcode.h"
 #include "source/val/instruction.h"
+#include "source/val/validate.h"
 #include "source/val/validation_state.h"
 
 namespace spvtools {
@@ -28,16 +26,16 @@
 
 // Validates correctness of primitive instructions.
 spv_result_t PrimitivesPass(ValidationState_t& _, const Instruction* inst) {
-  const SpvOp opcode = inst->opcode();
+  const spv::Op opcode = inst->opcode();
 
   switch (opcode) {
-    case SpvOpEmitVertex:
-    case SpvOpEndPrimitive:
-    case SpvOpEmitStreamVertex:
-    case SpvOpEndStreamPrimitive:
+    case spv::Op::OpEmitVertex:
+    case spv::Op::OpEndPrimitive:
+    case spv::Op::OpEmitStreamVertex:
+    case spv::Op::OpEndStreamPrimitive:
       _.function(inst->function()->id())
           ->RegisterExecutionModelLimitation(
-              SpvExecutionModelGeometry,
+              spv::ExecutionModel::Geometry,
               std::string(spvOpcodeString(opcode)) +
                   " instructions require Geometry execution model");
       break;
@@ -46,8 +44,8 @@
   }
 
   switch (opcode) {
-    case SpvOpEmitStreamVertex:
-    case SpvOpEndStreamPrimitive: {
+    case spv::Op::OpEmitStreamVertex:
+    case spv::Op::OpEndStreamPrimitive: {
       const uint32_t stream_id = inst->word(1);
       const uint32_t stream_type = _.GetTypeId(stream_id);
       if (!_.IsIntScalarType(stream_type)) {
@@ -56,7 +54,7 @@
                << ": expected Stream to be int scalar";
       }
 
-      const SpvOp stream_opcode = _.GetIdOpcode(stream_id);
+      const spv::Op stream_opcode = _.GetIdOpcode(stream_id);
       if (!spvOpcodeIsConstant(stream_opcode)) {
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
                << spvOpcodeString(opcode)
diff --git a/source/val/validate_ray_query.cpp b/source/val/validate_ray_query.cpp
index b553449..9b67fc9 100644
--- a/source/val/validate_ray_query.cpp
+++ b/source/val/validate_ray_query.cpp
@@ -29,19 +29,19 @@
   const uint32_t ray_query_id = inst->GetOperandAs<uint32_t>(ray_query_index);
   auto variable = _.FindDef(ray_query_id);
   const auto var_opcode = variable->opcode();
-  if (!variable ||
-      (var_opcode != SpvOpVariable && var_opcode != SpvOpFunctionParameter &&
-       var_opcode != SpvOpAccessChain)) {
+  if (!variable || (var_opcode != spv::Op::OpVariable &&
+                    var_opcode != spv::Op::OpFunctionParameter &&
+                    var_opcode != spv::Op::OpAccessChain)) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "Ray Query must be a memory object declaration";
   }
   auto pointer = _.FindDef(variable->GetOperandAs<uint32_t>(0));
-  if (!pointer || pointer->opcode() != SpvOpTypePointer) {
+  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() != SpvOpTypeRayQueryKHR) {
+  if (!type || type->opcode() != spv::Op::OpTypeRayQueryKHR) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "Ray Query must be a pointer to OpTypeRayQueryKHR";
   }
@@ -54,7 +54,7 @@
   const uint32_t intersection_id =
       inst->GetOperandAs<uint32_t>(intersection_index);
   const uint32_t intersection_type = _.GetTypeId(intersection_id);
-  const SpvOp intersection_opcode = _.GetIdOpcode(intersection_id);
+  const spv::Op intersection_opcode = _.GetIdOpcode(intersection_id);
   if (!_.IsIntScalarType(intersection_type) ||
       _.GetBitWidth(intersection_type) != 32 ||
       !spvOpcodeIsConstant(intersection_opcode)) {
@@ -68,15 +68,15 @@
 }  // namespace
 
 spv_result_t RayQueryPass(ValidationState_t& _, const Instruction* inst) {
-  const SpvOp opcode = inst->opcode();
+  const spv::Op opcode = inst->opcode();
   const uint32_t result_type = inst->type_id();
 
   switch (opcode) {
-    case SpvOpRayQueryInitializeKHR: {
+    case spv::Op::OpRayQueryInitializeKHR: {
       if (auto error = ValidateRayQueryPointer(_, inst, 0)) return error;
 
       if (_.GetIdOpcode(_.GetOperandTypeId(inst, 1)) !=
-          SpvOpTypeAccelerationStructureKHR) {
+          spv::Op::OpTypeAccelerationStructureKHR) {
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
                << "Expected Acceleration Structure to be of type "
                   "OpTypeAccelerationStructureKHR";
@@ -123,13 +123,13 @@
       break;
     }
 
-    case SpvOpRayQueryTerminateKHR:
-    case SpvOpRayQueryConfirmIntersectionKHR: {
+    case spv::Op::OpRayQueryTerminateKHR:
+    case spv::Op::OpRayQueryConfirmIntersectionKHR: {
       if (auto error = ValidateRayQueryPointer(_, inst, 0)) return error;
       break;
     }
 
-    case SpvOpRayQueryGenerateIntersectionKHR: {
+    case spv::Op::OpRayQueryGenerateIntersectionKHR: {
       if (auto error = ValidateRayQueryPointer(_, inst, 0)) return error;
 
       const uint32_t hit_t_id = _.GetOperandTypeId(inst, 1);
@@ -141,9 +141,9 @@
       break;
     }
 
-    case SpvOpRayQueryGetIntersectionFrontFaceKHR:
-    case SpvOpRayQueryProceedKHR:
-    case SpvOpRayQueryGetIntersectionCandidateAABBOpaqueKHR: {
+    case spv::Op::OpRayQueryGetIntersectionFrontFaceKHR:
+    case spv::Op::OpRayQueryProceedKHR:
+    case spv::Op::OpRayQueryGetIntersectionCandidateAABBOpaqueKHR: {
       if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error;
 
       if (!_.IsBoolScalarType(result_type)) {
@@ -151,15 +151,15 @@
                << "expected Result Type to be bool scalar type";
       }
 
-      if (opcode == SpvOpRayQueryGetIntersectionFrontFaceKHR) {
+      if (opcode == spv::Op::OpRayQueryGetIntersectionFrontFaceKHR) {
         if (auto error = ValidateIntersectionId(_, inst, 3)) return error;
       }
 
       break;
     }
 
-    case SpvOpRayQueryGetIntersectionTKHR:
-    case SpvOpRayQueryGetRayTMinKHR: {
+    case spv::Op::OpRayQueryGetIntersectionTKHR:
+    case spv::Op::OpRayQueryGetRayTMinKHR: {
       if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error;
 
       if (!_.IsFloatScalarType(result_type) ||
@@ -168,20 +168,21 @@
                << "expected Result Type to be 32-bit float scalar type";
       }
 
-      if (opcode == SpvOpRayQueryGetIntersectionTKHR) {
+      if (opcode == spv::Op::OpRayQueryGetIntersectionTKHR) {
         if (auto error = ValidateIntersectionId(_, inst, 3)) return error;
       }
 
       break;
     }
 
-    case SpvOpRayQueryGetIntersectionTypeKHR:
-    case SpvOpRayQueryGetIntersectionInstanceCustomIndexKHR:
-    case SpvOpRayQueryGetIntersectionInstanceIdKHR:
-    case SpvOpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR:
-    case SpvOpRayQueryGetIntersectionGeometryIndexKHR:
-    case SpvOpRayQueryGetIntersectionPrimitiveIndexKHR:
-    case SpvOpRayQueryGetRayFlagsKHR: {
+    case spv::Op::OpRayQueryGetIntersectionTypeKHR:
+    case spv::Op::OpRayQueryGetIntersectionInstanceCustomIndexKHR:
+    case spv::Op::OpRayQueryGetIntersectionInstanceIdKHR:
+    case spv::Op::
+        OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR:
+    case spv::Op::OpRayQueryGetIntersectionGeometryIndexKHR:
+    case spv::Op::OpRayQueryGetIntersectionPrimitiveIndexKHR:
+    case spv::Op::OpRayQueryGetRayFlagsKHR: {
       if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error;
 
       if (!_.IsIntScalarType(result_type) || _.GetBitWidth(result_type) != 32) {
@@ -189,17 +190,17 @@
                << "expected Result Type to be 32-bit int scalar type";
       }
 
-      if (opcode != SpvOpRayQueryGetRayFlagsKHR) {
+      if (opcode != spv::Op::OpRayQueryGetRayFlagsKHR) {
         if (auto error = ValidateIntersectionId(_, inst, 3)) return error;
       }
 
       break;
     }
 
-    case SpvOpRayQueryGetIntersectionObjectRayDirectionKHR:
-    case SpvOpRayQueryGetIntersectionObjectRayOriginKHR:
-    case SpvOpRayQueryGetWorldRayDirectionKHR:
-    case SpvOpRayQueryGetWorldRayOriginKHR: {
+    case spv::Op::OpRayQueryGetIntersectionObjectRayDirectionKHR:
+    case spv::Op::OpRayQueryGetIntersectionObjectRayOriginKHR:
+    case spv::Op::OpRayQueryGetWorldRayDirectionKHR:
+    case spv::Op::OpRayQueryGetWorldRayOriginKHR: {
       if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error;
 
       if (!_.IsFloatVectorType(result_type) ||
@@ -210,15 +211,15 @@
                   "vector type";
       }
 
-      if (opcode == SpvOpRayQueryGetIntersectionObjectRayDirectionKHR ||
-          opcode == SpvOpRayQueryGetIntersectionObjectRayOriginKHR) {
+      if (opcode == spv::Op::OpRayQueryGetIntersectionObjectRayDirectionKHR ||
+          opcode == spv::Op::OpRayQueryGetIntersectionObjectRayOriginKHR) {
         if (auto error = ValidateIntersectionId(_, inst, 3)) return error;
       }
 
       break;
     }
 
-    case SpvOpRayQueryGetIntersectionBarycentricsKHR: {
+    case spv::Op::OpRayQueryGetIntersectionBarycentricsKHR: {
       if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error;
       if (auto error = ValidateIntersectionId(_, inst, 3)) return error;
 
@@ -233,8 +234,8 @@
       break;
     }
 
-    case SpvOpRayQueryGetIntersectionObjectToWorldKHR:
-    case SpvOpRayQueryGetIntersectionWorldToObjectKHR: {
+    case spv::Op::OpRayQueryGetIntersectionObjectToWorldKHR:
+    case spv::Op::OpRayQueryGetIntersectionWorldToObjectKHR: {
       if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error;
       if (auto error = ValidateIntersectionId(_, inst, 3)) return error;
 
diff --git a/source/val/validate_ray_tracing.cpp b/source/val/validate_ray_tracing.cpp
index 5b5c8da..f74e9d4 100644
--- a/source/val/validate_ray_tracing.cpp
+++ b/source/val/validate_ray_tracing.cpp
@@ -23,17 +23,17 @@
 namespace val {
 
 spv_result_t RayTracingPass(ValidationState_t& _, const Instruction* inst) {
-  const SpvOp opcode = inst->opcode();
+  const spv::Op opcode = inst->opcode();
   const uint32_t result_type = inst->type_id();
 
   switch (opcode) {
-    case SpvOpTraceRayKHR: {
+    case spv::Op::OpTraceRayKHR: {
       _.function(inst->function()->id())
           ->RegisterExecutionModelLimitation(
-              [](SpvExecutionModel model, std::string* message) {
-                if (model != SpvExecutionModelRayGenerationKHR &&
-                    model != SpvExecutionModelClosestHitKHR &&
-                    model != SpvExecutionModelMissKHR) {
+              [](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, "
@@ -45,7 +45,7 @@
               });
 
       if (_.GetIdOpcode(_.GetOperandTypeId(inst, 0)) !=
-          SpvOpTypeAccelerationStructureKHR) {
+          spv::Op::OpTypeAccelerationStructureKHR) {
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
                << "Expected Acceleration Structure to be of type "
                   "OpTypeAccelerationStructureKHR";
@@ -109,13 +109,13 @@
       }
 
       const Instruction* payload = _.FindDef(inst->GetOperandAs<uint32_t>(10));
-      if (payload->opcode() != SpvOpVariable) {
+      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<uint32_t>(2) !=
-                     SpvStorageClassRayPayloadKHR &&
-                 payload->GetOperandAs<uint32_t>(2) !=
-                     SpvStorageClassIncomingRayPayloadKHR) {
+      } 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";
@@ -123,11 +123,11 @@
       break;
     }
 
-    case SpvOpReportIntersectionKHR: {
+    case spv::Op::OpReportIntersectionKHR: {
       _.function(inst->function()->id())
           ->RegisterExecutionModelLimitation(
-              [](SpvExecutionModel model, std::string* message) {
-                if (model != SpvExecutionModelIntersectionKHR) {
+              [](spv::ExecutionModel model, std::string* message) {
+                if (model != spv::ExecutionModel::IntersectionKHR) {
                   if (message) {
                     *message =
                         "OpReportIntersectionKHR requires IntersectionKHR "
@@ -158,14 +158,14 @@
       break;
     }
 
-    case SpvOpExecuteCallableKHR: {
+    case spv::Op::OpExecuteCallableKHR: {
       _.function(inst->function()->id())
-          ->RegisterExecutionModelLimitation([](SpvExecutionModel model,
+          ->RegisterExecutionModelLimitation([](spv::ExecutionModel model,
                                                 std::string* message) {
-            if (model != SpvExecutionModelRayGenerationKHR &&
-                model != SpvExecutionModelClosestHitKHR &&
-                model != SpvExecutionModelMissKHR &&
-                model != SpvExecutionModelCallableKHR) {
+            if (model != spv::ExecutionModel::RayGenerationKHR &&
+                model != spv::ExecutionModel::ClosestHitKHR &&
+                model != spv::ExecutionModel::MissKHR &&
+                model != spv::ExecutionModel::CallableKHR) {
               if (message) {
                 *message =
                     "OpExecuteCallableKHR requires RayGenerationKHR, "
@@ -184,13 +184,13 @@
       }
 
       const auto callable_data = _.FindDef(inst->GetOperandAs<uint32_t>(1));
-      if (callable_data->opcode() != SpvOpVariable) {
+      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<uint32_t>(2) !=
-                     SpvStorageClassCallableDataKHR &&
-                 callable_data->GetOperandAs<uint32_t>(2) !=
-                     SpvStorageClassIncomingCallableDataKHR) {
+      } 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";
diff --git a/source/val/validate_ray_tracing_reorder.cpp b/source/val/validate_ray_tracing_reorder.cpp
new file mode 100644
index 0000000..cb190f9
--- /dev/null
+++ b/source/val/validate_ray_tracing_reorder.cpp
@@ -0,0 +1,625 @@
+// Copyright (c) 2022 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.
+
+// Validates ray tracing instructions from SPV_NV_shader_execution_reorder
+
+#include "source/opcode.h"
+#include "source/val/instruction.h"
+#include "source/val/validate.h"
+#include "source/val/validation_state.h"
+
+#include <limits>
+
+namespace spvtools {
+namespace val {
+
+static const uint32_t KRayParamInvalidId = std::numeric_limits<uint32_t>::max();
+
+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)
+           << "Hit Object must be a pointer";
+  }
+  auto type = _.FindDef(pointer->GetOperandAs<uint32_t>(2));
+  if (!type || type->opcode() != spv::Op::OpTypeHitObjectNV) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Type must be OpTypeHitObjectNV";
+  }
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateHitObjectInstructionCommonParameters(
+    ValidationState_t& _, const Instruction* inst,
+    uint32_t acceleration_struct_index, uint32_t instance_id_index,
+    uint32_t primtive_id_index, uint32_t geometry_index,
+    uint32_t ray_flags_index, uint32_t cull_mask_index, uint32_t hit_kind_index,
+    uint32_t sbt_index, uint32_t sbt_offset_index, uint32_t sbt_stride_index,
+    uint32_t sbt_record_offset_index, uint32_t sbt_record_stride_index,
+    uint32_t miss_index, uint32_t ray_origin_index, uint32_t ray_tmin_index,
+    uint32_t ray_direction_index, uint32_t ray_tmax_index,
+    uint32_t payload_index, uint32_t hit_object_attr_index) {
+  auto isValidId = [](uint32_t spvid) { return spvid < KRayParamInvalidId; };
+  if (isValidId(acceleration_struct_index) &&
+      _.GetIdOpcode(_.GetOperandTypeId(inst, acceleration_struct_index)) !=
+          spv::Op::OpTypeAccelerationStructureKHR) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected Acceleration Structure to be of type "
+              "OpTypeAccelerationStructureKHR";
+  }
+
+  if (isValidId(instance_id_index)) {
+    const uint32_t instance_id = _.GetOperandTypeId(inst, instance_id_index);
+    if (!_.IsIntScalarType(instance_id) || _.GetBitWidth(instance_id) != 32) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Instance Id must be a 32-bit int scalar";
+    }
+  }
+
+  if (isValidId(primtive_id_index)) {
+    const uint32_t primitive_id = _.GetOperandTypeId(inst, primtive_id_index);
+    if (!_.IsIntScalarType(primitive_id) || _.GetBitWidth(primitive_id) != 32) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Primitive Id must be a 32-bit int scalar";
+    }
+  }
+
+  if (isValidId(geometry_index)) {
+    const uint32_t geometry_index_id = _.GetOperandTypeId(inst, geometry_index);
+    if (!_.IsIntScalarType(geometry_index_id) ||
+        _.GetBitWidth(geometry_index_id) != 32) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Geometry Index must be a 32-bit int scalar";
+    }
+  }
+
+  if (isValidId(miss_index)) {
+    const uint32_t miss_index_id = _.GetOperandTypeId(inst, miss_index);
+    if (!_.IsUnsignedIntScalarType(miss_index_id) ||
+        _.GetBitWidth(miss_index_id) != 32) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Miss Index must be a 32-bit int scalar";
+    }
+  }
+
+  if (isValidId(cull_mask_index)) {
+    const uint32_t cull_mask_id = _.GetOperandTypeId(inst, cull_mask_index);
+    if (!_.IsUnsignedIntScalarType(cull_mask_id) ||
+        _.GetBitWidth(cull_mask_id) != 32) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Cull mask must be a 32-bit int scalar";
+    }
+  }
+
+  if (isValidId(sbt_index)) {
+    const uint32_t sbt_index_id = _.GetOperandTypeId(inst, sbt_index);
+    if (!_.IsUnsignedIntScalarType(sbt_index_id) ||
+        _.GetBitWidth(sbt_index_id) != 32) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "SBT Index must be a 32-bit unsigned int scalar";
+    }
+  }
+
+  if (isValidId(sbt_offset_index)) {
+    const uint32_t sbt_offset_id = _.GetOperandTypeId(inst, sbt_offset_index);
+    if (!_.IsUnsignedIntScalarType(sbt_offset_id) ||
+        _.GetBitWidth(sbt_offset_id) != 32) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "SBT Offset must be a 32-bit unsigned int scalar";
+    }
+  }
+
+  if (isValidId(sbt_stride_index)) {
+    const uint32_t sbt_stride_index_id =
+        _.GetOperandTypeId(inst, sbt_stride_index);
+    if (!_.IsUnsignedIntScalarType(sbt_stride_index_id) ||
+        _.GetBitWidth(sbt_stride_index_id) != 32) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "SBT Stride must be a 32-bit unsigned int scalar";
+    }
+  }
+
+  if (isValidId(sbt_record_offset_index)) {
+    const uint32_t sbt_record_offset_index_id =
+        _.GetOperandTypeId(inst, sbt_record_offset_index);
+    if (!_.IsUnsignedIntScalarType(sbt_record_offset_index_id) ||
+        _.GetBitWidth(sbt_record_offset_index_id) != 32) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "SBT record offset must be a 32-bit unsigned int scalar";
+    }
+  }
+
+  if (isValidId(sbt_record_stride_index)) {
+    const uint32_t sbt_record_stride_index_id =
+        _.GetOperandTypeId(inst, sbt_record_stride_index);
+    if (!_.IsUnsignedIntScalarType(sbt_record_stride_index_id) ||
+        _.GetBitWidth(sbt_record_stride_index_id) != 32) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "SBT record stride must be a 32-bit unsigned int scalar";
+    }
+  }
+
+  if (isValidId(ray_origin_index)) {
+    const uint32_t ray_origin_id = _.GetOperandTypeId(inst, ray_origin_index);
+    if (!_.IsFloatVectorType(ray_origin_id) ||
+        _.GetDimension(ray_origin_id) != 3 ||
+        _.GetBitWidth(ray_origin_id) != 32) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Ray Origin must be a 32-bit float 3-component vector";
+    }
+  }
+
+  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) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Ray TMin must be a 32-bit float scalar";
+    }
+  }
+
+  if (isValidId(ray_direction_index)) {
+    const uint32_t ray_direction_id =
+        _.GetOperandTypeId(inst, ray_direction_index);
+    if (!_.IsFloatVectorType(ray_direction_id) ||
+        _.GetDimension(ray_direction_id) != 3 ||
+        _.GetBitWidth(ray_direction_id) != 32) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Ray Direction must be a 32-bit float 3-component vector";
+    }
+  }
+
+  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) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Ray TMax must be a 32-bit float scalar";
+    }
+  }
+
+  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) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Ray Flags must be a 32-bit int scalar";
+    }
+  }
+
+  if (isValidId(payload_index)) {
+    const uint32_t payload_id = inst->GetOperandAs<uint32_t>(payload_index);
+    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";
+    }
+  }
+
+  if (isValidId(hit_kind_index)) {
+    const uint32_t hit_kind_id = _.GetOperandTypeId(inst, hit_kind_index);
+    if (!_.IsUnsignedIntScalarType(hit_kind_id) ||
+        _.GetBitWidth(hit_kind_id) != 32) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Hit Kind must be a 32-bit unsigned int scalar";
+    }
+  }
+
+  if (isValidId(hit_object_attr_index)) {
+    const uint32_t hit_object_attr_id =
+        inst->GetOperandAs<uint32_t>(hit_object_attr_index);
+    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) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Hit Object Attributes id must be a OpVariable of storage "
+                "class HitObjectAttributeNV";
+    }
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t RayReorderNVPass(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::OpHitObjectIsMissNV:
+    case spv::Op::OpHitObjectIsHitNV:
+    case spv::Op::OpHitObjectIsEmptyNV: {
+      RegisterOpcodeForValidModel(_, inst);
+      if (!_.IsBoolScalarType(result_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "expected Result Type to be bool scalar type";
+      }
+
+      if (auto error = ValidateHitObjectPointer(_, inst, 2)) return error;
+      break;
+    }
+
+    case spv::Op::OpHitObjectGetShaderRecordBufferHandleNV: {
+      RegisterOpcodeForValidModel(_, inst);
+      if (auto error = ValidateHitObjectPointer(_, 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::OpHitObjectGetHitKindNV:
+    case spv::Op::OpHitObjectGetPrimitiveIndexNV:
+    case spv::Op::OpHitObjectGetGeometryIndexNV:
+    case spv::Op::OpHitObjectGetInstanceIdNV:
+    case spv::Op::OpHitObjectGetInstanceCustomIndexNV:
+    case spv::Op::OpHitObjectGetShaderBindingTableRecordIndexNV: {
+      RegisterOpcodeForValidModel(_, inst);
+      if (auto error = ValidateHitObjectPointer(_, inst, 2)) return error;
+
+      if (!_.IsIntScalarType(result_type) || !_.GetBitWidth(result_type))
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Expected 32-bit integer type scalar as Result Type: "
+               << spvOpcodeString(opcode);
+      break;
+    }
+
+    case spv::Op::OpHitObjectGetCurrentTimeNV:
+    case spv::Op::OpHitObjectGetRayTMaxNV:
+    case spv::Op::OpHitObjectGetRayTMinNV: {
+      RegisterOpcodeForValidModel(_, inst);
+      if (auto error = ValidateHitObjectPointer(_, inst, 2)) return error;
+
+      if (!_.IsFloatScalarType(result_type) || _.GetBitWidth(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::OpHitObjectGetObjectToWorldNV:
+    case spv::Op::OpHitObjectGetWorldToObjectNV: {
+      RegisterOpcodeForValidModel(_, inst);
+      if (auto error = ValidateHitObjectPointer(_, 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::OpHitObjectGetObjectRayOriginNV:
+    case spv::Op::OpHitObjectGetObjectRayDirectionNV:
+    case spv::Op::OpHitObjectGetWorldRayDirectionNV:
+    case spv::Op::OpHitObjectGetWorldRayOriginNV: {
+      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 type 3-component vector as "
+                  "Result Type: "
+               << spvOpcodeString(opcode);
+      break;
+    }
+
+    case spv::Op::OpHitObjectGetAttributesNV: {
+      RegisterOpcodeForValidModel(_, inst);
+      if (auto error = ValidateHitObjectPointer(_, 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::HitObjectAttributeNV) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Hit Object Attributes id must be a OpVariable of storage "
+                  "class HitObjectAttributeNV";
+      }
+      break;
+    }
+
+    case spv::Op::OpHitObjectExecuteShaderNV: {
+      RegisterOpcodeForValidModel(_, inst);
+      if (auto error = ValidateHitObjectPointer(_, 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::RayPayloadKHR) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Hit Object Attributes id must be a OpVariable of storage "
+                  "class RayPayloadKHR";
+      }
+      break;
+    }
+
+    case spv::Op::OpHitObjectRecordEmptyNV: {
+      RegisterOpcodeForValidModel(_, inst);
+      if (auto error = ValidateHitObjectPointer(_, inst, 0)) return error;
+      break;
+    }
+
+    case spv::Op::OpHitObjectRecordMissNV: {
+      RegisterOpcodeForValidModel(_, inst);
+      if (auto error = ValidateHitObjectPointer(_, inst, 0)) return error;
+
+      const uint32_t miss_index = _.GetOperandTypeId(inst, 1);
+      if (!_.IsUnsignedIntScalarType(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, 2);
+      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, 3);
+      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, 4);
+      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, 5);
+      if (!_.IsFloatScalarType(ray_tmax) || _.GetBitWidth(ray_tmax) != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Ray TMax must be a 32-bit float scalar";
+      }
+      break;
+    }
+
+    case spv::Op::OpHitObjectRecordHitWithIndexNV: {
+      RegisterOpcodeForValidModel(_, inst);
+      if (auto error = ValidateHitObjectPointer(_, inst, 0)) return error;
+
+      if (auto error = ValidateHitObjectInstructionCommonParameters(
+              _, inst, 1 /* Acceleration Struct */, 2 /* Instance Id */,
+              3 /* Primtive Id */, 4 /* Geometry Index */,
+              KRayParamInvalidId /* Ray Flags */,
+              KRayParamInvalidId /* Cull Mask */, 5 /* Hit Kind*/,
+              6 /* SBT index */, KRayParamInvalidId /* SBT Offset */,
+              KRayParamInvalidId /* SBT Stride */,
+              KRayParamInvalidId /* SBT Record Offset */,
+              KRayParamInvalidId /* SBT Record Stride */,
+              KRayParamInvalidId /* Miss Index */, 7 /* Ray Origin */,
+              8 /* Ray TMin */, 9 /* Ray Direction */, 10 /* Ray TMax */,
+              KRayParamInvalidId /* Payload */, 11 /* Hit Object Attribute */))
+        return error;
+
+      break;
+    }
+
+    case spv::Op::OpHitObjectRecordHitNV: {
+      RegisterOpcodeForValidModel(_, inst);
+      if (auto error = ValidateHitObjectPointer(_, inst, 0)) return error;
+
+      if (auto error = ValidateHitObjectInstructionCommonParameters(
+              _, inst, 1 /* Acceleration Struct */, 2 /* Instance Id */,
+              3 /* Primtive Id */, 4 /* Geometry Index */,
+              KRayParamInvalidId /* Ray Flags */,
+              KRayParamInvalidId /* Cull Mask */, 5 /* Hit Kind*/,
+              KRayParamInvalidId /* SBT index */,
+              KRayParamInvalidId /* SBT Offset */,
+              KRayParamInvalidId /* SBT Stride */, 6 /* SBT Record Offset */,
+              7 /* SBT Record Stride */, KRayParamInvalidId /* Miss Index */,
+              8 /* Ray Origin */, 9 /* Ray TMin */, 10 /* Ray Direction */,
+              11 /* Ray TMax */, KRayParamInvalidId /* Payload */,
+              12 /* Hit Object Attribute */))
+        return error;
+
+      break;
+    }
+
+    case spv::Op::OpHitObjectTraceRayMotionNV: {
+      RegisterOpcodeForValidModel(_, inst);
+      if (auto error = ValidateHitObjectPointer(_, inst, 0)) return error;
+
+      if (auto error = ValidateHitObjectInstructionCommonParameters(
+              _, inst, 1 /* Acceleration Struct */,
+              KRayParamInvalidId /* Instance Id */,
+              KRayParamInvalidId /* Primtive 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
+      const uint32_t current_time_id = _.GetOperandTypeId(inst, 11);
+      if (!_.IsFloatScalarType(current_time_id) ||
+          _.GetBitWidth(current_time_id) != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Current Times must be a 32-bit float scalar type";
+      }
+
+      break;
+    }
+
+    case spv::Op::OpHitObjectTraceRayNV: {
+      RegisterOpcodeForValidModel(_, inst);
+      if (auto error = ValidateHitObjectPointer(_, inst, 0)) return error;
+
+      if (auto error = ValidateHitObjectInstructionCommonParameters(
+              _, inst, 1 /* Acceleration Struct */,
+              KRayParamInvalidId /* Instance Id */,
+              KRayParamInvalidId /* Primtive 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::OpReorderThreadWithHitObjectNV: {
+      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 = ValidateHitObjectPointer(_, 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 opreands Hint and Bits
+        const uint32_t hint_id = _.GetOperandTypeId(inst, 1);
+        if (!_.IsIntScalarType(hint_id) || _.GetBitWidth(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) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << "bits must be a 32-bit int scalar";
+        }
+      }
+      break;
+    }
+
+    case spv::Op::OpReorderThreadWithHintNV: {
+      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) || _.GetBitWidth(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) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "bits must be a 32-bit int scalar";
+      }
+    }
+
+    default:
+      break;
+  }
+  return SPV_SUCCESS;
+}
+}  // namespace val
+}  // namespace spvtools
diff --git a/source/val/validate_scopes.cpp b/source/val/validate_scopes.cpp
index e978180..40c49d1 100644
--- a/source/val/validate_scopes.cpp
+++ b/source/val/validate_scopes.cpp
@@ -14,7 +14,6 @@
 
 #include "source/val/validate_scopes.h"
 
-#include "source/diagnostic.h"
 #include "source/spirv_target_env.h"
 #include "source/val/instruction.h"
 #include "source/val/validation_state.h"
@@ -25,16 +24,16 @@
 bool IsValidScope(uint32_t scope) {
   // Deliberately avoid a default case so we have to update the list when the
   // scopes list changes.
-  switch (static_cast<SpvScope>(scope)) {
-    case SpvScopeCrossDevice:
-    case SpvScopeDevice:
-    case SpvScopeWorkgroup:
-    case SpvScopeSubgroup:
-    case SpvScopeInvocation:
-    case SpvScopeQueueFamilyKHR:
-    case SpvScopeShaderCallKHR:
+  switch (static_cast<spv::Scope>(scope)) {
+    case spv::Scope::CrossDevice:
+    case spv::Scope::Device:
+    case spv::Scope::Workgroup:
+    case spv::Scope::Subgroup:
+    case spv::Scope::Invocation:
+    case spv::Scope::QueueFamilyKHR:
+    case spv::Scope::ShaderCallKHR:
       return true;
-    case SpvScopeMax:
+    case spv::Scope::Max:
       break;
   }
   return false;
@@ -42,7 +41,7 @@
 
 spv_result_t ValidateScope(ValidationState_t& _, const Instruction* inst,
                            uint32_t scope) {
-  SpvOp opcode = inst->opcode();
+  spv::Op opcode = inst->opcode();
   bool is_int32 = false, is_const_int32 = false;
   uint32_t value = 0;
   std::tie(is_int32, is_const_int32, value) = _.EvalInt32IfConst(scope);
@@ -53,14 +52,14 @@
   }
 
   if (!is_const_int32) {
-    if (_.HasCapability(SpvCapabilityShader) &&
-        !_.HasCapability(SpvCapabilityCooperativeMatrixNV)) {
+    if (_.HasCapability(spv::Capability::Shader) &&
+        !_.HasCapability(spv::Capability::CooperativeMatrixNV)) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Scope ids must be OpConstant when Shader capability is "
              << "present";
     }
-    if (_.HasCapability(SpvCapabilityShader) &&
-        _.HasCapability(SpvCapabilityCooperativeMatrixNV) &&
+    if (_.HasCapability(spv::Capability::Shader) &&
+        _.HasCapability(spv::Capability::CooperativeMatrixNV) &&
         !spvOpcodeIsConstant(_.GetIdOpcode(scope))) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Scope ids must be constant or specialization constant when "
@@ -78,10 +77,10 @@
 
 spv_result_t ValidateExecutionScope(ValidationState_t& _,
                                     const Instruction* inst, uint32_t scope) {
-  SpvOp opcode = inst->opcode();
+  spv::Op opcode = inst->opcode();
   bool is_int32 = false, is_const_int32 = false;
-  uint32_t value = 0;
-  std::tie(is_int32, is_const_int32, value) = _.EvalInt32IfConst(scope);
+  uint32_t tmp_value = 0;
+  std::tie(is_int32, is_const_int32, tmp_value) = _.EvalInt32IfConst(scope);
 
   if (auto error = ValidateScope(_, inst, scope)) {
     return error;
@@ -91,13 +90,15 @@
     return SPV_SUCCESS;
   }
 
+  spv::Scope value = spv::Scope(tmp_value);
+
   // Vulkan specific rules
   if (spvIsVulkanEnv(_.context()->target_env)) {
     // Vulkan 1.1 specific rules
     if (_.context()->target_env != SPV_ENV_VULKAN_1_0) {
       // Scope for Non Uniform Group Operations must be limited to Subgroup
       if (spvOpcodeIsNonUniformGroupOperation(opcode) &&
-          value != SpvScopeSubgroup) {
+          value != spv::Scope::Subgroup) {
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
                << _.VkErrorID(4642) << spvOpcodeString(opcode)
                << ": in Vulkan environment Execution scope is limited to "
@@ -107,21 +108,21 @@
 
     // OpControlBarrier must only use Subgroup execution scope for a subset of
     // execution models.
-    if (opcode == SpvOpControlBarrier && value != SpvScopeSubgroup) {
+    if (opcode == spv::Op::OpControlBarrier && value != spv::Scope::Subgroup) {
       std::string errorVUID = _.VkErrorID(4682);
       _.function(inst->function()->id())
           ->RegisterExecutionModelLimitation([errorVUID](
-                                                 SpvExecutionModel model,
+                                                 spv::ExecutionModel model,
                                                  std::string* message) {
-            if (model == SpvExecutionModelFragment ||
-                model == SpvExecutionModelVertex ||
-                model == SpvExecutionModelGeometry ||
-                model == SpvExecutionModelTessellationEvaluation ||
-                model == SpvExecutionModelRayGenerationKHR ||
-                model == SpvExecutionModelIntersectionKHR ||
-                model == SpvExecutionModelAnyHitKHR ||
-                model == SpvExecutionModelClosestHitKHR ||
-                model == SpvExecutionModelMissKHR) {
+            if (model == spv::ExecutionModel::Fragment ||
+                model == spv::ExecutionModel::Vertex ||
+                model == spv::ExecutionModel::Geometry ||
+                model == spv::ExecutionModel::TessellationEvaluation ||
+                model == spv::ExecutionModel::RayGenerationKHR ||
+                model == spv::ExecutionModel::IntersectionKHR ||
+                model == spv::ExecutionModel::AnyHitKHR ||
+                model == spv::ExecutionModel::ClosestHitKHR ||
+                model == spv::ExecutionModel::MissKHR) {
               if (message) {
                 *message =
                     errorVUID +
@@ -137,17 +138,17 @@
     }
 
     // Only subset of execution models support Workgroup.
-    if (value == SpvScopeWorkgroup) {
+    if (value == spv::Scope::Workgroup) {
       std::string errorVUID = _.VkErrorID(4637);
       _.function(inst->function()->id())
           ->RegisterExecutionModelLimitation(
-              [errorVUID](SpvExecutionModel model, std::string* message) {
-                if (model != SpvExecutionModelTaskNV &&
-                    model != SpvExecutionModelMeshNV &&
-                    model != SpvExecutionModelTaskEXT &&
-                    model != SpvExecutionModelMeshEXT &&
-                    model != SpvExecutionModelTessellationControl &&
-                    model != SpvExecutionModelGLCompute) {
+              [errorVUID](spv::ExecutionModel model, std::string* message) {
+                if (model != spv::ExecutionModel::TaskNV &&
+                    model != spv::ExecutionModel::MeshNV &&
+                    model != spv::ExecutionModel::TaskEXT &&
+                    model != spv::ExecutionModel::MeshEXT &&
+                    model != spv::ExecutionModel::TessellationControl &&
+                    model != spv::ExecutionModel::GLCompute) {
                   if (message) {
                     *message =
                         errorVUID +
@@ -163,7 +164,7 @@
 
     // Vulkan generic rules
     // Scope for execution must be limited to Workgroup or Subgroup
-    if (value != SpvScopeWorkgroup && value != SpvScopeSubgroup) {
+    if (value != spv::Scope::Workgroup && value != spv::Scope::Subgroup) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << _.VkErrorID(4636) << spvOpcodeString(opcode)
              << ": in Vulkan environment Execution Scope is limited to "
@@ -177,7 +178,7 @@
   // Scope for execution must be limited to Workgroup or Subgroup for
   // non-uniform operations
   if (spvOpcodeIsNonUniformGroupOperation(opcode) &&
-      value != SpvScopeSubgroup && value != SpvScopeWorkgroup) {
+      value != spv::Scope::Subgroup && value != spv::Scope::Workgroup) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << spvOpcodeString(opcode)
            << ": Execution scope is limited to Subgroup or Workgroup";
@@ -188,10 +189,10 @@
 
 spv_result_t ValidateMemoryScope(ValidationState_t& _, const Instruction* inst,
                                  uint32_t scope) {
-  const SpvOp opcode = inst->opcode();
+  const spv::Op opcode = inst->opcode();
   bool is_int32 = false, is_const_int32 = false;
-  uint32_t value = 0;
-  std::tie(is_int32, is_const_int32, value) = _.EvalInt32IfConst(scope);
+  uint32_t tmp_value = 0;
+  std::tie(is_int32, is_const_int32, tmp_value) = _.EvalInt32IfConst(scope);
 
   if (auto error = ValidateScope(_, inst, scope)) {
     return error;
@@ -201,8 +202,10 @@
     return SPV_SUCCESS;
   }
 
-  if (value == SpvScopeQueueFamilyKHR) {
-    if (_.HasCapability(SpvCapabilityVulkanMemoryModelKHR)) {
+  spv::Scope value = spv::Scope(tmp_value);
+
+  if (value == spv::Scope::QueueFamilyKHR) {
+    if (_.HasCapability(spv::Capability::VulkanMemoryModelKHR)) {
       return SPV_SUCCESS;
     } else {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
@@ -212,9 +215,9 @@
     }
   }
 
-  if (value == SpvScopeDevice &&
-      _.HasCapability(SpvCapabilityVulkanMemoryModelKHR) &&
-      !_.HasCapability(SpvCapabilityVulkanMemoryModelDeviceScopeKHR)) {
+  if (value == spv::Scope::Device &&
+      _.HasCapability(spv::Capability::VulkanMemoryModelKHR) &&
+      !_.HasCapability(spv::Capability::VulkanMemoryModelDeviceScopeKHR)) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "Use of device scope with VulkanKHR memory model requires the "
            << "VulkanMemoryModelDeviceScopeKHR capability";
@@ -222,36 +225,37 @@
 
   // Vulkan Specific rules
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    if (value != SpvScopeDevice && value != SpvScopeWorkgroup &&
-        value != SpvScopeSubgroup && value != SpvScopeInvocation &&
-        value != SpvScopeShaderCallKHR && value != SpvScopeQueueFamily) {
+    if (value != spv::Scope::Device && value != spv::Scope::Workgroup &&
+        value != spv::Scope::Subgroup && value != spv::Scope::Invocation &&
+        value != spv::Scope::ShaderCallKHR &&
+        value != spv::Scope::QueueFamily) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << _.VkErrorID(4638) << spvOpcodeString(opcode)
              << ": in Vulkan environment Memory Scope is limited to Device, "
                 "QueueFamily, Workgroup, ShaderCallKHR, Subgroup, or "
                 "Invocation";
     } else if (_.context()->target_env == SPV_ENV_VULKAN_1_0 &&
-               value == SpvScopeSubgroup &&
-               !_.HasCapability(SpvCapabilitySubgroupBallotKHR) &&
-               !_.HasCapability(SpvCapabilitySubgroupVoteKHR)) {
+               value == spv::Scope::Subgroup &&
+               !_.HasCapability(spv::Capability::SubgroupBallotKHR) &&
+               !_.HasCapability(spv::Capability::SubgroupVoteKHR)) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
-             << _.VkErrorID(6997) << spvOpcodeString(opcode)
+             << _.VkErrorID(7951) << spvOpcodeString(opcode)
              << ": in Vulkan 1.0 environment Memory Scope is can not be "
                 "Subgroup without SubgroupBallotKHR or SubgroupVoteKHR "
                 "declared";
     }
 
-    if (value == SpvScopeShaderCallKHR) {
+    if (value == spv::Scope::ShaderCallKHR) {
       std::string errorVUID = _.VkErrorID(4640);
       _.function(inst->function()->id())
           ->RegisterExecutionModelLimitation(
-              [errorVUID](SpvExecutionModel model, std::string* message) {
-                if (model != SpvExecutionModelRayGenerationKHR &&
-                    model != SpvExecutionModelIntersectionKHR &&
-                    model != SpvExecutionModelAnyHitKHR &&
-                    model != SpvExecutionModelClosestHitKHR &&
-                    model != SpvExecutionModelMissKHR &&
-                    model != SpvExecutionModelCallableKHR) {
+              [errorVUID](spv::ExecutionModel model, std::string* message) {
+                if (model != spv::ExecutionModel::RayGenerationKHR &&
+                    model != spv::ExecutionModel::IntersectionKHR &&
+                    model != spv::ExecutionModel::AnyHitKHR &&
+                    model != spv::ExecutionModel::ClosestHitKHR &&
+                    model != spv::ExecutionModel::MissKHR &&
+                    model != spv::ExecutionModel::CallableKHR) {
                   if (message) {
                     *message =
                         errorVUID +
@@ -264,17 +268,17 @@
               });
     }
 
-    if (value == SpvScopeWorkgroup) {
+    if (value == spv::Scope::Workgroup) {
       std::string errorVUID = _.VkErrorID(7321);
       _.function(inst->function()->id())
           ->RegisterExecutionModelLimitation(
-              [errorVUID](SpvExecutionModel model, std::string* message) {
-                if (model != SpvExecutionModelGLCompute &&
-                    model != SpvExecutionModelTessellationControl &&
-                    model != SpvExecutionModelTaskNV &&
-                    model != SpvExecutionModelMeshNV &&
-                    model != SpvExecutionModelTaskEXT &&
-                    model != SpvExecutionModelMeshEXT) {
+              [errorVUID](spv::ExecutionModel model, std::string* message) {
+                if (model != spv::ExecutionModel::GLCompute &&
+                    model != spv::ExecutionModel::TessellationControl &&
+                    model != spv::ExecutionModel::TaskNV &&
+                    model != spv::ExecutionModel::MeshNV &&
+                    model != spv::ExecutionModel::TaskEXT &&
+                    model != spv::ExecutionModel::MeshEXT) {
                   if (message) {
                     *message = errorVUID +
                                "Workgroup Memory Scope is limited to MeshNV, "
@@ -286,12 +290,12 @@
                 return true;
               });
 
-      if (_.memory_model() == SpvMemoryModelGLSL450) {
+      if (_.memory_model() == spv::MemoryModel::GLSL450) {
         errorVUID = _.VkErrorID(7320);
         _.function(inst->function()->id())
             ->RegisterExecutionModelLimitation(
-                [errorVUID](SpvExecutionModel model, std::string* message) {
-                  if (model == SpvExecutionModelTessellationControl) {
+                [errorVUID](spv::ExecutionModel model, std::string* message) {
+                  if (model == spv::ExecutionModel::TessellationControl) {
                     if (message) {
                       *message =
                           errorVUID +
diff --git a/source/val/validate_small_type_uses.cpp b/source/val/validate_small_type_uses.cpp
index 9db82e7..69f61ee 100644
--- a/source/val/validate_small_type_uses.cpp
+++ b/source/val/validate_small_type_uses.cpp
@@ -22,7 +22,7 @@
 
 spv_result_t ValidateSmallTypeUses(ValidationState_t& _,
                                    const Instruction* inst) {
-  if (!_.HasCapability(SpvCapabilityShader) || inst->type_id() == 0 ||
+  if (!_.HasCapability(spv::Capability::Shader) || inst->type_id() == 0 ||
       !_.ContainsLimitedUseIntOrFloatType(inst->type_id())) {
     return SPV_SUCCESS;
   }
@@ -36,13 +36,13 @@
   for (auto use : inst->uses()) {
     const auto* user = use.first;
     switch (user->opcode()) {
-      case SpvOpDecorate:
-      case SpvOpDecorateId:
-      case SpvOpCopyObject:
-      case SpvOpStore:
-      case SpvOpFConvert:
-      case SpvOpUConvert:
-      case SpvOpSConvert:
+      case spv::Op::OpDecorate:
+      case spv::Op::OpDecorateId:
+      case spv::Op::OpCopyObject:
+      case spv::Op::OpStore:
+      case spv::Op::OpFConvert:
+      case spv::Op::OpUConvert:
+      case spv::Op::OpSConvert:
         break;
       default:
         return _.diag(SPV_ERROR_INVALID_ID, user)
diff --git a/source/val/validate_type.cpp b/source/val/validate_type.cpp
index 6b0881c..7edd12f 100644
--- a/source/val/validate_type.cpp
+++ b/source/val/validate_type.cpp
@@ -19,7 +19,6 @@
 #include "source/val/instruction.h"
 #include "source/val/validate.h"
 #include "source/val/validation_state.h"
-#include "spirv/unified1/spirv.h"
 
 namespace spvtools {
 namespace val {
@@ -50,8 +49,8 @@
     return SPV_SUCCESS;
 
   const auto opcode = inst->opcode();
-  if (opcode != SpvOpTypeArray && opcode != SpvOpTypeRuntimeArray &&
-      opcode != SpvOpTypeStruct && opcode != SpvOpTypePointer &&
+  if (opcode != spv::Op::OpTypeArray && opcode != spv::Op::OpTypeRuntimeArray &&
+      opcode != spv::Op::OpTypeStruct && opcode != spv::Op::OpTypePointer &&
       !_.RegisterUniqueTypeDeclaration(inst)) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "Duplicate non-aggregate type declarations are not allowed. "
@@ -84,7 +83,7 @@
              << "Using a 16-bit integer type requires the Int16 capability,"
                 " or an extension that explicitly enables 16-bit integers.";
     } else if (num_bits == 64) {
-      if (_.HasCapability(SpvCapabilityInt64)) {
+      if (_.HasCapability(spv::Capability::Int64)) {
         return SPV_SUCCESS;
       }
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
@@ -105,7 +104,8 @@
 
   // SPIR-V Spec 2.16.3: Validation Rules for Kernel Capabilities: The
   // Signedness in OpTypeInt must always be 0.
-  if (SpvOpTypeInt == inst->opcode() && _.HasCapability(SpvCapabilityKernel) &&
+  if (spv::Op::OpTypeInt == inst->opcode() &&
+      _.HasCapability(spv::Capability::Kernel) &&
       inst->GetOperandAs<uint32_t>(2) != 0u) {
     return _.diag(SPV_ERROR_INVALID_BINARY, inst)
            << "The Signedness in OpTypeInt "
@@ -135,7 +135,7 @@
               " or an extension that explicitly enables 16-bit floating point.";
   }
   if (num_bits == 64) {
-    if (_.HasCapability(SpvCapabilityFloat64)) {
+    if (_.HasCapability(spv::Capability::Float64)) {
       return SPV_SUCCESS;
     }
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
@@ -163,7 +163,7 @@
   if (num_components == 2 || num_components == 3 || num_components == 4) {
     return SPV_SUCCESS;
   } else if (num_components == 8 || num_components == 16) {
-    if (_.HasCapability(SpvCapabilityVector16)) {
+    if (_.HasCapability(spv::Capability::Vector16)) {
       return SPV_SUCCESS;
     }
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
@@ -183,7 +183,7 @@
   const auto column_type_index = 1;
   const auto column_type_id = inst->GetOperandAs<uint32_t>(column_type_index);
   const auto column_type = _.FindDef(column_type_id);
-  if (!column_type || SpvOpTypeVector != column_type->opcode()) {
+  if (!column_type || spv::Op::OpTypeVector != column_type->opcode()) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "Columns in a matrix must be of type vector.";
   }
@@ -192,7 +192,7 @@
   // Operand 1 is the <id> of the type of data in the vector.
   const auto comp_type_id = column_type->GetOperandAs<uint32_t>(1);
   auto comp_type_instruction = _.FindDef(comp_type_id);
-  if (comp_type_instruction->opcode() != SpvOpTypeFloat) {
+  if (comp_type_instruction->opcode() != spv::Op::OpTypeFloat) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Matrix types can only be "
                                                    "parameterized with "
                                                    "floating-point types.";
@@ -219,14 +219,14 @@
            << " is not a type.";
   }
 
-  if (element_type->opcode() == SpvOpTypeVoid) {
+  if (element_type->opcode() == spv::Op::OpTypeVoid) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "OpTypeArray Element Type <id> " << _.getIdName(element_type_id)
            << " is a void type.";
   }
 
   if (spvIsVulkanEnv(_.context()->target_env) &&
-      element_type->opcode() == SpvOpTypeRuntimeArray) {
+      element_type->opcode() == spv::Op::OpTypeRuntimeArray) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << _.VkErrorID(4680) << "OpTypeArray Element Type <id> "
            << _.getIdName(element_type_id) << " is not valid in "
@@ -246,15 +246,15 @@
   const auto const_inst = length->words();
   const auto const_result_type_index = 1;
   const auto const_result_type = _.FindDef(const_inst[const_result_type_index]);
-  if (!const_result_type || SpvOpTypeInt != const_result_type->opcode()) {
+  if (!const_result_type || spv::Op::OpTypeInt != const_result_type->opcode()) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "OpTypeArray Length <id> " << _.getIdName(length_id)
            << " is not a constant integer type.";
   }
 
   switch (length->opcode()) {
-    case SpvOpSpecConstant:
-    case SpvOpConstant: {
+    case spv::Op::OpSpecConstant:
+    case spv::Op::OpConstant: {
       auto& type_words = const_result_type->words();
       const bool is_signed = type_words[3] > 0;
       const uint32_t width = type_words[2];
@@ -265,11 +265,11 @@
                << " default value must be at least 1: found " << ivalue;
       }
     } break;
-    case SpvOpConstantNull:
+    case spv::Op::OpConstantNull:
       return _.diag(SPV_ERROR_INVALID_ID, inst)
              << "OpTypeArray Length <id> " << _.getIdName(length_id)
              << " default value must be at least 1.";
-    case SpvOpSpecConstantOp:
+    case spv::Op::OpSpecConstantOp:
       // Assume it's OK, rather than try to evaluate the operation.
       break;
     default:
@@ -289,14 +289,14 @@
            << " is not a type.";
   }
 
-  if (element_type->opcode() == SpvOpTypeVoid) {
+  if (element_type->opcode() == spv::Op::OpTypeVoid) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "OpTypeRuntimeArray Element Type <id> " << _.getIdName(element_id)
            << " is a void type.";
   }
 
   if (spvIsVulkanEnv(_.context()->target_env) &&
-      element_type->opcode() == SpvOpTypeRuntimeArray) {
+      element_type->opcode() == spv::Op::OpTypeRuntimeArray) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << _.VkErrorID(4680) << "OpTypeRuntimeArray Element Type <id> "
            << _.getIdName(element_id) << " is not valid in "
@@ -322,11 +322,11 @@
              << "OpTypeStruct Member Type <id> " << _.getIdName(member_type_id)
              << " is not a type.";
     }
-    if (member_type->opcode() == SpvOpTypeVoid) {
+    if (member_type->opcode() == spv::Op::OpTypeVoid) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
              << "Structures cannot contain a void type.";
     }
-    if (SpvOpTypeStruct == member_type->opcode() &&
+    if (spv::Op::OpTypeStruct == member_type->opcode() &&
         _.IsStructTypeWithBuiltInMember(member_type_id)) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
              << "Structure <id> " << _.getIdName(member_type_id)
@@ -339,7 +339,7 @@
     }
 
     if (spvIsVulkanEnv(_.context()->target_env) &&
-        member_type->opcode() == SpvOpTypeRuntimeArray) {
+        member_type->opcode() == spv::Op::OpTypeRuntimeArray) {
       const bool is_last_member =
           member_type_index == inst->operands().size() - 1;
       if (!is_last_member) {
@@ -349,6 +349,15 @@
                << ", OpTypeRuntimeArray must only be used for the last member "
                   "of an OpTypeStruct";
       }
+
+      if (!_.HasDecoration(inst->id(), spv::Decoration::Block) &&
+          !_.HasDecoration(inst->id(), spv::Decoration::BufferBlock)) {
+        return _.diag(SPV_ERROR_INVALID_ID, inst)
+               << _.VkErrorID(4680)
+               << spvLogStringForEnv(_.context()->target_env)
+               << ", OpTypeStruct containing an OpTypeRuntimeArray "
+               << "must be decorated with Block or BufferBlock.";
+      }
     }
   }
 
@@ -357,9 +366,10 @@
   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 && SpvOpTypeStruct == memberTypeInstr->opcode()) {
-      if (_.HasDecoration(memberTypeInstr->id(), SpvDecorationBlock) ||
-          _.HasDecoration(memberTypeInstr->id(), SpvDecorationBufferBlock) ||
+    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;
     }
@@ -368,8 +378,8 @@
   _.SetHasNestedBlockOrBufferBlockStruct(inst->id(),
                                          has_nested_blockOrBufferBlock_struct);
   if (_.GetHasNestedBlockOrBufferBlockStruct(inst->id()) &&
-      (_.HasDecoration(inst->id(), SpvDecorationBufferBlock) ||
-       _.HasDecoration(inst->id(), SpvDecorationBlock))) {
+      (_.HasDecoration(inst->id(), spv::Decoration::BufferBlock) ||
+       _.HasDecoration(inst->id(), spv::Decoration::Block))) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "rules: A Block or BufferBlock cannot be nested within another "
               "Block or BufferBlock. ";
@@ -377,7 +387,7 @@
 
   std::unordered_set<uint32_t> built_in_members;
   for (auto decoration : _.id_decorations(struct_id)) {
-    if (decoration.dec_type() == SpvDecorationBuiltIn &&
+    if (decoration.dec_type() == spv::Decoration::BuiltIn &&
         decoration.struct_member_index() != Decoration::kInvalidMember) {
       built_in_members.insert(decoration.struct_member_index());
     }
@@ -398,9 +408,9 @@
 
   const auto isOpaqueType = [&_](const Instruction* opaque_inst) {
     auto opcode = opaque_inst->opcode();
-    if (_.HasCapability(SpvCapabilityBindlessTextureNV) &&
-        (opcode == SpvOpTypeImage || opcode == SpvOpTypeSampler ||
-         opcode == SpvOpTypeSampledImage)) {
+    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;
@@ -430,15 +440,15 @@
            << " is not a type.";
   }
   // See if this points to a storage image.
-  const auto storage_class = inst->GetOperandAs<SpvStorageClass>(1);
-  if (storage_class == SpvStorageClassUniformConstant) {
+  const auto storage_class = inst->GetOperandAs<spv::StorageClass>(1);
+  if (storage_class == spv::StorageClass::UniformConstant) {
     // Unpack an optional level of arraying.
-    if (type->opcode() == SpvOpTypeArray ||
-        type->opcode() == SpvOpTypeRuntimeArray) {
+    if (type->opcode() == spv::Op::OpTypeArray ||
+        type->opcode() == spv::Op::OpTypeRuntimeArray) {
       type_id = type->GetOperandAs<uint32_t>(1);
       type = _.FindDef(type_id);
     }
-    if (type->opcode() == SpvOpTypeImage) {
+    if (type->opcode() == spv::Op::OpTypeImage) {
       const auto sampled = type->GetOperandAs<uint32_t>(6);
       // 2 indicates this image is known to be be used without a sampler, i.e.
       // a storage image.
@@ -475,7 +485,7 @@
              << " is not a type.";
     }
 
-    if (param_type->opcode() == SpvOpTypeVoid) {
+    if (param_type->opcode() == spv::Op::OpTypeVoid) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
              << "OpTypeFunction Parameter Type <id> " << _.getIdName(param_id)
              << " cannot be OpTypeVoid.";
@@ -495,8 +505,9 @@
   // decoration instruction.
   for (auto& pair : inst->uses()) {
     const auto* use = pair.first;
-    if (use->opcode() != SpvOpFunction && !spvOpcodeIsDebug(use->opcode()) &&
-        !use->IsNonSemantic() && !spvOpcodeIsDecoration(use->opcode())) {
+    if (use->opcode() != spv::Op::OpFunction &&
+        !spvOpcodeIsDebug(use->opcode()) && !use->IsNonSemantic() &&
+        !spvOpcodeIsDecoration(use->opcode())) {
       return _.diag(SPV_ERROR_INVALID_ID, use)
              << "Invalid use of function type result id "
              << _.getIdName(inst->id()) << ".";
@@ -510,13 +521,13 @@
                                         const Instruction* inst) {
   const auto pointer_type_id = inst->GetOperandAs<uint32_t>(0);
   const auto pointer_type_inst = _.FindDef(pointer_type_id);
-  if (pointer_type_inst->opcode() != SpvOpTypePointer) {
+  if (pointer_type_inst->opcode() != spv::Op::OpTypePointer) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "Pointer type in OpTypeForwardPointer is not a pointer type.";
   }
 
-  const auto storage_class = inst->GetOperandAs<SpvStorageClass>(1);
-  if (storage_class != pointer_type_inst->GetOperandAs<uint32_t>(1)) {
+  const auto storage_class = inst->GetOperandAs<spv::StorageClass>(1);
+  if (storage_class != pointer_type_inst->GetOperandAs<spv::StorageClass>(1)) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "Storage class in OpTypeForwardPointer does not match the "
            << "pointer definition.";
@@ -524,13 +535,13 @@
 
   const auto pointee_type_id = pointer_type_inst->GetOperandAs<uint32_t>(2);
   const auto pointee_type = _.FindDef(pointee_type_id);
-  if (!pointee_type || pointee_type->opcode() != SpvOpTypeStruct) {
+  if (!pointee_type || pointee_type->opcode() != spv::Op::OpTypeStruct) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "Forward pointers must point to a structure";
   }
 
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    if (storage_class != SpvStorageClassPhysicalStorageBuffer) {
+    if (storage_class != spv::StorageClass::PhysicalStorageBuffer) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
              << _.VkErrorID(4711)
              << "In Vulkan, OpTypeForwardPointer must have "
@@ -541,16 +552,16 @@
   return SPV_SUCCESS;
 }
 
-spv_result_t ValidateTypeCooperativeMatrixNV(ValidationState_t& _,
-                                             const Instruction* inst) {
+spv_result_t ValidateTypeCooperativeMatrix(ValidationState_t& _,
+                                           const Instruction* inst) {
   const auto component_type_index = 1;
   const auto component_type_id =
       inst->GetOperandAs<uint32_t>(component_type_index);
   const auto component_type = _.FindDef(component_type_id);
-  if (!component_type || (SpvOpTypeFloat != component_type->opcode() &&
-                          SpvOpTypeInt != component_type->opcode())) {
+  if (!component_type || (spv::Op::OpTypeFloat != component_type->opcode() &&
+                          spv::Op::OpTypeInt != component_type->opcode())) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
-           << "OpTypeCooperativeMatrixNV Component Type <id> "
+           << "OpTypeCooperativeMatrix Component Type <id> "
            << _.getIdName(component_type_id)
            << " is not a scalar numerical type.";
   }
@@ -561,7 +572,7 @@
   if (!scope || !_.IsIntScalarType(scope->type_id()) ||
       !spvOpcodeIsConstant(scope->opcode())) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
-           << "OpTypeCooperativeMatrixNV Scope <id> " << _.getIdName(scope_id)
+           << "OpTypeCooperativeMatrix Scope <id> " << _.getIdName(scope_id)
            << " is not a constant instruction with scalar integer type.";
   }
 
@@ -571,7 +582,7 @@
   if (!rows || !_.IsIntScalarType(rows->type_id()) ||
       !spvOpcodeIsConstant(rows->opcode())) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
-           << "OpTypeCooperativeMatrixNV Rows <id> " << _.getIdName(rows_id)
+           << "OpTypeCooperativeMatrix Rows <id> " << _.getIdName(rows_id)
            << " is not a constant instruction with scalar integer type.";
   }
 
@@ -581,55 +592,68 @@
   if (!cols || !_.IsIntScalarType(cols->type_id()) ||
       !spvOpcodeIsConstant(cols->opcode())) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
-           << "OpTypeCooperativeMatrixNV Cols <id> " << _.getIdName(cols_id)
+           << "OpTypeCooperativeMatrix Cols <id> " << _.getIdName(cols_id)
            << " is not a constant instruction with scalar integer type.";
   }
 
+  if (inst->opcode() == spv::Op::OpTypeCooperativeMatrixKHR) {
+    const auto use_index = 5;
+    const auto use_id = inst->GetOperandAs<uint32_t>(use_index);
+    const auto use = _.FindDef(use_id);
+    if (!use || !_.IsIntScalarType(use->type_id()) ||
+        !spvOpcodeIsConstant(use->opcode())) {
+      return _.diag(SPV_ERROR_INVALID_ID, inst)
+             << "OpTypeCooperativeMatrixKHR Use <id> " << _.getIdName(use_id)
+             << " is not a constant instruction with scalar integer type.";
+    }
+  }
+
   return SPV_SUCCESS;
 }
 }  // namespace
 
 spv_result_t TypePass(ValidationState_t& _, const Instruction* inst) {
   if (!spvOpcodeGeneratesType(inst->opcode()) &&
-      inst->opcode() != SpvOpTypeForwardPointer) {
+      inst->opcode() != spv::Op::OpTypeForwardPointer) {
     return SPV_SUCCESS;
   }
 
   if (auto error = ValidateUniqueness(_, inst)) return error;
 
   switch (inst->opcode()) {
-    case SpvOpTypeInt:
+    case spv::Op::OpTypeInt:
       if (auto error = ValidateTypeInt(_, inst)) return error;
       break;
-    case SpvOpTypeFloat:
+    case spv::Op::OpTypeFloat:
       if (auto error = ValidateTypeFloat(_, inst)) return error;
       break;
-    case SpvOpTypeVector:
+    case spv::Op::OpTypeVector:
       if (auto error = ValidateTypeVector(_, inst)) return error;
       break;
-    case SpvOpTypeMatrix:
+    case spv::Op::OpTypeMatrix:
       if (auto error = ValidateTypeMatrix(_, inst)) return error;
       break;
-    case SpvOpTypeArray:
+    case spv::Op::OpTypeArray:
       if (auto error = ValidateTypeArray(_, inst)) return error;
       break;
-    case SpvOpTypeRuntimeArray:
+    case spv::Op::OpTypeRuntimeArray:
       if (auto error = ValidateTypeRuntimeArray(_, inst)) return error;
       break;
-    case SpvOpTypeStruct:
+    case spv::Op::OpTypeStruct:
       if (auto error = ValidateTypeStruct(_, inst)) return error;
       break;
-    case SpvOpTypePointer:
+    case spv::Op::OpTypePointer:
       if (auto error = ValidateTypePointer(_, inst)) return error;
       break;
-    case SpvOpTypeFunction:
+    case spv::Op::OpTypeFunction:
       if (auto error = ValidateTypeFunction(_, inst)) return error;
       break;
-    case SpvOpTypeForwardPointer:
+    case spv::Op::OpTypeForwardPointer:
       if (auto error = ValidateTypeForwardPointer(_, inst)) return error;
       break;
-    case SpvOpTypeCooperativeMatrixNV:
-      if (auto error = ValidateTypeCooperativeMatrixNV(_, inst)) return error;
+    case spv::Op::OpTypeCooperativeMatrixNV:
+    case spv::Op::OpTypeCooperativeMatrixKHR:
+      if (auto error = ValidateTypeCooperativeMatrix(_, inst)) return error;
       break;
     default:
       break;
diff --git a/source/val/validation_state.cpp b/source/val/validation_state.cpp
index d5ddc9c..6685985 100644
--- a/source/val/validation_state.cpp
+++ b/source/val/validation_state.cpp
@@ -21,6 +21,7 @@
 #include "source/opcode.h"
 #include "source/spirv_constant.h"
 #include "source/spirv_target_env.h"
+#include "source/util/make_unique.h"
 #include "source/val/basic_block.h"
 #include "source/val/construct.h"
 #include "source/val/function.h"
@@ -31,66 +32,66 @@
 namespace {
 
 ModuleLayoutSection InstructionLayoutSection(
-    ModuleLayoutSection current_section, SpvOp op) {
+    ModuleLayoutSection current_section, spv::Op op) {
   // See Section 2.4
   if (spvOpcodeGeneratesType(op) || spvOpcodeIsConstant(op))
     return kLayoutTypes;
 
   switch (op) {
-    case SpvOpCapability:
+    case spv::Op::OpCapability:
       return kLayoutCapabilities;
-    case SpvOpExtension:
+    case spv::Op::OpExtension:
       return kLayoutExtensions;
-    case SpvOpExtInstImport:
+    case spv::Op::OpExtInstImport:
       return kLayoutExtInstImport;
-    case SpvOpMemoryModel:
+    case spv::Op::OpMemoryModel:
       return kLayoutMemoryModel;
-    case SpvOpEntryPoint:
+    case spv::Op::OpEntryPoint:
       return kLayoutEntryPoint;
-    case SpvOpExecutionMode:
-    case SpvOpExecutionModeId:
+    case spv::Op::OpExecutionMode:
+    case spv::Op::OpExecutionModeId:
       return kLayoutExecutionMode;
-    case SpvOpSourceContinued:
-    case SpvOpSource:
-    case SpvOpSourceExtension:
-    case SpvOpString:
+    case spv::Op::OpSourceContinued:
+    case spv::Op::OpSource:
+    case spv::Op::OpSourceExtension:
+    case spv::Op::OpString:
       return kLayoutDebug1;
-    case SpvOpName:
-    case SpvOpMemberName:
+    case spv::Op::OpName:
+    case spv::Op::OpMemberName:
       return kLayoutDebug2;
-    case SpvOpModuleProcessed:
+    case spv::Op::OpModuleProcessed:
       return kLayoutDebug3;
-    case SpvOpDecorate:
-    case SpvOpMemberDecorate:
-    case SpvOpGroupDecorate:
-    case SpvOpGroupMemberDecorate:
-    case SpvOpDecorationGroup:
-    case SpvOpDecorateId:
-    case SpvOpDecorateStringGOOGLE:
-    case SpvOpMemberDecorateStringGOOGLE:
+    case spv::Op::OpDecorate:
+    case spv::Op::OpMemberDecorate:
+    case spv::Op::OpGroupDecorate:
+    case spv::Op::OpGroupMemberDecorate:
+    case spv::Op::OpDecorationGroup:
+    case spv::Op::OpDecorateId:
+    case spv::Op::OpDecorateStringGOOGLE:
+    case spv::Op::OpMemberDecorateStringGOOGLE:
       return kLayoutAnnotations;
-    case SpvOpTypeForwardPointer:
+    case spv::Op::OpTypeForwardPointer:
       return kLayoutTypes;
-    case SpvOpVariable:
+    case spv::Op::OpVariable:
       if (current_section == kLayoutTypes) return kLayoutTypes;
       return kLayoutFunctionDefinitions;
-    case SpvOpExtInst:
-      // SpvOpExtInst is only allowed in types section for certain extended
-      // instruction sets. This will be checked separately.
+    case spv::Op::OpExtInst:
+      // 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;
       return kLayoutFunctionDefinitions;
-    case SpvOpLine:
-    case SpvOpNoLine:
-    case SpvOpUndef:
+    case spv::Op::OpLine:
+    case spv::Op::OpNoLine:
+    case spv::Op::OpUndef:
       if (current_section == kLayoutTypes) return kLayoutTypes;
       return kLayoutFunctionDefinitions;
-    case SpvOpFunction:
-    case SpvOpFunctionParameter:
-    case SpvOpFunctionEnd:
+    case spv::Op::OpFunction:
+    case spv::Op::OpFunctionParameter:
+    case spv::Op::OpFunctionEnd:
       if (current_section == kLayoutFunctionDeclarations)
         return kLayoutFunctionDeclarations;
       return kLayoutFunctionDefinitions;
-    case SpvOpSamplerImageAddressingModeNV:
+    case spv::Op::OpSamplerImageAddressingModeNV:
       return kLayoutSamplerImageAddressMode;
     default:
       break;
@@ -98,7 +99,7 @@
   return kLayoutFunctionDefinitions;
 }
 
-bool IsInstructionInLayoutSection(ModuleLayoutSection layout, SpvOp op) {
+bool IsInstructionInLayoutSection(ModuleLayoutSection layout, spv::Op op) {
   return layout == InstructionLayoutSection(layout, op);
 }
 
@@ -106,7 +107,9 @@
 spv_result_t CountInstructions(void* user_data,
                                const spv_parsed_instruction_t* inst) {
   ValidationState_t& _ = *(reinterpret_cast<ValidationState_t*>(user_data));
-  if (inst->opcode == SpvOpFunction) _.increment_total_functions();
+  if (spv::Op(inst->opcode) == spv::Op::OpFunction) {
+    _.increment_total_functions();
+  }
   _.increment_total_instructions();
 
   return SPV_SUCCESS;
@@ -160,8 +163,8 @@
       struct_nesting_depth_(),
       struct_has_nested_blockorbufferblock_struct_(),
       grammar_(ctx),
-      addressing_model_(SpvAddressingModelMax),
-      memory_model_(SpvMemoryModelMax),
+      addressing_model_(spv::AddressingModel::Max),
+      memory_model_(spv::MemoryModel::Max),
       pointer_size_and_alignment_(0),
       sampler_image_addressing_mode_(0),
       in_function_(false),
@@ -290,13 +293,13 @@
   }
 }
 
-bool ValidationState_t::IsOpcodeInPreviousLayoutSection(SpvOp op) {
+bool ValidationState_t::IsOpcodeInPreviousLayoutSection(spv::Op op) {
   ModuleLayoutSection section =
       InstructionLayoutSection(current_layout_section_, op);
   return section < current_layout_section_;
 }
 
-bool ValidationState_t::IsOpcodeInCurrentLayoutSection(SpvOp op) {
+bool ValidationState_t::IsOpcodeInCurrentLayoutSection(spv::Op op) {
   return IsInstructionInLayoutSection(current_layout_section_, op);
 }
 
@@ -353,59 +356,61 @@
          module_functions_.back().current_block() != nullptr;
 }
 
-void ValidationState_t::RegisterCapability(SpvCapability cap) {
+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
   // it's something.)
-  if (module_capabilities_.Contains(cap)) return;
+  if (module_capabilities_.contains(cap)) return;
 
-  module_capabilities_.Add(cap);
+  module_capabilities_.insert(cap);
   spv_operand_desc desc;
-  if (SPV_SUCCESS ==
-      grammar_.lookupOperand(SPV_OPERAND_TYPE_CAPABILITY, cap, &desc)) {
-    CapabilitySet(desc->numCapabilities, desc->capabilities)
-        .ForEach([this](SpvCapability c) { RegisterCapability(c); });
+  if (SPV_SUCCESS == grammar_.lookupOperand(SPV_OPERAND_TYPE_CAPABILITY,
+                                            uint32_t(cap), &desc)) {
+    for (auto capability :
+         CapabilitySet(desc->numCapabilities, desc->capabilities)) {
+      RegisterCapability(capability);
+    }
   }
 
   switch (cap) {
-    case SpvCapabilityKernel:
+    case spv::Capability::Kernel:
       features_.group_ops_reduce_and_scans = true;
       break;
-    case SpvCapabilityInt8:
+    case spv::Capability::Int8:
       features_.use_int8_type = true;
       features_.declare_int8_type = true;
       break;
-    case SpvCapabilityStorageBuffer8BitAccess:
-    case SpvCapabilityUniformAndStorageBuffer8BitAccess:
-    case SpvCapabilityStoragePushConstant8:
-    case SpvCapabilityWorkgroupMemoryExplicitLayout8BitAccessKHR:
+    case spv::Capability::StorageBuffer8BitAccess:
+    case spv::Capability::UniformAndStorageBuffer8BitAccess:
+    case spv::Capability::StoragePushConstant8:
+    case spv::Capability::WorkgroupMemoryExplicitLayout8BitAccessKHR:
       features_.declare_int8_type = true;
       break;
-    case SpvCapabilityInt16:
+    case spv::Capability::Int16:
       features_.declare_int16_type = true;
       break;
-    case SpvCapabilityFloat16:
-    case SpvCapabilityFloat16Buffer:
+    case spv::Capability::Float16:
+    case spv::Capability::Float16Buffer:
       features_.declare_float16_type = true;
       break;
-    case SpvCapabilityStorageUniformBufferBlock16:
-    case SpvCapabilityStorageUniform16:
-    case SpvCapabilityStoragePushConstant16:
-    case SpvCapabilityStorageInputOutput16:
-    case SpvCapabilityWorkgroupMemoryExplicitLayout16BitAccessKHR:
+    case spv::Capability::StorageUniformBufferBlock16:
+    case spv::Capability::StorageUniform16:
+    case spv::Capability::StoragePushConstant16:
+    case spv::Capability::StorageInputOutput16:
+    case spv::Capability::WorkgroupMemoryExplicitLayout16BitAccessKHR:
       features_.declare_int16_type = true;
       features_.declare_float16_type = true;
       features_.free_fp_rounding_mode = true;
       break;
-    case SpvCapabilityVariablePointers:
-    case SpvCapabilityVariablePointersStorageBuffer:
+    case spv::Capability::VariablePointers:
+    case spv::Capability::VariablePointersStorageBuffer:
       features_.variable_pointers = true;
       break;
     default:
       // TODO(dneto): For now don't validate SPV_NV_ray_tracing, which uses
-      // capability SpvCapabilityRayTracingNV.
-      // SpvCapabilityRayTracingProvisionalKHR would need the same treatment.
-      // One of the differences going from SPV_KHR_ray_tracing from
+      // capability spv::Capability::RayTracingNV.
+      // spv::Capability::RayTracingProvisionalKHR would need the same
+      // treatment. One of the differences going from SPV_KHR_ray_tracing from
       // provisional to final spec was the provisional spec uses Locations
       // for variables in certain storage classes, just like the
       // SPV_NV_ray_tracing extension.  So it mimics the NVIDIA extension.
@@ -416,9 +421,9 @@
 }
 
 void ValidationState_t::RegisterExtension(Extension ext) {
-  if (module_extensions_.Contains(ext)) return;
+  if (module_extensions_.contains(ext)) return;
 
-  module_extensions_.Add(ext);
+  module_extensions_.insert(ext);
 
   switch (ext) {
     case kSPV_AMD_gpu_shader_half_float:
@@ -454,30 +459,32 @@
   return module_extensions_.HasAnyOf(extensions);
 }
 
-void ValidationState_t::set_addressing_model(SpvAddressingModel am) {
+void ValidationState_t::set_addressing_model(spv::AddressingModel am) {
   addressing_model_ = am;
   switch (am) {
-    case SpvAddressingModelPhysical32:
+    case spv::AddressingModel::Physical32:
       pointer_size_and_alignment_ = 4;
       break;
     default:
     // fall through
-    case SpvAddressingModelPhysical64:
-    case SpvAddressingModelPhysicalStorageBuffer64:
+    case spv::AddressingModel::Physical64:
+    case spv::AddressingModel::PhysicalStorageBuffer64:
       pointer_size_and_alignment_ = 8;
       break;
   }
 }
 
-SpvAddressingModel ValidationState_t::addressing_model() const {
+spv::AddressingModel ValidationState_t::addressing_model() const {
   return addressing_model_;
 }
 
-void ValidationState_t::set_memory_model(SpvMemoryModel mm) {
+void ValidationState_t::set_memory_model(spv::MemoryModel mm) {
   memory_model_ = mm;
 }
 
-SpvMemoryModel ValidationState_t::memory_model() const { return memory_model_; }
+spv::MemoryModel ValidationState_t::memory_model() const {
+  return memory_model_;
+}
 
 void ValidationState_t::set_samplerimage_variable_address_mode(
     uint32_t bit_width) {
@@ -489,8 +496,8 @@
 }
 
 spv_result_t ValidationState_t::RegisterFunction(
-    uint32_t id, uint32_t ret_type_id, SpvFunctionControlMask function_control,
-    uint32_t function_type_id) {
+    uint32_t id, uint32_t ret_type_id,
+    spv::FunctionControlMask function_control, uint32_t function_type_id) {
   assert(in_function_body() == false &&
          "RegisterFunction can only be called when parsing the binary outside "
          "of another function");
@@ -526,24 +533,24 @@
 // Improves diagnostic messages by collecting names of IDs
 void ValidationState_t::RegisterDebugInstruction(const Instruction* inst) {
   switch (inst->opcode()) {
-    case SpvOpName: {
+    case spv::Op::OpName: {
       const auto target = inst->GetOperandAs<uint32_t>(0);
       const std::string str = inst->GetOperandAs<std::string>(1);
       AssignNameToId(target, str);
       break;
     }
-    case SpvOpMemberName: {
+    case spv::Op::OpMemberName: {
       const auto target = inst->GetOperandAs<uint32_t>(0);
       const std::string str = inst->GetOperandAs<std::string>(2);
       AssignNameToId(target, str);
       break;
     }
-    case SpvOpSourceContinued:
-    case SpvOpSource:
-    case SpvOpSourceExtension:
-    case SpvOpString:
-    case SpvOpLine:
-    case SpvOpNoLine:
+    case spv::Op::OpSourceContinued:
+    case spv::Op::OpSource:
+    case spv::Op::OpSourceExtension:
+    case spv::Op::OpString:
+    case spv::Op::OpLine:
+    case spv::Op::OpNoLine:
     default:
       break;
   }
@@ -567,7 +574,7 @@
       // should be recorded. The validator will ensure that all usages of an
       // OpTypeSampledImage and its definition are in the same basic block.
       if ((SPV_OPERAND_TYPE_ID == operand.type) &&
-          (SpvOpSampledImage == operand_inst->opcode())) {
+          (spv::Op::OpSampledImage == operand_inst->opcode())) {
         RegisterSampledImageConsumer(operand_word, inst);
       }
 
@@ -577,12 +584,12 @@
       // Instead just need to register storage class usage for consumers in a
       // function block.
       if (inst->function()) {
-        if (operand_inst->opcode() == SpvOpTypePointer) {
+        if (operand_inst->opcode() == spv::Op::OpTypePointer) {
           RegisterStorageClassConsumer(
-              operand_inst->GetOperandAs<SpvStorageClass>(1), inst);
-        } else if (operand_inst->opcode() == SpvOpVariable) {
+              operand_inst->GetOperandAs<spv::StorageClass>(1), inst);
+        } else if (operand_inst->opcode() == spv::Op::OpVariable) {
           RegisterStorageClassConsumer(
-              operand_inst->GetOperandAs<SpvStorageClass>(2), inst);
+              operand_inst->GetOperandAs<spv::StorageClass>(2), inst);
         }
       }
     }
@@ -604,22 +611,34 @@
   sampled_image_consumers_[sampled_image_id].push_back(consumer);
 }
 
+void ValidationState_t::RegisterQCOMImageProcessingTextureConsumer(
+    uint32_t texture_id, const Instruction* consumer0,
+    const Instruction* consumer1) {
+  if (HasDecoration(texture_id, spv::Decoration::WeightTextureQCOM) ||
+      HasDecoration(texture_id, spv::Decoration::BlockMatchTextureQCOM)) {
+    qcom_image_processing_consumers_.insert(consumer0->id());
+    if (consumer1) {
+      qcom_image_processing_consumers_.insert(consumer1->id());
+    }
+  }
+}
+
 void ValidationState_t::RegisterStorageClassConsumer(
-    SpvStorageClass storage_class, Instruction* consumer) {
+    spv::StorageClass storage_class, Instruction* consumer) {
   if (spvIsVulkanEnv(context()->target_env)) {
-    if (storage_class == SpvStorageClassOutput) {
+    if (storage_class == spv::StorageClass::Output) {
       std::string errorVUID = VkErrorID(4644);
       function(consumer->function()->id())
           ->RegisterExecutionModelLimitation([errorVUID](
-                                                 SpvExecutionModel model,
+                                                 spv::ExecutionModel model,
                                                  std::string* message) {
-            if (model == SpvExecutionModelGLCompute ||
-                model == SpvExecutionModelRayGenerationKHR ||
-                model == SpvExecutionModelIntersectionKHR ||
-                model == SpvExecutionModelAnyHitKHR ||
-                model == SpvExecutionModelClosestHitKHR ||
-                model == SpvExecutionModelMissKHR ||
-                model == SpvExecutionModelCallableKHR) {
+            if (model == spv::ExecutionModel::GLCompute ||
+                model == spv::ExecutionModel::RayGenerationKHR ||
+                model == spv::ExecutionModel::IntersectionKHR ||
+                model == spv::ExecutionModel::AnyHitKHR ||
+                model == spv::ExecutionModel::ClosestHitKHR ||
+                model == spv::ExecutionModel::MissKHR ||
+                model == spv::ExecutionModel::CallableKHR) {
               if (message) {
                 *message =
                     errorVUID +
@@ -634,17 +653,17 @@
           });
     }
 
-    if (storage_class == SpvStorageClassWorkgroup) {
+    if (storage_class == spv::StorageClass::Workgroup) {
       std::string errorVUID = VkErrorID(4645);
       function(consumer->function()->id())
           ->RegisterExecutionModelLimitation([errorVUID](
-                                                 SpvExecutionModel model,
+                                                 spv::ExecutionModel model,
                                                  std::string* message) {
-            if (model != SpvExecutionModelGLCompute &&
-                model != SpvExecutionModelTaskNV &&
-                model != SpvExecutionModelMeshNV &&
-                model != SpvExecutionModelTaskEXT &&
-                model != SpvExecutionModelMeshEXT) {
+            if (model != spv::ExecutionModel::GLCompute &&
+                model != spv::ExecutionModel::TaskNV &&
+                model != spv::ExecutionModel::MeshNV &&
+                model != spv::ExecutionModel::TaskEXT &&
+                model != spv::ExecutionModel::MeshEXT) {
               if (message) {
                 *message =
                     errorVUID +
@@ -658,48 +677,51 @@
     }
   }
 
-  if (storage_class == SpvStorageClassCallableDataKHR) {
+  if (storage_class == spv::StorageClass::CallableDataKHR) {
     std::string errorVUID = VkErrorID(4704);
     function(consumer->function()->id())
-        ->RegisterExecutionModelLimitation([errorVUID](SpvExecutionModel model,
-                                                       std::string* message) {
-          if (model != SpvExecutionModelRayGenerationKHR &&
-              model != SpvExecutionModelClosestHitKHR &&
-              model != SpvExecutionModelCallableKHR &&
-              model != SpvExecutionModelMissKHR) {
-            if (message) {
-              *message = errorVUID +
-                         "CallableDataKHR Storage Class is limited to "
-                         "RayGenerationKHR, ClosestHitKHR, CallableKHR, and "
-                         "MissKHR execution model";
-            }
-            return false;
-          }
-          return true;
-        });
-  } else if (storage_class == SpvStorageClassIncomingCallableDataKHR) {
+        ->RegisterExecutionModelLimitation(
+            [errorVUID](spv::ExecutionModel model, std::string* message) {
+              if (model != spv::ExecutionModel::RayGenerationKHR &&
+                  model != spv::ExecutionModel::ClosestHitKHR &&
+                  model != spv::ExecutionModel::CallableKHR &&
+                  model != spv::ExecutionModel::MissKHR) {
+                if (message) {
+                  *message =
+                      errorVUID +
+                      "CallableDataKHR Storage Class is limited to "
+                      "RayGenerationKHR, ClosestHitKHR, CallableKHR, and "
+                      "MissKHR execution model";
+                }
+                return false;
+              }
+              return true;
+            });
+  } else if (storage_class == spv::StorageClass::IncomingCallableDataKHR) {
     std::string errorVUID = VkErrorID(4705);
     function(consumer->function()->id())
-        ->RegisterExecutionModelLimitation([errorVUID](SpvExecutionModel model,
-                                                       std::string* message) {
-          if (model != SpvExecutionModelCallableKHR) {
-            if (message) {
-              *message = errorVUID +
-                         "IncomingCallableDataKHR Storage Class is limited to "
-                         "CallableKHR execution model";
-            }
-            return false;
-          }
-          return true;
-        });
-  } else if (storage_class == SpvStorageClassRayPayloadKHR) {
+        ->RegisterExecutionModelLimitation(
+            [errorVUID](spv::ExecutionModel model, std::string* message) {
+              if (model != spv::ExecutionModel::CallableKHR) {
+                if (message) {
+                  *message =
+                      errorVUID +
+                      "IncomingCallableDataKHR Storage Class is limited to "
+                      "CallableKHR execution model";
+                }
+                return false;
+              }
+              return true;
+            });
+  } else if (storage_class == spv::StorageClass::RayPayloadKHR) {
     std::string errorVUID = VkErrorID(4698);
     function(consumer->function()->id())
-        ->RegisterExecutionModelLimitation([errorVUID](SpvExecutionModel model,
-                                                       std::string* message) {
-          if (model != SpvExecutionModelRayGenerationKHR &&
-              model != SpvExecutionModelClosestHitKHR &&
-              model != SpvExecutionModelMissKHR) {
+        ->RegisterExecutionModelLimitation([errorVUID](
+                                               spv::ExecutionModel model,
+                                               std::string* message) {
+          if (model != spv::ExecutionModel::RayGenerationKHR &&
+              model != spv::ExecutionModel::ClosestHitKHR &&
+              model != spv::ExecutionModel::MissKHR) {
             if (message) {
               *message =
                   errorVUID +
@@ -710,14 +732,14 @@
           }
           return true;
         });
-  } else if (storage_class == SpvStorageClassHitAttributeKHR) {
+  } else if (storage_class == spv::StorageClass::HitAttributeKHR) {
     std::string errorVUID = VkErrorID(4701);
     function(consumer->function()->id())
         ->RegisterExecutionModelLimitation(
-            [errorVUID](SpvExecutionModel model, std::string* message) {
-              if (model != SpvExecutionModelIntersectionKHR &&
-                  model != SpvExecutionModelAnyHitKHR &&
-                  model != SpvExecutionModelClosestHitKHR) {
+            [errorVUID](spv::ExecutionModel model, std::string* message) {
+              if (model != spv::ExecutionModel::IntersectionKHR &&
+                  model != spv::ExecutionModel::AnyHitKHR &&
+                  model != spv::ExecutionModel::ClosestHitKHR) {
                 if (message) {
                   *message = errorVUID +
                              "HitAttributeKHR Storage Class is limited to "
@@ -728,14 +750,14 @@
               }
               return true;
             });
-  } else if (storage_class == SpvStorageClassIncomingRayPayloadKHR) {
+  } else if (storage_class == spv::StorageClass::IncomingRayPayloadKHR) {
     std::string errorVUID = VkErrorID(4699);
     function(consumer->function()->id())
         ->RegisterExecutionModelLimitation(
-            [errorVUID](SpvExecutionModel model, std::string* message) {
-              if (model != SpvExecutionModelAnyHitKHR &&
-                  model != SpvExecutionModelClosestHitKHR &&
-                  model != SpvExecutionModelMissKHR) {
+            [errorVUID](spv::ExecutionModel model, std::string* message) {
+              if (model != spv::ExecutionModel::AnyHitKHR &&
+                  model != spv::ExecutionModel::ClosestHitKHR &&
+                  model != spv::ExecutionModel::MissKHR) {
                 if (message) {
                   *message =
                       errorVUID +
@@ -746,17 +768,17 @@
               }
               return true;
             });
-  } else if (storage_class == SpvStorageClassShaderRecordBufferKHR) {
+  } else if (storage_class == spv::StorageClass::ShaderRecordBufferKHR) {
     std::string errorVUID = VkErrorID(7119);
     function(consumer->function()->id())
         ->RegisterExecutionModelLimitation(
-            [errorVUID](SpvExecutionModel model, std::string* message) {
-              if (model != SpvExecutionModelRayGenerationKHR &&
-                  model != SpvExecutionModelIntersectionKHR &&
-                  model != SpvExecutionModelAnyHitKHR &&
-                  model != SpvExecutionModelClosestHitKHR &&
-                  model != SpvExecutionModelCallableKHR &&
-                  model != SpvExecutionModelMissKHR) {
+            [errorVUID](spv::ExecutionModel model, std::string* message) {
+              if (model != spv::ExecutionModel::RayGenerationKHR &&
+                  model != spv::ExecutionModel::IntersectionKHR &&
+                  model != spv::ExecutionModel::AnyHitKHR &&
+                  model != spv::ExecutionModel::ClosestHitKHR &&
+                  model != spv::ExecutionModel::CallableKHR &&
+                  model != spv::ExecutionModel::MissKHR) {
                 if (message) {
                   *message =
                       errorVUID +
@@ -768,12 +790,12 @@
               }
               return true;
             });
-  } else if (storage_class == SpvStorageClassTaskPayloadWorkgroupEXT) {
+  } else if (storage_class == spv::StorageClass::TaskPayloadWorkgroupEXT) {
     function(consumer->function()->id())
         ->RegisterExecutionModelLimitation(
-            [](SpvExecutionModel model, std::string* message) {
-              if (model != SpvExecutionModelTaskEXT &&
-                  model != SpvExecutionModelMeshEXT) {
+            [](spv::ExecutionModel model, std::string* message) {
+              if (model != spv::ExecutionModel::TaskEXT &&
+                  model != spv::ExecutionModel::MeshEXT) {
                 if (message) {
                   *message =
                       "TaskPayloadWorkgroupEXT Storage Class is limited to "
@@ -783,6 +805,22 @@
               }
               return true;
             });
+  } else if (storage_class == spv::StorageClass::HitObjectAttributeNV) {
+    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 =
+                  "HitObjectAttributeNV Storage Class is limited to "
+                  "RayGenerationKHR, ClosestHitKHR or MissKHR execution model";
+            }
+            return false;
+          }
+          return true;
+        });
   }
 }
 
@@ -814,9 +852,9 @@
   return inst ? inst->type_id() : 0;
 }
 
-SpvOp ValidationState_t::GetIdOpcode(uint32_t id) const {
+spv::Op ValidationState_t::GetIdOpcode(uint32_t id) const {
   const Instruction* inst = FindDef(id);
-  return inst ? inst->opcode() : SpvOpNop;
+  return inst ? inst->opcode() : spv::Op::OpNop;
 }
 
 uint32_t ValidationState_t::GetComponentType(uint32_t id) const {
@@ -824,18 +862,19 @@
   assert(inst);
 
   switch (inst->opcode()) {
-    case SpvOpTypeFloat:
-    case SpvOpTypeInt:
-    case SpvOpTypeBool:
+    case spv::Op::OpTypeFloat:
+    case spv::Op::OpTypeInt:
+    case spv::Op::OpTypeBool:
       return id;
 
-    case SpvOpTypeVector:
+    case spv::Op::OpTypeVector:
       return inst->word(2);
 
-    case SpvOpTypeMatrix:
+    case spv::Op::OpTypeMatrix:
       return GetComponentType(inst->word(2));
 
-    case SpvOpTypeCooperativeMatrixNV:
+    case spv::Op::OpTypeCooperativeMatrixNV:
+    case spv::Op::OpTypeCooperativeMatrixKHR:
       return inst->word(2);
 
     default:
@@ -853,16 +892,17 @@
   assert(inst);
 
   switch (inst->opcode()) {
-    case SpvOpTypeFloat:
-    case SpvOpTypeInt:
-    case SpvOpTypeBool:
+    case spv::Op::OpTypeFloat:
+    case spv::Op::OpTypeInt:
+    case spv::Op::OpTypeBool:
       return 1;
 
-    case SpvOpTypeVector:
-    case SpvOpTypeMatrix:
+    case spv::Op::OpTypeVector:
+    case spv::Op::OpTypeMatrix:
       return inst->word(3);
 
-    case SpvOpTypeCooperativeMatrixNV:
+    case spv::Op::OpTypeCooperativeMatrixNV:
+    case spv::Op::OpTypeCooperativeMatrixKHR:
       // Actual dimension isn't known, return 0
       return 0;
 
@@ -881,10 +921,11 @@
   const Instruction* inst = FindDef(component_type_id);
   assert(inst);
 
-  if (inst->opcode() == SpvOpTypeFloat || inst->opcode() == SpvOpTypeInt)
+  if (inst->opcode() == spv::Op::OpTypeFloat ||
+      inst->opcode() == spv::Op::OpTypeInt)
     return inst->word(2);
 
-  if (inst->opcode() == SpvOpTypeBool) return 1;
+  if (inst->opcode() == spv::Op::OpTypeBool) return 1;
 
   assert(0);
   return 0;
@@ -892,12 +933,12 @@
 
 bool ValidationState_t::IsVoidType(uint32_t id) const {
   const Instruction* inst = FindDef(id);
-  return inst && inst->opcode() == SpvOpTypeVoid;
+  return inst && inst->opcode() == spv::Op::OpTypeVoid;
 }
 
 bool ValidationState_t::IsFloatScalarType(uint32_t id) const {
   const Instruction* inst = FindDef(id);
-  return inst && inst->opcode() == SpvOpTypeFloat;
+  return inst && inst->opcode() == spv::Op::OpTypeFloat;
 }
 
 bool ValidationState_t::IsFloatVectorType(uint32_t id) const {
@@ -906,7 +947,7 @@
     return false;
   }
 
-  if (inst->opcode() == SpvOpTypeVector) {
+  if (inst->opcode() == spv::Op::OpTypeVector) {
     return IsFloatScalarType(GetComponentType(id));
   }
 
@@ -919,11 +960,11 @@
     return false;
   }
 
-  if (inst->opcode() == SpvOpTypeFloat) {
+  if (inst->opcode() == spv::Op::OpTypeFloat) {
     return true;
   }
 
-  if (inst->opcode() == SpvOpTypeVector) {
+  if (inst->opcode() == spv::Op::OpTypeVector) {
     return IsFloatScalarType(GetComponentType(id));
   }
 
@@ -932,7 +973,7 @@
 
 bool ValidationState_t::IsIntScalarType(uint32_t id) const {
   const Instruction* inst = FindDef(id);
-  return inst && inst->opcode() == SpvOpTypeInt;
+  return inst && inst->opcode() == spv::Op::OpTypeInt;
 }
 
 bool ValidationState_t::IsIntVectorType(uint32_t id) const {
@@ -941,7 +982,7 @@
     return false;
   }
 
-  if (inst->opcode() == SpvOpTypeVector) {
+  if (inst->opcode() == spv::Op::OpTypeVector) {
     return IsIntScalarType(GetComponentType(id));
   }
 
@@ -954,11 +995,11 @@
     return false;
   }
 
-  if (inst->opcode() == SpvOpTypeInt) {
+  if (inst->opcode() == spv::Op::OpTypeInt) {
     return true;
   }
 
-  if (inst->opcode() == SpvOpTypeVector) {
+  if (inst->opcode() == spv::Op::OpTypeVector) {
     return IsIntScalarType(GetComponentType(id));
   }
 
@@ -967,7 +1008,7 @@
 
 bool ValidationState_t::IsUnsignedIntScalarType(uint32_t id) const {
   const Instruction* inst = FindDef(id);
-  return inst && inst->opcode() == SpvOpTypeInt && inst->word(3) == 0;
+  return inst && inst->opcode() == spv::Op::OpTypeInt && inst->word(3) == 0;
 }
 
 bool ValidationState_t::IsUnsignedIntVectorType(uint32_t id) const {
@@ -976,7 +1017,24 @@
     return false;
   }
 
-  if (inst->opcode() == SpvOpTypeVector) {
+  if (inst->opcode() == spv::Op::OpTypeVector) {
+    return IsUnsignedIntScalarType(GetComponentType(id));
+  }
+
+  return false;
+}
+
+bool ValidationState_t::IsUnsignedIntScalarOrVectorType(uint32_t id) const {
+  const Instruction* inst = FindDef(id);
+  if (!inst) {
+    return false;
+  }
+
+  if (inst->opcode() == spv::Op::OpTypeInt) {
+    return inst->GetOperandAs<uint32_t>(2) == 0;
+  }
+
+  if (inst->opcode() == spv::Op::OpTypeVector) {
     return IsUnsignedIntScalarType(GetComponentType(id));
   }
 
@@ -985,7 +1043,7 @@
 
 bool ValidationState_t::IsSignedIntScalarType(uint32_t id) const {
   const Instruction* inst = FindDef(id);
-  return inst && inst->opcode() == SpvOpTypeInt && inst->word(3) == 1;
+  return inst && inst->opcode() == spv::Op::OpTypeInt && inst->word(3) == 1;
 }
 
 bool ValidationState_t::IsSignedIntVectorType(uint32_t id) const {
@@ -994,7 +1052,7 @@
     return false;
   }
 
-  if (inst->opcode() == SpvOpTypeVector) {
+  if (inst->opcode() == spv::Op::OpTypeVector) {
     return IsSignedIntScalarType(GetComponentType(id));
   }
 
@@ -1003,7 +1061,7 @@
 
 bool ValidationState_t::IsBoolScalarType(uint32_t id) const {
   const Instruction* inst = FindDef(id);
-  return inst && inst->opcode() == SpvOpTypeBool;
+  return inst && inst->opcode() == spv::Op::OpTypeBool;
 }
 
 bool ValidationState_t::IsBoolVectorType(uint32_t id) const {
@@ -1012,7 +1070,7 @@
     return false;
   }
 
-  if (inst->opcode() == SpvOpTypeVector) {
+  if (inst->opcode() == spv::Op::OpTypeVector) {
     return IsBoolScalarType(GetComponentType(id));
   }
 
@@ -1025,11 +1083,11 @@
     return false;
   }
 
-  if (inst->opcode() == SpvOpTypeBool) {
+  if (inst->opcode() == spv::Op::OpTypeBool) {
     return true;
   }
 
-  if (inst->opcode() == SpvOpTypeVector) {
+  if (inst->opcode() == spv::Op::OpTypeVector) {
     return IsBoolScalarType(GetComponentType(id));
   }
 
@@ -1042,7 +1100,7 @@
     return false;
   }
 
-  if (inst->opcode() == SpvOpTypeMatrix) {
+  if (inst->opcode() == spv::Op::OpTypeMatrix) {
     return IsFloatScalarType(GetComponentType(id));
   }
 
@@ -1057,13 +1115,13 @@
 
   const Instruction* mat_inst = FindDef(id);
   assert(mat_inst);
-  if (mat_inst->opcode() != SpvOpTypeMatrix) return false;
+  if (mat_inst->opcode() != spv::Op::OpTypeMatrix) return false;
 
   const uint32_t vec_type = mat_inst->word(2);
   const Instruction* vec_inst = FindDef(vec_type);
   assert(vec_inst);
 
-  if (vec_inst->opcode() != SpvOpTypeVector) {
+  if (vec_inst->opcode() != spv::Op::OpTypeVector) {
     assert(0);
     return false;
   }
@@ -1083,7 +1141,7 @@
 
   const Instruction* inst = FindDef(struct_type_id);
   assert(inst);
-  if (inst->opcode() != SpvOpTypeStruct) return false;
+  if (inst->opcode() != spv::Op::OpTypeStruct) return false;
 
   *member_types =
       std::vector<uint32_t>(inst->words().cbegin() + 2, inst->words().cend());
@@ -1095,44 +1153,91 @@
 
 bool ValidationState_t::IsPointerType(uint32_t id) const {
   const Instruction* inst = FindDef(id);
-  return inst && inst->opcode() == SpvOpTypePointer;
+  return inst && inst->opcode() == spv::Op::OpTypePointer;
 }
 
-bool ValidationState_t::GetPointerTypeInfo(uint32_t id, uint32_t* data_type,
-                                           uint32_t* storage_class) const {
+bool ValidationState_t::GetPointerTypeInfo(
+    uint32_t id, uint32_t* data_type, spv::StorageClass* storage_class) const {
+  *storage_class = spv::StorageClass::Max;
   if (!id) return false;
 
   const Instruction* inst = FindDef(id);
   assert(inst);
-  if (inst->opcode() != SpvOpTypePointer) return false;
+  if (inst->opcode() != spv::Op::OpTypePointer) return false;
 
-  *storage_class = inst->word(2);
+  *storage_class = spv::StorageClass(inst->word(2));
   *data_type = inst->word(3);
   return true;
 }
 
 bool ValidationState_t::IsAccelerationStructureType(uint32_t id) const {
   const Instruction* inst = FindDef(id);
-  return inst && inst->opcode() == SpvOpTypeAccelerationStructureKHR;
+  return inst && inst->opcode() == spv::Op::OpTypeAccelerationStructureKHR;
 }
 
 bool ValidationState_t::IsCooperativeMatrixType(uint32_t id) const {
   const Instruction* inst = FindDef(id);
-  return inst && inst->opcode() == SpvOpTypeCooperativeMatrixNV;
+  return inst && (inst->opcode() == spv::Op::OpTypeCooperativeMatrixNV ||
+                  inst->opcode() == spv::Op::OpTypeCooperativeMatrixKHR);
+}
+
+bool ValidationState_t::IsCooperativeMatrixNVType(uint32_t id) const {
+  const Instruction* inst = FindDef(id);
+  return inst && inst->opcode() == spv::Op::OpTypeCooperativeMatrixNV;
+}
+
+bool ValidationState_t::IsCooperativeMatrixKHRType(uint32_t id) const {
+  const Instruction* inst = FindDef(id);
+  return inst && inst->opcode() == spv::Op::OpTypeCooperativeMatrixKHR;
+}
+
+bool ValidationState_t::IsCooperativeMatrixAType(uint32_t id) const {
+  if (!IsCooperativeMatrixKHRType(id)) return false;
+  const Instruction* inst = FindDef(id);
+  uint64_t matrixUse = 0;
+  if (GetConstantValUint64(inst->word(6), &matrixUse)) {
+    return matrixUse ==
+           static_cast<uint64_t>(spv::CooperativeMatrixUse::MatrixAKHR);
+  }
+  return false;
+}
+
+bool ValidationState_t::IsCooperativeMatrixBType(uint32_t id) const {
+  if (!IsCooperativeMatrixKHRType(id)) return false;
+  const Instruction* inst = FindDef(id);
+  uint64_t matrixUse = 0;
+  if (GetConstantValUint64(inst->word(6), &matrixUse)) {
+    return matrixUse ==
+           static_cast<uint64_t>(spv::CooperativeMatrixUse::MatrixBKHR);
+  }
+  return false;
+}
+bool ValidationState_t::IsCooperativeMatrixAccType(uint32_t id) const {
+  if (!IsCooperativeMatrixKHRType(id)) return false;
+  const Instruction* inst = FindDef(id);
+  uint64_t matrixUse = 0;
+  if (GetConstantValUint64(inst->word(6), &matrixUse)) {
+    return matrixUse == static_cast<uint64_t>(
+                            spv::CooperativeMatrixUse::MatrixAccumulatorKHR);
+  }
+  return false;
 }
 
 bool ValidationState_t::IsFloatCooperativeMatrixType(uint32_t id) const {
-  if (!IsCooperativeMatrixType(id)) return false;
+  if (!IsCooperativeMatrixNVType(id) && !IsCooperativeMatrixKHRType(id))
+    return false;
   return IsFloatScalarType(FindDef(id)->word(2));
 }
 
 bool ValidationState_t::IsIntCooperativeMatrixType(uint32_t id) const {
-  if (!IsCooperativeMatrixType(id)) return false;
+  if (!IsCooperativeMatrixNVType(id) && !IsCooperativeMatrixKHRType(id))
+    return false;
   return IsIntScalarType(FindDef(id)->word(2));
 }
 
 bool ValidationState_t::IsUnsignedIntCooperativeMatrixType(uint32_t id) const {
-  if (!IsCooperativeMatrixType(id)) return false;
+  if (!IsCooperativeMatrixNVType(id) && !IsCooperativeMatrixKHRType(id))
+    return false;
   return IsUnsignedIntScalarType(FindDef(id)->word(2));
 }
 
@@ -1148,8 +1253,7 @@
   const auto m1_type = FindDef(m1);
   const auto m2_type = FindDef(m2);
 
-  if (m1_type->opcode() != SpvOpTypeCooperativeMatrixNV ||
-      m2_type->opcode() != SpvOpTypeCooperativeMatrixNV) {
+  if (m1_type->opcode() != m2_type->opcode()) {
     return diag(SPV_ERROR_INVALID_DATA, inst)
            << "Expected cooperative matrix types";
   }
@@ -1199,6 +1303,21 @@
            << "identical";
   }
 
+  if (m1_type->opcode() == spv::Op::OpTypeCooperativeMatrixKHR) {
+    uint32_t m1_use_id = m1_type->GetOperandAs<uint32_t>(5);
+    uint32_t m2_use_id = m2_type->GetOperandAs<uint32_t>(5);
+    std::tie(m1_is_int32, m1_is_const_int32, m1_value) =
+        EvalInt32IfConst(m1_use_id);
+    std::tie(m2_is_int32, m2_is_const_int32, m2_value) =
+        EvalInt32IfConst(m2_use_id);
+
+    if (m1_is_const_int32 && m2_is_const_int32 && m1_value != m2_value) {
+      return diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Expected Use of Matrix type and Result Type to be "
+             << "identical";
+    }
+  }
+
   return SPV_SUCCESS;
 }
 
@@ -1214,7 +1333,8 @@
     return false;
   }
 
-  if (inst->opcode() != SpvOpConstant && inst->opcode() != SpvOpSpecConstant)
+  if (inst->opcode() != spv::Op::OpConstant &&
+      inst->opcode() != spv::Op::OpSpecConstant)
     return false;
 
   if (!IsIntScalarType(inst->type_id())) return false;
@@ -1246,7 +1366,7 @@
     return std::make_tuple(true, false, 0);
   }
 
-  if (inst->opcode() == SpvOpConstantNull) {
+  if (inst->opcode() == spv::Op::OpConstantNull) {
     return std::make_tuple(true, true, 0);
   }
 
@@ -1380,7 +1500,7 @@
     }
   }
 
-  if (lhs->opcode() == SpvOpTypeArray) {
+  if (lhs->opcode() == spv::Op::OpTypeArray) {
     // Size operands must match.
     if (lhs->GetOperandAs<uint32_t>(2u) != rhs->GetOperandAs<uint32_t>(2u)) {
       return false;
@@ -1399,7 +1519,7 @@
       return false;
     }
     return LogicallyMatch(lhs_ele, rhs_ele, check_decorations);
-  } else if (lhs->opcode() == SpvOpTypeStruct) {
+  } else if (lhs->opcode() == spv::Op::OpTypeStruct) {
     // Number of elements must match.
     if (lhs->operands().size() != rhs->operands().size()) {
       return false;
@@ -1437,11 +1557,11 @@
 const Instruction* ValidationState_t::TracePointer(
     const Instruction* inst) const {
   auto base_ptr = inst;
-  while (base_ptr->opcode() == SpvOpAccessChain ||
-         base_ptr->opcode() == SpvOpInBoundsAccessChain ||
-         base_ptr->opcode() == SpvOpPtrAccessChain ||
-         base_ptr->opcode() == SpvOpInBoundsPtrAccessChain ||
-         base_ptr->opcode() == SpvOpCopyObject) {
+  while (base_ptr->opcode() == spv::Op::OpAccessChain ||
+         base_ptr->opcode() == spv::Op::OpInBoundsAccessChain ||
+         base_ptr->opcode() == spv::Op::OpPtrAccessChain ||
+         base_ptr->opcode() == spv::Op::OpInBoundsPtrAccessChain ||
+         base_ptr->opcode() == spv::Op::OpCopyObject) {
     base_ptr = FindDef(base_ptr->GetOperandAs<uint32_t>(2u));
   }
   return base_ptr;
@@ -1456,25 +1576,26 @@
   if (f(inst)) return true;
 
   switch (inst->opcode()) {
-    case SpvOpTypeArray:
-    case SpvOpTypeRuntimeArray:
-    case SpvOpTypeVector:
-    case SpvOpTypeMatrix:
-    case SpvOpTypeImage:
-    case SpvOpTypeSampledImage:
-    case SpvOpTypeCooperativeMatrixNV:
+    case spv::Op::OpTypeArray:
+    case spv::Op::OpTypeRuntimeArray:
+    case spv::Op::OpTypeVector:
+    case spv::Op::OpTypeMatrix:
+    case spv::Op::OpTypeImage:
+    case spv::Op::OpTypeSampledImage:
+    case spv::Op::OpTypeCooperativeMatrixNV:
+    case spv::Op::OpTypeCooperativeMatrixKHR:
       return ContainsType(inst->GetOperandAs<uint32_t>(1u), f,
                           traverse_all_types);
-    case SpvOpTypePointer:
+    case spv::Op::OpTypePointer:
       if (IsForwardPointer(id)) return false;
       if (traverse_all_types) {
         return ContainsType(inst->GetOperandAs<uint32_t>(2u), f,
                             traverse_all_types);
       }
       break;
-    case SpvOpTypeFunction:
-    case SpvOpTypeStruct:
-      if (inst->opcode() == SpvOpTypeFunction && !traverse_all_types) {
+    case spv::Op::OpTypeFunction:
+    case spv::Op::OpTypeStruct:
+      if (inst->opcode() == spv::Op::OpTypeFunction && !traverse_all_types) {
         return false;
       }
       for (uint32_t i = 1; i < inst->operands().size(); ++i) {
@@ -1491,9 +1612,9 @@
   return false;
 }
 
-bool ValidationState_t::ContainsSizedIntOrFloatType(uint32_t id, SpvOp type,
+bool ValidationState_t::ContainsSizedIntOrFloatType(uint32_t id, spv::Op type,
                                                     uint32_t width) const {
-  if (type != SpvOpTypeInt && type != SpvOpTypeFloat) return false;
+  if (type != spv::Op::OpTypeInt && type != spv::Op::OpTypeFloat) return false;
 
   const auto f = [type, width](const Instruction* inst) {
     if (inst->opcode() == type) {
@@ -1505,12 +1626,12 @@
 }
 
 bool ValidationState_t::ContainsLimitedUseIntOrFloatType(uint32_t id) const {
-  if ((!HasCapability(SpvCapabilityInt16) &&
-       ContainsSizedIntOrFloatType(id, SpvOpTypeInt, 16)) ||
-      (!HasCapability(SpvCapabilityInt8) &&
-       ContainsSizedIntOrFloatType(id, SpvOpTypeInt, 8)) ||
-      (!HasCapability(SpvCapabilityFloat16) &&
-       ContainsSizedIntOrFloatType(id, SpvOpTypeFloat, 16))) {
+  if ((!HasCapability(spv::Capability::Int16) &&
+       ContainsSizedIntOrFloatType(id, spv::Op::OpTypeInt, 16)) ||
+      (!HasCapability(spv::Capability::Int8) &&
+       ContainsSizedIntOrFloatType(id, spv::Op::OpTypeInt, 8)) ||
+      (!HasCapability(spv::Capability::Float16) &&
+       ContainsSizedIntOrFloatType(id, spv::Op::OpTypeFloat, 16))) {
     return true;
   }
   return false;
@@ -1518,33 +1639,35 @@
 
 bool ValidationState_t::ContainsRuntimeArray(uint32_t id) const {
   const auto f = [](const Instruction* inst) {
-    return inst->opcode() == SpvOpTypeRuntimeArray;
+    return inst->opcode() == spv::Op::OpTypeRuntimeArray;
   };
   return ContainsType(id, f, /* traverse_all_types = */ false);
 }
 
 bool ValidationState_t::IsValidStorageClass(
-    SpvStorageClass storage_class) const {
+    spv::StorageClass storage_class) const {
   if (spvIsVulkanEnv(context()->target_env)) {
     switch (storage_class) {
-      case SpvStorageClassUniformConstant:
-      case SpvStorageClassUniform:
-      case SpvStorageClassStorageBuffer:
-      case SpvStorageClassInput:
-      case SpvStorageClassOutput:
-      case SpvStorageClassImage:
-      case SpvStorageClassWorkgroup:
-      case SpvStorageClassPrivate:
-      case SpvStorageClassFunction:
-      case SpvStorageClassPushConstant:
-      case SpvStorageClassPhysicalStorageBuffer:
-      case SpvStorageClassRayPayloadKHR:
-      case SpvStorageClassIncomingRayPayloadKHR:
-      case SpvStorageClassHitAttributeKHR:
-      case SpvStorageClassCallableDataKHR:
-      case SpvStorageClassIncomingCallableDataKHR:
-      case SpvStorageClassShaderRecordBufferKHR:
-      case SpvStorageClassTaskPayloadWorkgroupEXT:
+      case spv::StorageClass::UniformConstant:
+      case spv::StorageClass::Uniform:
+      case spv::StorageClass::StorageBuffer:
+      case spv::StorageClass::Input:
+      case spv::StorageClass::Output:
+      case spv::StorageClass::Image:
+      case spv::StorageClass::Workgroup:
+      case spv::StorageClass::Private:
+      case spv::StorageClass::Function:
+      case spv::StorageClass::PushConstant:
+      case spv::StorageClass::PhysicalStorageBuffer:
+      case spv::StorageClass::RayPayloadKHR:
+      case spv::StorageClass::IncomingRayPayloadKHR:
+      case spv::StorageClass::HitAttributeKHR:
+      case spv::StorageClass::CallableDataKHR:
+      case spv::StorageClass::IncomingCallableDataKHR:
+      case spv::StorageClass::ShaderRecordBufferKHR:
+      case spv::StorageClass::TaskPayloadWorkgroupEXT:
+      case spv::StorageClass::HitObjectAttributeNV:
+      case spv::StorageClass::TileImageEXT:
         return true;
       default:
         return false;
@@ -2020,8 +2143,6 @@
       return VUID_WRAP(VUID-StandaloneSpirv-OpImageTexelPointer-04658);
     case 4659:
       return VUID_WRAP(VUID-StandaloneSpirv-OpImageQuerySizeLod-04659);
-    case 4662:
-      return VUID_WRAP(VUID-StandaloneSpirv-Offset-04662);
     case 4663:
       return VUID_WRAP(VUID-StandaloneSpirv-Offset-04663);
     case 4664:
@@ -2094,6 +2215,16 @@
       return VUID_WRAP(VUID-StandaloneSpirv-Location-04918);
     case 4919:
       return VUID_WRAP(VUID-StandaloneSpirv-Location-04919);
+    case 4920:
+      return VUID_WRAP(VUID-StandaloneSpirv-Component-04920);
+    case 4921:
+      return VUID_WRAP(VUID-StandaloneSpirv-Component-04921);
+    case 4922:
+      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 6201:
       return VUID_WRAP(VUID-StandaloneSpirv-Flat-06201);
     case 6202:
@@ -2126,14 +2257,28 @@
       return VUID_WRAP(VUID-StandaloneSpirv-PushConstant-06808);
     case 6925:
       return VUID_WRAP(VUID-StandaloneSpirv-Uniform-06925);
-    case 6997:
-      return VUID_WRAP(VUID-StandaloneSpirv-SubgroupVoteKHR-06997);
     case 7102:
       return VUID_WRAP(VUID-StandaloneSpirv-MeshEXT-07102);
     case 7320:
       return VUID_WRAP(VUID-StandaloneSpirv-ExecutionModel-07320);
     case 7290:
       return VUID_WRAP(VUID-StandaloneSpirv-Input-07290);
+    case 7650:
+      return VUID_WRAP(VUID-StandaloneSpirv-Base-07650);
+    case 7651:
+      return VUID_WRAP(VUID-StandaloneSpirv-Base-07651);
+    case 7652:
+      return VUID_WRAP(VUID-StandaloneSpirv-Base-07652);
+    case 7703:
+      return VUID_WRAP(VUID-StandaloneSpirv-Component-07703);
+    case 7951:
+      return VUID_WRAP(VUID-StandaloneSpirv-SubgroupVoteKHR-07951);
+    case 8721:
+      return VUID_WRAP(VUID-StandaloneSpirv-OpEntryPoint-08721);
+    case 8722:
+      return VUID_WRAP(VUID-StandaloneSpirv-OpEntryPoint-08722);
+    case 8973:
+      return VUID_WRAP(VUID-StandaloneSpirv-Pointer-08973);
     default:
       return "";  // unknown id
   }
diff --git a/source/val/validation_state.h b/source/val/validation_state.h
index 1b599ff..0cd6c78 100644
--- a/source/val/validation_state.h
+++ b/source/val/validation_state.h
@@ -185,10 +185,10 @@
   void ProgressToNextLayoutSectionOrder();
 
   /// Determines if the op instruction is in a previous layout section
-  bool IsOpcodeInPreviousLayoutSection(SpvOp op);
+  bool IsOpcodeInPreviousLayoutSection(spv::Op op);
 
   /// Determines if the op instruction is part of the current section
-  bool IsOpcodeInCurrentLayoutSection(SpvOp op);
+  bool IsOpcodeInCurrentLayoutSection(spv::Op op);
 
   DiagnosticStream diag(spv_result_t error_code, const Instruction* inst);
 
@@ -217,7 +217,8 @@
   };
 
   /// Registers |id| as an entry point with |execution_model| and |interfaces|.
-  void RegisterEntryPoint(const uint32_t id, SpvExecutionModel execution_model,
+  void RegisterEntryPoint(const uint32_t id,
+                          spv::ExecutionModel execution_model,
                           EntryPointDescription&& desc) {
     entry_points_.push_back(id);
     entry_point_to_execution_models_[id].insert(execution_model);
@@ -235,7 +236,7 @@
 
   /// Registers execution mode for the given entry point.
   void RegisterExecutionModeForEntryPoint(uint32_t entry_point,
-                                          SpvExecutionMode execution_mode) {
+                                          spv::ExecutionMode execution_mode) {
     entry_point_to_execution_modes_[entry_point].insert(execution_mode);
   }
 
@@ -247,7 +248,7 @@
 
   /// Returns Execution Models for the given Entry Point.
   /// Returns nullptr if none found (would trigger assertion).
-  const std::set<SpvExecutionModel>* GetExecutionModels(
+  const std::set<spv::ExecutionModel>* GetExecutionModels(
       uint32_t entry_point) const {
     const auto it = entry_point_to_execution_models_.find(entry_point);
     if (it == entry_point_to_execution_models_.end()) {
@@ -259,7 +260,7 @@
 
   /// Returns Execution Modes for the given Entry Point.
   /// Returns nullptr if none found.
-  const std::set<SpvExecutionMode>* GetExecutionModes(
+  const std::set<spv::ExecutionMode>* GetExecutionModes(
       uint32_t entry_point) const {
     const auto it = entry_point_to_execution_modes_.find(entry_point);
     if (it == entry_point_to_execution_modes_.end()) {
@@ -300,7 +301,7 @@
     return (id_to_function_.find(id) != id_to_function_.end());
   }
   /// Registers the capability and its dependent capabilities
-  void RegisterCapability(SpvCapability cap);
+  void RegisterCapability(spv::Capability cap);
 
   /// Registers the extension.
   void RegisterExtension(Extension ext);
@@ -308,15 +309,15 @@
   /// Registers the function in the module. Subsequent instructions will be
   /// called against this function
   spv_result_t RegisterFunction(uint32_t id, uint32_t ret_type_id,
-                                SpvFunctionControlMask function_control,
+                                spv::FunctionControlMask function_control,
                                 uint32_t function_type_id);
 
   /// Register a function end instruction
   spv_result_t RegisterFunctionEnd();
 
   /// Returns true if the capability is enabled in the module.
-  bool HasCapability(SpvCapability cap) const {
-    return module_capabilities_.Contains(cap);
+  bool HasCapability(spv::Capability cap) const {
+    return module_capabilities_.contains(cap);
   }
 
   /// Returns a reference to the set of capabilities in the module.
@@ -327,7 +328,7 @@
 
   /// Returns true if the extension is enabled in the module.
   bool HasExtension(Extension ext) const {
-    return module_extensions_.Contains(ext);
+    return module_extensions_.contains(ext);
   }
 
   /// Returns true if any of the capabilities is enabled, or if |capabilities|
@@ -339,16 +340,16 @@
   bool HasAnyOfExtensions(const ExtensionSet& extensions) const;
 
   /// Sets the addressing model of this module (logical/physical).
-  void set_addressing_model(SpvAddressingModel am);
+  void set_addressing_model(spv::AddressingModel am);
 
   /// Returns true if the OpMemoryModel was found.
   bool has_memory_model_specified() const {
-    return addressing_model_ != SpvAddressingModelMax &&
-           memory_model_ != SpvMemoryModelMax;
+    return addressing_model_ != spv::AddressingModel::Max &&
+           memory_model_ != spv::MemoryModel::Max;
   }
 
   /// Returns the addressing model of this module, or Logical if uninitialized.
-  SpvAddressingModel addressing_model() const;
+  spv::AddressingModel addressing_model() const;
 
   /// Returns the addressing model of this module, or Logical if uninitialized.
   uint32_t pointer_size_and_alignment() const {
@@ -356,10 +357,10 @@
   }
 
   /// Sets the memory model of this module.
-  void set_memory_model(SpvMemoryModel mm);
+  void set_memory_model(spv::MemoryModel mm);
 
   /// Returns the memory model of this module, or Simple if uninitialized.
-  SpvMemoryModel memory_model() const;
+  spv::MemoryModel memory_model() const;
 
   /// Sets the bit width for sampler/image type variables. If not set, they are
   /// considered opaque
@@ -432,8 +433,8 @@
 
     // The decorations are sorted by member_index, so this look up will give the
     // exact range of decorations for this member index.
-    Decoration min_decoration((SpvDecoration)0, {}, member_index);
-    Decoration max_decoration(SpvDecorationMax, {}, member_index);
+    Decoration min_decoration((spv::Decoration)0, {}, member_index);
+    Decoration max_decoration(spv::Decoration::Max, {}, member_index);
 
     FieldDecorationsIter result;
     result.begin = decorations.lower_bound(min_decoration);
@@ -449,7 +450,7 @@
 
   /// Returns true if the given id <id> has the given decoration <dec>,
   /// otherwise returns false.
-  bool HasDecoration(uint32_t id, SpvDecoration dec) {
+  bool HasDecoration(uint32_t id, spv::Decoration dec) {
     const auto& decorations = id_decorations_.find(id);
     if (decorations == id_decorations_.end()) return false;
 
@@ -484,8 +485,15 @@
   void RegisterSampledImageConsumer(uint32_t sampled_image_id,
                                     Instruction* consumer);
 
+  // Record a cons_id as a consumer of texture_id
+  // if texture 'texture_id' has a QCOM image processing decoration
+  // and consumer is a load or a sampled image instruction
+  void RegisterQCOMImageProcessingTextureConsumer(uint32_t texture_id,
+                                                  const Instruction* consumer0,
+                                                  const Instruction* consumer1);
+
   // Record a function's storage class consumer instruction
-  void RegisterStorageClassConsumer(SpvStorageClass storage_class,
+  void RegisterStorageClassConsumer(spv::StorageClass storage_class,
                                     Instruction* consumer);
 
   /// Returns the set of Global Variables.
@@ -601,6 +609,7 @@
   bool IsIntScalarOrVectorType(uint32_t id) const;
   bool IsUnsignedIntScalarType(uint32_t id) const;
   bool IsUnsignedIntVectorType(uint32_t id) const;
+  bool IsUnsignedIntScalarOrVectorType(uint32_t id) const;
   bool IsSignedIntScalarType(uint32_t id) const;
   bool IsSignedIntVectorType(uint32_t id) const;
   bool IsBoolScalarType(uint32_t id) const;
@@ -609,6 +618,11 @@
   bool IsPointerType(uint32_t id) const;
   bool IsAccelerationStructureType(uint32_t id) const;
   bool IsCooperativeMatrixType(uint32_t id) const;
+  bool IsCooperativeMatrixNVType(uint32_t id) const;
+  bool IsCooperativeMatrixKHRType(uint32_t id) const;
+  bool IsCooperativeMatrixAType(uint32_t id) const;
+  bool IsCooperativeMatrixBType(uint32_t id) const;
+  bool IsCooperativeMatrixAccType(uint32_t id) const;
   bool IsFloatCooperativeMatrixType(uint32_t id) const;
   bool IsIntCooperativeMatrixType(uint32_t id) const;
   bool IsUnsignedIntCooperativeMatrixType(uint32_t id) const;
@@ -616,7 +630,7 @@
 
   // Returns true if |id| is a type id that contains |type| (or integer or
   // floating point type) of |width| bits.
-  bool ContainsSizedIntOrFloatType(uint32_t id, SpvOp type,
+  bool ContainsSizedIntOrFloatType(uint32_t id, spv::Op type,
                                    uint32_t width) const;
   // Returns true if |id| is a type id that contains a 8- or 16-bit int or
   // 16-bit float that is not generally enabled for use.
@@ -642,7 +656,7 @@
 
   // Returns opcode of the instruction which issued the id or OpNop if the
   // instruction is not registered.
-  SpvOp GetIdOpcode(uint32_t id) const;
+  spv::Op GetIdOpcode(uint32_t id) const;
 
   // Returns type_id for given id operand if it has a type or zero otherwise.
   // |operand_index| is expected to be pointing towards an operand which is an
@@ -652,7 +666,7 @@
 
   // Provides information on pointer type. Returns false iff not pointer type.
   bool GetPointerTypeInfo(uint32_t id, uint32_t* data_type,
-                          uint32_t* storage_class) const;
+                          spv::StorageClass* storage_class) const;
 
   // 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
@@ -732,6 +746,9 @@
     }
     return std::string(desc->name);
   }
+  std::string SpvDecorationString(spv::Decoration decoration) {
+    return SpvDecorationString(uint32_t(decoration));
+  }
 
   // Returns whether type m1 and type m2 are cooperative matrices with
   // the same "shape" (matching scope, rows, cols). If any are specialization
@@ -764,7 +781,7 @@
   const Instruction* TracePointer(const Instruction* inst) const;
 
   // Validates the storage class for the target environment.
-  bool IsValidStorageClass(SpvStorageClass storage_class) const;
+  bool IsValidStorageClass(spv::StorageClass storage_class) 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.
@@ -782,6 +799,13 @@
     current_layout_section_ = section;
   }
 
+  // Check if instruction 'id' is a consumer of a texture decorated
+  // with a QCOM image processing decoration
+  bool IsQCOMImageProcessingTextureConsumer(uint32_t id) {
+    return qcom_image_processing_consumers_.find(id) !=
+           qcom_image_processing_consumers_.end();
+  }
+
  private:
   ValidationState_t(const ValidationState_t&);
 
@@ -816,6 +840,10 @@
   std::unordered_map<uint32_t, std::vector<Instruction*>>
       sampled_image_consumers_;
 
+  /// Stores load instructions that load textures used
+  //  in QCOM image processing functions
+  std::unordered_set<uint32_t> qcom_image_processing_consumers_;
+
   /// A map of operand IDs and their names defined by the OpName instruction
   std::unordered_map<uint32_t, std::string> operand_names_;
 
@@ -883,8 +911,8 @@
 
   AssemblyGrammar grammar_;
 
-  SpvAddressingModel addressing_model_;
-  SpvMemoryModel memory_model_;
+  spv::AddressingModel addressing_model_;
+  spv::MemoryModel memory_model_;
   // pointer size derived from addressing model. Assumes all storage classes
   // have the same pointer size (for physical pointer types).
   uint32_t pointer_size_and_alignment_;
@@ -905,11 +933,11 @@
   /// Mapping entry point -> execution models. It is presumed that the same
   /// function could theoretically be used as 'main' by multiple OpEntryPoint
   /// instructions.
-  std::unordered_map<uint32_t, std::set<SpvExecutionModel>>
+  std::unordered_map<uint32_t, std::set<spv::ExecutionModel>>
       entry_point_to_execution_models_;
 
   /// Mapping entry point -> execution modes.
-  std::unordered_map<uint32_t, std::set<SpvExecutionMode>>
+  std::unordered_map<uint32_t, std::set<spv::ExecutionMode>>
       entry_point_to_execution_modes_;
 
   /// Mapping function -> array of entry points inside this
diff --git a/source/wasm/build.sh b/source/wasm/build.sh
index f02ae52..69468c9 100755
--- a/source/wasm/build.sh
+++ b/source/wasm/build.sh
@@ -16,6 +16,11 @@
 
 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 /app
+
 NUM_CORES=$(nproc)
 echo "Detected $NUM_CORES cores for building"
 
@@ -40,7 +45,7 @@
     emcc \
         --bind \
         -I../../include \
-        -std=c++11 \
+        -std=c++17 \
         ../../source/wasm/spirv-tools.cpp \
         source/libSPIRV-Tools.a \
         -o spirv-tools.js \
diff --git a/docker-compose.yml b/source/wasm/docker-compose.yml
similarity index 83%
rename from docker-compose.yml
rename to source/wasm/docker-compose.yml
index fb6d114..2340fdb 100644
--- a/docker-compose.yml
+++ b/source/wasm/docker-compose.yml
@@ -1,7 +1,7 @@
 version: "3"
 services:
   build:
-    image: emscripten/emsdk:2.0.2
+    image: emscripten/emsdk:3.1.28
     environment:
       GITHUB_RUN_NUMBER: ${GITHUB_RUN_NUMBER:-}
     working_dir: /app
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 4ca8ef8..37c5e1d 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -31,7 +31,7 @@
 function(add_spvtools_unittest)
   if (NOT "${SPIRV_SKIP_TESTS}" AND TARGET gmock_main)
     set(one_value_args TARGET PCH_FILE)
-    set(multi_value_args SRCS LIBS ENVIRONMENT)
+    set(multi_value_args SRCS LIBS ENVIRONMENT DEFINES)
     cmake_parse_arguments(
       ARG "" "${one_value_args}" "${multi_value_args}" ${ARGN})
     set(target test_${ARG_TARGET})
@@ -40,6 +40,7 @@
       spvtools_pch(SRC_COPY ${ARG_PCH_FILE})
     endif()
     add_executable(${target} ${SRC_COPY})
+    target_compile_definitions(${target} PUBLIC ${ARG_DEFINES})
     spvtools_default_compile_options(${target})
     if(${COMPILER_IS_LIKE_GNU})
       target_compile_options(${target} PRIVATE -Wno-undef)
diff --git a/test/binary_header_get_test.cpp b/test/binary_header_get_test.cpp
index f94f0c1..aca09b0 100644
--- a/test/binary_header_get_test.cpp
+++ b/test/binary_header_get_test.cpp
@@ -23,8 +23,8 @@
   BinaryHeaderGet() { memset(code, 0, sizeof(code)); }
 
   virtual void SetUp() {
-    code[0] = SpvMagicNumber;
-    code[1] = SpvVersion;
+    code[0] = static_cast<uint32_t>(spv::MagicNumber);
+    code[1] = static_cast<uint32_t>(spv::Version);
     code[2] = SPV_GENERATOR_CODEPLAY;
     code[3] = 1;  // NOTE: Bound
     code[4] = 0;  // NOTE: Schema; reserved
@@ -50,7 +50,7 @@
   spv_header_t header;
   ASSERT_EQ(SPV_SUCCESS, spvBinaryHeaderGet(&const_bin, endian, &header));
 
-  ASSERT_EQ(static_cast<uint32_t>(SpvMagicNumber), header.magic);
+  ASSERT_EQ(static_cast<uint32_t>(spv::MagicNumber), header.magic);
   // Expect SPIRV-Headers updated to SPIR-V 1.6.
   ASSERT_EQ(0x00010600u, header.version);
   ASSERT_EQ(static_cast<uint32_t>(SPV_GENERATOR_CODEPLAY), header.generator);
diff --git a/test/binary_parse_test.cpp b/test/binary_parse_test.cpp
index f0810a3..1a868db 100644
--- a/test/binary_parse_test.cpp
+++ b/test/binary_parse_test.cpp
@@ -54,14 +54,14 @@
 struct ParsedInstruction {
   explicit ParsedInstruction(const spv_parsed_instruction_t& inst)
       : words(inst.words, inst.words + inst.num_words),
-        opcode(static_cast<SpvOp>(inst.opcode)),
+        opcode(static_cast<spv::Op>(inst.opcode)),
         ext_inst_type(inst.ext_inst_type),
         type_id(inst.type_id),
         result_id(inst.result_id),
         operands(inst.operands, inst.operands + inst.num_operands) {}
 
   std::vector<uint32_t> words;
-  SpvOp opcode;
+  spv::Op opcode;
   spv_ext_inst_type_t ext_inst_type;
   uint32_t type_id;
   uint32_t result_id;
@@ -127,14 +127,14 @@
 // The SPIR-V module header words for the Khronos Assembler generator,
 // for a module with an ID bound of 1.
 const uint32_t kHeaderForBound1[] = {
-    SpvMagicNumber, SpvVersion,
+    spv::MagicNumber, spv::Version,
     SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS_ASSEMBLER, 0), 1 /*bound*/,
     0 /*schema*/};
 
 // Returns the expected SPIR-V module header words for the Khronos
 // Assembler generator, and with a given Id bound.
 std::vector<uint32_t> ExpectedHeaderForBound(uint32_t bound) {
-  return {SpvMagicNumber, 0x10000,
+  return {spv::MagicNumber, 0x10000,
           SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS_ASSEMBLER, 0), bound, 0};
 }
 
@@ -162,13 +162,13 @@
 // Returns a ParsedInstruction for an OpTypeVoid instruction that would
 // generate the given result Id.
 ParsedInstruction MakeParsedVoidTypeInstruction(uint32_t result_id) {
-  const auto void_inst = MakeInstruction(SpvOpTypeVoid, {result_id});
+  const auto void_inst = MakeInstruction(spv::Op::OpTypeVoid, {result_id});
   const auto void_operands = std::vector<spv_parsed_operand_t>{
       MakeSimpleOperand(1, SPV_OPERAND_TYPE_RESULT_ID)};
   const spv_parsed_instruction_t parsed_void_inst = {
       void_inst.data(),
       static_cast<uint16_t>(void_inst.size()),
-      SpvOpTypeVoid,
+      uint16_t(spv::Op::OpTypeVoid),
       SPV_EXT_INST_TYPE_NONE,
       0,  // type id
       result_id,
@@ -180,14 +180,14 @@
 // Returns a ParsedInstruction for an OpTypeInt instruction that generates
 // the given result Id for a 32-bit signed integer scalar type.
 ParsedInstruction MakeParsedInt32TypeInstruction(uint32_t result_id) {
-  const auto i32_inst = MakeInstruction(SpvOpTypeInt, {result_id, 32, 1});
+  const auto i32_inst = MakeInstruction(spv::Op::OpTypeInt, {result_id, 32, 1});
   const auto i32_operands = std::vector<spv_parsed_operand_t>{
       MakeSimpleOperand(1, SPV_OPERAND_TYPE_RESULT_ID),
       MakeLiteralNumberOperand(2), MakeLiteralNumberOperand(3)};
   spv_parsed_instruction_t parsed_i32_inst = {
       i32_inst.data(),
       static_cast<uint16_t>(i32_inst.size()),
-      SpvOpTypeInt,
+      uint16_t(spv::Op::OpTypeInt),
       SPV_EXT_INST_TYPE_NONE,
       0,  // type id
       result_id,
@@ -214,14 +214,48 @@
   MockParseClient client_;
 };
 
+class CxxBinaryParseTest
+    : public spvtest::TextToBinaryTestBase<::testing::Test> {
+ protected:
+  CxxBinaryParseTest() {
+    header_parser_ = [this](const spv_endianness_t endianness,
+                            const spv_parsed_header_t& header) {
+      return this->client_.Header(endianness, header.magic, header.version,
+                                  header.generator, header.bound,
+                                  header.reserved);
+    };
+
+    instruction_parser_ = [this](const spv_parsed_instruction_t& instruction) {
+      return this->client_.Instruction(ParsedInstruction(instruction));
+    };
+  }
+
+  ~CxxBinaryParseTest() override { spvDiagnosticDestroy(diagnostic_); }
+
+  void Parse(const SpirvVector& words, bool expected_result,
+             bool flip_words = false,
+             spv_target_env env = SPV_ENV_UNIVERSAL_1_0) {
+    SpirvVector flipped_words(words);
+    MaybeFlipWords(flip_words, flipped_words.begin(), flipped_words.end());
+    spvtools::SpirvTools tools(env);
+    EXPECT_EQ(expected_result, tools.Parse(flipped_words, header_parser_,
+                                           instruction_parser_, &diagnostic_));
+  }
+
+  spv_diagnostic diagnostic_ = nullptr;
+  MockParseClient client_;
+  HeaderParser header_parser_;
+  InstructionParser instruction_parser_;
+};
+
 // Adds an EXPECT_CALL to client_->Header() with appropriate parameters,
 // including bound.  Returns the EXPECT_CALL result.
-#define EXPECT_HEADER(bound)                                                   \
-  EXPECT_CALL(                                                                 \
-      client_,                                                                 \
-      Header(AnyOf(SPV_ENDIANNESS_LITTLE, SPV_ENDIANNESS_BIG), SpvMagicNumber, \
-             0x10000, SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS_ASSEMBLER, 0),  \
-             bound, 0 /*reserved*/))
+#define EXPECT_HEADER(bound)                                                 \
+  EXPECT_CALL(client_,                                                       \
+              Header(AnyOf(SPV_ENDIANNESS_LITTLE, SPV_ENDIANNESS_BIG),       \
+                     spv::MagicNumber, 0x10000,                              \
+                     SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS_ASSEMBLER, 0), \
+                     bound, 0 /*reserved*/))
 
 static const bool kSwapEndians[] = {false, true};
 
@@ -235,6 +269,16 @@
   }
 }
 
+TEST_F(CxxBinaryParseTest, EmptyModuleHasValidHeaderAndNoInstructionCallbacks) {
+  for (bool endian_swap : kSwapEndians) {
+    const auto words = CompileSuccessfully("");
+    EXPECT_HEADER(1).WillOnce(Return(SPV_SUCCESS));
+    EXPECT_CALL(client_, Instruction(_)).Times(0);  // No instruction callback.
+    Parse(words, true, endian_swap);
+    EXPECT_EQ(nullptr, diagnostic_);
+  }
+}
+
 TEST_F(BinaryParseTest, NullDiagnosticsIsOkForGoodParse) {
   const auto words = CompileSuccessfully("");
   EXPECT_HEADER(1).WillOnce(Return(SPV_SUCCESS));
@@ -245,6 +289,15 @@
                      words.size(), invoke_header, invoke_instruction, nullptr));
 }
 
+TEST_F(CxxBinaryParseTest, NullDiagnosticsIsOkForGoodParse) {
+  const auto words = CompileSuccessfully("");
+  EXPECT_HEADER(1).WillOnce(Return(SPV_SUCCESS));
+  EXPECT_CALL(client_, Instruction(_)).Times(0);  // No instruction callback.
+  spvtools::SpirvTools tools(SPV_ENV_UNIVERSAL_1_0);
+  EXPECT_EQ(true,
+            tools.Parse(words, header_parser_, instruction_parser_, nullptr));
+}
+
 TEST_F(BinaryParseTest, NullDiagnosticsIsOkForBadParse) {
   auto words = CompileSuccessfully("");
   words.push_back(0xffffffff);  // Certainly invalid instruction header.
@@ -256,6 +309,16 @@
                      words.size(), invoke_header, invoke_instruction, nullptr));
 }
 
+TEST_F(CxxBinaryParseTest, NullDiagnosticsIsOkForBadParse) {
+  auto words = CompileSuccessfully("");
+  words.push_back(0xffffffff);  // Certainly invalid instruction header.
+  EXPECT_HEADER(1).WillOnce(Return(SPV_SUCCESS));
+  EXPECT_CALL(client_, Instruction(_)).Times(0);  // No instruction callback.
+  spvtools::SpirvTools tools(SPV_ENV_UNIVERSAL_1_0);
+  EXPECT_EQ(false,
+            tools.Parse(words, header_parser_, instruction_parser_, nullptr));
+}
+
 // Make sure that we don't blow up when both the consumer and the diagnostic are
 // null.
 TEST_F(BinaryParseTest, NullConsumerNullDiagnosticsForBadParse) {
@@ -272,6 +335,18 @@
                            invoke_header, invoke_instruction, nullptr));
 }
 
+TEST_F(CxxBinaryParseTest, NullConsumerNullDiagnosticsForBadParse) {
+  spvtools::SpirvTools tools(SPV_ENV_UNIVERSAL_1_1);
+  tools.SetMessageConsumer(nullptr);
+
+  auto words = CompileSuccessfully("");
+  words.push_back(0xffffffff);  // Certainly invalid instruction header.
+  EXPECT_HEADER(1).WillOnce(Return(SPV_SUCCESS));
+  EXPECT_CALL(client_, Instruction(_)).Times(0);  // No instruction callback.
+  EXPECT_EQ(false,
+            tools.Parse(words, header_parser_, instruction_parser_, nullptr));
+}
+
 TEST_F(BinaryParseTest, SpecifyConsumerNullDiagnosticsForGoodParse) {
   const auto words = CompileSuccessfully("");
 
@@ -289,6 +364,21 @@
   EXPECT_EQ(0, invocation);
 }
 
+TEST_F(CxxBinaryParseTest, SpecifyConsumerNullDiagnosticsForGoodParse) {
+  const auto words = CompileSuccessfully("");
+  spvtools::SpirvTools tools(SPV_ENV_UNIVERSAL_1_1);
+  int invocation = 0;
+  tools.SetMessageConsumer([&invocation](spv_message_level_t, const char*,
+                                         const spv_position_t&,
+                                         const char*) { ++invocation; });
+
+  EXPECT_HEADER(1).WillOnce(Return(SPV_SUCCESS));
+  EXPECT_CALL(client_, Instruction(_)).Times(0);  // No instruction callback.
+  EXPECT_EQ(true,
+            tools.Parse(words, header_parser_, instruction_parser_, nullptr));
+  EXPECT_EQ(0, invocation);
+}
+
 TEST_F(BinaryParseTest, SpecifyConsumerNullDiagnosticsForBadParse) {
   auto words = CompileSuccessfully("");
 
@@ -315,6 +405,30 @@
   EXPECT_EQ(1, invocation);
 }
 
+TEST_F(CxxBinaryParseTest, SpecifyConsumerNullDiagnosticsForBadParse) {
+  auto words = CompileSuccessfully("");
+  spvtools::SpirvTools tools(SPV_ENV_UNIVERSAL_1_1);
+  int invocation = 0;
+  tools.SetMessageConsumer(
+      [&invocation](spv_message_level_t level, const char* source,
+                    const spv_position_t& position, const char* message) {
+        ++invocation;
+        EXPECT_EQ(SPV_MSG_ERROR, level);
+        EXPECT_STREQ("input", source);
+        EXPECT_EQ(0u, position.line);
+        EXPECT_EQ(0u, position.column);
+        EXPECT_EQ(1u, position.index);
+        EXPECT_STREQ("Invalid opcode: 65535", message);
+      });
+
+  words.push_back(0xffffffff);  // Certainly invalid instruction header.
+  EXPECT_HEADER(1).WillOnce(Return(SPV_SUCCESS));
+  EXPECT_CALL(client_, Instruction(_)).Times(0);  // No instruction callback.
+  EXPECT_EQ(false,
+            tools.Parse(words, header_parser_, instruction_parser_, nullptr));
+  EXPECT_EQ(1, invocation);
+}
+
 TEST_F(BinaryParseTest, SpecifyConsumerSpecifyDiagnosticsForGoodParse) {
   const auto words = CompileSuccessfully("");
 
@@ -333,6 +447,22 @@
   EXPECT_EQ(nullptr, diagnostic_);
 }
 
+TEST_F(CxxBinaryParseTest, SpecifyConsumerSpecifyDiagnosticsForGoodParse) {
+  const auto words = CompileSuccessfully("");
+  spvtools::SpirvTools tools(SPV_ENV_UNIVERSAL_1_1);
+  int invocation = 0;
+  tools.SetMessageConsumer([&invocation](spv_message_level_t, const char*,
+                                         const spv_position_t&,
+                                         const char*) { ++invocation; });
+
+  EXPECT_HEADER(1).WillOnce(Return(SPV_SUCCESS));
+  EXPECT_CALL(client_, Instruction(_)).Times(0);  // No instruction callback.
+  EXPECT_EQ(true, tools.Parse(words, header_parser_, instruction_parser_,
+                              &diagnostic_));
+  EXPECT_EQ(0, invocation);
+  EXPECT_EQ(nullptr, diagnostic_);
+}
+
 TEST_F(BinaryParseTest, SpecifyConsumerSpecifyDiagnosticsForBadParse) {
   auto words = CompileSuccessfully("");
 
@@ -352,6 +482,23 @@
   EXPECT_STREQ("Invalid opcode: 65535", diagnostic_->error);
 }
 
+TEST_F(CxxBinaryParseTest, SpecifyConsumerSpecifyDiagnosticsForBadParse) {
+  auto words = CompileSuccessfully("");
+  spvtools::SpirvTools tools(SPV_ENV_UNIVERSAL_1_1);
+  int invocation = 0;
+  tools.SetMessageConsumer([&invocation](spv_message_level_t, const char*,
+                                         const spv_position_t&,
+                                         const char*) { ++invocation; });
+
+  words.push_back(0xffffffff);  // Certainly invalid instruction header.
+  EXPECT_HEADER(1).WillOnce(Return(SPV_SUCCESS));
+  EXPECT_CALL(client_, Instruction(_)).Times(0);  // No instruction callback.
+  EXPECT_EQ(false, tools.Parse(words, header_parser_, instruction_parser_,
+                               &diagnostic_));
+  EXPECT_EQ(0, invocation);
+  EXPECT_STREQ("Invalid opcode: 65535", diagnostic_->error);
+}
+
 TEST_F(BinaryParseTest,
        ModuleWithSingleInstructionHasValidHeaderAndInstructionCallback) {
   for (bool endian_swap : kSwapEndians) {
@@ -365,6 +512,19 @@
   }
 }
 
+TEST_F(CxxBinaryParseTest,
+       ModuleWithSingleInstructionHasValidHeaderAndInstructionCallback) {
+  for (bool endian_swap : kSwapEndians) {
+    const auto words = CompileSuccessfully("%1 = OpTypeVoid");
+    InSequence calls_expected_in_specific_order;
+    EXPECT_HEADER(2).WillOnce(Return(SPV_SUCCESS));
+    EXPECT_CALL(client_, Instruction(MakeParsedVoidTypeInstruction(1)))
+        .WillOnce(Return(SPV_SUCCESS));
+    Parse(words, true, endian_swap);
+    EXPECT_EQ(nullptr, diagnostic_);
+  }
+}
+
 TEST_F(BinaryParseTest, NullHeaderCallbackIsIgnored) {
   const auto words = CompileSuccessfully("%1 = OpTypeVoid");
   EXPECT_CALL(client_, Header(_, _, _, _, _, _))
@@ -408,6 +568,22 @@
   }
 }
 
+TEST_F(CxxBinaryParseTest, TwoScalarTypesGenerateTwoInstructionCallbacks) {
+  for (bool endian_swap : kSwapEndians) {
+    const auto words = CompileSuccessfully(
+        "%1 = OpTypeVoid "
+        "%2 = OpTypeInt 32 1");
+    InSequence calls_expected_in_specific_order;
+    EXPECT_HEADER(3).WillOnce(Return(SPV_SUCCESS));
+    EXPECT_CALL(client_, Instruction(MakeParsedVoidTypeInstruction(1)))
+        .WillOnce(Return(SPV_SUCCESS));
+    EXPECT_CALL(client_, Instruction(MakeParsedInt32TypeInstruction(2)))
+        .WillOnce(Return(SPV_SUCCESS));
+    Parse(words, true, endian_swap);
+    EXPECT_EQ(nullptr, diagnostic_);
+  }
+}
+
 TEST_F(BinaryParseTest, EarlyReturnWithZeroPassingCallbacks) {
   for (bool endian_swap : kSwapEndians) {
     const auto words = CompileSuccessfully(
@@ -423,6 +599,21 @@
   }
 }
 
+TEST_F(CxxBinaryParseTest, EarlyReturnWithZeroPassingCallbacks) {
+  for (bool endian_swap : kSwapEndians) {
+    const auto words = CompileSuccessfully(
+        "%1 = OpTypeVoid "
+        "%2 = OpTypeInt 32 1");
+    InSequence calls_expected_in_specific_order;
+    EXPECT_HEADER(3).WillOnce(Return(SPV_ERROR_INVALID_BINARY));
+    // Early exit means no calls to Instruction().
+    EXPECT_CALL(client_, Instruction(_)).Times(0);
+    Parse(words, false, endian_swap);
+    // On error, the binary parser doesn't generate its own diagnostics.
+    EXPECT_EQ(nullptr, diagnostic_);
+  }
+}
+
 TEST_F(BinaryParseTest,
        EarlyReturnWithZeroPassingCallbacksAndSpecifiedResultCode) {
   for (bool endian_swap : kSwapEndians) {
@@ -440,6 +631,23 @@
   }
 }
 
+TEST_F(CxxBinaryParseTest,
+       EarlyReturnWithZeroPassingCallbacksAndSpecifiedResultCode) {
+  for (bool endian_swap : kSwapEndians) {
+    const auto words = CompileSuccessfully(
+        "%1 = OpTypeVoid "
+        "%2 = OpTypeInt 32 1");
+    InSequence calls_expected_in_specific_order;
+    EXPECT_HEADER(3).WillOnce(Return(SPV_REQUESTED_TERMINATION));
+    // Early exit means no calls to Instruction().
+    EXPECT_CALL(client_, Instruction(_)).Times(0);
+    Parse(words, false, endian_swap);
+    // On early termination, the binary parser doesn't generate its own
+    // diagnostics.
+    EXPECT_EQ(nullptr, diagnostic_);
+  }
+}
+
 TEST_F(BinaryParseTest, EarlyReturnWithOnePassingCallback) {
   for (bool endian_swap : kSwapEndians) {
     const auto words = CompileSuccessfully(
@@ -457,6 +665,23 @@
   }
 }
 
+TEST_F(CxxBinaryParseTest, EarlyReturnWithOnePassingCallback) {
+  for (bool endian_swap : kSwapEndians) {
+    const auto words = CompileSuccessfully(
+        "%1 = OpTypeVoid "
+        "%2 = OpTypeInt 32 1 "
+        "%3 = OpTypeFloat 32");
+    InSequence calls_expected_in_specific_order;
+    EXPECT_HEADER(4).WillOnce(Return(SPV_SUCCESS));
+    EXPECT_CALL(client_, Instruction(MakeParsedVoidTypeInstruction(1)))
+        .WillOnce(Return(SPV_REQUESTED_TERMINATION));
+    Parse(words, false, endian_swap);
+    // On early termination, the binary parser doesn't generate its own
+    // diagnostics.
+    EXPECT_EQ(nullptr, diagnostic_);
+  }
+}
+
 TEST_F(BinaryParseTest, EarlyReturnWithTwoPassingCallbacks) {
   for (bool endian_swap : kSwapEndians) {
     const auto words = CompileSuccessfully(
@@ -476,30 +701,75 @@
   }
 }
 
+TEST_F(CxxBinaryParseTest, EarlyReturnWithTwoPassingCallbacks) {
+  for (bool endian_swap : kSwapEndians) {
+    const auto words = CompileSuccessfully(
+        "%1 = OpTypeVoid "
+        "%2 = OpTypeInt 32 1 "
+        "%3 = OpTypeFloat 32");
+    InSequence calls_expected_in_specific_order;
+    EXPECT_HEADER(4).WillOnce(Return(SPV_SUCCESS));
+    EXPECT_CALL(client_, Instruction(MakeParsedVoidTypeInstruction(1)))
+        .WillOnce(Return(SPV_SUCCESS));
+    EXPECT_CALL(client_, Instruction(MakeParsedInt32TypeInstruction(2)))
+        .WillOnce(Return(SPV_REQUESTED_TERMINATION));
+    Parse(words, false, endian_swap);
+    // On early termination, the binary parser doesn't generate its own
+    // diagnostics.
+    EXPECT_EQ(nullptr, diagnostic_);
+  }
+}
+
 TEST_F(BinaryParseTest, InstructionWithStringOperand) {
   for (bool endian_swap : kSwapEndians) {
     const std::string str =
         "the future is already here, it's just not evenly distributed";
     const auto str_words = MakeVector(str);
-    const auto instruction = MakeInstruction(SpvOpName, {99}, str_words);
+    const auto instruction = MakeInstruction(spv::Op::OpName, {99}, str_words);
     const auto words = Concatenate({ExpectedHeaderForBound(100), instruction});
     InSequence calls_expected_in_specific_order;
     EXPECT_HEADER(100).WillOnce(Return(SPV_SUCCESS));
     const auto operands = std::vector<spv_parsed_operand_t>{
         MakeSimpleOperand(1, SPV_OPERAND_TYPE_ID),
         MakeLiteralStringOperand(2, static_cast<uint16_t>(str_words.size()))};
-    EXPECT_CALL(client_, Instruction(ParsedInstruction(spv_parsed_instruction_t{
-                             instruction.data(),
-                             static_cast<uint16_t>(instruction.size()),
-                             SpvOpName, SPV_EXT_INST_TYPE_NONE, 0 /*type id*/,
-                             0 /* No result id for OpName*/, operands.data(),
-                             static_cast<uint16_t>(operands.size())})))
+    EXPECT_CALL(
+        client_,
+        Instruction(ParsedInstruction(spv_parsed_instruction_t{
+            instruction.data(), static_cast<uint16_t>(instruction.size()),
+            uint16_t(spv::Op::OpName), SPV_EXT_INST_TYPE_NONE, 0 /*type id*/,
+            0 /* No result id for OpName*/, operands.data(),
+            static_cast<uint16_t>(operands.size())})))
         .WillOnce(Return(SPV_SUCCESS));
     Parse(words, SPV_SUCCESS, endian_swap);
     EXPECT_EQ(nullptr, diagnostic_);
   }
 }
 
+TEST_F(CxxBinaryParseTest, InstructionWithStringOperand) {
+  for (bool endian_swap : kSwapEndians) {
+    const std::string str =
+        "the future is already here, it's just not evenly distributed";
+    const auto str_words = MakeVector(str);
+    const auto instruction = MakeInstruction(spv::Op::OpName, {99}, str_words);
+    const auto words = Concatenate({ExpectedHeaderForBound(100), instruction});
+    InSequence calls_expected_in_specific_order;
+    EXPECT_HEADER(100).WillOnce(Return(SPV_SUCCESS));
+    const auto operands = std::vector<spv_parsed_operand_t>{
+        MakeSimpleOperand(1, SPV_OPERAND_TYPE_ID),
+        MakeLiteralStringOperand(2, static_cast<uint16_t>(str_words.size()))};
+    EXPECT_CALL(
+        client_,
+        Instruction(ParsedInstruction(spv_parsed_instruction_t{
+            instruction.data(), static_cast<uint16_t>(instruction.size()),
+            uint16_t(spv::Op::OpName), SPV_EXT_INST_TYPE_NONE, 0 /*type id*/,
+            0 /* No result id for OpName*/, operands.data(),
+            static_cast<uint16_t>(operands.size())})))
+        .WillOnce(Return(SPV_SUCCESS));
+    Parse(words, true, endian_swap);
+    EXPECT_EQ(nullptr, diagnostic_);
+  }
+}
+
 // Checks for non-zero values for the result_id and ext_inst_type members
 // spv_parsed_instruction_t.
 TEST_F(BinaryParseTest, ExtendedInstruction) {
@@ -518,13 +788,13 @@
       MakeSimpleOperand(5, SPV_OPERAND_TYPE_ID),  // Id of the argument
   };
   const auto instruction = MakeInstruction(
-      SpvOpExtInst,
+      spv::Op::OpExtInst,
       {2, 3, 1, static_cast<uint32_t>(OpenCLLIB::Entrypoints::Sqrt), 4});
   EXPECT_CALL(client_,
               Instruction(ParsedInstruction(spv_parsed_instruction_t{
                   instruction.data(), static_cast<uint16_t>(instruction.size()),
-                  SpvOpExtInst, SPV_EXT_INST_TYPE_OPENCL_STD, 2 /*type id*/,
-                  3 /*result id*/, operands.data(),
+                  uint16_t(spv::Op::OpExtInst), SPV_EXT_INST_TYPE_OPENCL_STD,
+                  2 /*type id*/, 3 /*result id*/, operands.data(),
                   static_cast<uint16_t>(operands.size())})))
       .WillOnce(Return(SPV_SUCCESS));
   // Since we are actually checking the output, don't test the
@@ -533,6 +803,37 @@
   EXPECT_EQ(nullptr, diagnostic_);
 }
 
+TEST_F(CxxBinaryParseTest, ExtendedInstruction) {
+  const auto words = CompileSuccessfully(
+      "%extcl = OpExtInstImport \"OpenCL.std\" "
+      "%result = OpExtInst %float %extcl sqrt %x");
+  EXPECT_HEADER(5).WillOnce(Return(SPV_SUCCESS));
+  EXPECT_CALL(client_, Instruction(_)).WillOnce(Return(SPV_SUCCESS));
+  // We're only interested in the second call to Instruction():
+  const auto operands = std::vector<spv_parsed_operand_t>{
+      MakeSimpleOperand(1, SPV_OPERAND_TYPE_TYPE_ID),
+      MakeSimpleOperand(2, SPV_OPERAND_TYPE_RESULT_ID),
+      MakeSimpleOperand(3,
+                        SPV_OPERAND_TYPE_ID),  // Extended instruction set Id
+      MakeSimpleOperand(4, SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER),
+      MakeSimpleOperand(5, SPV_OPERAND_TYPE_ID),  // Id of the argument
+  };
+  const auto instruction = MakeInstruction(
+      spv::Op::OpExtInst,
+      {2, 3, 1, static_cast<uint32_t>(OpenCLLIB::Entrypoints::Sqrt), 4});
+  EXPECT_CALL(client_,
+              Instruction(ParsedInstruction(spv_parsed_instruction_t{
+                  instruction.data(), static_cast<uint16_t>(instruction.size()),
+                  uint16_t(spv::Op::OpExtInst), SPV_EXT_INST_TYPE_OPENCL_STD,
+                  2 /*type id*/, 3 /*result id*/, operands.data(),
+                  static_cast<uint16_t>(operands.size())})))
+      .WillOnce(Return(SPV_SUCCESS));
+  // Since we are actually checking the output, don't test the
+  // endian-swapped version.
+  Parse(words, true, false);
+  EXPECT_EQ(nullptr, diagnostic_);
+}
+
 // A binary parser diagnostic test case where we provide the words array
 // pointer and word count explicitly.
 struct WordsAndCountDiagnosticCase {
@@ -593,36 +894,38 @@
 INSTANTIATE_TEST_SUITE_P(
     BinaryParseDiagnostic, BinaryParseWordVectorDiagnosticTest,
     ::testing::ValuesIn(std::vector<WordVectorDiagnosticCase>{
-        {Concatenate({ExpectedHeaderForBound(1), {spvOpcodeMake(0, SpvOpNop)}}),
+        {Concatenate({ExpectedHeaderForBound(1),
+                      {spvOpcodeMake(0, spv::Op::OpNop)}}),
          "Invalid instruction word count: 0"},
         {Concatenate(
              {ExpectedHeaderForBound(1),
-              {spvOpcodeMake(1, static_cast<SpvOp>(
+              {spvOpcodeMake(1, static_cast<spv::Op>(
                                     std::numeric_limits<uint16_t>::max()))}}),
          "Invalid opcode: 65535"},
         {Concatenate({ExpectedHeaderForBound(1),
-                      MakeInstruction(SpvOpNop, {42})}),
+                      MakeInstruction(spv::Op::OpNop, {42})}),
          "Invalid instruction OpNop starting at word 5: expected "
          "no more operands after 1 words, but stated word count is 2."},
         // Supply several more unexpected words.
         {Concatenate({ExpectedHeaderForBound(1),
-                      MakeInstruction(SpvOpNop, {42, 43, 44, 45, 46, 47})}),
+                      MakeInstruction(spv::Op::OpNop,
+                                      {42, 43, 44, 45, 46, 47})}),
          "Invalid instruction OpNop starting at word 5: expected "
          "no more operands after 1 words, but stated word count is 7."},
         {Concatenate({ExpectedHeaderForBound(1),
-                      MakeInstruction(SpvOpTypeVoid, {1, 2})}),
+                      MakeInstruction(spv::Op::OpTypeVoid, {1, 2})}),
          "Invalid instruction OpTypeVoid starting at word 5: expected "
          "no more operands after 2 words, but stated word count is 3."},
         {Concatenate({ExpectedHeaderForBound(1),
-                      MakeInstruction(SpvOpTypeVoid, {1, 2, 5, 9, 10})}),
+                      MakeInstruction(spv::Op::OpTypeVoid, {1, 2, 5, 9, 10})}),
          "Invalid instruction OpTypeVoid starting at word 5: expected "
          "no more operands after 2 words, but stated word count is 6."},
         {Concatenate({ExpectedHeaderForBound(1),
-                      MakeInstruction(SpvOpTypeInt, {1, 32, 1, 9})}),
+                      MakeInstruction(spv::Op::OpTypeInt, {1, 32, 1, 9})}),
          "Invalid instruction OpTypeInt starting at word 5: expected "
          "no more operands after 4 words, but stated word count is 5."},
         {Concatenate({ExpectedHeaderForBound(1),
-                      MakeInstruction(SpvOpTypeInt, {1})}),
+                      MakeInstruction(spv::Op::OpTypeInt, {1})}),
          "End of input reached while decoding OpTypeInt starting at word 5:"
          " expected more operands after 2 words."},
 
@@ -630,7 +933,7 @@
 
         // Detect a missing single word operand.
         {Concatenate({ExpectedHeaderForBound(1),
-                      {spvOpcodeMake(2, SpvOpTypeStruct)}}),
+                      {spvOpcodeMake(2, spv::Op::OpTypeStruct)}}),
          "End of input reached while decoding OpTypeStruct starting at word"
          " 5: missing result ID operand at word offset 1."},
         // Detect this a missing a multi-word operand to OpConstant.
@@ -639,29 +942,29 @@
         //    %1 = OpTypeInt 64 0
         //    %2 = OpConstant %1 <missing>
         {Concatenate({ExpectedHeaderForBound(3),
-                      {MakeInstruction(SpvOpTypeInt, {1, 64, 0})},
-                      {spvOpcodeMake(5, SpvOpConstant), 1, 2}}),
+                      {MakeInstruction(spv::Op::OpTypeInt, {1, 64, 0})},
+                      {spvOpcodeMake(5, spv::Op::OpConstant), 1, 2}}),
          "End of input reached while decoding OpConstant starting at word"
          " 9: missing possibly multi-word literal number operand at word "
          "offset 3."},
         // Detect when we provide only one word from the 64-bit literal,
         // and again lie about the number of words in the instruction.
         {Concatenate({ExpectedHeaderForBound(3),
-                      {MakeInstruction(SpvOpTypeInt, {1, 64, 0})},
-                      {spvOpcodeMake(5, SpvOpConstant), 1, 2, 42}}),
+                      {MakeInstruction(spv::Op::OpTypeInt, {1, 64, 0})},
+                      {spvOpcodeMake(5, spv::Op::OpConstant), 1, 2, 42}}),
          "End of input reached while decoding OpConstant starting at word"
          " 9: truncated possibly multi-word literal number operand at word "
          "offset 3."},
         // Detect when a required string operand is missing.
         // Also, lie about the length of the instruction.
         {Concatenate({ExpectedHeaderForBound(3),
-                      {spvOpcodeMake(3, SpvOpString), 1}}),
+                      {spvOpcodeMake(3, spv::Op::OpString), 1}}),
          "End of input reached while decoding OpString starting at word"
          " 5: missing literal string operand at word offset 2."},
         // Detect when a required string operand is truncated: it's missing
         // a null terminator.  Catching the error avoids a buffer overrun.
         {Concatenate({ExpectedHeaderForBound(3),
-                      {spvOpcodeMake(4, SpvOpString), 1, 0x41414141,
+                      {spvOpcodeMake(4, spv::Op::OpString), 1, 0x41414141,
                        0x41414141}}),
          "End of input reached while decoding OpString starting at word"
          " 5: truncated literal string operand at word offset 2."},
@@ -669,9 +972,9 @@
         // a null terminator.  Catching the error avoids a buffer overrun.
         // (It is valid for an optional string operand to be absent.)
         {Concatenate({ExpectedHeaderForBound(3),
-                      {spvOpcodeMake(6, SpvOpSource),
-                       static_cast<uint32_t>(SpvSourceLanguageOpenCL_C), 210,
-                       1 /* file id */,
+                      {spvOpcodeMake(6, spv::Op::OpSource),
+                       static_cast<uint32_t>(spv::SourceLanguage::OpenCL_C),
+                       210, 1 /* file id */,
                        /*start of string*/ 0x41414141, 0x41414141}}),
          "End of input reached while decoding OpSource starting at word"
          " 5: truncated literal string operand at word offset 4."},
@@ -681,19 +984,19 @@
         // In this case the instruction word count is too small, where
         // it would truncate a multi-word operand to OpConstant.
         {Concatenate({ExpectedHeaderForBound(3),
-                      {MakeInstruction(SpvOpTypeInt, {1, 64, 0})},
-                      {spvOpcodeMake(4, SpvOpConstant), 1, 2, 44, 44}}),
+                      {MakeInstruction(spv::Op::OpTypeInt, {1, 64, 0})},
+                      {spvOpcodeMake(4, spv::Op::OpConstant), 1, 2, 44, 44}}),
          "Invalid word count: OpConstant starting at word 9 says it has 4"
          " words, but found 5 words instead."},
         // Word count is to small, where it would truncate a literal string.
         {Concatenate({ExpectedHeaderForBound(2),
-                      {spvOpcodeMake(3, SpvOpString), 1, 0x41414141, 0}}),
+                      {spvOpcodeMake(3, spv::Op::OpString), 1, 0x41414141, 0}}),
          "Invalid word count: OpString starting at word 5 says it has 3"
          " words, but found 4 words instead."},
         // Word count is too large.  The string terminates before the last
         // word.
         {Concatenate({ExpectedHeaderForBound(2),
-                      {spvOpcodeMake(4, SpvOpString), 1 /* result id */},
+                      {spvOpcodeMake(4, spv::Op::OpString), 1 /* result id */},
                       MakeVector("abc"),
                       {0 /* this word does not belong*/}}),
          "Invalid instruction OpString starting at word 5: expected no more"
@@ -701,111 +1004,116 @@
         // Word count is too large.  There are too many words after the string
         // literal.  A linkage attribute decoration is the only case in SPIR-V
         // where a string operand is followed by another operand.
-        {Concatenate({ExpectedHeaderForBound(2),
-                      {spvOpcodeMake(6, SpvOpDecorate), 1 /* target id */,
-                       static_cast<uint32_t>(SpvDecorationLinkageAttributes)},
-                      MakeVector("abc"),
-                      {static_cast<uint32_t>(SpvLinkageTypeImport),
-                       0 /* does not belong */}}),
+        {Concatenate(
+             {ExpectedHeaderForBound(2),
+              {spvOpcodeMake(6, spv::Op::OpDecorate), 1 /* target id */,
+               static_cast<uint32_t>(spv::Decoration::LinkageAttributes)},
+              MakeVector("abc"),
+              {static_cast<uint32_t>(spv::LinkageType::Import),
+               0 /* does not belong */}}),
          "Invalid instruction OpDecorate starting at word 5: expected no more"
          " operands after 5 words, but stated word count is 6."},
         // Like the previous case, but with 5 extra words.
-        {Concatenate({ExpectedHeaderForBound(2),
-                      {spvOpcodeMake(10, SpvOpDecorate), 1 /* target id */,
-                       static_cast<uint32_t>(SpvDecorationLinkageAttributes)},
-                      MakeVector("abc"),
-                      {static_cast<uint32_t>(SpvLinkageTypeImport),
-                       /* don't belong */ 0, 1, 2, 3, 4}}),
+        {Concatenate(
+             {ExpectedHeaderForBound(2),
+              {spvOpcodeMake(10, spv::Op::OpDecorate), 1 /* target id */,
+               static_cast<uint32_t>(spv::Decoration::LinkageAttributes)},
+              MakeVector("abc"),
+              {static_cast<uint32_t>(spv::LinkageType::Import),
+               /* don't belong */ 0, 1, 2, 3, 4}}),
          "Invalid instruction OpDecorate starting at word 5: expected no more"
          " operands after 5 words, but stated word count is 10."},
         // Like the previous two cases, but with OpMemberDecorate.
-        {Concatenate({ExpectedHeaderForBound(2),
-                      {spvOpcodeMake(7, SpvOpMemberDecorate), 1 /* target id */,
-                       42 /* member index */,
-                       static_cast<uint32_t>(SpvDecorationLinkageAttributes)},
-                      MakeVector("abc"),
-                      {static_cast<uint32_t>(SpvLinkageTypeImport),
-                       0 /* does not belong */}}),
+        {Concatenate(
+             {ExpectedHeaderForBound(2),
+              {spvOpcodeMake(7, spv::Op::OpMemberDecorate), 1 /* target id */,
+               42 /* member index */,
+               static_cast<uint32_t>(spv::Decoration::LinkageAttributes)},
+              MakeVector("abc"),
+              {static_cast<uint32_t>(spv::LinkageType::Import),
+               0 /* does not belong */}}),
          "Invalid instruction OpMemberDecorate starting at word 5: expected no"
          " more operands after 6 words, but stated word count is 7."},
-        {Concatenate({ExpectedHeaderForBound(2),
-                      {spvOpcodeMake(11, SpvOpMemberDecorate),
-                       1 /* target id */, 42 /* member index */,
-                       static_cast<uint32_t>(SpvDecorationLinkageAttributes)},
-                      MakeVector("abc"),
-                      {static_cast<uint32_t>(SpvLinkageTypeImport),
-                       /* don't belong */ 0, 1, 2, 3, 4}}),
+        {Concatenate(
+             {ExpectedHeaderForBound(2),
+              {spvOpcodeMake(11, spv::Op::OpMemberDecorate), 1 /* target id */,
+               42 /* member index */,
+               static_cast<uint32_t>(spv::Decoration::LinkageAttributes)},
+              MakeVector("abc"),
+              {static_cast<uint32_t>(spv::LinkageType::Import),
+               /* don't belong */ 0, 1, 2, 3, 4}}),
          "Invalid instruction OpMemberDecorate starting at word 5: expected no"
          " more operands after 6 words, but stated word count is 11."},
         // Word count is too large.  There should be no more words
         // after the RelaxedPrecision decoration.
         {Concatenate({ExpectedHeaderForBound(2),
-                      {spvOpcodeMake(4, SpvOpDecorate), 1 /* target id */,
-                       static_cast<uint32_t>(SpvDecorationRelaxedPrecision),
+                      {spvOpcodeMake(4, spv::Op::OpDecorate), 1 /* target id */,
+                       static_cast<uint32_t>(spv::Decoration::RelaxedPrecision),
                        0 /* does not belong */}}),
          "Invalid instruction OpDecorate starting at word 5: expected no"
          " more operands after 3 words, but stated word count is 4."},
         // Word count is too large.  There should be only one word after
         // the SpecId decoration enum word.
         {Concatenate({ExpectedHeaderForBound(2),
-                      {spvOpcodeMake(5, SpvOpDecorate), 1 /* target id */,
-                       static_cast<uint32_t>(SpvDecorationSpecId),
+                      {spvOpcodeMake(5, spv::Op::OpDecorate), 1 /* target id */,
+                       static_cast<uint32_t>(spv::Decoration::SpecId),
                        42 /* the spec id */, 0 /* does not belong */}}),
          "Invalid instruction OpDecorate starting at word 5: expected no"
          " more operands after 4 words, but stated word count is 5."},
         {Concatenate({ExpectedHeaderForBound(2),
-                      {spvOpcodeMake(2, SpvOpTypeVoid), 0}}),
+                      {spvOpcodeMake(2, spv::Op::OpTypeVoid), 0}}),
          "Error: Result Id is 0"},
         {Concatenate({
              ExpectedHeaderForBound(2),
-             {spvOpcodeMake(2, SpvOpTypeVoid), 1},
-             {spvOpcodeMake(2, SpvOpTypeBool), 1},
+             {spvOpcodeMake(2, spv::Op::OpTypeVoid), 1},
+             {spvOpcodeMake(2, spv::Op::OpTypeBool), 1},
          }),
          "Id 1 is defined more than once"},
         {Concatenate({ExpectedHeaderForBound(3),
-                      MakeInstruction(SpvOpExtInst, {2, 3, 100, 4, 5})}),
+                      MakeInstruction(spv::Op::OpExtInst, {2, 3, 100, 4, 5})}),
          "OpExtInst set Id 100 does not reference an OpExtInstImport result "
          "Id"},
         {Concatenate({ExpectedHeaderForBound(101),
-                      MakeInstruction(SpvOpExtInstImport, {100},
+                      MakeInstruction(spv::Op::OpExtInstImport, {100},
                                       MakeVector("OpenCL.std")),
                       // OpenCL cos is #14
-                      MakeInstruction(SpvOpExtInst, {2, 3, 100, 14, 5, 999})}),
+                      MakeInstruction(spv::Op::OpExtInst,
+                                      {2, 3, 100, 14, 5, 999})}),
          "Invalid instruction OpExtInst starting at word 10: expected no "
          "more operands after 6 words, but stated word count is 7."},
         // In this case, the OpSwitch selector refers to an invalid ID.
         {Concatenate({ExpectedHeaderForBound(3),
-                      MakeInstruction(SpvOpSwitch, {1, 2, 42, 3})}),
+                      MakeInstruction(spv::Op::OpSwitch, {1, 2, 42, 3})}),
          "Invalid OpSwitch: selector id 1 has no type"},
         // In this case, the OpSwitch selector refers to an ID that has
         // no type.
         {Concatenate({ExpectedHeaderForBound(3),
-                      MakeInstruction(SpvOpLabel, {1}),
-                      MakeInstruction(SpvOpSwitch, {1, 2, 42, 3})}),
+                      MakeInstruction(spv::Op::OpLabel, {1}),
+                      MakeInstruction(spv::Op::OpSwitch, {1, 2, 42, 3})}),
          "Invalid OpSwitch: selector id 1 has no type"},
         {Concatenate({ExpectedHeaderForBound(3),
-                      MakeInstruction(SpvOpTypeInt, {1, 32, 0}),
-                      MakeInstruction(SpvOpSwitch, {1, 3, 42, 3})}),
+                      MakeInstruction(spv::Op::OpTypeInt, {1, 32, 0}),
+                      MakeInstruction(spv::Op::OpSwitch, {1, 3, 42, 3})}),
          "Invalid OpSwitch: selector id 1 is a type, not a value"},
         {Concatenate({ExpectedHeaderForBound(3),
-                      MakeInstruction(SpvOpTypeFloat, {1, 32}),
-                      MakeInstruction(SpvOpConstant, {1, 2, 0x78f00000}),
-                      MakeInstruction(SpvOpSwitch, {2, 3, 42, 3})}),
+                      MakeInstruction(spv::Op::OpTypeFloat, {1, 32}),
+                      MakeInstruction(spv::Op::OpConstant, {1, 2, 0x78f00000}),
+                      MakeInstruction(spv::Op::OpSwitch, {2, 3, 42, 3})}),
          "Invalid OpSwitch: selector id 2 is not a scalar integer"},
         {Concatenate({ExpectedHeaderForBound(3),
-                      MakeInstruction(SpvOpExtInstImport, {1},
+                      MakeInstruction(spv::Op::OpExtInstImport, {1},
                                       MakeVector("invalid-import"))}),
          "Invalid extended instruction import 'invalid-import'"},
         {Concatenate({
              ExpectedHeaderForBound(3),
-             MakeInstruction(SpvOpTypeInt, {1, 32, 0}),
-             MakeInstruction(SpvOpConstant, {2, 2, 42}),
+             MakeInstruction(spv::Op::OpTypeInt, {1, 32, 0}),
+             MakeInstruction(spv::Op::OpConstant, {2, 2, 42}),
          }),
          "Type Id 2 is not a type"},
         {Concatenate({
              ExpectedHeaderForBound(3),
-             MakeInstruction(SpvOpTypeBool, {1}),
-             MakeInstruction(SpvOpConstant, {1, 2, 42}),
+             MakeInstruction(spv::Op::OpTypeBool, {1}),
+             MakeInstruction(spv::Op::OpConstant, {1, 2, 42}),
          }),
          "Type Id 1 is not a scalar numeric type"},
     }));
@@ -846,7 +1154,10 @@
         {"%2 = OpSpecConstantOp %1 !1000 %2",
          "Invalid OpSpecConstantOp opcode: 1000"},
         {"OpCapability !9999", "Invalid capability operand: 9999"},
-        {"OpSource !9999 100", "Invalid source language operand: 9999"},
+        {"OpSource !9999 100",
+         "Invalid source language operand: 9999, if you are creating a new "
+         "source language please use value 0 (Unknown) and when ready, add "
+         "your source language to SPRIV-Headers"},
         {"OpEntryPoint !9999", "Invalid execution model operand: 9999"},
         {"OpMemoryModel !9999", "Invalid addressing model operand: 9999"},
         {"OpMemoryModel Logical !9999", "Invalid memory model operand: 9999"},
diff --git a/test/binary_to_text_test.cpp b/test/binary_to_text_test.cpp
index 44705f2..85d5bd1 100644
--- a/test/binary_to_text_test.cpp
+++ b/test/binary_to_text_test.cpp
@@ -185,43 +185,43 @@
 INSTANTIATE_TEST_SUITE_P(
     InvalidIds, BinaryToTextFail,
     ::testing::ValuesIn(std::vector<FailedDecodeCase>{
-        {"", spvtest::MakeInstruction(SpvOpTypeVoid, {0}),
+        {"", spvtest::MakeInstruction(spv::Op::OpTypeVoid, {0}),
          "Error: Result Id is 0"},
-        {"", spvtest::MakeInstruction(SpvOpConstant, {0, 1, 42}),
+        {"", spvtest::MakeInstruction(spv::Op::OpConstant, {0, 1, 42}),
          "Error: Type Id is 0"},
-        {"%1 = OpTypeVoid", spvtest::MakeInstruction(SpvOpTypeVoid, {1}),
+        {"%1 = OpTypeVoid", spvtest::MakeInstruction(spv::Op::OpTypeVoid, {1}),
          "Id 1 is defined more than once"},
         {"%1 = OpTypeVoid\n"
          "%2 = OpNot %1 %foo",
-         spvtest::MakeInstruction(SpvOpNot, {1, 2, 3}),
+         spvtest::MakeInstruction(spv::Op::OpNot, {1, 2, 3}),
          "Id 2 is defined more than once"},
         {"%1 = OpTypeVoid\n"
          "%2 = OpNot %1 %foo",
-         spvtest::MakeInstruction(SpvOpNot, {1, 1, 3}),
+         spvtest::MakeInstruction(spv::Op::OpNot, {1, 1, 3}),
          "Id 1 is defined more than once"},
         // The following are the two failure cases for
         // Parser::setNumericTypeInfoForType.
-        {"", spvtest::MakeInstruction(SpvOpConstant, {500, 1, 42}),
+        {"", spvtest::MakeInstruction(spv::Op::OpConstant, {500, 1, 42}),
          "Type Id 500 is not a type"},
         {"%1 = OpTypeInt 32 0\n"
          "%2 = OpTypeVector %1 4",
-         spvtest::MakeInstruction(SpvOpConstant, {2, 3, 999}),
+         spvtest::MakeInstruction(spv::Op::OpConstant, {2, 3, 999}),
          "Type Id 2 is not a scalar numeric type"},
     }));
 
 INSTANTIATE_TEST_SUITE_P(
     InvalidIdsCheckedDuringLiteralCaseParsing, BinaryToTextFail,
     ::testing::ValuesIn(std::vector<FailedDecodeCase>{
-        {"", spvtest::MakeInstruction(SpvOpSwitch, {1, 2, 3, 4}),
+        {"", spvtest::MakeInstruction(spv::Op::OpSwitch, {1, 2, 3, 4}),
          "Invalid OpSwitch: selector id 1 has no type"},
         {"%1 = OpTypeVoid\n",
-         spvtest::MakeInstruction(SpvOpSwitch, {1, 2, 3, 4}),
+         spvtest::MakeInstruction(spv::Op::OpSwitch, {1, 2, 3, 4}),
          "Invalid OpSwitch: selector id 1 is a type, not a value"},
         {"%1 = OpConstantTrue !500",
-         spvtest::MakeInstruction(SpvOpSwitch, {1, 2, 3, 4}),
+         spvtest::MakeInstruction(spv::Op::OpSwitch, {1, 2, 3, 4}),
          "Type Id 500 is not a type"},
         {"%1 = OpTypeFloat 32\n%2 = OpConstant %1 1.5",
-         spvtest::MakeInstruction(SpvOpSwitch, {2, 3, 4, 5}),
+         spvtest::MakeInstruction(spv::Op::OpSwitch, {2, 3, 4, 5}),
          "Invalid OpSwitch: selector id 2 is not a scalar integer"},
     }));
 
diff --git a/test/comment_test.cpp b/test/comment_test.cpp
index 49f8df6..5cc9b1d 100644
--- a/test/comment_test.cpp
+++ b/test/comment_test.cpp
@@ -40,10 +40,10 @@
 
   EXPECT_THAT(
       CompiledInstructions(input),
-      Eq(Concatenate({MakeInstruction(SpvOpMemoryModel,
-                                      {uint32_t(SpvAddressingModelLogical),
-                                       uint32_t(SpvMemoryModelSimple)}),
-                      MakeInstruction(SpvOpExtInstImport, {1},
+      Eq(Concatenate({MakeInstruction(spv::Op::OpMemoryModel,
+                                      {uint32_t(spv::AddressingModel::Logical),
+                                       uint32_t(spv::MemoryModel::Simple)}),
+                      MakeInstruction(spv::Op::OpExtInstImport, {1},
                                       MakeVector("GLSL.std.450"))})));
 }
 
diff --git a/test/cpp_interface_test.cpp b/test/cpp_interface_test.cpp
index 4cab4df..222f44f 100644
--- a/test/cpp_interface_test.cpp
+++ b/test/cpp_interface_test.cpp
@@ -19,7 +19,7 @@
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 #include "spirv-tools/optimizer.hpp"
-#include "spirv/1.1/spirv.h"
+#include "spirv/unified1/spirv.hpp11"
 
 namespace spvtools {
 namespace {
@@ -47,7 +47,7 @@
   std::vector<uint32_t> binary;
   EXPECT_TRUE(t.Assemble(input_text, &binary));
   EXPECT_TRUE(binary.size() > 5u);
-  EXPECT_EQ(SpvMagicNumber, binary[0]);
+  EXPECT_EQ(spv::MagicNumber, binary[0]);
   EXPECT_EQ(kExpectedSpvVersion, binary[1]);
 
   // This cannot pass validation since %1 is not defined.
@@ -74,7 +74,7 @@
   EXPECT_TRUE(t.Assemble("", &binary));
   // We only have the header.
   EXPECT_EQ(5u, binary.size());
-  EXPECT_EQ(SpvMagicNumber, binary[0]);
+  EXPECT_EQ(spv::MagicNumber, binary[0]);
   EXPECT_EQ(kExpectedSpvVersion, binary[1]);
 }
 
@@ -85,21 +85,21 @@
     std::vector<uint32_t> binary;
     EXPECT_TRUE(t.Assemble(input_text, &binary));
     EXPECT_TRUE(binary.size() > 5u);
-    EXPECT_EQ(SpvMagicNumber, binary[0]);
+    EXPECT_EQ(spv::MagicNumber, binary[0]);
     EXPECT_EQ(kExpectedSpvVersion, binary[1]);
   }
   {
     std::vector<uint32_t> binary;
     EXPECT_TRUE(t.Assemble(input_text.data(), input_text.size(), &binary));
     EXPECT_TRUE(binary.size() > 5u);
-    EXPECT_EQ(SpvMagicNumber, binary[0]);
+    EXPECT_EQ(spv::MagicNumber, binary[0]);
     EXPECT_EQ(kExpectedSpvVersion, binary[1]);
   }
   {  // Ignore the last newline.
     std::vector<uint32_t> binary;
     EXPECT_TRUE(t.Assemble(input_text.data(), input_text.size() - 1, &binary));
     EXPECT_TRUE(binary.size() > 5u);
-    EXPECT_EQ(SpvMagicNumber, binary[0]);
+    EXPECT_EQ(spv::MagicNumber, binary[0]);
     EXPECT_EQ(kExpectedSpvVersion, binary[1]);
   }
 }
@@ -322,6 +322,36 @@
   EXPECT_EQ(Header(), optimized_text);
 }
 
+TEST(SpirvHeadersCpp, BitwiseOrMemoryAccessMask) {
+  EXPECT_EQ(spv::MemoryAccessMask(6), spv::MemoryAccessMask::Aligned |
+                                          spv::MemoryAccessMask::Nontemporal);
+}
+
+TEST(SpirvHeadersCpp, BitwiseAndMemoryAccessMask) {
+  EXPECT_EQ(spv::MemoryAccessMask::Aligned,
+            spv::MemoryAccessMask::Aligned & spv::MemoryAccessMask(6));
+  EXPECT_EQ(spv::MemoryAccessMask::Nontemporal,
+            spv::MemoryAccessMask::Nontemporal & spv::MemoryAccessMask(6));
+  EXPECT_EQ(spv::MemoryAccessMask(0), spv::MemoryAccessMask::Nontemporal &
+                                          spv::MemoryAccessMask::Aligned);
+}
+
+TEST(SpirvHeadersCpp, BitwiseXorMemoryAccessMask) {
+  EXPECT_EQ(spv::MemoryAccessMask::Nontemporal,
+            spv::MemoryAccessMask::Aligned ^ spv::MemoryAccessMask(6));
+  EXPECT_EQ(spv::MemoryAccessMask::Aligned,
+            spv::MemoryAccessMask::Nontemporal ^ spv::MemoryAccessMask(6));
+  EXPECT_EQ(spv::MemoryAccessMask(6), spv::MemoryAccessMask::Nontemporal ^
+                                          spv::MemoryAccessMask::Aligned);
+  EXPECT_EQ(spv::MemoryAccessMask(0), spv::MemoryAccessMask::Nontemporal ^
+                                          spv::MemoryAccessMask::Nontemporal);
+}
+
+TEST(SpirvHeadersCpp, BitwiseNegateMemoryAccessMask) {
+  EXPECT_EQ(spv::MemoryAccessMask(~(uint32_t(4))),
+            ~spv::MemoryAccessMask::Nontemporal);
+}
+
 // TODO(antiagainst): tests for SetMessageConsumer().
 
 }  // namespace
diff --git a/test/diff/diff_files/OpExtInst_in_src_only_autogen.cpp b/test/diff/diff_files/OpExtInst_in_src_only_autogen.cpp
index 9944c2c..bd5a7d5 100644
--- a/test/diff/diff_files/OpExtInst_in_src_only_autogen.cpp
+++ b/test/diff/diff_files/OpExtInst_in_src_only_autogen.cpp
@@ -95,8 +95,7 @@
   constexpr char kDiff[] = R"( ; SPIR-V
  ; Version: 1.6
  ; Generator: Khronos SPIR-V Tools Assembler; 0
--; Bound: 15
-+; Bound: 16
+ ; Bound: 15
  ; Schema: 0
  OpCapability Shader
 -%1 = OpExtInstImport "GLSL.std.450"
@@ -199,8 +198,7 @@
   constexpr char kDiff[] = R"( ; SPIR-V
  ; Version: 1.6
  ; Generator: Khronos SPIR-V Tools Assembler; 0
--; Bound: 15
-+; Bound: 16
+ ; Bound: 15
  ; Schema: 0
  OpCapability Shader
 -%1 = OpExtInstImport "GLSL.std.450"
diff --git a/test/diff/diff_files/basic_autogen.cpp b/test/diff/diff_files/basic_autogen.cpp
index f3afc70..d4b6846 100644
--- a/test/diff/diff_files/basic_autogen.cpp
+++ b/test/diff/diff_files/basic_autogen.cpp
@@ -129,7 +129,7 @@
  ; Version: 1.6
  ; Generator: Khronos SPIR-V Tools Assembler; 0
 -; Bound: 27
-+; Bound: 36
++; Bound: 30
  ; Schema: 0
  OpCapability Shader
 +%27 = OpExtInstImport "GLSL.std.450"
@@ -272,7 +272,7 @@
  ; Version: 1.6
  ; Generator: Khronos SPIR-V Tools Assembler; 0
 -; Bound: 27
-+; Bound: 36
++; Bound: 30
  ; Schema: 0
  OpCapability Shader
 +%27 = OpExtInstImport "GLSL.std.450"
@@ -324,7 +324,7 @@
  ; Version: 1.6
  ; Generator: Khronos SPIR-V Tools Assembler; 0
 -; Bound: 27
-+; Bound: 36
++; Bound: 30
  ; Schema: 0
  OpCapability Shader
 +%27 = OpExtInstImport "GLSL.std.450"
@@ -384,8 +384,8 @@
    6 ->   14 [TypeInt]
   13 ->   19 [TypePointer]
   14 ->   27 [Variable]
-  15 ->   34 [Constant]
-  16 ->   35 [TypeArray]
+  15 ->   28 [Constant]
+  16 ->   29 [TypeArray]
   17 ->   11 [TypeStruct]
   18 ->   12 [TypePointer]
   19 ->   13 [Variable]
diff --git a/test/diff/diff_files/constant_array_size_autogen.cpp b/test/diff/diff_files/constant_array_size_autogen.cpp
index 16975ff..2b6d7d8 100644
--- a/test/diff/diff_files/constant_array_size_autogen.cpp
+++ b/test/diff/diff_files/constant_array_size_autogen.cpp
@@ -125,7 +125,7 @@
  ; Version: 1.6
  ; Generator: Khronos SPIR-V Tools Assembler; 0
 -; Bound: 27
-+; Bound: 34
++; Bound: 28
  ; Schema: 0
  OpCapability Shader
  OpMemoryModel Logical GLSL450
@@ -259,7 +259,7 @@
  ; Version: 1.6
  ; Generator: Khronos SPIR-V Tools Assembler; 0
 -; Bound: 27
-+; Bound: 34
++; Bound: 28
  ; Schema: 0
  OpCapability Shader
  OpMemoryModel Logical GLSL450
diff --git a/test/diff/diff_files/diff_test_files_autogen.cmake b/test/diff/diff_files/diff_test_files_autogen.cmake
index 6440d0b..51cb62f 100644
--- a/test/diff/diff_files/diff_test_files_autogen.cmake
+++ b/test/diff/diff_files/diff_test_files_autogen.cmake
@@ -36,6 +36,7 @@
 "diff_files/large_functions_small_diffs_autogen.cpp"
 "diff_files/multiple_different_entry_points_autogen.cpp"
 "diff_files/multiple_same_entry_points_autogen.cpp"
+"diff_files/ray_query_types_autogen.cpp"
 "diff_files/reordered_if_blocks_autogen.cpp"
 "diff_files/reordered_switch_blocks_autogen.cpp"
 "diff_files/small_functions_small_diffs_autogen.cpp"
diff --git a/test/diff/diff_files/different_decorations_fragment_autogen.cpp b/test/diff/diff_files/different_decorations_fragment_autogen.cpp
index 0d34654..ec9074c 100644
--- a/test/diff/diff_files/different_decorations_fragment_autogen.cpp
+++ b/test/diff/diff_files/different_decorations_fragment_autogen.cpp
@@ -977,7 +977,7 @@
  ; Version: 1.6
  ; Generator: Khronos SPIR-V Tools Assembler; 0
 -; Bound: 82
-+; Bound: 92
++; Bound: 89
  ; Schema: 0
  OpCapability Shader
  OpMemoryModel Logical GLSL450
@@ -1030,8 +1030,7 @@
 +OpDecorate %83 DescriptorSet 0
 +OpDecorate %83 Binding 0
  OpDecorate %32 RelaxedPrecision
--OpDecorate %33 RelaxedPrecision
-+OpDecorate %84 RelaxedPrecision
+ OpDecorate %33 RelaxedPrecision
  OpDecorate %36 RelaxedPrecision
  OpDecorate %37 RelaxedPrecision
  OpDecorate %38 RelaxedPrecision
@@ -1040,10 +1039,8 @@
  OpDecorate %42 RelaxedPrecision
  OpDecorate %43 RelaxedPrecision
  OpDecorate %48 RelaxedPrecision
--OpDecorate %49 RelaxedPrecision
--OpDecorate %50 RelaxedPrecision
-+OpDecorate %85 RelaxedPrecision
-+OpDecorate %86 RelaxedPrecision
+ OpDecorate %49 RelaxedPrecision
+ OpDecorate %50 RelaxedPrecision
  OpDecorate %52 RelaxedPrecision
  OpDecorate %53 RelaxedPrecision
  OpDecorate %54 RelaxedPrecision
@@ -1082,13 +1079,13 @@
  %61 = OpTypeVoid
  %69 = OpConstant %16 0
  %78 = OpConstant %16 1
-+%88 = OpTypePointer Private %2
++%85 = OpTypePointer Private %2
  %3 = OpTypePointer Input %2
  %7 = OpTypePointer UniformConstant %6
  %10 = OpTypePointer UniformConstant %9
  %13 = OpTypePointer Uniform %12
  %19 = OpTypePointer Uniform %18
-+%89 = OpTypePointer Private %2
++%86 = OpTypePointer Private %2
  %21 = OpTypePointer Output %2
  %28 = OpTypePointer Uniform %27
  %30 = OpTypePointer Function %2
@@ -1106,19 +1103,16 @@
  %22 = OpVariable %21 Output
 -%29 = OpVariable %28 Uniform
 +%83 = OpVariable %28 Uniform
-+%90 = OpConstant %23 0
-+%91 = OpConstant %1 0.5
++%87 = OpConstant %23 0
++%88 = OpConstant %1 0.5
  %32 = OpFunction %2 None %31
--%33 = OpFunctionParameter %30
-+%84 = OpFunctionParameter %30
+ %33 = OpFunctionParameter %30
  %34 = OpLabel
  %36 = OpLoad %6 %8
--%37 = OpLoad %2 %33
-+%37 = OpLoad %2 %84
+ %37 = OpLoad %2 %33
  %38 = OpVectorShuffle %35 %37 %37 0 1
  %39 = OpImageSampleImplicitLod %2 %36 %38
--%41 = OpLoad %2 %33
-+%41 = OpLoad %2 %84
+ %41 = OpLoad %2 %33
  %42 = OpVectorShuffle %35 %41 %41 2 3
  %43 = OpConvertFToS %40 %42
  %44 = OpLoad %9 %11
@@ -1127,16 +1121,12 @@
  OpReturnValue %46
  OpFunctionEnd
  %48 = OpFunction %2 None %47
--%49 = OpFunctionParameter %30
--%50 = OpFunctionParameter %30
-+%85 = OpFunctionParameter %30
-+%86 = OpFunctionParameter %30
+ %49 = OpFunctionParameter %30
+ %50 = OpFunctionParameter %30
  %51 = OpLabel
--%52 = OpLoad %2 %49
-+%52 = OpLoad %2 %85
+ %52 = OpLoad %2 %49
  %53 = OpVectorShuffle %35 %52 %52 0 1
--%54 = OpLoad %2 %50
-+%54 = OpLoad %2 %86
+ %54 = OpLoad %2 %50
  %55 = OpVectorShuffle %35 %54 %54 2 3
  %56 = OpCompositeExtract %1 %53 0
  %57 = OpCompositeExtract %1 %53 1
@@ -1154,9 +1144,9 @@
  OpStore %65 %66
  %67 = OpFunctionCall %2 %32 %65
 -%71 = OpAccessChain %70 %14 %69
-+%87 = OpAccessChain %70 %82 %69
++%84 = OpAccessChain %70 %82 %69
 -%72 = OpLoad %2 %71
-+%72 = OpLoad %2 %87
++%72 = OpLoad %2 %84
  OpStore %68 %72
 -%74 = OpAccessChain %70 %20 %69 %69
 +%74 = OpAccessChain %70 %14 %69 %69
diff --git a/test/diff/diff_files/different_decorations_vertex_autogen.cpp b/test/diff/diff_files/different_decorations_vertex_autogen.cpp
index f65ee5a..134ebb4 100644
--- a/test/diff/diff_files/different_decorations_vertex_autogen.cpp
+++ b/test/diff/diff_files/different_decorations_vertex_autogen.cpp
@@ -777,7 +777,7 @@
  ; Version: 1.6
  ; Generator: Khronos SPIR-V Tools Assembler; 0
 -; Bound: 58
-+; Bound: 79
++; Bound: 77
  ; Schema: 0
  OpCapability Shader
  OpMemoryModel Logical GLSL450
@@ -817,12 +817,10 @@
 -OpMemberDecorate %23 3 BuiltIn CullDistance
  OpDecorate %23 Block
  OpDecorate %28 RelaxedPrecision
--OpDecorate %29 RelaxedPrecision
-+OpDecorate %59 RelaxedPrecision
+ OpDecorate %29 RelaxedPrecision
  OpDecorate %31 RelaxedPrecision
  OpDecorate %32 RelaxedPrecision
--OpDecorate %33 RelaxedPrecision
-+OpDecorate %60 RelaxedPrecision
+ OpDecorate %33 RelaxedPrecision
  OpDecorate %35 RelaxedPrecision
  OpDecorate %36 RelaxedPrecision
  OpDecorate %37 RelaxedPrecision
@@ -845,9 +843,9 @@
 +%23 = OpTypeStruct %2
  %38 = OpTypeVoid
  %45 = OpConstant %12 0
-+%65 = OpTypePointer Private %2
++%63 = OpTypePointer Private %2
  %3 = OpTypePointer Input %2
-+%66 = OpTypePointer Private %2
++%64 = OpTypePointer Private %2
  %7 = OpTypePointer Output %2
  %10 = OpTypePointer Uniform %9
  %18 = OpTypePointer Uniform %17
@@ -865,26 +863,21 @@
 -%19 = OpVariable %18 Uniform
 +%19 = OpVariable %10 Uniform
  %20 = OpVariable %7 Output
-+%58 = OpVariable %66 Private
++%58 = OpVariable %64 Private
  %25 = OpVariable %24 Output
-+%67 = OpConstant %13 0
-+%68 = OpConstant %1 0.5
++%65 = OpConstant %13 0
++%66 = OpConstant %1 0.5
  %28 = OpFunction %2 None %27
--%29 = OpFunctionParameter %26
-+%59 = OpFunctionParameter %26
+ %29 = OpFunctionParameter %26
  %30 = OpLabel
--%31 = OpLoad %2 %29
-+%31 = OpLoad %2 %59
+ %31 = OpLoad %2 %29
  OpReturnValue %31
  OpFunctionEnd
  %32 = OpFunction %2 None %27
--%33 = OpFunctionParameter %26
-+%60 = OpFunctionParameter %26
+ %33 = OpFunctionParameter %26
  %34 = OpLabel
--%35 = OpLoad %2 %33
-+%35 = OpLoad %2 %60
--%36 = OpLoad %2 %33
-+%36 = OpLoad %2 %60
+ %35 = OpLoad %2 %33
+ %36 = OpLoad %2 %33
  %37 = OpFAdd %2 %35 %36
  OpReturnValue %37
  OpFunctionEnd
@@ -894,41 +887,41 @@
  %50 = OpVariable %26 Function
  %53 = OpVariable %26 Function
 -%43 = OpLoad %2 %4
-+%61 = OpLoad %2 %5
++%59 = OpLoad %2 %5
 -OpStore %42 %43
-+OpStore %42 %61
++OpStore %42 %59
  %44 = OpFunctionCall %2 %28 %42
 -%47 = OpAccessChain %46 %11 %45
-+%62 = OpAccessChain %46 %19 %45
++%60 = OpAccessChain %46 %19 %45
 -%48 = OpLoad %2 %47
-+%48 = OpLoad %2 %62
++%48 = OpLoad %2 %60
  %49 = OpFAdd %2 %44 %48
 -OpStore %8 %49
 +OpStore %20 %49
 -%51 = OpLoad %2 %5
-+%63 = OpLoad %2 %6
++%61 = OpLoad %2 %6
 -OpStore %50 %51
-+OpStore %50 %63
++OpStore %50 %61
  %52 = OpFunctionCall %2 %32 %50
 -%54 = OpLoad %2 %6
-+%64 = OpLoad %2 %4
++%62 = OpLoad %2 %4
 -OpStore %53 %54
-+OpStore %53 %64
++OpStore %53 %62
  %55 = OpFunctionCall %2 %28 %53
  %56 = OpFAdd %2 %52 %55
  %57 = OpAccessChain %7 %25 %45
  OpStore %57 %56
-+%69 = OpAccessChain %7 %25 %67
-+%70 = OpLoad %2 %69
-+%71 = OpCompositeExtract %1 %70 0
-+%72 = OpCompositeExtract %1 %70 1
-+%73 = OpCompositeExtract %1 %70 2
-+%74 = OpCompositeExtract %1 %70 3
-+%76 = OpFNegate %1 %71
-+%77 = OpFAdd %1 %73 %74
-+%78 = OpFMul %1 %77 %68
-+%75 = OpCompositeConstruct %2 %72 %76 %78 %74
-+OpStore %69 %75
++%67 = OpAccessChain %7 %25 %65
++%68 = OpLoad %2 %67
++%69 = OpCompositeExtract %1 %68 0
++%70 = OpCompositeExtract %1 %68 1
++%71 = OpCompositeExtract %1 %68 2
++%72 = OpCompositeExtract %1 %68 3
++%74 = OpFNegate %1 %69
++%75 = OpFAdd %1 %71 %72
++%76 = OpFMul %1 %75 %66
++%73 = OpCompositeConstruct %2 %70 %74 %76 %72
++OpStore %67 %73
  OpReturn
  OpFunctionEnd
 )";
diff --git a/test/diff/diff_files/different_function_parameter_count_autogen.cpp b/test/diff/diff_files/different_function_parameter_count_autogen.cpp
index 3a077fb..e31a4a8 100644
--- a/test/diff/diff_files/different_function_parameter_count_autogen.cpp
+++ b/test/diff/diff_files/different_function_parameter_count_autogen.cpp
@@ -128,7 +128,7 @@
  ; Version: 1.6
  ; Generator: Khronos SPIR-V Tools Assembler; 0
 -; Bound: 25
-+; Bound: 33
++; Bound: 31
  ; Schema: 0
  OpCapability Shader
  %1 = OpExtInstImport "GLSL.std.450"
@@ -143,7 +143,7 @@
 +OpName %26 "v2"
  OpName %20 "o"
  OpName %23 "param"
-+OpName %31 "param"
++OpName %29 "param"
  OpDecorate %20 RelaxedPrecision
  OpDecorate %20 Location 0
  %2 = OpTypeVoid
@@ -162,13 +162,13 @@
  %4 = OpFunction %2 None %3
  %5 = OpLabel
  %23 = OpVariable %8 Function
-+%31 = OpVariable %8 Function
++%29 = OpVariable %8 Function
  OpStore %23 %22
 -%24 = OpFunctionCall %7 %11 %23
-+OpStore %31 %15
-+%32 = OpFunctionCall %7 %11 %23 %31
++OpStore %29 %15
++%30 = OpFunctionCall %7 %11 %23 %29
 -OpStore %20 %24
-+OpStore %20 %32
++OpStore %20 %30
  OpReturn
  OpFunctionEnd
 -%11 = OpFunction %7 None %9
@@ -280,7 +280,7 @@
  ; Version: 1.6
  ; Generator: Khronos SPIR-V Tools Assembler; 0
 -; Bound: 25
-+; Bound: 34
++; Bound: 31
  ; Schema: 0
  OpCapability Shader
  %1 = OpExtInstImport "GLSL.std.450"
@@ -306,28 +306,26 @@
  %4 = OpFunction %2 None %3
  %5 = OpLabel
  %23 = OpVariable %8 Function
-+%32 = OpVariable %8 Function
++%29 = OpVariable %8 Function
  OpStore %23 %22
 -%24 = OpFunctionCall %7 %11 %23
-+OpStore %32 %15
-+%33 = OpFunctionCall %7 %11 %23 %32
++OpStore %29 %15
++%30 = OpFunctionCall %7 %11 %23 %29
 -OpStore %20 %24
-+OpStore %20 %33
++OpStore %20 %30
  OpReturn
  OpFunctionEnd
 -%11 = OpFunction %7 None %9
 +%11 = OpFunction %7 None %25
--%10 = OpFunctionParameter %8
+ %10 = OpFunctionParameter %8
 +%26 = OpFunctionParameter %8
-+%27 = OpFunctionParameter %8
  %12 = OpLabel
--%13 = OpLoad %7 %10
-+%13 = OpLoad %7 %26
+ %13 = OpLoad %7 %10
 -%16 = OpFAdd %7 %13 %15
-+%28 = OpLoad %7 %27
-+%29 = OpFAdd %7 %13 %28
++%27 = OpLoad %7 %26
++%28 = OpFAdd %7 %13 %27
 -OpReturnValue %16
-+OpReturnValue %29
++OpReturnValue %28
  OpFunctionEnd
 )";
   Options options;
diff --git a/test/diff/diff_files/extra_if_block_autogen.cpp b/test/diff/diff_files/extra_if_block_autogen.cpp
index 4f91319..fee34ae 100644
--- a/test/diff/diff_files/extra_if_block_autogen.cpp
+++ b/test/diff/diff_files/extra_if_block_autogen.cpp
@@ -303,7 +303,7 @@
  ; Version: 1.6
  ; Generator: Khronos SPIR-V Tools Assembler; 0
 -; Bound: 69
-+; Bound: 81
++; Bound: 77
  ; Schema: 0
  OpCapability Shader
  %1 = OpExtInstImport "GLSL.std.450"
@@ -352,10 +352,10 @@
  OpDecorate %54 RelaxedPrecision
  OpDecorate %55 RelaxedPrecision
  OpDecorate %56 RelaxedPrecision
-+OpDecorate %72 RelaxedPrecision
++OpDecorate %70 RelaxedPrecision
  OpDecorate %57 RelaxedPrecision
-+OpDecorate %77 RelaxedPrecision
-+OpDecorate %78 RelaxedPrecision
++OpDecorate %75 RelaxedPrecision
++OpDecorate %76 RelaxedPrecision
  OpDecorate %58 RelaxedPrecision
  OpDecorate %63 RelaxedPrecision
  OpDecorate %63 Location 0
@@ -383,7 +383,7 @@
  %32 = OpConstant %19 1
  %49 = OpConstant %6 10
  %52 = OpConstant %6 0.5
-+%76 = OpConstant %6 0.100000001
++%74 = OpConstant %6 0.100000001
  %53 = OpConstant %6 0.699999988
  %61 = OpTypeVector %6 4
  %62 = OpTypePointer Output %61
@@ -439,20 +439,20 @@
  %55 = OpLoad %6 %45
  %56 = OpFMul %6 %55 %54
  OpStore %45 %56
-+%71 = OpAccessChain %21 %18 %32
-+%72 = OpLoad %15 %71
-+%73 = OpINotEqual %25 %72 %24
-+OpSelectionMerge %75 None
-+OpBranchConditional %73 %74 %75
-+%74 = OpLabel
++%69 = OpAccessChain %21 %18 %32
++%70 = OpLoad %15 %69
++%71 = OpINotEqual %25 %70 %24
++OpSelectionMerge %73 None
++OpBranchConditional %71 %72 %73
++%72 = OpLabel
  %57 = OpLoad %6 %45
-+%77 = OpFSub %6 %57 %76
-+OpStore %45 %77
-+OpBranch %75
-+%75 = OpLabel
-+%78 = OpLoad %6 %45
++%75 = OpFSub %6 %57 %74
++OpStore %45 %75
++OpBranch %73
++%73 = OpLabel
++%76 = OpLoad %6 %45
 -%58 = OpExtInst %6 %1 Exp %57
-+%58 = OpExtInst %6 %1 Exp %78
++%58 = OpExtInst %6 %1 Exp %76
  OpReturnValue %58
  OpFunctionEnd
 )";
@@ -716,7 +716,7 @@
  ; Version: 1.6
  ; Generator: Khronos SPIR-V Tools Assembler; 0
 -; Bound: 69
-+; Bound: 81
++; Bound: 77
  ; Schema: 0
  OpCapability Shader
  %1 = OpExtInstImport "GLSL.std.450"
@@ -754,10 +754,10 @@
  OpDecorate %54 RelaxedPrecision
  OpDecorate %55 RelaxedPrecision
  OpDecorate %56 RelaxedPrecision
-+OpDecorate %72 RelaxedPrecision
++OpDecorate %70 RelaxedPrecision
  OpDecorate %57 RelaxedPrecision
-+OpDecorate %77 RelaxedPrecision
-+OpDecorate %78 RelaxedPrecision
++OpDecorate %75 RelaxedPrecision
++OpDecorate %76 RelaxedPrecision
  OpDecorate %58 RelaxedPrecision
  OpDecorate %63 RelaxedPrecision
  OpDecorate %63 Location 0
@@ -785,7 +785,7 @@
  %32 = OpConstant %19 1
  %49 = OpConstant %6 10
  %52 = OpConstant %6 0.5
-+%76 = OpConstant %6 0.100000001
++%74 = OpConstant %6 0.100000001
  %53 = OpConstant %6 0.699999988
  %61 = OpTypeVector %6 4
  %62 = OpTypePointer Output %61
@@ -841,20 +841,20 @@
  %55 = OpLoad %6 %45
  %56 = OpFMul %6 %55 %54
  OpStore %45 %56
-+%71 = OpAccessChain %21 %18 %32
-+%72 = OpLoad %15 %71
-+%73 = OpINotEqual %25 %72 %24
-+OpSelectionMerge %75 None
-+OpBranchConditional %73 %74 %75
-+%74 = OpLabel
++%69 = OpAccessChain %21 %18 %32
++%70 = OpLoad %15 %69
++%71 = OpINotEqual %25 %70 %24
++OpSelectionMerge %73 None
++OpBranchConditional %71 %72 %73
++%72 = OpLabel
  %57 = OpLoad %6 %45
-+%77 = OpFSub %6 %57 %76
-+OpStore %45 %77
-+OpBranch %75
-+%75 = OpLabel
-+%78 = OpLoad %6 %45
++%75 = OpFSub %6 %57 %74
++OpStore %45 %75
++OpBranch %73
++%73 = OpLabel
++%76 = OpLoad %6 %45
 -%58 = OpExtInst %6 %1 Exp %57
-+%58 = OpExtInst %6 %1 Exp %78
++%58 = OpExtInst %6 %1 Exp %76
  OpReturnValue %58
  OpFunctionEnd
 )";
diff --git a/test/diff/diff_files/int_vs_uint_constants_autogen.cpp b/test/diff/diff_files/int_vs_uint_constants_autogen.cpp
index 187722e..11bb811 100644
--- a/test/diff/diff_files/int_vs_uint_constants_autogen.cpp
+++ b/test/diff/diff_files/int_vs_uint_constants_autogen.cpp
@@ -371,10 +371,10 @@
    3 ->   16 [TypePointer]
    4 ->   17 [Variable]
    5 ->    8 [TypeInt]
-   8 ->   23 [TypeVector]
+   8 ->   21 [TypeVector]
   13 ->   19 [TypePointer]
-  15 ->   29 [Constant]
-  16 ->   30 [TypeArray]
+  15 ->   22 [Constant]
+  16 ->   23 [TypeArray]
   17 ->   11 [TypeStruct]
   18 ->   12 [TypePointer]
   19 ->   13 [Variable]
diff --git a/test/diff/diff_files/multiple_same_entry_points_autogen.cpp b/test/diff/diff_files/multiple_same_entry_points_autogen.cpp
index 9d01166..00bee6b 100644
--- a/test/diff/diff_files/multiple_same_entry_points_autogen.cpp
+++ b/test/diff/diff_files/multiple_same_entry_points_autogen.cpp
@@ -125,9 +125,8 @@
  OpCapability Shader
  %1 = OpExtInstImport "GLSL.std.450"
  OpMemoryModel Logical GLSL450
-+OpEntryPoint Vertex %12 "main2" %13 %14 %15
  OpEntryPoint Vertex %4 "main1" %8 %10
--OpEntryPoint Vertex %12 "main2" %13 %14 %15
+ OpEntryPoint Vertex %12 "main2" %13 %14 %15
  OpSource ESSL 310
  OpName %4 "main1"
  OpName %12 "main2"
@@ -257,9 +256,8 @@
  OpCapability Shader
  %1 = OpExtInstImport "GLSL.std.450"
  OpMemoryModel Logical GLSL450
-+OpEntryPoint Vertex %12 "main2" %13 %14 %15
  OpEntryPoint Vertex %4 "main1" %8 %10
--OpEntryPoint Vertex %12 "main2" %13 %14 %15
+ OpEntryPoint Vertex %12 "main2" %13 %14 %15
  OpSource ESSL 310
  OpDecorate %8 Location 0
  OpDecorate %10 Location 0
@@ -304,9 +302,8 @@
  OpCapability Shader
  %1 = OpExtInstImport "GLSL.std.450"
  OpMemoryModel Logical GLSL450
-+OpEntryPoint Vertex %12 "main2" %13 %14 %15
  OpEntryPoint Vertex %4 "main1" %8 %10
--OpEntryPoint Vertex %12 "main2" %13 %14 %15
+ OpEntryPoint Vertex %12 "main2" %13 %14 %15
  OpSource ESSL 310
  OpName %4 "main1"
  OpName %12 "main2"
diff --git a/test/diff/diff_files/ray_query_types_autogen.cpp b/test/diff/diff_files/ray_query_types_autogen.cpp
new file mode 100644
index 0000000..5507def
--- /dev/null
+++ b/test/diff/diff_files/ray_query_types_autogen.cpp
@@ -0,0 +1,148 @@
+// 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 {
+
+// Test that OpTypeAccelerationStructureNV and OpTypeRayQueryKHR are
+// matched.
+constexpr char kSrc[] = R"(OpCapability RayQueryKHR
+OpCapability Shader
+OpExtension "SPV_KHR_ray_query"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %43 "main"
+OpExecutionMode %43 LocalSize 1 1 1
+%2 = OpTypeVoid
+%3 = OpTypeAccelerationStructureNV
+%13 = OpTypeRayQueryKHR
+%44 = OpTypeFunction %2
+%43 = OpFunction  %2  None %44
+%42 = OpLabel
+OpReturn
+OpFunctionEnd)";
+constexpr char kDst[] = R"(; SPIR-V
+; Version: 1.4
+; Generator: rspirv
+; Bound: 95
+OpCapability RayQueryKHR
+OpCapability Shader
+OpExtension "SPV_KHR_ray_query"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %43 "main"
+OpExecutionMode %43 LocalSize 1 1 1
+%2 = OpTypeVoid
+%3 = OpTypeAccelerationStructureNV
+%13 = OpTypeRayQueryKHR
+%44 = OpTypeFunction %2
+%43 = OpFunction  %2  None %44
+%42 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+TEST(DiffTest, RayQueryTypes) {
+  constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+ ; Bound: 45
+ ; Schema: 0
+ OpCapability RayQueryKHR
+ OpCapability Shader
+ OpExtension "SPV_KHR_ray_query"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %43 "main"
+ OpExecutionMode %43 LocalSize 1 1 1
+ %2 = OpTypeVoid
+ %3 = OpTypeAccelerationStructureKHR
+ %13 = OpTypeRayQueryKHR
+ %44 = OpTypeFunction %2
+ %43 = OpFunction %2 None %44
+ %42 = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+  Options options;
+  DoStringDiffTest(kSrc, kDst, kDiff, options);
+}
+
+TEST(DiffTest, RayQueryTypesNoDebug) {
+  constexpr char kSrcNoDebug[] = R"(OpCapability RayQueryKHR
+OpCapability Shader
+OpExtension "SPV_KHR_ray_query"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %43 "main"
+OpExecutionMode %43 LocalSize 1 1 1
+%2 = OpTypeVoid
+%3 = OpTypeAccelerationStructureNV
+%13 = OpTypeRayQueryKHR
+%44 = OpTypeFunction %2
+%43 = OpFunction  %2  None %44
+%42 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+  constexpr char kDstNoDebug[] = R"(; SPIR-V
+; Version: 1.4
+; Generator: rspirv
+; Bound: 95
+OpCapability RayQueryKHR
+OpCapability Shader
+OpExtension "SPV_KHR_ray_query"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %43 "main"
+OpExecutionMode %43 LocalSize 1 1 1
+%2 = OpTypeVoid
+%3 = OpTypeAccelerationStructureNV
+%13 = OpTypeRayQueryKHR
+%44 = OpTypeFunction %2
+%43 = OpFunction  %2  None %44
+%42 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+  constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+ ; Bound: 45
+ ; Schema: 0
+ OpCapability RayQueryKHR
+ OpCapability Shader
+ OpExtension "SPV_KHR_ray_query"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %43 "main"
+ OpExecutionMode %43 LocalSize 1 1 1
+ %2 = OpTypeVoid
+ %3 = OpTypeAccelerationStructureKHR
+ %13 = OpTypeRayQueryKHR
+ %44 = OpTypeFunction %2
+ %43 = OpFunction %2 None %44
+ %42 = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+  Options options;
+  DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options);
+}
+
+}  // namespace
+}  // namespace diff
+}  // namespace spvtools
diff --git a/test/diff/diff_files/ray_query_types_dst.spvasm b/test/diff/diff_files/ray_query_types_dst.spvasm
new file mode 100644
index 0000000..5f8be53
--- /dev/null
+++ b/test/diff/diff_files/ray_query_types_dst.spvasm
@@ -0,0 +1,18 @@
+; SPIR-V
+; Version: 1.4
+; Generator: rspirv
+; Bound: 95
+OpCapability RayQueryKHR
+OpCapability Shader
+OpExtension "SPV_KHR_ray_query"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %43 "main"
+OpExecutionMode %43 LocalSize 1 1 1
+%2 = OpTypeVoid
+%3 = OpTypeAccelerationStructureNV
+%13 = OpTypeRayQueryKHR
+%44 = OpTypeFunction %2
+%43 = OpFunction  %2  None %44
+%42 = OpLabel
+OpReturn
+OpFunctionEnd
diff --git a/test/diff/diff_files/ray_query_types_src.spvasm b/test/diff/diff_files/ray_query_types_src.spvasm
new file mode 100644
index 0000000..0b64015
--- /dev/null
+++ b/test/diff/diff_files/ray_query_types_src.spvasm
@@ -0,0 +1,16 @@
+;; Test that OpTypeAccelerationStructureNV and OpTypeRayQueryKHR are
+;; matched.
+OpCapability RayQueryKHR
+OpCapability Shader
+OpExtension "SPV_KHR_ray_query"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %43 "main"
+OpExecutionMode %43 LocalSize 1 1 1
+%2 = OpTypeVoid
+%3 = OpTypeAccelerationStructureNV
+%13 = OpTypeRayQueryKHR
+%44 = OpTypeFunction %2
+%43 = OpFunction  %2  None %44
+%42 = OpLabel
+OpReturn
+OpFunctionEnd
diff --git a/test/diff/diff_files/reordered_if_blocks_autogen.cpp b/test/diff/diff_files/reordered_if_blocks_autogen.cpp
index 0788199..3abaf40 100644
--- a/test/diff/diff_files/reordered_if_blocks_autogen.cpp
+++ b/test/diff/diff_files/reordered_if_blocks_autogen.cpp
@@ -203,8 +203,7 @@
   constexpr char kDiff[] = R"( ; SPIR-V
  ; Version: 1.6
  ; Generator: Khronos SPIR-V Tools Assembler; 0
--; Bound: 46
-+; Bound: 47
+ ; Bound: 46
  ; Schema: 0
  OpCapability Shader
  %1 = OpExtInstImport "GLSL.std.450"
@@ -471,8 +470,7 @@
   constexpr char kDiff[] = R"( ; SPIR-V
  ; Version: 1.6
  ; Generator: Khronos SPIR-V Tools Assembler; 0
--; Bound: 46
-+; Bound: 47
+ ; Bound: 46
  ; Schema: 0
  OpCapability Shader
  %1 = OpExtInstImport "GLSL.std.450"
diff --git a/test/diff/diff_files/reordered_switch_blocks_autogen.cpp b/test/diff/diff_files/reordered_switch_blocks_autogen.cpp
index c0ba48d..ade5350 100644
--- a/test/diff/diff_files/reordered_switch_blocks_autogen.cpp
+++ b/test/diff/diff_files/reordered_switch_blocks_autogen.cpp
@@ -212,8 +212,7 @@
   constexpr char kDiff[] = R"( ; SPIR-V
  ; Version: 1.6
  ; Generator: Khronos SPIR-V Tools Assembler; 0
--; Bound: 58
-+; Bound: 62
+ ; Bound: 58
  ; Schema: 0
  OpCapability Shader
  %1 = OpExtInstImport "GLSL.std.450"
@@ -485,8 +484,7 @@
   constexpr char kDiff[] = R"( ; SPIR-V
  ; Version: 1.6
  ; Generator: Khronos SPIR-V Tools Assembler; 0
--; Bound: 58
-+; Bound: 62
+ ; Bound: 58
  ; Schema: 0
  OpCapability Shader
  %1 = OpExtInstImport "GLSL.std.450"
diff --git a/test/diff/diff_files/spec_constant_array_size_autogen.cpp b/test/diff/diff_files/spec_constant_array_size_autogen.cpp
index 1962d27..98ad072 100644
--- a/test/diff/diff_files/spec_constant_array_size_autogen.cpp
+++ b/test/diff/diff_files/spec_constant_array_size_autogen.cpp
@@ -125,7 +125,7 @@
  ; Version: 1.6
  ; Generator: Khronos SPIR-V Tools Assembler; 0
 -; Bound: 27
-+; Bound: 36
++; Bound: 29
  ; Schema: 0
  OpCapability Shader
  OpMemoryModel Logical GLSL450
@@ -140,7 +140,7 @@
  OpName %19 ""
  OpName %22 "main"
  OpDecorate %4 Location 0
-+OpDecorate %34 SpecId 4
++OpDecorate %27 SpecId 4
  OpMemberDecorate %17 1 RelaxedPrecision
  OpMemberDecorate %17 0 BuiltIn Position
  OpMemberDecorate %17 1 BuiltIn PointSize
@@ -153,10 +153,10 @@
  %8 = OpTypeVector %5 4
 -%15 = OpConstant %5 8
 -%16 = OpTypeArray %1 %15
-+%34 = OpSpecConstant %5 8
-+%35 = OpTypeArray %1 %34
++%27 = OpSpecConstant %5 8
++%28 = OpTypeArray %1 %27
 -%17 = OpTypeStruct %2 %1 %16 %16
-+%17 = OpTypeStruct %2 %1 %35 %35
++%17 = OpTypeStruct %2 %1 %28 %28
  %20 = OpTypeVoid
  %25 = OpConstant %5 0
  %3 = OpTypePointer Input %2
@@ -261,14 +261,14 @@
  ; Version: 1.6
  ; Generator: Khronos SPIR-V Tools Assembler; 0
 -; Bound: 27
-+; Bound: 36
++; Bound: 29
  ; Schema: 0
  OpCapability Shader
  OpMemoryModel Logical GLSL450
  OpEntryPoint Vertex %22 "main" %4 %19
  OpSource GLSL 450
  OpDecorate %4 Location 0
-+OpDecorate %34 SpecId 4
++OpDecorate %27 SpecId 4
  OpMemberDecorate %17 1 RelaxedPrecision
  OpMemberDecorate %17 0 BuiltIn Position
  OpMemberDecorate %17 1 BuiltIn PointSize
@@ -281,10 +281,10 @@
  %8 = OpTypeVector %5 4
 -%15 = OpConstant %5 8
 -%16 = OpTypeArray %1 %15
-+%34 = OpSpecConstant %5 8
-+%35 = OpTypeArray %1 %34
++%27 = OpSpecConstant %5 8
++%28 = OpTypeArray %1 %27
 -%17 = OpTypeStruct %2 %1 %16 %16
-+%17 = OpTypeStruct %2 %1 %35 %35
++%17 = OpTypeStruct %2 %1 %28 %28
  %20 = OpTypeVoid
  %25 = OpConstant %5 0
  %3 = OpTypePointer Input %2
diff --git a/test/diff/diff_test.cpp b/test/diff/diff_test.cpp
index 5b11d0e..3b63c69 100644
--- a/test/diff/diff_test.cpp
+++ b/test/diff/diff_test.cpp
@@ -195,14 +195,14 @@
 
   // Differentiate them in the header.
   const spvtools::opt::ModuleHeader src_header = {
-      SpvMagicNumber,
+      spv::MagicNumber,
       SPV_SPIRV_VERSION_WORD(1, 3),
       SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS_ASSEMBLER, 3),
       src->module()->IdBound(),
       src->module()->schema(),
   };
   const spvtools::opt::ModuleHeader dst_header = {
-      SpvMagicNumber,
+      spv::MagicNumber,
       SPV_SPIRV_VERSION_WORD(1, 2),
       SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS_GLSLANG, 10),
       dst->module()->IdBound(),
diff --git a/test/enum_set_test.cpp b/test/enum_set_test.cpp
index 047d642..7a6e4ca 100644
--- a/test/enum_set_test.cpp
+++ b/test/enum_set_test.cpp
@@ -12,12 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "source/enum_set.h"
+
 #include <algorithm>
+#include <array>
+#include <random>
 #include <utility>
 #include <vector>
 
 #include "gmock/gmock.h"
-#include "source/enum_set.h"
 #include "test/unit_spirv.h"
 
 namespace spvtools {
@@ -25,214 +28,806 @@
 
 using spvtest::ElementsIn;
 using ::testing::Eq;
+using ::testing::Values;
 using ::testing::ValuesIn;
 
+enum class TestEnum : uint32_t {
+  ZERO = 0,
+  ONE = 1,
+  TWO = 2,
+  THREE = 3,
+  FOUR = 4,
+  FIVE = 5,
+  EIGHT = 8,
+  TWENTY = 20,
+  TWENTY_FOUR = 24,
+  THIRTY = 30,
+  ONE_HUNDRED = 100,
+  ONE_HUNDRED_FIFTY = 150,
+  TWO_HUNDRED = 200,
+  THREE_HUNDRED = 300,
+  FOUR_HUNDRED = 400,
+  FIVE_HUNDRED = 500,
+  SIX_HUNDRED = 600,
+};
+
+constexpr std::array kCapabilities{
+    spv::Capability::Matrix,
+    spv::Capability::Shader,
+    spv::Capability::Geometry,
+    spv::Capability::Tessellation,
+    spv::Capability::Addresses,
+    spv::Capability::Linkage,
+    spv::Capability::Kernel,
+    spv::Capability::Vector16,
+    spv::Capability::Float16Buffer,
+    spv::Capability::Float16,
+    spv::Capability::Float64,
+    spv::Capability::Int64,
+    spv::Capability::Int64Atomics,
+    spv::Capability::ImageBasic,
+    spv::Capability::ImageReadWrite,
+    spv::Capability::ImageMipmap,
+    spv::Capability::Pipes,
+    spv::Capability::Groups,
+    spv::Capability::DeviceEnqueue,
+    spv::Capability::LiteralSampler,
+    spv::Capability::AtomicStorage,
+    spv::Capability::Int16,
+    spv::Capability::TessellationPointSize,
+    spv::Capability::GeometryPointSize,
+    spv::Capability::ImageGatherExtended,
+    spv::Capability::StorageImageMultisample,
+    spv::Capability::UniformBufferArrayDynamicIndexing,
+    spv::Capability::SampledImageArrayDynamicIndexing,
+    spv::Capability::StorageBufferArrayDynamicIndexing,
+    spv::Capability::StorageImageArrayDynamicIndexing,
+    spv::Capability::ClipDistance,
+    spv::Capability::CullDistance,
+    spv::Capability::ImageCubeArray,
+    spv::Capability::SampleRateShading,
+    spv::Capability::ImageRect,
+    spv::Capability::SampledRect,
+    spv::Capability::GenericPointer,
+    spv::Capability::Int8,
+    spv::Capability::InputAttachment,
+    spv::Capability::SparseResidency,
+    spv::Capability::MinLod,
+    spv::Capability::Sampled1D,
+    spv::Capability::Image1D,
+    spv::Capability::SampledCubeArray,
+    spv::Capability::SampledBuffer,
+    spv::Capability::ImageBuffer,
+    spv::Capability::ImageMSArray,
+    spv::Capability::StorageImageExtendedFormats,
+    spv::Capability::ImageQuery,
+    spv::Capability::DerivativeControl,
+    spv::Capability::InterpolationFunction,
+    spv::Capability::TransformFeedback,
+    spv::Capability::GeometryStreams,
+    spv::Capability::StorageImageReadWithoutFormat,
+    spv::Capability::StorageImageWriteWithoutFormat,
+    spv::Capability::MultiViewport,
+    spv::Capability::SubgroupDispatch,
+    spv::Capability::NamedBarrier,
+    spv::Capability::PipeStorage,
+    spv::Capability::GroupNonUniform,
+    spv::Capability::GroupNonUniformVote,
+    spv::Capability::GroupNonUniformArithmetic,
+    spv::Capability::GroupNonUniformBallot,
+    spv::Capability::GroupNonUniformShuffle,
+    spv::Capability::GroupNonUniformShuffleRelative,
+    spv::Capability::GroupNonUniformClustered,
+    spv::Capability::GroupNonUniformQuad,
+    spv::Capability::ShaderLayer,
+    spv::Capability::ShaderViewportIndex,
+    spv::Capability::UniformDecoration,
+    spv::Capability::CoreBuiltinsARM,
+    spv::Capability::FragmentShadingRateKHR,
+    spv::Capability::SubgroupBallotKHR,
+    spv::Capability::DrawParameters,
+    spv::Capability::WorkgroupMemoryExplicitLayoutKHR,
+    spv::Capability::WorkgroupMemoryExplicitLayout8BitAccessKHR,
+    spv::Capability::WorkgroupMemoryExplicitLayout16BitAccessKHR,
+    spv::Capability::SubgroupVoteKHR,
+    spv::Capability::StorageBuffer16BitAccess,
+    spv::Capability::StorageUniformBufferBlock16,
+    spv::Capability::StorageUniform16,
+    spv::Capability::UniformAndStorageBuffer16BitAccess,
+    spv::Capability::StoragePushConstant16,
+    spv::Capability::StorageInputOutput16,
+    spv::Capability::DeviceGroup,
+    spv::Capability::MultiView,
+    spv::Capability::VariablePointersStorageBuffer,
+    spv::Capability::VariablePointers,
+    spv::Capability::AtomicStorageOps,
+    spv::Capability::SampleMaskPostDepthCoverage,
+    spv::Capability::StorageBuffer8BitAccess,
+    spv::Capability::UniformAndStorageBuffer8BitAccess,
+    spv::Capability::StoragePushConstant8,
+    spv::Capability::DenormPreserve,
+    spv::Capability::DenormFlushToZero,
+    spv::Capability::SignedZeroInfNanPreserve,
+    spv::Capability::RoundingModeRTE,
+    spv::Capability::RoundingModeRTZ,
+    spv::Capability::RayQueryProvisionalKHR,
+    spv::Capability::RayQueryKHR,
+    spv::Capability::RayTraversalPrimitiveCullingKHR,
+    spv::Capability::RayTracingKHR,
+    spv::Capability::Float16ImageAMD,
+    spv::Capability::ImageGatherBiasLodAMD,
+    spv::Capability::FragmentMaskAMD,
+    spv::Capability::StencilExportEXT,
+    spv::Capability::ImageReadWriteLodAMD,
+    spv::Capability::Int64ImageEXT,
+    spv::Capability::ShaderClockKHR,
+    spv::Capability::SampleMaskOverrideCoverageNV,
+    spv::Capability::GeometryShaderPassthroughNV,
+    spv::Capability::ShaderViewportIndexLayerEXT,
+    spv::Capability::ShaderViewportIndexLayerNV,
+    spv::Capability::ShaderViewportMaskNV,
+    spv::Capability::ShaderStereoViewNV,
+    spv::Capability::PerViewAttributesNV,
+    spv::Capability::FragmentFullyCoveredEXT,
+    spv::Capability::MeshShadingNV,
+    spv::Capability::ImageFootprintNV,
+    spv::Capability::MeshShadingEXT,
+    spv::Capability::FragmentBarycentricKHR,
+    spv::Capability::FragmentBarycentricNV,
+    spv::Capability::ComputeDerivativeGroupQuadsNV,
+    spv::Capability::FragmentDensityEXT,
+    spv::Capability::ShadingRateNV,
+    spv::Capability::GroupNonUniformPartitionedNV,
+    spv::Capability::ShaderNonUniform,
+    spv::Capability::ShaderNonUniformEXT,
+    spv::Capability::RuntimeDescriptorArray,
+    spv::Capability::RuntimeDescriptorArrayEXT,
+    spv::Capability::InputAttachmentArrayDynamicIndexing,
+    spv::Capability::InputAttachmentArrayDynamicIndexingEXT,
+    spv::Capability::UniformTexelBufferArrayDynamicIndexing,
+    spv::Capability::UniformTexelBufferArrayDynamicIndexingEXT,
+    spv::Capability::StorageTexelBufferArrayDynamicIndexing,
+    spv::Capability::StorageTexelBufferArrayDynamicIndexingEXT,
+    spv::Capability::UniformBufferArrayNonUniformIndexing,
+    spv::Capability::UniformBufferArrayNonUniformIndexingEXT,
+    spv::Capability::SampledImageArrayNonUniformIndexing,
+    spv::Capability::SampledImageArrayNonUniformIndexingEXT,
+    spv::Capability::StorageBufferArrayNonUniformIndexing,
+    spv::Capability::StorageBufferArrayNonUniformIndexingEXT,
+    spv::Capability::StorageImageArrayNonUniformIndexing,
+    spv::Capability::StorageImageArrayNonUniformIndexingEXT,
+    spv::Capability::InputAttachmentArrayNonUniformIndexing,
+    spv::Capability::InputAttachmentArrayNonUniformIndexingEXT,
+    spv::Capability::UniformTexelBufferArrayNonUniformIndexing,
+    spv::Capability::UniformTexelBufferArrayNonUniformIndexingEXT,
+    spv::Capability::StorageTexelBufferArrayNonUniformIndexing,
+    spv::Capability::StorageTexelBufferArrayNonUniformIndexingEXT,
+    spv::Capability::RayTracingNV,
+    spv::Capability::RayTracingMotionBlurNV,
+    spv::Capability::VulkanMemoryModel,
+    spv::Capability::VulkanMemoryModelKHR,
+    spv::Capability::VulkanMemoryModelDeviceScope,
+    spv::Capability::VulkanMemoryModelDeviceScopeKHR,
+    spv::Capability::PhysicalStorageBufferAddresses,
+    spv::Capability::PhysicalStorageBufferAddressesEXT,
+    spv::Capability::ComputeDerivativeGroupLinearNV,
+    spv::Capability::RayTracingProvisionalKHR,
+    spv::Capability::CooperativeMatrixNV,
+    spv::Capability::FragmentShaderSampleInterlockEXT,
+    spv::Capability::FragmentShaderShadingRateInterlockEXT,
+    spv::Capability::ShaderSMBuiltinsNV,
+    spv::Capability::FragmentShaderPixelInterlockEXT,
+    spv::Capability::DemoteToHelperInvocation,
+    spv::Capability::DemoteToHelperInvocationEXT,
+    spv::Capability::RayTracingOpacityMicromapEXT,
+    spv::Capability::ShaderInvocationReorderNV,
+    spv::Capability::BindlessTextureNV,
+    spv::Capability::SubgroupShuffleINTEL,
+    spv::Capability::SubgroupBufferBlockIOINTEL,
+    spv::Capability::SubgroupImageBlockIOINTEL,
+    spv::Capability::SubgroupImageMediaBlockIOINTEL,
+    spv::Capability::RoundToInfinityINTEL,
+    spv::Capability::FloatingPointModeINTEL,
+    spv::Capability::IntegerFunctions2INTEL,
+    spv::Capability::FunctionPointersINTEL,
+    spv::Capability::IndirectReferencesINTEL,
+    spv::Capability::AsmINTEL,
+    spv::Capability::AtomicFloat32MinMaxEXT,
+    spv::Capability::AtomicFloat64MinMaxEXT,
+    spv::Capability::AtomicFloat16MinMaxEXT,
+    spv::Capability::VectorComputeINTEL,
+    spv::Capability::VectorAnyINTEL,
+    spv::Capability::ExpectAssumeKHR,
+    spv::Capability::SubgroupAvcMotionEstimationINTEL,
+    spv::Capability::SubgroupAvcMotionEstimationIntraINTEL,
+    spv::Capability::SubgroupAvcMotionEstimationChromaINTEL,
+    spv::Capability::VariableLengthArrayINTEL,
+    spv::Capability::FunctionFloatControlINTEL,
+    spv::Capability::FPGAMemoryAttributesINTEL,
+    spv::Capability::FPFastMathModeINTEL,
+    spv::Capability::ArbitraryPrecisionIntegersINTEL,
+    spv::Capability::ArbitraryPrecisionFloatingPointINTEL,
+    spv::Capability::UnstructuredLoopControlsINTEL,
+    spv::Capability::FPGALoopControlsINTEL,
+    spv::Capability::KernelAttributesINTEL,
+    spv::Capability::FPGAKernelAttributesINTEL,
+    spv::Capability::FPGAMemoryAccessesINTEL,
+    spv::Capability::FPGAClusterAttributesINTEL,
+    spv::Capability::LoopFuseINTEL,
+    spv::Capability::FPGADSPControlINTEL,
+    spv::Capability::MemoryAccessAliasingINTEL,
+    spv::Capability::FPGAInvocationPipeliningAttributesINTEL,
+    spv::Capability::FPGABufferLocationINTEL,
+    spv::Capability::ArbitraryPrecisionFixedPointINTEL,
+    spv::Capability::USMStorageClassesINTEL,
+    spv::Capability::RuntimeAlignedAttributeINTEL,
+    spv::Capability::IOPipesINTEL,
+    spv::Capability::BlockingPipesINTEL,
+    spv::Capability::FPGARegINTEL,
+    spv::Capability::DotProductInputAll,
+    spv::Capability::DotProductInputAllKHR,
+    spv::Capability::DotProductInput4x8Bit,
+    spv::Capability::DotProductInput4x8BitKHR,
+    spv::Capability::DotProductInput4x8BitPacked,
+    spv::Capability::DotProductInput4x8BitPackedKHR,
+    spv::Capability::DotProduct,
+    spv::Capability::DotProductKHR,
+    spv::Capability::RayCullMaskKHR,
+    spv::Capability::BitInstructions,
+    spv::Capability::GroupNonUniformRotateKHR,
+    spv::Capability::AtomicFloat32AddEXT,
+    spv::Capability::AtomicFloat64AddEXT,
+    spv::Capability::LongConstantCompositeINTEL,
+    spv::Capability::OptNoneINTEL,
+    spv::Capability::AtomicFloat16AddEXT,
+    spv::Capability::DebugInfoModuleINTEL,
+    spv::Capability::SplitBarrierINTEL,
+    spv::Capability::GroupUniformArithmeticKHR,
+    spv::Capability::Max,
+};
+
+namespace {
+std::vector<TestEnum> enumerateValuesFromToWithStep(size_t start, size_t end,
+                                                    size_t step) {
+  assert(end > start && "end > start");
+  std::vector<TestEnum> orderedValues;
+  for (size_t i = start; i < end; i += step) {
+    orderedValues.push_back(static_cast<TestEnum>(i));
+  }
+  return orderedValues;
+}
+
+EnumSet<TestEnum> createSetUnorderedInsertion(
+    const std::vector<TestEnum>& values) {
+  std::vector shuffledValues(values.cbegin(), values.cend());
+  std::mt19937 rng(0);
+  std::shuffle(shuffledValues.begin(), shuffledValues.end(), rng);
+  EnumSet<TestEnum> set;
+  for (auto value : shuffledValues) {
+    set.insert(value);
+  }
+  return set;
+}
+}  // namespace
+
 TEST(EnumSet, IsEmpty1) {
-  EnumSet<uint32_t> set;
-  EXPECT_TRUE(set.IsEmpty());
-  set.Add(0);
-  EXPECT_FALSE(set.IsEmpty());
+  EnumSet<TestEnum> set;
+  EXPECT_TRUE(set.empty());
+  set.insert(TestEnum::ZERO);
+  EXPECT_FALSE(set.empty());
 }
 
 TEST(EnumSet, IsEmpty2) {
-  EnumSet<uint32_t> set;
-  EXPECT_TRUE(set.IsEmpty());
-  set.Add(150);
-  EXPECT_FALSE(set.IsEmpty());
+  EnumSet<TestEnum> set;
+  EXPECT_TRUE(set.empty());
+  set.insert(TestEnum::ONE_HUNDRED_FIFTY);
+  EXPECT_FALSE(set.empty());
 }
 
 TEST(EnumSet, IsEmpty3) {
-  EnumSet<uint32_t> set(4);
-  EXPECT_FALSE(set.IsEmpty());
+  EnumSet<TestEnum> set(TestEnum::FOUR);
+  EXPECT_FALSE(set.empty());
 }
 
 TEST(EnumSet, IsEmpty4) {
-  EnumSet<uint32_t> set(300);
-  EXPECT_FALSE(set.IsEmpty());
+  EnumSet<TestEnum> set(TestEnum::THREE_HUNDRED);
+  EXPECT_FALSE(set.empty());
 }
 
 TEST(EnumSetHasAnyOf, EmptySetEmptyQuery) {
-  const EnumSet<uint32_t> set;
-  const EnumSet<uint32_t> empty;
+  const EnumSet<TestEnum> set;
+  const EnumSet<TestEnum> empty;
   EXPECT_TRUE(set.HasAnyOf(empty));
-  EXPECT_TRUE(EnumSet<uint32_t>().HasAnyOf(EnumSet<uint32_t>()));
+  EXPECT_TRUE(EnumSet<TestEnum>().HasAnyOf(EnumSet<TestEnum>()));
 }
 
 TEST(EnumSetHasAnyOf, MaskSetEmptyQuery) {
-  EnumSet<uint32_t> set;
-  const EnumSet<uint32_t> empty;
-  set.Add(5);
-  set.Add(8);
+  EnumSet<TestEnum> set;
+  const EnumSet<TestEnum> empty;
+  set.insert(TestEnum::FIVE);
+  set.insert(TestEnum::EIGHT);
   EXPECT_TRUE(set.HasAnyOf(empty));
 }
 
 TEST(EnumSetHasAnyOf, OverflowSetEmptyQuery) {
-  EnumSet<uint32_t> set;
-  const EnumSet<uint32_t> empty;
-  set.Add(200);
-  set.Add(300);
+  EnumSet<TestEnum> set;
+  const EnumSet<TestEnum> empty;
+  set.insert(TestEnum::TWO_HUNDRED);
+  set.insert(TestEnum::THREE_HUNDRED);
   EXPECT_TRUE(set.HasAnyOf(empty));
 }
 
 TEST(EnumSetHasAnyOf, EmptyQuery) {
-  EnumSet<uint32_t> set;
-  const EnumSet<uint32_t> empty;
-  set.Add(5);
-  set.Add(8);
-  set.Add(200);
-  set.Add(300);
+  EnumSet<TestEnum> set;
+  const EnumSet<TestEnum> empty;
+  set.insert(TestEnum::FIVE);
+  set.insert(TestEnum::EIGHT);
+  set.insert(TestEnum::TWO_HUNDRED);
+  set.insert(TestEnum::THREE_HUNDRED);
   EXPECT_TRUE(set.HasAnyOf(empty));
 }
 
 TEST(EnumSetHasAnyOf, EmptyQueryAlwaysTrue) {
-  EnumSet<uint32_t> set;
-  const EnumSet<uint32_t> empty;
+  EnumSet<TestEnum> set;
+  const EnumSet<TestEnum> empty;
   EXPECT_TRUE(set.HasAnyOf(empty));
-  set.Add(5);
+  set.insert(TestEnum::FIVE);
   EXPECT_TRUE(set.HasAnyOf(empty));
 
-  EXPECT_TRUE(EnumSet<uint32_t>(100).HasAnyOf(EnumSet<uint32_t>()));
+  EXPECT_TRUE(
+      EnumSet<TestEnum>(TestEnum::ONE_HUNDRED).HasAnyOf(EnumSet<TestEnum>()));
 }
 
 TEST(EnumSetHasAnyOf, ReflexiveMask) {
-  EnumSet<uint32_t> set(3);
-  set.Add(24);
-  set.Add(30);
+  EnumSet<TestEnum> set(TestEnum::THREE);
+  set.insert(TestEnum::TWENTY_FOUR);
+  set.insert(TestEnum::THIRTY);
   EXPECT_TRUE(set.HasAnyOf(set));
 }
 
 TEST(EnumSetHasAnyOf, ReflexiveOverflow) {
-  EnumSet<uint32_t> set(200);
-  set.Add(300);
-  set.Add(400);
+  EnumSet<TestEnum> set(TestEnum::TWO_HUNDRED);
+  set.insert(TestEnum::TWO_HUNDRED);
+  set.insert(TestEnum::FOUR_HUNDRED);
   EXPECT_TRUE(set.HasAnyOf(set));
 }
 
 TEST(EnumSetHasAnyOf, Reflexive) {
-  EnumSet<uint32_t> set(3);
-  set.Add(24);
-  set.Add(300);
-  set.Add(400);
+  EnumSet<TestEnum> set(TestEnum::THREE);
+  set.insert(TestEnum::TWENTY_FOUR);
+  set.insert(TestEnum::THREE_HUNDRED);
+  set.insert(TestEnum::FOUR_HUNDRED);
   EXPECT_TRUE(set.HasAnyOf(set));
 }
 
 TEST(EnumSetHasAnyOf, EmptySetHasNone) {
-  EnumSet<uint32_t> set;
-  EnumSet<uint32_t> items;
+  EnumSet<TestEnum> set;
+  EnumSet<TestEnum> items;
   for (uint32_t i = 0; i < 200; ++i) {
-    items.Add(i);
+    TestEnum enumValue = static_cast<TestEnum>(i);
+    items.insert(enumValue);
     EXPECT_FALSE(set.HasAnyOf(items));
-    EXPECT_FALSE(set.HasAnyOf(EnumSet<uint32_t>(i)));
+    EXPECT_FALSE(set.HasAnyOf(EnumSet<TestEnum>(enumValue)));
   }
 }
 
 TEST(EnumSetHasAnyOf, MaskSetMaskQuery) {
-  EnumSet<uint32_t> set(0);
-  EnumSet<uint32_t> items(1);
+  EnumSet<TestEnum> set(TestEnum::ZERO);
+  EnumSet<TestEnum> items(TestEnum::ONE);
   EXPECT_FALSE(set.HasAnyOf(items));
-  set.Add(2);
-  items.Add(3);
+  set.insert(TestEnum::TWO);
+  items.insert(TestEnum::THREE);
   EXPECT_FALSE(set.HasAnyOf(items));
-  set.Add(3);
+  set.insert(TestEnum::THREE);
   EXPECT_TRUE(set.HasAnyOf(items));
-  set.Add(4);
+  set.insert(TestEnum::FOUR);
   EXPECT_TRUE(set.HasAnyOf(items));
 }
 
 TEST(EnumSetHasAnyOf, OverflowSetOverflowQuery) {
-  EnumSet<uint32_t> set(100);
-  EnumSet<uint32_t> items(200);
+  EnumSet<TestEnum> set(TestEnum::ONE_HUNDRED);
+  EnumSet<TestEnum> items(TestEnum::TWO_HUNDRED);
   EXPECT_FALSE(set.HasAnyOf(items));
-  set.Add(300);
-  items.Add(400);
+  set.insert(TestEnum::THREE_HUNDRED);
+  items.insert(TestEnum::FOUR_HUNDRED);
   EXPECT_FALSE(set.HasAnyOf(items));
-  set.Add(200);
+  set.insert(TestEnum::TWO_HUNDRED);
   EXPECT_TRUE(set.HasAnyOf(items));
-  set.Add(500);
+  set.insert(TestEnum::FIVE_HUNDRED);
   EXPECT_TRUE(set.HasAnyOf(items));
 }
 
 TEST(EnumSetHasAnyOf, GeneralCase) {
-  EnumSet<uint32_t> set(0);
-  EnumSet<uint32_t> items(100);
+  EnumSet<TestEnum> set(TestEnum::ZERO);
+  EnumSet<TestEnum> items(TestEnum::ONE_HUNDRED);
   EXPECT_FALSE(set.HasAnyOf(items));
-  set.Add(300);
-  items.Add(4);
+  set.insert(TestEnum::THREE_HUNDRED);
+  items.insert(TestEnum::FOUR);
   EXPECT_FALSE(set.HasAnyOf(items));
-  set.Add(5);
-  items.Add(500);
+  set.insert(TestEnum::FIVE);
+  items.insert(TestEnum::FIVE_HUNDRED);
   EXPECT_FALSE(set.HasAnyOf(items));
-  set.Add(500);
+  set.insert(TestEnum::FIVE_HUNDRED);
   EXPECT_TRUE(set.HasAnyOf(items));
-  EXPECT_FALSE(set.HasAnyOf(EnumSet<uint32_t>(20)));
-  EXPECT_FALSE(set.HasAnyOf(EnumSet<uint32_t>(600)));
-  EXPECT_TRUE(set.HasAnyOf(EnumSet<uint32_t>(5)));
-  EXPECT_TRUE(set.HasAnyOf(EnumSet<uint32_t>(300)));
-  EXPECT_TRUE(set.HasAnyOf(EnumSet<uint32_t>(0)));
+  EXPECT_FALSE(set.HasAnyOf(EnumSet<TestEnum>(TestEnum::TWENTY)));
+  EXPECT_FALSE(set.HasAnyOf(EnumSet<TestEnum>(TestEnum::SIX_HUNDRED)));
+  EXPECT_TRUE(set.HasAnyOf(EnumSet<TestEnum>(TestEnum::FIVE)));
+  EXPECT_TRUE(set.HasAnyOf(EnumSet<TestEnum>(TestEnum::THREE_HUNDRED)));
+  EXPECT_TRUE(set.HasAnyOf(EnumSet<TestEnum>(TestEnum::ZERO)));
 }
 
 TEST(EnumSet, DefaultIsEmpty) {
-  EnumSet<uint32_t> set;
+  EnumSet<TestEnum> set;
   for (uint32_t i = 0; i < 1000; ++i) {
-    EXPECT_FALSE(set.Contains(i));
+    EXPECT_FALSE(set.contains(static_cast<TestEnum>(i)));
+  }
+}
+
+TEST(EnumSet, EqualityCompareEmpty) {
+  EnumSet<TestEnum> set1;
+  EnumSet<TestEnum> set2;
+
+  EXPECT_TRUE(set1 == set2);
+  EXPECT_FALSE(set1 != set2);
+}
+
+TEST(EnumSet, EqualityCompareSame) {
+  EnumSet<TestEnum> set1;
+  EnumSet<TestEnum> set2;
+
+  set1.insert(TestEnum::ONE);
+  set1.insert(TestEnum::TWENTY);
+  set2.insert(TestEnum::TWENTY);
+  set2.insert(TestEnum::ONE);
+
+  EXPECT_TRUE(set1 == set2);
+  EXPECT_FALSE(set1 != set2);
+}
+
+TEST(EnumSet, EqualityCompareDifferent) {
+  EnumSet<TestEnum> set1;
+  EnumSet<TestEnum> set2;
+
+  set1.insert(TestEnum::ONE);
+  set1.insert(TestEnum::TWENTY);
+  set2.insert(TestEnum::FIVE);
+  set2.insert(TestEnum::ONE);
+
+  EXPECT_FALSE(set1 == set2);
+  EXPECT_TRUE(set1 != set2);
+}
+
+TEST(EnumSet, ConstructFromIterators) {
+  auto orderedValues = enumerateValuesFromToWithStep(0, 2, /* step= */ 1);
+  EnumSet<TestEnum> set1 = createSetUnorderedInsertion(orderedValues);
+
+  EnumSet<TestEnum> set2(orderedValues.cbegin(), orderedValues.cend());
+
+  EXPECT_EQ(set1, set2);
+}
+
+TEST(EnumSet, InsertUsingIteratorRange) {
+  auto orderedValues = enumerateValuesFromToWithStep(0, 2, /* step= */ 1);
+  EnumSet<TestEnum> set1 = createSetUnorderedInsertion(orderedValues);
+
+  EnumSet<TestEnum> set2;
+  set2.insert(orderedValues.cbegin(), orderedValues.cend());
+
+  EXPECT_EQ(set1, set2);
+}
+
+TEST(CapabilitySet, RangeBasedLoopOrderIsEnumOrder) {
+  auto orderedValues = enumerateValuesFromToWithStep(0, 2, /* step= */ 1);
+  auto set = createSetUnorderedInsertion(orderedValues);
+
+  size_t index = 0;
+  for (auto value : set) {
+    ASSERT_THAT(value, Eq(orderedValues[index]));
+    index++;
   }
 }
 
 TEST(CapabilitySet, ConstructSingleMemberMatrix) {
-  CapabilitySet s(SpvCapabilityMatrix);
-  EXPECT_TRUE(s.Contains(SpvCapabilityMatrix));
-  EXPECT_FALSE(s.Contains(SpvCapabilityShader));
-  EXPECT_FALSE(s.Contains(static_cast<SpvCapability>(1000)));
+  CapabilitySet s(spv::Capability::Matrix);
+  EXPECT_TRUE(s.contains(spv::Capability::Matrix));
+  EXPECT_FALSE(s.contains(spv::Capability::Shader));
+  EXPECT_FALSE(s.contains(static_cast<spv::Capability>(1000)));
 }
 
 TEST(CapabilitySet, ConstructSingleMemberMaxInMask) {
-  CapabilitySet s(static_cast<SpvCapability>(63));
-  EXPECT_FALSE(s.Contains(SpvCapabilityMatrix));
-  EXPECT_FALSE(s.Contains(SpvCapabilityShader));
-  EXPECT_TRUE(s.Contains(static_cast<SpvCapability>(63)));
-  EXPECT_FALSE(s.Contains(static_cast<SpvCapability>(64)));
-  EXPECT_FALSE(s.Contains(static_cast<SpvCapability>(1000)));
+  CapabilitySet s(static_cast<spv::Capability>(63));
+  EXPECT_FALSE(s.contains(spv::Capability::Matrix));
+  EXPECT_FALSE(s.contains(spv::Capability::Shader));
+  EXPECT_TRUE(s.contains(static_cast<spv::Capability>(63)));
+  EXPECT_FALSE(s.contains(static_cast<spv::Capability>(64)));
+  EXPECT_FALSE(s.contains(static_cast<spv::Capability>(1000)));
 }
 
 TEST(CapabilitySet, ConstructSingleMemberMinOverflow) {
   // Check the first one that forces overflow beyond the mask.
-  CapabilitySet s(static_cast<SpvCapability>(64));
-  EXPECT_FALSE(s.Contains(SpvCapabilityMatrix));
-  EXPECT_FALSE(s.Contains(SpvCapabilityShader));
-  EXPECT_FALSE(s.Contains(static_cast<SpvCapability>(63)));
-  EXPECT_TRUE(s.Contains(static_cast<SpvCapability>(64)));
-  EXPECT_FALSE(s.Contains(static_cast<SpvCapability>(1000)));
+  CapabilitySet s(static_cast<spv::Capability>(64));
+  EXPECT_FALSE(s.contains(spv::Capability::Matrix));
+  EXPECT_FALSE(s.contains(spv::Capability::Shader));
+  EXPECT_FALSE(s.contains(static_cast<spv::Capability>(63)));
+  EXPECT_TRUE(s.contains(static_cast<spv::Capability>(64)));
+  EXPECT_FALSE(s.contains(static_cast<spv::Capability>(1000)));
 }
 
 TEST(CapabilitySet, ConstructSingleMemberMaxOverflow) {
   // Check the max 32-bit signed int.
-  CapabilitySet s(static_cast<SpvCapability>(0x7fffffffu));
-  EXPECT_FALSE(s.Contains(SpvCapabilityMatrix));
-  EXPECT_FALSE(s.Contains(SpvCapabilityShader));
-  EXPECT_FALSE(s.Contains(static_cast<SpvCapability>(1000)));
-  EXPECT_TRUE(s.Contains(static_cast<SpvCapability>(0x7fffffffu)));
+  CapabilitySet s(static_cast<spv::Capability>(0x7fffffffu));
+  EXPECT_FALSE(s.contains(spv::Capability::Matrix));
+  EXPECT_FALSE(s.contains(spv::Capability::Shader));
+  EXPECT_FALSE(s.contains(static_cast<spv::Capability>(1000)));
+  EXPECT_TRUE(s.contains(static_cast<spv::Capability>(0x7fffffffu)));
 }
 
 TEST(CapabilitySet, AddEnum) {
-  CapabilitySet s(SpvCapabilityShader);
-  s.Add(SpvCapabilityKernel);
-  s.Add(static_cast<SpvCapability>(42));
-  EXPECT_FALSE(s.Contains(SpvCapabilityMatrix));
-  EXPECT_TRUE(s.Contains(SpvCapabilityShader));
-  EXPECT_TRUE(s.Contains(SpvCapabilityKernel));
-  EXPECT_TRUE(s.Contains(static_cast<SpvCapability>(42)));
+  CapabilitySet s(spv::Capability::Shader);
+  s.insert(spv::Capability::Kernel);
+  s.insert(static_cast<spv::Capability>(42));
+  EXPECT_FALSE(s.contains(spv::Capability::Matrix));
+  EXPECT_TRUE(s.contains(spv::Capability::Shader));
+  EXPECT_TRUE(s.contains(spv::Capability::Kernel));
+  EXPECT_TRUE(s.contains(static_cast<spv::Capability>(42)));
+}
+
+TEST(CapabilitySet, InsertReturnsIteratorToInserted) {
+  CapabilitySet set;
+
+  auto[it, inserted] = set.insert(spv::Capability::Kernel);
+
+  EXPECT_TRUE(inserted);
+  EXPECT_EQ(*it, spv::Capability::Kernel);
+}
+
+TEST(CapabilitySet, InsertReturnsIteratorToElementOnDoubleInsertion) {
+  CapabilitySet set;
+  EXPECT_FALSE(set.contains(spv::Capability::Shader));
+  {
+    auto[it, inserted] = set.insert(spv::Capability::Shader);
+    EXPECT_TRUE(inserted);
+    EXPECT_EQ(*it, spv::Capability::Shader);
+  }
+  EXPECT_TRUE(set.contains(spv::Capability::Shader));
+
+  auto[it, inserted] = set.insert(spv::Capability::Shader);
+
+  EXPECT_FALSE(inserted);
+  EXPECT_EQ(*it, spv::Capability::Shader);
+  EXPECT_TRUE(set.contains(spv::Capability::Shader));
+}
+
+TEST(CapabilitySet, InsertWithHintWorks) {
+  CapabilitySet set;
+  EXPECT_FALSE(set.contains(spv::Capability::Shader));
+
+  auto it = set.insert(set.begin(), spv::Capability::Shader);
+
+  EXPECT_EQ(*it, spv::Capability::Shader);
+  EXPECT_TRUE(set.contains(spv::Capability::Shader));
+}
+
+TEST(CapabilitySet, InsertWithEndHintWorks) {
+  CapabilitySet set;
+  EXPECT_FALSE(set.contains(spv::Capability::Shader));
+
+  auto it = set.insert(set.end(), spv::Capability::Shader);
+
+  EXPECT_EQ(*it, spv::Capability::Shader);
+  EXPECT_TRUE(set.contains(spv::Capability::Shader));
+}
+
+TEST(CapabilitySet, IteratorCanBeCopied) {
+  CapabilitySet set;
+  set.insert(spv::Capability::Matrix);
+  set.insert(spv::Capability::Shader);
+  set.insert(spv::Capability::Geometry);
+  set.insert(spv::Capability::Float64);
+  set.insert(spv::Capability::Float16);
+
+  auto a = set.begin();
+  ++a;
+  auto b = a;
+
+  EXPECT_EQ(*b, *a);
+  ++b;
+  EXPECT_NE(*b, *a);
+
+  ++a;
+  EXPECT_EQ(*b, *a);
+
+  ++a;
+  EXPECT_NE(*b, *a);
+}
+
+TEST(CapabilitySet, IteratorBeginToEndPostfix) {
+  auto orderedValues = enumerateValuesFromToWithStep(0, 100, /* step= */ 1);
+  auto set = createSetUnorderedInsertion(orderedValues);
+
+  size_t index = 0;
+  for (auto it = set.cbegin(); it != set.cend(); it++, index++) {
+    EXPECT_EQ(*it, orderedValues[index]);
+  }
+}
+
+TEST(CapabilitySet, IteratorBeginToEndPrefix) {
+  auto orderedValues = enumerateValuesFromToWithStep(0, 100, /* step= */ 1);
+  auto set = createSetUnorderedInsertion(orderedValues);
+
+  size_t index = 0;
+  for (auto it = set.cbegin(); it != set.cend(); ++it, index++) {
+    EXPECT_EQ(*it, orderedValues[index]);
+  }
+}
+
+TEST(CapabilitySet, IteratorBeginToEndPrefixStep) {
+  auto orderedValues = enumerateValuesFromToWithStep(0, 100, /* step= */ 8);
+  auto set = createSetUnorderedInsertion(orderedValues);
+
+  size_t index = 0;
+  for (auto it = set.cbegin(); it != set.cend(); ++it, index++) {
+    ASSERT_EQ(*it, orderedValues[index]);
+  }
+}
+
+TEST(CapabilitySet, IteratorBeginOnEmpty) {
+  CapabilitySet set;
+
+  auto begin = set.begin();
+  auto end = set.end();
+  ASSERT_EQ(begin, end);
+}
+
+TEST(CapabilitySet, IteratorBeginOnSingleNonZeroValue) {
+  CapabilitySet set;
+  set.insert(spv::Capability::Shader);
+
+  auto begin = set.begin();
+  auto end = set.end();
+
+  ASSERT_NE(begin, end);
+  ASSERT_EQ(*begin, spv::Capability::Shader);
+}
+
+TEST(CapabilitySet, IteratorForLoopNonZeroValue) {
+  CapabilitySet set;
+  set.insert(spv::Capability::Shader);
+  set.insert(spv::Capability::Tessellation);
+
+  auto begin = set.begin();
+  auto end = set.end();
+
+  ASSERT_NE(begin, end);
+  ASSERT_EQ(*begin, spv::Capability::Shader);
+
+  begin++;
+  ASSERT_NE(begin, end);
+  ASSERT_EQ(*begin, spv::Capability::Tessellation);
+
+  begin++;
+  ASSERT_EQ(begin, end);
+}
+
+TEST(CapabilitySet, IteratorPastEnd) {
+  CapabilitySet set;
+  set.insert(spv::Capability::Shader);
+
+  auto begin = set.begin();
+  auto end = set.end();
+
+  ASSERT_NE(begin, end);
+  ASSERT_EQ(*begin, spv::Capability::Shader);
+
+  begin++;
+  ASSERT_EQ(begin, end);
+
+  begin++;
+  ASSERT_EQ(begin, end);
+}
+
+TEST(CapabilitySet, CompatibleWithSTLFind) {
+  CapabilitySet set;
+  set.insert(spv::Capability::Matrix);
+  set.insert(spv::Capability::Shader);
+  set.insert(spv::Capability::Geometry);
+  set.insert(spv::Capability::Tessellation);
+  set.insert(spv::Capability::Addresses);
+  set.insert(spv::Capability::Linkage);
+  set.insert(spv::Capability::Kernel);
+  set.insert(spv::Capability::Vector16);
+  set.insert(spv::Capability::Float16Buffer);
+  set.insert(spv::Capability::Float64);
+
+  {
+    auto it = std::find(set.cbegin(), set.cend(), spv::Capability::Vector16);
+    ASSERT_NE(it, set.end());
+    ASSERT_EQ(*it, spv::Capability::Vector16);
+  }
+
+  {
+    auto it = std::find(set.cbegin(), set.cend(), spv::Capability::Float16);
+    ASSERT_EQ(it, set.end());
+  }
+}
+
+TEST(CapabilitySet, CompatibleWithSTLForEach) {
+  auto orderedValues = enumerateValuesFromToWithStep(0, 100, /* step= */ 15);
+  auto set = createSetUnorderedInsertion(orderedValues);
+
+  size_t index = 0;
+  std::for_each(set.cbegin(), set.cend(), [&](auto item) {
+    ASSERT_EQ(item, orderedValues[index]);
+    index++;
+  });
 }
 
 TEST(CapabilitySet, InitializerListEmpty) {
   CapabilitySet s{};
   for (uint32_t i = 0; i < 1000; i++) {
-    EXPECT_FALSE(s.Contains(static_cast<SpvCapability>(i)));
+    EXPECT_FALSE(s.contains(static_cast<spv::Capability>(i)));
+  }
+}
+
+TEST(CapabilitySet, LargeSetHasInsertedElements) {
+  CapabilitySet set;
+  for (auto c : kCapabilities) {
+    EXPECT_FALSE(set.contains(c));
+  }
+
+  for (auto c : kCapabilities) {
+    set.insert(c);
+    EXPECT_TRUE(set.contains(c));
+  }
+
+  for (auto c : kCapabilities) {
+    EXPECT_TRUE(set.contains(c));
+  }
+}
+
+TEST(CapabilitySet, LargeSetHasUnsortedInsertedElements) {
+  std::vector shuffledCapabilities(kCapabilities.cbegin(),
+                                   kCapabilities.cend());
+  std::mt19937 rng(0);
+  std::shuffle(shuffledCapabilities.begin(), shuffledCapabilities.end(), rng);
+  CapabilitySet set;
+  for (auto c : shuffledCapabilities) {
+    EXPECT_FALSE(set.contains(c));
+  }
+
+  for (auto c : shuffledCapabilities) {
+    set.insert(c);
+    EXPECT_TRUE(set.contains(c));
+  }
+
+  for (auto c : shuffledCapabilities) {
+    EXPECT_TRUE(set.contains(c));
+  }
+}
+
+TEST(CapabilitySet, LargeSetHasUnsortedRemovedElement) {
+  std::vector shuffledCapabilities(kCapabilities.cbegin(),
+                                   kCapabilities.cend());
+  std::mt19937 rng(0);
+  std::shuffle(shuffledCapabilities.begin(), shuffledCapabilities.end(), rng);
+  CapabilitySet set;
+  for (auto c : shuffledCapabilities) {
+    set.insert(c);
+    EXPECT_TRUE(set.contains(c));
+  }
+
+  for (auto c : kCapabilities) {
+    set.erase(c);
+  }
+
+  for (auto c : shuffledCapabilities) {
+    EXPECT_FALSE(set.contains(c));
   }
 }
 
 struct ForEachCase {
   CapabilitySet capabilities;
-  std::vector<SpvCapability> expected;
+  std::vector<spv::Capability> expected;
 };
 
 using CapabilitySetForEachTest = ::testing::TestWithParam<ForEachCase>;
@@ -253,7 +848,7 @@
   EXPECT_THAT(ElementsIn(moved), Eq(GetParam().expected));
 
   // The moved-from set is empty.
-  EXPECT_THAT(ElementsIn(copy), Eq(std::vector<SpvCapability>{}));
+  EXPECT_THAT(ElementsIn(copy), Eq(std::vector<spv::Capability>{}));
 }
 
 TEST_P(CapabilitySetForEachTest, OperatorEquals) {
@@ -267,24 +862,40 @@
   EXPECT_THAT(ElementsIn(assigned), Eq(GetParam().expected));
 }
 
-INSTANTIATE_TEST_SUITE_P(Samples, CapabilitySetForEachTest,
-                         ValuesIn(std::vector<ForEachCase>{
-                             {{}, {}},
-                             {{SpvCapabilityMatrix}, {SpvCapabilityMatrix}},
-                             {{SpvCapabilityKernel, SpvCapabilityShader},
-                              {SpvCapabilityShader, SpvCapabilityKernel}},
-                             {{static_cast<SpvCapability>(999)},
-                              {static_cast<SpvCapability>(999)}},
-                             {{static_cast<SpvCapability>(0x7fffffff)},
-                              {static_cast<SpvCapability>(0x7fffffff)}},
-                             // Mixture and out of order
-                             {{static_cast<SpvCapability>(0x7fffffff),
-                               static_cast<SpvCapability>(100),
-                               SpvCapabilityShader, SpvCapabilityMatrix},
-                              {SpvCapabilityMatrix, SpvCapabilityShader,
-                               static_cast<SpvCapability>(100),
-                               static_cast<SpvCapability>(0x7fffffff)}},
-                         }));
+INSTANTIATE_TEST_SUITE_P(
+    Samples, CapabilitySetForEachTest,
+    ValuesIn(std::vector<ForEachCase>{
+        {{}, {}},
+        {{spv::Capability::Matrix}, {spv::Capability::Matrix}},
+        {{spv::Capability::Kernel, spv::Capability::Shader},
+         {spv::Capability::Shader, spv::Capability::Kernel}},
+        {{static_cast<spv::Capability>(999)},
+         {static_cast<spv::Capability>(999)}},
+        {{static_cast<spv::Capability>(0x7fffffff)},
+         {static_cast<spv::Capability>(0x7fffffff)}},
+        // Mixture and out of order
+        {{static_cast<spv::Capability>(0x7fffffff),
+          static_cast<spv::Capability>(100), spv::Capability::Shader,
+          spv::Capability::Matrix},
+         {spv::Capability::Matrix, spv::Capability::Shader,
+          static_cast<spv::Capability>(100),
+          static_cast<spv::Capability>(0x7fffffff)}},
+    }));
+
+using BoundaryTestWithParam = ::testing::TestWithParam<spv::Capability>;
+
+TEST_P(BoundaryTestWithParam, InsertedContains) {
+  CapabilitySet set;
+  set.insert(GetParam());
+  EXPECT_TRUE(set.contains(GetParam()));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    Samples, BoundaryTestWithParam,
+    Values(static_cast<spv::Capability>(0), static_cast<spv::Capability>(63),
+           static_cast<spv::Capability>(64), static_cast<spv::Capability>(65),
+           static_cast<spv::Capability>(127), static_cast<spv::Capability>(128),
+           static_cast<spv::Capability>(129)));
 
 }  // namespace
 }  // namespace spvtools
diff --git a/test/enum_string_mapping_test.cpp b/test/enum_string_mapping_test.cpp
index 52aa653..01dede7 100644
--- a/test/enum_string_mapping_test.cpp
+++ b/test/enum_string_mapping_test.cpp
@@ -34,7 +34,7 @@
     ::testing::TestWithParam<std::pair<Extension, std::string>>;
 using UnknownExtensionTest = ::testing::TestWithParam<std::string>;
 using CapabilityTest =
-    ::testing::TestWithParam<std::pair<SpvCapability, std::string>>;
+    ::testing::TestWithParam<std::pair<spv::Capability, std::string>>;
 
 TEST_P(ExtensionTest, TestExtensionFromString) {
   const std::pair<Extension, std::string>& param = GetParam();
@@ -59,8 +59,8 @@
 }
 
 TEST_P(CapabilityTest, TestCapabilityToString) {
-  const std::pair<SpvCapability, std::string>& param = GetParam();
-  const SpvCapability capability = param.first;
+  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);
@@ -99,104 +99,105 @@
 
 INSTANTIATE_TEST_SUITE_P(
     AllCapabilities, CapabilityTest,
-    ValuesIn(std::vector<std::pair<SpvCapability, std::string>>(
-        {{SpvCapabilityMatrix, "Matrix"},
-         {SpvCapabilityShader, "Shader"},
-         {SpvCapabilityGeometry, "Geometry"},
-         {SpvCapabilityTessellation, "Tessellation"},
-         {SpvCapabilityAddresses, "Addresses"},
-         {SpvCapabilityLinkage, "Linkage"},
-         {SpvCapabilityKernel, "Kernel"},
-         {SpvCapabilityVector16, "Vector16"},
-         {SpvCapabilityFloat16Buffer, "Float16Buffer"},
-         {SpvCapabilityFloat16, "Float16"},
-         {SpvCapabilityFloat64, "Float64"},
-         {SpvCapabilityInt64, "Int64"},
-         {SpvCapabilityInt64Atomics, "Int64Atomics"},
-         {SpvCapabilityImageBasic, "ImageBasic"},
-         {SpvCapabilityImageReadWrite, "ImageReadWrite"},
-         {SpvCapabilityImageMipmap, "ImageMipmap"},
-         {SpvCapabilityPipes, "Pipes"},
-         {SpvCapabilityGroups, "Groups"},
-         {SpvCapabilityDeviceEnqueue, "DeviceEnqueue"},
-         {SpvCapabilityLiteralSampler, "LiteralSampler"},
-         {SpvCapabilityAtomicStorage, "AtomicStorage"},
-         {SpvCapabilityInt16, "Int16"},
-         {SpvCapabilityTessellationPointSize, "TessellationPointSize"},
-         {SpvCapabilityGeometryPointSize, "GeometryPointSize"},
-         {SpvCapabilityImageGatherExtended, "ImageGatherExtended"},
-         {SpvCapabilityStorageImageMultisample, "StorageImageMultisample"},
-         {SpvCapabilityUniformBufferArrayDynamicIndexing,
+    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"},
-         {SpvCapabilitySampledImageArrayDynamicIndexing,
+         {spv::Capability::SampledImageArrayDynamicIndexing,
           "SampledImageArrayDynamicIndexing"},
-         {SpvCapabilityStorageBufferArrayDynamicIndexing,
+         {spv::Capability::StorageBufferArrayDynamicIndexing,
           "StorageBufferArrayDynamicIndexing"},
-         {SpvCapabilityStorageImageArrayDynamicIndexing,
+         {spv::Capability::StorageImageArrayDynamicIndexing,
           "StorageImageArrayDynamicIndexing"},
-         {SpvCapabilityClipDistance, "ClipDistance"},
-         {SpvCapabilityCullDistance, "CullDistance"},
-         {SpvCapabilityImageCubeArray, "ImageCubeArray"},
-         {SpvCapabilitySampleRateShading, "SampleRateShading"},
-         {SpvCapabilityImageRect, "ImageRect"},
-         {SpvCapabilitySampledRect, "SampledRect"},
-         {SpvCapabilityGenericPointer, "GenericPointer"},
-         {SpvCapabilityInt8, "Int8"},
-         {SpvCapabilityInputAttachment, "InputAttachment"},
-         {SpvCapabilitySparseResidency, "SparseResidency"},
-         {SpvCapabilityMinLod, "MinLod"},
-         {SpvCapabilitySampled1D, "Sampled1D"},
-         {SpvCapabilityImage1D, "Image1D"},
-         {SpvCapabilitySampledCubeArray, "SampledCubeArray"},
-         {SpvCapabilitySampledBuffer, "SampledBuffer"},
-         {SpvCapabilityImageBuffer, "ImageBuffer"},
-         {SpvCapabilityImageMSArray, "ImageMSArray"},
-         {SpvCapabilityStorageImageExtendedFormats,
+         {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"},
-         {SpvCapabilityImageQuery, "ImageQuery"},
-         {SpvCapabilityDerivativeControl, "DerivativeControl"},
-         {SpvCapabilityInterpolationFunction, "InterpolationFunction"},
-         {SpvCapabilityTransformFeedback, "TransformFeedback"},
-         {SpvCapabilityGeometryStreams, "GeometryStreams"},
-         {SpvCapabilityStorageImageReadWithoutFormat,
+         {spv::Capability::ImageQuery, "ImageQuery"},
+         {spv::Capability::DerivativeControl, "DerivativeControl"},
+         {spv::Capability::InterpolationFunction, "InterpolationFunction"},
+         {spv::Capability::TransformFeedback, "TransformFeedback"},
+         {spv::Capability::GeometryStreams, "GeometryStreams"},
+         {spv::Capability::StorageImageReadWithoutFormat,
           "StorageImageReadWithoutFormat"},
-         {SpvCapabilityStorageImageWriteWithoutFormat,
+         {spv::Capability::StorageImageWriteWithoutFormat,
           "StorageImageWriteWithoutFormat"},
-         {SpvCapabilityMultiViewport, "MultiViewport"},
-         {SpvCapabilitySubgroupDispatch, "SubgroupDispatch"},
-         {SpvCapabilityNamedBarrier, "NamedBarrier"},
-         {SpvCapabilityPipeStorage, "PipeStorage"},
-         {SpvCapabilitySubgroupBallotKHR, "SubgroupBallotKHR"},
-         {SpvCapabilityDrawParameters, "DrawParameters"},
-         {SpvCapabilitySubgroupVoteKHR, "SubgroupVoteKHR"},
-         {SpvCapabilityStorageBuffer16BitAccess, "StorageBuffer16BitAccess"},
-         {SpvCapabilityStorageUniformBufferBlock16,
+         {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
-         {SpvCapabilityUniformAndStorageBuffer16BitAccess,
+         {spv::Capability::UniformAndStorageBuffer16BitAccess,
           "UniformAndStorageBuffer16BitAccess"},
-         {SpvCapabilityStorageUniform16,
+         {spv::Capability::StorageUniform16,
           "UniformAndStorageBuffer16BitAccess"},  // Preferred name
-         {SpvCapabilityStoragePushConstant16, "StoragePushConstant16"},
-         {SpvCapabilityStorageInputOutput16, "StorageInputOutput16"},
-         {SpvCapabilityDeviceGroup, "DeviceGroup"},
-         {SpvCapabilityAtomicFloat32AddEXT, "AtomicFloat32AddEXT"},
-         {SpvCapabilityAtomicFloat64AddEXT, "AtomicFloat64AddEXT"},
-         {SpvCapabilityAtomicFloat32MinMaxEXT, "AtomicFloat32MinMaxEXT"},
-         {SpvCapabilityAtomicFloat64MinMaxEXT, "AtomicFloat64MinMaxEXT"},
-         {SpvCapabilityMultiView, "MultiView"},
-         {SpvCapabilityInt64ImageEXT, "Int64ImageEXT"},
-         {SpvCapabilitySampleMaskOverrideCoverageNV,
+         {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"},
-         {SpvCapabilityGeometryShaderPassthroughNV,
+         {spv::Capability::GeometryShaderPassthroughNV,
           "GeometryShaderPassthroughNV"},
          // The next two are different names for the same token.
-         {SpvCapabilityShaderViewportIndexLayerNV,
+         {spv::Capability::ShaderViewportIndexLayerNV,
           "ShaderViewportIndexLayerEXT"},
-         {SpvCapabilityShaderViewportIndexLayerEXT,
+         {spv::Capability::ShaderViewportIndexLayerEXT,
           "ShaderViewportIndexLayerEXT"},
-         {SpvCapabilityShaderViewportMaskNV, "ShaderViewportMaskNV"},
-         {SpvCapabilityShaderStereoViewNV, "ShaderStereoViewNV"},
-         {SpvCapabilityPerViewAttributesNV, "PerViewAttributesNV"}})));
+         {spv::Capability::ShaderViewportMaskNV, "ShaderViewportMaskNV"},
+         {spv::Capability::ShaderStereoViewNV, "ShaderStereoViewNV"},
+         {spv::Capability::PerViewAttributesNV, "PerViewAttributesNV"}})));
 
 }  // namespace
 }  // namespace spvtools
diff --git a/test/ext_inst.cldebug100_test.cpp b/test/ext_inst.cldebug100_test.cpp
index 0bbdd3a..cfc68e8 100644
--- a/test/ext_inst.cldebug100_test.cpp
+++ b/test/ext_inst.cldebug100_test.cpp
@@ -18,7 +18,6 @@
 #include "OpenCLDebugInfo100.h"
 #include "gmock/gmock.h"
 #include "source/util/string_utils.h"
-#include "spirv/unified1/spirv.h"
 #include "test/test_fixture.h"
 #include "test/unit_spirv.h"
 
@@ -158,12 +157,13 @@
       GetParam().name + GetParam().operands + "\n";
   // First make sure it assembles correctly.
   std::cout << input << std::endl;
-  EXPECT_THAT(CompiledInstructions(input),
-              Eq(Concatenate(
-                  {MakeInstruction(SpvOpExtInstImport, {1},
-                                   MakeVector("OpenCL.DebugInfo.100")),
-                   MakeInstruction(SpvOpExtInst, {2, 3, 1, GetParam().opcode},
-                                   GetParam().expected_operands)})))
+  EXPECT_THAT(
+      CompiledInstructions(input),
+      Eq(Concatenate(
+          {MakeInstruction(spv::Op::OpExtInstImport, {1},
+                           MakeVector("OpenCL.DebugInfo.100")),
+           MakeInstruction(spv::Op::OpExtInst, {2, 3, 1, GetParam().opcode},
+                           GetParam().expected_operands)})))
       << input;
   // Now check the round trip through the disassembler.
   EXPECT_THAT(EncodeAndDecodeSuccessfully(input), input) << input;
@@ -297,7 +297,7 @@
   {                                                         \
     uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
         " %4 " #S0 " " Fstr, {                              \
-      4, uint32_t(SpvStorageClass##S0), Fnum                \
+      4, uint32_t(spv::StorageClass::S0), Fnum              \
     }                                                       \
   }
 
@@ -313,7 +313,7 @@
   {                                                         \
     uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
         " " #L0 " " #L1 " %4 " RawEnumName, {               \
-      L0, L1, 4, RawEnumValue                               \
+      L0, L1, 4, (uint32_t)RawEnumValue                     \
     }                                                       \
   }
 
@@ -574,7 +574,7 @@
 INSTANTIATE_TEST_SUITE_P(
     OpenCLDebugInfo100DebugCompilationUnit, ExtInstCLDebugInfo100RoundTripTest,
     ::testing::ValuesIn(std::vector<InstructionCase>({
-        CASE_LLIe(CompilationUnit, 100, 42, "HLSL", SpvSourceLanguageHLSL),
+        CASE_LLIe(CompilationUnit, 100, 42, "HLSL", spv::SourceLanguage::HLSL),
     })));
 
 INSTANTIATE_TEST_SUITE_P(
@@ -612,14 +612,14 @@
   const std::string input = prefix + "FlagIsPublic\n";
   const std::string expected = prefix + "FlagIsProtected|FlagIsPrivate\n";
   // First make sure it assembles correctly.
-  EXPECT_THAT(
-      CompiledInstructions(input),
-      Eq(Concatenate(
-          {MakeInstruction(SpvOpExtInstImport, {1}, MakeVector("DebugInfo")),
-           MakeInstruction(SpvOpExtInst,
-                           {2, 3, 1, OpenCLDebugInfo100DebugTypePointer, 4,
-                            uint32_t(SpvStorageClassPrivate),
-                            OpenCLDebugInfo100FlagIsPublic})})))
+  EXPECT_THAT(CompiledInstructions(input),
+              Eq(Concatenate(
+                  {MakeInstruction(spv::Op::OpExtInstImport, {1},
+                                   MakeVector("DebugInfo")),
+                   MakeInstruction(spv::Op::OpExtInst,
+                                   {2, 3, 1, OpenCLDebugInfo100DebugTypePointer,
+                                    4, uint32_t(spv::StorageClass::Private),
+                                    OpenCLDebugInfo100FlagIsPublic})})))
       << input;
   // Now check the round trip through the disassembler.
   EXPECT_THAT(EncodeAndDecodeSuccessfully(input), Eq(expected)) << input;
diff --git a/test/ext_inst.debuginfo_test.cpp b/test/ext_inst.debuginfo_test.cpp
index 9090c24..5ccbde6 100644
--- a/test/ext_inst.debuginfo_test.cpp
+++ b/test/ext_inst.debuginfo_test.cpp
@@ -52,12 +52,12 @@
       "%3 = OpExtInst %2 %1 " +
       GetParam().name + GetParam().operands + "\n";
   // First make sure it assembles correctly.
-  EXPECT_THAT(
-      CompiledInstructions(input),
-      Eq(Concatenate(
-          {MakeInstruction(SpvOpExtInstImport, {1}, MakeVector("DebugInfo")),
-           MakeInstruction(SpvOpExtInst, {2, 3, 1, GetParam().opcode},
-                           GetParam().expected_operands)})))
+  EXPECT_THAT(CompiledInstructions(input),
+              Eq(Concatenate({MakeInstruction(spv::Op::OpExtInstImport, {1},
+                                              MakeVector("DebugInfo")),
+                              MakeInstruction(spv::Op::OpExtInst,
+                                              {2, 3, 1, GetParam().opcode},
+                                              GetParam().expected_operands)})))
       << input;
   // Now check the round trip through the disassembler.
   EXPECT_THAT(EncodeAndDecodeSuccessfully(input), input) << input;
@@ -156,7 +156,7 @@
 #define CASE_ISF(Enum, S0, Fstr, Fnum)                                    \
   {                                                                       \
     uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " %4 " #S0 " " Fstr, { \
-      4, uint32_t(SpvStorageClass##S0), Fnum                              \
+      4, uint32_t(spv::StorageClass::S0), Fnum                            \
     }                                                                     \
   }
 
@@ -408,11 +408,12 @@
   // First make sure it assembles correctly.
   EXPECT_THAT(
       CompiledInstructions(input),
-      Eq(Concatenate(
-          {MakeInstruction(SpvOpExtInstImport, {1}, MakeVector("DebugInfo")),
-           MakeInstruction(SpvOpExtInst, {2, 3, 1, DebugInfoDebugTypePointer, 4,
-                                          uint32_t(SpvStorageClassPrivate),
-                                          DebugInfoFlagIsPublic})})))
+      Eq(Concatenate({MakeInstruction(spv::Op::OpExtInstImport, {1},
+                                      MakeVector("DebugInfo")),
+                      MakeInstruction(spv::Op::OpExtInst,
+                                      {2, 3, 1, DebugInfoDebugTypePointer, 4,
+                                       uint32_t(spv::StorageClass::Private),
+                                       DebugInfoFlagIsPublic})})))
       << input;
   // Now check the round trip through the disassembler.
   EXPECT_THAT(EncodeAndDecodeSuccessfully(input), Eq(expected)) << input;
diff --git a/test/ext_inst.opencl_test.cpp b/test/ext_inst.opencl_test.cpp
index d80a9bd..51c2b01 100644
--- a/test/ext_inst.opencl_test.cpp
+++ b/test/ext_inst.opencl_test.cpp
@@ -47,12 +47,12 @@
       "%3 = OpExtInst %2 %1 " +
       GetParam().name + " " + GetParam().operands + "\n";
   // First make sure it assembles correctly.
-  EXPECT_THAT(
-      CompiledInstructions(input),
-      Eq(Concatenate(
-          {MakeInstruction(SpvOpExtInstImport, {1}, MakeVector("OpenCL.std")),
-           MakeInstruction(SpvOpExtInst, {2, 3, 1, GetParam().opcode},
-                           GetParam().expected_operands)})))
+  EXPECT_THAT(CompiledInstructions(input),
+              Eq(Concatenate({MakeInstruction(spv::Op::OpExtInstImport, {1},
+                                              MakeVector("OpenCL.std")),
+                              MakeInstruction(spv::Op::OpExtInst,
+                                              {2, 3, 1, GetParam().opcode},
+                                              GetParam().expected_operands)})))
       << input;
   // Now check the round trip through the disassembler.
   EXPECT_THAT(EncodeAndDecodeSuccessfully(input), input) << input;
@@ -85,7 +85,7 @@
 #define CASE3Round(Enum, Name, Mode)                                    \
   {                                                                     \
     uint32_t(OpenCLLIB::Entrypoints::Enum), #Name, "%4 %5 %6 " #Mode, { \
-      4, 5, 6, uint32_t(SpvFPRoundingMode##Mode)                        \
+      4, 5, 6, uint32_t(spv::FPRoundingMode::Mode)                      \
     }                                                                   \
   }
 
diff --git a/test/fuzz/available_instructions_test.cpp b/test/fuzz/available_instructions_test.cpp
index dc8a3b5..bbe524a 100644
--- a/test/fuzz/available_instructions_test.cpp
+++ b/test/fuzz/available_instructions_test.cpp
@@ -176,41 +176,41 @@
       auto available = all_instructions.GetAvailableBeforeInstruction(i1);
       ASSERT_FALSE(available.empty());
       ASSERT_EQ(30, available.size());
-      ASSERT_EQ(SpvOpTypeVoid, available[0]->opcode());
-      ASSERT_EQ(SpvOpVariable, available[15]->opcode());
+      ASSERT_EQ(spv::Op::OpTypeVoid, available[0]->opcode());
+      ASSERT_EQ(spv::Op::OpVariable, available[15]->opcode());
     }
     {
       auto available = all_instructions.GetAvailableBeforeInstruction(i2);
       ASSERT_FALSE(available.empty());
       ASSERT_EQ(46, available.size());
-      ASSERT_EQ(SpvOpTypeVoid, available[0]->opcode());
-      ASSERT_EQ(SpvOpTypePointer, available[3]->opcode());
-      ASSERT_EQ(SpvOpVariable, available[15]->opcode());
-      ASSERT_EQ(SpvOpFunctionCall, available[40]->opcode());
-      ASSERT_EQ(SpvOpStore, available[45]->opcode());
+      ASSERT_EQ(spv::Op::OpTypeVoid, available[0]->opcode());
+      ASSERT_EQ(spv::Op::OpTypePointer, available[3]->opcode());
+      ASSERT_EQ(spv::Op::OpVariable, available[15]->opcode());
+      ASSERT_EQ(spv::Op::OpFunctionCall, available[40]->opcode());
+      ASSERT_EQ(spv::Op::OpStore, available[45]->opcode());
     }
     {
       auto available = all_instructions.GetAvailableBeforeInstruction(i3);
       ASSERT_FALSE(available.empty());
       ASSERT_EQ(45, available.size());
-      ASSERT_EQ(SpvOpTypeVoid, available[0]->opcode());
-      ASSERT_EQ(SpvOpTypePointer, available[3]->opcode());
-      ASSERT_EQ(SpvOpVariable, available[15]->opcode());
-      ASSERT_EQ(SpvOpFunctionCall, available[40]->opcode());
-      ASSERT_EQ(SpvOpBranchConditional, available[44]->opcode());
+      ASSERT_EQ(spv::Op::OpTypeVoid, available[0]->opcode());
+      ASSERT_EQ(spv::Op::OpTypePointer, available[3]->opcode());
+      ASSERT_EQ(spv::Op::OpVariable, available[15]->opcode());
+      ASSERT_EQ(spv::Op::OpFunctionCall, available[40]->opcode());
+      ASSERT_EQ(spv::Op::OpBranchConditional, available[44]->opcode());
     }
     {
       auto available = all_instructions.GetAvailableBeforeInstruction(i6);
       ASSERT_FALSE(available.empty());
       ASSERT_EQ(33, available.size());
-      ASSERT_EQ(SpvOpTypeVoid, available[0]->opcode());
-      ASSERT_EQ(SpvOpTypeFloat, available[4]->opcode());
-      ASSERT_EQ(SpvOpTypePointer, available[8]->opcode());
-      ASSERT_EQ(SpvOpConstantComposite, available[12]->opcode());
-      ASSERT_EQ(SpvOpConstant, available[16]->opcode());
-      ASSERT_EQ(SpvOpFunctionParameter, available[30]->opcode());
-      ASSERT_EQ(SpvOpFunctionParameter, available[31]->opcode());
-      ASSERT_EQ(SpvOpVariable, available[32]->opcode());
+      ASSERT_EQ(spv::Op::OpTypeVoid, available[0]->opcode());
+      ASSERT_EQ(spv::Op::OpTypeFloat, available[4]->opcode());
+      ASSERT_EQ(spv::Op::OpTypePointer, available[8]->opcode());
+      ASSERT_EQ(spv::Op::OpConstantComposite, available[12]->opcode());
+      ASSERT_EQ(spv::Op::OpConstant, available[16]->opcode());
+      ASSERT_EQ(spv::Op::OpFunctionParameter, available[30]->opcode());
+      ASSERT_EQ(spv::Op::OpFunctionParameter, available[31]->opcode());
+      ASSERT_EQ(spv::Op::OpVariable, available[32]->opcode());
     }
   }
   {
@@ -225,51 +225,51 @@
       auto available = vector_instructions.GetAvailableBeforeInstruction(i4);
       ASSERT_FALSE(available.empty());
       ASSERT_EQ(3, available.size());
-      ASSERT_EQ(SpvOpConstantComposite, available[0]->opcode());
-      ASSERT_EQ(SpvOpConstantComposite, available[1]->opcode());
-      ASSERT_EQ(SpvOpCopyObject, available[2]->opcode());
+      ASSERT_EQ(spv::Op::OpConstantComposite, available[0]->opcode());
+      ASSERT_EQ(spv::Op::OpConstantComposite, available[1]->opcode());
+      ASSERT_EQ(spv::Op::OpCopyObject, available[2]->opcode());
     }
     {
       auto available = vector_instructions.GetAvailableBeforeInstruction(i5);
       ASSERT_FALSE(available.empty());
       ASSERT_EQ(3, available.size());
-      ASSERT_EQ(SpvOpConstantComposite, available[0]->opcode());
-      ASSERT_EQ(SpvOpConstantComposite, available[1]->opcode());
-      ASSERT_EQ(SpvOpCopyObject, available[2]->opcode());
+      ASSERT_EQ(spv::Op::OpConstantComposite, available[0]->opcode());
+      ASSERT_EQ(spv::Op::OpConstantComposite, available[1]->opcode());
+      ASSERT_EQ(spv::Op::OpCopyObject, available[2]->opcode());
     }
     {
       auto available = vector_instructions.GetAvailableBeforeInstruction(i6);
       ASSERT_FALSE(available.empty());
       ASSERT_EQ(2, available.size());
-      ASSERT_EQ(SpvOpConstantComposite, available[0]->opcode());
-      ASSERT_EQ(SpvOpConstantComposite, available[1]->opcode());
+      ASSERT_EQ(spv::Op::OpConstantComposite, available[0]->opcode());
+      ASSERT_EQ(spv::Op::OpConstantComposite, available[1]->opcode());
     }
   }
   {
     AvailableInstructions integer_add_instructions(
         context.get(), [](opt::IRContext*, opt::Instruction* inst) -> bool {
-          return inst->opcode() == SpvOpIAdd;
+          return inst->opcode() == spv::Op::OpIAdd;
         });
     {
       auto available =
           integer_add_instructions.GetAvailableBeforeInstruction(i7);
       ASSERT_FALSE(available.empty());
       ASSERT_EQ(1, available.size());
-      ASSERT_EQ(SpvOpIAdd, available[0]->opcode());
+      ASSERT_EQ(spv::Op::OpIAdd, available[0]->opcode());
     }
     {
       auto available =
           integer_add_instructions.GetAvailableBeforeInstruction(i8);
       ASSERT_FALSE(available.empty());
       ASSERT_EQ(1, available.size());
-      ASSERT_EQ(SpvOpIAdd, available[0]->opcode());
+      ASSERT_EQ(spv::Op::OpIAdd, available[0]->opcode());
     }
     {
       auto available =
           integer_add_instructions.GetAvailableBeforeInstruction(i9);
       ASSERT_FALSE(available.empty());
       ASSERT_EQ(1, available.size());
-      ASSERT_EQ(SpvOpIAdd, available[0]->opcode());
+      ASSERT_EQ(spv::Op::OpIAdd, available[0]->opcode());
     }
   }
 }
diff --git a/test/fuzz/data_synonym_transformation_test.cpp b/test/fuzz/data_synonym_transformation_test.cpp
index 7e93f29..3ba8093 100644
--- a/test/fuzz/data_synonym_transformation_test.cpp
+++ b/test/fuzz/data_synonym_transformation_test.cpp
@@ -146,12 +146,12 @@
 
   // Replace %12 with %100[0] in '%25 = OpAccessChain %24 %20 %12'
   auto instruction_descriptor_1 =
-      MakeInstructionDescriptor(25, SpvOpAccessChain, 0);
+      MakeInstructionDescriptor(25, spv::Op::OpAccessChain, 0);
   auto good_extract_1 =
       TransformationCompositeExtract(instruction_descriptor_1, 102, 100, {0});
   // Bad: id already in use
   auto bad_extract_1 = TransformationCompositeExtract(
-      MakeInstructionDescriptor(25, SpvOpAccessChain, 0), 25, 100, {0});
+      MakeInstructionDescriptor(25, spv::Op::OpAccessChain, 0), 25, 100, {0});
   ASSERT_TRUE(
       good_extract_1.IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
@@ -166,7 +166,8 @@
                                                kConsoleMessageConsumer));
 
   // Replace %13 with %100[1] in 'OpStore %15 %13'
-  auto instruction_descriptor_2 = MakeInstructionDescriptor(100, SpvOpStore, 0);
+  auto instruction_descriptor_2 =
+      MakeInstructionDescriptor(100, spv::Op::OpStore, 0);
   auto good_extract_2 =
       TransformationCompositeExtract(instruction_descriptor_2, 103, 100, {1});
   // No bad example provided here.
@@ -183,7 +184,7 @@
 
   // Replace %22 with %100[2] in '%23 = OpConvertSToF %16 %22'
   auto instruction_descriptor_3 =
-      MakeInstructionDescriptor(23, SpvOpConvertSToF, 0);
+      MakeInstructionDescriptor(23, spv::Op::OpConvertSToF, 0);
   auto good_extract_3 =
       TransformationCompositeExtract(instruction_descriptor_3, 104, 100, {2});
   ASSERT_TRUE(
@@ -203,12 +204,13 @@
                                                kConsoleMessageConsumer));
 
   // Replace %28 with %101[0] in 'OpStore %33 %28'
-  auto instruction_descriptor_4 = MakeInstructionDescriptor(33, SpvOpStore, 0);
+  auto instruction_descriptor_4 =
+      MakeInstructionDescriptor(33, spv::Op::OpStore, 0);
   auto good_extract_4 =
       TransformationCompositeExtract(instruction_descriptor_4, 105, 101, {0});
   // Bad: instruction descriptor does not identify an appropriate instruction
   auto bad_extract_4 = TransformationCompositeExtract(
-      MakeInstructionDescriptor(33, SpvOpCopyObject, 0), 105, 101, {0});
+      MakeInstructionDescriptor(33, spv::Op::OpCopyObject, 0), 105, 101, {0});
   ASSERT_TRUE(
       good_extract_4.IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
@@ -224,7 +226,7 @@
 
   // Replace %23 with %101[1] in '%50 = OpCopyObject %16 %23'
   auto instruction_descriptor_5 =
-      MakeInstructionDescriptor(50, SpvOpCopyObject, 0);
+      MakeInstructionDescriptor(50, spv::Op::OpCopyObject, 0);
   auto good_extract_5 =
       TransformationCompositeExtract(instruction_descriptor_5, 106, 101, {1});
   ASSERT_TRUE(
@@ -244,7 +246,8 @@
                                                kConsoleMessageConsumer));
 
   // Replace %32 with %101[2] in 'OpStore %33 %32'
-  auto instruction_descriptor_6 = MakeInstructionDescriptor(33, SpvOpStore, 1);
+  auto instruction_descriptor_6 =
+      MakeInstructionDescriptor(33, spv::Op::OpStore, 1);
   auto good_extract_6 =
       TransformationCompositeExtract(instruction_descriptor_6, 107, 101, {2});
   // Bad: id 1001 does not exist
@@ -265,7 +268,7 @@
 
   // Replace %23 with %101[3] in '%51 = OpCopyObject %16 %23'
   auto instruction_descriptor_7 =
-      MakeInstructionDescriptor(51, SpvOpCopyObject, 0);
+      MakeInstructionDescriptor(51, spv::Op::OpCopyObject, 0);
   auto good_extract_7 =
       TransformationCompositeExtract(instruction_descriptor_7, 108, 101, {3});
   ASSERT_TRUE(
@@ -431,7 +434,8 @@
       MakeSynonymFact(50, {}, 100, {2}));
 
   // Replace %23 with %100[0] in '%26 = OpFAdd %7 %23 %25'
-  auto instruction_descriptor_1 = MakeInstructionDescriptor(26, SpvOpFAdd, 0);
+  auto instruction_descriptor_1 =
+      MakeInstructionDescriptor(26, spv::Op::OpFAdd, 0);
   auto extract_1 =
       TransformationCompositeExtract(instruction_descriptor_1, 101, 100, {0});
   ASSERT_TRUE(extract_1.IsApplicable(context.get(), transformation_context));
@@ -445,7 +449,8 @@
                                                kConsoleMessageConsumer));
 
   // Replace %25 with %100[1] in '%26 = OpFAdd %7 %23 %25'
-  auto instruction_descriptor_2 = MakeInstructionDescriptor(26, SpvOpFAdd, 0);
+  auto instruction_descriptor_2 =
+      MakeInstructionDescriptor(26, spv::Op::OpFAdd, 0);
   auto extract_2 =
       TransformationCompositeExtract(instruction_descriptor_2, 102, 100, {1});
   ASSERT_TRUE(extract_2.IsApplicable(context.get(), transformation_context));
@@ -611,7 +616,7 @@
 
   // Replace %45 with %100[1] in '%46 = OpCompositeConstruct %32 %35 %45'
   auto instruction_descriptor_1 =
-      MakeInstructionDescriptor(46, SpvOpCompositeConstruct, 0);
+      MakeInstructionDescriptor(46, spv::Op::OpCompositeConstruct, 0);
   auto extract_1 =
       TransformationCompositeExtract(instruction_descriptor_1, 201, 100, {1});
   ASSERT_TRUE(extract_1.IsApplicable(context.get(), transformation_context));
@@ -627,7 +632,7 @@
   // Replace second occurrence of %27 with %101[0] in '%28 =
   // OpCompositeConstruct %8 %27 %27'
   auto instruction_descriptor_2 =
-      MakeInstructionDescriptor(28, SpvOpCompositeConstruct, 0);
+      MakeInstructionDescriptor(28, spv::Op::OpCompositeConstruct, 0);
   auto extract_2 =
       TransformationCompositeExtract(instruction_descriptor_2, 202, 101, {0});
   ASSERT_TRUE(extract_2.IsApplicable(context.get(), transformation_context));
@@ -642,7 +647,7 @@
 
   // Replace %36 with %101[1] in '%45 = OpCompositeConstruct %31 %36 %41 %44'
   auto instruction_descriptor_3 =
-      MakeInstructionDescriptor(45, SpvOpCompositeConstruct, 0);
+      MakeInstructionDescriptor(45, spv::Op::OpCompositeConstruct, 0);
   auto extract_3 =
       TransformationCompositeExtract(instruction_descriptor_3, 203, 101, {1});
   ASSERT_TRUE(extract_3.IsApplicable(context.get(), transformation_context));
@@ -658,7 +663,7 @@
   // Replace first occurrence of %27 with %101[2] in '%28 = OpCompositeConstruct
   // %8 %27 %27'
   auto instruction_descriptor_4 =
-      MakeInstructionDescriptor(28, SpvOpCompositeConstruct, 0);
+      MakeInstructionDescriptor(28, spv::Op::OpCompositeConstruct, 0);
   auto extract_4 =
       TransformationCompositeExtract(instruction_descriptor_4, 204, 101, {2});
   ASSERT_TRUE(extract_4.IsApplicable(context.get(), transformation_context));
@@ -672,7 +677,8 @@
                                                kConsoleMessageConsumer));
 
   // Replace %22 with %102[0] in 'OpStore %23 %22'
-  auto instruction_descriptor_5 = MakeInstructionDescriptor(23, SpvOpStore, 0);
+  auto instruction_descriptor_5 =
+      MakeInstructionDescriptor(23, spv::Op::OpStore, 0);
   auto extract_5 =
       TransformationCompositeExtract(instruction_descriptor_5, 205, 102, {0});
   ASSERT_TRUE(extract_5.IsApplicable(context.get(), transformation_context));
@@ -931,7 +937,7 @@
 
   // Replace %20 with %100[0:2] in '%80 = OpCopyObject %16 %20'
   auto instruction_descriptor_1 =
-      MakeInstructionDescriptor(80, SpvOpCopyObject, 0);
+      MakeInstructionDescriptor(80, spv::Op::OpCopyObject, 0);
   auto shuffle_1 = TransformationVectorShuffle(instruction_descriptor_1, 200,
                                                100, 100, {0, 1, 2});
   ASSERT_TRUE(shuffle_1.IsApplicable(context.get(), transformation_context));
@@ -948,7 +954,7 @@
 
   // Replace %54 with %100[3] in '%56 = OpFOrdNotEqual %30 %54 %55'
   auto instruction_descriptor_2 =
-      MakeInstructionDescriptor(56, SpvOpFOrdNotEqual, 0);
+      MakeInstructionDescriptor(56, spv::Op::OpFOrdNotEqual, 0);
   auto extract_2 =
       TransformationCompositeExtract(instruction_descriptor_2, 201, 100, {3});
 
@@ -963,7 +969,8 @@
                                                kConsoleMessageConsumer));
 
   // Replace %15 with %101[0:1] in 'OpStore %12 %15'
-  auto instruction_descriptor_3 = MakeInstructionDescriptor(64, SpvOpStore, 0);
+  auto instruction_descriptor_3 =
+      MakeInstructionDescriptor(64, spv::Op::OpStore, 0);
   auto shuffle_3 = TransformationVectorShuffle(instruction_descriptor_3, 202,
                                                101, 101, {0, 1});
   ASSERT_TRUE(shuffle_3.IsApplicable(context.get(), transformation_context));
@@ -980,7 +987,7 @@
 
   // Replace %19 with %101[2:3] in '%81 = OpVectorShuffle %16 %19 %19 0 0 1'
   auto instruction_descriptor_4 =
-      MakeInstructionDescriptor(81, SpvOpVectorShuffle, 0);
+      MakeInstructionDescriptor(81, spv::Op::OpVectorShuffle, 0);
   auto shuffle_4 = TransformationVectorShuffle(instruction_descriptor_4, 203,
                                                101, 101, {2, 3});
   ASSERT_TRUE(shuffle_4.IsApplicable(context.get(), transformation_context));
@@ -998,7 +1005,7 @@
   // Replace %27 with %102[0] in '%82 = OpCompositeConstruct %21 %26 %27 %28
   // %25'
   auto instruction_descriptor_5 =
-      MakeInstructionDescriptor(82, SpvOpCompositeConstruct, 0);
+      MakeInstructionDescriptor(82, spv::Op::OpCompositeConstruct, 0);
   auto extract_5 =
       TransformationCompositeExtract(instruction_descriptor_5, 204, 102, {0});
 
@@ -1014,7 +1021,7 @@
 
   // Replace %15 with %102[1:2] in '%83 = OpCopyObject %10 %15'
   auto instruction_descriptor_6 =
-      MakeInstructionDescriptor(83, SpvOpCopyObject, 0);
+      MakeInstructionDescriptor(83, spv::Op::OpCopyObject, 0);
   auto shuffle_6 = TransformationVectorShuffle(instruction_descriptor_6, 205,
                                                102, 102, {1, 2});
   ASSERT_TRUE(shuffle_6.IsApplicable(context.get(), transformation_context));
@@ -1031,7 +1038,7 @@
 
   // Replace %33 with %103[0] in '%86 = OpCopyObject %30 %33'
   auto instruction_descriptor_7 =
-      MakeInstructionDescriptor(86, SpvOpCopyObject, 0);
+      MakeInstructionDescriptor(86, spv::Op::OpCopyObject, 0);
   auto extract_7 =
       TransformationCompositeExtract(instruction_descriptor_7, 206, 103, {0});
   ASSERT_TRUE(extract_7.IsApplicable(context.get(), transformation_context));
@@ -1046,7 +1053,7 @@
 
   // Replace %47 with %103[1:3] in '%84 = OpCopyObject %39 %47'
   auto instruction_descriptor_8 =
-      MakeInstructionDescriptor(84, SpvOpCopyObject, 0);
+      MakeInstructionDescriptor(84, spv::Op::OpCopyObject, 0);
   auto shuffle_8 = TransformationVectorShuffle(instruction_descriptor_8, 207,
                                                103, 103, {1, 2, 3});
   ASSERT_TRUE(shuffle_8.IsApplicable(context.get(), transformation_context));
@@ -1063,7 +1070,7 @@
 
   // Replace %42 with %104[0] in '%85 = OpCopyObject %30 %42'
   auto instruction_descriptor_9 =
-      MakeInstructionDescriptor(85, SpvOpCopyObject, 0);
+      MakeInstructionDescriptor(85, spv::Op::OpCopyObject, 0);
   auto extract_9 =
       TransformationCompositeExtract(instruction_descriptor_9, 208, 104, {0});
   ASSERT_TRUE(extract_9.IsApplicable(context.get(), transformation_context));
@@ -1078,7 +1085,7 @@
 
   // Replace %45 with %104[1] in '%63 = OpLogicalOr %30 %45 %46'
   auto instruction_descriptor_10 =
-      MakeInstructionDescriptor(63, SpvOpLogicalOr, 0);
+      MakeInstructionDescriptor(63, spv::Op::OpLogicalOr, 0);
   auto extract_10 =
       TransformationCompositeExtract(instruction_descriptor_10, 209, 104, {1});
   ASSERT_TRUE(extract_10.IsApplicable(context.get(), transformation_context));
@@ -1092,7 +1099,8 @@
                                                kConsoleMessageConsumer));
 
   // Replace %38 with %105[0:1] in 'OpStore %36 %38'
-  auto instruction_descriptor_11 = MakeInstructionDescriptor(85, SpvOpStore, 0);
+  auto instruction_descriptor_11 =
+      MakeInstructionDescriptor(85, spv::Op::OpStore, 0);
   auto shuffle_11 = TransformationVectorShuffle(instruction_descriptor_11, 210,
                                                 105, 105, {0, 1});
   ASSERT_TRUE(shuffle_11.IsApplicable(context.get(), transformation_context));
@@ -1109,7 +1117,7 @@
 
   // Replace %46 with %105[2] in '%62 = OpLogicalAnd %30 %45 %46'
   auto instruction_descriptor_12 =
-      MakeInstructionDescriptor(62, SpvOpLogicalAnd, 0);
+      MakeInstructionDescriptor(62, spv::Op::OpLogicalAnd, 0);
   auto extract_12 =
       TransformationCompositeExtract(instruction_descriptor_12, 211, 105, {2});
   ASSERT_TRUE(extract_12.IsApplicable(context.get(), transformation_context));
diff --git a/test/fuzz/fact_manager/constant_uniform_facts_test.cpp b/test/fuzz/fact_manager/constant_uniform_facts_test.cpp
index 4d80f0a..b267e3d 100644
--- a/test/fuzz/fact_manager/constant_uniform_facts_test.cpp
+++ b/test/fuzz/fact_manager/constant_uniform_facts_test.cpp
@@ -366,41 +366,41 @@
   opt::Instruction::OperandList operands = {
       {SPV_OPERAND_TYPE_LITERAL_INTEGER, {1}}};
   context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
-      context.get(), SpvOpConstant, type_int32_id, 50, operands));
+      context.get(), spv::Op::OpConstant, type_int32_id, 50, operands));
   operands = {{SPV_OPERAND_TYPE_LITERAL_INTEGER, {buffer_int32_min[0]}}};
   context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
-      context.get(), SpvOpConstant, type_int32_id, 51, operands));
+      context.get(), spv::Op::OpConstant, type_int32_id, 51, operands));
   operands = {{SPV_OPERAND_TYPE_LITERAL_INTEGER, {buffer_int64_max[0]}},
               {SPV_OPERAND_TYPE_LITERAL_INTEGER, {buffer_int64_max[1]}}};
   context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
-      context.get(), SpvOpConstant, type_int64_id, 52, operands));
+      context.get(), spv::Op::OpConstant, type_int64_id, 52, operands));
   operands = {{SPV_OPERAND_TYPE_LITERAL_INTEGER, {1}}};
   context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
-      context.get(), SpvOpConstant, type_uint32_id, 53, operands));
+      context.get(), spv::Op::OpConstant, type_uint32_id, 53, operands));
   operands = {{SPV_OPERAND_TYPE_LITERAL_INTEGER, {buffer_uint64_1[0]}},
               {SPV_OPERAND_TYPE_LITERAL_INTEGER, {buffer_uint64_1[1]}}};
   context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
-      context.get(), SpvOpConstant, type_uint64_id, 54, operands));
+      context.get(), spv::Op::OpConstant, type_uint64_id, 54, operands));
   operands = {{SPV_OPERAND_TYPE_LITERAL_INTEGER, {buffer_uint64_max[0]}},
               {SPV_OPERAND_TYPE_LITERAL_INTEGER, {buffer_uint64_max[1]}}};
   context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
-      context.get(), SpvOpConstant, type_uint64_id, 55, operands));
+      context.get(), spv::Op::OpConstant, type_uint64_id, 55, operands));
   operands = {{SPV_OPERAND_TYPE_LITERAL_INTEGER, {buffer_float_10[0]}}};
   context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
-      context.get(), SpvOpConstant, type_float_id, 56, operands));
+      context.get(), spv::Op::OpConstant, type_float_id, 56, operands));
   operands = {{SPV_OPERAND_TYPE_LITERAL_INTEGER, {buffer_double_10[0]}},
               {SPV_OPERAND_TYPE_LITERAL_INTEGER, {buffer_double_10[1]}}};
   context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
-      context.get(), SpvOpConstant, type_double_id, 57, operands));
+      context.get(), spv::Op::OpConstant, type_double_id, 57, operands));
   operands = {{SPV_OPERAND_TYPE_LITERAL_INTEGER, {buffer_double_20[0]}},
               {SPV_OPERAND_TYPE_LITERAL_INTEGER, {buffer_double_20[1]}}};
   context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
-      context.get(), SpvOpConstant, type_double_id, 58, operands));
+      context.get(), spv::Op::OpConstant, type_double_id, 58, operands));
 
   // A duplicate of the constant with id 59.
   operands = {{SPV_OPERAND_TYPE_LITERAL_INTEGER, {1}}};
   context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
-      context.get(), SpvOpConstant, type_int32_id, 59, operands));
+      context.get(), spv::Op::OpConstant, type_int32_id, 59, operands));
 
   context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
 
diff --git a/test/fuzz/fact_manager/data_synonym_and_id_equation_facts_test.cpp b/test/fuzz/fact_manager/data_synonym_and_id_equation_facts_test.cpp
index 689d2e7..c3b47a4 100644
--- a/test/fuzz/fact_manager/data_synonym_and_id_equation_facts_test.cpp
+++ b/test/fuzz/fact_manager/data_synonym_and_id_equation_facts_test.cpp
@@ -136,15 +136,15 @@
   FactManager fact_manager(context.get());
 
   // Add equation facts
-  fact_manager.AddFactIdEquation(24, SpvOpConvertSToF, {15});
-  fact_manager.AddFactIdEquation(25, SpvOpConvertSToF, {16});
-  fact_manager.AddFactIdEquation(26, SpvOpConvertUToF, {17});
-  fact_manager.AddFactIdEquation(27, SpvOpConvertSToF, {18});
-  fact_manager.AddFactIdEquation(28, SpvOpConvertUToF, {19});
-  fact_manager.AddFactIdEquation(29, SpvOpConvertUToF, {20});
-  fact_manager.AddFactIdEquation(30, SpvOpConvertSToF, {21});
-  fact_manager.AddFactIdEquation(31, SpvOpConvertUToF, {22});
-  fact_manager.AddFactIdEquation(32, SpvOpConvertUToF, {23});
+  fact_manager.AddFactIdEquation(24, spv::Op::OpConvertSToF, {15});
+  fact_manager.AddFactIdEquation(25, spv::Op::OpConvertSToF, {16});
+  fact_manager.AddFactIdEquation(26, spv::Op::OpConvertUToF, {17});
+  fact_manager.AddFactIdEquation(27, spv::Op::OpConvertSToF, {18});
+  fact_manager.AddFactIdEquation(28, spv::Op::OpConvertUToF, {19});
+  fact_manager.AddFactIdEquation(29, spv::Op::OpConvertUToF, {20});
+  fact_manager.AddFactIdEquation(30, spv::Op::OpConvertSToF, {21});
+  fact_manager.AddFactIdEquation(31, spv::Op::OpConvertUToF, {22});
+  fact_manager.AddFactIdEquation(32, spv::Op::OpConvertUToF, {23});
 
   fact_manager.AddFactDataSynonym(MakeDataDescriptor(15, {}),
                                   MakeDataDescriptor(16, {}));
@@ -212,7 +212,7 @@
 
   // Add required facts.
   transformation_context.GetFactManager()->AddFactIdEquation(
-      14, SpvOpConvertSToF, {9});
+      14, spv::Op::OpConvertSToF, {9});
   transformation_context.GetFactManager()->AddFactDataSynonym(
       MakeDataDescriptor(14, {}), MakeDataDescriptor(17, {}));
 
@@ -232,7 +232,7 @@
 
   // Add another equation.
   transformation_context.GetFactManager()->AddFactIdEquation(
-      15, SpvOpConvertSToF, {9});
+      15, spv::Op::OpConvertSToF, {9});
 
   // Check that two ids are synonymous even though one of them doesn't exist in
   // the module (%17).
@@ -249,7 +249,7 @@
   ASSERT_TRUE(context->KillDef(15));
 
   transformation_context.GetFactManager()->AddFactIdEquation(
-      18, SpvOpConvertSToF, {9});
+      18, spv::Op::OpConvertSToF, {9});
 
   CheckConsistencyOfSynonymFacts(context.get(), transformation_context);
 
@@ -294,8 +294,8 @@
                                   MakeDataDescriptor(7, {}));
   fact_manager.AddFactDataSynonym(MakeDataDescriptor(16, {}),
                                   MakeDataDescriptor(14, {}));
-  fact_manager.AddFactIdEquation(14, SpvOpLogicalNot, {7});
-  fact_manager.AddFactIdEquation(17, SpvOpLogicalNot, {16});
+  fact_manager.AddFactIdEquation(14, spv::Op::OpLogicalNot, {7});
+  fact_manager.AddFactIdEquation(17, spv::Op::OpLogicalNot, {16});
 
   ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(15, {}),
                                         MakeDataDescriptor(7, {})));
@@ -336,8 +336,8 @@
 
   FactManager fact_manager(context.get());
 
-  fact_manager.AddFactIdEquation(14, SpvOpSNegate, {7});
-  fact_manager.AddFactIdEquation(15, SpvOpSNegate, {14});
+  fact_manager.AddFactIdEquation(14, spv::Op::OpSNegate, {7});
+  fact_manager.AddFactIdEquation(15, spv::Op::OpSNegate, {14});
 
   ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(7, {}),
                                         MakeDataDescriptor(15, {})));
@@ -380,19 +380,19 @@
 
   FactManager fact_manager(context.get());
 
-  fact_manager.AddFactIdEquation(14, SpvOpIAdd, {15, 16});
+  fact_manager.AddFactIdEquation(14, spv::Op::OpIAdd, {15, 16});
   fact_manager.AddFactDataSynonym(MakeDataDescriptor(17, {}),
                                   MakeDataDescriptor(15, {}));
   fact_manager.AddFactDataSynonym(MakeDataDescriptor(18, {}),
                                   MakeDataDescriptor(16, {}));
-  fact_manager.AddFactIdEquation(19, SpvOpISub, {14, 18});
-  fact_manager.AddFactIdEquation(20, SpvOpISub, {14, 17});
+  fact_manager.AddFactIdEquation(19, spv::Op::OpISub, {14, 18});
+  fact_manager.AddFactIdEquation(20, spv::Op::OpISub, {14, 17});
   fact_manager.AddFactDataSynonym(MakeDataDescriptor(21, {}),
                                   MakeDataDescriptor(14, {}));
-  fact_manager.AddFactIdEquation(22, SpvOpISub, {16, 21});
+  fact_manager.AddFactIdEquation(22, spv::Op::OpISub, {16, 21});
   fact_manager.AddFactDataSynonym(MakeDataDescriptor(23, {}),
                                   MakeDataDescriptor(22, {}));
-  fact_manager.AddFactIdEquation(24, SpvOpSNegate, {23});
+  fact_manager.AddFactIdEquation(24, spv::Op::OpSNegate, {23});
 
   ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(19, {}),
                                         MakeDataDescriptor(15, {})));
@@ -438,31 +438,31 @@
 
   FactManager fact_manager(context.get());
 
-  fact_manager.AddFactIdEquation(14, SpvOpISub, {15, 16});
-  fact_manager.AddFactIdEquation(17, SpvOpIAdd, {14, 16});
+  fact_manager.AddFactIdEquation(14, spv::Op::OpISub, {15, 16});
+  fact_manager.AddFactIdEquation(17, spv::Op::OpIAdd, {14, 16});
 
   ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(17, {}),
                                         MakeDataDescriptor(15, {})));
 
-  fact_manager.AddFactIdEquation(18, SpvOpIAdd, {16, 14});
+  fact_manager.AddFactIdEquation(18, spv::Op::OpIAdd, {16, 14});
 
   ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(18, {}),
                                         MakeDataDescriptor(15, {})));
   ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(17, {}),
                                         MakeDataDescriptor(18, {})));
 
-  fact_manager.AddFactIdEquation(19, SpvOpISub, {14, 15});
-  fact_manager.AddFactIdEquation(20, SpvOpSNegate, {19});
+  fact_manager.AddFactIdEquation(19, spv::Op::OpISub, {14, 15});
+  fact_manager.AddFactIdEquation(20, spv::Op::OpSNegate, {19});
 
   ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(20, {}),
                                         MakeDataDescriptor(16, {})));
 
-  fact_manager.AddFactIdEquation(21, SpvOpISub, {14, 19});
+  fact_manager.AddFactIdEquation(21, spv::Op::OpISub, {14, 19});
   ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(21, {}),
                                         MakeDataDescriptor(15, {})));
 
-  fact_manager.AddFactIdEquation(22, SpvOpISub, {14, 18});
-  fact_manager.AddFactIdEquation(23, SpvOpSNegate, {22});
+  fact_manager.AddFactIdEquation(22, spv::Op::OpISub, {14, 18});
+  fact_manager.AddFactIdEquation(23, spv::Op::OpSNegate, {22});
   ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(23, {}),
                                         MakeDataDescriptor(16, {})));
 }
@@ -525,31 +525,31 @@
   fact_manager.AddFactDataSynonym(MakeDataDescriptor(22, {}),
                                   MakeDataDescriptor(23, {}));
 
-  fact_manager.AddFactIdEquation(25, SpvOpConvertUToF, {16});
-  fact_manager.AddFactIdEquation(26, SpvOpConvertUToF, {17});
+  fact_manager.AddFactIdEquation(25, spv::Op::OpConvertUToF, {16});
+  fact_manager.AddFactIdEquation(26, spv::Op::OpConvertUToF, {17});
   ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(25, {}),
                                         MakeDataDescriptor(26, {})));
 
-  fact_manager.AddFactIdEquation(27, SpvOpConvertSToF, {20});
-  fact_manager.AddFactIdEquation(28, SpvOpConvertUToF, {21});
+  fact_manager.AddFactIdEquation(27, spv::Op::OpConvertSToF, {20});
+  fact_manager.AddFactIdEquation(28, spv::Op::OpConvertUToF, {21});
   ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(27, {}),
                                          MakeDataDescriptor(28, {})));
 
-  fact_manager.AddFactIdEquation(29, SpvOpConvertSToF, {18});
-  fact_manager.AddFactIdEquation(30, SpvOpConvertUToF, {19});
+  fact_manager.AddFactIdEquation(29, spv::Op::OpConvertSToF, {18});
+  fact_manager.AddFactIdEquation(30, spv::Op::OpConvertUToF, {19});
   ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(29, {}),
                                          MakeDataDescriptor(30, {})));
 
-  fact_manager.AddFactIdEquation(31, SpvOpConvertSToF, {22});
-  fact_manager.AddFactIdEquation(32, SpvOpConvertSToF, {23});
+  fact_manager.AddFactIdEquation(31, spv::Op::OpConvertSToF, {22});
+  fact_manager.AddFactIdEquation(32, spv::Op::OpConvertSToF, {23});
   ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(31, {}),
                                         MakeDataDescriptor(32, {})));
 
-  fact_manager.AddFactIdEquation(33, SpvOpConvertUToF, {17});
+  fact_manager.AddFactIdEquation(33, spv::Op::OpConvertUToF, {17});
   ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(33, {}),
                                         MakeDataDescriptor(26, {})));
 
-  fact_manager.AddFactIdEquation(34, SpvOpConvertSToF, {23});
+  fact_manager.AddFactIdEquation(34, spv::Op::OpConvertSToF, {23});
   ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(32, {}),
                                         MakeDataDescriptor(34, {})));
 }
@@ -605,7 +605,7 @@
 
   uint32_t lhs_id = 30;
   for (uint32_t rhs_id : {6, 6, 7, 7, 19, 19, 20, 20, 21, 21, 22, 22}) {
-    fact_manager.AddFactIdEquation(lhs_id, SpvOpBitcast, {rhs_id});
+    fact_manager.AddFactIdEquation(lhs_id, spv::Op::OpBitcast, {rhs_id});
     ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(lhs_id, {}),
                                           MakeDataDescriptor(rhs_id, {})));
     ++lhs_id;
@@ -651,37 +651,37 @@
 
   FactManager fact_manager(context.get());
 
-  fact_manager.AddFactIdEquation(14, SpvOpISub, {15, 16});
+  fact_manager.AddFactIdEquation(14, spv::Op::OpISub, {15, 16});
   fact_manager.AddFactDataSynonym(MakeDataDescriptor(114, {}),
                                   MakeDataDescriptor(14, {}));
-  fact_manager.AddFactIdEquation(17, SpvOpIAdd, {114, 16});
+  fact_manager.AddFactIdEquation(17, spv::Op::OpIAdd, {114, 16});
 
   ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(17, {}),
                                         MakeDataDescriptor(15, {})));
 
-  fact_manager.AddFactIdEquation(18, SpvOpIAdd, {16, 114});
+  fact_manager.AddFactIdEquation(18, spv::Op::OpIAdd, {16, 114});
 
   ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(18, {}),
                                         MakeDataDescriptor(15, {})));
   ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(17, {}),
                                         MakeDataDescriptor(18, {})));
 
-  fact_manager.AddFactIdEquation(19, SpvOpISub, {14, 15});
+  fact_manager.AddFactIdEquation(19, spv::Op::OpISub, {14, 15});
   fact_manager.AddFactDataSynonym(MakeDataDescriptor(119, {}),
                                   MakeDataDescriptor(19, {}));
-  fact_manager.AddFactIdEquation(20, SpvOpSNegate, {119});
+  fact_manager.AddFactIdEquation(20, spv::Op::OpSNegate, {119});
 
   ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(20, {}),
                                         MakeDataDescriptor(16, {})));
 
-  fact_manager.AddFactIdEquation(21, SpvOpISub, {14, 19});
+  fact_manager.AddFactIdEquation(21, spv::Op::OpISub, {14, 19});
   ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(21, {}),
                                         MakeDataDescriptor(15, {})));
 
-  fact_manager.AddFactIdEquation(22, SpvOpISub, {14, 18});
+  fact_manager.AddFactIdEquation(22, spv::Op::OpISub, {14, 18});
   fact_manager.AddFactDataSynonym(MakeDataDescriptor(22, {}),
                                   MakeDataDescriptor(220, {}));
-  fact_manager.AddFactIdEquation(23, SpvOpSNegate, {220});
+  fact_manager.AddFactIdEquation(23, spv::Op::OpSNegate, {220});
   ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(23, {}),
                                         MakeDataDescriptor(16, {})));
 }
diff --git a/test/fuzz/fuzzer_pass_donate_modules_test.cpp b/test/fuzz/fuzzer_pass_donate_modules_test.cpp
index fe8e671..81687ac 100644
--- a/test/fuzz/fuzzer_pass_donate_modules_test.cpp
+++ b/test/fuzz/fuzzer_pass_donate_modules_test.cpp
@@ -2025,9 +2025,9 @@
                                       &transformation_sequence, false, {});
 
   ASSERT_TRUE(donor_context->get_feature_mgr()->HasCapability(
-      SpvCapabilityVariablePointersStorageBuffer));
+      spv::Capability::VariablePointersStorageBuffer));
   ASSERT_FALSE(recipient_context->get_feature_mgr()->HasCapability(
-      SpvCapabilityVariablePointersStorageBuffer));
+      spv::Capability::VariablePointersStorageBuffer));
 
   fuzzer_pass.DonateSingleModule(donor_context.get(), false);
 
@@ -2040,12 +2040,12 @@
   // have different OpCapability instructions but the same capabilities. In our
   // example, VariablePointers implicitly declares
   // VariablePointersStorageBuffer. Thus, two modules must be compatible.
-  recipient_context->AddCapability(SpvCapabilityVariablePointers);
+  recipient_context->AddCapability(spv::Capability::VariablePointers);
 
   ASSERT_TRUE(donor_context->get_feature_mgr()->HasCapability(
-      SpvCapabilityVariablePointersStorageBuffer));
+      spv::Capability::VariablePointersStorageBuffer));
   ASSERT_TRUE(recipient_context->get_feature_mgr()->HasCapability(
-      SpvCapabilityVariablePointersStorageBuffer));
+      spv::Capability::VariablePointersStorageBuffer));
 
   fuzzer_pass.DonateSingleModule(donor_context.get(), false);
 
diff --git a/test/fuzz/fuzzerutil_test.cpp b/test/fuzz/fuzzerutil_test.cpp
index 1286d38..6555336 100644
--- a/test/fuzz/fuzzerutil_test.cpp
+++ b/test/fuzz/fuzzerutil_test.cpp
@@ -939,9 +939,9 @@
                                                kConsoleMessageConsumer));
 
   opt::IRContext* ir_context = context.get();
-  auto private_storage_class = SpvStorageClassPrivate;
-  auto function_storage_class = SpvStorageClassFunction;
-  auto input_storage_class = SpvStorageClassInput;
+  auto private_storage_class = spv::StorageClass::Private;
+  auto function_storage_class = spv::StorageClass::Function;
+  auto input_storage_class = spv::StorageClass::Input;
 
   // A valid pointer must have the correct |pointee_type_id| and |storageClass|.
   // A function type pointer with id = 9 and pointee type id 8 should be found.
@@ -1610,196 +1610,206 @@
 
   // OpAtomicLoad
 #ifndef NDEBUG
-  ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicLoad, 0,
-                                              int_type, uint_type),
-               "Signedness check should not occur on a pointer operand.");
-#endif
-  ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicLoad, 1,
-                                             int_type, uint_type));
-  ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicLoad, 2,
-                                             int_type, uint_type));
-
-  // OpAtomicExchange
-#ifndef NDEBUG
-  ASSERT_DEATH(fuzzerutil::TypesAreCompatible(
-                   context.get(), SpvOpAtomicExchange, 0, int_type, uint_type),
-               "Signedness check should not occur on a pointer operand.");
-#endif
-  ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicExchange,
-                                             1, int_type, uint_type));
-  ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicExchange,
-                                             2, int_type, uint_type));
-  ASSERT_FALSE(fuzzerutil::TypesAreCompatible(
-      context.get(), SpvOpAtomicExchange, 3, int_type, uint_type));
-
-  // OpAtomicStore
-#ifndef NDEBUG
-  ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicStore,
-                                              0, int_type, uint_type),
-               "Signedness check should not occur on a pointer operand.");
-#endif
-  ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicStore, 1,
-                                             int_type, uint_type));
-  ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicStore, 2,
-                                             int_type, uint_type));
-  ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicStore,
-                                              3, int_type, uint_type));
-
-  // OpAtomicCompareExchange
-#ifndef NDEBUG
   ASSERT_DEATH(
-      fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicCompareExchange,
-                                     0, int_type, uint_type),
-      "Signedness check should not occur on a pointer operand.");
-#endif
-  ASSERT_TRUE(fuzzerutil::TypesAreCompatible(
-      context.get(), SpvOpAtomicCompareExchange, 1, int_type, uint_type));
-  ASSERT_TRUE(fuzzerutil::TypesAreCompatible(
-      context.get(), SpvOpAtomicCompareExchange, 2, int_type, uint_type));
-  ASSERT_TRUE(fuzzerutil::TypesAreCompatible(
-      context.get(), SpvOpAtomicCompareExchange, 3, int_type, uint_type));
-  ASSERT_FALSE(fuzzerutil::TypesAreCompatible(
-      context.get(), SpvOpAtomicCompareExchange, 4, int_type, uint_type));
-
-  // OpAtomicIIncrement
-#ifndef NDEBUG
-  ASSERT_DEATH(
-      fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicIIncrement, 0,
+      fuzzerutil::TypesAreCompatible(context.get(), spv::Op::OpAtomicLoad, 0,
                                      int_type, uint_type),
       "Signedness check should not occur on a pointer operand.");
 #endif
   ASSERT_TRUE(fuzzerutil::TypesAreCompatible(
-      context.get(), SpvOpAtomicIIncrement, 1, int_type, uint_type));
+      context.get(), spv::Op::OpAtomicLoad, 1, int_type, uint_type));
   ASSERT_TRUE(fuzzerutil::TypesAreCompatible(
-      context.get(), SpvOpAtomicIIncrement, 2, int_type, uint_type));
+      context.get(), spv::Op::OpAtomicLoad, 2, int_type, uint_type));
 
-// OpAtomicIDecrement
+  // OpAtomicExchange
 #ifndef NDEBUG
-  ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicStore,
+  ASSERT_DEATH(
+      fuzzerutil::TypesAreCompatible(context.get(), spv::Op::OpAtomicExchange,
+                                     0, int_type, uint_type),
+      "Signedness check should not occur on a pointer operand.");
+#endif
+  ASSERT_TRUE(fuzzerutil::TypesAreCompatible(
+      context.get(), spv::Op::OpAtomicExchange, 1, int_type, uint_type));
+  ASSERT_TRUE(fuzzerutil::TypesAreCompatible(
+      context.get(), spv::Op::OpAtomicExchange, 2, int_type, uint_type));
+  ASSERT_FALSE(fuzzerutil::TypesAreCompatible(
+      context.get(), spv::Op::OpAtomicExchange, 3, int_type, uint_type));
+
+  // OpAtomicStore
+#ifndef NDEBUG
+  ASSERT_DEATH(
+      fuzzerutil::TypesAreCompatible(context.get(), spv::Op::OpAtomicStore, 0,
+                                     int_type, uint_type),
+      "Signedness check should not occur on a pointer operand.");
+#endif
+  ASSERT_TRUE(fuzzerutil::TypesAreCompatible(
+      context.get(), spv::Op::OpAtomicStore, 1, int_type, uint_type));
+  ASSERT_TRUE(fuzzerutil::TypesAreCompatible(
+      context.get(), spv::Op::OpAtomicStore, 2, int_type, uint_type));
+  ASSERT_FALSE(fuzzerutil::TypesAreCompatible(
+      context.get(), spv::Op::OpAtomicStore, 3, int_type, uint_type));
+
+  // OpAtomicCompareExchange
+#ifndef NDEBUG
+  ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(),
+                                              spv::Op::OpAtomicCompareExchange,
                                               0, int_type, uint_type),
                "Signedness check should not occur on a pointer operand.");
 #endif
-  ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicStore, 1,
-                                             int_type, uint_type));
-  ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicStore, 2,
-                                             int_type, uint_type));
+  ASSERT_TRUE(fuzzerutil::TypesAreCompatible(
+      context.get(), spv::Op::OpAtomicCompareExchange, 1, int_type, uint_type));
+  ASSERT_TRUE(fuzzerutil::TypesAreCompatible(
+      context.get(), spv::Op::OpAtomicCompareExchange, 2, int_type, uint_type));
+  ASSERT_TRUE(fuzzerutil::TypesAreCompatible(
+      context.get(), spv::Op::OpAtomicCompareExchange, 3, int_type, uint_type));
+  ASSERT_FALSE(fuzzerutil::TypesAreCompatible(
+      context.get(), spv::Op::OpAtomicCompareExchange, 4, int_type, uint_type));
+
+  // OpAtomicIIncrement
+#ifndef NDEBUG
+  ASSERT_DEATH(
+      fuzzerutil::TypesAreCompatible(context.get(), spv::Op::OpAtomicIIncrement,
+                                     0, int_type, uint_type),
+      "Signedness check should not occur on a pointer operand.");
+#endif
+  ASSERT_TRUE(fuzzerutil::TypesAreCompatible(
+      context.get(), spv::Op::OpAtomicIIncrement, 1, int_type, uint_type));
+  ASSERT_TRUE(fuzzerutil::TypesAreCompatible(
+      context.get(), spv::Op::OpAtomicIIncrement, 2, int_type, uint_type));
+
+// OpAtomicIDecrement
+#ifndef NDEBUG
+  ASSERT_DEATH(
+      fuzzerutil::TypesAreCompatible(context.get(), spv::Op::OpAtomicStore, 0,
+                                     int_type, uint_type),
+      "Signedness check should not occur on a pointer operand.");
+#endif
+  ASSERT_TRUE(fuzzerutil::TypesAreCompatible(
+      context.get(), spv::Op::OpAtomicStore, 1, int_type, uint_type));
+  ASSERT_TRUE(fuzzerutil::TypesAreCompatible(
+      context.get(), spv::Op::OpAtomicStore, 2, int_type, uint_type));
 
 // OpAtomicIAdd
 #ifndef NDEBUG
-  ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicIAdd, 0,
-                                              int_type, uint_type),
-               "Signedness check should not occur on a pointer operand.");
+  ASSERT_DEATH(
+      fuzzerutil::TypesAreCompatible(context.get(), spv::Op::OpAtomicIAdd, 0,
+                                     int_type, uint_type),
+      "Signedness check should not occur on a pointer operand.");
 #endif
-  ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicIAdd, 1,
-                                             int_type, uint_type));
-  ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicIAdd, 2,
-                                             int_type, uint_type));
-  ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicIAdd, 3,
-                                              int_type, uint_type));
+  ASSERT_TRUE(fuzzerutil::TypesAreCompatible(
+      context.get(), spv::Op::OpAtomicIAdd, 1, int_type, uint_type));
+  ASSERT_TRUE(fuzzerutil::TypesAreCompatible(
+      context.get(), spv::Op::OpAtomicIAdd, 2, int_type, uint_type));
+  ASSERT_FALSE(fuzzerutil::TypesAreCompatible(
+      context.get(), spv::Op::OpAtomicIAdd, 3, int_type, uint_type));
 
 // OpAtomicISub
 #ifndef NDEBUG
-  ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicISub, 0,
-                                              int_type, uint_type),
-               "Signedness check should not occur on a pointer operand.");
+  ASSERT_DEATH(
+      fuzzerutil::TypesAreCompatible(context.get(), spv::Op::OpAtomicISub, 0,
+                                     int_type, uint_type),
+      "Signedness check should not occur on a pointer operand.");
 #endif
-  ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicISub, 1,
-                                             int_type, uint_type));
-  ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicISub, 2,
-                                             int_type, uint_type));
-  ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicISub, 3,
-                                              int_type, uint_type));
+  ASSERT_TRUE(fuzzerutil::TypesAreCompatible(
+      context.get(), spv::Op::OpAtomicISub, 1, int_type, uint_type));
+  ASSERT_TRUE(fuzzerutil::TypesAreCompatible(
+      context.get(), spv::Op::OpAtomicISub, 2, int_type, uint_type));
+  ASSERT_FALSE(fuzzerutil::TypesAreCompatible(
+      context.get(), spv::Op::OpAtomicISub, 3, int_type, uint_type));
 
 // OpAtomicSMin
 #ifndef NDEBUG
-  ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicSMin, 0,
-                                              int_type, uint_type),
-               "Signedness check should not occur on a pointer operand.");
+  ASSERT_DEATH(
+      fuzzerutil::TypesAreCompatible(context.get(), spv::Op::OpAtomicSMin, 0,
+                                     int_type, uint_type),
+      "Signedness check should not occur on a pointer operand.");
 #endif
-  ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicSMin, 1,
-                                             int_type, uint_type));
-  ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicSMin, 2,
-                                             int_type, uint_type));
-  ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicSMin, 3,
-                                              int_type, uint_type));
+  ASSERT_TRUE(fuzzerutil::TypesAreCompatible(
+      context.get(), spv::Op::OpAtomicSMin, 1, int_type, uint_type));
+  ASSERT_TRUE(fuzzerutil::TypesAreCompatible(
+      context.get(), spv::Op::OpAtomicSMin, 2, int_type, uint_type));
+  ASSERT_FALSE(fuzzerutil::TypesAreCompatible(
+      context.get(), spv::Op::OpAtomicSMin, 3, int_type, uint_type));
 
 // OpAtomicUMin
 #ifndef NDEBUG
-  ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicUMin, 0,
-                                              int_type, uint_type),
-               "Signedness check should not occur on a pointer operand.");
+  ASSERT_DEATH(
+      fuzzerutil::TypesAreCompatible(context.get(), spv::Op::OpAtomicUMin, 0,
+                                     int_type, uint_type),
+      "Signedness check should not occur on a pointer operand.");
 #endif
-  ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicUMin, 1,
-                                             int_type, uint_type));
-  ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicUMin, 2,
-                                             int_type, uint_type));
-  ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicUMin, 3,
-                                              int_type, uint_type));
+  ASSERT_TRUE(fuzzerutil::TypesAreCompatible(
+      context.get(), spv::Op::OpAtomicUMin, 1, int_type, uint_type));
+  ASSERT_TRUE(fuzzerutil::TypesAreCompatible(
+      context.get(), spv::Op::OpAtomicUMin, 2, int_type, uint_type));
+  ASSERT_FALSE(fuzzerutil::TypesAreCompatible(
+      context.get(), spv::Op::OpAtomicUMin, 3, int_type, uint_type));
 
 // OpAtomicSMax
 #ifndef NDEBUG
-  ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicSMax, 0,
-                                              int_type, uint_type),
-               "Signedness check should not occur on a pointer operand.");
+  ASSERT_DEATH(
+      fuzzerutil::TypesAreCompatible(context.get(), spv::Op::OpAtomicSMax, 0,
+                                     int_type, uint_type),
+      "Signedness check should not occur on a pointer operand.");
 #endif
-  ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicSMax, 1,
-                                             int_type, uint_type));
-  ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicSMax, 2,
-                                             int_type, uint_type));
-  ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicSMax, 3,
-                                              int_type, uint_type));
+  ASSERT_TRUE(fuzzerutil::TypesAreCompatible(
+      context.get(), spv::Op::OpAtomicSMax, 1, int_type, uint_type));
+  ASSERT_TRUE(fuzzerutil::TypesAreCompatible(
+      context.get(), spv::Op::OpAtomicSMax, 2, int_type, uint_type));
+  ASSERT_FALSE(fuzzerutil::TypesAreCompatible(
+      context.get(), spv::Op::OpAtomicSMax, 3, int_type, uint_type));
 
 // OpAtomicUMax
 #ifndef NDEBUG
-  ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicUMax, 0,
-                                              int_type, uint_type),
-               "Signedness check should not occur on a pointer operand.");
+  ASSERT_DEATH(
+      fuzzerutil::TypesAreCompatible(context.get(), spv::Op::OpAtomicUMax, 0,
+                                     int_type, uint_type),
+      "Signedness check should not occur on a pointer operand.");
 #endif
-  ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicUMax, 1,
-                                             int_type, uint_type));
-  ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicUMax, 2,
-                                             int_type, uint_type));
-  ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicUMax, 3,
-                                              int_type, uint_type));
+  ASSERT_TRUE(fuzzerutil::TypesAreCompatible(
+      context.get(), spv::Op::OpAtomicUMax, 1, int_type, uint_type));
+  ASSERT_TRUE(fuzzerutil::TypesAreCompatible(
+      context.get(), spv::Op::OpAtomicUMax, 2, int_type, uint_type));
+  ASSERT_FALSE(fuzzerutil::TypesAreCompatible(
+      context.get(), spv::Op::OpAtomicUMax, 3, int_type, uint_type));
 
 // OpAtomicAnd
 #ifndef NDEBUG
-  ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicAnd, 0,
-                                              int_type, uint_type),
+  ASSERT_DEATH(fuzzerutil::TypesAreCompatible(
+                   context.get(), spv::Op::OpAtomicAnd, 0, int_type, uint_type),
                "Signedness check should not occur on a pointer operand.");
 #endif
-  ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicAnd, 1,
-                                             int_type, uint_type));
-  ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicAnd, 2,
-                                             int_type, uint_type));
-  ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicAnd, 3,
-                                              int_type, uint_type));
+  ASSERT_TRUE(fuzzerutil::TypesAreCompatible(
+      context.get(), spv::Op::OpAtomicAnd, 1, int_type, uint_type));
+  ASSERT_TRUE(fuzzerutil::TypesAreCompatible(
+      context.get(), spv::Op::OpAtomicAnd, 2, int_type, uint_type));
+  ASSERT_FALSE(fuzzerutil::TypesAreCompatible(
+      context.get(), spv::Op::OpAtomicAnd, 3, int_type, uint_type));
 
 // OpAtomicOr
 #ifndef NDEBUG
-  ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicOr, 0,
-                                              int_type, uint_type),
+  ASSERT_DEATH(fuzzerutil::TypesAreCompatible(
+                   context.get(), spv::Op::OpAtomicOr, 0, int_type, uint_type),
                "Signedness check should not occur on a pointer operand.");
 #endif
-  ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicOr, 1,
-                                             int_type, uint_type));
-  ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicOr, 2,
-                                             int_type, uint_type));
-  ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicOr, 3,
-                                              int_type, uint_type));
+  ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), spv::Op::OpAtomicOr,
+                                             1, int_type, uint_type));
+  ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), spv::Op::OpAtomicOr,
+                                             2, int_type, uint_type));
+  ASSERT_FALSE(fuzzerutil::TypesAreCompatible(
+      context.get(), spv::Op::OpAtomicOr, 3, int_type, uint_type));
 
 // OpAtomicXor
 #ifndef NDEBUG
-  ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicXor, 0,
-                                              int_type, uint_type),
+  ASSERT_DEATH(fuzzerutil::TypesAreCompatible(
+                   context.get(), spv::Op::OpAtomicXor, 0, int_type, uint_type),
                "Signedness check should not occur on a pointer operand.");
 #endif
-  ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicXor, 1,
-                                             int_type, uint_type));
-  ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicXor, 2,
-                                             int_type, uint_type));
-  ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicXor, 3,
-                                              int_type, uint_type));
+  ASSERT_TRUE(fuzzerutil::TypesAreCompatible(
+      context.get(), spv::Op::OpAtomicXor, 1, int_type, uint_type));
+  ASSERT_TRUE(fuzzerutil::TypesAreCompatible(
+      context.get(), spv::Op::OpAtomicXor, 2, int_type, uint_type));
+  ASSERT_FALSE(fuzzerutil::TypesAreCompatible(
+      context.get(), spv::Op::OpAtomicXor, 3, int_type, uint_type));
 }
 
 }  // namespace
diff --git a/test/fuzz/replayer_test.cpp b/test/fuzz/replayer_test.cpp
index 151f263..b87d4f8 100644
--- a/test/fuzz/replayer_test.cpp
+++ b/test/fuzz/replayer_test.cpp
@@ -90,8 +90,8 @@
   protobufs::TransformationSequence transformations;
   for (uint32_t id = 12; id <= 22; id++) {
     *transformations.add_transformation() =
-        TransformationSplitBlock(MakeInstructionDescriptor(id, SpvOpLoad, 0),
-                                 id + 100)
+        TransformationSplitBlock(
+            MakeInstructionDescriptor(id, spv::Op::OpLoad, 0), id + 100)
             .ToMessage();
   }
 
@@ -335,7 +335,7 @@
   *transformations.add_transformation() =
       TransformationAddConstantScalar(100, 8, {42}, true).ToMessage();
   *transformations.add_transformation() =
-      TransformationAddGlobalVariable(101, 50, SpvStorageClassPrivate, 100,
+      TransformationAddGlobalVariable(101, 50, spv::StorageClass::Private, 100,
                                       true)
           .ToMessage();
   *transformations.add_transformation() =
@@ -345,7 +345,7 @@
           11,
           protobufs::TransformationAddSynonym::SynonymType::
               TransformationAddSynonym_SynonymType_COPY_OBJECT,
-          104, MakeInstructionDescriptor(12, SpvOpFunctionCall, 0))
+          104, MakeInstructionDescriptor(12, spv::Op::OpFunctionCall, 0))
           .ToMessage();
 
   // Full replay
@@ -455,7 +455,8 @@
   *transformations.add_transformation() =
       TransformationFlattenConditionalBranch(5, true, 0, 0, 0, {}).ToMessage();
   *transformations.add_transformation() =
-      TransformationAddGlobalVariable(101, 50, SpvStorageClassPrivate, 11, true)
+      TransformationAddGlobalVariable(101, 50, spv::StorageClass::Private, 11,
+                                      true)
           .ToMessage();
 
   protobufs::FactSequence empty_facts;
diff --git a/test/fuzz/shrinker_test.cpp b/test/fuzz/shrinker_test.cpp
index 942de29..699730c 100644
--- a/test/fuzz/shrinker_test.cpp
+++ b/test/fuzz/shrinker_test.cpp
@@ -186,9 +186,9 @@
     for (auto& function : *temp_ir_context->module()) {
       for (auto& block : function) {
         for (auto& inst : block) {
-          if (inst.opcode() == SpvOpNot) {
+          if (inst.opcode() == spv::Op::OpNot) {
             found_op_not = true;
-          } else if (inst.opcode() == SpvOpFunctionCall) {
+          } else if (inst.opcode() == spv::Op::OpFunctionCall) {
             op_call_count++;
           }
         }
@@ -221,21 +221,21 @@
         for (auto& inst : block) {
           switch (counter) {
             case 0:
-              ASSERT_EQ(SpvOpVariable, inst.opcode());
+              ASSERT_EQ(spv::Op::OpVariable, inst.opcode());
               ASSERT_EQ(11, inst.result_id());
               break;
             case 1:
-              ASSERT_EQ(SpvOpStore, inst.opcode());
+              ASSERT_EQ(spv::Op::OpStore, inst.opcode());
               break;
             case 2:
-              ASSERT_EQ(SpvOpLoad, inst.opcode());
+              ASSERT_EQ(spv::Op::OpLoad, inst.opcode());
               ASSERT_EQ(12, inst.result_id());
               break;
             case 3:
-              ASSERT_EQ(SpvOpStore, inst.opcode());
+              ASSERT_EQ(spv::Op::OpStore, inst.opcode());
               break;
             case 4:
-              ASSERT_EQ(SpvOpReturn, inst.opcode());
+              ASSERT_EQ(spv::Op::OpReturn, inst.opcode());
               break;
             default:
               FAIL();
@@ -250,11 +250,11 @@
         first_block = false;
         for (auto& inst : block) {
           switch (inst.opcode()) {
-            case SpvOpVariable:
-            case SpvOpNot:
-            case SpvOpReturn:
-            case SpvOpReturnValue:
-            case SpvOpFunctionCall:
+            case spv::Op::OpVariable:
+            case spv::Op::OpNot:
+            case spv::Op::OpReturn:
+            case spv::Op::OpReturnValue:
+            case spv::Op::OpFunctionCall:
               // These are the only instructions we expect to see.
               break;
             default:
@@ -362,7 +362,7 @@
     uint32_t copy_object_count = 0;
     temp_ir_context->module()->ForEachInst(
         [&copy_object_count](opt::Instruction* inst) {
-          if (inst->opcode() == SpvOpCopyObject) {
+          if (inst->opcode() == spv::Op::OpCopyObject) {
             copy_object_count++;
           }
         });
diff --git a/test/fuzz/transformation_access_chain_test.cpp b/test/fuzz/transformation_access_chain_test.cpp
index bddcf5f..fa19aa5 100644
--- a/test/fuzz/transformation_access_chain_test.cpp
+++ b/test/fuzz/transformation_access_chain_test.cpp
@@ -129,7 +129,7 @@
 
   // Check the case where the index type is not a 32-bit integer.
   TransformationAccessChain invalid_index_example1(
-      101, 28, {29}, MakeInstructionDescriptor(42, SpvOpReturn, 0));
+      101, 28, {29}, MakeInstructionDescriptor(42, spv::Op::OpReturn, 0));
 
   // Since the index  is not a 32-bit integer type but a 32-bit float type,
   // ValidIndexComposite should return false and thus the transformation is not
@@ -138,86 +138,95 @@
                                                    transformation_context));
 
   // Bad: id is not fresh
-  ASSERT_FALSE(TransformationAccessChain(
-                   43, 43, {80}, MakeInstructionDescriptor(24, SpvOpLoad, 0))
-                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationAccessChain(
+          43, 43, {80}, MakeInstructionDescriptor(24, spv::Op::OpLoad, 0))
+          .IsApplicable(context.get(), transformation_context));
 
   // Bad: pointer id does not exist
-  ASSERT_FALSE(TransformationAccessChain(
-                   100, 1000, {80}, MakeInstructionDescriptor(24, SpvOpLoad, 0))
-                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationAccessChain(
+          100, 1000, {80}, MakeInstructionDescriptor(24, spv::Op::OpLoad, 0))
+          .IsApplicable(context.get(), transformation_context));
 
   // Bad: pointer id is not a type
-  ASSERT_FALSE(TransformationAccessChain(
-                   100, 5, {80}, MakeInstructionDescriptor(24, SpvOpLoad, 0))
-                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationAccessChain(
+          100, 5, {80}, MakeInstructionDescriptor(24, spv::Op::OpLoad, 0))
+          .IsApplicable(context.get(), transformation_context));
 
   // Bad: pointer id is not a pointer
-  ASSERT_FALSE(TransformationAccessChain(
-                   100, 23, {80}, MakeInstructionDescriptor(24, SpvOpLoad, 0))
-                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationAccessChain(
+          100, 23, {80}, MakeInstructionDescriptor(24, spv::Op::OpLoad, 0))
+          .IsApplicable(context.get(), transformation_context));
 
   // Bad: index id does not exist
-  ASSERT_FALSE(TransformationAccessChain(
-                   100, 43, {1000}, MakeInstructionDescriptor(24, SpvOpLoad, 0))
-                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationAccessChain(
+          100, 43, {1000}, MakeInstructionDescriptor(24, spv::Op::OpLoad, 0))
+          .IsApplicable(context.get(), transformation_context));
 
   // Bad: index id is not a constant and the pointer refers to a struct
-  ASSERT_FALSE(TransformationAccessChain(
-                   100, 43, {24}, MakeInstructionDescriptor(25, SpvOpIAdd, 0))
-                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationAccessChain(
+          100, 43, {24}, MakeInstructionDescriptor(25, spv::Op::OpIAdd, 0))
+          .IsApplicable(context.get(), transformation_context));
 
   // Bad: too many indices
-  ASSERT_FALSE(
-      TransformationAccessChain(100, 43, {80, 80, 80},
-                                MakeInstructionDescriptor(24, SpvOpLoad, 0))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationAccessChain(
+                   100, 43, {80, 80, 80},
+                   MakeInstructionDescriptor(24, spv::Op::OpLoad, 0))
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: index id is out of bounds when accessing a struct
   ASSERT_FALSE(
-      TransformationAccessChain(100, 43, {83, 80},
-                                MakeInstructionDescriptor(24, SpvOpLoad, 0))
+      TransformationAccessChain(
+          100, 43, {83, 80}, MakeInstructionDescriptor(24, spv::Op::OpLoad, 0))
           .IsApplicable(context.get(), transformation_context));
 
   // Bad: attempt to insert before variable
-  ASSERT_FALSE(TransformationAccessChain(
-                   100, 34, {}, MakeInstructionDescriptor(36, SpvOpVariable, 0))
-                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationAccessChain(
+          100, 34, {}, MakeInstructionDescriptor(36, spv::Op::OpVariable, 0))
+          .IsApplicable(context.get(), transformation_context));
 
   // Bad: OpTypeBool must be present in the module to clamp an index
   ASSERT_FALSE(
-      TransformationAccessChain(100, 36, {80, 81},
-                                MakeInstructionDescriptor(37, SpvOpStore, 0))
+      TransformationAccessChain(
+          100, 36, {80, 81}, MakeInstructionDescriptor(37, spv::Op::OpStore, 0))
           .IsApplicable(context.get(), transformation_context));
 
   // Bad: pointer not available
-  ASSERT_FALSE(
-      TransformationAccessChain(
-          100, 43, {80}, MakeInstructionDescriptor(21, SpvOpAccessChain, 0))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationAccessChain(
+                   100, 43, {80},
+                   MakeInstructionDescriptor(21, spv::Op::OpAccessChain, 0))
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: instruction descriptor does not identify anything
-  ASSERT_FALSE(TransformationAccessChain(
-                   100, 43, {80}, MakeInstructionDescriptor(24, SpvOpLoad, 100))
-                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationAccessChain(
+          100, 43, {80}, MakeInstructionDescriptor(24, spv::Op::OpLoad, 100))
+          .IsApplicable(context.get(), transformation_context));
 
 #ifndef NDEBUG
   // Bad: pointer is null
   ASSERT_DEATH(
-      TransformationAccessChain(100, 46, {80},
-                                MakeInstructionDescriptor(24, SpvOpLoad, 0))
+      TransformationAccessChain(
+          100, 46, {80}, MakeInstructionDescriptor(24, spv::Op::OpLoad, 0))
           .IsApplicable(context.get(), transformation_context),
       "Access chains should not be created from null/undefined pointers");
 #endif
 
   // Bad: pointer to result type does not exist
-  ASSERT_FALSE(TransformationAccessChain(
-                   100, 52, {0}, MakeInstructionDescriptor(24, SpvOpLoad, 0))
-                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationAccessChain(
+          100, 52, {0}, MakeInstructionDescriptor(24, spv::Op::OpLoad, 0))
+          .IsApplicable(context.get(), transformation_context));
 
   {
     TransformationAccessChain transformation(
-        100, 43, {80}, MakeInstructionDescriptor(24, SpvOpLoad, 0));
+        100, 43, {80}, MakeInstructionDescriptor(24, spv::Op::OpLoad, 0));
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
@@ -230,7 +239,7 @@
 
   {
     TransformationAccessChain transformation(
-        101, 28, {81}, MakeInstructionDescriptor(42, SpvOpReturn, 0));
+        101, 28, {81}, MakeInstructionDescriptor(42, spv::Op::OpReturn, 0));
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
@@ -243,7 +252,7 @@
 
   {
     TransformationAccessChain transformation(
-        102, 44, {}, MakeInstructionDescriptor(44, SpvOpStore, 0));
+        102, 44, {}, MakeInstructionDescriptor(44, spv::Op::OpStore, 0));
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
@@ -256,7 +265,8 @@
 
   {
     TransformationAccessChain transformation(
-        103, 13, {80}, MakeInstructionDescriptor(21, SpvOpAccessChain, 0));
+        103, 13, {80},
+        MakeInstructionDescriptor(21, spv::Op::OpAccessChain, 0));
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
@@ -269,7 +279,7 @@
 
   {
     TransformationAccessChain transformation(
-        104, 34, {}, MakeInstructionDescriptor(44, SpvOpStore, 1));
+        104, 34, {}, MakeInstructionDescriptor(44, spv::Op::OpStore, 1));
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
@@ -282,7 +292,7 @@
 
   {
     TransformationAccessChain transformation(
-        105, 38, {}, MakeInstructionDescriptor(40, SpvOpFunctionCall, 0));
+        105, 38, {}, MakeInstructionDescriptor(40, spv::Op::OpFunctionCall, 0));
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
@@ -295,7 +305,7 @@
 
   {
     TransformationAccessChain transformation(
-        106, 14, {}, MakeInstructionDescriptor(24, SpvOpLoad, 0));
+        106, 14, {}, MakeInstructionDescriptor(24, spv::Op::OpLoad, 0));
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
@@ -309,7 +319,7 @@
     // Check the case where the access chain's base pointer has the irrelevant
     // pointee fact; the resulting access chain should inherit this fact.
     TransformationAccessChain transformation(
-        107, 54, {}, MakeInstructionDescriptor(24, SpvOpLoad, 0));
+        107, 54, {}, MakeInstructionDescriptor(24, spv::Op::OpLoad, 0));
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
@@ -440,9 +450,10 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
   // Bad: %9 is a pointer to a struct, but %20 is not a constant.
-  ASSERT_FALSE(TransformationAccessChain(
-                   100, 9, {20}, MakeInstructionDescriptor(9, SpvOpReturn, 0))
-                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationAccessChain(
+          100, 9, {20}, MakeInstructionDescriptor(9, spv::Op::OpReturn, 0))
+          .IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationAccessChainTest, IsomorphicStructs) {
@@ -478,7 +489,7 @@
       MakeUnique<FactManager>(context.get()), validator_options);
   {
     TransformationAccessChain transformation(
-        100, 11, {}, MakeInstructionDescriptor(5, SpvOpReturn, 0));
+        100, 11, {}, MakeInstructionDescriptor(5, spv::Op::OpReturn, 0));
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
@@ -488,7 +499,7 @@
   }
   {
     TransformationAccessChain transformation(
-        101, 12, {}, MakeInstructionDescriptor(5, SpvOpReturn, 0));
+        101, 12, {}, MakeInstructionDescriptor(5, spv::Op::OpReturn, 0));
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
@@ -587,51 +598,56 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
   // Bad: no ids given for clamping
-  ASSERT_FALSE(TransformationAccessChain(
-                   100, 29, {17}, MakeInstructionDescriptor(36, SpvOpLoad, 0))
-                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationAccessChain(
+          100, 29, {17}, MakeInstructionDescriptor(36, spv::Op::OpLoad, 0))
+          .IsApplicable(context.get(), transformation_context));
 
   // Bad: an id given for clamping is not fresh
   ASSERT_FALSE(TransformationAccessChain(
-                   100, 29, {17}, MakeInstructionDescriptor(36, SpvOpLoad, 0),
+                   100, 29, {17},
+                   MakeInstructionDescriptor(36, spv::Op::OpLoad, 0),
                    {{46, 201}})
                    .IsApplicable(context.get(), transformation_context));
 
   // Bad: an id given for clamping is not fresh
   ASSERT_FALSE(TransformationAccessChain(
-                   100, 29, {17}, MakeInstructionDescriptor(36, SpvOpLoad, 0),
+                   100, 29, {17},
+                   MakeInstructionDescriptor(36, spv::Op::OpLoad, 0),
                    {{200, 46}})
                    .IsApplicable(context.get(), transformation_context));
 
   // Bad: an id given for clamping is the same as the id for the access chain
   ASSERT_FALSE(TransformationAccessChain(
-                   100, 29, {17}, MakeInstructionDescriptor(36, SpvOpLoad, 0),
+                   100, 29, {17},
+                   MakeInstructionDescriptor(36, spv::Op::OpLoad, 0),
                    {{100, 201}})
                    .IsApplicable(context.get(), transformation_context));
 
   // Bad: the fresh ids given are not distinct
   ASSERT_FALSE(TransformationAccessChain(
-                   100, 29, {17}, MakeInstructionDescriptor(36, SpvOpLoad, 0),
+                   100, 29, {17},
+                   MakeInstructionDescriptor(36, spv::Op::OpLoad, 0),
                    {{200, 200}})
                    .IsApplicable(context.get(), transformation_context));
 
   // Bad: not enough ids given for clamping (2 pairs needed)
-  ASSERT_FALSE(
-      TransformationAccessChain(104, 34, {45, 10, 46},
-                                MakeInstructionDescriptor(46, SpvOpReturn, 0),
-                                {{208, 209}, {209, 211}})
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationAccessChain(
+                   104, 34, {45, 10, 46},
+                   MakeInstructionDescriptor(46, spv::Op::OpReturn, 0),
+                   {{208, 209}, {209, 211}})
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: the fresh ids given are not distinct
-  ASSERT_FALSE(
-      TransformationAccessChain(104, 34, {45, 10, 46},
-                                MakeInstructionDescriptor(46, SpvOpReturn, 0),
-                                {{208, 209}, {209, 211}})
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationAccessChain(
+                   104, 34, {45, 10, 46},
+                   MakeInstructionDescriptor(46, spv::Op::OpReturn, 0),
+                   {{208, 209}, {209, 211}})
+                   .IsApplicable(context.get(), transformation_context));
 
   {
     TransformationAccessChain transformation(
-        100, 29, {17}, MakeInstructionDescriptor(36, SpvOpLoad, 0),
+        100, 29, {17}, MakeInstructionDescriptor(36, spv::Op::OpLoad, 0),
         {{200, 201}});
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
@@ -643,7 +659,7 @@
 
   {
     TransformationAccessChain transformation(
-        101, 29, {36}, MakeInstructionDescriptor(38, SpvOpLoad, 0),
+        101, 29, {36}, MakeInstructionDescriptor(38, spv::Op::OpLoad, 0),
         {{202, 203}});
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
@@ -655,7 +671,7 @@
 
   {
     TransformationAccessChain transformation(
-        102, 32, {10, 40}, MakeInstructionDescriptor(42, SpvOpLoad, 0),
+        102, 32, {10, 40}, MakeInstructionDescriptor(42, spv::Op::OpLoad, 0),
         {{204, 205}});
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
@@ -667,7 +683,7 @@
 
   {
     TransformationAccessChain transformation(
-        103, 34, {11}, MakeInstructionDescriptor(45, SpvOpLoad, 0),
+        103, 34, {11}, MakeInstructionDescriptor(45, spv::Op::OpLoad, 0),
         {{206, 207}});
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
@@ -679,7 +695,8 @@
 
   {
     TransformationAccessChain transformation(
-        104, 34, {45, 10, 46}, MakeInstructionDescriptor(46, SpvOpReturn, 0),
+        104, 34, {45, 10, 46},
+        MakeInstructionDescriptor(46, spv::Op::OpReturn, 0),
         {{208, 209}, {210, 211}});
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
diff --git a/test/fuzz/transformation_add_constant_boolean_test.cpp b/test/fuzz/transformation_add_constant_boolean_test.cpp
index bd8d91c..bb8817e 100644
--- a/test/fuzz/transformation_add_constant_boolean_test.cpp
+++ b/test/fuzz/transformation_add_constant_boolean_test.cpp
@@ -72,7 +72,8 @@
   ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(7));
   ASSERT_EQ(nullptr, context->get_constant_mgr()->FindDeclaredConstant(7));
   ApplyAndCheckFreshIds(add_true, context.get(), &transformation_context);
-  ASSERT_EQ(SpvOpConstantTrue, context->get_def_use_mgr()->GetDef(7)->opcode());
+  ASSERT_EQ(spv::Op::OpConstantTrue,
+            context->get_def_use_mgr()->GetDef(7)->opcode());
   ASSERT_TRUE(context->get_constant_mgr()
                   ->FindDeclaredConstant(7)
                   ->AsBoolConstant()
diff --git a/test/fuzz/transformation_add_constant_composite_test.cpp b/test/fuzz/transformation_add_constant_composite_test.cpp
index e5cbeec..193aa0a 100644
--- a/test/fuzz/transformation_add_constant_composite_test.cpp
+++ b/test/fuzz/transformation_add_constant_composite_test.cpp
@@ -91,7 +91,7 @@
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
                           &transformation_context);
-    ASSERT_EQ(SpvOpConstantComposite,
+    ASSERT_EQ(spv::Op::OpConstantComposite,
               context->get_def_use_mgr()->GetDef(100)->opcode());
     ASSERT_EQ(0.0F, context->get_constant_mgr()
                         ->FindDeclaredConstant(100)
diff --git a/test/fuzz/transformation_add_constant_null_test.cpp b/test/fuzz/transformation_add_constant_null_test.cpp
index 1553e9f..44e92f8 100644
--- a/test/fuzz/transformation_add_constant_null_test.cpp
+++ b/test/fuzz/transformation_add_constant_null_test.cpp
@@ -87,7 +87,7 @@
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
                           &transformation_context);
-    ASSERT_EQ(SpvOpConstantNull,
+    ASSERT_EQ(spv::Op::OpConstantNull,
               context->get_def_use_mgr()->GetDef(100)->opcode());
     ASSERT_EQ(
         0.0F,
diff --git a/test/fuzz/transformation_add_constant_scalar_test.cpp b/test/fuzz/transformation_add_constant_scalar_test.cpp
index 00c0541..8c742a4 100644
--- a/test/fuzz/transformation_add_constant_scalar_test.cpp
+++ b/test/fuzz/transformation_add_constant_scalar_test.cpp
@@ -179,7 +179,8 @@
   ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(19));
   ASSERT_EQ(nullptr, context->get_constant_mgr()->FindDeclaredConstant(19));
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
-  ASSERT_EQ(SpvOpConstant, context->get_def_use_mgr()->GetDef(19)->opcode());
+  ASSERT_EQ(spv::Op::OpConstant,
+            context->get_def_use_mgr()->GetDef(19)->opcode());
   ASSERT_EQ(4, context->get_constant_mgr()->FindDeclaredConstant(19)->GetU32());
   auto* constant_instruction = context->get_def_use_mgr()->GetDef(19);
   EXPECT_EQ(constant_instruction->NumInOperands(), 1);
diff --git a/test/fuzz/transformation_add_copy_memory_test.cpp b/test/fuzz/transformation_add_copy_memory_test.cpp
index ff8ac72..29a936c 100644
--- a/test/fuzz/transformation_add_copy_memory_test.cpp
+++ b/test/fuzz/transformation_add_copy_memory_test.cpp
@@ -148,89 +148,89 @@
       MakeUnique<FactManager>(context.get()), validator_options);
   // Target id is not fresh (59).
   ASSERT_FALSE(TransformationAddCopyMemory(
-                   MakeInstructionDescriptor(27, SpvOpFunctionCall, 0), 59, 19,
-                   SpvStorageClassPrivate, 20)
+                   MakeInstructionDescriptor(27, spv::Op::OpFunctionCall, 0),
+                   59, 19, spv::StorageClass::Private, 20)
                    .IsApplicable(context.get(), transformation_context));
 
   // Instruction descriptor is invalid (id 90 is undefined).
   ASSERT_FALSE(TransformationAddCopyMemory(
-                   MakeInstructionDescriptor(90, SpvOpVariable, 0), 90, 19,
-                   SpvStorageClassPrivate, 20)
+                   MakeInstructionDescriptor(90, spv::Op::OpVariable, 0), 90,
+                   19, spv::StorageClass::Private, 20)
                    .IsApplicable(context.get(), transformation_context));
 
   // Cannot insert OpCopyMemory before OpPhi.
-  ASSERT_FALSE(
-      TransformationAddCopyMemory(MakeInstructionDescriptor(75, SpvOpPhi, 0),
-                                  90, 19, SpvStorageClassPrivate, 20)
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationAddCopyMemory(
+                   MakeInstructionDescriptor(75, spv::Op::OpPhi, 0), 90, 19,
+                   spv::StorageClass::Private, 20)
+                   .IsApplicable(context.get(), transformation_context));
 
   // Source instruction is invalid.
   ASSERT_FALSE(TransformationAddCopyMemory(
-                   MakeInstructionDescriptor(27, SpvOpFunctionCall, 0), 90, 76,
-                   SpvStorageClassPrivate, 0)
+                   MakeInstructionDescriptor(27, spv::Op::OpFunctionCall, 0),
+                   90, 76, spv::StorageClass::Private, 0)
                    .IsApplicable(context.get(), transformation_context));
 
   // Source instruction's type doesn't exist.
   ASSERT_FALSE(TransformationAddCopyMemory(
-                   MakeInstructionDescriptor(27, SpvOpFunctionCall, 0), 90, 5,
-                   SpvStorageClassPrivate, 0)
+                   MakeInstructionDescriptor(27, spv::Op::OpFunctionCall, 0),
+                   90, 5, spv::StorageClass::Private, 0)
                    .IsApplicable(context.get(), transformation_context));
 
   // Source instruction's type is invalid.
-  ASSERT_FALSE(
-      TransformationAddCopyMemory(MakeInstructionDescriptor(41, SpvOpLoad, 0),
-                                  90, 40, SpvStorageClassPrivate, 0)
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationAddCopyMemory(
+                   MakeInstructionDescriptor(41, spv::Op::OpLoad, 0), 90, 40,
+                   spv::StorageClass::Private, 0)
+                   .IsApplicable(context.get(), transformation_context));
 
   // Source instruction is OpConstantNull.
-  ASSERT_FALSE(
-      TransformationAddCopyMemory(MakeInstructionDescriptor(41, SpvOpLoad, 0),
-                                  90, 88, SpvStorageClassPrivate, 0)
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationAddCopyMemory(
+                   MakeInstructionDescriptor(41, spv::Op::OpLoad, 0), 90, 88,
+                   spv::StorageClass::Private, 0)
+                   .IsApplicable(context.get(), transformation_context));
 
   // Storage class is invalid.
   ASSERT_FALSE(TransformationAddCopyMemory(
-                   MakeInstructionDescriptor(27, SpvOpFunctionCall, 0), 90, 19,
-                   SpvStorageClassWorkgroup, 20)
+                   MakeInstructionDescriptor(27, spv::Op::OpFunctionCall, 0),
+                   90, 19, spv::StorageClass::Workgroup, 20)
                    .IsApplicable(context.get(), transformation_context));
 
   // Initializer is 0.
   ASSERT_FALSE(TransformationAddCopyMemory(
-                   MakeInstructionDescriptor(27, SpvOpFunctionCall, 0), 90, 19,
-                   SpvStorageClassPrivate, 0)
+                   MakeInstructionDescriptor(27, spv::Op::OpFunctionCall, 0),
+                   90, 19, spv::StorageClass::Private, 0)
                    .IsApplicable(context.get(), transformation_context));
 
   // Initializer has wrong type.
   ASSERT_FALSE(TransformationAddCopyMemory(
-                   MakeInstructionDescriptor(27, SpvOpFunctionCall, 0), 90, 19,
-                   SpvStorageClassPrivate, 25)
+                   MakeInstructionDescriptor(27, spv::Op::OpFunctionCall, 0),
+                   90, 19, spv::StorageClass::Private, 25)
                    .IsApplicable(context.get(), transformation_context));
 
   // Source and target instructions are in different functions.
-  ASSERT_FALSE(
-      TransformationAddCopyMemory(MakeInstructionDescriptor(13, SpvOpLoad, 0),
-                                  90, 19, SpvStorageClassPrivate, 20)
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationAddCopyMemory(
+                   MakeInstructionDescriptor(13, spv::Op::OpLoad, 0), 90, 19,
+                   spv::StorageClass::Private, 20)
+                   .IsApplicable(context.get(), transformation_context));
 
   // Source instruction doesn't dominate the target instruction.
   ASSERT_FALSE(TransformationAddCopyMemory(
-                   MakeInstructionDescriptor(77, SpvOpLogicalEqual, 0), 90, 89,
-                   SpvStorageClassPrivate, 20)
+                   MakeInstructionDescriptor(77, spv::Op::OpLogicalEqual, 0),
+                   90, 89, spv::StorageClass::Private, 20)
                    .IsApplicable(context.get(), transformation_context));
 
   // Source and target instructions are the same.
   ASSERT_FALSE(TransformationAddCopyMemory(
-                   MakeInstructionDescriptor(19, SpvOpVariable, 0), 90, 19,
-                   SpvStorageClassPrivate, 20)
+                   MakeInstructionDescriptor(19, spv::Op::OpVariable, 0), 90,
+                   19, spv::StorageClass::Private, 20)
                    .IsApplicable(context.get(), transformation_context));
 
   // Correct transformations.
   uint32_t fresh_id = 90;
-  auto descriptor = MakeInstructionDescriptor(27, SpvOpFunctionCall, 0);
+  auto descriptor = MakeInstructionDescriptor(27, spv::Op::OpFunctionCall, 0);
   std::vector<uint32_t> source_ids = {19, 23, 26, 30, 35, 39, 68, 86};
   std::vector<uint32_t> initializers = {20, 24, 25, 25, 36, 84, 85, 20};
-  std::vector<SpvStorageClass> storage_classes = {SpvStorageClassPrivate,
-                                                  SpvStorageClassFunction};
+  std::vector<spv::StorageClass> storage_classes = {
+      spv::StorageClass::Private, spv::StorageClass::Function};
   for (size_t i = 0, n = source_ids.size(); i < n; ++i) {
     TransformationAddCopyMemory transformation(
         descriptor, fresh_id, source_ids[i],
@@ -420,10 +420,10 @@
                                                kConsoleMessageConsumer));
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
-  ASSERT_FALSE(
-      TransformationAddCopyMemory(MakeInstructionDescriptor(5, SpvOpReturn, 0),
-                                  100, 9, SpvStorageClassPrivate, 50)
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationAddCopyMemory(
+                   MakeInstructionDescriptor(5, spv::Op::OpReturn, 0), 100, 9,
+                   spv::StorageClass::Private, 50)
+                   .IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationAddCopyMemoryTest, DisallowBlockDecoration) {
@@ -466,12 +466,12 @@
                                                kConsoleMessageConsumer));
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
-  ASSERT_FALSE(
-      TransformationAddCopyMemory(MakeInstructionDescriptor(5, SpvOpReturn, 0),
-                                  100, 9, SpvStorageClassPrivate, 50)
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationAddCopyMemory(
+                   MakeInstructionDescriptor(5, spv::Op::OpReturn, 0), 100, 9,
+                   spv::StorageClass::Private, 50)
+                   .IsApplicable(context.get(), transformation_context));
 }
 
 }  // namespace
 }  // namespace fuzz
-}  // namespace spvtools
\ No newline at end of file
+}  // namespace spvtools
diff --git a/test/fuzz/transformation_add_dead_block_test.cpp b/test/fuzz/transformation_add_dead_block_test.cpp
index 3c9e6b4..534ad69 100644
--- a/test/fuzz/transformation_add_dead_block_test.cpp
+++ b/test/fuzz/transformation_add_dead_block_test.cpp
@@ -295,11 +295,16 @@
                OpBranch %8
           %8 = OpLabel
                OpLoopMerge %12 %11 None
+               OpBranch %13
+         %13 = OpLabel
+               OpSelectionMerge %14 None
                OpBranchConditional %5 %9 %10
           %9 = OpLabel
                OpBranch %11
          %10 = OpLabel
                OpBranch %12
+         %14 = OpLabel
+               OpUnreachable
          %11 = OpLabel
                OpBranch %8
          %12 = OpLabel
diff --git a/test/fuzz/transformation_add_dead_break_test.cpp b/test/fuzz/transformation_add_dead_break_test.cpp
index 5302d8a..fd46c96 100644
--- a/test/fuzz/transformation_add_dead_break_test.cpp
+++ b/test/fuzz/transformation_add_dead_break_test.cpp
@@ -2743,6 +2743,9 @@
                OpBranch %100
         %100 = OpLabel
                OpLoopMerge %101 %104 None
+               OpBranch %105
+        %105 = OpLabel
+               OpSelectionMerge %106 None
                OpBranchConditional %11 %102 %103
         %103 = OpLabel
         %200 = OpCopyObject %10 %11
@@ -2752,6 +2755,8 @@
                OpReturn
         %102 = OpLabel
                OpBranch %103
+        %106 = OpLabel
+               OpUnreachable
         %104 = OpLabel
                OpBranch %100
                OpFunctionEnd
@@ -2791,12 +2796,17 @@
                OpBranch %100
         %100 = OpLabel
                OpLoopMerge %101 %104 None
+               OpBranch %105
+        %105 = OpLabel
+               OpSelectionMerge %106 None
                OpBranchConditional %11 %102 %103
         %103 = OpLabel
         %200 = OpCopyObject %10 %11
                OpBranch %101
         %102 = OpLabel
                OpBranch %103
+        %106 = OpLabel
+               OpUnreachable
         %101 = OpLabel
         %201 = OpCopyObject %10 %200
                OpReturn
diff --git a/test/fuzz/transformation_add_early_terminator_wrapper_test.cpp b/test/fuzz/transformation_add_early_terminator_wrapper_test.cpp
index 8239e21..8cb04a9 100644
--- a/test/fuzz/transformation_add_early_terminator_wrapper_test.cpp
+++ b/test/fuzz/transformation_add_early_terminator_wrapper_test.cpp
@@ -40,8 +40,9 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
 
-  ASSERT_FALSE(TransformationAddEarlyTerminatorWrapper(100, 101, SpvOpKill)
-                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationAddEarlyTerminatorWrapper(100, 101, spv::Op::OpKill)
+          .IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationAddEarlyTerminatorWrapperTest, NoVoidFunctionType) {
@@ -63,8 +64,9 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
 
-  ASSERT_FALSE(TransformationAddEarlyTerminatorWrapper(100, 101, SpvOpKill)
-                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationAddEarlyTerminatorWrapper(100, 101, spv::Op::OpKill)
+          .IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationAddEarlyTerminatorWrapperTest, BasicTest) {
@@ -93,25 +95,27 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
 
-  ASSERT_FALSE(TransformationAddEarlyTerminatorWrapper(2, 101, SpvOpKill)
+  ASSERT_FALSE(TransformationAddEarlyTerminatorWrapper(2, 101, spv::Op::OpKill)
                    .IsApplicable(context.get(), transformation_context));
-  ASSERT_FALSE(TransformationAddEarlyTerminatorWrapper(100, 4, SpvOpKill)
+  ASSERT_FALSE(TransformationAddEarlyTerminatorWrapper(100, 4, spv::Op::OpKill)
                    .IsApplicable(context.get(), transformation_context));
-  ASSERT_FALSE(TransformationAddEarlyTerminatorWrapper(100, 100, SpvOpKill)
-                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationAddEarlyTerminatorWrapper(100, 100, spv::Op::OpKill)
+          .IsApplicable(context.get(), transformation_context));
 
 #ifndef NDEBUG
-  ASSERT_DEATH(TransformationAddEarlyTerminatorWrapper(100, 101, SpvOpReturn)
-                   .IsApplicable(context.get(), transformation_context),
-               "Invalid opcode.");
+  ASSERT_DEATH(
+      TransformationAddEarlyTerminatorWrapper(100, 101, spv::Op::OpReturn)
+          .IsApplicable(context.get(), transformation_context),
+      "Invalid opcode.");
 #endif
 
   auto transformation1 =
-      TransformationAddEarlyTerminatorWrapper(100, 101, SpvOpKill);
+      TransformationAddEarlyTerminatorWrapper(100, 101, spv::Op::OpKill);
   auto transformation2 =
-      TransformationAddEarlyTerminatorWrapper(102, 103, SpvOpUnreachable);
+      TransformationAddEarlyTerminatorWrapper(102, 103, spv::Op::OpUnreachable);
   auto transformation3 = TransformationAddEarlyTerminatorWrapper(
-      104, 105, SpvOpTerminateInvocation);
+      104, 105, spv::Op::OpTerminateInvocation);
 
   ASSERT_TRUE(
       transformation1.IsApplicable(context.get(), transformation_context));
diff --git a/test/fuzz/transformation_add_function_test.cpp b/test/fuzz/transformation_add_function_test.cpp
index d55fb93..65ae2f2 100644
--- a/test/fuzz/transformation_add_function_test.cpp
+++ b/test/fuzz/transformation_add_function_test.cpp
@@ -84,7 +84,7 @@
                            &found_non_irrelevant_parameter](
                               opt::Instruction* inst) {
       if (context->get_def_use_mgr()->GetDef(inst->type_id())->opcode() ==
-              SpvOpTypePointer &&
+              spv::Op::OpTypePointer &&
           !transformation_context.GetFactManager()->PointeeValueIsIrrelevant(
               inst->result_id())) {
         found_non_irrelevant_parameter = true;
@@ -96,7 +96,7 @@
     }
     // Look through the instructions in the function's first block.
     for (auto& inst : *function.begin()) {
-      if (inst.opcode() != SpvOpVariable) {
+      if (inst.opcode() != spv::Op::OpVariable) {
         // We have found a non-variable instruction; this means we have gotten
         // past all variables, so we are done.
         return true;
@@ -154,73 +154,84 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
   TransformationAddFunction transformation1(std::vector<protobufs::Instruction>(
-      {MakeInstructionMessage(
-           SpvOpFunction, 8, 13,
-           {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}},
-            {SPV_OPERAND_TYPE_ID, {10}}}),
-       MakeInstructionMessage(SpvOpFunctionParameter, 7, 11, {}),
-       MakeInstructionMessage(SpvOpFunctionParameter, 9, 12, {}),
-       MakeInstructionMessage(SpvOpLabel, 0, 14, {}),
+      {MakeInstructionMessage(spv::Op::OpFunction, 8, 13,
+                              {{SPV_OPERAND_TYPE_FUNCTION_CONTROL,
+                                {uint32_t(spv::FunctionControlMask::MaskNone)}},
+                               {SPV_OPERAND_TYPE_ID, {10}}}),
+       MakeInstructionMessage(spv::Op::OpFunctionParameter, 7, 11, {}),
+       MakeInstructionMessage(spv::Op::OpFunctionParameter, 9, 12, {}),
+       MakeInstructionMessage(spv::Op::OpLabel, 0, 14, {}),
+       MakeInstructionMessage(spv::Op::OpVariable, 9, 17,
+                              {{SPV_OPERAND_TYPE_STORAGE_CLASS,
+                                {uint32_t(spv::StorageClass::Function)}}}),
+       MakeInstructionMessage(spv::Op::OpVariable, 7, 19,
+                              {{SPV_OPERAND_TYPE_STORAGE_CLASS,
+                                {uint32_t(spv::StorageClass::Function)}}}),
        MakeInstructionMessage(
-           SpvOpVariable, 9, 17,
-           {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}),
-       MakeInstructionMessage(
-           SpvOpVariable, 7, 19,
-           {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}),
-       MakeInstructionMessage(
-           SpvOpStore, 0, 0,
+           spv::Op::OpStore, 0, 0,
            {{SPV_OPERAND_TYPE_ID, {17}}, {SPV_OPERAND_TYPE_ID, {18}}}),
        MakeInstructionMessage(
-           SpvOpStore, 0, 0,
+           spv::Op::OpStore, 0, 0,
            {{SPV_OPERAND_TYPE_ID, {19}}, {SPV_OPERAND_TYPE_ID, {20}}}),
-       MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {21}}}),
-       MakeInstructionMessage(SpvOpLabel, 0, 21, {}),
+       MakeInstructionMessage(spv::Op::OpBranch, 0, 0,
+                              {{SPV_OPERAND_TYPE_ID, {21}}}),
+       MakeInstructionMessage(spv::Op::OpLabel, 0, 21, {}),
+       MakeInstructionMessage(spv::Op::OpLoopMerge, 0, 0,
+                              {{SPV_OPERAND_TYPE_ID, {23}},
+                               {SPV_OPERAND_TYPE_ID, {24}},
+                               {SPV_OPERAND_TYPE_LOOP_CONTROL,
+                                {uint32_t(spv::LoopControlMask::MaskNone)}}}),
+       MakeInstructionMessage(spv::Op::OpBranch, 0, 0,
+                              {{SPV_OPERAND_TYPE_ID, {25}}}),
+       MakeInstructionMessage(spv::Op::OpLabel, 0, 25, {}),
+       MakeInstructionMessage(spv::Op::OpLoad, 6, 26,
+                              {{SPV_OPERAND_TYPE_ID, {19}}}),
+       MakeInstructionMessage(spv::Op::OpLoad, 6, 27,
+                              {{SPV_OPERAND_TYPE_ID, {11}}}),
        MakeInstructionMessage(
-           SpvOpLoopMerge, 0, 0,
-           {{SPV_OPERAND_TYPE_ID, {23}},
-            {SPV_OPERAND_TYPE_ID, {24}},
-            {SPV_OPERAND_TYPE_LOOP_CONTROL, {SpvLoopControlMaskNone}}}),
-       MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {25}}}),
-       MakeInstructionMessage(SpvOpLabel, 0, 25, {}),
-       MakeInstructionMessage(SpvOpLoad, 6, 26, {{SPV_OPERAND_TYPE_ID, {19}}}),
-       MakeInstructionMessage(SpvOpLoad, 6, 27, {{SPV_OPERAND_TYPE_ID, {11}}}),
-       MakeInstructionMessage(
-           SpvOpSLessThan, 28, 29,
+           spv::Op::OpSLessThan, 28, 29,
            {{SPV_OPERAND_TYPE_ID, {26}}, {SPV_OPERAND_TYPE_ID, {27}}}),
-       MakeInstructionMessage(SpvOpBranchConditional, 0, 0,
+       MakeInstructionMessage(spv::Op::OpBranchConditional, 0, 0,
                               {{SPV_OPERAND_TYPE_ID, {29}},
                                {SPV_OPERAND_TYPE_ID, {22}},
                                {SPV_OPERAND_TYPE_ID, {23}}}),
-       MakeInstructionMessage(SpvOpLabel, 0, 22, {}),
-       MakeInstructionMessage(SpvOpLoad, 8, 30, {{SPV_OPERAND_TYPE_ID, {12}}}),
-       MakeInstructionMessage(SpvOpLoad, 6, 31, {{SPV_OPERAND_TYPE_ID, {19}}}),
-       MakeInstructionMessage(SpvOpConvertSToF, 8, 32,
+       MakeInstructionMessage(spv::Op::OpLabel, 0, 22, {}),
+       MakeInstructionMessage(spv::Op::OpLoad, 8, 30,
+                              {{SPV_OPERAND_TYPE_ID, {12}}}),
+       MakeInstructionMessage(spv::Op::OpLoad, 6, 31,
+                              {{SPV_OPERAND_TYPE_ID, {19}}}),
+       MakeInstructionMessage(spv::Op::OpConvertSToF, 8, 32,
                               {{SPV_OPERAND_TYPE_ID, {31}}}),
        MakeInstructionMessage(
-           SpvOpFMul, 8, 33,
+           spv::Op::OpFMul, 8, 33,
            {{SPV_OPERAND_TYPE_ID, {30}}, {SPV_OPERAND_TYPE_ID, {32}}}),
-       MakeInstructionMessage(SpvOpLoad, 8, 34, {{SPV_OPERAND_TYPE_ID, {17}}}),
+       MakeInstructionMessage(spv::Op::OpLoad, 8, 34,
+                              {{SPV_OPERAND_TYPE_ID, {17}}}),
        MakeInstructionMessage(
-           SpvOpFAdd, 8, 35,
+           spv::Op::OpFAdd, 8, 35,
            {{SPV_OPERAND_TYPE_ID, {34}}, {SPV_OPERAND_TYPE_ID, {33}}}),
        MakeInstructionMessage(
-           SpvOpStore, 0, 0,
+           spv::Op::OpStore, 0, 0,
            {{SPV_OPERAND_TYPE_ID, {17}}, {SPV_OPERAND_TYPE_ID, {35}}}),
-       MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {24}}}),
-       MakeInstructionMessage(SpvOpLabel, 0, 24, {}),
-       MakeInstructionMessage(SpvOpLoad, 6, 36, {{SPV_OPERAND_TYPE_ID, {19}}}),
+       MakeInstructionMessage(spv::Op::OpBranch, 0, 0,
+                              {{SPV_OPERAND_TYPE_ID, {24}}}),
+       MakeInstructionMessage(spv::Op::OpLabel, 0, 24, {}),
+       MakeInstructionMessage(spv::Op::OpLoad, 6, 36,
+                              {{SPV_OPERAND_TYPE_ID, {19}}}),
        MakeInstructionMessage(
-           SpvOpIAdd, 6, 38,
+           spv::Op::OpIAdd, 6, 38,
            {{SPV_OPERAND_TYPE_ID, {36}}, {SPV_OPERAND_TYPE_ID, {37}}}),
        MakeInstructionMessage(
-           SpvOpStore, 0, 0,
+           spv::Op::OpStore, 0, 0,
            {{SPV_OPERAND_TYPE_ID, {19}}, {SPV_OPERAND_TYPE_ID, {38}}}),
-       MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {21}}}),
-       MakeInstructionMessage(SpvOpLabel, 0, 23, {}),
-       MakeInstructionMessage(SpvOpLoad, 8, 39, {{SPV_OPERAND_TYPE_ID, {17}}}),
-       MakeInstructionMessage(SpvOpReturnValue, 0, 0,
+       MakeInstructionMessage(spv::Op::OpBranch, 0, 0,
+                              {{SPV_OPERAND_TYPE_ID, {21}}}),
+       MakeInstructionMessage(spv::Op::OpLabel, 0, 23, {}),
+       MakeInstructionMessage(spv::Op::OpLoad, 8, 39,
+                              {{SPV_OPERAND_TYPE_ID, {17}}}),
+       MakeInstructionMessage(spv::Op::OpReturnValue, 0, 0,
                               {{SPV_OPERAND_TYPE_ID, {39}}}),
-       MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {})}));
+       MakeInstructionMessage(spv::Op::OpFunctionEnd, 0, 0, {})}));
 
   ASSERT_TRUE(
       transformation1.IsApplicable(context.get(), transformation_context));
@@ -299,51 +310,51 @@
   ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(25));
 
   TransformationAddFunction transformation2(std::vector<protobufs::Instruction>(
-      {MakeInstructionMessage(
-           SpvOpFunction, 2, 15,
-           {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}},
-            {SPV_OPERAND_TYPE_ID, {3}}}),
-       MakeInstructionMessage(SpvOpLabel, 0, 16, {}),
+      {MakeInstructionMessage(spv::Op::OpFunction, 2, 15,
+                              {{SPV_OPERAND_TYPE_FUNCTION_CONTROL,
+                                {uint32_t(spv::FunctionControlMask::MaskNone)}},
+                               {SPV_OPERAND_TYPE_ID, {3}}}),
+       MakeInstructionMessage(spv::Op::OpLabel, 0, 16, {}),
+       MakeInstructionMessage(spv::Op::OpVariable, 7, 44,
+                              {{SPV_OPERAND_TYPE_STORAGE_CLASS,
+                                {uint32_t(spv::StorageClass::Function)}}}),
+       MakeInstructionMessage(spv::Op::OpVariable, 9, 45,
+                              {{SPV_OPERAND_TYPE_STORAGE_CLASS,
+                                {uint32_t(spv::StorageClass::Function)}}}),
+       MakeInstructionMessage(spv::Op::OpVariable, 7, 48,
+                              {{SPV_OPERAND_TYPE_STORAGE_CLASS,
+                                {uint32_t(spv::StorageClass::Function)}}}),
+       MakeInstructionMessage(spv::Op::OpVariable, 9, 49,
+                              {{SPV_OPERAND_TYPE_STORAGE_CLASS,
+                                {uint32_t(spv::StorageClass::Function)}}}),
        MakeInstructionMessage(
-           SpvOpVariable, 7, 44,
-           {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}),
-       MakeInstructionMessage(
-           SpvOpVariable, 9, 45,
-           {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}),
-       MakeInstructionMessage(
-           SpvOpVariable, 7, 48,
-           {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}),
-       MakeInstructionMessage(
-           SpvOpVariable, 9, 49,
-           {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}),
-       MakeInstructionMessage(
-           SpvOpStore, 0, 0,
+           spv::Op::OpStore, 0, 0,
            {{SPV_OPERAND_TYPE_ID, {44}}, {SPV_OPERAND_TYPE_ID, {20}}}),
        MakeInstructionMessage(
-           SpvOpStore, 0, 0,
+           spv::Op::OpStore, 0, 0,
            {{SPV_OPERAND_TYPE_ID, {45}}, {SPV_OPERAND_TYPE_ID, {18}}}),
-       MakeInstructionMessage(SpvOpFunctionCall, 8, 46,
+       MakeInstructionMessage(spv::Op::OpFunctionCall, 8, 46,
                               {{SPV_OPERAND_TYPE_ID, {13}},
                                {SPV_OPERAND_TYPE_ID, {44}},
                                {SPV_OPERAND_TYPE_ID, {45}}}),
        MakeInstructionMessage(
-           SpvOpStore, 0, 0,
+           spv::Op::OpStore, 0, 0,
            {{SPV_OPERAND_TYPE_ID, {48}}, {SPV_OPERAND_TYPE_ID, {37}}}),
        MakeInstructionMessage(
-           SpvOpStore, 0, 0,
+           spv::Op::OpStore, 0, 0,
            {{SPV_OPERAND_TYPE_ID, {49}}, {SPV_OPERAND_TYPE_ID, {47}}}),
-       MakeInstructionMessage(SpvOpFunctionCall, 8, 50,
+       MakeInstructionMessage(spv::Op::OpFunctionCall, 8, 50,
                               {{SPV_OPERAND_TYPE_ID, {13}},
                                {SPV_OPERAND_TYPE_ID, {48}},
                                {SPV_OPERAND_TYPE_ID, {49}}}),
        MakeInstructionMessage(
-           SpvOpFAdd, 8, 51,
+           spv::Op::OpFAdd, 8, 51,
            {{SPV_OPERAND_TYPE_ID, {46}}, {SPV_OPERAND_TYPE_ID, {50}}}),
        MakeInstructionMessage(
-           SpvOpStore, 0, 0,
+           spv::Op::OpStore, 0, 0,
            {{SPV_OPERAND_TYPE_ID, {43}}, {SPV_OPERAND_TYPE_ID, {51}}}),
-       MakeInstructionMessage(SpvOpReturn, 0, 0, {}),
-       MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {})}));
+       MakeInstructionMessage(spv::Op::OpReturn, 0, 0, {}),
+       MakeInstructionMessage(spv::Op::OpFunctionEnd, 0, 0, {})}));
 
   ASSERT_TRUE(
       transformation2.IsApplicable(context.get(), transformation_context));
@@ -513,45 +524,46 @@
   ASSERT_FALSE(
       TransformationAddFunction(
           std::vector<protobufs::Instruction>(
-              {MakeInstructionMessage(SpvOpFunctionParameter, 7, 11, {}),
-               MakeInstructionMessage(SpvOpFunctionParameter, 9, 12, {}),
-               MakeInstructionMessage(SpvOpLabel, 0, 14, {})}))
+              {MakeInstructionMessage(spv::Op::OpFunctionParameter, 7, 11, {}),
+               MakeInstructionMessage(spv::Op::OpFunctionParameter, 9, 12, {}),
+               MakeInstructionMessage(spv::Op::OpLabel, 0, 14, {})}))
           .IsApplicable(context.get(), transformation_context));
 
   // No OpLabel
   ASSERT_FALSE(
       TransformationAddFunction(
           std::vector<protobufs::Instruction>(
-              {MakeInstructionMessage(SpvOpFunction, 8, 13,
-                                      {{SPV_OPERAND_TYPE_FUNCTION_CONTROL,
-                                        {SpvFunctionControlMaskNone}},
-                                       {SPV_OPERAND_TYPE_ID, {10}}}),
-               MakeInstructionMessage(SpvOpReturnValue, 0, 0,
+              {MakeInstructionMessage(
+                   spv::Op::OpFunction, 8, 13,
+                   {{SPV_OPERAND_TYPE_FUNCTION_CONTROL,
+                     {uint32_t(spv::FunctionControlMask::MaskNone)}},
+                    {SPV_OPERAND_TYPE_ID, {10}}}),
+               MakeInstructionMessage(spv::Op::OpReturnValue, 0, 0,
                                       {{SPV_OPERAND_TYPE_ID, {39}}}),
-               MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {})}))
+               MakeInstructionMessage(spv::Op::OpFunctionEnd, 0, 0, {})}))
           .IsApplicable(context.get(), transformation_context));
 
   // Abrupt end of instructions
   ASSERT_FALSE(TransformationAddFunction(
                    std::vector<protobufs::Instruction>({MakeInstructionMessage(
-                       SpvOpFunction, 8, 13,
+                       spv::Op::OpFunction, 8, 13,
                        {{SPV_OPERAND_TYPE_FUNCTION_CONTROL,
-                         {SpvFunctionControlMaskNone}},
+                         {uint32_t(spv::FunctionControlMask::MaskNone)}},
                         {SPV_OPERAND_TYPE_ID, {10}}})}))
                    .IsApplicable(context.get(), transformation_context));
 
   // No function end
-  ASSERT_FALSE(
-      TransformationAddFunction(
-          std::vector<protobufs::Instruction>(
-              {MakeInstructionMessage(SpvOpFunction, 8, 13,
-                                      {{SPV_OPERAND_TYPE_FUNCTION_CONTROL,
-                                        {SpvFunctionControlMaskNone}},
-                                       {SPV_OPERAND_TYPE_ID, {10}}}),
-               MakeInstructionMessage(SpvOpLabel, 0, 14, {}),
-               MakeInstructionMessage(SpvOpReturnValue, 0, 0,
-                                      {{SPV_OPERAND_TYPE_ID, {39}}})}))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationAddFunction(
+                   std::vector<protobufs::Instruction>(
+                       {MakeInstructionMessage(
+                            spv::Op::OpFunction, 8, 13,
+                            {{SPV_OPERAND_TYPE_FUNCTION_CONTROL,
+                              {uint32_t(spv::FunctionControlMask::MaskNone)}},
+                             {SPV_OPERAND_TYPE_ID, {10}}}),
+                        MakeInstructionMessage(spv::Op::OpLabel, 0, 14, {}),
+                        MakeInstructionMessage(spv::Op::OpReturnValue, 0, 0,
+                                               {{SPV_OPERAND_TYPE_ID, {39}}})}))
+                   .IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationAddFunctionTest, LoopLimiters) {
@@ -581,61 +593,69 @@
   const auto consumer = nullptr;
 
   std::vector<protobufs::Instruction> instructions;
-  instructions.push_back(MakeInstructionMessage(
-      SpvOpFunction, 2, 30,
-      {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}},
-       {SPV_OPERAND_TYPE_TYPE_ID, {3}}}));
-  instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 31, {}));
   instructions.push_back(
-      MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {20}}}));
-  instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 20, {}));
-  instructions.push_back(MakeInstructionMessage(
-      SpvOpLoopMerge, 0, 0,
-      {{SPV_OPERAND_TYPE_ID, {21}},
-       {SPV_OPERAND_TYPE_ID, {22}},
-       {SPV_OPERAND_TYPE_LOOP_CONTROL, {SpvLoopControlMaskNone}}}));
-  instructions.push_back(MakeInstructionMessage(SpvOpBranchConditional, 0, 0,
+      MakeInstructionMessage(spv::Op::OpFunction, 2, 30,
+                             {{SPV_OPERAND_TYPE_FUNCTION_CONTROL,
+                               {uint32_t(spv::FunctionControlMask::MaskNone)}},
+                              {SPV_OPERAND_TYPE_TYPE_ID, {3}}}));
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpLabel, 0, 31, {}));
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpBranch, 0, 0,
+                                                {{SPV_OPERAND_TYPE_ID, {20}}}));
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpLabel, 0, 20, {}));
+  instructions.push_back(
+      MakeInstructionMessage(spv::Op::OpLoopMerge, 0, 0,
+                             {{SPV_OPERAND_TYPE_ID, {21}},
+                              {SPV_OPERAND_TYPE_ID, {22}},
+                              {SPV_OPERAND_TYPE_LOOP_CONTROL,
+                               {uint32_t(spv::LoopControlMask::MaskNone)}}}));
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpBranchConditional, 0,
+                                                0,
                                                 {{SPV_OPERAND_TYPE_ID, {12}},
                                                  {SPV_OPERAND_TYPE_ID, {23}},
                                                  {SPV_OPERAND_TYPE_ID, {21}}}));
-  instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 23, {}));
-  instructions.push_back(MakeInstructionMessage(
-      SpvOpLoopMerge, 0, 0,
-      {{SPV_OPERAND_TYPE_ID, {25}},
-       {SPV_OPERAND_TYPE_ID, {26}},
-       {SPV_OPERAND_TYPE_LOOP_CONTROL, {SpvLoopControlMaskNone}}}));
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpLabel, 0, 23, {}));
   instructions.push_back(
-      MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {28}}}));
-  instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 28, {}));
-  instructions.push_back(MakeInstructionMessage(SpvOpBranchConditional, 0, 0,
+      MakeInstructionMessage(spv::Op::OpLoopMerge, 0, 0,
+                             {{SPV_OPERAND_TYPE_ID, {25}},
+                              {SPV_OPERAND_TYPE_ID, {26}},
+                              {SPV_OPERAND_TYPE_LOOP_CONTROL,
+                               {uint32_t(spv::LoopControlMask::MaskNone)}}}));
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpBranch, 0, 0,
+                                                {{SPV_OPERAND_TYPE_ID, {28}}}));
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpLabel, 0, 28, {}));
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpBranchConditional, 0,
+                                                0,
                                                 {{SPV_OPERAND_TYPE_ID, {12}},
                                                  {SPV_OPERAND_TYPE_ID, {26}},
                                                  {SPV_OPERAND_TYPE_ID, {25}}}));
-  instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 26, {}));
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpLabel, 0, 26, {}));
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpBranch, 0, 0,
+                                                {{SPV_OPERAND_TYPE_ID, {23}}}));
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpLabel, 0, 25, {}));
   instructions.push_back(
-      MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {23}}}));
-  instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 25, {}));
-  instructions.push_back(MakeInstructionMessage(
-      SpvOpLoopMerge, 0, 0,
-      {{SPV_OPERAND_TYPE_ID, {24}},
-       {SPV_OPERAND_TYPE_ID, {27}},
-       {SPV_OPERAND_TYPE_LOOP_CONTROL, {SpvLoopControlMaskNone}}}));
-  instructions.push_back(MakeInstructionMessage(SpvOpBranchConditional, 0, 0,
+      MakeInstructionMessage(spv::Op::OpLoopMerge, 0, 0,
+                             {{SPV_OPERAND_TYPE_ID, {24}},
+                              {SPV_OPERAND_TYPE_ID, {27}},
+                              {SPV_OPERAND_TYPE_LOOP_CONTROL,
+                               {uint32_t(spv::LoopControlMask::MaskNone)}}}));
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpBranchConditional, 0,
+                                                0,
                                                 {{SPV_OPERAND_TYPE_ID, {12}},
                                                  {SPV_OPERAND_TYPE_ID, {24}},
                                                  {SPV_OPERAND_TYPE_ID, {27}}}));
-  instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 27, {}));
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpLabel, 0, 27, {}));
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpBranch, 0, 0,
+                                                {{SPV_OPERAND_TYPE_ID, {25}}}));
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpLabel, 0, 24, {}));
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpBranch, 0, 0,
+                                                {{SPV_OPERAND_TYPE_ID, {22}}}));
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpLabel, 0, 22, {}));
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpBranch, 0, 0,
+                                                {{SPV_OPERAND_TYPE_ID, {20}}}));
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpLabel, 0, 21, {}));
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpReturn, 0, 0, {}));
   instructions.push_back(
-      MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {25}}}));
-  instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 24, {}));
-  instructions.push_back(
-      MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {22}}}));
-  instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 22, {}));
-  instructions.push_back(
-      MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {20}}}));
-  instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 21, {}));
-  instructions.push_back(MakeInstructionMessage(SpvOpReturn, 0, 0, {}));
-  instructions.push_back(MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {}));
+      MakeInstructionMessage(spv::Op::OpFunctionEnd, 0, 0, {}));
 
   spvtools::ValidatorOptions validator_options;
 
@@ -837,31 +857,36 @@
 
   std::vector<protobufs::Instruction> instructions;
 
-  instructions.push_back(MakeInstructionMessage(
-      SpvOpFunction, 2, 10,
-      {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}},
-       {SPV_OPERAND_TYPE_TYPE_ID, {8}}}));
   instructions.push_back(
-      MakeInstructionMessage(SpvOpFunctionParameter, 7, 9, {}));
-  instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 11, {}));
+      MakeInstructionMessage(spv::Op::OpFunction, 2, 10,
+                             {{SPV_OPERAND_TYPE_FUNCTION_CONTROL,
+                               {uint32_t(spv::FunctionControlMask::MaskNone)}},
+                              {SPV_OPERAND_TYPE_TYPE_ID, {8}}}));
   instructions.push_back(
-      MakeInstructionMessage(SpvOpLoad, 6, 12, {{SPV_OPERAND_TYPE_ID, {9}}}));
+      MakeInstructionMessage(spv::Op::OpFunctionParameter, 7, 9, {}));
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpLabel, 0, 11, {}));
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpLoad, 6, 12,
+                                                {{SPV_OPERAND_TYPE_ID, {9}}}));
   instructions.push_back(MakeInstructionMessage(
-      SpvOpIEqual, 14, 15,
+      spv::Op::OpIEqual, 14, 15,
       {{SPV_OPERAND_TYPE_ID, {12}}, {SPV_OPERAND_TYPE_ID, {13}}}));
   instructions.push_back(MakeInstructionMessage(
-      SpvOpSelectionMerge, 0, 0,
+      spv::Op::OpSelectionMerge, 0, 0,
       {{SPV_OPERAND_TYPE_ID, {17}},
-       {SPV_OPERAND_TYPE_SELECTION_CONTROL, {SpvSelectionControlMaskNone}}}));
-  instructions.push_back(MakeInstructionMessage(SpvOpBranchConditional, 0, 0,
+       {SPV_OPERAND_TYPE_SELECTION_CONTROL,
+        {uint32_t(spv::SelectionControlMask::MaskNone)}}}));
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpBranchConditional, 0,
+                                                0,
                                                 {{SPV_OPERAND_TYPE_ID, {15}},
                                                  {SPV_OPERAND_TYPE_ID, {16}},
                                                  {SPV_OPERAND_TYPE_ID, {17}}}));
-  instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 16, {}));
-  instructions.push_back(MakeInstructionMessage(SpvOpUnreachable, 0, 0, {}));
-  instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 17, {}));
-  instructions.push_back(MakeInstructionMessage(SpvOpKill, 0, 0, {}));
-  instructions.push_back(MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {}));
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpLabel, 0, 16, {}));
+  instructions.push_back(
+      MakeInstructionMessage(spv::Op::OpUnreachable, 0, 0, {}));
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpLabel, 0, 17, {}));
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpKill, 0, 0, {}));
+  instructions.push_back(
+      MakeInstructionMessage(spv::Op::OpFunctionEnd, 0, 0, {}));
 
   spvtools::ValidatorOptions validator_options;
 
@@ -996,31 +1021,36 @@
 
   std::vector<protobufs::Instruction> instructions;
 
-  instructions.push_back(MakeInstructionMessage(
-      SpvOpFunction, 6, 10,
-      {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}},
-       {SPV_OPERAND_TYPE_TYPE_ID, {50}}}));
   instructions.push_back(
-      MakeInstructionMessage(SpvOpFunctionParameter, 7, 9, {}));
-  instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 11, {}));
+      MakeInstructionMessage(spv::Op::OpFunction, 6, 10,
+                             {{SPV_OPERAND_TYPE_FUNCTION_CONTROL,
+                               {uint32_t(spv::FunctionControlMask::MaskNone)}},
+                              {SPV_OPERAND_TYPE_TYPE_ID, {50}}}));
   instructions.push_back(
-      MakeInstructionMessage(SpvOpLoad, 6, 12, {{SPV_OPERAND_TYPE_ID, {9}}}));
+      MakeInstructionMessage(spv::Op::OpFunctionParameter, 7, 9, {}));
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpLabel, 0, 11, {}));
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpLoad, 6, 12,
+                                                {{SPV_OPERAND_TYPE_ID, {9}}}));
   instructions.push_back(MakeInstructionMessage(
-      SpvOpIEqual, 14, 15,
+      spv::Op::OpIEqual, 14, 15,
       {{SPV_OPERAND_TYPE_ID, {12}}, {SPV_OPERAND_TYPE_ID, {13}}}));
   instructions.push_back(MakeInstructionMessage(
-      SpvOpSelectionMerge, 0, 0,
+      spv::Op::OpSelectionMerge, 0, 0,
       {{SPV_OPERAND_TYPE_ID, {17}},
-       {SPV_OPERAND_TYPE_SELECTION_CONTROL, {SpvSelectionControlMaskNone}}}));
-  instructions.push_back(MakeInstructionMessage(SpvOpBranchConditional, 0, 0,
+       {SPV_OPERAND_TYPE_SELECTION_CONTROL,
+        {uint32_t(spv::SelectionControlMask::MaskNone)}}}));
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpBranchConditional, 0,
+                                                0,
                                                 {{SPV_OPERAND_TYPE_ID, {15}},
                                                  {SPV_OPERAND_TYPE_ID, {16}},
                                                  {SPV_OPERAND_TYPE_ID, {17}}}));
-  instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 16, {}));
-  instructions.push_back(MakeInstructionMessage(SpvOpUnreachable, 0, 0, {}));
-  instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 17, {}));
-  instructions.push_back(MakeInstructionMessage(SpvOpKill, 0, 0, {}));
-  instructions.push_back(MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {}));
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpLabel, 0, 16, {}));
+  instructions.push_back(
+      MakeInstructionMessage(spv::Op::OpUnreachable, 0, 0, {}));
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpLabel, 0, 17, {}));
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpKill, 0, 0, {}));
+  instructions.push_back(
+      MakeInstructionMessage(spv::Op::OpFunctionEnd, 0, 0, {}));
 
   spvtools::ValidatorOptions validator_options;
 
@@ -1189,129 +1219,133 @@
 
   std::vector<protobufs::Instruction> instructions;
 
-  instructions.push_back(MakeInstructionMessage(
-      SpvOpFunction, 2, 12,
-      {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}},
-       {SPV_OPERAND_TYPE_TYPE_ID, {8}}}));
   instructions.push_back(
-      MakeInstructionMessage(SpvOpFunctionParameter, 7, 9, {}));
+      MakeInstructionMessage(spv::Op::OpFunction, 2, 12,
+                             {{SPV_OPERAND_TYPE_FUNCTION_CONTROL,
+                               {uint32_t(spv::FunctionControlMask::MaskNone)}},
+                              {SPV_OPERAND_TYPE_TYPE_ID, {8}}}));
   instructions.push_back(
-      MakeInstructionMessage(SpvOpFunctionParameter, 102, 10, {}));
+      MakeInstructionMessage(spv::Op::OpFunctionParameter, 7, 9, {}));
   instructions.push_back(
-      MakeInstructionMessage(SpvOpFunctionParameter, 7, 11, {}));
-  instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 13, {}));
+      MakeInstructionMessage(spv::Op::OpFunctionParameter, 102, 10, {}));
+  instructions.push_back(
+      MakeInstructionMessage(spv::Op::OpFunctionParameter, 7, 11, {}));
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpLabel, 0, 13, {}));
 
-  instructions.push_back(MakeInstructionMessage(
-      SpvOpVariable, 7, 14,
-      {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}));
-  instructions.push_back(MakeInstructionMessage(
-      SpvOpVariable, 26, 27,
-      {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}));
   instructions.push_back(
-      MakeInstructionMessage(SpvOpLoad, 6, 22, {{SPV_OPERAND_TYPE_ID, {11}}}));
-  instructions.push_back(MakeInstructionMessage(SpvOpAccessChain, 23, 24,
+      MakeInstructionMessage(spv::Op::OpVariable, 7, 14,
+                             {{SPV_OPERAND_TYPE_STORAGE_CLASS,
+                               {uint32_t(spv::StorageClass::Function)}}}));
+  instructions.push_back(
+      MakeInstructionMessage(spv::Op::OpVariable, 26, 27,
+                             {{SPV_OPERAND_TYPE_STORAGE_CLASS,
+                               {uint32_t(spv::StorageClass::Function)}}}));
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpLoad, 6, 22,
+                                                {{SPV_OPERAND_TYPE_ID, {11}}}));
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpAccessChain, 23, 24,
                                                 {{SPV_OPERAND_TYPE_ID, {20}},
                                                  {SPV_OPERAND_TYPE_ID, {21}},
                                                  {SPV_OPERAND_TYPE_ID, {22}}}));
-  instructions.push_back(
-      MakeInstructionMessage(SpvOpLoad, 6, 25, {{SPV_OPERAND_TYPE_ID, {24}}}));
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpLoad, 6, 25,
+                                                {{SPV_OPERAND_TYPE_ID, {24}}}));
   instructions.push_back(MakeInstructionMessage(
-      SpvOpStore, 0, 0,
+      spv::Op::OpStore, 0, 0,
       {{SPV_OPERAND_TYPE_ID, {14}}, {SPV_OPERAND_TYPE_ID, {25}}}));
-  instructions.push_back(
-      MakeInstructionMessage(SpvOpLoad, 15, 28, {{SPV_OPERAND_TYPE_ID, {10}}}));
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpLoad, 15, 28,
+                                                {{SPV_OPERAND_TYPE_ID, {10}}}));
   instructions.push_back(MakeInstructionMessage(
-      SpvOpAccessChain, 29, 30,
+      spv::Op::OpAccessChain, 29, 30,
       {{SPV_OPERAND_TYPE_ID, {20}}, {SPV_OPERAND_TYPE_ID, {28}}}));
-  instructions.push_back(
-      MakeInstructionMessage(SpvOpLoad, 17, 31, {{SPV_OPERAND_TYPE_ID, {30}}}));
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpLoad, 17, 31,
+                                                {{SPV_OPERAND_TYPE_ID, {30}}}));
   instructions.push_back(MakeInstructionMessage(
-      SpvOpStore, 0, 0,
+      spv::Op::OpStore, 0, 0,
       {{SPV_OPERAND_TYPE_ID, {27}}, {SPV_OPERAND_TYPE_ID, {31}}}));
-  instructions.push_back(
-      MakeInstructionMessage(SpvOpLoad, 6, 32, {{SPV_OPERAND_TYPE_ID, {9}}}));
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpLoad, 6, 32,
+                                                {{SPV_OPERAND_TYPE_ID, {9}}}));
   instructions.push_back(MakeInstructionMessage(
-      SpvOpInBoundsAccessChain, 7, 34,
+      spv::Op::OpInBoundsAccessChain, 7, 34,
       {{SPV_OPERAND_TYPE_ID, {27}}, {SPV_OPERAND_TYPE_ID, {32}}}));
   instructions.push_back(MakeInstructionMessage(
-      SpvOpStore, 0, 0,
+      spv::Op::OpStore, 0, 0,
       {{SPV_OPERAND_TYPE_ID, {34}}, {SPV_OPERAND_TYPE_ID, {33}}}));
-  instructions.push_back(
-      MakeInstructionMessage(SpvOpLoad, 6, 39, {{SPV_OPERAND_TYPE_ID, {9}}}));
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpLoad, 6, 39,
+                                                {{SPV_OPERAND_TYPE_ID, {9}}}));
   instructions.push_back(MakeInstructionMessage(
-      SpvOpAccessChain, 23, 40,
+      spv::Op::OpAccessChain, 23, 40,
       {{SPV_OPERAND_TYPE_ID, {38}}, {SPV_OPERAND_TYPE_ID, {33}}}));
-  instructions.push_back(
-      MakeInstructionMessage(SpvOpLoad, 6, 41, {{SPV_OPERAND_TYPE_ID, {40}}}));
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpLoad, 6, 41,
+                                                {{SPV_OPERAND_TYPE_ID, {40}}}));
   instructions.push_back(MakeInstructionMessage(
-      SpvOpInBoundsAccessChain, 23, 42,
+      spv::Op::OpInBoundsAccessChain, 23, 42,
       {{SPV_OPERAND_TYPE_ID, {38}}, {SPV_OPERAND_TYPE_ID, {39}}}));
   instructions.push_back(MakeInstructionMessage(
-      SpvOpStore, 0, 0,
+      spv::Op::OpStore, 0, 0,
       {{SPV_OPERAND_TYPE_ID, {42}}, {SPV_OPERAND_TYPE_ID, {41}}}));
-  instructions.push_back(
-      MakeInstructionMessage(SpvOpLoad, 15, 43, {{SPV_OPERAND_TYPE_ID, {10}}}));
-  instructions.push_back(
-      MakeInstructionMessage(SpvOpLoad, 6, 44, {{SPV_OPERAND_TYPE_ID, {11}}}));
-  instructions.push_back(
-      MakeInstructionMessage(SpvOpLoad, 6, 45, {{SPV_OPERAND_TYPE_ID, {9}}}));
-  instructions.push_back(
-      MakeInstructionMessage(SpvOpLoad, 15, 46, {{SPV_OPERAND_TYPE_ID, {10}}}));
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpLoad, 15, 43,
+                                                {{SPV_OPERAND_TYPE_ID, {10}}}));
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpLoad, 6, 44,
+                                                {{SPV_OPERAND_TYPE_ID, {11}}}));
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpLoad, 6, 45,
+                                                {{SPV_OPERAND_TYPE_ID, {9}}}));
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpLoad, 15, 46,
+                                                {{SPV_OPERAND_TYPE_ID, {10}}}));
   instructions.push_back(MakeInstructionMessage(
-      SpvOpIAdd, 6, 47,
+      spv::Op::OpIAdd, 6, 47,
       {{SPV_OPERAND_TYPE_ID, {45}}, {SPV_OPERAND_TYPE_ID, {46}}}));
   instructions.push_back(MakeInstructionMessage(
-      SpvOpAccessChain, 23, 48,
+      spv::Op::OpAccessChain, 23, 48,
       {{SPV_OPERAND_TYPE_ID, {38}}, {SPV_OPERAND_TYPE_ID, {47}}}));
-  instructions.push_back(
-      MakeInstructionMessage(SpvOpLoad, 6, 49, {{SPV_OPERAND_TYPE_ID, {48}}}));
-  instructions.push_back(MakeInstructionMessage(SpvOpInBoundsAccessChain, 23,
-                                                50,
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpLoad, 6, 49,
+                                                {{SPV_OPERAND_TYPE_ID, {48}}}));
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpInBoundsAccessChain,
+                                                23, 50,
                                                 {{SPV_OPERAND_TYPE_ID, {20}},
                                                  {SPV_OPERAND_TYPE_ID, {43}},
                                                  {SPV_OPERAND_TYPE_ID, {44}}}));
-  instructions.push_back(
-      MakeInstructionMessage(SpvOpLoad, 6, 51, {{SPV_OPERAND_TYPE_ID, {50}}}));
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpLoad, 6, 51,
+                                                {{SPV_OPERAND_TYPE_ID, {50}}}));
   instructions.push_back(MakeInstructionMessage(
-      SpvOpIAdd, 6, 52,
+      spv::Op::OpIAdd, 6, 52,
       {{SPV_OPERAND_TYPE_ID, {51}}, {SPV_OPERAND_TYPE_ID, {49}}}));
-  instructions.push_back(MakeInstructionMessage(SpvOpAccessChain, 23, 53,
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpAccessChain, 23, 53,
                                                 {{SPV_OPERAND_TYPE_ID, {20}},
                                                  {SPV_OPERAND_TYPE_ID, {43}},
                                                  {SPV_OPERAND_TYPE_ID, {44}}}));
   instructions.push_back(MakeInstructionMessage(
-      SpvOpStore, 0, 0,
+      spv::Op::OpStore, 0, 0,
       {{SPV_OPERAND_TYPE_ID, {53}}, {SPV_OPERAND_TYPE_ID, {52}}}));
-  instructions.push_back(
-      MakeInstructionMessage(SpvOpLoad, 15, 58, {{SPV_OPERAND_TYPE_ID, {10}}}));
-  instructions.push_back(
-      MakeInstructionMessage(SpvOpLoad, 6, 63, {{SPV_OPERAND_TYPE_ID, {11}}}));
-  instructions.push_back(MakeInstructionMessage(SpvOpAccessChain, 64, 65,
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpLoad, 15, 58,
+                                                {{SPV_OPERAND_TYPE_ID, {10}}}));
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpLoad, 6, 63,
+                                                {{SPV_OPERAND_TYPE_ID, {11}}}));
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpAccessChain, 64, 65,
                                                 {{SPV_OPERAND_TYPE_ID, {62}},
                                                  {SPV_OPERAND_TYPE_ID, {21}},
                                                  {SPV_OPERAND_TYPE_ID, {63}}}));
-  instructions.push_back(MakeInstructionMessage(SpvOpAccessChain, 64, 101,
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpAccessChain, 64, 101,
                                                 {{SPV_OPERAND_TYPE_ID, {62}},
                                                  {SPV_OPERAND_TYPE_ID, {45}},
                                                  {SPV_OPERAND_TYPE_ID, {46}}}));
-  instructions.push_back(
-      MakeInstructionMessage(SpvOpLoad, 54, 66, {{SPV_OPERAND_TYPE_ID, {65}}}));
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpLoad, 54, 66,
+                                                {{SPV_OPERAND_TYPE_ID, {65}}}));
   instructions.push_back(MakeInstructionMessage(
-      SpvOpAccessChain, 64, 67,
+      spv::Op::OpAccessChain, 64, 67,
       {{SPV_OPERAND_TYPE_ID, {57}}, {SPV_OPERAND_TYPE_ID, {58}}}));
   instructions.push_back(MakeInstructionMessage(
-      SpvOpStore, 0, 0,
+      spv::Op::OpStore, 0, 0,
       {{SPV_OPERAND_TYPE_ID, {67}}, {SPV_OPERAND_TYPE_ID, {66}}}));
-  instructions.push_back(
-      MakeInstructionMessage(SpvOpLoad, 6, 68, {{SPV_OPERAND_TYPE_ID, {9}}}));
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpLoad, 6, 68,
+                                                {{SPV_OPERAND_TYPE_ID, {9}}}));
   instructions.push_back(MakeInstructionMessage(
-      SpvOpInBoundsAccessChain, 64, 70,
+      spv::Op::OpInBoundsAccessChain, 64, 70,
       {{SPV_OPERAND_TYPE_ID, {57}}, {SPV_OPERAND_TYPE_ID, {68}}}));
   instructions.push_back(MakeInstructionMessage(
-      SpvOpStore, 0, 0,
+      spv::Op::OpStore, 0, 0,
       {{SPV_OPERAND_TYPE_ID, {70}}, {SPV_OPERAND_TYPE_ID, {69}}}));
-  instructions.push_back(MakeInstructionMessage(SpvOpReturn, 0, 0, {}));
-  instructions.push_back(MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {}));
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpReturn, 0, 0, {}));
+  instructions.push_back(
+      MakeInstructionMessage(spv::Op::OpFunctionEnd, 0, 0, {}));
 
   spvtools::ValidatorOptions validator_options;
 
@@ -1634,15 +1668,17 @@
 
   std::vector<protobufs::Instruction> instructions;
 
-  instructions.push_back(MakeInstructionMessage(
-      SpvOpFunction, 2, 8,
-      {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}},
-       {SPV_OPERAND_TYPE_TYPE_ID, {3}}}));
-  instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 9, {}));
-  instructions.push_back(MakeInstructionMessage(SpvOpFunctionCall, 2, 11,
+  instructions.push_back(
+      MakeInstructionMessage(spv::Op::OpFunction, 2, 8,
+                             {{SPV_OPERAND_TYPE_FUNCTION_CONTROL,
+                               {uint32_t(spv::FunctionControlMask::MaskNone)}},
+                              {SPV_OPERAND_TYPE_TYPE_ID, {3}}}));
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpLabel, 0, 9, {}));
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpFunctionCall, 2, 11,
                                                 {{SPV_OPERAND_TYPE_ID, {6}}}));
-  instructions.push_back(MakeInstructionMessage(SpvOpReturn, 0, 0, {}));
-  instructions.push_back(MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {}));
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpReturn, 0, 0, {}));
+  instructions.push_back(
+      MakeInstructionMessage(spv::Op::OpFunctionEnd, 0, 0, {}));
 
   spvtools::ValidatorOptions validator_options;
 
@@ -1738,15 +1774,17 @@
 
   std::vector<protobufs::Instruction> instructions;
 
-  instructions.push_back(MakeInstructionMessage(
-      SpvOpFunction, 2, 8,
-      {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}},
-       {SPV_OPERAND_TYPE_TYPE_ID, {3}}}));
-  instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 9, {}));
-  instructions.push_back(MakeInstructionMessage(SpvOpFunctionCall, 2, 11,
+  instructions.push_back(
+      MakeInstructionMessage(spv::Op::OpFunction, 2, 8,
+                             {{SPV_OPERAND_TYPE_FUNCTION_CONTROL,
+                               {uint32_t(spv::FunctionControlMask::MaskNone)}},
+                              {SPV_OPERAND_TYPE_TYPE_ID, {3}}}));
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpLabel, 0, 9, {}));
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpFunctionCall, 2, 11,
                                                 {{SPV_OPERAND_TYPE_ID, {6}}}));
-  instructions.push_back(MakeInstructionMessage(SpvOpReturn, 0, 0, {}));
-  instructions.push_back(MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {}));
+  instructions.push_back(MakeInstructionMessage(spv::Op::OpReturn, 0, 0, {}));
+  instructions.push_back(
+      MakeInstructionMessage(spv::Op::OpFunctionEnd, 0, 0, {}));
 
   spvtools::ValidatorOptions validator_options;
 
diff --git a/test/fuzz/transformation_add_global_undef_test.cpp b/test/fuzz/transformation_add_global_undef_test.cpp
index 03b9157..b87970f 100644
--- a/test/fuzz/transformation_add_global_undef_test.cpp
+++ b/test/fuzz/transformation_add_global_undef_test.cpp
@@ -71,7 +71,8 @@
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
                           &transformation_context);
-    ASSERT_EQ(SpvOpUndef, context->get_def_use_mgr()->GetDef(100)->opcode());
+    ASSERT_EQ(spv::Op::OpUndef,
+              context->get_def_use_mgr()->GetDef(100)->opcode());
   }
 
   TransformationAddGlobalUndef transformations[] = {
diff --git a/test/fuzz/transformation_add_global_variable_test.cpp b/test/fuzz/transformation_add_global_variable_test.cpp
index 9531ade..fc452df 100644
--- a/test/fuzz/transformation_add_global_variable_test.cpp
+++ b/test/fuzz/transformation_add_global_variable_test.cpp
@@ -66,94 +66,95 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
   // Id already in use
-  ASSERT_FALSE(
-      TransformationAddGlobalVariable(4, 10, SpvStorageClassPrivate, 0, true)
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationAddGlobalVariable(
+                   4, 10, spv::StorageClass::Private, 0, true)
+                   .IsApplicable(context.get(), transformation_context));
   // %1 is not a type
-  ASSERT_FALSE(
-      TransformationAddGlobalVariable(100, 1, SpvStorageClassPrivate, 0, false)
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationAddGlobalVariable(
+                   100, 1, spv::StorageClass::Private, 0, false)
+                   .IsApplicable(context.get(), transformation_context));
 
   // %7 is not a pointer type
-  ASSERT_FALSE(
-      TransformationAddGlobalVariable(100, 7, SpvStorageClassPrivate, 0, true)
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationAddGlobalVariable(
+                   100, 7, spv::StorageClass::Private, 0, true)
+                   .IsApplicable(context.get(), transformation_context));
 
   // %9 does not have Private storage class
-  ASSERT_FALSE(
-      TransformationAddGlobalVariable(100, 9, SpvStorageClassPrivate, 0, false)
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationAddGlobalVariable(
+                   100, 9, spv::StorageClass::Private, 0, false)
+                   .IsApplicable(context.get(), transformation_context));
 
   // %15 does not have Private storage class
-  ASSERT_FALSE(
-      TransformationAddGlobalVariable(100, 15, SpvStorageClassPrivate, 0, true)
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationAddGlobalVariable(
+                   100, 15, spv::StorageClass::Private, 0, true)
+                   .IsApplicable(context.get(), transformation_context));
 
   // %10 is a pointer to float, while %16 is an int constant
-  ASSERT_FALSE(TransformationAddGlobalVariable(100, 10, SpvStorageClassPrivate,
-                                               16, false)
+  ASSERT_FALSE(TransformationAddGlobalVariable(
+                   100, 10, spv::StorageClass::Private, 16, false)
                    .IsApplicable(context.get(), transformation_context));
 
   // %10 is a Private pointer to float, while %15 is a variable with type
   // Uniform float pointer
-  ASSERT_FALSE(
-      TransformationAddGlobalVariable(100, 10, SpvStorageClassPrivate, 15, true)
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationAddGlobalVariable(
+                   100, 10, spv::StorageClass::Private, 15, true)
+                   .IsApplicable(context.get(), transformation_context));
 
   // %12 is a Private pointer to int, while %10 is a variable with type
   // Private float pointer
-  ASSERT_FALSE(TransformationAddGlobalVariable(100, 12, SpvStorageClassPrivate,
-                                               10, false)
+  ASSERT_FALSE(TransformationAddGlobalVariable(
+                   100, 12, spv::StorageClass::Private, 10, false)
                    .IsApplicable(context.get(), transformation_context));
 
   // %10 is pointer-to-float, and %14 has type pointer-to-float; that's not OK
   // since the initializer's type should be the *pointee* type.
-  ASSERT_FALSE(
-      TransformationAddGlobalVariable(104, 10, SpvStorageClassPrivate, 14, true)
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationAddGlobalVariable(
+                   104, 10, spv::StorageClass::Private, 14, true)
+                   .IsApplicable(context.get(), transformation_context));
 
   // This would work in principle, but logical addressing does not allow
   // a pointer to a pointer.
-  ASSERT_FALSE(TransformationAddGlobalVariable(104, 17, SpvStorageClassPrivate,
-                                               14, false)
+  ASSERT_FALSE(TransformationAddGlobalVariable(
+                   104, 17, spv::StorageClass::Private, 14, false)
                    .IsApplicable(context.get(), transformation_context));
 
   {
     // %100 = OpVariable %12 Private
     ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(100));
     TransformationAddGlobalVariable transformation(
-        100, 12, SpvStorageClassPrivate, 16, true);
+        100, 12, spv::StorageClass::Private, 16, true);
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
                           &transformation_context);
-    ASSERT_EQ(SpvOpVariable, context->get_def_use_mgr()->GetDef(100)->opcode());
+    ASSERT_EQ(spv::Op::OpVariable,
+              context->get_def_use_mgr()->GetDef(100)->opcode());
     ASSERT_EQ(
-        SpvStorageClassPrivate,
-        static_cast<SpvStorageClass>(
+        spv::StorageClass::Private,
+        static_cast<spv::StorageClass>(
             context->get_def_use_mgr()->GetDef(100)->GetSingleWordInOperand(
                 0)));
   }
 
   TransformationAddGlobalVariable transformations[] = {
       // %101 = OpVariable %10 Private
-      TransformationAddGlobalVariable(101, 10, SpvStorageClassPrivate, 40,
+      TransformationAddGlobalVariable(101, 10, spv::StorageClass::Private, 40,
                                       false),
 
       // %102 = OpVariable %13 Private
-      TransformationAddGlobalVariable(102, 13, SpvStorageClassPrivate, 41,
+      TransformationAddGlobalVariable(102, 13, spv::StorageClass::Private, 41,
                                       true),
 
       // %103 = OpVariable %12 Private %16
-      TransformationAddGlobalVariable(103, 12, SpvStorageClassPrivate, 16,
+      TransformationAddGlobalVariable(103, 12, spv::StorageClass::Private, 16,
                                       false),
 
       // %104 = OpVariable %19 Private %21
-      TransformationAddGlobalVariable(104, 19, SpvStorageClassPrivate, 21,
+      TransformationAddGlobalVariable(104, 19, spv::StorageClass::Private, 21,
                                       true),
 
       // %105 = OpVariable %19 Private %22
-      TransformationAddGlobalVariable(105, 19, SpvStorageClassPrivate, 22,
+      TransformationAddGlobalVariable(105, 19, spv::StorageClass::Private, 22,
                                       false)};
 
   for (auto& transformation : transformations) {
@@ -269,15 +270,15 @@
         MakeUnique<FactManager>(context.get()), validator_options);
     TransformationAddGlobalVariable transformations[] = {
         // %100 = OpVariable %12 Private
-        TransformationAddGlobalVariable(100, 12, SpvStorageClassPrivate, 16,
+        TransformationAddGlobalVariable(100, 12, spv::StorageClass::Private, 16,
                                         true),
 
         // %101 = OpVariable %12 Private %16
-        TransformationAddGlobalVariable(101, 12, SpvStorageClassPrivate, 16,
+        TransformationAddGlobalVariable(101, 12, spv::StorageClass::Private, 16,
                                         false),
 
         // %102 = OpVariable %19 Private %21
-        TransformationAddGlobalVariable(102, 19, SpvStorageClassPrivate, 21,
+        TransformationAddGlobalVariable(102, 19, spv::StorageClass::Private, 21,
                                         true)};
 
     for (auto& transformation : transformations) {
@@ -388,15 +389,15 @@
         MakeUnique<FactManager>(context.get()), validator_options);
     TransformationAddGlobalVariable transformations[] = {
         // %100 = OpVariable %12 Private
-        TransformationAddGlobalVariable(100, 12, SpvStorageClassPrivate, 16,
+        TransformationAddGlobalVariable(100, 12, spv::StorageClass::Private, 16,
                                         true),
 
         // %101 = OpVariable %12 Private %16
-        TransformationAddGlobalVariable(101, 12, SpvStorageClassPrivate, 16,
+        TransformationAddGlobalVariable(101, 12, spv::StorageClass::Private, 16,
                                         false),
 
         // %102 = OpVariable %19 Private %21
-        TransformationAddGlobalVariable(102, 19, SpvStorageClassPrivate, 21,
+        TransformationAddGlobalVariable(102, 19, spv::StorageClass::Private, 21,
                                         true)};
 
     for (auto& transformation : transformations) {
@@ -486,7 +487,8 @@
       MakeUnique<FactManager>(context.get()), validator_options);
 #ifndef NDEBUG
   ASSERT_DEATH(
-      TransformationAddGlobalVariable(8, 7, SpvStorageClassWorkgroup, 50, true)
+      TransformationAddGlobalVariable(8, 7, spv::StorageClass::Workgroup, 50,
+                                      true)
           .IsApplicable(context.get(), transformation_context),
       "By construction this transformation should not have an.*initializer "
       "when Workgroup storage class is used");
@@ -494,10 +496,11 @@
 
   TransformationAddGlobalVariable transformations[] = {
       // %8 = OpVariable %7 Workgroup
-      TransformationAddGlobalVariable(8, 7, SpvStorageClassWorkgroup, 0, true),
+      TransformationAddGlobalVariable(8, 7, spv::StorageClass::Workgroup, 0,
+                                      true),
 
       // %10 = OpVariable %7 Workgroup
-      TransformationAddGlobalVariable(10, 7, SpvStorageClassWorkgroup, 0,
+      TransformationAddGlobalVariable(10, 7, spv::StorageClass::Workgroup, 0,
                                       false)};
 
   for (auto& transformation : transformations) {
diff --git a/test/fuzz/transformation_add_image_sample_unused_components_test.cpp b/test/fuzz/transformation_add_image_sample_unused_components_test.cpp
index 072378c..7047544 100644
--- a/test/fuzz/transformation_add_image_sample_unused_components_test.cpp
+++ b/test/fuzz/transformation_add_image_sample_unused_components_test.cpp
@@ -73,14 +73,14 @@
       MakeUnique<FactManager>(context.get()), validator_options);
   // Tests applicable image instruction.
   auto instruction_descriptor =
-      MakeInstructionDescriptor(25, SpvOpImageSampleImplicitLod, 0);
+      MakeInstructionDescriptor(25, spv::Op::OpImageSampleImplicitLod, 0);
   auto transformation =
       TransformationAddImageSampleUnusedComponents(23, instruction_descriptor);
   ASSERT_TRUE(
       transformation.IsApplicable(context.get(), transformation_context));
 
   instruction_descriptor =
-      MakeInstructionDescriptor(26, SpvOpImageSampleExplicitLod, 0);
+      MakeInstructionDescriptor(26, spv::Op::OpImageSampleExplicitLod, 0);
   transformation =
       TransformationAddImageSampleUnusedComponents(24, instruction_descriptor);
   ASSERT_TRUE(
@@ -88,27 +88,27 @@
 
   // Tests undefined image instructions.
   instruction_descriptor =
-      MakeInstructionDescriptor(27, SpvOpImageSampleImplicitLod, 0);
+      MakeInstructionDescriptor(27, spv::Op::OpImageSampleImplicitLod, 0);
   transformation =
       TransformationAddImageSampleUnusedComponents(23, instruction_descriptor);
   ASSERT_FALSE(
       transformation.IsApplicable(context.get(), transformation_context));
 
   instruction_descriptor =
-      MakeInstructionDescriptor(28, SpvOpImageSampleExplicitLod, 0);
+      MakeInstructionDescriptor(28, spv::Op::OpImageSampleExplicitLod, 0);
   transformation =
       TransformationAddImageSampleUnusedComponents(23, instruction_descriptor);
   ASSERT_FALSE(
       transformation.IsApplicable(context.get(), transformation_context));
 
   // Tests non-image instructions.
-  instruction_descriptor = MakeInstructionDescriptor(19, SpvOpLabel, 0);
+  instruction_descriptor = MakeInstructionDescriptor(19, spv::Op::OpLabel, 0);
   transformation =
       TransformationAddImageSampleUnusedComponents(24, instruction_descriptor);
   ASSERT_FALSE(
       transformation.IsApplicable(context.get(), transformation_context));
 
-  instruction_descriptor = MakeInstructionDescriptor(20, SpvOpLoad, 0);
+  instruction_descriptor = MakeInstructionDescriptor(20, spv::Op::OpLoad, 0);
   transformation =
       TransformationAddImageSampleUnusedComponents(24, instruction_descriptor);
   ASSERT_FALSE(
@@ -116,7 +116,7 @@
 
   // Tests coordinate operand being a vec4.
   instruction_descriptor =
-      MakeInstructionDescriptor(27, SpvOpImageSampleExplicitLod, 0);
+      MakeInstructionDescriptor(27, spv::Op::OpImageSampleExplicitLod, 0);
   transformation =
       TransformationAddImageSampleUnusedComponents(22, instruction_descriptor);
   ASSERT_FALSE(
@@ -124,7 +124,7 @@
 
   // Tests undefined coordinate with unused operands.
   instruction_descriptor =
-      MakeInstructionDescriptor(25, SpvOpImageSampleImplicitLod, 0);
+      MakeInstructionDescriptor(25, spv::Op::OpImageSampleImplicitLod, 0);
   transformation =
       TransformationAddImageSampleUnusedComponents(27, instruction_descriptor);
   ASSERT_FALSE(
@@ -133,7 +133,7 @@
   // Tests coordinate with unused operands being a non-OpCompositeConstruct
   // instruction.
   instruction_descriptor =
-      MakeInstructionDescriptor(25, SpvOpImageSampleImplicitLod, 0);
+      MakeInstructionDescriptor(25, spv::Op::OpImageSampleImplicitLod, 0);
   transformation =
       TransformationAddImageSampleUnusedComponents(21, instruction_descriptor);
   ASSERT_FALSE(
@@ -142,7 +142,7 @@
   // Tests the first OpCompositeConstruct constituent not being the original
   // coordinate.
   instruction_descriptor =
-      MakeInstructionDescriptor(25, SpvOpImageSampleImplicitLod, 0);
+      MakeInstructionDescriptor(25, spv::Op::OpImageSampleImplicitLod, 0);
   transformation =
       TransformationAddImageSampleUnusedComponents(22, instruction_descriptor);
   ASSERT_FALSE(
@@ -198,13 +198,13 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
   auto instruction_descriptor =
-      MakeInstructionDescriptor(25, SpvOpImageSampleImplicitLod, 0);
+      MakeInstructionDescriptor(25, spv::Op::OpImageSampleImplicitLod, 0);
   auto transformation =
       TransformationAddImageSampleUnusedComponents(23, instruction_descriptor);
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
 
   instruction_descriptor =
-      MakeInstructionDescriptor(26, SpvOpImageSampleExplicitLod, 0);
+      MakeInstructionDescriptor(26, spv::Op::OpImageSampleExplicitLod, 0);
   transformation =
       TransformationAddImageSampleUnusedComponents(24, instruction_descriptor);
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
diff --git a/test/fuzz/transformation_add_local_variable_test.cpp b/test/fuzz/transformation_add_local_variable_test.cpp
index de88573..2af8597 100644
--- a/test/fuzz/transformation_add_local_variable_test.cpp
+++ b/test/fuzz/transformation_add_local_variable_test.cpp
@@ -104,7 +104,8 @@
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
                           &transformation_context);
-    ASSERT_EQ(SpvOpVariable, context->get_def_use_mgr()->GetDef(105)->opcode());
+    ASSERT_EQ(spv::Op::OpVariable,
+              context->get_def_use_mgr()->GetDef(105)->opcode());
     ASSERT_EQ(5, context->get_instr_block(105)->id());
   }
 
diff --git a/test/fuzz/transformation_add_opphi_synonym_test.cpp b/test/fuzz/transformation_add_opphi_synonym_test.cpp
index 3501f8e..03fd39a 100644
--- a/test/fuzz/transformation_add_opphi_synonym_test.cpp
+++ b/test/fuzz/transformation_add_opphi_synonym_test.cpp
@@ -365,8 +365,7 @@
       MakeSynonymFact(12, 16));
 
   // Remove the VariablePointers capability.
-  context.get()->get_feature_mgr()->RemoveCapability(
-      SpvCapabilityVariablePointers);
+  context.get()->RemoveCapability(spv::Capability::VariablePointers);
 
   // The VariablePointers capability is required to add an OpPhi instruction of
   // pointer type.
@@ -374,8 +373,7 @@
                    .IsApplicable(context.get(), transformation_context));
 
   // Add the VariablePointers capability back.
-  context.get()->get_feature_mgr()->AddCapability(
-      SpvCapabilityVariablePointers);
+  context.get()->AddCapability(spv::Capability::VariablePointers);
 
   // If the ids have pointer type, the storage class must be Workgroup or
   // StorageBuffer, but it is Function in this case.
diff --git a/test/fuzz/transformation_add_synonym_test.cpp b/test/fuzz/transformation_add_synonym_test.cpp
index ffcf1c9..385590e 100644
--- a/test/fuzz/transformation_add_synonym_test.cpp
+++ b/test/fuzz/transformation_add_synonym_test.cpp
@@ -77,7 +77,7 @@
       MakeUnique<FactManager>(context.get()), validator_options);
   transformation_context.GetFactManager()->AddFactIdIsIrrelevant(24);
 
-  auto insert_before = MakeInstructionDescriptor(22, SpvOpReturn, 0);
+  auto insert_before = MakeInstructionDescriptor(22, spv::Op::OpReturn, 0);
 
 #ifndef NDEBUG
   ASSERT_DEATH(
@@ -126,34 +126,34 @@
                      .IsApplicable(context.get(), transformation_context));
 
     // |insert_before| is invalid.
-    ASSERT_FALSE(
-        TransformationAddSynonym(9, synonym_type, 40,
-                                 MakeInstructionDescriptor(25, SpvOpStore, 0))
-            .IsApplicable(context.get(), transformation_context));
-
-    // Can't insert before |insert_before|.
-    ASSERT_FALSE(
-        TransformationAddSynonym(9, synonym_type, 40,
-                                 MakeInstructionDescriptor(5, SpvOpLabel, 0))
-            .IsApplicable(context.get(), transformation_context));
     ASSERT_FALSE(TransformationAddSynonym(
                      9, synonym_type, 40,
-                     MakeInstructionDescriptor(22, SpvOpVariable, 0))
+                     MakeInstructionDescriptor(25, spv::Op::OpStore, 0))
+                     .IsApplicable(context.get(), transformation_context));
+
+    // Can't insert before |insert_before|.
+    ASSERT_FALSE(TransformationAddSynonym(
+                     9, synonym_type, 40,
+                     MakeInstructionDescriptor(5, spv::Op::OpLabel, 0))
                      .IsApplicable(context.get(), transformation_context));
     ASSERT_FALSE(TransformationAddSynonym(
                      9, synonym_type, 40,
-                     MakeInstructionDescriptor(25, SpvOpFunctionEnd, 0))
+                     MakeInstructionDescriptor(22, spv::Op::OpVariable, 0))
+                     .IsApplicable(context.get(), transformation_context));
+    ASSERT_FALSE(TransformationAddSynonym(
+                     9, synonym_type, 40,
+                     MakeInstructionDescriptor(25, spv::Op::OpFunctionEnd, 0))
                      .IsApplicable(context.get(), transformation_context));
 
     // Domination rules are not satisfied.
-    ASSERT_FALSE(
-        TransformationAddSynonym(27, synonym_type, 40,
-                                 MakeInstructionDescriptor(27, SpvOpLoad, 0))
-            .IsApplicable(context.get(), transformation_context));
-    ASSERT_FALSE(
-        TransformationAddSynonym(27, synonym_type, 40,
-                                 MakeInstructionDescriptor(22, SpvOpStore, 1))
-            .IsApplicable(context.get(), transformation_context));
+    ASSERT_FALSE(TransformationAddSynonym(
+                     27, synonym_type, 40,
+                     MakeInstructionDescriptor(27, spv::Op::OpLoad, 0))
+                     .IsApplicable(context.get(), transformation_context));
+    ASSERT_FALSE(TransformationAddSynonym(
+                     27, synonym_type, 40,
+                     MakeInstructionDescriptor(22, spv::Op::OpStore, 1))
+                     .IsApplicable(context.get(), transformation_context));
   }
 }
 
@@ -212,7 +212,7 @@
                                                kConsoleMessageConsumer));
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
-  auto insert_before = MakeInstructionDescriptor(5, SpvOpReturn, 0);
+  auto insert_before = MakeInstructionDescriptor(5, spv::Op::OpReturn, 0);
 
   uint32_t fresh_id = 50;
   for (auto synonym_type : {protobufs::TransformationAddSynonym::ADD_ZERO,
@@ -374,7 +374,7 @@
                                                kConsoleMessageConsumer));
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
-  auto insert_before = MakeInstructionDescriptor(5, SpvOpReturn, 0);
+  auto insert_before = MakeInstructionDescriptor(5, spv::Op::OpReturn, 0);
 
   uint32_t fresh_id = 50;
   for (auto synonym_type : {protobufs::TransformationAddSynonym::LOGICAL_AND,
@@ -467,7 +467,7 @@
                                                kConsoleMessageConsumer));
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
-  auto insert_before = MakeInstructionDescriptor(5, SpvOpReturn, 0);
+  auto insert_before = MakeInstructionDescriptor(5, spv::Op::OpReturn, 0);
   const auto synonym_type = protobufs::TransformationAddSynonym::LOGICAL_AND;
 
   // Required constant is not present in the module.
@@ -505,7 +505,7 @@
                                                kConsoleMessageConsumer));
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
-  auto insert_before = MakeInstructionDescriptor(5, SpvOpReturn, 0);
+  auto insert_before = MakeInstructionDescriptor(5, spv::Op::OpReturn, 0);
   const auto synonym_type = protobufs::TransformationAddSynonym::LOGICAL_OR;
 
   // Required constant is not present in the module.
@@ -563,7 +563,7 @@
                                                kConsoleMessageConsumer));
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
-  auto insert_before = MakeInstructionDescriptor(5, SpvOpReturn, 0);
+  auto insert_before = MakeInstructionDescriptor(5, spv::Op::OpReturn, 0);
   const auto synonym_type = protobufs::TransformationAddSynonym::COPY_OBJECT;
 
   ASSERT_FALSE(
@@ -666,7 +666,7 @@
   {
     TransformationAddSynonym copy_true(
         7, protobufs::TransformationAddSynonym::COPY_OBJECT, 100,
-        MakeInstructionDescriptor(5, SpvOpReturn, 0));
+        MakeInstructionDescriptor(5, spv::Op::OpReturn, 0));
     ASSERT_TRUE(copy_true.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(copy_true, context.get(), &transformation_context);
 
@@ -687,7 +687,7 @@
   {
     TransformationAddSynonym copy_false(
         8, protobufs::TransformationAddSynonym::COPY_OBJECT, 101,
-        MakeInstructionDescriptor(100, SpvOpReturn, 0));
+        MakeInstructionDescriptor(100, spv::Op::OpReturn, 0));
     ASSERT_TRUE(copy_false.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(copy_false, context.get(), &transformation_context);
     std::vector<uint32_t> ids_for_which_synonyms_are_known =
@@ -707,7 +707,7 @@
   {
     TransformationAddSynonym copy_false_again(
         101, protobufs::TransformationAddSynonym::COPY_OBJECT, 102,
-        MakeInstructionDescriptor(5, SpvOpReturn, 0));
+        MakeInstructionDescriptor(5, spv::Op::OpReturn, 0));
     ASSERT_TRUE(
         copy_false_again.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(copy_false_again, context.get(),
@@ -730,7 +730,7 @@
   {
     TransformationAddSynonym copy_true_again(
         7, protobufs::TransformationAddSynonym::COPY_OBJECT, 103,
-        MakeInstructionDescriptor(102, SpvOpReturn, 0));
+        MakeInstructionDescriptor(102, spv::Op::OpReturn, 0));
     ASSERT_TRUE(
         copy_true_again.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(copy_true_again, context.get(),
@@ -970,128 +970,133 @@
   // Inapplicable because %18 is decorated.
   ASSERT_FALSE(TransformationAddSynonym(
                    18, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
-                   MakeInstructionDescriptor(21, SpvOpAccessChain, 0))
+                   MakeInstructionDescriptor(21, spv::Op::OpAccessChain, 0))
                    .IsApplicable(context.get(), transformation_context));
 
   // Inapplicable because %77 is decorated.
   ASSERT_FALSE(TransformationAddSynonym(
                    77, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
-                   MakeInstructionDescriptor(77, SpvOpBranch, 0))
+                   MakeInstructionDescriptor(77, spv::Op::OpBranch, 0))
                    .IsApplicable(context.get(), transformation_context));
 
   // Inapplicable because %80 is decorated.
   ASSERT_FALSE(TransformationAddSynonym(
                    80, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
-                   MakeInstructionDescriptor(77, SpvOpIAdd, 0))
+                   MakeInstructionDescriptor(77, spv::Op::OpIAdd, 0))
                    .IsApplicable(context.get(), transformation_context));
 
   // Inapplicable because %84 is not available at the requested point
-  ASSERT_FALSE(TransformationAddSynonym(
-                   84, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
-                   MakeInstructionDescriptor(32, SpvOpCompositeExtract, 0))
-                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationAddSynonym(
+          84, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
+          MakeInstructionDescriptor(32, spv::Op::OpCompositeExtract, 0))
+          .IsApplicable(context.get(), transformation_context));
 
   // Fine because %84 is available at the requested point
-  ASSERT_TRUE(TransformationAddSynonym(
-                  84, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
-                  MakeInstructionDescriptor(32, SpvOpCompositeConstruct, 0))
-                  .IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(
+      TransformationAddSynonym(
+          84, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
+          MakeInstructionDescriptor(32, spv::Op::OpCompositeConstruct, 0))
+          .IsApplicable(context.get(), transformation_context));
 
   // Inapplicable because id %9 is already in use
-  ASSERT_FALSE(TransformationAddSynonym(
-                   84, protobufs::TransformationAddSynonym::COPY_OBJECT, 9,
-                   MakeInstructionDescriptor(32, SpvOpCompositeConstruct, 0))
-                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationAddSynonym(
+          84, protobufs::TransformationAddSynonym::COPY_OBJECT, 9,
+          MakeInstructionDescriptor(32, spv::Op::OpCompositeConstruct, 0))
+          .IsApplicable(context.get(), transformation_context));
 
   // Inapplicable because the requested point does not exist
   ASSERT_FALSE(TransformationAddSynonym(
                    84, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
-                   MakeInstructionDescriptor(86, SpvOpReturn, 2))
+                   MakeInstructionDescriptor(86, spv::Op::OpReturn, 2))
                    .IsApplicable(context.get(), transformation_context));
 
   // Inapplicable because %9 is not in a function
   ASSERT_FALSE(TransformationAddSynonym(
                    9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
-                   MakeInstructionDescriptor(9, SpvOpTypeInt, 0))
+                   MakeInstructionDescriptor(9, spv::Op::OpTypeInt, 0))
                    .IsApplicable(context.get(), transformation_context));
 
   // Inapplicable because the insert point is right before, or inside, a chunk
   // of OpPhis
   ASSERT_FALSE(TransformationAddSynonym(
                    9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
-                   MakeInstructionDescriptor(30, SpvOpPhi, 0))
+                   MakeInstructionDescriptor(30, spv::Op::OpPhi, 0))
                    .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationAddSynonym(
                    9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
-                   MakeInstructionDescriptor(99, SpvOpPhi, 1))
+                   MakeInstructionDescriptor(99, spv::Op::OpPhi, 1))
                    .IsApplicable(context.get(), transformation_context));
 
   // OK, because the insert point is just after a chunk of OpPhis.
   ASSERT_TRUE(TransformationAddSynonym(
                   9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
-                  MakeInstructionDescriptor(96, SpvOpAccessChain, 0))
+                  MakeInstructionDescriptor(96, spv::Op::OpAccessChain, 0))
+                  .IsApplicable(context.get(), transformation_context));
+
+  // Inapplicable because the insert point is right after an OpSelectionMerge
+  ASSERT_FALSE(
+      TransformationAddSynonym(
+          9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
+          MakeInstructionDescriptor(58, spv::Op::OpBranchConditional, 0))
+          .IsApplicable(context.get(), transformation_context));
+
+  // OK, because the insert point is right before the OpSelectionMerge
+  ASSERT_TRUE(TransformationAddSynonym(
+                  9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
+                  MakeInstructionDescriptor(58, spv::Op::OpSelectionMerge, 0))
                   .IsApplicable(context.get(), transformation_context));
 
   // Inapplicable because the insert point is right after an OpSelectionMerge
   ASSERT_FALSE(TransformationAddSynonym(
                    9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
-                   MakeInstructionDescriptor(58, SpvOpBranchConditional, 0))
+                   MakeInstructionDescriptor(43, spv::Op::OpSwitch, 0))
                    .IsApplicable(context.get(), transformation_context));
 
   // OK, because the insert point is right before the OpSelectionMerge
   ASSERT_TRUE(TransformationAddSynonym(
                   9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
-                  MakeInstructionDescriptor(58, SpvOpSelectionMerge, 0))
-                  .IsApplicable(context.get(), transformation_context));
-
-  // Inapplicable because the insert point is right after an OpSelectionMerge
-  ASSERT_FALSE(TransformationAddSynonym(
-                   9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
-                   MakeInstructionDescriptor(43, SpvOpSwitch, 0))
-                   .IsApplicable(context.get(), transformation_context));
-
-  // OK, because the insert point is right before the OpSelectionMerge
-  ASSERT_TRUE(TransformationAddSynonym(
-                  9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
-                  MakeInstructionDescriptor(43, SpvOpSelectionMerge, 0))
+                  MakeInstructionDescriptor(43, spv::Op::OpSelectionMerge, 0))
                   .IsApplicable(context.get(), transformation_context));
 
   // Inapplicable because the insert point is right after an OpLoopMerge
-  ASSERT_FALSE(TransformationAddSynonym(
-                   9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
-                   MakeInstructionDescriptor(40, SpvOpBranchConditional, 0))
-                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationAddSynonym(
+          9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
+          MakeInstructionDescriptor(40, spv::Op::OpBranchConditional, 0))
+          .IsApplicable(context.get(), transformation_context));
 
   // OK, because the insert point is right before the OpLoopMerge
   ASSERT_TRUE(TransformationAddSynonym(
                   9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
-                  MakeInstructionDescriptor(40, SpvOpLoopMerge, 0))
+                  MakeInstructionDescriptor(40, spv::Op::OpLoopMerge, 0))
                   .IsApplicable(context.get(), transformation_context));
 
   // Inapplicable because id %300 does not exist
   ASSERT_FALSE(TransformationAddSynonym(
                    300, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
-                   MakeInstructionDescriptor(40, SpvOpLoopMerge, 0))
+                   MakeInstructionDescriptor(40, spv::Op::OpLoopMerge, 0))
                    .IsApplicable(context.get(), transformation_context));
 
   // Inapplicable because the following instruction is OpVariable
   ASSERT_FALSE(TransformationAddSynonym(
                    9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
-                   MakeInstructionDescriptor(180, SpvOpVariable, 0))
+                   MakeInstructionDescriptor(180, spv::Op::OpVariable, 0))
                    .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationAddSynonym(
                    9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
-                   MakeInstructionDescriptor(181, SpvOpVariable, 0))
+                   MakeInstructionDescriptor(181, spv::Op::OpVariable, 0))
                    .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationAddSynonym(
                    9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
-                   MakeInstructionDescriptor(182, SpvOpVariable, 0))
+                   MakeInstructionDescriptor(182, spv::Op::OpVariable, 0))
                    .IsApplicable(context.get(), transformation_context));
 
   // OK, because this is just past the group of OpVariable instructions.
   ASSERT_TRUE(TransformationAddSynonym(
                   9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
-                  MakeInstructionDescriptor(182, SpvOpAccessChain, 0))
+                  MakeInstructionDescriptor(182, spv::Op::OpAccessChain, 0))
                   .IsApplicable(context.get(), transformation_context));
 }
 
@@ -1163,25 +1168,25 @@
   std::vector<TransformationAddSynonym> transformations = {
       TransformationAddSynonym(
           19, protobufs::TransformationAddSynonym::COPY_OBJECT, 100,
-          MakeInstructionDescriptor(22, SpvOpStore, 0)),
+          MakeInstructionDescriptor(22, spv::Op::OpStore, 0)),
       TransformationAddSynonym(
           22, protobufs::TransformationAddSynonym::COPY_OBJECT, 101,
-          MakeInstructionDescriptor(22, SpvOpCopyObject, 0)),
+          MakeInstructionDescriptor(22, spv::Op::OpCopyObject, 0)),
       TransformationAddSynonym(
           12, protobufs::TransformationAddSynonym::COPY_OBJECT, 102,
-          MakeInstructionDescriptor(22, SpvOpCopyObject, 0)),
+          MakeInstructionDescriptor(22, spv::Op::OpCopyObject, 0)),
       TransformationAddSynonym(
           11, protobufs::TransformationAddSynonym::COPY_OBJECT, 103,
-          MakeInstructionDescriptor(22, SpvOpCopyObject, 0)),
+          MakeInstructionDescriptor(22, spv::Op::OpCopyObject, 0)),
       TransformationAddSynonym(
           16, protobufs::TransformationAddSynonym::COPY_OBJECT, 104,
-          MakeInstructionDescriptor(22, SpvOpCopyObject, 0)),
+          MakeInstructionDescriptor(22, spv::Op::OpCopyObject, 0)),
       TransformationAddSynonym(
           8, protobufs::TransformationAddSynonym::COPY_OBJECT, 105,
-          MakeInstructionDescriptor(22, SpvOpCopyObject, 0)),
+          MakeInstructionDescriptor(22, spv::Op::OpCopyObject, 0)),
       TransformationAddSynonym(
           17, protobufs::TransformationAddSynonym::COPY_OBJECT, 106,
-          MakeInstructionDescriptor(22, SpvOpCopyObject, 0))};
+          MakeInstructionDescriptor(22, spv::Op::OpCopyObject, 0))};
 
   for (auto& transformation : transformations) {
     ASSERT_TRUE(
@@ -1273,7 +1278,7 @@
   // Illegal to copy null.
   ASSERT_FALSE(TransformationAddSynonym(
                    8, protobufs::TransformationAddSynonym::COPY_OBJECT, 100,
-                   MakeInstructionDescriptor(5, SpvOpReturn, 0))
+                   MakeInstructionDescriptor(5, spv::Op::OpReturn, 0))
                    .IsApplicable(context.get(), transformation_context));
 }
 
@@ -1312,13 +1317,13 @@
 
   TransformationAddSynonym transformation1(
       8, protobufs::TransformationAddSynonym::COPY_OBJECT, 100,
-      MakeInstructionDescriptor(9, SpvOpReturn, 0));
+      MakeInstructionDescriptor(9, spv::Op::OpReturn, 0));
   TransformationAddSynonym transformation2(
       9, protobufs::TransformationAddSynonym::COPY_OBJECT, 101,
-      MakeInstructionDescriptor(9, SpvOpReturn, 0));
+      MakeInstructionDescriptor(9, spv::Op::OpReturn, 0));
   TransformationAddSynonym transformation3(
       100, protobufs::TransformationAddSynonym::COPY_OBJECT, 102,
-      MakeInstructionDescriptor(9, SpvOpReturn, 0));
+      MakeInstructionDescriptor(9, spv::Op::OpReturn, 0));
 
   ASSERT_TRUE(
       transformation1.IsApplicable(context.get(), transformation_context));
@@ -1397,7 +1402,7 @@
   ASSERT_FALSE(
       TransformationAddSynonym(
           216, protobufs::TransformationAddSynonym::COPY_OBJECT, 500,
-          MakeInstructionDescriptor(217, SpvOpImageSampleImplicitLod, 0))
+          MakeInstructionDescriptor(217, spv::Op::OpImageSampleImplicitLod, 0))
           .IsApplicable(context.get(), transformation_context));
 }
 
@@ -1434,7 +1439,7 @@
       MakeUnique<FactManager>(context.get()), validator_options);
   ASSERT_FALSE(TransformationAddSynonym(
                    8, protobufs::TransformationAddSynonym::COPY_OBJECT, 500,
-                   MakeInstructionDescriptor(8, SpvOpReturn, 0))
+                   MakeInstructionDescriptor(8, spv::Op::OpReturn, 0))
                    .IsApplicable(context.get(), transformation_context));
 }
 
@@ -1475,7 +1480,7 @@
 
   transformation_context.GetFactManager()->AddFactBlockIsDead(9);
 
-  auto insert_before = MakeInstructionDescriptor(9, SpvOpBranch, 0);
+  auto insert_before = MakeInstructionDescriptor(9, spv::Op::OpBranch, 0);
 
   ASSERT_FALSE(TransformationAddSynonym(
                    7, protobufs::TransformationAddSynonym::COPY_OBJECT, 100,
diff --git a/test/fuzz/transformation_add_type_array_test.cpp b/test/fuzz/transformation_add_type_array_test.cpp
index 2ef8200..ab480fb 100644
--- a/test/fuzz/transformation_add_type_array_test.cpp
+++ b/test/fuzz/transformation_add_type_array_test.cpp
@@ -99,7 +99,7 @@
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
                           &transformation_context);
-    ASSERT_EQ(SpvOpTypeArray,
+    ASSERT_EQ(spv::Op::OpTypeArray,
               context->get_def_use_mgr()->GetDef(100)->opcode());
     ASSERT_NE(nullptr, context->get_type_mgr()->GetType(100)->AsArray());
   }
@@ -113,7 +113,7 @@
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
                           &transformation_context);
-    ASSERT_EQ(SpvOpTypeArray,
+    ASSERT_EQ(spv::Op::OpTypeArray,
               context->get_def_use_mgr()->GetDef(100)->opcode());
     ASSERT_NE(nullptr, context->get_type_mgr()->GetType(100)->AsArray());
   }
diff --git a/test/fuzz/transformation_add_type_boolean_test.cpp b/test/fuzz/transformation_add_type_boolean_test.cpp
index a8e657b..388d4f6 100644
--- a/test/fuzz/transformation_add_type_boolean_test.cpp
+++ b/test/fuzz/transformation_add_type_boolean_test.cpp
@@ -57,7 +57,8 @@
   ASSERT_TRUE(
       add_type_bool.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(add_type_bool, context.get(), &transformation_context);
-  ASSERT_EQ(SpvOpTypeBool, context->get_def_use_mgr()->GetDef(100)->opcode());
+  ASSERT_EQ(spv::Op::OpTypeBool,
+            context->get_def_use_mgr()->GetDef(100)->opcode());
   ASSERT_NE(nullptr, context->get_type_mgr()->GetType(100)->AsBool());
   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
                                                kConsoleMessageConsumer));
diff --git a/test/fuzz/transformation_add_type_float_test.cpp b/test/fuzz/transformation_add_type_float_test.cpp
index 9275bec..135190a 100644
--- a/test/fuzz/transformation_add_type_float_test.cpp
+++ b/test/fuzz/transformation_add_type_float_test.cpp
@@ -74,7 +74,7 @@
 
   // By default, SPIR-V does not support 64-bit float types.
   // Below we add such capability, so the test should now pass.
-  context.get()->get_feature_mgr()->AddCapability(SpvCapabilityFloat64);
+  context.get()->AddCapability(spv::Capability::Float64);
   ASSERT_TRUE(TransformationAddTypeFloat(7, 64).IsApplicable(
       context.get(), transformation_context));
 
@@ -120,7 +120,8 @@
   ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(6));
   ASSERT_EQ(nullptr, context->get_type_mgr()->GetType(6));
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
-  ASSERT_EQ(SpvOpTypeFloat, context->get_def_use_mgr()->GetDef(6)->opcode());
+  ASSERT_EQ(spv::Op::OpTypeFloat,
+            context->get_def_use_mgr()->GetDef(6)->opcode());
   ASSERT_NE(nullptr, context->get_type_mgr()->GetType(6)->AsFloat());
 
   // Adds 32-bit float type.
@@ -128,7 +129,8 @@
   ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(7));
   ASSERT_EQ(nullptr, context->get_type_mgr()->GetType(7));
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
-  ASSERT_EQ(SpvOpTypeFloat, context->get_def_use_mgr()->GetDef(7)->opcode());
+  ASSERT_EQ(spv::Op::OpTypeFloat,
+            context->get_def_use_mgr()->GetDef(7)->opcode());
   ASSERT_NE(nullptr, context->get_type_mgr()->GetType(7)->AsFloat());
 
   // Adds 64-bit float type.
@@ -136,7 +138,8 @@
   ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(8));
   ASSERT_EQ(nullptr, context->get_type_mgr()->GetType(8));
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
-  ASSERT_EQ(SpvOpTypeFloat, context->get_def_use_mgr()->GetDef(8)->opcode());
+  ASSERT_EQ(spv::Op::OpTypeFloat,
+            context->get_def_use_mgr()->GetDef(8)->opcode());
   ASSERT_NE(nullptr, context->get_type_mgr()->GetType(8)->AsFloat());
 
   std::string variant_shader = R"(
diff --git a/test/fuzz/transformation_add_type_int_test.cpp b/test/fuzz/transformation_add_type_int_test.cpp
index 4cbfed0..e31730c 100644
--- a/test/fuzz/transformation_add_type_int_test.cpp
+++ b/test/fuzz/transformation_add_type_int_test.cpp
@@ -88,13 +88,13 @@
 
   // By default SPIR-V does not support 16-bit integers.
   // Below we add such capability, so the test should now be successful.
-  context.get()->get_feature_mgr()->AddCapability(SpvCapabilityInt16);
+  context.get()->AddCapability(spv::Capability::Int16);
   ASSERT_TRUE(TransformationAddTypeInt(7, 16, true)
                   .IsApplicable(context.get(), transformation_context));
 
   // By default SPIR-V does not support 64-bit integers.
   // Below we add such capability, so the test should now pass.
-  context.get()->get_feature_mgr()->AddCapability(SpvCapabilityInt64);
+  context.get()->AddCapability(spv::Capability::Int64);
   ASSERT_TRUE(TransformationAddTypeInt(7, 64, true)
                   .IsApplicable(context.get(), transformation_context));
 
@@ -147,7 +147,8 @@
   ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(6));
   ASSERT_EQ(nullptr, context->get_type_mgr()->GetType(6));
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
-  ASSERT_EQ(SpvOpTypeInt, context->get_def_use_mgr()->GetDef(6)->opcode());
+  ASSERT_EQ(spv::Op::OpTypeInt,
+            context->get_def_use_mgr()->GetDef(6)->opcode());
   ASSERT_NE(nullptr, context->get_type_mgr()->GetType(6)->AsInteger());
 
   // Adds signed 16-bit integer type.
diff --git a/test/fuzz/transformation_add_type_matrix_test.cpp b/test/fuzz/transformation_add_type_matrix_test.cpp
index df0111e..9b10dbd 100644
--- a/test/fuzz/transformation_add_type_matrix_test.cpp
+++ b/test/fuzz/transformation_add_type_matrix_test.cpp
@@ -72,7 +72,7 @@
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
                           &transformation_context);
-    ASSERT_EQ(SpvOpTypeMatrix,
+    ASSERT_EQ(spv::Op::OpTypeMatrix,
               context->get_def_use_mgr()->GetDef(100)->opcode());
     ASSERT_NE(nullptr, context->get_type_mgr()->GetType(100)->AsMatrix());
   }
diff --git a/test/fuzz/transformation_add_type_pointer_test.cpp b/test/fuzz/transformation_add_type_pointer_test.cpp
index b9072e3..5e25f0e 100644
--- a/test/fuzz/transformation_add_type_pointer_test.cpp
+++ b/test/fuzz/transformation_add_type_pointer_test.cpp
@@ -103,28 +103,28 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
   auto bad_type_id_does_not_exist =
-      TransformationAddTypePointer(100, SpvStorageClassFunction, 101);
+      TransformationAddTypePointer(100, spv::StorageClass::Function, 101);
   auto bad_type_id_is_not_type =
-      TransformationAddTypePointer(100, SpvStorageClassFunction, 23);
+      TransformationAddTypePointer(100, spv::StorageClass::Function, 23);
   auto bad_result_id_is_not_fresh =
-      TransformationAddTypePointer(17, SpvStorageClassFunction, 21);
+      TransformationAddTypePointer(17, spv::StorageClass::Function, 21);
 
   auto good_new_private_pointer_to_t =
-      TransformationAddTypePointer(101, SpvStorageClassPrivate, 7);
+      TransformationAddTypePointer(101, spv::StorageClass::Private, 7);
   auto good_new_uniform_pointer_to_t =
-      TransformationAddTypePointer(102, SpvStorageClassUniform, 7);
+      TransformationAddTypePointer(102, spv::StorageClass::Uniform, 7);
   auto good_another_function_pointer_to_s =
-      TransformationAddTypePointer(103, SpvStorageClassFunction, 8);
+      TransformationAddTypePointer(103, spv::StorageClass::Function, 8);
   auto good_new_uniform_pointer_to_s =
-      TransformationAddTypePointer(104, SpvStorageClassUniform, 8);
+      TransformationAddTypePointer(104, spv::StorageClass::Uniform, 8);
   auto good_another_private_pointer_to_float =
-      TransformationAddTypePointer(105, SpvStorageClassPrivate, 21);
+      TransformationAddTypePointer(105, spv::StorageClass::Private, 21);
   auto good_new_private_pointer_to_private_pointer_to_float =
-      TransformationAddTypePointer(106, SpvStorageClassPrivate, 105);
+      TransformationAddTypePointer(106, spv::StorageClass::Private, 105);
   auto good_new_uniform_pointer_to_vec2 =
-      TransformationAddTypePointer(107, SpvStorageClassUniform, 24);
+      TransformationAddTypePointer(107, spv::StorageClass::Uniform, 24);
   auto good_new_private_pointer_to_uniform_pointer_to_vec2 =
-      TransformationAddTypePointer(108, SpvStorageClassPrivate, 107);
+      TransformationAddTypePointer(108, spv::StorageClass::Private, 107);
 
   ASSERT_FALSE(bad_type_id_does_not_exist.IsApplicable(context.get(),
                                                        transformation_context));
@@ -143,7 +143,7 @@
                           &transformation_context);
     ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
         context.get(), validator_options, kConsoleMessageConsumer));
-    ASSERT_EQ(SpvOpTypePointer,
+    ASSERT_EQ(spv::Op::OpTypePointer,
               context->get_def_use_mgr()->GetDef(101)->opcode());
     ASSERT_NE(nullptr, context->get_type_mgr()->GetType(101)->AsPointer());
   }
diff --git a/test/fuzz/transformation_add_type_struct_test.cpp b/test/fuzz/transformation_add_type_struct_test.cpp
index 7fb91ab..1440b4a 100644
--- a/test/fuzz/transformation_add_type_struct_test.cpp
+++ b/test/fuzz/transformation_add_type_struct_test.cpp
@@ -72,7 +72,7 @@
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
                           &transformation_context);
-    ASSERT_EQ(SpvOpTypeStruct,
+    ASSERT_EQ(spv::Op::OpTypeStruct,
               context->get_def_use_mgr()->GetDef(100)->opcode());
     ASSERT_NE(nullptr, context->get_type_mgr()->GetType(100)->AsStruct());
   }
diff --git a/test/fuzz/transformation_add_type_vector_test.cpp b/test/fuzz/transformation_add_type_vector_test.cpp
index 755bc4a..b39c1c9 100644
--- a/test/fuzz/transformation_add_type_vector_test.cpp
+++ b/test/fuzz/transformation_add_type_vector_test.cpp
@@ -66,7 +66,7 @@
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
                           &transformation_context);
-    ASSERT_EQ(SpvOpTypeVector,
+    ASSERT_EQ(spv::Op::OpTypeVector,
               context->get_def_use_mgr()->GetDef(100)->opcode());
     ASSERT_NE(nullptr, context->get_type_mgr()->GetType(100)->AsVector());
   }
diff --git a/test/fuzz/transformation_adjust_branch_weights_test.cpp b/test/fuzz/transformation_adjust_branch_weights_test.cpp
index 5984a3e..2b532bf 100644
--- a/test/fuzz/transformation_adjust_branch_weights_test.cpp
+++ b/test/fuzz/transformation_adjust_branch_weights_test.cpp
@@ -108,7 +108,7 @@
       MakeUnique<FactManager>(context.get()), validator_options);
   // Tests OpBranchConditional instruction with weights.
   auto instruction_descriptor =
-      MakeInstructionDescriptor(33, SpvOpBranchConditional, 0);
+      MakeInstructionDescriptor(33, spv::Op::OpBranchConditional, 0);
   auto transformation =
       TransformationAdjustBranchWeights(instruction_descriptor, {0, 1});
   ASSERT_TRUE(
@@ -116,7 +116,7 @@
 
   // Tests the two branch weights equal to 0.
   instruction_descriptor =
-      MakeInstructionDescriptor(33, SpvOpBranchConditional, 0);
+      MakeInstructionDescriptor(33, spv::Op::OpBranchConditional, 0);
   transformation =
       TransformationAdjustBranchWeights(instruction_descriptor, {0, 0});
 #ifndef NDEBUG
@@ -127,14 +127,14 @@
 
   // Tests 32-bit unsigned integer overflow.
   instruction_descriptor =
-      MakeInstructionDescriptor(33, SpvOpBranchConditional, 0);
+      MakeInstructionDescriptor(33, spv::Op::OpBranchConditional, 0);
   transformation = TransformationAdjustBranchWeights(instruction_descriptor,
                                                      {UINT32_MAX, 0});
   ASSERT_TRUE(
       transformation.IsApplicable(context.get(), transformation_context));
 
   instruction_descriptor =
-      MakeInstructionDescriptor(33, SpvOpBranchConditional, 0);
+      MakeInstructionDescriptor(33, spv::Op::OpBranchConditional, 0);
   transformation = TransformationAdjustBranchWeights(instruction_descriptor,
                                                      {1, UINT32_MAX});
 #ifndef NDEBUG
@@ -145,26 +145,26 @@
 
   // Tests OpBranchConditional instruction with no weights.
   instruction_descriptor =
-      MakeInstructionDescriptor(21, SpvOpBranchConditional, 0);
+      MakeInstructionDescriptor(21, spv::Op::OpBranchConditional, 0);
   transformation =
       TransformationAdjustBranchWeights(instruction_descriptor, {0, 1});
   ASSERT_TRUE(
       transformation.IsApplicable(context.get(), transformation_context));
 
   // Tests non-OpBranchConditional instructions.
-  instruction_descriptor = MakeInstructionDescriptor(2, SpvOpTypeVoid, 0);
+  instruction_descriptor = MakeInstructionDescriptor(2, spv::Op::OpTypeVoid, 0);
   transformation =
       TransformationAdjustBranchWeights(instruction_descriptor, {5, 6});
   ASSERT_FALSE(
       transformation.IsApplicable(context.get(), transformation_context));
 
-  instruction_descriptor = MakeInstructionDescriptor(20, SpvOpLabel, 0);
+  instruction_descriptor = MakeInstructionDescriptor(20, spv::Op::OpLabel, 0);
   transformation =
       TransformationAdjustBranchWeights(instruction_descriptor, {1, 2});
   ASSERT_FALSE(
       transformation.IsApplicable(context.get(), transformation_context));
 
-  instruction_descriptor = MakeInstructionDescriptor(49, SpvOpIAdd, 0);
+  instruction_descriptor = MakeInstructionDescriptor(49, spv::Op::OpIAdd, 0);
   transformation =
       TransformationAdjustBranchWeights(instruction_descriptor, {1, 2});
   ASSERT_FALSE(
@@ -255,13 +255,13 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
   auto instruction_descriptor =
-      MakeInstructionDescriptor(33, SpvOpBranchConditional, 0);
+      MakeInstructionDescriptor(33, spv::Op::OpBranchConditional, 0);
   auto transformation =
       TransformationAdjustBranchWeights(instruction_descriptor, {5, 6});
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
 
   instruction_descriptor =
-      MakeInstructionDescriptor(21, SpvOpBranchConditional, 0);
+      MakeInstructionDescriptor(21, spv::Op::OpBranchConditional, 0);
   transformation =
       TransformationAdjustBranchWeights(instruction_descriptor, {7, 8});
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
diff --git a/test/fuzz/transformation_composite_construct_test.cpp b/test/fuzz/transformation_composite_construct_test.cpp
index 3c5f731..c4eef87 100644
--- a/test/fuzz/transformation_composite_construct_test.cpp
+++ b/test/fuzz/transformation_composite_construct_test.cpp
@@ -136,16 +136,17 @@
       MakeUnique<FactManager>(context.get()), validator_options);
   // Make a vec2[3]
   TransformationCompositeConstruct make_vec2_array_length_3(
-      37, {41, 45, 27}, MakeInstructionDescriptor(46, SpvOpAccessChain, 0),
-      200);
+      37, {41, 45, 27},
+      MakeInstructionDescriptor(46, spv::Op::OpAccessChain, 0), 200);
   // Bad: there are too many components
   TransformationCompositeConstruct make_vec2_array_length_3_bad(
-      37, {41, 45, 27, 27}, MakeInstructionDescriptor(46, SpvOpAccessChain, 0),
-      200);
+      37, {41, 45, 27, 27},
+      MakeInstructionDescriptor(46, spv::Op::OpAccessChain, 0), 200);
   // The first component does not correspond to an instruction with a result
   // type so this check should return false.
   TransformationCompositeConstruct make_vec2_array_length_3_nores(
-      37, {2, 45, 27}, MakeInstructionDescriptor(46, SpvOpAccessChain, 0), 200);
+      37, {2, 45, 27}, MakeInstructionDescriptor(46, spv::Op::OpAccessChain, 0),
+      200);
   ASSERT_TRUE(make_vec2_array_length_3.IsApplicable(context.get(),
                                                     transformation_context));
   ASSERT_FALSE(make_vec2_array_length_3_bad.IsApplicable(
@@ -159,7 +160,7 @@
   uint32_t num_uses_of_27_before = context->get_def_use_mgr()->NumUses(27);
   ApplyAndCheckFreshIds(make_vec2_array_length_3, context.get(),
                         &transformation_context);
-  ASSERT_EQ(SpvOpCompositeConstruct,
+  ASSERT_EQ(spv::Op::OpCompositeConstruct,
             context->get_def_use_mgr()->GetDef(200)->opcode());
   ASSERT_EQ(34, context->get_instr_block(200)->id());
   ASSERT_EQ(num_uses_of_41_before + 1, context->get_def_use_mgr()->NumUses(41));
@@ -176,10 +177,10 @@
 
   // Make a float[2]
   TransformationCompositeConstruct make_float_array_length_2(
-      9, {24, 40}, MakeInstructionDescriptor(71, SpvOpStore, 0), 201);
+      9, {24, 40}, MakeInstructionDescriptor(71, spv::Op::OpStore, 0), 201);
   // Bad: %41 does not have type float
   TransformationCompositeConstruct make_float_array_length_2_bad(
-      9, {41, 40}, MakeInstructionDescriptor(71, SpvOpStore, 0), 201);
+      9, {41, 40}, MakeInstructionDescriptor(71, spv::Op::OpStore, 0), 201);
   ASSERT_TRUE(make_float_array_length_2.IsApplicable(context.get(),
                                                      transformation_context));
   ASSERT_FALSE(make_float_array_length_2_bad.IsApplicable(
@@ -195,12 +196,12 @@
 
   // Make a bool[3]
   TransformationCompositeConstruct make_bool_array_length_3(
-      47, {33, 50, 50}, MakeInstructionDescriptor(33, SpvOpSelectionMerge, 0),
-      202);
+      47, {33, 50, 50},
+      MakeInstructionDescriptor(33, spv::Op::OpSelectionMerge, 0), 202);
   // Bad: %54 is not available at the desired program point.
   TransformationCompositeConstruct make_bool_array_length_3_bad(
-      47, {33, 54, 50}, MakeInstructionDescriptor(33, SpvOpSelectionMerge, 0),
-      202);
+      47, {33, 54, 50},
+      MakeInstructionDescriptor(33, spv::Op::OpSelectionMerge, 0), 202);
   ASSERT_TRUE(make_bool_array_length_3.IsApplicable(context.get(),
                                                     transformation_context));
   ASSERT_FALSE(make_bool_array_length_3_bad.IsApplicable(
@@ -218,10 +219,10 @@
 
   // make a uvec3[2][2]
   TransformationCompositeConstruct make_uvec3_array_length_2_2(
-      58, {69, 100}, MakeInstructionDescriptor(64, SpvOpStore, 0), 203);
+      58, {69, 100}, MakeInstructionDescriptor(64, spv::Op::OpStore, 0), 203);
   // Bad: Skip count 100 is too large.
   TransformationCompositeConstruct make_uvec3_array_length_2_2_bad(
-      58, {33, 54}, MakeInstructionDescriptor(64, SpvOpStore, 100), 203);
+      58, {33, 54}, MakeInstructionDescriptor(64, spv::Op::OpStore, 100), 203);
   ASSERT_TRUE(make_uvec3_array_length_2_2.IsApplicable(context.get(),
                                                        transformation_context));
   ASSERT_FALSE(make_uvec3_array_length_2_2_bad.IsApplicable(
@@ -425,14 +426,17 @@
       MakeUnique<FactManager>(context.get()), validator_options);
   // make a mat3x4
   TransformationCompositeConstruct make_mat34(
-      32, {25, 28, 31}, MakeInstructionDescriptor(31, SpvOpReturn, 0), 200);
+      32, {25, 28, 31}, MakeInstructionDescriptor(31, spv::Op::OpReturn, 0),
+      200);
   // Bad: %35 is mat4x3, not mat3x4.
   TransformationCompositeConstruct make_mat34_bad(
-      35, {25, 28, 31}, MakeInstructionDescriptor(31, SpvOpReturn, 0), 200);
+      35, {25, 28, 31}, MakeInstructionDescriptor(31, spv::Op::OpReturn, 0),
+      200);
   // The first component does not correspond to an instruction with a result
   // type so this check should return false.
   TransformationCompositeConstruct make_mat34_nores(
-      32, {2, 28, 31}, MakeInstructionDescriptor(31, SpvOpReturn, 0), 200);
+      32, {2, 28, 31}, MakeInstructionDescriptor(31, spv::Op::OpReturn, 0),
+      200);
   ASSERT_TRUE(make_mat34.IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       make_mat34_bad.IsApplicable(context.get(), transformation_context));
@@ -450,10 +454,12 @@
 
   // make a mat4x3
   TransformationCompositeConstruct make_mat43(
-      35, {11, 13, 16, 100}, MakeInstructionDescriptor(31, SpvOpStore, 0), 201);
+      35, {11, 13, 16, 100}, MakeInstructionDescriptor(31, spv::Op::OpStore, 0),
+      201);
   // Bad: %25 does not match the matrix's column type.
   TransformationCompositeConstruct make_mat43_bad(
-      35, {25, 13, 16, 100}, MakeInstructionDescriptor(31, SpvOpStore, 0), 201);
+      35, {25, 13, 16, 100}, MakeInstructionDescriptor(31, spv::Op::OpStore, 0),
+      201);
   ASSERT_TRUE(make_mat43.IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       make_mat43_bad.IsApplicable(context.get(), transformation_context));
@@ -644,14 +650,16 @@
       MakeUnique<FactManager>(context.get()), validator_options);
   // make an Inner
   TransformationCompositeConstruct make_inner(
-      9, {25, 19}, MakeInstructionDescriptor(57, SpvOpAccessChain, 0), 200);
+      9, {25, 19}, MakeInstructionDescriptor(57, spv::Op::OpAccessChain, 0),
+      200);
   // Bad: Too few fields to make the struct.
   TransformationCompositeConstruct make_inner_bad(
-      9, {25}, MakeInstructionDescriptor(57, SpvOpAccessChain, 0), 200);
+      9, {25}, MakeInstructionDescriptor(57, spv::Op::OpAccessChain, 0), 200);
   // The first component does not correspond to an instruction with a result
   // type so this check should return false.
   TransformationCompositeConstruct make_inner_nores(
-      9, {2, 19}, MakeInstructionDescriptor(57, SpvOpAccessChain, 0), 200);
+      9, {2, 19}, MakeInstructionDescriptor(57, spv::Op::OpAccessChain, 0),
+      200);
   ASSERT_TRUE(make_inner.IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       make_inner_bad.IsApplicable(context.get(), transformation_context));
@@ -667,12 +675,12 @@
 
   // make an Outer
   TransformationCompositeConstruct make_outer(
-      33, {46, 200, 56}, MakeInstructionDescriptor(200, SpvOpAccessChain, 0),
-      201);
+      33, {46, 200, 56},
+      MakeInstructionDescriptor(200, spv::Op::OpAccessChain, 0), 201);
   // Bad: %200 is not available at the desired program point.
   TransformationCompositeConstruct make_outer_bad(
       33, {46, 200, 56},
-      MakeInstructionDescriptor(200, SpvOpCompositeConstruct, 0), 201);
+      MakeInstructionDescriptor(200, spv::Op::OpCompositeConstruct, 0), 201);
   ASSERT_TRUE(make_outer.IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       make_outer_bad.IsApplicable(context.get(), transformation_context));
@@ -973,10 +981,10 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
   TransformationCompositeConstruct make_vec2(
-      7, {17, 11}, MakeInstructionDescriptor(100, SpvOpStore, 0), 200);
+      7, {17, 11}, MakeInstructionDescriptor(100, spv::Op::OpStore, 0), 200);
   // Bad: not enough data for a vec2
   TransformationCompositeConstruct make_vec2_bad(
-      7, {11}, MakeInstructionDescriptor(100, SpvOpStore, 0), 200);
+      7, {11}, MakeInstructionDescriptor(100, spv::Op::OpStore, 0), 200);
   ASSERT_TRUE(make_vec2.IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       make_vec2_bad.IsApplicable(context.get(), transformation_context));
@@ -989,12 +997,12 @@
       MakeDataDescriptor(11, {}), MakeDataDescriptor(200, {1})));
 
   TransformationCompositeConstruct make_vec3(
-      25, {12, 32}, MakeInstructionDescriptor(35, SpvOpCompositeConstruct, 0),
-      201);
+      25, {12, 32},
+      MakeInstructionDescriptor(35, spv::Op::OpCompositeConstruct, 0), 201);
   // Bad: too much data for a vec3
   TransformationCompositeConstruct make_vec3_bad(
       25, {12, 32, 32},
-      MakeInstructionDescriptor(35, SpvOpCompositeConstruct, 0), 201);
+      MakeInstructionDescriptor(35, spv::Op::OpCompositeConstruct, 0), 201);
   ASSERT_TRUE(make_vec3.IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       make_vec3_bad.IsApplicable(context.get(), transformation_context));
@@ -1009,12 +1017,12 @@
       MakeDataDescriptor(32, {}), MakeDataDescriptor(201, {2})));
 
   TransformationCompositeConstruct make_vec4(
-      44, {32, 32, 10, 11}, MakeInstructionDescriptor(75, SpvOpAccessChain, 0),
-      202);
+      44, {32, 32, 10, 11},
+      MakeInstructionDescriptor(75, spv::Op::OpAccessChain, 0), 202);
   // Bad: id 48 is not available at the insertion points
   TransformationCompositeConstruct make_vec4_bad(
-      44, {48, 32, 10, 11}, MakeInstructionDescriptor(75, SpvOpAccessChain, 0),
-      202);
+      44, {48, 32, 10, 11},
+      MakeInstructionDescriptor(75, spv::Op::OpAccessChain, 0), 202);
   ASSERT_TRUE(make_vec4.IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       make_vec4_bad.IsApplicable(context.get(), transformation_context));
@@ -1031,10 +1039,10 @@
       MakeDataDescriptor(11, {}), MakeDataDescriptor(202, {3})));
 
   TransformationCompositeConstruct make_ivec2(
-      51, {126, 120}, MakeInstructionDescriptor(128, SpvOpLoad, 0), 203);
+      51, {126, 120}, MakeInstructionDescriptor(128, spv::Op::OpLoad, 0), 203);
   // Bad: if 128 is not available at the instruction that defines 128
   TransformationCompositeConstruct make_ivec2_bad(
-      51, {128, 120}, MakeInstructionDescriptor(128, SpvOpLoad, 0), 203);
+      51, {128, 120}, MakeInstructionDescriptor(128, spv::Op::OpLoad, 0), 203);
   ASSERT_TRUE(make_ivec2.IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       make_ivec2_bad.IsApplicable(context.get(), transformation_context));
@@ -1047,12 +1055,12 @@
       MakeDataDescriptor(120, {}), MakeDataDescriptor(203, {1})));
 
   TransformationCompositeConstruct make_ivec3(
-      114, {56, 117, 56}, MakeInstructionDescriptor(66, SpvOpAccessChain, 0),
-      204);
+      114, {56, 117, 56},
+      MakeInstructionDescriptor(66, spv::Op::OpAccessChain, 0), 204);
   // Bad because 1300 is not an id
   TransformationCompositeConstruct make_ivec3_bad(
-      114, {56, 117, 1300}, MakeInstructionDescriptor(66, SpvOpAccessChain, 0),
-      204);
+      114, {56, 117, 1300},
+      MakeInstructionDescriptor(66, spv::Op::OpAccessChain, 0), 204);
   ASSERT_TRUE(make_ivec3.IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       make_ivec3_bad.IsApplicable(context.get(), transformation_context));
@@ -1067,12 +1075,12 @@
       MakeDataDescriptor(56, {}), MakeDataDescriptor(204, {2})));
 
   TransformationCompositeConstruct make_ivec4(
-      122, {56, 117, 117, 117}, MakeInstructionDescriptor(66, SpvOpIAdd, 0),
-      205);
+      122, {56, 117, 117, 117},
+      MakeInstructionDescriptor(66, spv::Op::OpIAdd, 0), 205);
   // Bad because 86 is the wrong type.
   TransformationCompositeConstruct make_ivec4_bad(
-      86, {56, 117, 117, 117}, MakeInstructionDescriptor(66, SpvOpIAdd, 0),
-      205);
+      86, {56, 117, 117, 117},
+      MakeInstructionDescriptor(66, spv::Op::OpIAdd, 0), 205);
   ASSERT_TRUE(make_ivec4.IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       make_ivec4_bad.IsApplicable(context.get(), transformation_context));
@@ -1089,9 +1097,11 @@
       MakeDataDescriptor(117, {}), MakeDataDescriptor(205, {3})));
 
   TransformationCompositeConstruct make_uvec2(
-      86, {18, 38}, MakeInstructionDescriptor(133, SpvOpAccessChain, 0), 206);
+      86, {18, 38}, MakeInstructionDescriptor(133, spv::Op::OpAccessChain, 0),
+      206);
   TransformationCompositeConstruct make_uvec2_bad(
-      86, {18, 38}, MakeInstructionDescriptor(133, SpvOpAccessChain, 200), 206);
+      86, {18, 38}, MakeInstructionDescriptor(133, spv::Op::OpAccessChain, 200),
+      206);
   ASSERT_TRUE(make_uvec2.IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       make_uvec2_bad.IsApplicable(context.get(), transformation_context));
@@ -1104,10 +1114,12 @@
       MakeDataDescriptor(38, {}), MakeDataDescriptor(206, {1})));
 
   TransformationCompositeConstruct make_uvec3(
-      59, {14, 18, 136}, MakeInstructionDescriptor(137, SpvOpReturn, 0), 207);
+      59, {14, 18, 136}, MakeInstructionDescriptor(137, spv::Op::OpReturn, 0),
+      207);
   // Bad because 1300 is not an id
   TransformationCompositeConstruct make_uvec3_bad(
-      59, {14, 18, 1300}, MakeInstructionDescriptor(137, SpvOpReturn, 0), 207);
+      59, {14, 18, 1300}, MakeInstructionDescriptor(137, spv::Op::OpReturn, 0),
+      207);
   ASSERT_TRUE(make_uvec3.IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       make_uvec3_bad.IsApplicable(context.get(), transformation_context));
@@ -1123,11 +1135,11 @@
 
   TransformationCompositeConstruct make_uvec4(
       131, {14, 18, 136, 136},
-      MakeInstructionDescriptor(137, SpvOpAccessChain, 0), 208);
+      MakeInstructionDescriptor(137, spv::Op::OpAccessChain, 0), 208);
   // Bad because 86 is the wrong type.
   TransformationCompositeConstruct make_uvec4_bad(
       86, {14, 18, 136, 136},
-      MakeInstructionDescriptor(137, SpvOpAccessChain, 0), 208);
+      MakeInstructionDescriptor(137, spv::Op::OpAccessChain, 0), 208);
   ASSERT_TRUE(make_uvec4.IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       make_uvec4_bad.IsApplicable(context.get(), transformation_context));
@@ -1149,7 +1161,7 @@
           111,
           41,
       },
-      MakeInstructionDescriptor(75, SpvOpAccessChain, 0), 209);
+      MakeInstructionDescriptor(75, spv::Op::OpAccessChain, 0), 209);
   // Bad because 0 is not a valid base instruction id
   TransformationCompositeConstruct make_bvec2_bad(
       102,
@@ -1157,7 +1169,7 @@
           111,
           41,
       },
-      MakeInstructionDescriptor(0, SpvOpExtInstImport, 0), 209);
+      MakeInstructionDescriptor(0, spv::Op::OpExtInstImport, 0), 209);
   ASSERT_TRUE(make_bvec2.IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       make_bvec2_bad.IsApplicable(context.get(), transformation_context));
@@ -1170,10 +1182,10 @@
       MakeDataDescriptor(41, {}), MakeDataDescriptor(209, {1})));
 
   TransformationCompositeConstruct make_bvec3(
-      93, {108, 73}, MakeInstructionDescriptor(108, SpvOpStore, 0), 210);
+      93, {108, 73}, MakeInstructionDescriptor(108, spv::Op::OpStore, 0), 210);
   // Bad because there are too many components for a bvec3
   TransformationCompositeConstruct make_bvec3_bad(
-      93, {108, 108}, MakeInstructionDescriptor(108, SpvOpStore, 0), 210);
+      93, {108, 108}, MakeInstructionDescriptor(108, spv::Op::OpStore, 0), 210);
   ASSERT_TRUE(make_bvec3.IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       make_bvec3_bad.IsApplicable(context.get(), transformation_context));
@@ -1188,10 +1200,11 @@
       MakeDataDescriptor(73, {}), MakeDataDescriptor(210, {2})));
 
   TransformationCompositeConstruct make_bvec4(
-      70, {108, 108}, MakeInstructionDescriptor(108, SpvOpBranch, 0), 211);
+      70, {108, 108}, MakeInstructionDescriptor(108, spv::Op::OpBranch, 0),
+      211);
   // Bad because 21 is a type, not a result id
   TransformationCompositeConstruct make_bvec4_bad(
-      70, {21, 108}, MakeInstructionDescriptor(108, SpvOpBranch, 0), 211);
+      70, {21, 108}, MakeInstructionDescriptor(108, spv::Op::OpBranch, 0), 211);
   ASSERT_TRUE(make_bvec4.IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       make_bvec4_bad.IsApplicable(context.get(), transformation_context));
@@ -1477,7 +1490,8 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
   TransformationCompositeConstruct transformation(
-      32, {25, 28, 31}, MakeInstructionDescriptor(31, SpvOpReturn, 0), 200);
+      32, {25, 28, 31}, MakeInstructionDescriptor(31, spv::Op::OpReturn, 0),
+      200);
   ASSERT_TRUE(
       transformation.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
@@ -1562,7 +1576,8 @@
   transformation_context.GetFactManager()->AddFactIdIsIrrelevant(25);
 
   TransformationCompositeConstruct transformation(
-      32, {25, 28, 31}, MakeInstructionDescriptor(31, SpvOpReturn, 0), 200);
+      32, {25, 28, 31}, MakeInstructionDescriptor(31, spv::Op::OpReturn, 0),
+      200);
   ASSERT_TRUE(
       transformation.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
@@ -1618,7 +1633,7 @@
   transformation_context.GetFactManager()->AddFactBlockIsDead(15);
 
   TransformationCompositeConstruct transformation(
-      7, {10, 11}, MakeInstructionDescriptor(15, SpvOpBranch, 0), 100);
+      7, {10, 11}, MakeInstructionDescriptor(15, spv::Op::OpBranch, 0), 100);
   ASSERT_TRUE(
       transformation.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
@@ -1660,7 +1675,7 @@
   transformation_context.GetFactManager()->AddFactIdIsIrrelevant(8);
 
   TransformationCompositeConstruct transformation(
-      7, {8, 9, 10}, MakeInstructionDescriptor(5, SpvOpReturn, 0), 100);
+      7, {8, 9, 10}, MakeInstructionDescriptor(5, spv::Op::OpReturn, 0), 100);
   ASSERT_TRUE(
       transformation.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
@@ -1707,7 +1722,7 @@
   transformation_context.GetFactManager()->AddFactIdIsIrrelevant(10);
 
   TransformationCompositeConstruct transformation(
-      8, {10, 9}, MakeInstructionDescriptor(5, SpvOpReturn, 0), 100);
+      8, {10, 9}, MakeInstructionDescriptor(5, spv::Op::OpReturn, 0), 100);
   ASSERT_TRUE(
       transformation.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
diff --git a/test/fuzz/transformation_composite_extract_test.cpp b/test/fuzz/transformation_composite_extract_test.cpp
index 1df5591..d468e41 100644
--- a/test/fuzz/transformation_composite_extract_test.cpp
+++ b/test/fuzz/transformation_composite_extract_test.cpp
@@ -102,47 +102,49 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
   // Instruction does not exist.
-  ASSERT_FALSE(TransformationCompositeExtract(
-                   MakeInstructionDescriptor(36, SpvOpIAdd, 0), 200, 101, {0})
-                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationCompositeExtract(
+          MakeInstructionDescriptor(36, spv::Op::OpIAdd, 0), 200, 101, {0})
+          .IsApplicable(context.get(), transformation_context));
 
   // Id for composite is not a composite.
   ASSERT_FALSE(
       TransformationCompositeExtract(
-          MakeInstructionDescriptor(37, SpvOpAccessChain, 0), 200, 32, {})
+          MakeInstructionDescriptor(37, spv::Op::OpAccessChain, 0), 200, 32, {})
           .IsApplicable(context.get(), transformation_context));
 
   // Composite does not dominate instruction being inserted before.
-  ASSERT_FALSE(
-      TransformationCompositeExtract(
-          MakeInstructionDescriptor(37, SpvOpAccessChain, 0), 200, 101, {0})
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationCompositeExtract(
+                   MakeInstructionDescriptor(37, spv::Op::OpAccessChain, 0),
+                   200, 101, {0})
+                   .IsApplicable(context.get(), transformation_context));
 
   // Too many indices for extraction from struct composite.
-  ASSERT_FALSE(
-      TransformationCompositeExtract(
-          MakeInstructionDescriptor(24, SpvOpAccessChain, 0), 200, 101, {0, 0})
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationCompositeExtract(
+                   MakeInstructionDescriptor(24, spv::Op::OpAccessChain, 0),
+                   200, 101, {0, 0})
+                   .IsApplicable(context.get(), transformation_context));
 
   // Too many indices for extraction from struct composite.
-  ASSERT_FALSE(
-      TransformationCompositeExtract(
-          MakeInstructionDescriptor(13, SpvOpIEqual, 0), 200, 104, {0, 0, 0})
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationCompositeExtract(
+                   MakeInstructionDescriptor(13, spv::Op::OpIEqual, 0), 200,
+                   104, {0, 0, 0})
+                   .IsApplicable(context.get(), transformation_context));
 
   // Out of bounds index for extraction from struct composite.
   ASSERT_FALSE(
       TransformationCompositeExtract(
-          MakeInstructionDescriptor(13, SpvOpIEqual, 0), 200, 104, {0, 3})
+          MakeInstructionDescriptor(13, spv::Op::OpIEqual, 0), 200, 104, {0, 3})
           .IsApplicable(context.get(), transformation_context));
 
   // Result id already used.
-  ASSERT_FALSE(TransformationCompositeExtract(
-                   MakeInstructionDescriptor(35, SpvOpFAdd, 0), 80, 103, {0})
-                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationCompositeExtract(
+          MakeInstructionDescriptor(35, spv::Op::OpFAdd, 0), 80, 103, {0})
+          .IsApplicable(context.get(), transformation_context));
 
   TransformationCompositeExtract transformation_1(
-      MakeInstructionDescriptor(36, SpvOpConvertFToS, 0), 201, 100, {2});
+      MakeInstructionDescriptor(36, spv::Op::OpConvertFToS, 0), 201, 100, {2});
   ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(201));
   ASSERT_EQ(nullptr, context->get_instr_block(201));
   uint32_t num_uses_of_100_before = context->get_def_use_mgr()->NumUses(100);
@@ -150,7 +152,7 @@
       transformation_1.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation_1, context.get(),
                         &transformation_context);
-  ASSERT_EQ(SpvOpCompositeExtract,
+  ASSERT_EQ(spv::Op::OpCompositeExtract,
             context->get_def_use_mgr()->GetDef(201)->opcode());
   ASSERT_EQ(15, context->get_instr_block(201)->id());
   ASSERT_EQ(num_uses_of_100_before + 1,
@@ -159,7 +161,8 @@
                                                kConsoleMessageConsumer));
 
   TransformationCompositeExtract transformation_2(
-      MakeInstructionDescriptor(37, SpvOpAccessChain, 0), 202, 104, {0, 2});
+      MakeInstructionDescriptor(37, spv::Op::OpAccessChain, 0), 202, 104,
+      {0, 2});
   ASSERT_TRUE(
       transformation_2.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation_2, context.get(),
@@ -168,7 +171,7 @@
                                                kConsoleMessageConsumer));
 
   TransformationCompositeExtract transformation_3(
-      MakeInstructionDescriptor(29, SpvOpAccessChain, 0), 203, 104, {0});
+      MakeInstructionDescriptor(29, spv::Op::OpAccessChain, 0), 203, 104, {0});
   ASSERT_TRUE(
       transformation_3.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation_3, context.get(),
@@ -177,7 +180,7 @@
                                                kConsoleMessageConsumer));
 
   TransformationCompositeExtract transformation_4(
-      MakeInstructionDescriptor(24, SpvOpStore, 0), 204, 101, {0});
+      MakeInstructionDescriptor(24, spv::Op::OpStore, 0), 204, 101, {0});
   ASSERT_TRUE(
       transformation_4.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation_4, context.get(),
@@ -186,7 +189,7 @@
                                                kConsoleMessageConsumer));
 
   TransformationCompositeExtract transformation_5(
-      MakeInstructionDescriptor(29, SpvOpBranch, 0), 205, 102, {2});
+      MakeInstructionDescriptor(29, spv::Op::OpBranch, 0), 205, 102, {2});
   ASSERT_TRUE(
       transformation_5.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation_5, context.get(),
@@ -195,7 +198,7 @@
                                                kConsoleMessageConsumer));
 
   TransformationCompositeExtract transformation_6(
-      MakeInstructionDescriptor(37, SpvOpReturn, 0), 206, 103, {1});
+      MakeInstructionDescriptor(37, spv::Op::OpReturn, 0), 206, 103, {1});
   ASSERT_TRUE(
       transformation_6.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation_6, context.get(),
@@ -378,45 +381,50 @@
   // Cannot insert before the OpVariables of a function.
   ASSERT_FALSE(
       TransformationCompositeExtract(
-          MakeInstructionDescriptor(101, SpvOpVariable, 0), 200, 14, {0})
+          MakeInstructionDescriptor(101, spv::Op::OpVariable, 0), 200, 14, {0})
           .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       TransformationCompositeExtract(
-          MakeInstructionDescriptor(101, SpvOpVariable, 1), 200, 14, {1})
+          MakeInstructionDescriptor(101, spv::Op::OpVariable, 1), 200, 14, {1})
           .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       TransformationCompositeExtract(
-          MakeInstructionDescriptor(102, SpvOpVariable, 0), 200, 14, {1})
+          MakeInstructionDescriptor(102, spv::Op::OpVariable, 0), 200, 14, {1})
           .IsApplicable(context.get(), transformation_context));
   // OK to insert right after the OpVariables.
-  ASSERT_FALSE(TransformationCompositeExtract(
-                   MakeInstructionDescriptor(102, SpvOpBranch, 1), 200, 14, {1})
-                   .IsApplicable(context.get(), transformation_context));
-
-  // Cannot insert before the OpPhis of a block.
-  ASSERT_FALSE(TransformationCompositeExtract(
-                   MakeInstructionDescriptor(60, SpvOpPhi, 0), 200, 14, {2})
-                   .IsApplicable(context.get(), transformation_context));
-  ASSERT_FALSE(TransformationCompositeExtract(
-                   MakeInstructionDescriptor(59, SpvOpPhi, 0), 200, 14, {3})
-                   .IsApplicable(context.get(), transformation_context));
-  // OK to insert after the OpPhis.
-  ASSERT_TRUE(
+  ASSERT_FALSE(
       TransformationCompositeExtract(
-          MakeInstructionDescriptor(59, SpvOpAccessChain, 0), 200, 14, {3})
+          MakeInstructionDescriptor(102, spv::Op::OpBranch, 1), 200, 14, {1})
           .IsApplicable(context.get(), transformation_context));
 
+  // Cannot insert before the OpPhis of a block.
+  ASSERT_FALSE(
+      TransformationCompositeExtract(
+          MakeInstructionDescriptor(60, spv::Op::OpPhi, 0), 200, 14, {2})
+          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationCompositeExtract(
+          MakeInstructionDescriptor(59, spv::Op::OpPhi, 0), 200, 14, {3})
+          .IsApplicable(context.get(), transformation_context));
+  // OK to insert after the OpPhis.
+  ASSERT_TRUE(TransformationCompositeExtract(
+                  MakeInstructionDescriptor(59, spv::Op::OpAccessChain, 0), 200,
+                  14, {3})
+                  .IsApplicable(context.get(), transformation_context));
+
   // Cannot insert before OpLoopMerge
-  ASSERT_FALSE(TransformationCompositeExtract(
-                   MakeInstructionDescriptor(33, SpvOpBranchConditional, 0),
-                   200, 14, {3})
-                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationCompositeExtract(
+          MakeInstructionDescriptor(33, spv::Op::OpBranchConditional, 0), 200,
+          14, {3})
+          .IsApplicable(context.get(), transformation_context));
 
   // Cannot insert before OpSelectionMerge
-  ASSERT_FALSE(TransformationCompositeExtract(
-                   MakeInstructionDescriptor(21, SpvOpBranchConditional, 0),
-                   200, 14, {2})
-                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationCompositeExtract(
+          MakeInstructionDescriptor(21, spv::Op::OpBranchConditional, 0), 200,
+          14, {2})
+          .IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationCompositeExtractTest, AddSynonymsForRelevantIds) {
@@ -498,7 +506,7 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
   TransformationCompositeExtract transformation(
-      MakeInstructionDescriptor(36, SpvOpConvertFToS, 0), 201, 100, {2});
+      MakeInstructionDescriptor(36, spv::Op::OpConvertFToS, 0), 201, 100, {2});
   ASSERT_TRUE(
       transformation.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
@@ -586,7 +594,7 @@
       MakeUnique<FactManager>(context.get()), validator_options);
   transformation_context.GetFactManager()->AddFactIdIsIrrelevant(100);
   TransformationCompositeExtract transformation(
-      MakeInstructionDescriptor(36, SpvOpConvertFToS, 0), 201, 100, {2});
+      MakeInstructionDescriptor(36, spv::Op::OpConvertFToS, 0), 201, 100, {2});
   ASSERT_TRUE(
       transformation.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
@@ -635,7 +643,7 @@
       MakeUnique<FactManager>(context.get()), validator_options);
   transformation_context.GetFactManager()->AddFactBlockIsDead(15);
   TransformationCompositeExtract transformation(
-      MakeInstructionDescriptor(15, SpvOpBranch, 0), 100, 12, {0});
+      MakeInstructionDescriptor(15, spv::Op::OpBranch, 0), 100, 12, {0});
   ASSERT_TRUE(
       transformation.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
diff --git a/test/fuzz/transformation_composite_insert_test.cpp b/test/fuzz/transformation_composite_insert_test.cpp
index 3b6f34d..e61c0fa 100644
--- a/test/fuzz/transformation_composite_insert_test.cpp
+++ b/test/fuzz/transformation_composite_insert_test.cpp
@@ -103,65 +103,69 @@
       MakeUnique<FactManager>(context.get()), validator_options);
   // Bad: |fresh_id| is not fresh.
   auto transformation_bad_1 = TransformationCompositeInsert(
-      MakeInstructionDescriptor(29, SpvOpStore, 0), 20, 29, 11, {1, 0, 0});
+      MakeInstructionDescriptor(29, spv::Op::OpStore, 0), 20, 29, 11,
+      {1, 0, 0});
   ASSERT_FALSE(
       transformation_bad_1.IsApplicable(context.get(), transformation_context));
 
   // Bad: |composite_id| does not refer to a existing instruction.
   auto transformation_bad_2 = TransformationCompositeInsert(
-      MakeInstructionDescriptor(29, SpvOpStore, 0), 50, 40, 11, {1, 0, 0});
+      MakeInstructionDescriptor(29, spv::Op::OpStore, 0), 50, 40, 11,
+      {1, 0, 0});
   ASSERT_FALSE(
       transformation_bad_2.IsApplicable(context.get(), transformation_context));
 
   // Bad: |composite_id| does not refer to a composite value.
   auto transformation_bad_3 = TransformationCompositeInsert(
-      MakeInstructionDescriptor(29, SpvOpStore, 0), 50, 9, 11, {1, 0, 0});
+      MakeInstructionDescriptor(29, spv::Op::OpStore, 0), 50, 9, 11, {1, 0, 0});
   ASSERT_FALSE(
       transformation_bad_3.IsApplicable(context.get(), transformation_context));
 
   // Bad: |object_id| does not refer to a defined instruction.
   auto transformation_bad_4 = TransformationCompositeInsert(
-      MakeInstructionDescriptor(29, SpvOpStore, 0), 50, 29, 40, {1, 0, 0});
+      MakeInstructionDescriptor(29, spv::Op::OpStore, 0), 50, 29, 40,
+      {1, 0, 0});
   ASSERT_FALSE(
       transformation_bad_4.IsApplicable(context.get(), transformation_context));
 
   // Bad: |object_id| cannot refer to a pointer.
   auto transformation_bad_5 = TransformationCompositeInsert(
-      MakeInstructionDescriptor(29, SpvOpStore, 0), 50, 29, 8, {1, 0, 0});
+      MakeInstructionDescriptor(29, spv::Op::OpStore, 0), 50, 29, 8, {1, 0, 0});
   ASSERT_FALSE(
       transformation_bad_5.IsApplicable(context.get(), transformation_context));
 
   // Bad: |index| is not a correct index.
   auto transformation_bad_6 = TransformationCompositeInsert(
-      MakeInstructionDescriptor(29, SpvOpStore, 0), 50, 29, 11, {2, 0, 0});
+      MakeInstructionDescriptor(29, spv::Op::OpStore, 0), 50, 29, 11,
+      {2, 0, 0});
   ASSERT_FALSE(
       transformation_bad_6.IsApplicable(context.get(), transformation_context));
 
   // Bad: Type id of the object to be inserted and the type id of the
   // component at |index| are not the same.
   auto transformation_bad_7 = TransformationCompositeInsert(
-      MakeInstructionDescriptor(29, SpvOpStore, 0), 50, 29, 11, {1, 0});
+      MakeInstructionDescriptor(29, spv::Op::OpStore, 0), 50, 29, 11, {1, 0});
   ASSERT_FALSE(
       transformation_bad_7.IsApplicable(context.get(), transformation_context));
 
   // Bad: |instruction_to_insert_before| does not refer to a defined
   // instruction.
   auto transformation_bad_8 = TransformationCompositeInsert(
-      MakeInstructionDescriptor(29, SpvOpIMul, 0), 50, 29, 11, {1, 0, 0});
+      MakeInstructionDescriptor(29, spv::Op::OpIMul, 0), 50, 29, 11, {1, 0, 0});
   ASSERT_FALSE(
       transformation_bad_8.IsApplicable(context.get(), transformation_context));
 
   // Bad: OpCompositeInsert cannot be inserted before OpBranchConditional with
   // OpSelectionMerge above it.
   auto transformation_bad_9 = TransformationCompositeInsert(
-      MakeInstructionDescriptor(29, SpvOpBranchConditional, 0), 50, 29, 11,
-      {1, 0, 0});
+      MakeInstructionDescriptor(29, spv::Op::OpBranchConditional, 0), 50, 29,
+      11, {1, 0, 0});
   ASSERT_FALSE(
       transformation_bad_9.IsApplicable(context.get(), transformation_context));
 
   // Bad: |composite_id| does not have a type_id.
   auto transformation_bad_10 = TransformationCompositeInsert(
-      MakeInstructionDescriptor(29, SpvOpStore, 0), 50, 1, 11, {1, 0, 0});
+      MakeInstructionDescriptor(29, spv::Op::OpStore, 0), 50, 1, 11, {1, 0, 0});
   ASSERT_FALSE(transformation_bad_10.IsApplicable(context.get(),
                                                   transformation_context));
 }
@@ -222,14 +226,14 @@
       MakeUnique<FactManager>(context.get()), validator_options);
   // Bad: The composite with |composite_id| cannot be empty.
   auto transformation_bad_1 = TransformationCompositeInsert(
-      MakeInstructionDescriptor(64, SpvOpStore, 0), 50, 61, 62, {1});
+      MakeInstructionDescriptor(64, spv::Op::OpStore, 0), 50, 61, 62, {1});
   ASSERT_FALSE(
       transformation_bad_1.IsApplicable(context.get(), transformation_context));
 
   // Good: It is possible to insert into a composite an element which is an
   // empty composite.
   auto transformation_good_1 = TransformationCompositeInsert(
-      MakeInstructionDescriptor(64, SpvOpStore, 0), 50, 64, 62, {1});
+      MakeInstructionDescriptor(64, spv::Op::OpStore, 0), 50, 64, 62, {1});
   ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(),
                                                  transformation_context));
   ApplyAndCheckFreshIds(transformation_good_1, context.get(),
@@ -368,7 +372,8 @@
   transformation_context.GetFactManager()->AddFactIdIsIrrelevant(30);
 
   auto transformation_good_1 = TransformationCompositeInsert(
-      MakeInstructionDescriptor(30, SpvOpStore, 0), 50, 30, 11, {1, 0, 0});
+      MakeInstructionDescriptor(30, spv::Op::OpStore, 0), 50, 30, 11,
+      {1, 0, 0});
   ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(),
                                                  transformation_context));
   ApplyAndCheckFreshIds(transformation_good_1, context.get(),
@@ -473,7 +478,8 @@
   transformation_context.GetFactManager()->AddFactIdIsIrrelevant(11);
 
   auto transformation_good_1 = TransformationCompositeInsert(
-      MakeInstructionDescriptor(30, SpvOpStore, 0), 50, 30, 11, {1, 0, 0});
+      MakeInstructionDescriptor(30, spv::Op::OpStore, 0), 50, 30, 11,
+      {1, 0, 0});
   ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(),
                                                  transformation_context));
   ApplyAndCheckFreshIds(transformation_good_1, context.get(),
@@ -580,7 +586,8 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
   auto transformation_good_1 = TransformationCompositeInsert(
-      MakeInstructionDescriptor(30, SpvOpStore, 0), 50, 30, 11, {1, 0, 0});
+      MakeInstructionDescriptor(30, spv::Op::OpStore, 0), 50, 30, 11,
+      {1, 0, 0});
   ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(),
                                                  transformation_context));
   ApplyAndCheckFreshIds(transformation_good_1, context.get(),
@@ -609,7 +616,8 @@
       MakeDataDescriptor(30, {1, 0, 0}), MakeDataDescriptor(50, {1, 0, 0})));
 
   auto transformation_good_2 = TransformationCompositeInsert(
-      MakeInstructionDescriptor(50, SpvOpStore, 0), 51, 50, 11, {0, 1, 1});
+      MakeInstructionDescriptor(50, spv::Op::OpStore, 0), 51, 50, 11,
+      {0, 1, 1});
   ASSERT_TRUE(transformation_good_2.IsApplicable(context.get(),
                                                  transformation_context));
   ApplyAndCheckFreshIds(transformation_good_2, context.get(),
@@ -787,30 +795,30 @@
   // Bad: The object with |object_id| is not available at
   // |instruction_to_insert_before|.
   auto transformation_bad_1 = TransformationCompositeInsert(
-      MakeInstructionDescriptor(31, SpvOpIMul, 0), 50, 27, 60, {1});
+      MakeInstructionDescriptor(31, spv::Op::OpIMul, 0), 50, 27, 60, {1});
   ASSERT_FALSE(
       transformation_bad_1.IsApplicable(context.get(), transformation_context));
 
   // Bad: The composite with |composite_id| is not available at
   // |instruction_to_insert_before|.
   auto transformation_bad_2 = TransformationCompositeInsert(
-      MakeInstructionDescriptor(31, SpvOpIMul, 0), 50, 61, 21, {1});
+      MakeInstructionDescriptor(31, spv::Op::OpIMul, 0), 50, 61, 21, {1});
   ASSERT_FALSE(
       transformation_bad_2.IsApplicable(context.get(), transformation_context));
 
   // Bad: The |instruction_to_insert_before| is the composite itself and is
   // available.
   auto transformation_bad_3 = TransformationCompositeInsert(
-      MakeInstructionDescriptor(61, SpvOpCompositeConstruct, 0), 50, 61, 21,
-      {1});
+      MakeInstructionDescriptor(61, spv::Op::OpCompositeConstruct, 0), 50, 61,
+      21, {1});
   ASSERT_FALSE(
       transformation_bad_3.IsApplicable(context.get(), transformation_context));
 
   // Bad: The |instruction_to_insert_before| is the object itself and is not
   // available.
   auto transformation_bad_4 = TransformationCompositeInsert(
-      MakeInstructionDescriptor(60, SpvOpCompositeConstruct, 0), 50, 27, 60,
-      {1});
+      MakeInstructionDescriptor(60, spv::Op::OpCompositeConstruct, 0), 50, 27,
+      60, {1});
   ASSERT_FALSE(
       transformation_bad_4.IsApplicable(context.get(), transformation_context));
 }
@@ -863,7 +871,8 @@
 
   // Leads to synonyms - nothing is irrelevant.
   auto transformation1 = TransformationCompositeInsert(
-      MakeInstructionDescriptor(13, SpvOpSelectionMerge, 0), 100, 9, 17, {0});
+      MakeInstructionDescriptor(13, spv::Op::OpSelectionMerge, 0), 100, 9, 17,
+      {0});
   ASSERT_TRUE(
       transformation1.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation1, context.get(),
@@ -876,7 +885,8 @@
   // Because %16 is irrelevant, we don't get a synonym with the component to
   // which it has been inserted (but we do for the other component).
   auto transformation2 = TransformationCompositeInsert(
-      MakeInstructionDescriptor(13, SpvOpSelectionMerge, 0), 101, 9, 16, {0});
+      MakeInstructionDescriptor(13, spv::Op::OpSelectionMerge, 0), 101, 9, 16,
+      {0});
   ASSERT_TRUE(
       transformation2.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation2, context.get(),
@@ -889,7 +899,8 @@
   // Because %18 is irrelevant we only get a synonym for the component into
   // which insertion has taken place.
   auto transformation3 = TransformationCompositeInsert(
-      MakeInstructionDescriptor(13, SpvOpSelectionMerge, 0), 102, 18, 17, {0});
+      MakeInstructionDescriptor(13, spv::Op::OpSelectionMerge, 0), 102, 18, 17,
+      {0});
   ASSERT_TRUE(
       transformation3.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation3, context.get(),
@@ -901,7 +912,7 @@
 
   // Does not lead to synonyms as block %14 is dead.
   auto transformation4 = TransformationCompositeInsert(
-      MakeInstructionDescriptor(14, SpvOpBranch, 0), 103, 9, 17, {0});
+      MakeInstructionDescriptor(14, spv::Op::OpBranch, 0), 103, 9, 17, {0});
   ASSERT_TRUE(
       transformation4.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation4, context.get(),
diff --git a/test/fuzz/transformation_equation_instruction_test.cpp b/test/fuzz/transformation_equation_instruction_test.cpp
index 5b5033d..3653fca 100644
--- a/test/fuzz/transformation_equation_instruction_test.cpp
+++ b/test/fuzz/transformation_equation_instruction_test.cpp
@@ -54,65 +54,66 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
   protobufs::InstructionDescriptor return_instruction =
-      MakeInstructionDescriptor(13, SpvOpReturn, 0);
+      MakeInstructionDescriptor(13, spv::Op::OpReturn, 0);
 
   // Bad: id already in use.
-  ASSERT_FALSE(TransformationEquationInstruction(7, SpvOpSNegate, {7},
+  ASSERT_FALSE(TransformationEquationInstruction(7, spv::Op::OpSNegate, {7},
                                                  return_instruction)
                    .IsApplicable(context.get(), transformation_context));
 
   // Bad: identified instruction does not exist.
-  ASSERT_FALSE(
-      TransformationEquationInstruction(
-          14, SpvOpSNegate, {7}, MakeInstructionDescriptor(13, SpvOpLoad, 0))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationEquationInstruction(
+                   14, spv::Op::OpSNegate, {7},
+                   MakeInstructionDescriptor(13, spv::Op::OpLoad, 0))
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: id 100 does not exist
-  ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpSNegate, {100},
+  ASSERT_FALSE(TransformationEquationInstruction(14, spv::Op::OpSNegate, {100},
                                                  return_instruction)
                    .IsApplicable(context.get(), transformation_context));
 
   // Bad: id 20 is an OpUndef
-  ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpSNegate, {20},
+  ASSERT_FALSE(TransformationEquationInstruction(14, spv::Op::OpSNegate, {20},
                                                  return_instruction)
                    .IsApplicable(context.get(), transformation_context));
 
   // Bad: id 30 is not available right before its definition
   ASSERT_FALSE(TransformationEquationInstruction(
-                   14, SpvOpSNegate, {30},
-                   MakeInstructionDescriptor(30, SpvOpCopyObject, 0))
+                   14, spv::Op::OpSNegate, {30},
+                   MakeInstructionDescriptor(30, spv::Op::OpCopyObject, 0))
                    .IsApplicable(context.get(), transformation_context));
 
   // Bad: too many arguments to OpSNegate.
-  ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpSNegate, {7, 7},
+  ASSERT_FALSE(TransformationEquationInstruction(14, spv::Op::OpSNegate, {7, 7},
                                                  return_instruction)
                    .IsApplicable(context.get(), transformation_context));
 
   // Bad: 40 is a type id.
-  ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpSNegate, {40},
+  ASSERT_FALSE(TransformationEquationInstruction(14, spv::Op::OpSNegate, {40},
                                                  return_instruction)
                    .IsApplicable(context.get(), transformation_context));
 
   // Bad: wrong type of argument to OpSNegate.
-  ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpSNegate, {41},
+  ASSERT_FALSE(TransformationEquationInstruction(14, spv::Op::OpSNegate, {41},
                                                  return_instruction)
                    .IsApplicable(context.get(), transformation_context));
 
   auto transformation1 = TransformationEquationInstruction(
-      14, SpvOpSNegate, {7}, return_instruction);
+      14, spv::Op::OpSNegate, {7}, return_instruction);
   ASSERT_TRUE(
       transformation1.IsApplicable(context.get(), transformation_context));
   ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(14));
   ASSERT_EQ(nullptr, context->get_instr_block(14));
   ApplyAndCheckFreshIds(transformation1, context.get(),
                         &transformation_context);
-  ASSERT_EQ(SpvOpSNegate, context->get_def_use_mgr()->GetDef(14)->opcode());
+  ASSERT_EQ(spv::Op::OpSNegate,
+            context->get_def_use_mgr()->GetDef(14)->opcode());
   ASSERT_EQ(13, context->get_instr_block(14)->id());
   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
                                                kConsoleMessageConsumer));
 
   auto transformation2 = TransformationEquationInstruction(
-      15, SpvOpSNegate, {14}, return_instruction);
+      15, spv::Op::OpSNegate, {14}, return_instruction);
   ASSERT_TRUE(
       transformation2.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation2, context.get(),
@@ -178,25 +179,25 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
   protobufs::InstructionDescriptor return_instruction =
-      MakeInstructionDescriptor(13, SpvOpReturn, 0);
+      MakeInstructionDescriptor(13, spv::Op::OpReturn, 0);
 
   // Bad: too few arguments to OpLogicalNot.
-  ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpLogicalNot, {},
+  ASSERT_FALSE(TransformationEquationInstruction(14, spv::Op::OpLogicalNot, {},
                                                  return_instruction)
                    .IsApplicable(context.get(), transformation_context));
 
   // Bad: 6 is a type id.
-  ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpLogicalNot, {6},
+  ASSERT_FALSE(TransformationEquationInstruction(14, spv::Op::OpLogicalNot, {6},
                                                  return_instruction)
                    .IsApplicable(context.get(), transformation_context));
 
   // Bad: wrong type of argument to OpLogicalNot.
-  ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpLogicalNot, {21},
-                                                 return_instruction)
+  ASSERT_FALSE(TransformationEquationInstruction(14, spv::Op::OpLogicalNot,
+                                                 {21}, return_instruction)
                    .IsApplicable(context.get(), transformation_context));
 
   auto transformation1 = TransformationEquationInstruction(
-      14, SpvOpLogicalNot, {7}, return_instruction);
+      14, spv::Op::OpLogicalNot, {7}, return_instruction);
   ASSERT_TRUE(
       transformation1.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation1, context.get(),
@@ -205,7 +206,7 @@
                                                kConsoleMessageConsumer));
 
   auto transformation2 = TransformationEquationInstruction(
-      15, SpvOpLogicalNot, {14}, return_instruction);
+      15, spv::Op::OpLogicalNot, {14}, return_instruction);
   ASSERT_TRUE(
       transformation2.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation2, context.get(),
@@ -272,31 +273,31 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
   protobufs::InstructionDescriptor return_instruction =
-      MakeInstructionDescriptor(13, SpvOpReturn, 0);
+      MakeInstructionDescriptor(13, spv::Op::OpReturn, 0);
 
   // Bad: too many arguments to OpIAdd.
-  ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpIAdd, {15, 16, 16},
-                                                 return_instruction)
+  ASSERT_FALSE(TransformationEquationInstruction(
+                   14, spv::Op::OpIAdd, {15, 16, 16}, return_instruction)
                    .IsApplicable(context.get(), transformation_context));
   // Bad: boolean argument to OpIAdd.
-  ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpIAdd, {15, 32},
+  ASSERT_FALSE(TransformationEquationInstruction(14, spv::Op::OpIAdd, {15, 32},
                                                  return_instruction)
                    .IsApplicable(context.get(), transformation_context));
   // Bad: type as argument to OpIAdd.
-  ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpIAdd, {33, 16},
+  ASSERT_FALSE(TransformationEquationInstruction(14, spv::Op::OpIAdd, {33, 16},
                                                  return_instruction)
                    .IsApplicable(context.get(), transformation_context));
   // Bad: arguments of mismatched widths
-  ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpIAdd, {15, 31},
+  ASSERT_FALSE(TransformationEquationInstruction(14, spv::Op::OpIAdd, {15, 31},
                                                  return_instruction)
                    .IsApplicable(context.get(), transformation_context));
   // Bad: arguments of mismatched widths
-  ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpIAdd, {31, 15},
+  ASSERT_FALSE(TransformationEquationInstruction(14, spv::Op::OpIAdd, {31, 15},
                                                  return_instruction)
                    .IsApplicable(context.get(), transformation_context));
 
   auto transformation1 = TransformationEquationInstruction(
-      14, SpvOpIAdd, {15, 16}, return_instruction);
+      14, spv::Op::OpIAdd, {15, 16}, return_instruction);
   ASSERT_TRUE(
       transformation1.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation1, context.get(),
@@ -305,7 +306,7 @@
                                                kConsoleMessageConsumer));
 
   auto transformation2 = TransformationEquationInstruction(
-      19, SpvOpISub, {14, 16}, return_instruction);
+      19, spv::Op::OpISub, {14, 16}, return_instruction);
   ASSERT_TRUE(
       transformation2.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation2, context.get(),
@@ -316,7 +317,7 @@
       MakeDataDescriptor(15, {}), MakeDataDescriptor(19, {})));
 
   auto transformation3 = TransformationEquationInstruction(
-      20, SpvOpISub, {14, 15}, return_instruction);
+      20, spv::Op::OpISub, {14, 15}, return_instruction);
   ASSERT_TRUE(
       transformation3.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation3, context.get(),
@@ -327,7 +328,7 @@
       MakeDataDescriptor(20, {}), MakeDataDescriptor(16, {})));
 
   auto transformation4 = TransformationEquationInstruction(
-      22, SpvOpISub, {16, 14}, return_instruction);
+      22, spv::Op::OpISub, {16, 14}, return_instruction);
   ASSERT_TRUE(
       transformation4.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation4, context.get(),
@@ -336,7 +337,7 @@
                                                kConsoleMessageConsumer));
 
   auto transformation5 = TransformationEquationInstruction(
-      24, SpvOpSNegate, {22}, return_instruction);
+      24, spv::Op::OpSNegate, {22}, return_instruction);
   ASSERT_TRUE(
       transformation5.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation5, context.get(),
@@ -404,10 +405,10 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
   protobufs::InstructionDescriptor return_instruction =
-      MakeInstructionDescriptor(13, SpvOpReturn, 0);
+      MakeInstructionDescriptor(13, spv::Op::OpReturn, 0);
 
   auto transformation1 = TransformationEquationInstruction(
-      14, SpvOpISub, {15, 16}, return_instruction);
+      14, spv::Op::OpISub, {15, 16}, return_instruction);
   ASSERT_TRUE(
       transformation1.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation1, context.get(),
@@ -416,7 +417,7 @@
                                                kConsoleMessageConsumer));
 
   auto transformation2 = TransformationEquationInstruction(
-      17, SpvOpIAdd, {14, 16}, return_instruction);
+      17, spv::Op::OpIAdd, {14, 16}, return_instruction);
   ASSERT_TRUE(
       transformation2.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation2, context.get(),
@@ -427,7 +428,7 @@
       MakeDataDescriptor(17, {}), MakeDataDescriptor(15, {})));
 
   auto transformation3 = TransformationEquationInstruction(
-      18, SpvOpIAdd, {16, 14}, return_instruction);
+      18, spv::Op::OpIAdd, {16, 14}, return_instruction);
   ASSERT_TRUE(
       transformation3.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation3, context.get(),
@@ -440,7 +441,7 @@
       MakeDataDescriptor(18, {}), MakeDataDescriptor(15, {})));
 
   auto transformation4 = TransformationEquationInstruction(
-      19, SpvOpISub, {14, 15}, return_instruction);
+      19, spv::Op::OpISub, {14, 15}, return_instruction);
   ASSERT_TRUE(
       transformation4.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation4, context.get(),
@@ -449,7 +450,7 @@
                                                kConsoleMessageConsumer));
 
   auto transformation5 = TransformationEquationInstruction(
-      20, SpvOpSNegate, {19}, return_instruction);
+      20, spv::Op::OpSNegate, {19}, return_instruction);
   ASSERT_TRUE(
       transformation5.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation5, context.get(),
@@ -460,7 +461,7 @@
       MakeDataDescriptor(20, {}), MakeDataDescriptor(16, {})));
 
   auto transformation6 = TransformationEquationInstruction(
-      21, SpvOpISub, {14, 19}, return_instruction);
+      21, spv::Op::OpISub, {14, 19}, return_instruction);
   ASSERT_TRUE(
       transformation6.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation6, context.get(),
@@ -471,7 +472,7 @@
       MakeDataDescriptor(21, {}), MakeDataDescriptor(15, {})));
 
   auto transformation7 = TransformationEquationInstruction(
-      22, SpvOpISub, {14, 18}, return_instruction);
+      22, spv::Op::OpISub, {14, 18}, return_instruction);
   ASSERT_TRUE(
       transformation7.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation7, context.get(),
@@ -480,7 +481,7 @@
                                                kConsoleMessageConsumer));
 
   auto transformation8 = TransformationEquationInstruction(
-      23, SpvOpSNegate, {22}, return_instruction);
+      23, spv::Op::OpSNegate, {22}, return_instruction);
   ASSERT_TRUE(
       transformation8.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation8, context.get(),
@@ -559,51 +560,51 @@
                                                kConsoleMessageConsumer));
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
-  auto insert_before = MakeInstructionDescriptor(13, SpvOpReturn, 0);
+  auto insert_before = MakeInstructionDescriptor(13, spv::Op::OpReturn, 0);
 
   // Too many operands.
-  ASSERT_FALSE(TransformationEquationInstruction(50, SpvOpBitcast, {15, 16},
-                                                 insert_before)
+  ASSERT_FALSE(TransformationEquationInstruction(50, spv::Op::OpBitcast,
+                                                 {15, 16}, insert_before)
                    .IsApplicable(context.get(), transformation_context));
 
   // Too few operands.
-  ASSERT_FALSE(
-      TransformationEquationInstruction(50, SpvOpBitcast, {}, insert_before)
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationEquationInstruction(50, spv::Op::OpBitcast, {},
+                                                 insert_before)
+                   .IsApplicable(context.get(), transformation_context));
 
   // Operand's id is invalid.
-  ASSERT_FALSE(
-      TransformationEquationInstruction(50, SpvOpBitcast, {50}, insert_before)
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationEquationInstruction(50, spv::Op::OpBitcast, {50},
+                                                 insert_before)
+                   .IsApplicable(context.get(), transformation_context));
 
   // Operand's type is invalid
-  ASSERT_FALSE(
-      TransformationEquationInstruction(50, SpvOpBitcast, {13}, insert_before)
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationEquationInstruction(50, spv::Op::OpBitcast, {13},
+                                                 insert_before)
+                   .IsApplicable(context.get(), transformation_context));
 
   // Operand must be a scalar or a vector of numerical type.
 #ifndef NDEBUG
-  ASSERT_DEATH(
-      TransformationEquationInstruction(50, SpvOpBitcast, {23}, insert_before)
-          .IsApplicable(context.get(), transformation_context),
-      "Operand is not a scalar or a vector of numerical type");
-  ASSERT_DEATH(
-      TransformationEquationInstruction(50, SpvOpBitcast, {24}, insert_before)
-          .IsApplicable(context.get(), transformation_context),
-      "Only vectors of numerical components are supported");
+  ASSERT_DEATH(TransformationEquationInstruction(50, spv::Op::OpBitcast, {23},
+                                                 insert_before)
+                   .IsApplicable(context.get(), transformation_context),
+               "Operand is not a scalar or a vector of numerical type");
+  ASSERT_DEATH(TransformationEquationInstruction(50, spv::Op::OpBitcast, {24},
+                                                 insert_before)
+                   .IsApplicable(context.get(), transformation_context),
+               "Only vectors of numerical components are supported");
 #else
-  ASSERT_FALSE(
-      TransformationEquationInstruction(50, SpvOpBitcast, {23}, insert_before)
-          .IsApplicable(context.get(), transformation_context));
-  ASSERT_FALSE(
-      TransformationEquationInstruction(50, SpvOpBitcast, {24}, insert_before)
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationEquationInstruction(50, spv::Op::OpBitcast, {23},
+                                                 insert_before)
+                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationEquationInstruction(50, spv::Op::OpBitcast, {24},
+                                                 insert_before)
+                   .IsApplicable(context.get(), transformation_context));
 #endif
 
   for (uint32_t operand_id = 15, fresh_id = 50; operand_id <= 20;
        ++operand_id, ++fresh_id) {
     TransformationEquationInstruction transformation(
-        fresh_id, SpvOpBitcast, {operand_id}, insert_before);
+        fresh_id, spv::Op::OpBitcast, {operand_id}, insert_before);
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
@@ -683,23 +684,23 @@
                                                kConsoleMessageConsumer));
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
-  auto insert_before = MakeInstructionDescriptor(13, SpvOpReturn, 0);
+  auto insert_before = MakeInstructionDescriptor(13, spv::Op::OpReturn, 0);
 
   // Scalar floating-point type does not exist.
-  ASSERT_FALSE(
-      TransformationEquationInstruction(50, SpvOpBitcast, {15}, insert_before)
-          .IsApplicable(context.get(), transformation_context));
-  ASSERT_FALSE(
-      TransformationEquationInstruction(50, SpvOpBitcast, {16}, insert_before)
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationEquationInstruction(50, spv::Op::OpBitcast, {15},
+                                                 insert_before)
+                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationEquationInstruction(50, spv::Op::OpBitcast, {16},
+                                                 insert_before)
+                   .IsApplicable(context.get(), transformation_context));
 
   // Vector of floating-point components does not exist.
-  ASSERT_FALSE(
-      TransformationEquationInstruction(50, SpvOpBitcast, {18}, insert_before)
-          .IsApplicable(context.get(), transformation_context));
-  ASSERT_FALSE(
-      TransformationEquationInstruction(50, SpvOpBitcast, {19}, insert_before)
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationEquationInstruction(50, spv::Op::OpBitcast, {18},
+                                                 insert_before)
+                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationEquationInstruction(50, spv::Op::OpBitcast, {19},
+                                                 insert_before)
+                   .IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationEquationInstructionTest, BitcastResultTypeIntDoesNotExist1) {
@@ -730,17 +731,17 @@
                                                kConsoleMessageConsumer));
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
-  auto insert_before = MakeInstructionDescriptor(13, SpvOpReturn, 0);
+  auto insert_before = MakeInstructionDescriptor(13, spv::Op::OpReturn, 0);
 
   // Scalar integral type does not exist.
-  ASSERT_FALSE(
-      TransformationEquationInstruction(50, SpvOpBitcast, {17}, insert_before)
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationEquationInstruction(50, spv::Op::OpBitcast, {17},
+                                                 insert_before)
+                   .IsApplicable(context.get(), transformation_context));
 
   // Vector of integral components does not exist.
-  ASSERT_FALSE(
-      TransformationEquationInstruction(50, SpvOpBitcast, {20}, insert_before)
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationEquationInstruction(50, spv::Op::OpBitcast, {20},
+                                                 insert_before)
+                   .IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationEquationInstructionTest, BitcastResultTypeIntDoesNotExist2) {
@@ -773,19 +774,19 @@
                                                kConsoleMessageConsumer));
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
-  auto insert_before = MakeInstructionDescriptor(13, SpvOpReturn, 0);
+  auto insert_before = MakeInstructionDescriptor(13, spv::Op::OpReturn, 0);
 
   {
-    TransformationEquationInstruction transformation(50, SpvOpBitcast, {17},
-                                                     insert_before);
+    TransformationEquationInstruction transformation(50, spv::Op::OpBitcast,
+                                                     {17}, insert_before);
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
                           &transformation_context);
   }
   {
-    TransformationEquationInstruction transformation(51, SpvOpBitcast, {20},
-                                                     insert_before);
+    TransformationEquationInstruction transformation(51, spv::Op::OpBitcast,
+                                                     {20}, insert_before);
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
@@ -848,19 +849,19 @@
                                                kConsoleMessageConsumer));
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
-  auto insert_before = MakeInstructionDescriptor(13, SpvOpReturn, 0);
+  auto insert_before = MakeInstructionDescriptor(13, spv::Op::OpReturn, 0);
 
   {
-    TransformationEquationInstruction transformation(50, SpvOpBitcast, {17},
-                                                     insert_before);
+    TransformationEquationInstruction transformation(50, spv::Op::OpBitcast,
+                                                     {17}, insert_before);
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
                           &transformation_context);
   }
   {
-    TransformationEquationInstruction transformation(51, SpvOpBitcast, {20},
-                                                     insert_before);
+    TransformationEquationInstruction transformation(51, spv::Op::OpBitcast,
+                                                     {20}, insert_before);
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
@@ -922,20 +923,20 @@
                                                kConsoleMessageConsumer));
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
-  auto insert_before = MakeInstructionDescriptor(13, SpvOpReturn, 0);
+  auto insert_before = MakeInstructionDescriptor(13, spv::Op::OpReturn, 0);
 
   {
-    TransformationEquationInstruction transformation(50, SpvOpBitcast, {17},
-                                                     insert_before);
+    TransformationEquationInstruction transformation(50, spv::Op::OpBitcast,
+                                                     {17}, insert_before);
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
                           &transformation_context);
   }
 
-  ASSERT_FALSE(
-      TransformationEquationInstruction(51, SpvOpBitcast, {20}, insert_before)
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationEquationInstruction(51, spv::Op::OpBitcast, {20},
+                                                 insert_before)
+                   .IsApplicable(context.get(), transformation_context));
 
   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
                                                kConsoleMessageConsumer));
@@ -993,20 +994,20 @@
                                                kConsoleMessageConsumer));
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
-  auto insert_before = MakeInstructionDescriptor(13, SpvOpReturn, 0);
+  auto insert_before = MakeInstructionDescriptor(13, spv::Op::OpReturn, 0);
 
   {
-    TransformationEquationInstruction transformation(50, SpvOpBitcast, {17},
-                                                     insert_before);
+    TransformationEquationInstruction transformation(50, spv::Op::OpBitcast,
+                                                     {17}, insert_before);
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
                           &transformation_context);
   }
 
-  ASSERT_FALSE(
-      TransformationEquationInstruction(51, SpvOpBitcast, {20}, insert_before)
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationEquationInstruction(51, spv::Op::OpBitcast, {20},
+                                                 insert_before)
+                   .IsApplicable(context.get(), transformation_context));
 
   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
                                                kConsoleMessageConsumer));
@@ -1066,19 +1067,19 @@
                                                kConsoleMessageConsumer));
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
-  auto insert_before = MakeInstructionDescriptor(13, SpvOpReturn, 0);
+  auto insert_before = MakeInstructionDescriptor(13, spv::Op::OpReturn, 0);
 
   {
-    TransformationEquationInstruction transformation(50, SpvOpBitcast, {17},
-                                                     insert_before);
+    TransformationEquationInstruction transformation(50, spv::Op::OpBitcast,
+                                                     {17}, insert_before);
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
                           &transformation_context);
   }
   {
-    TransformationEquationInstruction transformation(51, SpvOpBitcast, {20},
-                                                     insert_before);
+    TransformationEquationInstruction transformation(51, spv::Op::OpBitcast,
+                                                     {20}, insert_before);
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
@@ -1146,19 +1147,19 @@
                                                kConsoleMessageConsumer));
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
-  auto insert_before = MakeInstructionDescriptor(13, SpvOpReturn, 0);
+  auto insert_before = MakeInstructionDescriptor(13, spv::Op::OpReturn, 0);
 
   {
-    TransformationEquationInstruction transformation(50, SpvOpBitcast, {17},
-                                                     insert_before);
+    TransformationEquationInstruction transformation(50, spv::Op::OpBitcast,
+                                                     {17}, insert_before);
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
                           &transformation_context);
   }
   {
-    TransformationEquationInstruction transformation(51, SpvOpBitcast, {20},
-                                                     insert_before);
+    TransformationEquationInstruction transformation(51, spv::Op::OpBitcast,
+                                                     {20}, insert_before);
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
@@ -1222,10 +1223,10 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
   protobufs::InstructionDescriptor return_instruction =
-      MakeInstructionDescriptor(13, SpvOpReturn, 0);
+      MakeInstructionDescriptor(13, spv::Op::OpReturn, 0);
 
   auto transformation1 = TransformationEquationInstruction(
-      522, SpvOpISub, {113, 113}, return_instruction);
+      522, spv::Op::OpISub, {113, 113}, return_instruction);
   ASSERT_TRUE(
       transformation1.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation1, context.get(),
@@ -1234,7 +1235,7 @@
                                                kConsoleMessageConsumer));
 
   auto transformation2 = TransformationEquationInstruction(
-      570, SpvOpIAdd, {522, 113}, return_instruction);
+      570, spv::Op::OpIAdd, {522, 113}, return_instruction);
   ASSERT_TRUE(
       transformation2.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation2, context.get(),
@@ -1294,10 +1295,10 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
   protobufs::InstructionDescriptor return_instruction =
-      MakeInstructionDescriptor(13, SpvOpReturn, 0);
+      MakeInstructionDescriptor(13, spv::Op::OpReturn, 0);
 
   auto transformation1 = TransformationEquationInstruction(
-      522, SpvOpISub, {113, 113}, return_instruction);
+      522, spv::Op::OpISub, {113, 113}, return_instruction);
   ASSERT_TRUE(
       transformation1.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation1, context.get(),
@@ -1306,7 +1307,7 @@
                                                kConsoleMessageConsumer));
 
   auto transformation2 = TransformationEquationInstruction(
-      570, SpvOpIAdd, {522, 113}, return_instruction);
+      570, spv::Op::OpIAdd, {522, 113}, return_instruction);
   ASSERT_TRUE(
       transformation2.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation2, context.get(),
@@ -1380,97 +1381,97 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
   protobufs::InstructionDescriptor return_instruction =
-      MakeInstructionDescriptor(13, SpvOpReturn, 0);
+      MakeInstructionDescriptor(13, spv::Op::OpReturn, 0);
 
   // Too few instruction operands.
-  ASSERT_FALSE(TransformationEquationInstruction(50, SpvOpConvertSToF, {},
+  ASSERT_FALSE(TransformationEquationInstruction(50, spv::Op::OpConvertSToF, {},
                                                  return_instruction)
                    .IsApplicable(context.get(), transformation_context));
 
   // Too many instruction operands.
-  ASSERT_FALSE(TransformationEquationInstruction(50, SpvOpConvertSToF, {15, 16},
-                                                 return_instruction)
+  ASSERT_FALSE(TransformationEquationInstruction(50, spv::Op::OpConvertSToF,
+                                                 {15, 16}, return_instruction)
                    .IsApplicable(context.get(), transformation_context));
 
   // Operand has no type id.
-  ASSERT_FALSE(TransformationEquationInstruction(50, SpvOpConvertSToF, {7},
-                                                 return_instruction)
+  ASSERT_FALSE(TransformationEquationInstruction(50, spv::Op::OpConvertSToF,
+                                                 {7}, return_instruction)
                    .IsApplicable(context.get(), transformation_context));
 
   // OpConvertSToF and OpConvertUToF require an operand to have scalar or vector
   // of integral components type.
-  ASSERT_FALSE(TransformationEquationInstruction(50, SpvOpConvertSToF, {17},
-                                                 return_instruction)
+  ASSERT_FALSE(TransformationEquationInstruction(50, spv::Op::OpConvertSToF,
+                                                 {17}, return_instruction)
                    .IsApplicable(context.get(), transformation_context));
-  ASSERT_FALSE(TransformationEquationInstruction(50, SpvOpConvertSToF, {14},
-                                                 return_instruction)
+  ASSERT_FALSE(TransformationEquationInstruction(50, spv::Op::OpConvertSToF,
+                                                 {14}, return_instruction)
                    .IsApplicable(context.get(), transformation_context));
-  ASSERT_FALSE(TransformationEquationInstruction(50, SpvOpConvertUToF, {17},
-                                                 return_instruction)
+  ASSERT_FALSE(TransformationEquationInstruction(50, spv::Op::OpConvertUToF,
+                                                 {17}, return_instruction)
                    .IsApplicable(context.get(), transformation_context));
-  ASSERT_FALSE(TransformationEquationInstruction(50, SpvOpConvertUToF, {14},
-                                                 return_instruction)
+  ASSERT_FALSE(TransformationEquationInstruction(50, spv::Op::OpConvertUToF,
+                                                 {14}, return_instruction)
                    .IsApplicable(context.get(), transformation_context));
 
   {
-    TransformationEquationInstruction transformation(50, SpvOpConvertSToF, {15},
-                                                     return_instruction);
+    TransformationEquationInstruction transformation(50, spv::Op::OpConvertSToF,
+                                                     {15}, return_instruction);
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
                           &transformation_context);
   }
   {
-    TransformationEquationInstruction transformation(51, SpvOpConvertSToF, {10},
-                                                     return_instruction);
+    TransformationEquationInstruction transformation(51, spv::Op::OpConvertSToF,
+                                                     {10}, return_instruction);
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
                           &transformation_context);
   }
   {
-    TransformationEquationInstruction transformation(52, SpvOpConvertUToF, {16},
-                                                     return_instruction);
+    TransformationEquationInstruction transformation(52, spv::Op::OpConvertUToF,
+                                                     {16}, return_instruction);
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
                           &transformation_context);
   }
   {
-    TransformationEquationInstruction transformation(53, SpvOpConvertUToF, {11},
-                                                     return_instruction);
+    TransformationEquationInstruction transformation(53, spv::Op::OpConvertUToF,
+                                                     {11}, return_instruction);
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
                           &transformation_context);
   }
   {
-    TransformationEquationInstruction transformation(58, SpvOpConvertSToF, {18},
-                                                     return_instruction);
+    TransformationEquationInstruction transformation(58, spv::Op::OpConvertSToF,
+                                                     {18}, return_instruction);
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
                           &transformation_context);
   }
   {
-    TransformationEquationInstruction transformation(59, SpvOpConvertUToF, {19},
-                                                     return_instruction);
+    TransformationEquationInstruction transformation(59, spv::Op::OpConvertUToF,
+                                                     {19}, return_instruction);
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
                           &transformation_context);
   }
   {
-    TransformationEquationInstruction transformation(60, SpvOpConvertSToF, {20},
-                                                     return_instruction);
+    TransformationEquationInstruction transformation(60, spv::Op::OpConvertSToF,
+                                                     {20}, return_instruction);
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
                           &transformation_context);
   }
   {
-    TransformationEquationInstruction transformation(61, SpvOpConvertUToF, {21},
-                                                     return_instruction);
+    TransformationEquationInstruction transformation(61, spv::Op::OpConvertUToF,
+                                                     {21}, return_instruction);
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
@@ -1555,22 +1556,22 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
   protobufs::InstructionDescriptor return_instruction =
-      MakeInstructionDescriptor(13, SpvOpReturn, 0);
+      MakeInstructionDescriptor(13, spv::Op::OpReturn, 0);
 
   // Scalar float type doesn't exist.
-  ASSERT_FALSE(TransformationEquationInstruction(16, SpvOpConvertUToF, {10},
-                                                 return_instruction)
+  ASSERT_FALSE(TransformationEquationInstruction(16, spv::Op::OpConvertUToF,
+                                                 {10}, return_instruction)
                    .IsApplicable(context.get(), transformation_context));
-  ASSERT_FALSE(TransformationEquationInstruction(16, SpvOpConvertSToF, {11},
-                                                 return_instruction)
+  ASSERT_FALSE(TransformationEquationInstruction(16, spv::Op::OpConvertSToF,
+                                                 {11}, return_instruction)
                    .IsApplicable(context.get(), transformation_context));
 
   // Vector float type doesn't exist.
-  ASSERT_FALSE(TransformationEquationInstruction(16, SpvOpConvertUToF, {14},
-                                                 return_instruction)
+  ASSERT_FALSE(TransformationEquationInstruction(16, spv::Op::OpConvertUToF,
+                                                 {14}, return_instruction)
                    .IsApplicable(context.get(), transformation_context));
-  ASSERT_FALSE(TransformationEquationInstruction(16, SpvOpConvertSToF, {15},
-                                                 return_instruction)
+  ASSERT_FALSE(TransformationEquationInstruction(16, spv::Op::OpConvertSToF,
+                                                 {15}, return_instruction)
                    .IsApplicable(context.get(), transformation_context));
 }
 
@@ -1605,11 +1606,11 @@
                                                kConsoleMessageConsumer));
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
-  auto return_instruction = MakeInstructionDescriptor(13, SpvOpReturn, 0);
+  auto return_instruction = MakeInstructionDescriptor(13, spv::Op::OpReturn, 0);
 
   // Applicable.
-  TransformationEquationInstruction transformation(14, SpvOpIAdd, {15, 16},
-                                                   return_instruction);
+  TransformationEquationInstruction transformation(
+      14, spv::Op::OpIAdd, {15, 16}, return_instruction);
   ASSERT_TRUE(
       transformation.IsApplicable(context.get(), transformation_context));
 
@@ -1662,11 +1663,12 @@
   transformation_context.GetFactManager()->AddFactBlockIsDead(41);
 
   TransformationEquationInstruction transformation1(
-      14, SpvOpIAdd, {15, 16},
-      MakeInstructionDescriptor(13, SpvOpSelectionMerge, 0));
+      14, spv::Op::OpIAdd, {15, 16},
+      MakeInstructionDescriptor(13, spv::Op::OpSelectionMerge, 0));
   // No synonym is created since block is dead.
   TransformationEquationInstruction transformation2(
-      100, SpvOpISub, {14, 16}, MakeInstructionDescriptor(41, SpvOpBranch, 0));
+      100, spv::Op::OpISub, {14, 16},
+      MakeInstructionDescriptor(41, spv::Op::OpBranch, 0));
   ASSERT_TRUE(
       transformation1.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation1, context.get(),
diff --git a/test/fuzz/transformation_flatten_conditional_branch_test.cpp b/test/fuzz/transformation_flatten_conditional_branch_test.cpp
index 800af0e..0631899 100644
--- a/test/fuzz/transformation_flatten_conditional_branch_test.cpp
+++ b/test/fuzz/transformation_flatten_conditional_branch_test.cpp
@@ -441,8 +441,8 @@
   ASSERT_DEATH(TransformationFlattenConditionalBranch(
                    31, true, 0, 0, 0,
                    {{MakeSideEffectWrapperInfo(
-                       MakeInstructionDescriptor(6, SpvOpLoad, 0), 100, 101,
-                       102, 103, 104, 14)}})
+                       MakeInstructionDescriptor(6, spv::Op::OpLoad, 0), 100,
+                       101, 102, 103, 104, 14)}})
                    .IsApplicable(context.get(), transformation_context),
                "Bad attempt to query whether overflow ids are available.");
 #endif
@@ -451,52 +451,55 @@
   ASSERT_FALSE(TransformationFlattenConditionalBranch(
                    31, true, 0, 0, 0,
                    {{MakeSideEffectWrapperInfo(
-                       MakeInstructionDescriptor(6, SpvOpLoad, 0), 100, 101,
-                       102, 103, 0, 0)}})
+                       MakeInstructionDescriptor(6, spv::Op::OpLoad, 0), 100,
+                       101, 102, 103, 0, 0)}})
                    .IsApplicable(context.get(), transformation_context));
 
   // Not all fresh ids given are distinct.
   ASSERT_FALSE(TransformationFlattenConditionalBranch(
                    31, true, 0, 0, 0,
                    {{MakeSideEffectWrapperInfo(
-                       MakeInstructionDescriptor(6, SpvOpLoad, 0), 100, 100,
-                       102, 103, 104, 0)}})
+                       MakeInstructionDescriptor(6, spv::Op::OpLoad, 0), 100,
+                       100, 102, 103, 104, 0)}})
                    .IsApplicable(context.get(), transformation_context));
 
   // %48 heads a construct containing an OpSampledImage instruction.
   ASSERT_FALSE(TransformationFlattenConditionalBranch(
                    48, true, 0, 0, 0,
                    {{MakeSideEffectWrapperInfo(
-                       MakeInstructionDescriptor(53, SpvOpLoad, 0), 100, 101,
-                       102, 103, 104, 0)}})
+                       MakeInstructionDescriptor(53, spv::Op::OpLoad, 0), 100,
+                       101, 102, 103, 104, 0)}})
                    .IsApplicable(context.get(), transformation_context));
 
   // %0 is not a valid id.
   ASSERT_FALSE(
       TransformationFlattenConditionalBranch(
           31, true, 0, 0, 0,
-          {MakeSideEffectWrapperInfo(MakeInstructionDescriptor(6, SpvOpLoad, 0),
-                                     104, 100, 101, 102, 103, 0),
+          {MakeSideEffectWrapperInfo(
+               MakeInstructionDescriptor(6, spv::Op::OpLoad, 0), 104, 100, 101,
+               102, 103, 0),
            MakeSideEffectWrapperInfo(
-               MakeInstructionDescriptor(6, SpvOpStore, 0), 106, 105)})
+               MakeInstructionDescriptor(6, spv::Op::OpStore, 0), 106, 105)})
           .IsApplicable(context.get(), transformation_context));
 
   // %17 is a float constant, while %6 has int type.
   ASSERT_FALSE(
       TransformationFlattenConditionalBranch(
           31, true, 0, 0, 0,
-          {MakeSideEffectWrapperInfo(MakeInstructionDescriptor(6, SpvOpLoad, 0),
-                                     104, 100, 101, 102, 103, 17),
+          {MakeSideEffectWrapperInfo(
+               MakeInstructionDescriptor(6, spv::Op::OpLoad, 0), 104, 100, 101,
+               102, 103, 17),
            MakeSideEffectWrapperInfo(
-               MakeInstructionDescriptor(6, SpvOpStore, 0), 106, 105)})
+               MakeInstructionDescriptor(6, spv::Op::OpStore, 0), 106, 105)})
           .IsApplicable(context.get(), transformation_context));
 
   auto transformation1 = TransformationFlattenConditionalBranch(
       31, true, 0, 0, 0,
-      {MakeSideEffectWrapperInfo(MakeInstructionDescriptor(6, SpvOpLoad, 0),
-                                 104, 100, 101, 102, 103, 70),
-       MakeSideEffectWrapperInfo(MakeInstructionDescriptor(6, SpvOpStore, 0),
-                                 106, 105)});
+      {MakeSideEffectWrapperInfo(
+           MakeInstructionDescriptor(6, spv::Op::OpLoad, 0), 104, 100, 101, 102,
+           103, 70),
+       MakeSideEffectWrapperInfo(
+           MakeInstructionDescriptor(6, spv::Op::OpStore, 0), 106, 105)});
   ASSERT_TRUE(
       transformation1.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation1, context.get(),
@@ -514,8 +517,8 @@
 
   auto transformation2 = TransformationFlattenConditionalBranch(
       36, false, 0, 0, 0,
-      {MakeSideEffectWrapperInfo(MakeInstructionDescriptor(8, SpvOpStore, 0),
-                                 114, 113)});
+      {MakeSideEffectWrapperInfo(
+          MakeInstructionDescriptor(8, spv::Op::OpStore, 0), 114, 113)});
   ASSERT_TRUE(
       transformation2.IsApplicable(context.get(), new_transformation_context));
   ApplyAndCheckFreshIds(transformation2, context.get(),
@@ -717,7 +720,8 @@
   auto transformation1 = TransformationFlattenConditionalBranch(
       7, true, 0, 0, 0,
       {{MakeSideEffectWrapperInfo(
-          MakeInstructionDescriptor(10, SpvOpFunctionCall, 0), 100, 101)}});
+          MakeInstructionDescriptor(10, spv::Op::OpFunctionCall, 0), 100,
+          101)}});
   ASSERT_TRUE(
       transformation1.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation1, context.get(),
@@ -729,7 +733,8 @@
       TransformationFlattenConditionalBranch(
           7, true, 0, 0, 0,
           {{MakeSideEffectWrapperInfo(
-              MakeInstructionDescriptor(14, SpvOpFunctionCall, 0), 102, 103)}})
+              MakeInstructionDescriptor(14, spv::Op::OpFunctionCall, 0), 102,
+              103)}})
           .IsApplicable(context.get(), transformation_context));
 
   // Block %16 is unreachable.
@@ -1126,10 +1131,12 @@
 
   auto transformation = TransformationFlattenConditionalBranch(
       7, true, 0, 0, 0,
-      {MakeSideEffectWrapperInfo(MakeInstructionDescriptor(522, SpvOpLoad, 0),
-                                 200, 201, 202, 203, 204, 5),
-       MakeSideEffectWrapperInfo(MakeInstructionDescriptor(466, SpvOpLoad, 0),
-                                 300, 301, 302, 303, 304, 5)});
+      {MakeSideEffectWrapperInfo(
+           MakeInstructionDescriptor(522, spv::Op::OpLoad, 0), 200, 201, 202,
+           203, 204, 5),
+       MakeSideEffectWrapperInfo(
+           MakeInstructionDescriptor(466, spv::Op::OpLoad, 0), 300, 301, 302,
+           303, 304, 5)});
   ASSERT_TRUE(
       transformation.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
@@ -1231,8 +1238,9 @@
 
   auto transformation = TransformationFlattenConditionalBranch(
       5, true, 0, 0, 0,
-      {MakeSideEffectWrapperInfo(MakeInstructionDescriptor(20, SpvOpLoad, 0),
-                                 100, 101, 102, 103, 104, 21)});
+      {MakeSideEffectWrapperInfo(
+          MakeInstructionDescriptor(20, spv::Op::OpLoad, 0), 100, 101, 102, 103,
+          104, 21)});
   ASSERT_TRUE(
       transformation.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
@@ -1299,8 +1307,8 @@
   ASSERT_FALSE(TransformationFlattenConditionalBranch(
                    28, true, 0, 0, 0,
                    {MakeSideEffectWrapperInfo(
-                       MakeInstructionDescriptor(40, SpvOpLoad, 0), 100, 101,
-                       102, 103, 104, 200)})
+                       MakeInstructionDescriptor(40, spv::Op::OpLoad, 0), 100,
+                       101, 102, 103, 104, 200)})
                    .IsApplicable(context.get(), transformation_context));
 }
 
@@ -1730,7 +1738,7 @@
   // Check that the in operands of any OpSelect instructions all have the
   // appropriate operand type.
   context->module()->ForEachInst([](opt::Instruction* inst) {
-    if (inst->opcode() == SpvOpSelect) {
+    if (inst->opcode() == spv::Op::OpSelect) {
       ASSERT_EQ(SPV_OPERAND_TYPE_ID, inst->GetInOperand(0).type);
       ASSERT_EQ(SPV_OPERAND_TYPE_ID, inst->GetInOperand(1).type);
       ASSERT_EQ(SPV_OPERAND_TYPE_ID, inst->GetInOperand(2).type);
@@ -2183,14 +2191,15 @@
 
   transformation_context.GetFactManager()->AddFactDataSynonym(
       MakeDataDescriptor(10, {}), MakeDataDescriptor(21, {}));
-  ASSERT_FALSE(TransformationFlattenConditionalBranch(
-                   12, true, 0, 0, 0,
-                   {MakeSideEffectWrapperInfo(
-                        MakeInstructionDescriptor(30, SpvOpStore, 0), 100, 101),
-                    MakeSideEffectWrapperInfo(
-                        MakeInstructionDescriptor(21, SpvOpLoad, 0), 102, 103,
-                        104, 105, 106, 80)})
-                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationFlattenConditionalBranch(
+          12, true, 0, 0, 0,
+          {MakeSideEffectWrapperInfo(
+               MakeInstructionDescriptor(30, spv::Op::OpStore, 0), 100, 101),
+           MakeSideEffectWrapperInfo(
+               MakeInstructionDescriptor(21, spv::Op::OpLoad, 0), 102, 103, 104,
+               105, 106, 80)})
+          .IsApplicable(context.get(), transformation_context));
 }
 
 }  // namespace
diff --git a/test/fuzz/transformation_function_call_test.cpp b/test/fuzz/transformation_function_call_test.cpp
index b27b3ca..c963e33 100644
--- a/test/fuzz/transformation_function_call_test.cpp
+++ b/test/fuzz/transformation_function_call_test.cpp
@@ -174,88 +174,91 @@
 
   // Bad transformations
   // Too many arguments
-  ASSERT_FALSE(
-      TransformationFunctionCall(100, 21, {71, 72, 71},
-                                 MakeInstructionDescriptor(59, SpvOpBranch, 0))
-          .IsApplicable(context.get(), transformation_context));
-  // Too few arguments
   ASSERT_FALSE(TransformationFunctionCall(
-                   100, 21, {71}, MakeInstructionDescriptor(59, SpvOpBranch, 0))
+                   100, 21, {71, 72, 71},
+                   MakeInstructionDescriptor(59, spv::Op::OpBranch, 0))
                    .IsApplicable(context.get(), transformation_context));
+  // Too few arguments
+  ASSERT_FALSE(
+      TransformationFunctionCall(
+          100, 21, {71}, MakeInstructionDescriptor(59, spv::Op::OpBranch, 0))
+          .IsApplicable(context.get(), transformation_context));
   // Arguments are the wrong way around (types do not match)
-  ASSERT_FALSE(
-      TransformationFunctionCall(100, 21, {72, 71},
-                                 MakeInstructionDescriptor(59, SpvOpBranch, 0))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationFunctionCall(
+                   100, 21, {72, 71},
+                   MakeInstructionDescriptor(59, spv::Op::OpBranch, 0))
+                   .IsApplicable(context.get(), transformation_context));
   // 21 is not an appropriate argument
-  ASSERT_FALSE(
-      TransformationFunctionCall(100, 21, {21, 72},
-                                 MakeInstructionDescriptor(59, SpvOpBranch, 0))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationFunctionCall(
+                   100, 21, {21, 72},
+                   MakeInstructionDescriptor(59, spv::Op::OpBranch, 0))
+                   .IsApplicable(context.get(), transformation_context));
   // 300 does not exist
-  ASSERT_FALSE(
-      TransformationFunctionCall(100, 21, {300, 72},
-                                 MakeInstructionDescriptor(59, SpvOpBranch, 0))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationFunctionCall(
+                   100, 21, {300, 72},
+                   MakeInstructionDescriptor(59, spv::Op::OpBranch, 0))
+                   .IsApplicable(context.get(), transformation_context));
   // 71 is not a function
-  ASSERT_FALSE(
-      TransformationFunctionCall(100, 71, {71, 72},
-                                 MakeInstructionDescriptor(59, SpvOpBranch, 0))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationFunctionCall(
+                   100, 71, {71, 72},
+                   MakeInstructionDescriptor(59, spv::Op::OpBranch, 0))
+                   .IsApplicable(context.get(), transformation_context));
   // 500 does not exist
-  ASSERT_FALSE(
-      TransformationFunctionCall(100, 500, {71, 72},
-                                 MakeInstructionDescriptor(59, SpvOpBranch, 0))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationFunctionCall(
+                   100, 500, {71, 72},
+                   MakeInstructionDescriptor(59, spv::Op::OpBranch, 0))
+                   .IsApplicable(context.get(), transformation_context));
   // Id is not fresh
   ASSERT_FALSE(
-      TransformationFunctionCall(21, 21, {71, 72},
-                                 MakeInstructionDescriptor(59, SpvOpBranch, 0))
+      TransformationFunctionCall(
+          21, 21, {71, 72}, MakeInstructionDescriptor(59, spv::Op::OpBranch, 0))
           .IsApplicable(context.get(), transformation_context));
   // Access chain as pointer parameter
-  ASSERT_FALSE(
-      TransformationFunctionCall(100, 21, {98, 72},
-                                 MakeInstructionDescriptor(59, SpvOpBranch, 0))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationFunctionCall(
+                   100, 21, {98, 72},
+                   MakeInstructionDescriptor(59, spv::Op::OpBranch, 0))
+                   .IsApplicable(context.get(), transformation_context));
   // Copied object as pointer parameter
-  ASSERT_FALSE(
-      TransformationFunctionCall(100, 21, {99, 72},
-                                 MakeInstructionDescriptor(59, SpvOpBranch, 0))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationFunctionCall(
+                   100, 21, {99, 72},
+                   MakeInstructionDescriptor(59, spv::Op::OpBranch, 0))
+                   .IsApplicable(context.get(), transformation_context));
   // Non-livesafe called from original live block
-  ASSERT_FALSE(
-      TransformationFunctionCall(
-          100, 10, {71}, MakeInstructionDescriptor(99, SpvOpSelectionMerge, 0))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationFunctionCall(
+                   100, 10, {71},
+                   MakeInstructionDescriptor(99, spv::Op::OpSelectionMerge, 0))
+                   .IsApplicable(context.get(), transformation_context));
   // Non-livesafe called from livesafe function
-  ASSERT_FALSE(
-      TransformationFunctionCall(
-          100, 10, {19}, MakeInstructionDescriptor(38, SpvOpConvertFToS, 0))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationFunctionCall(
+                   100, 10, {19},
+                   MakeInstructionDescriptor(38, spv::Op::OpConvertFToS, 0))
+                   .IsApplicable(context.get(), transformation_context));
   // Livesafe function called with pointer to non-arbitrary local variable
+  ASSERT_FALSE(TransformationFunctionCall(
+                   100, 21, {61, 72},
+                   MakeInstructionDescriptor(38, spv::Op::OpConvertFToS, 0))
+                   .IsApplicable(context.get(), transformation_context));
+  // Direct recursion
   ASSERT_FALSE(
       TransformationFunctionCall(
-          100, 21, {61, 72}, MakeInstructionDescriptor(38, SpvOpConvertFToS, 0))
+          100, 4, {}, MakeInstructionDescriptor(59, spv::Op::OpBranch, 0))
           .IsApplicable(context.get(), transformation_context));
-  // Direct recursion
-  ASSERT_FALSE(TransformationFunctionCall(
-                   100, 4, {}, MakeInstructionDescriptor(59, SpvOpBranch, 0))
-                   .IsApplicable(context.get(), transformation_context));
   // Indirect recursion
-  ASSERT_FALSE(TransformationFunctionCall(
-                   100, 24, {9}, MakeInstructionDescriptor(96, SpvOpBranch, 0))
-                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationFunctionCall(
+          100, 24, {9}, MakeInstructionDescriptor(96, spv::Op::OpBranch, 0))
+          .IsApplicable(context.get(), transformation_context));
   // Parameter 23 is not available at the call site
   ASSERT_FALSE(
-      TransformationFunctionCall(104, 10, {23},
-                                 MakeInstructionDescriptor(205, SpvOpBranch, 0))
+      TransformationFunctionCall(
+          104, 10, {23}, MakeInstructionDescriptor(205, spv::Op::OpBranch, 0))
           .IsApplicable(context.get(), transformation_context));
 
   // Good transformations
   {
     // Livesafe called from dead block: fine
     TransformationFunctionCall transformation(
-        100, 21, {71, 72}, MakeInstructionDescriptor(59, SpvOpBranch, 0));
+        100, 21, {71, 72}, MakeInstructionDescriptor(59, spv::Op::OpBranch, 0));
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
@@ -266,7 +269,8 @@
   {
     // Livesafe called from original live block: fine
     TransformationFunctionCall transformation(
-        101, 21, {71, 72}, MakeInstructionDescriptor(98, SpvOpAccessChain, 0));
+        101, 21, {71, 72},
+        MakeInstructionDescriptor(98, spv::Op::OpAccessChain, 0));
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
@@ -277,7 +281,7 @@
   {
     // Livesafe called from livesafe function: fine
     TransformationFunctionCall transformation(
-        102, 200, {19, 20}, MakeInstructionDescriptor(36, SpvOpLoad, 0));
+        102, 200, {19, 20}, MakeInstructionDescriptor(36, spv::Op::OpLoad, 0));
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
@@ -288,7 +292,7 @@
   {
     // Dead called from dead block in injected function: fine
     TransformationFunctionCall transformation(
-        103, 10, {23}, MakeInstructionDescriptor(45, SpvOpLoad, 0));
+        103, 10, {23}, MakeInstructionDescriptor(45, spv::Op::OpLoad, 0));
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
@@ -299,7 +303,7 @@
   {
     // Non-livesafe called from dead block in livesafe function: OK
     TransformationFunctionCall transformation(
-        104, 10, {201}, MakeInstructionDescriptor(205, SpvOpBranch, 0));
+        104, 10, {201}, MakeInstructionDescriptor(205, spv::Op::OpBranch, 0));
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
@@ -310,7 +314,7 @@
   {
     // Livesafe called from dead block with non-arbitrary parameter
     TransformationFunctionCall transformation(
-        105, 21, {62, 65}, MakeInstructionDescriptor(59, SpvOpBranch, 0));
+        105, 21, {62, 65}, MakeInstructionDescriptor(59, spv::Op::OpBranch, 0));
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
@@ -465,9 +469,10 @@
   transformation_context.GetFactManager()->AddFactBlockIsDead(11);
 
   // 4 is an entry point, so it is not legal for it to be the target of a call.
-  ASSERT_FALSE(TransformationFunctionCall(
-                   100, 4, {}, MakeInstructionDescriptor(11, SpvOpReturn, 0))
-                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationFunctionCall(
+          100, 4, {}, MakeInstructionDescriptor(11, spv::Op::OpReturn, 0))
+          .IsApplicable(context.get(), transformation_context));
 }
 
 }  // namespace
diff --git a/test/fuzz/transformation_load_test.cpp b/test/fuzz/transformation_load_test.cpp
index 370826e..6fde49a 100644
--- a/test/fuzz/transformation_load_test.cpp
+++ b/test/fuzz/transformation_load_test.cpp
@@ -131,67 +131,68 @@
   //  60 - null
 
   // Bad: id is not fresh
-  ASSERT_FALSE(
-      TransformationLoad(33, 33, false, 0, 0,
-                         MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationLoad(
+                   33, 33, false, 0, 0,
+                   MakeInstructionDescriptor(38, spv::Op::OpAccessChain, 0))
+                   .IsApplicable(context.get(), transformation_context));
   // Bad: attempt to load from 11 from outside its function
-  ASSERT_FALSE(
-      TransformationLoad(100, 11, false, 0, 0,
-                         MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationLoad(
+                   100, 11, false, 0, 0,
+                   MakeInstructionDescriptor(38, spv::Op::OpAccessChain, 0))
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: pointer is not available
-  ASSERT_FALSE(
-      TransformationLoad(100, 33, false, 0, 0,
-                         MakeInstructionDescriptor(45, SpvOpCopyObject, 0))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationLoad(
+                   100, 33, false, 0, 0,
+                   MakeInstructionDescriptor(45, spv::Op::OpCopyObject, 0))
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: attempt to insert before OpVariable
   ASSERT_FALSE(
       TransformationLoad(100, 27, false, 0, 0,
-                         MakeInstructionDescriptor(27, SpvOpVariable, 0))
+                         MakeInstructionDescriptor(27, spv::Op::OpVariable, 0))
           .IsApplicable(context.get(), transformation_context));
 
   // Bad: pointer id does not exist
-  ASSERT_FALSE(
-      TransformationLoad(100, 1000, false, 0, 0,
-                         MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationLoad(
+                   100, 1000, false, 0, 0,
+                   MakeInstructionDescriptor(38, spv::Op::OpAccessChain, 0))
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: pointer id exists but does not have a type
-  ASSERT_FALSE(
-      TransformationLoad(100, 5, false, 0, 0,
-                         MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationLoad(
+                   100, 5, false, 0, 0,
+                   MakeInstructionDescriptor(38, spv::Op::OpAccessChain, 0))
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: pointer id exists and has a type, but is not a pointer
-  ASSERT_FALSE(
-      TransformationLoad(100, 24, false, 0, 0,
-                         MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationLoad(
+                   100, 24, false, 0, 0,
+                   MakeInstructionDescriptor(38, spv::Op::OpAccessChain, 0))
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: attempt to load from null pointer
-  ASSERT_FALSE(
-      TransformationLoad(100, 60, false, 0, 0,
-                         MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationLoad(
+                   100, 60, false, 0, 0,
+                   MakeInstructionDescriptor(38, spv::Op::OpAccessChain, 0))
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: %40 is not available at the program point
-  ASSERT_FALSE(TransformationLoad(100, 40, false, 0, 0,
-                                  MakeInstructionDescriptor(37, SpvOpReturn, 0))
-                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationLoad(100, 40, false, 0, 0,
+                         MakeInstructionDescriptor(37, spv::Op::OpReturn, 0))
+          .IsApplicable(context.get(), transformation_context));
 
   // Bad: The described instruction does not exist
   ASSERT_FALSE(
       TransformationLoad(100, 33, false, 0, 0,
-                         MakeInstructionDescriptor(1000, SpvOpReturn, 0))
+                         MakeInstructionDescriptor(1000, spv::Op::OpReturn, 0))
           .IsApplicable(context.get(), transformation_context));
 
   {
     TransformationLoad transformation(
         100, 33, false, 0, 0,
-        MakeInstructionDescriptor(38, SpvOpAccessChain, 0));
+        MakeInstructionDescriptor(38, spv::Op::OpAccessChain, 0));
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
@@ -203,7 +204,7 @@
   {
     TransformationLoad transformation(
         101, 46, false, 0, 0,
-        MakeInstructionDescriptor(16, SpvOpReturnValue, 0));
+        MakeInstructionDescriptor(16, spv::Op::OpReturnValue, 0));
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
@@ -215,7 +216,7 @@
   {
     TransformationLoad transformation(
         102, 16, false, 0, 0,
-        MakeInstructionDescriptor(16, SpvOpReturnValue, 0));
+        MakeInstructionDescriptor(16, spv::Op::OpReturnValue, 0));
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
@@ -227,7 +228,7 @@
   {
     TransformationLoad transformation(
         103, 40, false, 0, 0,
-        MakeInstructionDescriptor(43, SpvOpAccessChain, 0));
+        MakeInstructionDescriptor(43, spv::Op::OpAccessChain, 0));
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
@@ -344,80 +345,80 @@
       MakeUnique<FactManager>(context.get()), validator_options);
 
   // Bad: id is not fresh.
-  ASSERT_FALSE(
-      TransformationLoad(14, 14, true, 15, 20,
-                         MakeInstructionDescriptor(24, SpvOpAccessChain, 0))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationLoad(
+                   14, 14, true, 15, 20,
+                   MakeInstructionDescriptor(24, spv::Op::OpAccessChain, 0))
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: id 100 of memory scope instruction does not exist.
-  ASSERT_FALSE(
-      TransformationLoad(21, 14, true, 100, 20,
-                         MakeInstructionDescriptor(24, SpvOpAccessChain, 0))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationLoad(
+                   21, 14, true, 100, 20,
+                   MakeInstructionDescriptor(24, spv::Op::OpAccessChain, 0))
+                   .IsApplicable(context.get(), transformation_context));
   // Bad: id 100 of memory semantics instruction does not exist.
-  ASSERT_FALSE(
-      TransformationLoad(21, 14, true, 15, 100,
-                         MakeInstructionDescriptor(24, SpvOpAccessChain, 0))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationLoad(
+                   21, 14, true, 15, 100,
+                   MakeInstructionDescriptor(24, spv::Op::OpAccessChain, 0))
+                   .IsApplicable(context.get(), transformation_context));
   // Bad: memory scope should be |OpConstant| opcode.
-  ASSERT_FALSE(
-      TransformationLoad(21, 14, true, 5, 20,
-                         MakeInstructionDescriptor(24, SpvOpAccessChain, 0))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationLoad(
+                   21, 14, true, 5, 20,
+                   MakeInstructionDescriptor(24, spv::Op::OpAccessChain, 0))
+                   .IsApplicable(context.get(), transformation_context));
   // Bad: memory semantics should be |OpConstant| opcode.
-  ASSERT_FALSE(
-      TransformationLoad(21, 14, true, 15, 5,
-                         MakeInstructionDescriptor(24, SpvOpAccessChain, 0))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationLoad(
+                   21, 14, true, 15, 5,
+                   MakeInstructionDescriptor(24, spv::Op::OpAccessChain, 0))
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: The memory scope instruction must have an Integer operand.
-  ASSERT_FALSE(
-      TransformationLoad(21, 14, true, 15, 19,
-                         MakeInstructionDescriptor(24, SpvOpAccessChain, 0))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationLoad(
+                   21, 14, true, 15, 19,
+                   MakeInstructionDescriptor(24, spv::Op::OpAccessChain, 0))
+                   .IsApplicable(context.get(), transformation_context));
   // Bad: The memory memory semantics instruction must have an Integer operand.
-  ASSERT_FALSE(
-      TransformationLoad(21, 14, true, 19, 20,
-                         MakeInstructionDescriptor(24, SpvOpAccessChain, 0))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationLoad(
+                   21, 14, true, 19, 20,
+                   MakeInstructionDescriptor(24, spv::Op::OpAccessChain, 0))
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: Integer size of the memory scope must be equal to 32 bits.
-  ASSERT_FALSE(
-      TransformationLoad(21, 14, true, 17, 20,
-                         MakeInstructionDescriptor(24, SpvOpAccessChain, 0))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationLoad(
+                   21, 14, true, 17, 20,
+                   MakeInstructionDescriptor(24, spv::Op::OpAccessChain, 0))
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: Integer size of memory semantics must be equal to 32 bits.
-  ASSERT_FALSE(
-      TransformationLoad(21, 14, true, 15, 17,
-                         MakeInstructionDescriptor(24, SpvOpAccessChain, 0))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationLoad(
+                   21, 14, true, 15, 17,
+                   MakeInstructionDescriptor(24, spv::Op::OpAccessChain, 0))
+                   .IsApplicable(context.get(), transformation_context));
 
-  // Bad: memory scope value must be 4 (SpvScopeInvocation).
-  ASSERT_FALSE(
-      TransformationLoad(21, 14, true, 16, 20,
-                         MakeInstructionDescriptor(24, SpvOpAccessChain, 0))
-          .IsApplicable(context.get(), transformation_context));
+  // Bad: memory scope value must be 4 (spv::Scope::Invocation).
+  ASSERT_FALSE(TransformationLoad(
+                   21, 14, true, 16, 20,
+                   MakeInstructionDescriptor(24, spv::Op::OpAccessChain, 0))
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: memory semantics value must be either:
   // 64 (SpvMemorySemanticsUniformMemoryMask)
   // 256 (SpvMemorySemanticsWorkgroupMemoryMask)
-  ASSERT_FALSE(
-      TransformationLoad(21, 14, true, 15, 16,
-                         MakeInstructionDescriptor(24, SpvOpAccessChain, 0))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationLoad(
+                   21, 14, true, 15, 16,
+                   MakeInstructionDescriptor(24, spv::Op::OpAccessChain, 0))
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: The described instruction does not exist
-  ASSERT_FALSE(
-      TransformationLoad(21, 14, false, 15, 20,
-                         MakeInstructionDescriptor(150, SpvOpAccessChain, 0))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationLoad(
+                   21, 14, false, 15, 20,
+                   MakeInstructionDescriptor(150, spv::Op::OpAccessChain, 0))
+                   .IsApplicable(context.get(), transformation_context));
 
   // Successful transformations.
   {
     TransformationLoad transformation(
         21, 14, true, 15, 20,
-        MakeInstructionDescriptor(24, SpvOpAccessChain, 0));
+        MakeInstructionDescriptor(24, spv::Op::OpAccessChain, 0));
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
@@ -518,22 +519,22 @@
       MakeUnique<FactManager>(context.get()), validator_options);
 
   // Bad: Can't insert OpAccessChain before the id 23 of memory scope.
-  ASSERT_FALSE(
-      TransformationLoad(60, 38, true, 21, 23,
-                         MakeInstructionDescriptor(23, SpvOpAccessChain, 0))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationLoad(
+                   60, 38, true, 21, 23,
+                   MakeInstructionDescriptor(23, spv::Op::OpAccessChain, 0))
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: Can't insert OpAccessChain before the id 23 of memory semantics.
-  ASSERT_FALSE(
-      TransformationLoad(60, 38, true, 21, 23,
-                         MakeInstructionDescriptor(21, SpvOpAccessChain, 0))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationLoad(
+                   60, 38, true, 21, 23,
+                   MakeInstructionDescriptor(21, spv::Op::OpAccessChain, 0))
+                   .IsApplicable(context.get(), transformation_context));
 
   // Successful transformations.
   {
     TransformationLoad transformation(
         60, 38, true, 21, 23,
-        MakeInstructionDescriptor(40, SpvOpAccessChain, 0));
+        MakeInstructionDescriptor(40, spv::Op::OpAccessChain, 0));
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
diff --git a/test/fuzz/transformation_merge_function_returns_test.cpp b/test/fuzz/transformation_merge_function_returns_test.cpp
index 400d49a..dd1869f 100644
--- a/test/fuzz/transformation_merge_function_returns_test.cpp
+++ b/test/fuzz/transformation_merge_function_returns_test.cpp
@@ -1884,7 +1884,7 @@
   // Ensure that all input operands of OpBranchConditional instructions have
   // the right operand type.
   context->module()->ForEachInst([](opt::Instruction* inst) {
-    if (inst->opcode() == SpvOpBranchConditional) {
+    if (inst->opcode() == spv::Op::OpBranchConditional) {
       ASSERT_EQ(inst->GetInOperand(0).type, SPV_OPERAND_TYPE_ID);
       ASSERT_EQ(inst->GetInOperand(1).type, SPV_OPERAND_TYPE_ID);
       ASSERT_EQ(inst->GetInOperand(2).type, SPV_OPERAND_TYPE_ID);
diff --git a/test/fuzz/transformation_move_instruction_down_test.cpp b/test/fuzz/transformation_move_instruction_down_test.cpp
index 45dde7d..5ab1619 100644
--- a/test/fuzz/transformation_move_instruction_down_test.cpp
+++ b/test/fuzz/transformation_move_instruction_down_test.cpp
@@ -73,44 +73,45 @@
       MakeUnique<FactManager>(context.get()), validator_options);
   // Instruction descriptor is invalid.
   ASSERT_FALSE(TransformationMoveInstructionDown(
-                   MakeInstructionDescriptor(30, SpvOpNop, 0))
+                   MakeInstructionDescriptor(30, spv::Op::OpNop, 0))
                    .IsApplicable(context.get(), transformation_context));
 
   // Opcode is not supported.
   ASSERT_FALSE(TransformationMoveInstructionDown(
-                   MakeInstructionDescriptor(5, SpvOpLabel, 0))
+                   MakeInstructionDescriptor(5, spv::Op::OpLabel, 0))
                    .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationMoveInstructionDown(
-                   MakeInstructionDescriptor(12, SpvOpVariable, 0))
+                   MakeInstructionDescriptor(12, spv::Op::OpVariable, 0))
                    .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationMoveInstructionDown(
-                   MakeInstructionDescriptor(42, SpvOpFunctionCall, 0))
+                   MakeInstructionDescriptor(42, spv::Op::OpFunctionCall, 0))
                    .IsApplicable(context.get(), transformation_context));
 
   // Can't move the last instruction in the block.
-  ASSERT_FALSE(TransformationMoveInstructionDown(
-                   MakeInstructionDescriptor(15, SpvOpBranchConditional, 0))
-                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationMoveInstructionDown(
+          MakeInstructionDescriptor(15, spv::Op::OpBranchConditional, 0))
+          .IsApplicable(context.get(), transformation_context));
 
   // Can't move the instruction if the next instruction is the last one in the
   // block.
   ASSERT_FALSE(TransformationMoveInstructionDown(
-                   MakeInstructionDescriptor(21, SpvOpIAdd, 0))
+                   MakeInstructionDescriptor(21, spv::Op::OpIAdd, 0))
                    .IsApplicable(context.get(), transformation_context));
 
   // Can't insert instruction's opcode after its successor.
   ASSERT_FALSE(TransformationMoveInstructionDown(
-                   MakeInstructionDescriptor(15, SpvOpIMul, 0))
+                   MakeInstructionDescriptor(15, spv::Op::OpIMul, 0))
                    .IsApplicable(context.get(), transformation_context));
 
   // Instruction's successor depends on the instruction.
   ASSERT_FALSE(TransformationMoveInstructionDown(
-                   MakeInstructionDescriptor(10, SpvOpIAdd, 0))
+                   MakeInstructionDescriptor(10, spv::Op::OpIAdd, 0))
                    .IsApplicable(context.get(), transformation_context));
 
   {
     TransformationMoveInstructionDown transformation(
-        MakeInstructionDescriptor(11, SpvOpISub, 0));
+        MakeInstructionDescriptor(11, spv::Op::OpISub, 0));
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
@@ -120,7 +121,7 @@
   }
   {
     TransformationMoveInstructionDown transformation(
-        MakeInstructionDescriptor(22, SpvOpIAdd, 0));
+        MakeInstructionDescriptor(22, spv::Op::OpIAdd, 0));
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
@@ -218,17 +219,17 @@
       MakeUnique<FactManager>(context.get()), validator_options);
   // Swap memory instruction with an unsupported one.
   ASSERT_FALSE(TransformationMoveInstructionDown(
-                   MakeInstructionDescriptor(22, SpvOpLoad, 0))
+                   MakeInstructionDescriptor(22, spv::Op::OpLoad, 0))
                    .IsApplicable(context.get(), transformation_context));
 
   // Swap memory barrier with an unsupported one.
   ASSERT_FALSE(TransformationMoveInstructionDown(
-                   MakeInstructionDescriptor(23, SpvOpMemoryBarrier, 0))
+                   MakeInstructionDescriptor(23, spv::Op::OpMemoryBarrier, 0))
                    .IsApplicable(context.get(), transformation_context));
 
   // Swap simple instruction with an unsupported one.
   TransformationMoveInstructionDown transformation(
-      MakeInstructionDescriptor(8, SpvOpCopyObject, 0));
+      MakeInstructionDescriptor(8, spv::Op::OpCopyObject, 0));
   ASSERT_TRUE(
       transformation.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
@@ -322,21 +323,21 @@
       MakeUnique<FactManager>(context.get()), validator_options);
   // Swap two barrier instructions.
   ASSERT_FALSE(TransformationMoveInstructionDown(
-                   MakeInstructionDescriptor(21, SpvOpMemoryBarrier, 0))
+                   MakeInstructionDescriptor(21, spv::Op::OpMemoryBarrier, 0))
                    .IsApplicable(context.get(), transformation_context));
 
   // Swap barrier and memory instructions.
   ASSERT_FALSE(TransformationMoveInstructionDown(
-                   MakeInstructionDescriptor(21, SpvOpMemoryBarrier, 2))
+                   MakeInstructionDescriptor(21, spv::Op::OpMemoryBarrier, 2))
                    .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationMoveInstructionDown(
-                   MakeInstructionDescriptor(22, SpvOpLoad, 0))
+                   MakeInstructionDescriptor(22, spv::Op::OpLoad, 0))
                    .IsApplicable(context.get(), transformation_context));
 
   // Swap barrier and simple instructions.
   {
     TransformationMoveInstructionDown transformation(
-        MakeInstructionDescriptor(23, SpvOpCopyObject, 0));
+        MakeInstructionDescriptor(23, spv::Op::OpCopyObject, 0));
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
@@ -346,7 +347,7 @@
   }
   {
     TransformationMoveInstructionDown transformation(
-        MakeInstructionDescriptor(22, SpvOpMemoryBarrier, 1));
+        MakeInstructionDescriptor(22, spv::Op::OpMemoryBarrier, 1));
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
@@ -406,7 +407,7 @@
   // Swap simple and barrier instructions.
   {
     TransformationMoveInstructionDown transformation(
-        MakeInstructionDescriptor(40, SpvOpCopyObject, 0));
+        MakeInstructionDescriptor(40, spv::Op::OpCopyObject, 0));
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
@@ -416,7 +417,7 @@
   }
   {
     TransformationMoveInstructionDown transformation(
-        MakeInstructionDescriptor(21, SpvOpMemoryBarrier, 0));
+        MakeInstructionDescriptor(21, spv::Op::OpMemoryBarrier, 0));
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
@@ -428,7 +429,7 @@
   // Swap simple and memory instructions.
   {
     TransformationMoveInstructionDown transformation(
-        MakeInstructionDescriptor(41, SpvOpCopyObject, 0));
+        MakeInstructionDescriptor(41, spv::Op::OpCopyObject, 0));
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
@@ -438,7 +439,7 @@
   }
   {
     TransformationMoveInstructionDown transformation(
-        MakeInstructionDescriptor(22, SpvOpLoad, 0));
+        MakeInstructionDescriptor(22, spv::Op::OpLoad, 0));
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
@@ -450,7 +451,7 @@
   // Swap two simple instructions.
   {
     TransformationMoveInstructionDown transformation(
-        MakeInstructionDescriptor(23, SpvOpCopyObject, 0));
+        MakeInstructionDescriptor(23, spv::Op::OpCopyObject, 0));
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
@@ -625,22 +626,22 @@
 
   protobufs::InstructionDescriptor invalid_swaps[] = {
       // R and RW
-      MakeInstructionDescriptor(25, SpvOpLoad, 0),
+      MakeInstructionDescriptor(25, spv::Op::OpLoad, 0),
 
       // R and W
-      MakeInstructionDescriptor(29, SpvOpLoad, 0),
+      MakeInstructionDescriptor(29, spv::Op::OpLoad, 0),
 
       // RW and RW
-      MakeInstructionDescriptor(32, SpvOpCopyMemory, 0),
-      MakeInstructionDescriptor(32, SpvOpCopyMemory, 2),
-      MakeInstructionDescriptor(32, SpvOpCopyMemory, 4),
+      MakeInstructionDescriptor(32, spv::Op::OpCopyMemory, 0),
+      MakeInstructionDescriptor(32, spv::Op::OpCopyMemory, 2),
+      MakeInstructionDescriptor(32, spv::Op::OpCopyMemory, 4),
 
       // RW and W
-      MakeInstructionDescriptor(32, SpvOpCopyMemory, 12),
-      MakeInstructionDescriptor(32, SpvOpStore, 1),
+      MakeInstructionDescriptor(32, spv::Op::OpCopyMemory, 12),
+      MakeInstructionDescriptor(32, spv::Op::OpStore, 1),
 
       // W and W
-      MakeInstructionDescriptor(32, SpvOpStore, 6),
+      MakeInstructionDescriptor(32, spv::Op::OpStore, 6),
   };
 
   for (const auto& descriptor : invalid_swaps) {
@@ -651,55 +652,55 @@
   // Valid swaps.
   protobufs::InstructionDescriptor valid_swaps[] = {
       // R and R
-      MakeInstructionDescriptor(23, SpvOpLoad, 0),
-      MakeInstructionDescriptor(24, SpvOpLoad, 0),
+      MakeInstructionDescriptor(23, spv::Op::OpLoad, 0),
+      MakeInstructionDescriptor(24, spv::Op::OpLoad, 0),
 
       // R and RW
-      MakeInstructionDescriptor(26, SpvOpLoad, 0),
-      MakeInstructionDescriptor(25, SpvOpCopyMemory, 1),
+      MakeInstructionDescriptor(26, spv::Op::OpLoad, 0),
+      MakeInstructionDescriptor(25, spv::Op::OpCopyMemory, 1),
 
-      MakeInstructionDescriptor(27, SpvOpLoad, 0),
-      MakeInstructionDescriptor(26, SpvOpCopyMemory, 1),
+      MakeInstructionDescriptor(27, spv::Op::OpLoad, 0),
+      MakeInstructionDescriptor(26, spv::Op::OpCopyMemory, 1),
 
-      MakeInstructionDescriptor(28, SpvOpLoad, 0),
-      MakeInstructionDescriptor(27, SpvOpCopyMemory, 1),
+      MakeInstructionDescriptor(28, spv::Op::OpLoad, 0),
+      MakeInstructionDescriptor(27, spv::Op::OpCopyMemory, 1),
 
       // R and W
-      MakeInstructionDescriptor(30, SpvOpLoad, 0),
-      MakeInstructionDescriptor(29, SpvOpStore, 1),
+      MakeInstructionDescriptor(30, spv::Op::OpLoad, 0),
+      MakeInstructionDescriptor(29, spv::Op::OpStore, 1),
 
-      MakeInstructionDescriptor(31, SpvOpLoad, 0),
-      MakeInstructionDescriptor(30, SpvOpStore, 1),
+      MakeInstructionDescriptor(31, spv::Op::OpLoad, 0),
+      MakeInstructionDescriptor(30, spv::Op::OpStore, 1),
 
-      MakeInstructionDescriptor(32, SpvOpLoad, 0),
-      MakeInstructionDescriptor(31, SpvOpStore, 1),
+      MakeInstructionDescriptor(32, spv::Op::OpLoad, 0),
+      MakeInstructionDescriptor(31, spv::Op::OpStore, 1),
 
       // RW and RW
-      MakeInstructionDescriptor(32, SpvOpCopyMemory, 6),
-      MakeInstructionDescriptor(32, SpvOpCopyMemory, 6),
+      MakeInstructionDescriptor(32, spv::Op::OpCopyMemory, 6),
+      MakeInstructionDescriptor(32, spv::Op::OpCopyMemory, 6),
 
-      MakeInstructionDescriptor(32, SpvOpCopyMemory, 8),
-      MakeInstructionDescriptor(32, SpvOpCopyMemory, 8),
+      MakeInstructionDescriptor(32, spv::Op::OpCopyMemory, 8),
+      MakeInstructionDescriptor(32, spv::Op::OpCopyMemory, 8),
 
-      MakeInstructionDescriptor(32, SpvOpCopyMemory, 10),
-      MakeInstructionDescriptor(32, SpvOpCopyMemory, 10),
+      MakeInstructionDescriptor(32, spv::Op::OpCopyMemory, 10),
+      MakeInstructionDescriptor(32, spv::Op::OpCopyMemory, 10),
 
       // RW and W
-      MakeInstructionDescriptor(32, SpvOpCopyMemory, 14),
-      MakeInstructionDescriptor(32, SpvOpStore, 3),
+      MakeInstructionDescriptor(32, spv::Op::OpCopyMemory, 14),
+      MakeInstructionDescriptor(32, spv::Op::OpStore, 3),
 
-      MakeInstructionDescriptor(32, SpvOpCopyMemory, 15),
-      MakeInstructionDescriptor(32, SpvOpStore, 4),
+      MakeInstructionDescriptor(32, spv::Op::OpCopyMemory, 15),
+      MakeInstructionDescriptor(32, spv::Op::OpStore, 4),
 
-      MakeInstructionDescriptor(32, SpvOpCopyMemory, 16),
-      MakeInstructionDescriptor(32, SpvOpStore, 5),
+      MakeInstructionDescriptor(32, spv::Op::OpCopyMemory, 16),
+      MakeInstructionDescriptor(32, spv::Op::OpStore, 5),
 
       // W and W
-      MakeInstructionDescriptor(32, SpvOpStore, 8),
-      MakeInstructionDescriptor(32, SpvOpStore, 8),
+      MakeInstructionDescriptor(32, spv::Op::OpStore, 8),
+      MakeInstructionDescriptor(32, spv::Op::OpStore, 8),
 
-      MakeInstructionDescriptor(32, SpvOpStore, 10),
-      MakeInstructionDescriptor(32, SpvOpStore, 10),
+      MakeInstructionDescriptor(32, spv::Op::OpStore, 10),
+      MakeInstructionDescriptor(32, spv::Op::OpStore, 10),
   };
 
   for (const auto& descriptor : valid_swaps) {
diff --git a/test/fuzz/transformation_mutate_pointer_test.cpp b/test/fuzz/transformation_mutate_pointer_test.cpp
index e869efa..e0cb615 100644
--- a/test/fuzz/transformation_mutate_pointer_test.cpp
+++ b/test/fuzz/transformation_mutate_pointer_test.cpp
@@ -86,7 +86,8 @@
   transformation_context.GetFactManager()->AddFactIdIsIrrelevant(35);
   transformation_context.GetFactManager()->AddFactIdIsIrrelevant(39);
 
-  const auto insert_before = MakeInstructionDescriptor(26, SpvOpReturn, 0);
+  const auto insert_before =
+      MakeInstructionDescriptor(26, spv::Op::OpReturn, 0);
 
   // 20 is not a fresh id.
   ASSERT_FALSE(TransformationMutatePointer(20, 20, insert_before)
@@ -94,13 +95,14 @@
 
   // |insert_before| instruction descriptor is invalid.
   ASSERT_FALSE(TransformationMutatePointer(
-                   20, 70, MakeInstructionDescriptor(26, SpvOpStore, 0))
+                   20, 70, MakeInstructionDescriptor(26, spv::Op::OpStore, 0))
                    .IsApplicable(context.get(), transformation_context));
 
   // Can't insert OpLoad before OpVariable.
-  ASSERT_FALSE(TransformationMutatePointer(
-                   20, 70, MakeInstructionDescriptor(26, SpvOpVariable, 0))
-                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationMutatePointer(
+          20, 70, MakeInstructionDescriptor(26, spv::Op::OpVariable, 0))
+          .IsApplicable(context.get(), transformation_context));
 
   // |pointer_id| doesn't exist in the module.
   ASSERT_FALSE(TransformationMutatePointer(70, 70, insert_before)
@@ -131,9 +133,10 @@
                    .IsApplicable(context.get(), transformation_context));
 
   // |pointer_id| is not available before |insert_before|.
-  ASSERT_FALSE(TransformationMutatePointer(
-                   26, 70, MakeInstructionDescriptor(26, SpvOpAccessChain, 0))
-                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationMutatePointer(
+          26, 70, MakeInstructionDescriptor(26, spv::Op::OpAccessChain, 0))
+          .IsApplicable(context.get(), transformation_context));
 
   transformation_context.GetFactManager()->AddFactIdIsIrrelevant(40);
 
@@ -274,7 +277,8 @@
   ASSERT_FALSE(
       context->GetDominatorAnalysis(context->GetFunction(4))->IsReachable(10));
 
-  const auto insert_before = MakeInstructionDescriptor(10, SpvOpReturn, 0);
+  const auto insert_before =
+      MakeInstructionDescriptor(10, spv::Op::OpReturn, 0);
 
   // Can mutate a global variable in an unreachable block.
   TransformationMutatePointer transformation(12, 50, insert_before);
diff --git a/test/fuzz/transformation_permute_phi_operands_test.cpp b/test/fuzz/transformation_permute_phi_operands_test.cpp
index 0774dcf..320dd1d 100644
--- a/test/fuzz/transformation_permute_phi_operands_test.cpp
+++ b/test/fuzz/transformation_permute_phi_operands_test.cpp
@@ -128,11 +128,11 @@
   context->get_def_use_mgr()->ForEachUse(
       25, [&found_use_in_store, &found_use_in_add_lhs, &found_use_in_add_rhs](
               opt::Instruction* inst, uint32_t operand_index) {
-        if (inst->opcode() == SpvOpStore) {
+        if (inst->opcode() == spv::Op::OpStore) {
           ASSERT_FALSE(found_use_in_store);
           found_use_in_store = true;
         } else {
-          ASSERT_EQ(SpvOpIAdd, inst->opcode());
+          ASSERT_EQ(spv::Op::OpIAdd, inst->opcode());
           if (operand_index == 2) {
             ASSERT_FALSE(found_use_in_add_lhs);
             found_use_in_add_lhs = true;
diff --git a/test/fuzz/transformation_propagate_instruction_down_test.cpp b/test/fuzz/transformation_propagate_instruction_down_test.cpp
index 52974ca..8bedef2 100644
--- a/test/fuzz/transformation_propagate_instruction_down_test.cpp
+++ b/test/fuzz/transformation_propagate_instruction_down_test.cpp
@@ -499,13 +499,13 @@
     TransformationPropagateInstructionDown transformation(
         5, 200, {{{15, 203}, {16, 204}}});
     ASSERT_FALSE(context->get_feature_mgr()->HasCapability(
-        SpvCapabilityVariablePointersStorageBuffer));
+        spv::Capability::VariablePointersStorageBuffer));
     ASSERT_FALSE(
         transformation.IsApplicable(context.get(), transformation_context));
 
-    context->AddCapability(SpvCapabilityVariablePointers);
+    context->AddCapability(spv::Capability::VariablePointers);
     ASSERT_TRUE(context->get_feature_mgr()->HasCapability(
-        SpvCapabilityVariablePointersStorageBuffer));
+        spv::Capability::VariablePointersStorageBuffer));
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
diff --git a/test/fuzz/transformation_propagate_instruction_up_test.cpp b/test/fuzz/transformation_propagate_instruction_up_test.cpp
index 8a04270..20cf4b3 100644
--- a/test/fuzz/transformation_propagate_instruction_up_test.cpp
+++ b/test/fuzz/transformation_propagate_instruction_up_test.cpp
@@ -699,7 +699,7 @@
   ASSERT_FALSE(
       transformation.IsApplicable(context.get(), transformation_context));
 
-  context->AddCapability(SpvCapabilityVariablePointers);
+  context->AddCapability(spv::Capability::VariablePointers);
 
   ASSERT_TRUE(
       transformation.IsApplicable(context.get(), transformation_context));
@@ -779,7 +779,7 @@
   ASSERT_FALSE(
       transformation.IsApplicable(context.get(), transformation_context));
 
-  context->AddCapability(SpvCapabilityVariablePointersStorageBuffer);
+  context->AddCapability(spv::Capability::VariablePointersStorageBuffer);
 
   ASSERT_TRUE(
       transformation.IsApplicable(context.get(), 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 b0fff58..0d4850d 100644
--- a/test/fuzz/transformation_push_id_through_variable_test.cpp
+++ b/test/fuzz/transformation_push_id_through_variable_test.cpp
@@ -110,9 +110,9 @@
   uint32_t value_synonym_id = 62;
   uint32_t variable_id = 63;
   uint32_t initializer_id = 23;
-  uint32_t variable_storage_class = SpvStorageClassPrivate;
+  uint32_t variable_storage_class = (uint32_t)spv::StorageClass::Private;
   auto instruction_descriptor =
-      MakeInstructionDescriptor(95, SpvOpReturnValue, 0);
+      MakeInstructionDescriptor(95, spv::Op::OpReturnValue, 0);
   auto transformation = TransformationPushIdThroughVariable(
       value_id, value_synonym_id, variable_id, variable_storage_class,
       initializer_id, instruction_descriptor);
@@ -124,8 +124,9 @@
   value_synonym_id = 60;
   variable_id = 61;
   initializer_id = 80;
-  variable_storage_class = SpvStorageClassFunction;
-  instruction_descriptor = MakeInstructionDescriptor(38, SpvOpAccessChain, 0);
+  variable_storage_class = (uint32_t)spv::StorageClass::Function;
+  instruction_descriptor =
+      MakeInstructionDescriptor(38, spv::Op::OpAccessChain, 0);
   transformation = TransformationPushIdThroughVariable(
       value_id, value_synonym_id, variable_id, variable_storage_class,
       initializer_id, instruction_descriptor);
@@ -137,8 +138,9 @@
   value_synonym_id = 62;
   variable_id = 63;
   initializer_id = 80;
-  variable_storage_class = SpvStorageClassFunction;
-  instruction_descriptor = MakeInstructionDescriptor(64, SpvOpAccessChain, 0);
+  variable_storage_class = (uint32_t)spv::StorageClass::Function;
+  instruction_descriptor =
+      MakeInstructionDescriptor(64, spv::Op::OpAccessChain, 0);
   transformation = TransformationPushIdThroughVariable(
       value_id, value_synonym_id, variable_id, variable_storage_class,
       initializer_id, instruction_descriptor);
@@ -151,8 +153,9 @@
   value_synonym_id = 62;
   variable_id = 63;
   initializer_id = 24;
-  variable_storage_class = SpvStorageClassFunction;
-  instruction_descriptor = MakeInstructionDescriptor(27, SpvOpVariable, 0);
+  variable_storage_class = (uint32_t)spv::StorageClass::Function;
+  instruction_descriptor =
+      MakeInstructionDescriptor(27, spv::Op::OpVariable, 0);
   transformation = TransformationPushIdThroughVariable(
       value_id, value_synonym_id, variable_id, variable_storage_class,
       initializer_id, instruction_descriptor);
@@ -164,8 +167,9 @@
   value_synonym_id = 62;
   variable_id = 63;
   initializer_id = 80;
-  variable_storage_class = SpvStorageClassFunction;
-  instruction_descriptor = MakeInstructionDescriptor(100, SpvOpUnreachable, 0);
+  variable_storage_class = (uint32_t)spv::StorageClass::Function;
+  instruction_descriptor =
+      MakeInstructionDescriptor(100, spv::Op::OpUnreachable, 0);
   transformation = TransformationPushIdThroughVariable(
       value_id, value_synonym_id, variable_id, variable_storage_class,
       initializer_id, instruction_descriptor);
@@ -177,8 +181,9 @@
   value_synonym_id = 62;
   variable_id = 63;
   initializer_id = 23;
-  variable_storage_class = SpvStorageClassFunction;
-  instruction_descriptor = MakeInstructionDescriptor(95, SpvOpReturnValue, 0);
+  variable_storage_class = (uint32_t)spv::StorageClass::Function;
+  instruction_descriptor =
+      MakeInstructionDescriptor(95, spv::Op::OpReturnValue, 0);
   transformation = TransformationPushIdThroughVariable(
       value_id, value_synonym_id, variable_id, variable_storage_class,
       initializer_id, instruction_descriptor);
@@ -190,8 +195,9 @@
   value_synonym_id = 62;
   variable_id = 63;
   initializer_id = 80;
-  variable_storage_class = SpvStorageClassPrivate;
-  instruction_descriptor = MakeInstructionDescriptor(95, SpvOpReturnValue, 0);
+  variable_storage_class = (uint32_t)spv::StorageClass::Private;
+  instruction_descriptor =
+      MakeInstructionDescriptor(95, spv::Op::OpReturnValue, 0);
   transformation = TransformationPushIdThroughVariable(
       value_id, value_synonym_id, variable_id, variable_storage_class,
       initializer_id, instruction_descriptor);
@@ -203,8 +209,9 @@
   value_synonym_id = 62;
   variable_id = 63;
   initializer_id = 93;
-  variable_storage_class = SpvStorageClassInput;
-  instruction_descriptor = MakeInstructionDescriptor(95, SpvOpReturnValue, 0);
+  variable_storage_class = (uint32_t)spv::StorageClass::Input;
+  instruction_descriptor =
+      MakeInstructionDescriptor(95, spv::Op::OpReturnValue, 0);
   transformation = TransformationPushIdThroughVariable(
       value_id, value_synonym_id, variable_id, variable_storage_class,
       initializer_id, instruction_descriptor);
@@ -219,8 +226,9 @@
   value_synonym_id = 62;
   variable_id = 63;
   initializer_id = 80;
-  variable_storage_class = SpvStorageClassFunction;
-  instruction_descriptor = MakeInstructionDescriptor(40, SpvOpAccessChain, 0);
+  variable_storage_class = (uint32_t)spv::StorageClass::Function;
+  instruction_descriptor =
+      MakeInstructionDescriptor(40, spv::Op::OpAccessChain, 0);
   transformation = TransformationPushIdThroughVariable(
       value_id, value_synonym_id, variable_id, variable_storage_class,
       initializer_id, instruction_descriptor);
@@ -232,8 +240,9 @@
   value_synonym_id = 62;
   variable_id = 63;
   initializer_id = 95;
-  variable_storage_class = SpvStorageClassFunction;
-  instruction_descriptor = MakeInstructionDescriptor(40, SpvOpAccessChain, 0);
+  variable_storage_class = (uint32_t)spv::StorageClass::Function;
+  instruction_descriptor =
+      MakeInstructionDescriptor(40, spv::Op::OpAccessChain, 0);
   transformation = TransformationPushIdThroughVariable(
       value_id, value_synonym_id, variable_id, variable_storage_class,
       initializer_id, instruction_descriptor);
@@ -245,8 +254,9 @@
   value_synonym_id = 62;
   variable_id = 63;
   initializer_id = 93;
-  variable_storage_class = SpvStorageClassFunction;
-  instruction_descriptor = MakeInstructionDescriptor(40, SpvOpAccessChain, 0);
+  variable_storage_class = (uint32_t)spv::StorageClass::Function;
+  instruction_descriptor =
+      MakeInstructionDescriptor(40, spv::Op::OpAccessChain, 0);
   transformation = TransformationPushIdThroughVariable(
       value_id, value_synonym_id, variable_id, variable_storage_class,
       initializer_id, instruction_descriptor);
@@ -335,9 +345,9 @@
   uint32_t value_synonym_id = 100;
   uint32_t variable_id = 101;
   uint32_t initializer_id = 80;
-  uint32_t variable_storage_class = SpvStorageClassFunction;
+  uint32_t variable_storage_class = (uint32_t)spv::StorageClass::Function;
   auto instruction_descriptor =
-      MakeInstructionDescriptor(38, SpvOpAccessChain, 0);
+      MakeInstructionDescriptor(38, spv::Op::OpAccessChain, 0);
   auto transformation = TransformationPushIdThroughVariable(
       value_id, value_synonym_id, variable_id, variable_storage_class,
       initializer_id, instruction_descriptor);
@@ -346,18 +356,18 @@
   ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(variable_id));
   ASSERT_EQ(nullptr, context->get_instr_block(variable_id));
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
-  ASSERT_EQ(SpvOpLoad,
+  ASSERT_EQ(spv::Op::OpLoad,
             context->get_def_use_mgr()->GetDef(value_synonym_id)->opcode());
   ASSERT_EQ(36, context->get_instr_block(value_synonym_id)->id());
-  ASSERT_EQ(SpvOpVariable,
+  ASSERT_EQ(spv::Op::OpVariable,
             context->get_def_use_mgr()->GetDef(variable_id)->opcode());
   ASSERT_EQ(5, context->get_instr_block(variable_id)->id());
   uint32_t variable_use_count = 0;
   context->get_def_use_mgr()->ForEachUse(
       variable_id,
       [&variable_use_count](opt::Instruction* inst, uint32_t /*unused*/) {
-        ASSERT_TRUE(inst->opcode() == SpvOpLoad ||
-                    inst->opcode() == SpvOpStore);
+        ASSERT_TRUE(inst->opcode() == spv::Op::OpLoad ||
+                    inst->opcode() == spv::Op::OpStore);
         variable_use_count++;
       });
   ASSERT_EQ(2, variable_use_count);
@@ -366,8 +376,9 @@
   value_synonym_id = 102;
   variable_id = 103;
   initializer_id = 21;
-  variable_storage_class = SpvStorageClassFunction;
-  instruction_descriptor = MakeInstructionDescriptor(38, SpvOpAccessChain, 0);
+  variable_storage_class = (uint32_t)spv::StorageClass::Function;
+  instruction_descriptor =
+      MakeInstructionDescriptor(38, spv::Op::OpAccessChain, 0);
   transformation = TransformationPushIdThroughVariable(
       value_id, value_synonym_id, variable_id, variable_storage_class,
       initializer_id, instruction_descriptor);
@@ -377,8 +388,9 @@
   value_synonym_id = 104;
   variable_id = 105;
   initializer_id = 80;
-  variable_storage_class = SpvStorageClassFunction;
-  instruction_descriptor = MakeInstructionDescriptor(95, SpvOpReturnValue, 0);
+  variable_storage_class = (uint32_t)spv::StorageClass::Function;
+  instruction_descriptor =
+      MakeInstructionDescriptor(95, spv::Op::OpReturnValue, 0);
   transformation = TransformationPushIdThroughVariable(
       value_id, value_synonym_id, variable_id, variable_storage_class,
       initializer_id, instruction_descriptor);
@@ -388,8 +400,9 @@
   value_synonym_id = 106;
   variable_id = 107;
   initializer_id = 80;
-  variable_storage_class = SpvStorageClassFunction;
-  instruction_descriptor = MakeInstructionDescriptor(95, SpvOpReturnValue, 0);
+  variable_storage_class = (uint32_t)spv::StorageClass::Function;
+  instruction_descriptor =
+      MakeInstructionDescriptor(95, spv::Op::OpReturnValue, 0);
   transformation = TransformationPushIdThroughVariable(
       value_id, value_synonym_id, variable_id, variable_storage_class,
       initializer_id, instruction_descriptor);
@@ -399,8 +412,9 @@
   value_synonym_id = 108;
   variable_id = 109;
   initializer_id = 21;
-  variable_storage_class = SpvStorageClassPrivate;
-  instruction_descriptor = MakeInstructionDescriptor(95, SpvOpReturnValue, 0);
+  variable_storage_class = (uint32_t)spv::StorageClass::Private;
+  instruction_descriptor =
+      MakeInstructionDescriptor(95, spv::Op::OpReturnValue, 0);
   transformation = TransformationPushIdThroughVariable(
       value_id, value_synonym_id, variable_id, variable_storage_class,
       initializer_id, instruction_descriptor);
@@ -410,8 +424,8 @@
   value_synonym_id = 110;
   variable_id = 111;
   initializer_id = 21;
-  variable_storage_class = SpvStorageClassPrivate;
-  instruction_descriptor = MakeInstructionDescriptor(27, SpvOpStore, 0);
+  variable_storage_class = (uint32_t)spv::StorageClass::Private;
+  instruction_descriptor = MakeInstructionDescriptor(27, spv::Op::OpStore, 0);
   transformation = TransformationPushIdThroughVariable(
       value_id, value_synonym_id, variable_id, variable_storage_class,
       initializer_id, instruction_descriptor);
@@ -602,9 +616,9 @@
   uint32_t value_synonym_id = 62;
   uint32_t variable_id = 63;
   uint32_t initializer_id = 23;
-  uint32_t variable_storage_class = SpvStorageClassPrivate;
+  uint32_t variable_storage_class = (uint32_t)spv::StorageClass::Private;
   auto instruction_descriptor =
-      MakeInstructionDescriptor(95, SpvOpReturnValue, 0);
+      MakeInstructionDescriptor(95, spv::Op::OpReturnValue, 0);
   auto transformation = TransformationPushIdThroughVariable(
       value_id, value_synonym_id, variable_id, variable_storage_class,
       initializer_id, instruction_descriptor);
@@ -705,9 +719,9 @@
   uint32_t value_synonym_id = 62;
   uint32_t variable_id = 63;
   uint32_t initializer_id = 23;
-  uint32_t variable_storage_class = SpvStorageClassPrivate;
+  uint32_t variable_storage_class = (uint32_t)spv::StorageClass::Private;
   auto instruction_descriptor =
-      MakeInstructionDescriptor(95, SpvOpReturnValue, 0);
+      MakeInstructionDescriptor(95, spv::Op::OpReturnValue, 0);
   auto transformation = TransformationPushIdThroughVariable(
       value_id, value_synonym_id, variable_id, variable_storage_class,
       initializer_id, instruction_descriptor);
@@ -766,8 +780,8 @@
 
   transformation_context.GetFactManager()->AddFactBlockIsDead(15);
   auto transformation = TransformationPushIdThroughVariable(
-      14, 100, 101, SpvStorageClassFunction, 14,
-      MakeInstructionDescriptor(15, SpvOpBranch, 0));
+      14, 100, 101, uint32_t(spv::StorageClass::Function), 14,
+      MakeInstructionDescriptor(15, spv::Op::OpBranch, 0));
   ASSERT_TRUE(
       transformation.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
diff --git a/test/fuzz/transformation_replace_boolean_constant_with_constant_binary_test.cpp b/test/fuzz/transformation_replace_boolean_constant_with_constant_binary_test.cpp
index b8c2a8a..65cf3d4 100644
--- a/test/fuzz/transformation_replace_boolean_constant_with_constant_binary_test.cpp
+++ b/test/fuzz/transformation_replace_boolean_constant_with_constant_binary_test.cpp
@@ -167,34 +167,38 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
   std::vector<protobufs::IdUseDescriptor> uses_of_true = {
-      MakeIdUseDescriptor(41, MakeInstructionDescriptor(44, SpvOpStore, 12), 1),
-      MakeIdUseDescriptor(41, MakeInstructionDescriptor(46, SpvOpLogicalOr, 0),
-                          0)};
+      MakeIdUseDescriptor(
+          41, MakeInstructionDescriptor(44, spv::Op::OpStore, 12), 1),
+      MakeIdUseDescriptor(
+          41, MakeInstructionDescriptor(46, spv::Op::OpLogicalOr, 0), 0)};
 
   std::vector<protobufs::IdUseDescriptor> uses_of_false = {
-      MakeIdUseDescriptor(43, MakeInstructionDescriptor(44, SpvOpStore, 13), 1),
-      MakeIdUseDescriptor(43, MakeInstructionDescriptor(48, SpvOpLogicalAnd, 0),
-                          1)};
+      MakeIdUseDescriptor(
+          43, MakeInstructionDescriptor(44, spv::Op::OpStore, 13), 1),
+      MakeIdUseDescriptor(
+          43, MakeInstructionDescriptor(48, spv::Op::OpLogicalAnd, 0), 1)};
 
   const uint32_t fresh_id = 100;
 
-  std::vector<SpvOp> fp_gt_opcodes = {
-      SpvOpFOrdGreaterThan, SpvOpFOrdGreaterThanEqual, SpvOpFUnordGreaterThan,
-      SpvOpFUnordGreaterThanEqual};
+  std::vector<spv::Op> fp_gt_opcodes = {
+      spv::Op::OpFOrdGreaterThan, spv::Op::OpFOrdGreaterThanEqual,
+      spv::Op::OpFUnordGreaterThan, spv::Op::OpFUnordGreaterThanEqual};
 
-  std::vector<SpvOp> fp_lt_opcodes = {SpvOpFOrdLessThan, SpvOpFOrdLessThanEqual,
-                                      SpvOpFUnordLessThan,
-                                      SpvOpFUnordLessThanEqual};
+  std::vector<spv::Op> fp_lt_opcodes = {
+      spv::Op::OpFOrdLessThan, spv::Op::OpFOrdLessThanEqual,
+      spv::Op::OpFUnordLessThan, spv::Op::OpFUnordLessThanEqual};
 
-  std::vector<SpvOp> int_gt_opcodes = {SpvOpSGreaterThan,
-                                       SpvOpSGreaterThanEqual};
+  std::vector<spv::Op> int_gt_opcodes = {spv::Op::OpSGreaterThan,
+                                         spv::Op::OpSGreaterThanEqual};
 
-  std::vector<SpvOp> int_lt_opcodes = {SpvOpSLessThan, SpvOpSLessThanEqual};
+  std::vector<spv::Op> int_lt_opcodes = {spv::Op::OpSLessThan,
+                                         spv::Op::OpSLessThanEqual};
 
-  std::vector<SpvOp> uint_gt_opcodes = {SpvOpUGreaterThan,
-                                        SpvOpUGreaterThanEqual};
+  std::vector<spv::Op> uint_gt_opcodes = {spv::Op::OpUGreaterThan,
+                                          spv::Op::OpUGreaterThanEqual};
 
-  std::vector<SpvOp> uint_lt_opcodes = {SpvOpULessThan, SpvOpULessThanEqual};
+  std::vector<spv::Op> uint_lt_opcodes = {spv::Op::OpULessThan,
+                                          spv::Op::OpULessThanEqual};
 
 #define CHECK_OPERATOR(USE_DESCRIPTOR, LHS_ID, RHS_ID, OPCODE, FRESH_ID) \
   ASSERT_TRUE(TransformationReplaceBooleanConstantWithConstantBinary(    \
@@ -253,41 +257,41 @@
 
   // Target id is not fresh
   ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary(
-                   uses_of_true[0], 15, 17, SpvOpFOrdLessThan, 15)
+                   uses_of_true[0], 15, 17, spv::Op::OpFOrdLessThan, 15)
                    .IsApplicable(context.get(), transformation_context));
 
   // LHS id does not exist
   ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary(
-                   uses_of_true[0], 300, 17, SpvOpFOrdLessThan, 200)
+                   uses_of_true[0], 300, 17, spv::Op::OpFOrdLessThan, 200)
                    .IsApplicable(context.get(), transformation_context));
 
   // RHS id does not exist
   ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary(
-                   uses_of_true[0], 15, 300, SpvOpFOrdLessThan, 200)
+                   uses_of_true[0], 15, 300, spv::Op::OpFOrdLessThan, 200)
                    .IsApplicable(context.get(), transformation_context));
 
   // LHS and RHS ids do not match type
   ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary(
-                   uses_of_true[0], 11, 17, SpvOpFOrdLessThan, 200)
+                   uses_of_true[0], 11, 17, spv::Op::OpFOrdLessThan, 200)
                    .IsApplicable(context.get(), transformation_context));
 
   // Opcode not appropriate
   ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary(
-                   uses_of_true[0], 15, 17, SpvOpFDiv, 200)
+                   uses_of_true[0], 15, 17, spv::Op::OpFDiv, 200)
                    .IsApplicable(context.get(), transformation_context));
 
   auto replace_true_with_double_comparison =
       TransformationReplaceBooleanConstantWithConstantBinary(
-          uses_of_true[0], 11, 9, SpvOpFUnordGreaterThan, 100);
+          uses_of_true[0], 11, 9, spv::Op::OpFUnordGreaterThan, 100);
   auto replace_true_with_uint32_comparison =
       TransformationReplaceBooleanConstantWithConstantBinary(
-          uses_of_true[1], 27, 29, SpvOpULessThanEqual, 101);
+          uses_of_true[1], 27, 29, spv::Op::OpULessThanEqual, 101);
   auto replace_false_with_float_comparison =
       TransformationReplaceBooleanConstantWithConstantBinary(
-          uses_of_false[0], 17, 15, SpvOpFOrdLessThan, 102);
+          uses_of_false[0], 17, 15, spv::Op::OpFOrdLessThan, 102);
   auto replace_false_with_sint64_comparison =
       TransformationReplaceBooleanConstantWithConstantBinary(
-          uses_of_false[1], 33, 31, SpvOpSLessThan, 103);
+          uses_of_false[1], 33, 31, spv::Op::OpSLessThan, 103);
 
   ASSERT_TRUE(replace_true_with_double_comparison.IsApplicable(
       context.get(), transformation_context));
@@ -423,13 +427,13 @@
         {SPV_OPERAND_TYPE_LITERAL_INTEGER, {words[0]}},
         {SPV_OPERAND_TYPE_LITERAL_INTEGER, {words[1]}}};
     context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
-        context.get(), SpvOpConstant, 6, 200, operands));
+        context.get(), spv::Op::OpConstant, 6, 200, operands));
     fuzzerutil::UpdateModuleIdBound(context.get(), 200);
     ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
         context.get(), validator_options, kConsoleMessageConsumer));
     // The transformation is not applicable because %200 is NaN.
     ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary(
-                     uses_of_true[0], 11, 200, SpvOpFOrdLessThan, 300)
+                     uses_of_true[0], 11, 200, spv::Op::OpFOrdLessThan, 300)
                      .IsApplicable(context.get(), transformation_context));
   }
   if (std::numeric_limits<double>::has_infinity) {
@@ -440,14 +444,14 @@
         {SPV_OPERAND_TYPE_LITERAL_INTEGER, {words[0]}},
         {SPV_OPERAND_TYPE_LITERAL_INTEGER, {words[1]}}};
     context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
-        context.get(), SpvOpConstant, 6, 201, operands));
+        context.get(), spv::Op::OpConstant, 6, 201, operands));
     fuzzerutil::UpdateModuleIdBound(context.get(), 201);
     ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
         context.get(), validator_options, kConsoleMessageConsumer));
     // Even though the double constant %11 is less than the infinity %201, the
     // transformation is restricted to only apply to finite values.
     ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary(
-                     uses_of_true[0], 11, 201, SpvOpFOrdLessThan, 300)
+                     uses_of_true[0], 11, 201, spv::Op::OpFOrdLessThan, 300)
                      .IsApplicable(context.get(), transformation_context));
   }
   if (std::numeric_limits<float>::has_infinity) {
@@ -459,13 +463,14 @@
     memcpy(words_negative_infinity, &negative_infinity_float, sizeof(float));
     opt::Instruction::OperandList operands_positive_infinity = {
         {SPV_OPERAND_TYPE_LITERAL_INTEGER, {words_positive_infinity[0]}}};
-    context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
-        context.get(), SpvOpConstant, 12, 202, operands_positive_infinity));
+    context->module()->AddGlobalValue(
+        MakeUnique<opt::Instruction>(context.get(), spv::Op::OpConstant, 12,
+                                     202, operands_positive_infinity));
     fuzzerutil::UpdateModuleIdBound(context.get(), 202);
     opt::Instruction::OperandList operands = {
         {SPV_OPERAND_TYPE_LITERAL_INTEGER, {words_negative_infinity[0]}}};
     context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
-        context.get(), SpvOpConstant, 12, 203, operands));
+        context.get(), spv::Op::OpConstant, 12, 203, operands));
     fuzzerutil::UpdateModuleIdBound(context.get(), 203);
     ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
         context.get(), validator_options, kConsoleMessageConsumer));
@@ -473,7 +478,7 @@
     // infinity %202, the transformation is restricted to only apply to finite
     // values.
     ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary(
-                     uses_of_true[0], 203, 202, SpvOpFOrdLessThan, 300)
+                     uses_of_true[0], 203, 202, spv::Op::OpFOrdLessThan, 300)
                      .IsApplicable(context.get(), transformation_context));
   }
 }
@@ -547,14 +552,14 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
   auto use_of_true_in_if = MakeIdUseDescriptor(
-      13, MakeInstructionDescriptor(10, SpvOpBranchConditional, 0), 0);
+      13, MakeInstructionDescriptor(10, spv::Op::OpBranchConditional, 0), 0);
   auto use_of_false_in_while = MakeIdUseDescriptor(
-      21, MakeInstructionDescriptor(16, SpvOpBranchConditional, 0), 0);
+      21, MakeInstructionDescriptor(16, spv::Op::OpBranchConditional, 0), 0);
 
   auto replacement_1 = TransformationReplaceBooleanConstantWithConstantBinary(
-      use_of_true_in_if, 9, 11, SpvOpSLessThan, 100);
+      use_of_true_in_if, 9, 11, spv::Op::OpSLessThan, 100);
   auto replacement_2 = TransformationReplaceBooleanConstantWithConstantBinary(
-      use_of_false_in_while, 9, 11, SpvOpSGreaterThanEqual, 101);
+      use_of_false_in_while, 9, 11, spv::Op::OpSGreaterThanEqual, 101);
 
   ASSERT_TRUE(
       replacement_1.IsApplicable(context.get(), transformation_context));
@@ -662,10 +667,11 @@
                                                kConsoleMessageConsumer));
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
-  auto instruction_descriptor = MakeInstructionDescriptor(14, SpvOpPhi, 0);
+  auto instruction_descriptor =
+      MakeInstructionDescriptor(14, spv::Op::OpPhi, 0);
   auto id_use_descriptor = MakeIdUseDescriptor(8, instruction_descriptor, 0);
   auto transformation = TransformationReplaceBooleanConstantWithConstantBinary(
-      id_use_descriptor, 6, 7, SpvOpULessThan, 15);
+      id_use_descriptor, 6, 7, spv::Op::OpULessThan, 15);
   ASSERT_TRUE(
       transformation.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
@@ -740,11 +746,12 @@
                                                kConsoleMessageConsumer));
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
-  ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary(
-                   MakeIdUseDescriptor(
-                       9, MakeInstructionDescriptor(50, SpvOpVariable, 0), 1),
-                   13, 15, SpvOpSLessThan, 100)
-                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationReplaceBooleanConstantWithConstantBinary(
+          MakeIdUseDescriptor(
+              9, MakeInstructionDescriptor(50, spv::Op::OpVariable, 0), 1),
+          13, 15, spv::Op::OpSLessThan, 100)
+          .IsApplicable(context.get(), transformation_context));
 }
 
 }  // namespace
diff --git a/test/fuzz/transformation_replace_branch_from_dead_block_with_exit_test.cpp b/test/fuzz/transformation_replace_branch_from_dead_block_with_exit_test.cpp
index 6bba14f..012e317 100644
--- a/test/fuzz/transformation_replace_branch_from_dead_block_with_exit_test.cpp
+++ b/test/fuzz/transformation_replace_branch_from_dead_block_with_exit_test.cpp
@@ -79,35 +79,37 @@
   transformation_context.GetFactManager()->AddFactBlockIsDead(20);
 
   // Bad: 4 is not a block
-  ASSERT_FALSE(TransformationReplaceBranchFromDeadBlockWithExit(4, SpvOpKill, 0)
-                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationReplaceBranchFromDeadBlockWithExit(4, spv::Op::OpKill, 0)
+          .IsApplicable(context.get(), transformation_context));
   // Bad: 200 does not exist
   ASSERT_FALSE(
-      TransformationReplaceBranchFromDeadBlockWithExit(200, SpvOpKill, 0)
+      TransformationReplaceBranchFromDeadBlockWithExit(200, spv::Op::OpKill, 0)
           .IsApplicable(context.get(), transformation_context));
   // Bad: 21 is not a dead block
   ASSERT_FALSE(
-      TransformationReplaceBranchFromDeadBlockWithExit(21, SpvOpKill, 0)
+      TransformationReplaceBranchFromDeadBlockWithExit(21, spv::Op::OpKill, 0)
           .IsApplicable(context.get(), transformation_context));
   // Bad: terminator of 8 is not OpBranch
-  ASSERT_FALSE(TransformationReplaceBranchFromDeadBlockWithExit(8, SpvOpKill, 0)
-                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationReplaceBranchFromDeadBlockWithExit(8, spv::Op::OpKill, 0)
+          .IsApplicable(context.get(), transformation_context));
   // Bad: 10's successor only has 10 as a predecessor
   ASSERT_FALSE(
-      TransformationReplaceBranchFromDeadBlockWithExit(10, SpvOpKill, 0)
+      TransformationReplaceBranchFromDeadBlockWithExit(10, spv::Op::OpKill, 0)
           .IsApplicable(context.get(), transformation_context));
 
 #ifndef NDEBUG
   ASSERT_DEATH(
-      TransformationReplaceBranchFromDeadBlockWithExit(20, SpvOpSwitch, 0)
+      TransformationReplaceBranchFromDeadBlockWithExit(20, spv::Op::OpSwitch, 0)
           .IsApplicable(context.get(), transformation_context),
       "Invalid early exit opcode.");
 #endif
 
   auto transformation1 =
-      TransformationReplaceBranchFromDeadBlockWithExit(20, SpvOpKill, 0);
+      TransformationReplaceBranchFromDeadBlockWithExit(20, spv::Op::OpKill, 0);
   auto transformation2 =
-      TransformationReplaceBranchFromDeadBlockWithExit(16, SpvOpKill, 0);
+      TransformationReplaceBranchFromDeadBlockWithExit(16, spv::Op::OpKill, 0);
   ASSERT_TRUE(
       transformation1.IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(
@@ -281,48 +283,48 @@
 
   // Bad: OpKill not allowed in vertex shader
   ASSERT_FALSE(
-      TransformationReplaceBranchFromDeadBlockWithExit(201, SpvOpKill, 0)
+      TransformationReplaceBranchFromDeadBlockWithExit(201, spv::Op::OpKill, 0)
           .IsApplicable(context.get(), transformation_context));
 
   // Bad: OpReturn is not allowed in function that expects a returned value.
-  ASSERT_FALSE(
-      TransformationReplaceBranchFromDeadBlockWithExit(200, SpvOpReturn, 0)
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationReplaceBranchFromDeadBlockWithExit(
+                   200, spv::Op::OpReturn, 0)
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: Return value id does not exist
   ASSERT_FALSE(TransformationReplaceBranchFromDeadBlockWithExit(
-                   201, SpvOpReturnValue, 1000)
+                   201, spv::Op::OpReturnValue, 1000)
                    .IsApplicable(context.get(), transformation_context));
 
   // Bad: Return value id does not have a type
-  ASSERT_FALSE(
-      TransformationReplaceBranchFromDeadBlockWithExit(200, SpvOpReturnValue, 6)
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationReplaceBranchFromDeadBlockWithExit(
+                   200, spv::Op::OpReturnValue, 6)
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: Return value id does not have the right type
   ASSERT_FALSE(TransformationReplaceBranchFromDeadBlockWithExit(
-                   201, SpvOpReturnValue, 48)
+                   201, spv::Op::OpReturnValue, 48)
                    .IsApplicable(context.get(), transformation_context));
 
   // Bad: Return value id is not available
   ASSERT_FALSE(TransformationReplaceBranchFromDeadBlockWithExit(
-                   200, SpvOpReturnValue, 400)
+                   200, spv::Op::OpReturnValue, 400)
                    .IsApplicable(context.get(), transformation_context));
 
   // Bad: Early exit now allowed in continue construct
-  ASSERT_FALSE(
-      TransformationReplaceBranchFromDeadBlockWithExit(101, SpvOpUnreachable, 0)
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationReplaceBranchFromDeadBlockWithExit(
+                   101, spv::Op::OpUnreachable, 0)
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: Early exit now allowed in continue construct (again)
   ASSERT_FALSE(TransformationReplaceBranchFromDeadBlockWithExit(
-                   300, SpvOpReturnValue, 14)
+                   300, spv::Op::OpReturnValue, 14)
                    .IsApplicable(context.get(), transformation_context));
 
   auto transformation1 = TransformationReplaceBranchFromDeadBlockWithExit(
-      200, SpvOpUnreachable, 0);
+      200, spv::Op::OpUnreachable, 0);
   auto transformation2 = TransformationReplaceBranchFromDeadBlockWithExit(
-      201, SpvOpReturnValue, 400);
+      201, spv::Op::OpReturnValue, 400);
 
   ASSERT_TRUE(
       transformation1.IsApplicable(context.get(), transformation_context));
@@ -339,7 +341,7 @@
 
   opt::Instruction* return_value_inst =
       context->get_instr_block(201)->terminator();
-  ASSERT_EQ(SpvOpReturnValue, return_value_inst->opcode());
+  ASSERT_EQ(spv::Op::OpReturnValue, return_value_inst->opcode());
   ASSERT_EQ(SPV_OPERAND_TYPE_ID, return_value_inst->GetInOperand(0).type);
   ASSERT_EQ(400, return_value_inst->GetSingleWordInOperand(0));
 
@@ -504,9 +506,9 @@
   transformation_context.GetFactManager()->AddFactBlockIsDead(20);
 
   auto transformation1 =
-      TransformationReplaceBranchFromDeadBlockWithExit(20, SpvOpKill, 0);
+      TransformationReplaceBranchFromDeadBlockWithExit(20, spv::Op::OpKill, 0);
   auto transformation2 =
-      TransformationReplaceBranchFromDeadBlockWithExit(16, SpvOpKill, 0);
+      TransformationReplaceBranchFromDeadBlockWithExit(16, spv::Op::OpKill, 0);
   ASSERT_TRUE(
       transformation1.IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(
@@ -609,9 +611,9 @@
   transformation_context.GetFactManager()->AddFactBlockIsDead(9);
   transformation_context.GetFactManager()->AddFactBlockIsDead(11);
 
-  ASSERT_FALSE(
-      TransformationReplaceBranchFromDeadBlockWithExit(11, SpvOpUnreachable, 0)
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationReplaceBranchFromDeadBlockWithExit(
+                   11, spv::Op::OpUnreachable, 0)
+                   .IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationReplaceBranchFromDeadBlockWithExitTest,
@@ -658,7 +660,7 @@
   transformation_context.GetFactManager()->AddFactBlockIsDead(11);
 
   TransformationReplaceBranchFromDeadBlockWithExit transformation(
-      11, SpvOpUnreachable, 0);
+      11, spv::Op::OpUnreachable, 0);
   ASSERT_TRUE(
       transformation.IsApplicable(context.get(), transformation_context));
   transformation.Apply(context.get(), &transformation_context);
@@ -741,7 +743,7 @@
   transformation_context.GetFactManager()->AddFactBlockIsDead(13);
 
   TransformationReplaceBranchFromDeadBlockWithExit transformation(
-      13, SpvOpUnreachable, 0);
+      13, spv::Op::OpUnreachable, 0);
   ASSERT_TRUE(
       transformation.IsApplicable(context.get(), transformation_context));
   transformation.Apply(context.get(), &transformation_context);
@@ -823,7 +825,7 @@
   transformation_context.GetFactManager()->AddFactBlockIsDead(10);
 
   TransformationReplaceBranchFromDeadBlockWithExit transformation(
-      10, SpvOpUnreachable, 0);
+      10, spv::Op::OpUnreachable, 0);
   ASSERT_TRUE(
       transformation.IsApplicable(context.get(), transformation_context));
   transformation.Apply(context.get(), &transformation_context);
diff --git a/test/fuzz/transformation_replace_constant_with_uniform_test.cpp b/test/fuzz/transformation_replace_constant_with_uniform_test.cpp
index fe76068..7c6d126 100644
--- a/test/fuzz/transformation_replace_constant_with_uniform_test.cpp
+++ b/test/fuzz/transformation_replace_constant_with_uniform_test.cpp
@@ -121,12 +121,12 @@
   ASSERT_TRUE(AddFactHelper(&transformation_context, 3, blockname_c));
 
   // The constant ids are 9, 11 and 14, for 1, 2 and 3 respectively.
-  protobufs::IdUseDescriptor use_of_9_in_store =
-      MakeIdUseDescriptor(9, MakeInstructionDescriptor(8, SpvOpStore, 0), 1);
-  protobufs::IdUseDescriptor use_of_11_in_add =
-      MakeIdUseDescriptor(11, MakeInstructionDescriptor(12, SpvOpIAdd, 0), 1);
-  protobufs::IdUseDescriptor use_of_14_in_add =
-      MakeIdUseDescriptor(14, MakeInstructionDescriptor(15, SpvOpIAdd, 0), 0);
+  protobufs::IdUseDescriptor use_of_9_in_store = MakeIdUseDescriptor(
+      9, MakeInstructionDescriptor(8, spv::Op::OpStore, 0), 1);
+  protobufs::IdUseDescriptor use_of_11_in_add = MakeIdUseDescriptor(
+      11, MakeInstructionDescriptor(12, spv::Op::OpIAdd, 0), 1);
+  protobufs::IdUseDescriptor use_of_14_in_add = MakeIdUseDescriptor(
+      14, MakeInstructionDescriptor(15, spv::Op::OpIAdd, 0), 0);
 
   // These transformations work: they match the facts.
   auto transformation_use_of_9_in_store =
@@ -172,8 +172,8 @@
 
   // The following transformation does not apply because the id descriptor is
   // not sensible.
-  protobufs::IdUseDescriptor nonsense_id_use_descriptor =
-      MakeIdUseDescriptor(9, MakeInstructionDescriptor(15, SpvOpIAdd, 0), 0);
+  protobufs::IdUseDescriptor nonsense_id_use_descriptor = MakeIdUseDescriptor(
+      9, MakeInstructionDescriptor(15, spv::Op::OpIAdd, 0), 0);
   ASSERT_FALSE(TransformationReplaceConstantWithUniform(
                    nonsense_id_use_descriptor, blockname_a, 101, 102)
                    .IsApplicable(context.get(), transformation_context));
@@ -490,14 +490,14 @@
   ASSERT_TRUE(AddFactHelper(&transformation_context, 4, blockname_4));
 
   // The constant ids are 13, 15, 17 and 20, for 1, 2, 3 and 4 respectively.
-  protobufs::IdUseDescriptor use_of_13_in_store =
-      MakeIdUseDescriptor(13, MakeInstructionDescriptor(21, SpvOpStore, 0), 1);
-  protobufs::IdUseDescriptor use_of_15_in_add =
-      MakeIdUseDescriptor(15, MakeInstructionDescriptor(16, SpvOpIAdd, 0), 1);
-  protobufs::IdUseDescriptor use_of_17_in_add =
-      MakeIdUseDescriptor(17, MakeInstructionDescriptor(19, SpvOpIAdd, 0), 0);
-  protobufs::IdUseDescriptor use_of_20_in_store =
-      MakeIdUseDescriptor(20, MakeInstructionDescriptor(19, SpvOpStore, 1), 1);
+  protobufs::IdUseDescriptor use_of_13_in_store = MakeIdUseDescriptor(
+      13, MakeInstructionDescriptor(21, spv::Op::OpStore, 0), 1);
+  protobufs::IdUseDescriptor use_of_15_in_add = MakeIdUseDescriptor(
+      15, MakeInstructionDescriptor(16, spv::Op::OpIAdd, 0), 1);
+  protobufs::IdUseDescriptor use_of_17_in_add = MakeIdUseDescriptor(
+      17, MakeInstructionDescriptor(19, spv::Op::OpIAdd, 0), 0);
+  protobufs::IdUseDescriptor use_of_20_in_store = MakeIdUseDescriptor(
+      20, MakeInstructionDescriptor(19, spv::Op::OpStore, 1), 1);
 
   // These transformations work: they match the facts.
   auto transformation_use_of_13_in_store =
@@ -726,8 +726,8 @@
   ASSERT_TRUE(AddFactHelper(&transformation_context, 0, blockname_0));
 
   // The constant id is 9 for 0.
-  protobufs::IdUseDescriptor use_of_9_in_store =
-      MakeIdUseDescriptor(9, MakeInstructionDescriptor(8, SpvOpStore, 0), 1);
+  protobufs::IdUseDescriptor use_of_9_in_store = MakeIdUseDescriptor(
+      9, MakeInstructionDescriptor(8, spv::Op::OpStore, 0), 1);
 
   // This transformation is not available because no uniform pointer to integer
   // type is present:
@@ -803,8 +803,8 @@
   ASSERT_TRUE(AddFactHelper(&transformation_context, 9, blockname_9));
 
   // The constant id is 9 for 9.
-  protobufs::IdUseDescriptor use_of_9_in_store =
-      MakeIdUseDescriptor(9, MakeInstructionDescriptor(8, SpvOpStore, 0), 1);
+  protobufs::IdUseDescriptor use_of_9_in_store = MakeIdUseDescriptor(
+      9, MakeInstructionDescriptor(8, spv::Op::OpStore, 0), 1);
 
   // This transformation is not available because no constant is present for the
   // index 1 required to index into the uniform buffer:
@@ -879,8 +879,8 @@
       AddFactHelper(&transformation_context, float_data[0], blockname_3));
 
   // The constant id is 9 for 3.0.
-  protobufs::IdUseDescriptor use_of_9_in_store =
-      MakeIdUseDescriptor(9, MakeInstructionDescriptor(8, SpvOpStore, 0), 1);
+  protobufs::IdUseDescriptor use_of_9_in_store = MakeIdUseDescriptor(
+      9, MakeInstructionDescriptor(8, spv::Op::OpStore, 0), 1);
 
   // This transformation is not available because no integer type is present to
   // allow a constant index to be expressed:
@@ -966,10 +966,10 @@
   ASSERT_TRUE(AddFactHelper(&transformation_context, 10, blockname_10));
 
   // The constant ids for 9 and 10 are 9 and 11 respectively
-  protobufs::IdUseDescriptor use_of_9_in_store =
-      MakeIdUseDescriptor(9, MakeInstructionDescriptor(10, SpvOpStore, 0), 1);
-  protobufs::IdUseDescriptor use_of_11_in_store =
-      MakeIdUseDescriptor(11, MakeInstructionDescriptor(10, SpvOpStore, 1), 1);
+  protobufs::IdUseDescriptor use_of_9_in_store = MakeIdUseDescriptor(
+      9, MakeInstructionDescriptor(10, spv::Op::OpStore, 0), 1);
+  protobufs::IdUseDescriptor use_of_11_in_store = MakeIdUseDescriptor(
+      11, MakeInstructionDescriptor(10, spv::Op::OpStore, 1), 1);
 
   // These are right:
   ASSERT_TRUE(TransformationReplaceConstantWithUniform(use_of_9_in_store,
@@ -1251,57 +1251,73 @@
   std::vector<TransformationReplaceConstantWithUniform> transformations;
 
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      MakeIdUseDescriptor(18, MakeInstructionDescriptor(20, SpvOpStore, 0), 1),
+      MakeIdUseDescriptor(
+          18, MakeInstructionDescriptor(20, spv::Op::OpStore, 0), 1),
       uniform_f_a_4, 200, 201));
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      MakeIdUseDescriptor(22, MakeInstructionDescriptor(23, SpvOpStore, 0), 1),
+      MakeIdUseDescriptor(
+          22, MakeInstructionDescriptor(23, spv::Op::OpStore, 0), 1),
       uniform_f_a_3, 202, 203));
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      MakeIdUseDescriptor(25, MakeInstructionDescriptor(26, SpvOpStore, 0), 1),
+      MakeIdUseDescriptor(
+          25, MakeInstructionDescriptor(26, spv::Op::OpStore, 0), 1),
       uniform_f_a_2, 204, 205));
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      MakeIdUseDescriptor(28, MakeInstructionDescriptor(29, SpvOpStore, 0), 1),
+      MakeIdUseDescriptor(
+          28, MakeInstructionDescriptor(29, spv::Op::OpStore, 0), 1),
       uniform_f_a_1, 206, 207));
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      MakeIdUseDescriptor(31, MakeInstructionDescriptor(32, SpvOpStore, 0), 1),
+      MakeIdUseDescriptor(
+          31, MakeInstructionDescriptor(32, spv::Op::OpStore, 0), 1),
       uniform_f_a_0, 208, 209));
 
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      MakeIdUseDescriptor(30, MakeInstructionDescriptor(35, SpvOpStore, 0), 1),
+      MakeIdUseDescriptor(
+          30, MakeInstructionDescriptor(35, spv::Op::OpStore, 0), 1),
       uniform_f_b_w, 210, 211));
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      MakeIdUseDescriptor(27, MakeInstructionDescriptor(37, SpvOpStore, 0), 1),
+      MakeIdUseDescriptor(
+          27, MakeInstructionDescriptor(37, spv::Op::OpStore, 0), 1),
       uniform_f_b_z, 212, 213));
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      MakeIdUseDescriptor(24, MakeInstructionDescriptor(39, SpvOpStore, 0), 1),
+      MakeIdUseDescriptor(
+          24, MakeInstructionDescriptor(39, spv::Op::OpStore, 0), 1),
       uniform_f_b_y, 214, 215));
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      MakeIdUseDescriptor(21, MakeInstructionDescriptor(41, SpvOpStore, 0), 1),
+      MakeIdUseDescriptor(
+          21, MakeInstructionDescriptor(41, spv::Op::OpStore, 0), 1),
       uniform_f_b_x, 216, 217));
 
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      MakeIdUseDescriptor(44, MakeInstructionDescriptor(45, SpvOpStore, 0), 1),
+      MakeIdUseDescriptor(
+          44, MakeInstructionDescriptor(45, spv::Op::OpStore, 0), 1),
       uniform_f_c_z, 220, 221));
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      MakeIdUseDescriptor(46, MakeInstructionDescriptor(47, SpvOpStore, 0), 1),
+      MakeIdUseDescriptor(
+          46, MakeInstructionDescriptor(47, spv::Op::OpStore, 0), 1),
       uniform_f_c_y, 222, 223));
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      MakeIdUseDescriptor(48, MakeInstructionDescriptor(49, SpvOpStore, 0), 1),
+      MakeIdUseDescriptor(
+          48, MakeInstructionDescriptor(49, spv::Op::OpStore, 0), 1),
       uniform_f_c_x, 224, 225));
 
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      MakeIdUseDescriptor(50, MakeInstructionDescriptor(52, SpvOpStore, 0), 1),
+      MakeIdUseDescriptor(
+          50, MakeInstructionDescriptor(52, spv::Op::OpStore, 0), 1),
       uniform_f_d, 226, 227));
 
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      MakeIdUseDescriptor(53, MakeInstructionDescriptor(54, SpvOpStore, 0), 1),
+      MakeIdUseDescriptor(
+          53, MakeInstructionDescriptor(54, spv::Op::OpStore, 0), 1),
       uniform_h_x, 228, 229));
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      MakeIdUseDescriptor(55, MakeInstructionDescriptor(56, SpvOpStore, 0), 1),
+      MakeIdUseDescriptor(
+          55, MakeInstructionDescriptor(56, spv::Op::OpStore, 0), 1),
       uniform_h_y, 230, 231));
 
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      MakeIdUseDescriptor(42, MakeInstructionDescriptor(43, SpvOpStore, 0), 1),
+      MakeIdUseDescriptor(
+          42, MakeInstructionDescriptor(43, spv::Op::OpStore, 0), 1),
       uniform_g, 218, 219));
 
   for (auto& transformation : transformations) {
@@ -1520,11 +1536,12 @@
 
   ASSERT_TRUE(AddFactHelper(&transformation_context, 0, blockname_a));
 
-  ASSERT_FALSE(TransformationReplaceConstantWithUniform(
-                   MakeIdUseDescriptor(
-                       50, MakeInstructionDescriptor(8, SpvOpVariable, 0), 1),
-                   blockname_a, 100, 101)
-                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationReplaceConstantWithUniform(
+          MakeIdUseDescriptor(
+              50, MakeInstructionDescriptor(8, spv::Op::OpVariable, 0), 1),
+          blockname_a, 100, 101)
+          .IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationReplaceConstantWithUniformTest, ReplaceOpPhiOperand) {
@@ -1579,7 +1596,8 @@
 
   {
     TransformationReplaceConstantWithUniform transformation(
-        MakeIdUseDescriptor(7, MakeInstructionDescriptor(23, SpvOpPhi, 0), 0),
+        MakeIdUseDescriptor(7, MakeInstructionDescriptor(23, spv::Op::OpPhi, 0),
+                            0),
         int_descriptor, 50, 51);
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
diff --git a/test/fuzz/transformation_replace_copy_memory_with_load_store_test.cpp b/test/fuzz/transformation_replace_copy_memory_with_load_store_test.cpp
index bcd04af..689cf19 100644
--- a/test/fuzz/transformation_replace_copy_memory_with_load_store_test.cpp
+++ b/test/fuzz/transformation_replace_copy_memory_with_load_store_test.cpp
@@ -75,11 +75,11 @@
                                                kConsoleMessageConsumer));
 
   auto instruction_descriptor_invalid_1 =
-      MakeInstructionDescriptor(5, SpvOpStore, 0);
+      MakeInstructionDescriptor(5, spv::Op::OpStore, 0);
   auto instruction_descriptor_valid_1 =
-      MakeInstructionDescriptor(5, SpvOpCopyMemory, 0);
+      MakeInstructionDescriptor(5, spv::Op::OpCopyMemory, 0);
   auto instruction_descriptor_valid_2 =
-      MakeInstructionDescriptor(5, SpvOpCopyMemory, 0);
+      MakeInstructionDescriptor(5, spv::Op::OpCopyMemory, 0);
 
   // Invalid: |source_id| is not a fresh id.
   auto transformation_invalid_1 = TransformationReplaceCopyMemoryWithLoadStore(
diff --git a/test/fuzz/transformation_replace_copy_object_with_store_load_test.cpp b/test/fuzz/transformation_replace_copy_object_with_store_load_test.cpp
index fa8c068..368e208 100644
--- a/test/fuzz/transformation_replace_copy_object_with_store_load_test.cpp
+++ b/test/fuzz/transformation_replace_copy_object_with_store_load_test.cpp
@@ -88,44 +88,45 @@
 
   // Invalid: fresh_variable_id=10 is not fresh.
   auto transformation_invalid_1 = TransformationReplaceCopyObjectWithStoreLoad(
-      27, 10, SpvStorageClassFunction, 9);
+      27, 10, (uint32_t)spv::StorageClass::Function, 9);
   ASSERT_FALSE(transformation_invalid_1.IsApplicable(context.get(),
                                                      transformation_context));
 
   // Invalid: copy_object_result_id=26 is not a CopyObject instruction.
   auto transformation_invalid_2 = TransformationReplaceCopyObjectWithStoreLoad(
-      26, 30, SpvStorageClassFunction, 9);
+      26, 30, (uint32_t)spv::StorageClass::Function, 9);
   ASSERT_FALSE(transformation_invalid_2.IsApplicable(context.get(),
                                                      transformation_context));
 
   // Invalid: copy_object_result_id=40 is of type pointer.
   auto transformation_invalid_3 = TransformationReplaceCopyObjectWithStoreLoad(
-      40, 30, SpvStorageClassFunction, 9);
+      40, 30, (uint32_t)spv::StorageClass::Function, 9);
   ASSERT_FALSE(transformation_invalid_3.IsApplicable(context.get(),
                                                      transformation_context));
 
   // Invalid: Pointer type instruction in this storage class pointing to the
   // value type is not defined.
   auto transformation_invalid_4 = TransformationReplaceCopyObjectWithStoreLoad(
-      40, 30, SpvStorageClassPrivate, 9);
+      40, 30, (uint32_t)spv::StorageClass::Private, 9);
   ASSERT_FALSE(transformation_invalid_4.IsApplicable(context.get(),
                                                      transformation_context));
 
   // Invalid: initializer_id=15 has the wrong type relative to the OpCopyObject
   // instruction.
   auto transformation_invalid_5 = TransformationReplaceCopyObjectWithStoreLoad(
-      27, 30, SpvStorageClassFunction, 15);
+      27, 30, (uint32_t)spv::StorageClass::Function, 15);
   ASSERT_FALSE(transformation_invalid_5.IsApplicable(context.get(),
                                                      transformation_context));
 
-  // Invalid: SpvStorageClassUniform is not applicable to the transformation.
+  // Invalid: spv::StorageClass::Uniform is not applicable to the
+  // transformation.
   auto transformation_invalid_6 = TransformationReplaceCopyObjectWithStoreLoad(
-      27, 30, SpvStorageClassUniform, 9);
+      27, 30, (uint32_t)spv::StorageClass::Uniform, 9);
   ASSERT_FALSE(transformation_invalid_6.IsApplicable(context.get(),
                                                      transformation_context));
 
   auto transformation_valid_1 = TransformationReplaceCopyObjectWithStoreLoad(
-      27, 30, SpvStorageClassFunction, 9);
+      27, 30, (uint32_t)spv::StorageClass::Function, 9);
   ASSERT_TRUE(transformation_valid_1.IsApplicable(context.get(),
                                                   transformation_context));
   ApplyAndCheckFreshIds(transformation_valid_1, context.get(),
@@ -134,7 +135,7 @@
                                                kConsoleMessageConsumer));
 
   auto transformation_valid_2 = TransformationReplaceCopyObjectWithStoreLoad(
-      28, 32, SpvStorageClassPrivate, 15);
+      28, 32, (uint32_t)spv::StorageClass::Private, 15);
   ASSERT_TRUE(transformation_valid_2.IsApplicable(context.get(),
                                                   transformation_context));
   ApplyAndCheckFreshIds(transformation_valid_2, context.get(),
@@ -244,7 +245,7 @@
   transformation_context.GetFactManager()->AddFactIdIsIrrelevant(11);
 
   auto transformation_1 = TransformationReplaceCopyObjectWithStoreLoad(
-      50, 100, SpvStorageClassFunction, 10);
+      50, 100, (uint32_t)spv::StorageClass::Function, 10);
   ASSERT_TRUE(
       transformation_1.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation_1, context.get(),
@@ -253,7 +254,7 @@
       MakeDataDescriptor(100, {}), MakeDataDescriptor(50, {})));
 
   auto transformation_2 = TransformationReplaceCopyObjectWithStoreLoad(
-      51, 101, SpvStorageClassFunction, 10);
+      51, 101, (uint32_t)spv::StorageClass::Function, 10);
   ASSERT_TRUE(
       transformation_2.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation_2, context.get(),
diff --git a/test/fuzz/transformation_replace_id_with_synonym_test.cpp b/test/fuzz/transformation_replace_id_with_synonym_test.cpp
index b33dd48..bd27c29 100644
--- a/test/fuzz/transformation_replace_id_with_synonym_test.cpp
+++ b/test/fuzz/transformation_replace_id_with_synonym_test.cpp
@@ -230,7 +230,8 @@
   // %202 cannot replace %15 as in-operand 0 of %300, since %202 does not
   // dominate %300.
   auto synonym_does_not_dominate_use = TransformationReplaceIdWithSynonym(
-      MakeIdUseDescriptor(15, MakeInstructionDescriptor(300, SpvOpIAdd, 0), 0),
+      MakeIdUseDescriptor(
+          15, MakeInstructionDescriptor(300, spv::Op::OpIAdd, 0), 0),
       202);
   ASSERT_FALSE(synonym_does_not_dominate_use.IsApplicable(
       context.get(), transformation_context));
@@ -239,8 +240,8 @@
   // incoming value for block %72, and %202 does not dominate %72.
   auto synonym_does_not_dominate_use_op_phi =
       TransformationReplaceIdWithSynonym(
-          MakeIdUseDescriptor(15, MakeInstructionDescriptor(301, SpvOpPhi, 0),
-                              2),
+          MakeIdUseDescriptor(
+              15, MakeInstructionDescriptor(301, spv::Op::OpPhi, 0), 2),
           202);
   ASSERT_FALSE(synonym_does_not_dominate_use_op_phi.IsApplicable(
       context.get(), transformation_context));
@@ -248,14 +249,15 @@
   // %200 is not a synonym for %84
   auto id_in_use_is_not_synonymous = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(
-          84, MakeInstructionDescriptor(67, SpvOpSGreaterThan, 0), 0),
+          84, MakeInstructionDescriptor(67, spv::Op::OpSGreaterThan, 0), 0),
       200);
   ASSERT_FALSE(id_in_use_is_not_synonymous.IsApplicable(
       context.get(), transformation_context));
 
   // %86 is not a synonym for anything (and in particular not for %74)
   auto id_has_no_synonyms = TransformationReplaceIdWithSynonym(
-      MakeIdUseDescriptor(86, MakeInstructionDescriptor(84, SpvOpPhi, 0), 2),
+      MakeIdUseDescriptor(86, MakeInstructionDescriptor(84, spv::Op::OpPhi, 0),
+                          2),
       74);
   ASSERT_FALSE(
       id_has_no_synonyms.IsApplicable(context.get(), transformation_context));
@@ -264,7 +266,7 @@
   auto synonym_use_is_in_synonym_definition =
       TransformationReplaceIdWithSynonym(
           MakeIdUseDescriptor(
-              84, MakeInstructionDescriptor(207, SpvOpCopyObject, 0), 0),
+              84, MakeInstructionDescriptor(207, spv::Op::OpCopyObject, 0), 0),
           207);
   ASSERT_FALSE(synonym_use_is_in_synonym_definition.IsApplicable(
       context.get(), transformation_context));
@@ -273,7 +275,7 @@
   // definition of %207)
   auto bad_id_use_descriptor = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(
-          84, MakeInstructionDescriptor(200, SpvOpCopyObject, 0), 0),
+          84, MakeInstructionDescriptor(200, spv::Op::OpCopyObject, 0), 0),
       207);
   ASSERT_FALSE(bad_id_use_descriptor.IsApplicable(context.get(),
                                                   transformation_context));
@@ -282,7 +284,7 @@
   // non-constant index.
   auto bad_access_chain = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(
-          12, MakeInstructionDescriptor(14, SpvOpAccessChain, 0), 1),
+          12, MakeInstructionDescriptor(14, spv::Op::OpAccessChain, 0), 1),
       209);
   ASSERT_FALSE(
       bad_access_chain.IsApplicable(context.get(), transformation_context));
@@ -301,7 +303,8 @@
   SetUpIdSynonyms(transformation_context.GetFactManager());
 
   auto global_constant_synonym = TransformationReplaceIdWithSynonym(
-      MakeIdUseDescriptor(19, MakeInstructionDescriptor(47, SpvOpStore, 0), 1),
+      MakeIdUseDescriptor(
+          19, MakeInstructionDescriptor(47, spv::Op::OpStore, 0), 1),
       210);
   uint32_t num_uses_of_original_id_before_replacement =
       context->get_def_use_mgr()->NumUses(19);
@@ -320,7 +323,7 @@
 
   auto replace_vector_access_chain_index = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(
-          54, MakeInstructionDescriptor(55, SpvOpAccessChain, 0), 1),
+          54, MakeInstructionDescriptor(55, spv::Op::OpAccessChain, 0), 1),
       204);
   ASSERT_TRUE(replace_vector_access_chain_index.IsApplicable(
       context.get(), transformation_context));
@@ -333,7 +336,7 @@
   // copied with something that is already a synonym.
   auto regular_replacement = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(
-          15, MakeInstructionDescriptor(202, SpvOpCopyObject, 0), 0),
+          15, MakeInstructionDescriptor(202, spv::Op::OpCopyObject, 0), 0),
       201);
   ASSERT_TRUE(
       regular_replacement.IsApplicable(context.get(), transformation_context));
@@ -343,7 +346,8 @@
                                                kConsoleMessageConsumer));
 
   auto regular_replacement2 = TransformationReplaceIdWithSynonym(
-      MakeIdUseDescriptor(55, MakeInstructionDescriptor(203, SpvOpStore, 0), 0),
+      MakeIdUseDescriptor(
+          55, MakeInstructionDescriptor(203, spv::Op::OpStore, 0), 0),
       203);
   ASSERT_TRUE(
       regular_replacement2.IsApplicable(context.get(), transformation_context));
@@ -353,7 +357,8 @@
                                                kConsoleMessageConsumer));
 
   auto good_op_phi = TransformationReplaceIdWithSynonym(
-      MakeIdUseDescriptor(74, MakeInstructionDescriptor(86, SpvOpPhi, 0), 2),
+      MakeIdUseDescriptor(74, MakeInstructionDescriptor(86, spv::Op::OpPhi, 0),
+                          2),
       205);
   ASSERT_TRUE(good_op_phi.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(good_op_phi, context.get(), &transformation_context);
@@ -543,7 +548,8 @@
   // Replace %10 with %100 in:
   // %11 = OpLoad %6 %10
   auto replacement1 = TransformationReplaceIdWithSynonym(
-      MakeIdUseDescriptor(10, MakeInstructionDescriptor(11, SpvOpLoad, 0), 0),
+      MakeIdUseDescriptor(10, MakeInstructionDescriptor(11, spv::Op::OpLoad, 0),
+                          0),
       100);
   ASSERT_TRUE(replacement1.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(replacement1, context.get(), &transformation_context);
@@ -553,7 +559,8 @@
   // Replace %8 with %101 in:
   // OpStore %8 %11
   auto replacement2 = TransformationReplaceIdWithSynonym(
-      MakeIdUseDescriptor(8, MakeInstructionDescriptor(11, SpvOpStore, 0), 0),
+      MakeIdUseDescriptor(8, MakeInstructionDescriptor(11, spv::Op::OpStore, 0),
+                          0),
       101);
   ASSERT_TRUE(replacement2.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(replacement2, context.get(), &transformation_context);
@@ -563,7 +570,8 @@
   // Replace %8 with %101 in:
   // %12 = OpLoad %6 %8
   auto replacement3 = TransformationReplaceIdWithSynonym(
-      MakeIdUseDescriptor(8, MakeInstructionDescriptor(12, SpvOpLoad, 0), 0),
+      MakeIdUseDescriptor(8, MakeInstructionDescriptor(12, spv::Op::OpLoad, 0),
+                          0),
       101);
   ASSERT_TRUE(replacement3.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(replacement3, context.get(), &transformation_context);
@@ -573,7 +581,8 @@
   // Replace %10 with %100 in:
   // OpStore %10 %12
   auto replacement4 = TransformationReplaceIdWithSynonym(
-      MakeIdUseDescriptor(10, MakeInstructionDescriptor(12, SpvOpStore, 0), 0),
+      MakeIdUseDescriptor(
+          10, MakeInstructionDescriptor(12, spv::Op::OpStore, 0), 0),
       100);
   ASSERT_TRUE(replacement4.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(replacement4, context.get(), &transformation_context);
@@ -678,7 +687,7 @@
   // %16 = OpFunctionCall %2 %10 %14
   auto replacement = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(
-          14, MakeInstructionDescriptor(16, SpvOpFunctionCall, 0), 1),
+          14, MakeInstructionDescriptor(16, spv::Op::OpFunctionCall, 0), 1),
       100);
   ASSERT_FALSE(replacement.IsApplicable(context.get(), transformation_context));
 }
@@ -871,7 +880,7 @@
   // The index %16 used for a cannot be replaced
   auto replacement1 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(
-          16, MakeInstructionDescriptor(20, SpvOpAccessChain, 0), 1),
+          16, MakeInstructionDescriptor(20, spv::Op::OpAccessChain, 0), 1),
       100);
   ASSERT_FALSE(
       replacement1.IsApplicable(context.get(), transformation_context));
@@ -881,7 +890,7 @@
   // The index %16 used for f cannot be replaced
   auto replacement2 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(
-          16, MakeInstructionDescriptor(39, SpvOpAccessChain, 0), 1),
+          16, MakeInstructionDescriptor(39, spv::Op::OpAccessChain, 0), 1),
       100);
   ASSERT_FALSE(
       replacement2.IsApplicable(context.get(), transformation_context));
@@ -891,7 +900,7 @@
   // The index %16 used for a cannot be replaced
   auto replacement3 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(
-          16, MakeInstructionDescriptor(41, SpvOpAccessChain, 0), 2),
+          16, MakeInstructionDescriptor(41, spv::Op::OpAccessChain, 0), 2),
       100);
   ASSERT_FALSE(
       replacement3.IsApplicable(context.get(), transformation_context));
@@ -901,7 +910,7 @@
   // The index %16 used for 0 *can* be replaced
   auto replacement4 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(
-          16, MakeInstructionDescriptor(52, SpvOpAccessChain, 0), 1),
+          16, MakeInstructionDescriptor(52, spv::Op::OpAccessChain, 0), 1),
       100);
   ASSERT_TRUE(replacement4.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(replacement4, context.get(), &transformation_context);
@@ -913,7 +922,7 @@
   // The index %16 used for f cannot be replaced
   auto replacement5 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(
-          16, MakeInstructionDescriptor(52, SpvOpAccessChain, 0), 2),
+          16, MakeInstructionDescriptor(52, spv::Op::OpAccessChain, 0), 2),
       100);
   ASSERT_FALSE(
       replacement5.IsApplicable(context.get(), transformation_context));
@@ -923,7 +932,7 @@
   // The index %16 used for a cannot be replaced
   auto replacement6 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(
-          16, MakeInstructionDescriptor(53, SpvOpAccessChain, 0), 3),
+          16, MakeInstructionDescriptor(53, spv::Op::OpAccessChain, 0), 3),
       100);
   ASSERT_FALSE(
       replacement6.IsApplicable(context.get(), transformation_context));
@@ -933,7 +942,7 @@
   // The index %16 used for 0 *can* be replaced
   auto replacement7 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(
-          16, MakeInstructionDescriptor(53, SpvOpAccessChain, 0), 4),
+          16, MakeInstructionDescriptor(53, spv::Op::OpAccessChain, 0), 4),
       100);
   ASSERT_TRUE(replacement7.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(replacement7, context.get(), &transformation_context);
@@ -947,7 +956,7 @@
   // The index %24 used for b cannot be replaced
   auto replacement8 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(
-          21, MakeInstructionDescriptor(24, SpvOpAccessChain, 0), 1),
+          21, MakeInstructionDescriptor(24, spv::Op::OpAccessChain, 0), 1),
       101);
   ASSERT_FALSE(
       replacement8.IsApplicable(context.get(), transformation_context));
@@ -957,7 +966,7 @@
   // The index %24 used for g cannot be replaced
   auto replacement9 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(
-          21, MakeInstructionDescriptor(41, SpvOpAccessChain, 0), 1),
+          21, MakeInstructionDescriptor(41, spv::Op::OpAccessChain, 0), 1),
       101);
   ASSERT_FALSE(
       replacement9.IsApplicable(context.get(), transformation_context));
@@ -967,7 +976,7 @@
   // The index %24 used for 1 *can* be replaced
   auto replacement10 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(
-          21, MakeInstructionDescriptor(41, SpvOpAccessChain, 0), 3),
+          21, MakeInstructionDescriptor(41, spv::Op::OpAccessChain, 0), 3),
       101);
   ASSERT_TRUE(
       replacement10.IsApplicable(context.get(), transformation_context));
@@ -980,7 +989,7 @@
   // The index %24 used for g cannot be replaced
   auto replacement11 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(
-          21, MakeInstructionDescriptor(44, SpvOpAccessChain, 0), 1),
+          21, MakeInstructionDescriptor(44, spv::Op::OpAccessChain, 0), 1),
       101);
   ASSERT_FALSE(
       replacement11.IsApplicable(context.get(), transformation_context));
@@ -990,7 +999,7 @@
   // The index %24 used for b cannot be replaced
   auto replacement12 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(
-          21, MakeInstructionDescriptor(44, SpvOpAccessChain, 0), 2),
+          21, MakeInstructionDescriptor(44, spv::Op::OpAccessChain, 0), 2),
       101);
   ASSERT_FALSE(
       replacement12.IsApplicable(context.get(), transformation_context));
@@ -1000,7 +1009,7 @@
   // The index %24 used for g cannot be replaced
   auto replacement13 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(
-          21, MakeInstructionDescriptor(46, SpvOpAccessChain, 0), 1),
+          21, MakeInstructionDescriptor(46, spv::Op::OpAccessChain, 0), 1),
       101);
   ASSERT_FALSE(
       replacement13.IsApplicable(context.get(), transformation_context));
@@ -1010,7 +1019,7 @@
   // The index %24 used for 1 *can* be replaced
   auto replacement14 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(
-          21, MakeInstructionDescriptor(53, SpvOpAccessChain, 0), 1),
+          21, MakeInstructionDescriptor(53, spv::Op::OpAccessChain, 0), 1),
       101);
   ASSERT_TRUE(
       replacement14.IsApplicable(context.get(), transformation_context));
@@ -1023,7 +1032,7 @@
   // The index %24 used for g cannot be replaced
   auto replacement15 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(
-          21, MakeInstructionDescriptor(53, SpvOpAccessChain, 0), 2),
+          21, MakeInstructionDescriptor(53, spv::Op::OpAccessChain, 0), 2),
       101);
   ASSERT_FALSE(
       replacement15.IsApplicable(context.get(), transformation_context));
@@ -1033,7 +1042,7 @@
   // The index %24 used for g cannot be replaced
   auto replacement16 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(
-          21, MakeInstructionDescriptor(56, SpvOpAccessChain, 0), 2),
+          21, MakeInstructionDescriptor(56, spv::Op::OpAccessChain, 0), 2),
       101);
   ASSERT_FALSE(
       replacement16.IsApplicable(context.get(), transformation_context));
@@ -1043,7 +1052,7 @@
   // The index %24 used for b cannot be replaced
   auto replacement17 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(
-          21, MakeInstructionDescriptor(56, SpvOpAccessChain, 0), 3),
+          21, MakeInstructionDescriptor(56, spv::Op::OpAccessChain, 0), 3),
       101);
   ASSERT_FALSE(
       replacement17.IsApplicable(context.get(), transformation_context));
@@ -1053,7 +1062,8 @@
   // The index %24 used for g cannot be replaced
   auto replacement18 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(
-          21, MakeInstructionDescriptor(58, SpvOpInBoundsAccessChain, 0), 2),
+          21, MakeInstructionDescriptor(58, spv::Op::OpInBoundsAccessChain, 0),
+          2),
       101);
   ASSERT_FALSE(
       replacement18.IsApplicable(context.get(), transformation_context));
@@ -1065,7 +1075,7 @@
   // The index %17 used for 2 *can* be replaced
   auto replacement19 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(
-          17, MakeInstructionDescriptor(20, SpvOpAccessChain, 0), 2),
+          17, MakeInstructionDescriptor(20, spv::Op::OpAccessChain, 0), 2),
       102);
   ASSERT_TRUE(
       replacement19.IsApplicable(context.get(), transformation_context));
@@ -1078,7 +1088,7 @@
   // The index %17 used for c cannot be replaced
   auto replacement20 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(
-          17, MakeInstructionDescriptor(27, SpvOpAccessChain, 0), 1),
+          17, MakeInstructionDescriptor(27, spv::Op::OpAccessChain, 0), 1),
       102);
   ASSERT_FALSE(
       replacement20.IsApplicable(context.get(), transformation_context));
@@ -1088,7 +1098,7 @@
   // The index %17 used for c cannot be replaced
   auto replacement21 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(
-          17, MakeInstructionDescriptor(46, SpvOpAccessChain, 0), 2),
+          17, MakeInstructionDescriptor(46, spv::Op::OpAccessChain, 0), 2),
       102);
   ASSERT_FALSE(
       replacement21.IsApplicable(context.get(), transformation_context));
@@ -1098,7 +1108,7 @@
   // The index %17 used for 2 *can* be replaced
   auto replacement22 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(
-          17, MakeInstructionDescriptor(56, SpvOpAccessChain, 0), 1),
+          17, MakeInstructionDescriptor(56, spv::Op::OpAccessChain, 0), 1),
       102);
   ASSERT_TRUE(
       replacement22.IsApplicable(context.get(), transformation_context));
@@ -1111,7 +1121,8 @@
   // The index %17 used for c cannot be replaced
   auto replacement23 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(
-          17, MakeInstructionDescriptor(58, SpvOpInBoundsAccessChain, 0), 3),
+          17, MakeInstructionDescriptor(58, spv::Op::OpInBoundsAccessChain, 0),
+          3),
       102);
   ASSERT_FALSE(
       replacement23.IsApplicable(context.get(), transformation_context));
@@ -1123,7 +1134,8 @@
   // The index %57 used for 3 *can* be replaced
   auto replacement24 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(
-          57, MakeInstructionDescriptor(58, SpvOpInBoundsAccessChain, 0), 1),
+          57, MakeInstructionDescriptor(58, spv::Op::OpInBoundsAccessChain, 0),
+          1),
       103);
   ASSERT_TRUE(
       replacement24.IsApplicable(context.get(), transformation_context));
@@ -1138,7 +1150,7 @@
   // The index %32 used for 17 *can* be replaced
   auto replacement25 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(
-          32, MakeInstructionDescriptor(34, SpvOpAccessChain, 0), 1),
+          32, MakeInstructionDescriptor(34, spv::Op::OpAccessChain, 0), 1),
       106);
   ASSERT_TRUE(
       replacement25.IsApplicable(context.get(), transformation_context));
@@ -1153,7 +1165,7 @@
   // The index %43 used for 0 *can* be replaced
   auto replacement26 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(
-          43, MakeInstructionDescriptor(44, SpvOpAccessChain, 0), 3),
+          43, MakeInstructionDescriptor(44, spv::Op::OpAccessChain, 0), 3),
       107);
   ASSERT_TRUE(
       replacement26.IsApplicable(context.get(), transformation_context));
@@ -1168,7 +1180,7 @@
   // The index %55 used for 1 *can* be replaced
   auto replacement27 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(
-          55, MakeInstructionDescriptor(56, SpvOpAccessChain, 0), 4),
+          55, MakeInstructionDescriptor(56, spv::Op::OpAccessChain, 0), 4),
       108);
   ASSERT_TRUE(
       replacement27.IsApplicable(context.get(), transformation_context));
@@ -1182,8 +1194,8 @@
   // Corresponds to d.b[*3*]
   // The index %8 used for 3 *can* be replaced
   auto replacement28 = TransformationReplaceIdWithSynonym(
-      MakeIdUseDescriptor(8, MakeInstructionDescriptor(24, SpvOpAccessChain, 0),
-                          2),
+      MakeIdUseDescriptor(
+          8, MakeInstructionDescriptor(24, spv::Op::OpAccessChain, 0), 2),
       109);
   ASSERT_TRUE(
       replacement28.IsApplicable(context.get(), transformation_context));
@@ -1356,14 +1368,14 @@
   ASSERT_FALSE(
       TransformationReplaceIdWithSynonym(
           MakeIdUseDescriptor(
-              12, MakeInstructionDescriptor(16, SpvOpAccessChain, 0), 1),
+              12, MakeInstructionDescriptor(16, spv::Op::OpAccessChain, 0), 1),
           50)
           .IsApplicable(context.get(), transformation_context));
 
   // Fine to replace an index into a runtime array.
   auto replacement1 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(
-          12, MakeInstructionDescriptor(16, SpvOpAccessChain, 0), 2),
+          12, MakeInstructionDescriptor(16, spv::Op::OpAccessChain, 0), 2),
       50);
   ASSERT_TRUE(replacement1.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(replacement1, context.get(), &transformation_context);
@@ -1371,7 +1383,7 @@
   // Fine to replace an index into a vector inside the runtime array.
   auto replacement2 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(
-          14, MakeInstructionDescriptor(16, SpvOpAccessChain, 0), 3),
+          14, MakeInstructionDescriptor(16, spv::Op::OpAccessChain, 0), 3),
       51);
   ASSERT_TRUE(replacement2.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(replacement2, context.get(), &transformation_context);
@@ -1466,7 +1478,8 @@
   ASSERT_FALSE(
       TransformationReplaceIdWithSynonym(
           MakeIdUseDescriptor(
-              9, MakeInstructionDescriptor(20, SpvOpImageTexelPointer, 0), 2),
+              9, MakeInstructionDescriptor(20, spv::Op::OpImageTexelPointer, 0),
+              2),
           100)
           .IsApplicable(context.get(), transformation_context));
 }
@@ -1524,56 +1537,61 @@
 
   // Legal because OpSNegate always considers the integer as signed
   auto replacement1 = TransformationReplaceIdWithSynonym(
-      MakeIdUseDescriptor(10, MakeInstructionDescriptor(15, SpvOpSNegate, 0),
-                          0),
+      MakeIdUseDescriptor(
+          10, MakeInstructionDescriptor(15, spv::Op::OpSNegate, 0), 0),
       13);
   ASSERT_TRUE(replacement1.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(replacement1, context.get(), &transformation_context);
 
   // Legal because OpIAdd does not care about the signedness of the operands
   auto replacement2 = TransformationReplaceIdWithSynonym(
-      MakeIdUseDescriptor(10, MakeInstructionDescriptor(16, SpvOpIAdd, 0), 0),
+      MakeIdUseDescriptor(10, MakeInstructionDescriptor(16, spv::Op::OpIAdd, 0),
+                          0),
       13);
   ASSERT_TRUE(replacement2.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(replacement2, context.get(), &transformation_context);
 
   // Legal because OpSDiv does not care about the signedness of the operands
   auto replacement3 = TransformationReplaceIdWithSynonym(
-      MakeIdUseDescriptor(10, MakeInstructionDescriptor(17, SpvOpSDiv, 0), 0),
+      MakeIdUseDescriptor(10, MakeInstructionDescriptor(17, spv::Op::OpSDiv, 0),
+                          0),
       13);
   ASSERT_TRUE(replacement3.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(replacement3, context.get(), &transformation_context);
 
   // Not legal because OpUDiv requires unsigned integers
-  ASSERT_FALSE(TransformationReplaceIdWithSynonym(
-                   MakeIdUseDescriptor(
-                       13, MakeInstructionDescriptor(18, SpvOpUDiv, 0), 0),
-                   10)
-                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationReplaceIdWithSynonym(
+          MakeIdUseDescriptor(
+              13, MakeInstructionDescriptor(18, spv::Op::OpUDiv, 0), 0),
+          10)
+          .IsApplicable(context.get(), transformation_context));
 
   // Legal because OpSDiv does not care about the signedness of the operands
   auto replacement4 = TransformationReplaceIdWithSynonym(
-      MakeIdUseDescriptor(10, MakeInstructionDescriptor(19, SpvOpBitwiseAnd, 0),
-                          0),
+      MakeIdUseDescriptor(
+          10, MakeInstructionDescriptor(19, spv::Op::OpBitwiseAnd, 0), 0),
       13);
   ASSERT_TRUE(replacement4.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(replacement4, context.get(), &transformation_context);
 
   // Not legal because OpSelect requires both operands to have the same type as
   // the result type
-  ASSERT_FALSE(TransformationReplaceIdWithSynonym(
-                   MakeIdUseDescriptor(
-                       10, MakeInstructionDescriptor(20, SpvOpUDiv, 0), 1),
-                   13)
-                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationReplaceIdWithSynonym(
+          MakeIdUseDescriptor(
+              10, MakeInstructionDescriptor(20, spv::Op::OpUDiv, 0), 1),
+          13)
+          .IsApplicable(context.get(), transformation_context));
 
   // Not legal because OpStore requires the object to match the type pointed
   // to by the pointer.
-  ASSERT_FALSE(TransformationReplaceIdWithSynonym(
-                   MakeIdUseDescriptor(
-                       10, MakeInstructionDescriptor(21, SpvOpStore, 0), 1),
-                   13)
-                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationReplaceIdWithSynonym(
+          MakeIdUseDescriptor(
+              10, MakeInstructionDescriptor(21, spv::Op::OpStore, 0), 1),
+          13)
+          .IsApplicable(context.get(), transformation_context));
 
   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
                                                kConsoleMessageConsumer));
@@ -1668,18 +1686,20 @@
 
   // Legal because OpIAdd does not consider the signedness of the operands
   auto replacement1 = TransformationReplaceIdWithSynonym(
-      MakeIdUseDescriptor(14, MakeInstructionDescriptor(18, SpvOpIAdd, 0), 0),
+      MakeIdUseDescriptor(14, MakeInstructionDescriptor(18, spv::Op::OpIAdd, 0),
+                          0),
       15);
   ASSERT_TRUE(replacement1.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(replacement1, context.get(), &transformation_context);
 
   // Not legal because OpStore requires the object to match the type pointed
   // to by the pointer.
-  ASSERT_FALSE(TransformationReplaceIdWithSynonym(
-                   MakeIdUseDescriptor(
-                       14, MakeInstructionDescriptor(18, SpvOpStore, 0), 1),
-                   15)
-                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationReplaceIdWithSynonym(
+          MakeIdUseDescriptor(
+              14, MakeInstructionDescriptor(18, spv::Op::OpStore, 0), 1),
+          15)
+          .IsApplicable(context.get(), transformation_context));
 
   // Add synonym fact relating %12 and %13 (equivalent integer constants with
   // different signedness).
@@ -1689,7 +1709,7 @@
   // Legal because the indices of OpAccessChain are always treated as signed
   auto replacement2 = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(
-          13, MakeInstructionDescriptor(19, SpvOpAccessChain, 0), 1),
+          13, MakeInstructionDescriptor(19, spv::Op::OpAccessChain, 0), 1),
       12);
   ASSERT_TRUE(replacement2.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(replacement2, context.get(), &transformation_context);
diff --git a/test/fuzz/transformation_replace_irrelevant_id_test.cpp b/test/fuzz/transformation_replace_irrelevant_id_test.cpp
index c04a091..c5d6981 100644
--- a/test/fuzz/transformation_replace_irrelevant_id_test.cpp
+++ b/test/fuzz/transformation_replace_irrelevant_id_test.cpp
@@ -82,8 +82,9 @@
   SetUpIrrelevantIdFacts(transformation_context.GetFactManager());
 
   auto instruction_21_descriptor =
-      MakeInstructionDescriptor(21, SpvOpAccessChain, 0);
-  auto instruction_24_descriptor = MakeInstructionDescriptor(24, SpvOpIAdd, 0);
+      MakeInstructionDescriptor(21, spv::Op::OpAccessChain, 0);
+  auto instruction_24_descriptor =
+      MakeInstructionDescriptor(24, spv::Op::OpIAdd, 0);
 
   // %20 has not been declared as irrelevant.
   ASSERT_FALSE(TransformationReplaceIrrelevantId(
@@ -132,7 +133,8 @@
       MakeUnique<FactManager>(context.get()), validator_options);
   SetUpIrrelevantIdFacts(transformation_context.GetFactManager());
 
-  auto instruction_24_descriptor = MakeInstructionDescriptor(24, SpvOpIAdd, 0);
+  auto instruction_24_descriptor =
+      MakeInstructionDescriptor(24, spv::Op::OpIAdd, 0);
 
   // Replace the use of %23 in %24 with %22.
   auto transformation = TransformationReplaceIrrelevantId(
@@ -229,11 +231,12 @@
 
   // We cannot replace the use of %13 in the initializer of %12 with %9 because
   // %9 is not a constant.
-  ASSERT_FALSE(TransformationReplaceIrrelevantId(
-                   MakeIdUseDescriptor(
-                       13, MakeInstructionDescriptor(12, SpvOpVariable, 0), 1),
-                   9)
-                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationReplaceIrrelevantId(
+          MakeIdUseDescriptor(
+              13, MakeInstructionDescriptor(12, spv::Op::OpVariable, 0), 1),
+          9)
+          .IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationReplaceIrrelevantIdTest,
@@ -280,7 +283,7 @@
   ASSERT_FALSE(
       TransformationReplaceIrrelevantId(
           MakeIdUseDescriptor(
-              20, MakeInstructionDescriptor(21, SpvOpCopyObject, 0), 0),
+              20, MakeInstructionDescriptor(21, spv::Op::OpCopyObject, 0), 0),
           10)
           .IsApplicable(context.get(), transformation_context));
 }
@@ -326,7 +329,7 @@
   ASSERT_FALSE(
       TransformationReplaceIrrelevantId(
           MakeIdUseDescriptor(
-              10, MakeInstructionDescriptor(12, SpvOpAccessChain, 0), 1),
+              10, MakeInstructionDescriptor(12, spv::Op::OpAccessChain, 0), 1),
           11)
           .IsApplicable(context.get(), transformation_context));
 }
diff --git a/test/fuzz/transformation_replace_linear_algebra_instruction_test.cpp b/test/fuzz/transformation_replace_linear_algebra_instruction_test.cpp
index 8ec5552..2dedea1 100644
--- a/test/fuzz/transformation_replace_linear_algebra_instruction_test.cpp
+++ b/test/fuzz/transformation_replace_linear_algebra_instruction_test.cpp
@@ -76,54 +76,56 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
   // Tests linear algebra instructions.
-  auto instruction_descriptor = MakeInstructionDescriptor(24, SpvOpDot, 0);
+  auto instruction_descriptor =
+      MakeInstructionDescriptor(24, spv::Op::OpDot, 0);
   auto transformation = TransformationReplaceLinearAlgebraInstruction(
       {33, 34, 35, 36, 37, 38}, instruction_descriptor);
   ASSERT_TRUE(
       transformation.IsApplicable(context.get(), transformation_context));
 
   instruction_descriptor =
-      MakeInstructionDescriptor(27, SpvOpVectorTimesScalar, 0);
+      MakeInstructionDescriptor(27, spv::Op::OpVectorTimesScalar, 0);
   transformation = TransformationReplaceLinearAlgebraInstruction(
       {33, 34, 35, 36}, instruction_descriptor);
   ASSERT_TRUE(
       transformation.IsApplicable(context.get(), transformation_context));
 
   // Tests non-linear algebra instructions.
-  instruction_descriptor = MakeInstructionDescriptor(30, SpvOpCopyObject, 0);
+  instruction_descriptor =
+      MakeInstructionDescriptor(30, spv::Op::OpCopyObject, 0);
   transformation = TransformationReplaceLinearAlgebraInstruction(
       {33, 34, 35, 36, 37, 38}, instruction_descriptor);
   ASSERT_FALSE(
       transformation.IsApplicable(context.get(), transformation_context));
 
-  instruction_descriptor = MakeInstructionDescriptor(31, SpvOpFAdd, 0);
+  instruction_descriptor = MakeInstructionDescriptor(31, spv::Op::OpFAdd, 0);
   transformation = TransformationReplaceLinearAlgebraInstruction(
       {33, 34, 35, 36, 37}, instruction_descriptor);
   ASSERT_FALSE(
       transformation.IsApplicable(context.get(), transformation_context));
 
-  instruction_descriptor = MakeInstructionDescriptor(32, SpvOpFMul, 0);
+  instruction_descriptor = MakeInstructionDescriptor(32, spv::Op::OpFMul, 0);
   transformation = TransformationReplaceLinearAlgebraInstruction(
       {33, 34, 35, 36}, instruction_descriptor);
   ASSERT_FALSE(
       transformation.IsApplicable(context.get(), transformation_context));
 
   // Tests number of fresh ids is different than necessary.
-  instruction_descriptor = MakeInstructionDescriptor(25, SpvOpDot, 0);
+  instruction_descriptor = MakeInstructionDescriptor(25, spv::Op::OpDot, 0);
   transformation = TransformationReplaceLinearAlgebraInstruction(
       {33, 34, 35, 36}, instruction_descriptor);
   ASSERT_FALSE(
       transformation.IsApplicable(context.get(), transformation_context));
 
   instruction_descriptor =
-      MakeInstructionDescriptor(28, SpvOpVectorTimesScalar, 0);
+      MakeInstructionDescriptor(28, spv::Op::OpVectorTimesScalar, 0);
   transformation = TransformationReplaceLinearAlgebraInstruction(
       {33, 34, 35, 36, 37, 38, 39}, instruction_descriptor);
   ASSERT_FALSE(
       transformation.IsApplicable(context.get(), transformation_context));
 
   // Tests non-fresh ids.
-  instruction_descriptor = MakeInstructionDescriptor(26, SpvOpDot, 0);
+  instruction_descriptor = MakeInstructionDescriptor(26, spv::Op::OpDot, 0);
   transformation = TransformationReplaceLinearAlgebraInstruction(
       {33, 34, 5, 36, 37, 8, 39, 40, 1, 42, 3, 44, 45, 46},
       instruction_descriptor);
@@ -131,7 +133,7 @@
       transformation.IsApplicable(context.get(), transformation_context));
 
   instruction_descriptor =
-      MakeInstructionDescriptor(29, SpvOpVectorTimesScalar, 0);
+      MakeInstructionDescriptor(29, spv::Op::OpVectorTimesScalar, 0);
   transformation = TransformationReplaceLinearAlgebraInstruction(
       {33, 34, 35, 36, 7, 38, 9, 40}, instruction_descriptor);
   ASSERT_FALSE(
@@ -249,32 +251,36 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
   auto instruction_descriptor =
-      MakeInstructionDescriptor(56, SpvOpTranspose, 0);
+      MakeInstructionDescriptor(56, spv::Op::OpTranspose, 0);
   auto transformation = TransformationReplaceLinearAlgebraInstruction(
       {65, 66, 67, 68, 69, 70, 71, 72, 73, 74}, instruction_descriptor);
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
 
-  instruction_descriptor = MakeInstructionDescriptor(57, SpvOpTranspose, 0);
+  instruction_descriptor =
+      MakeInstructionDescriptor(57, spv::Op::OpTranspose, 0);
   transformation = TransformationReplaceLinearAlgebraInstruction(
       {75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88},
       instruction_descriptor);
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
 
-  instruction_descriptor = MakeInstructionDescriptor(58, SpvOpTranspose, 0);
+  instruction_descriptor =
+      MakeInstructionDescriptor(58, spv::Op::OpTranspose, 0);
   transformation = TransformationReplaceLinearAlgebraInstruction(
       {89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105,
        106},
       instruction_descriptor);
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
 
-  instruction_descriptor = MakeInstructionDescriptor(59, SpvOpTranspose, 0);
+  instruction_descriptor =
+      MakeInstructionDescriptor(59, spv::Op::OpTranspose, 0);
   transformation = TransformationReplaceLinearAlgebraInstruction(
       {107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120,
        121},
       instruction_descriptor);
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
 
-  instruction_descriptor = MakeInstructionDescriptor(60, SpvOpTranspose, 0);
+  instruction_descriptor =
+      MakeInstructionDescriptor(60, spv::Op::OpTranspose, 0);
   transformation = TransformationReplaceLinearAlgebraInstruction(
       {122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132,
        133, 134, 135, 136, 137, 138, 139, 140, 141, 142},
@@ -506,19 +512,19 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
   auto instruction_descriptor =
-      MakeInstructionDescriptor(17, SpvOpVectorTimesScalar, 0);
+      MakeInstructionDescriptor(17, spv::Op::OpVectorTimesScalar, 0);
   auto transformation = TransformationReplaceLinearAlgebraInstruction(
       {20, 21, 22, 23}, instruction_descriptor);
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
 
   instruction_descriptor =
-      MakeInstructionDescriptor(18, SpvOpVectorTimesScalar, 0);
+      MakeInstructionDescriptor(18, spv::Op::OpVectorTimesScalar, 0);
   transformation = TransformationReplaceLinearAlgebraInstruction(
       {24, 25, 26, 27, 28, 29}, instruction_descriptor);
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
 
   instruction_descriptor =
-      MakeInstructionDescriptor(19, SpvOpVectorTimesScalar, 0);
+      MakeInstructionDescriptor(19, spv::Op::OpVectorTimesScalar, 0);
   transformation = TransformationReplaceLinearAlgebraInstruction(
       {30, 31, 32, 33, 34, 35, 36, 37}, instruction_descriptor);
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
@@ -677,20 +683,20 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
   auto instruction_descriptor =
-      MakeInstructionDescriptor(56, SpvOpMatrixTimesScalar, 0);
+      MakeInstructionDescriptor(56, spv::Op::OpMatrixTimesScalar, 0);
   auto transformation = TransformationReplaceLinearAlgebraInstruction(
       {65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76}, instruction_descriptor);
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
 
   instruction_descriptor =
-      MakeInstructionDescriptor(57, SpvOpMatrixTimesScalar, 0);
+      MakeInstructionDescriptor(57, spv::Op::OpMatrixTimesScalar, 0);
   transformation = TransformationReplaceLinearAlgebraInstruction(
       {77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94},
       instruction_descriptor);
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
 
   instruction_descriptor =
-      MakeInstructionDescriptor(58, SpvOpMatrixTimesScalar, 0);
+      MakeInstructionDescriptor(58, spv::Op::OpMatrixTimesScalar, 0);
   transformation = TransformationReplaceLinearAlgebraInstruction(
       {95,  96,  97,  98,  99,  100, 101, 102, 103, 104, 105, 106,
        107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118},
@@ -964,14 +970,14 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
   auto instruction_descriptor =
-      MakeInstructionDescriptor(56, SpvOpVectorTimesMatrix, 0);
+      MakeInstructionDescriptor(56, spv::Op::OpVectorTimesMatrix, 0);
   auto transformation = TransformationReplaceLinearAlgebraInstruction(
       {65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78},
       instruction_descriptor);
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
 
   instruction_descriptor =
-      MakeInstructionDescriptor(57, SpvOpVectorTimesMatrix, 0);
+      MakeInstructionDescriptor(57, spv::Op::OpVectorTimesMatrix, 0);
   transformation = TransformationReplaceLinearAlgebraInstruction(
       {79, 80, 81, 82, 83, 84, 85, 86, 87, 88,
        89, 90, 91, 92, 93, 94, 95, 96, 97, 98},
@@ -979,7 +985,7 @@
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
 
   instruction_descriptor =
-      MakeInstructionDescriptor(58, SpvOpVectorTimesMatrix, 0);
+      MakeInstructionDescriptor(58, spv::Op::OpVectorTimesMatrix, 0);
   transformation = TransformationReplaceLinearAlgebraInstruction(
       {99,  100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
        112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124},
@@ -987,7 +993,7 @@
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
 
   instruction_descriptor =
-      MakeInstructionDescriptor(59, SpvOpVectorTimesMatrix, 0);
+      MakeInstructionDescriptor(59, spv::Op::OpVectorTimesMatrix, 0);
   transformation = TransformationReplaceLinearAlgebraInstruction(
       {125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135,
        136, 137, 138, 139, 140, 141, 142, 143, 144, 145},
@@ -1298,14 +1304,14 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
   auto instruction_descriptor =
-      MakeInstructionDescriptor(56, SpvOpMatrixTimesVector, 0);
+      MakeInstructionDescriptor(56, spv::Op::OpMatrixTimesVector, 0);
   auto transformation = TransformationReplaceLinearAlgebraInstruction(
       {65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78},
       instruction_descriptor);
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
 
   instruction_descriptor =
-      MakeInstructionDescriptor(57, SpvOpMatrixTimesVector, 0);
+      MakeInstructionDescriptor(57, spv::Op::OpMatrixTimesVector, 0);
   transformation = TransformationReplaceLinearAlgebraInstruction(
       {79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96,
        97},
@@ -1313,7 +1319,7 @@
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
 
   instruction_descriptor =
-      MakeInstructionDescriptor(58, SpvOpMatrixTimesVector, 0);
+      MakeInstructionDescriptor(58, spv::Op::OpMatrixTimesVector, 0);
   transformation = TransformationReplaceLinearAlgebraInstruction(
       {98,  99,  100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
        110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121},
@@ -1321,7 +1327,7 @@
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
 
   instruction_descriptor =
-      MakeInstructionDescriptor(59, SpvOpMatrixTimesVector, 0);
+      MakeInstructionDescriptor(59, spv::Op::OpMatrixTimesVector, 0);
   transformation = TransformationReplaceLinearAlgebraInstruction(
       {122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132,
        133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143},
@@ -1684,7 +1690,7 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
   auto instruction_descriptor =
-      MakeInstructionDescriptor(56, SpvOpMatrixTimesMatrix, 0);
+      MakeInstructionDescriptor(56, spv::Op::OpMatrixTimesMatrix, 0);
   auto transformation = TransformationReplaceLinearAlgebraInstruction(
       {83,  84,  85,  86,  87,  88,  89,  90,  91,  92,  93,  94,  95,  96,
        97,  98,  99,  100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110,
@@ -1693,7 +1699,7 @@
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
 
   instruction_descriptor =
-      MakeInstructionDescriptor(57, SpvOpMatrixTimesMatrix, 0);
+      MakeInstructionDescriptor(57, spv::Op::OpMatrixTimesMatrix, 0);
   transformation = TransformationReplaceLinearAlgebraInstruction(
       {123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134,
        135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146,
@@ -1704,7 +1710,7 @@
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
 
   instruction_descriptor =
-      MakeInstructionDescriptor(58, SpvOpMatrixTimesMatrix, 0);
+      MakeInstructionDescriptor(58, spv::Op::OpMatrixTimesMatrix, 0);
   transformation = TransformationReplaceLinearAlgebraInstruction(
       {183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196,
        197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210,
@@ -2157,25 +2163,28 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
   auto instruction_descriptor =
-      MakeInstructionDescriptor(47, SpvOpOuterProduct, 0);
+      MakeInstructionDescriptor(47, spv::Op::OpOuterProduct, 0);
   auto transformation = TransformationReplaceLinearAlgebraInstruction(
       {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67}, instruction_descriptor);
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
 
-  instruction_descriptor = MakeInstructionDescriptor(48, SpvOpOuterProduct, 0);
+  instruction_descriptor =
+      MakeInstructionDescriptor(48, spv::Op::OpOuterProduct, 0);
   transformation = TransformationReplaceLinearAlgebraInstruction(
       {68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85},
       instruction_descriptor);
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
 
-  instruction_descriptor = MakeInstructionDescriptor(49, SpvOpOuterProduct, 0);
+  instruction_descriptor =
+      MakeInstructionDescriptor(49, spv::Op::OpOuterProduct, 0);
   transformation = TransformationReplaceLinearAlgebraInstruction(
       {86, 87, 88,  89,  90,  91,  92,  93,  94,  95,  96,  97,
        98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109},
       instruction_descriptor);
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
 
-  instruction_descriptor = MakeInstructionDescriptor(50, SpvOpOuterProduct, 0);
+  instruction_descriptor =
+      MakeInstructionDescriptor(50, spv::Op::OpOuterProduct, 0);
   transformation = TransformationReplaceLinearAlgebraInstruction(
       {110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123,
        124, 125},
@@ -2393,17 +2402,18 @@
                                                kConsoleMessageConsumer));
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
-  auto instruction_descriptor = MakeInstructionDescriptor(24, SpvOpDot, 0);
+  auto instruction_descriptor =
+      MakeInstructionDescriptor(24, spv::Op::OpDot, 0);
   auto transformation = TransformationReplaceLinearAlgebraInstruction(
       {27, 28, 29, 30, 31, 32}, instruction_descriptor);
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
 
-  instruction_descriptor = MakeInstructionDescriptor(25, SpvOpDot, 0);
+  instruction_descriptor = MakeInstructionDescriptor(25, spv::Op::OpDot, 0);
   transformation = TransformationReplaceLinearAlgebraInstruction(
       {33, 34, 35, 36, 37, 38, 39, 40, 41, 42}, instruction_descriptor);
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
 
-  instruction_descriptor = MakeInstructionDescriptor(26, SpvOpDot, 0);
+  instruction_descriptor = MakeInstructionDescriptor(26, spv::Op::OpDot, 0);
   transformation = TransformationReplaceLinearAlgebraInstruction(
       {43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56},
       instruction_descriptor);
diff --git a/test/fuzz/transformation_replace_load_store_with_copy_memory_test.cpp b/test/fuzz/transformation_replace_load_store_with_copy_memory_test.cpp
index bc10099..74da98b 100644
--- a/test/fuzz/transformation_replace_load_store_with_copy_memory_test.cpp
+++ b/test/fuzz/transformation_replace_load_store_with_copy_memory_test.cpp
@@ -118,29 +118,29 @@
                                                kConsoleMessageConsumer));
 
   auto bad_instruction_descriptor_1 =
-      MakeInstructionDescriptor(11, SpvOpConstant, 0);
+      MakeInstructionDescriptor(11, spv::Op::OpConstant, 0);
 
   auto load_instruction_descriptor_1 =
-      MakeInstructionDescriptor(22, SpvOpLoad, 0);
+      MakeInstructionDescriptor(22, spv::Op::OpLoad, 0);
   auto load_instruction_descriptor_2 =
-      MakeInstructionDescriptor(23, SpvOpLoad, 0);
+      MakeInstructionDescriptor(23, spv::Op::OpLoad, 0);
   auto load_instruction_descriptor_3 =
-      MakeInstructionDescriptor(24, SpvOpLoad, 0);
+      MakeInstructionDescriptor(24, spv::Op::OpLoad, 0);
   auto load_instruction_descriptor_other_block =
-      MakeInstructionDescriptor(34, SpvOpLoad, 0);
+      MakeInstructionDescriptor(34, spv::Op::OpLoad, 0);
   auto load_instruction_descriptor_unsafe =
-      MakeInstructionDescriptor(29, SpvOpLoad, 0);
+      MakeInstructionDescriptor(29, spv::Op::OpLoad, 0);
 
   auto store_instruction_descriptor_1 =
-      MakeInstructionDescriptor(22, SpvOpStore, 0);
+      MakeInstructionDescriptor(22, spv::Op::OpStore, 0);
   auto store_instruction_descriptor_2 =
-      MakeInstructionDescriptor(23, SpvOpStore, 0);
+      MakeInstructionDescriptor(23, spv::Op::OpStore, 0);
   auto store_instruction_descriptor_3 =
-      MakeInstructionDescriptor(24, SpvOpStore, 0);
+      MakeInstructionDescriptor(24, spv::Op::OpStore, 0);
   auto store_instruction_descriptor_other_block =
-      MakeInstructionDescriptor(37, SpvOpStore, 0);
+      MakeInstructionDescriptor(37, spv::Op::OpStore, 0);
   auto store_instruction_descriptor_unsafe =
-      MakeInstructionDescriptor(29, SpvOpStore, 0);
+      MakeInstructionDescriptor(29, spv::Op::OpStore, 0);
 
   // Bad: |load_instruction_descriptor| is incorrect.
   auto transformation_bad_1 = TransformationReplaceLoadStoreWithCopyMemory(
diff --git a/test/fuzz/transformation_set_function_control_test.cpp b/test/fuzz/transformation_set_function_control_test.cpp
index 85402e1..e51e24a 100644
--- a/test/fuzz/transformation_set_function_control_test.cpp
+++ b/test/fuzz/transformation_set_function_control_test.cpp
@@ -124,19 +124,22 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
   // %36 is not a function
-  ASSERT_FALSE(TransformationSetFunctionControl(36, SpvFunctionControlMaskNone)
+  ASSERT_FALSE(TransformationSetFunctionControl(
+                   36, uint32_t(spv::FunctionControlMask::MaskNone))
                    .IsApplicable(context.get(), transformation_context));
   // Cannot add the Pure function control to %4 as it did not already have it
-  ASSERT_FALSE(TransformationSetFunctionControl(4, SpvFunctionControlPureMask)
+  ASSERT_FALSE(TransformationSetFunctionControl(
+                   4, uint32_t(spv::FunctionControlMask::Pure))
                    .IsApplicable(context.get(), transformation_context));
   // Cannot add the Const function control to %21 as it did not already
   // have it
-  ASSERT_FALSE(TransformationSetFunctionControl(21, SpvFunctionControlConstMask)
+  ASSERT_FALSE(TransformationSetFunctionControl(
+                   21, uint32_t(spv::FunctionControlMask::Const))
                    .IsApplicable(context.get(), transformation_context));
 
   // Set to None, removing Const
-  TransformationSetFunctionControl transformation1(11,
-                                                   SpvFunctionControlMaskNone);
+  TransformationSetFunctionControl transformation1(
+      11, uint32_t(spv::FunctionControlMask::MaskNone));
   ASSERT_TRUE(
       transformation1.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation1, context.get(),
@@ -144,15 +147,15 @@
 
   // Set to Inline; silly to do it on an entry point, but it is allowed
   TransformationSetFunctionControl transformation2(
-      4, SpvFunctionControlInlineMask);
+      4, uint32_t(spv::FunctionControlMask::Inline));
   ASSERT_TRUE(
       transformation2.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation2, context.get(),
                         &transformation_context);
 
   // Set to Pure, removing DontInline
-  TransformationSetFunctionControl transformation3(17,
-                                                   SpvFunctionControlPureMask);
+  TransformationSetFunctionControl transformation3(
+      17, uint32_t(spv::FunctionControlMask::Pure));
   ASSERT_TRUE(
       transformation3.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation3, context.get(),
@@ -160,7 +163,7 @@
 
   // Change from Inline to DontInline
   TransformationSetFunctionControl transformation4(
-      13, SpvFunctionControlDontInlineMask);
+      13, uint32_t(spv::FunctionControlMask::DontInline));
   ASSERT_TRUE(
       transformation4.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation4, context.get(),
diff --git a/test/fuzz/transformation_set_loop_control_test.cpp b/test/fuzz/transformation_set_loop_control_test.cpp
index 88b4aab..94a5b36 100644
--- a/test/fuzz/transformation_set_loop_control_test.cpp
+++ b/test/fuzz/transformation_set_loop_control_test.cpp
@@ -278,398 +278,458 @@
   // DependencyLength|MinIterations|MaxIterations|IterationMultiple|PeelCount|PartialCount
   // 2 5 90 4 7 14
 
-  ASSERT_TRUE(TransformationSetLoopControl(10, SpvLoopControlMaskNone, 0, 0)
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  10, (uint32_t)spv::LoopControlMask::MaskNone, 0, 0)
                   .IsApplicable(context.get(), transformation_context));
-  ASSERT_TRUE(TransformationSetLoopControl(10, SpvLoopControlUnrollMask, 0, 0)
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  10, (uint32_t)spv::LoopControlMask::Unroll, 0, 0)
                   .IsApplicable(context.get(), transformation_context));
-  ASSERT_TRUE(
-      TransformationSetLoopControl(10, SpvLoopControlDontUnrollMask, 0, 0)
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  10, (uint32_t)spv::LoopControlMask::DontUnroll, 0, 0)
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationSetLoopControl(
-                   10, SpvLoopControlDependencyInfiniteMask, 0, 0)
+                   10, (uint32_t)spv::LoopControlMask::DependencyInfinite, 0, 0)
                    .IsApplicable(context.get(), transformation_context));
-  ASSERT_FALSE(
-      TransformationSetLoopControl(10, SpvLoopControlDependencyLengthMask, 0, 0)
-          .IsApplicable(context.get(), transformation_context));
-  ASSERT_FALSE(
-      TransformationSetLoopControl(10, SpvLoopControlMinIterationsMask, 0, 0)
-          .IsApplicable(context.get(), transformation_context));
-  ASSERT_FALSE(
-      TransformationSetLoopControl(10, SpvLoopControlMaxIterationsMask, 0, 0)
-          .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationSetLoopControl(
-                   10, SpvLoopControlIterationMultipleMask, 0, 0)
+                   10, (uint32_t)spv::LoopControlMask::DependencyLength, 0, 0)
                    .IsApplicable(context.get(), transformation_context));
-  ASSERT_TRUE(
-      TransformationSetLoopControl(10, SpvLoopControlPeelCountMask, 3, 0)
-          .IsApplicable(context.get(), transformation_context));
-  ASSERT_FALSE(
-      TransformationSetLoopControl(10, SpvLoopControlPeelCountMask, 3, 3)
-          .IsApplicable(context.get(), transformation_context));
-  ASSERT_TRUE(
-      TransformationSetLoopControl(10, SpvLoopControlPartialCountMask, 0, 3)
-          .IsApplicable(context.get(), transformation_context));
-  ASSERT_FALSE(
-      TransformationSetLoopControl(10, SpvLoopControlPartialCountMask, 3, 3)
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationSetLoopControl(
+                   10, (uint32_t)spv::LoopControlMask::MinIterations, 0, 0)
+                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationSetLoopControl(
+                   10, (uint32_t)spv::LoopControlMask::MaxIterations, 0, 0)
+                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationSetLoopControl(
+                   10, (uint32_t)spv::LoopControlMask::IterationMultiple, 0, 0)
+                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  10, (uint32_t)spv::LoopControlMask::PeelCount, 3, 0)
+                  .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationSetLoopControl(
+                   10, (uint32_t)spv::LoopControlMask::PeelCount, 3, 3)
+                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  10, (uint32_t)spv::LoopControlMask::PartialCount, 0, 3)
+                  .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationSetLoopControl(
+                   10, (uint32_t)spv::LoopControlMask::PartialCount, 3, 3)
+                   .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(TransformationSetLoopControl(
                   10,
-                  SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask,
+                  (uint32_t)spv::LoopControlMask::PeelCount |
+                      (uint32_t)spv::LoopControlMask::PartialCount,
                   3, 3)
                   .IsApplicable(context.get(), transformation_context));
-  ASSERT_TRUE(TransformationSetLoopControl(10,
-                                           SpvLoopControlUnrollMask |
-                                               SpvLoopControlPeelCountMask |
-                                               SpvLoopControlPartialCountMask,
-                                           3, 3)
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  10,
+                  (uint32_t)spv::LoopControlMask::Unroll |
+                      (uint32_t)spv::LoopControlMask::PeelCount |
+                      (uint32_t)spv::LoopControlMask::PartialCount,
+                  3, 3)
                   .IsApplicable(context.get(), transformation_context));
-  ASSERT_FALSE(TransformationSetLoopControl(10,
-                                            SpvLoopControlDontUnrollMask |
-                                                SpvLoopControlPeelCountMask |
-                                                SpvLoopControlPartialCountMask,
-                                            3, 3)
+  ASSERT_FALSE(TransformationSetLoopControl(
+                   10,
+                   (uint32_t)spv::LoopControlMask::DontUnroll |
+                       (uint32_t)spv::LoopControlMask::PeelCount |
+                       (uint32_t)spv::LoopControlMask::PartialCount,
+                   3, 3)
                    .IsApplicable(context.get(), transformation_context));
 
-  ASSERT_TRUE(TransformationSetLoopControl(23, SpvLoopControlMaskNone, 0, 0)
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  23, (uint32_t)spv::LoopControlMask::MaskNone, 0, 0)
                   .IsApplicable(context.get(), transformation_context));
-  ASSERT_TRUE(TransformationSetLoopControl(23, SpvLoopControlUnrollMask, 0, 0)
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  23, (uint32_t)spv::LoopControlMask::Unroll, 0, 0)
                   .IsApplicable(context.get(), transformation_context));
-  ASSERT_TRUE(
-      TransformationSetLoopControl(23, SpvLoopControlDontUnrollMask, 0, 0)
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  23, (uint32_t)spv::LoopControlMask::DontUnroll, 0, 0)
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(TransformationSetLoopControl(
                   23,
-                  SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask,
+                  (uint32_t)spv::LoopControlMask::PeelCount |
+                      (uint32_t)spv::LoopControlMask::PartialCount,
                   3, 3)
                   .IsApplicable(context.get(), transformation_context));
-  ASSERT_FALSE(
-      TransformationSetLoopControl(23, SpvLoopControlMaxIterationsMask, 2, 3)
-          .IsApplicable(context.get(), transformation_context));
-
-  ASSERT_TRUE(TransformationSetLoopControl(33, SpvLoopControlMaskNone, 0, 0)
-                  .IsApplicable(context.get(), transformation_context));
-  ASSERT_TRUE(TransformationSetLoopControl(33, SpvLoopControlUnrollMask, 0, 0)
-                  .IsApplicable(context.get(), transformation_context));
-  ASSERT_TRUE(
-      TransformationSetLoopControl(33, SpvLoopControlDontUnrollMask, 0, 0)
-          .IsApplicable(context.get(), transformation_context));
-  ASSERT_FALSE(
-      TransformationSetLoopControl(33, SpvLoopControlMinIterationsMask, 0, 0)
-          .IsApplicable(context.get(), transformation_context));
-  ASSERT_TRUE(
-      TransformationSetLoopControl(
-          33, SpvLoopControlUnrollMask | SpvLoopControlPeelCountMask, 5, 0)
-          .IsApplicable(context.get(), transformation_context));
-  ASSERT_FALSE(TransformationSetLoopControl(33,
-                                            SpvLoopControlDontUnrollMask |
-                                                SpvLoopControlPartialCountMask,
-                                            0, 10)
+  ASSERT_FALSE(TransformationSetLoopControl(
+                   23, (uint32_t)spv::LoopControlMask::MaxIterations, 2, 3)
                    .IsApplicable(context.get(), transformation_context));
 
-  ASSERT_TRUE(TransformationSetLoopControl(43, SpvLoopControlMaskNone, 0, 0)
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  33, (uint32_t)spv::LoopControlMask::MaskNone, 0, 0)
                   .IsApplicable(context.get(), transformation_context));
-  ASSERT_TRUE(TransformationSetLoopControl(43, SpvLoopControlUnrollMask, 0, 0)
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  33, (uint32_t)spv::LoopControlMask::Unroll, 0, 0)
                   .IsApplicable(context.get(), transformation_context));
-  ASSERT_TRUE(
-      TransformationSetLoopControl(43, SpvLoopControlDontUnrollMask, 0, 0)
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  33, (uint32_t)spv::LoopControlMask::DontUnroll, 0, 0)
+                  .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationSetLoopControl(
+                   33, (uint32_t)spv::LoopControlMask::MinIterations, 0, 0)
+                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  33,
+                  (uint32_t)spv::LoopControlMask::Unroll |
+                      (uint32_t)spv::LoopControlMask::PeelCount,
+                  5, 0)
+                  .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationSetLoopControl(
+                   33,
+                   (uint32_t)spv::LoopControlMask::DontUnroll |
+                       (uint32_t)spv::LoopControlMask::PartialCount,
+                   0, 10)
+                   .IsApplicable(context.get(), transformation_context));
+
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  43, (uint32_t)spv::LoopControlMask::MaskNone, 0, 0)
+                  .IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  43, (uint32_t)spv::LoopControlMask::Unroll, 0, 0)
+                  .IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  43, (uint32_t)spv::LoopControlMask::DontUnroll, 0, 0)
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(TransformationSetLoopControl(
                   43,
-                  SpvLoopControlMaskNone | SpvLoopControlDependencyInfiniteMask,
+                  (uint32_t)spv::LoopControlMask::MaskNone |
+                      (uint32_t)spv::LoopControlMask::DependencyInfinite,
                   0, 0)
                   .IsApplicable(context.get(), transformation_context));
-  ASSERT_TRUE(
-      TransformationSetLoopControl(
-          43, SpvLoopControlUnrollMask | SpvLoopControlDependencyInfiniteMask,
-          0, 0)
-          .IsApplicable(context.get(), transformation_context));
-  ASSERT_TRUE(
-      TransformationSetLoopControl(
-          43,
-          SpvLoopControlDontUnrollMask | SpvLoopControlDependencyInfiniteMask,
-          0, 0)
-          .IsApplicable(context.get(), transformation_context));
-  ASSERT_FALSE(
-      TransformationSetLoopControl(43,
-                                   SpvLoopControlDependencyInfiniteMask |
-                                       SpvLoopControlDependencyLengthMask,
-                                   0, 0)
-          .IsApplicable(context.get(), transformation_context));
-  ASSERT_TRUE(
-      TransformationSetLoopControl(
-          43, SpvLoopControlUnrollMask | SpvLoopControlPeelCountMask, 5, 0)
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  43,
+                  (uint32_t)spv::LoopControlMask::Unroll |
+                      (uint32_t)spv::LoopControlMask::DependencyInfinite,
+                  0, 0)
+                  .IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  43,
+                  (uint32_t)spv::LoopControlMask::DontUnroll |
+                      (uint32_t)spv::LoopControlMask::DependencyInfinite,
+                  0, 0)
+                  .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationSetLoopControl(
+                   43,
+                   (uint32_t)spv::LoopControlMask::DependencyInfinite |
+                       (uint32_t)spv::LoopControlMask::DependencyLength,
+                   0, 0)
+                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  43,
+                  (uint32_t)spv::LoopControlMask::Unroll |
+                      (uint32_t)spv::LoopControlMask::PeelCount,
+                  5, 0)
+                  .IsApplicable(context.get(), transformation_context));
 
-  ASSERT_TRUE(TransformationSetLoopControl(53, SpvLoopControlMaskNone, 0, 0)
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  53, (uint32_t)spv::LoopControlMask::MaskNone, 0, 0)
                   .IsApplicable(context.get(), transformation_context));
-  ASSERT_TRUE(TransformationSetLoopControl(53, SpvLoopControlUnrollMask, 0, 0)
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  53, (uint32_t)spv::LoopControlMask::Unroll, 0, 0)
                   .IsApplicable(context.get(), transformation_context));
-  ASSERT_TRUE(
-      TransformationSetLoopControl(53, SpvLoopControlDontUnrollMask, 0, 0)
-          .IsApplicable(context.get(), transformation_context));
-  ASSERT_FALSE(
-      TransformationSetLoopControl(53, SpvLoopControlMaxIterationsMask, 0, 0)
-          .IsApplicable(context.get(), transformation_context));
-  ASSERT_TRUE(
-      TransformationSetLoopControl(
-          53, SpvLoopControlMaskNone | SpvLoopControlDependencyLengthMask, 0, 0)
-          .IsApplicable(context.get(), transformation_context));
-  ASSERT_FALSE(
-      TransformationSetLoopControl(
-          53, SpvLoopControlUnrollMask | SpvLoopControlDependencyInfiniteMask,
-          0, 0)
-          .IsApplicable(context.get(), transformation_context));
-  ASSERT_TRUE(
-      TransformationSetLoopControl(
-          53, SpvLoopControlDontUnrollMask | SpvLoopControlDependencyLengthMask,
-          0, 0)
-          .IsApplicable(context.get(), transformation_context));
-  ASSERT_FALSE(
-      TransformationSetLoopControl(53,
-                                   SpvLoopControlDependencyInfiniteMask |
-                                       SpvLoopControlDependencyLengthMask,
-                                   0, 0)
-          .IsApplicable(context.get(), transformation_context));
-  ASSERT_TRUE(
-      TransformationSetLoopControl(
-          53,
-          SpvLoopControlUnrollMask | SpvLoopControlDependencyLengthMask |
-              SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask,
-          5, 3)
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  53, (uint32_t)spv::LoopControlMask::DontUnroll, 0, 0)
+                  .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationSetLoopControl(
+                   53, (uint32_t)spv::LoopControlMask::MaxIterations, 0, 0)
+                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  53,
+                  (uint32_t)spv::LoopControlMask::MaskNone |
+                      (uint32_t)spv::LoopControlMask::DependencyLength,
+                  0, 0)
+                  .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationSetLoopControl(
+                   53,
+                   (uint32_t)spv::LoopControlMask::Unroll |
+                       (uint32_t)spv::LoopControlMask::DependencyInfinite,
+                   0, 0)
+                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  53,
+                  (uint32_t)spv::LoopControlMask::DontUnroll |
+                      (uint32_t)spv::LoopControlMask::DependencyLength,
+                  0, 0)
+                  .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationSetLoopControl(
+                   53,
+                   (uint32_t)spv::LoopControlMask::DependencyInfinite |
+                       (uint32_t)spv::LoopControlMask::DependencyLength,
+                   0, 0)
+                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  53,
+                  (uint32_t)spv::LoopControlMask::Unroll |
+                      (uint32_t)spv::LoopControlMask::DependencyLength |
+                      (uint32_t)spv::LoopControlMask::PeelCount |
+                      (uint32_t)spv::LoopControlMask::PartialCount,
+                  5, 3)
+                  .IsApplicable(context.get(), transformation_context));
 
-  ASSERT_TRUE(TransformationSetLoopControl(63, SpvLoopControlMaskNone, 0, 0)
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  63, (uint32_t)spv::LoopControlMask::MaskNone, 0, 0)
                   .IsApplicable(context.get(), transformation_context));
-  ASSERT_TRUE(TransformationSetLoopControl(63, SpvLoopControlUnrollMask, 0, 0)
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  63, (uint32_t)spv::LoopControlMask::Unroll, 0, 0)
                   .IsApplicable(context.get(), transformation_context));
-  ASSERT_TRUE(
-      TransformationSetLoopControl(63, SpvLoopControlDontUnrollMask, 0, 0)
-          .IsApplicable(context.get(), transformation_context));
-  ASSERT_TRUE(TransformationSetLoopControl(63,
-                                           SpvLoopControlUnrollMask |
-                                               SpvLoopControlMinIterationsMask |
-                                               SpvLoopControlPeelCountMask |
-                                               SpvLoopControlPartialCountMask,
-                                           5, 3)
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  63, (uint32_t)spv::LoopControlMask::DontUnroll, 0, 0)
                   .IsApplicable(context.get(), transformation_context));
-  ASSERT_TRUE(TransformationSetLoopControl(63,
-                                           SpvLoopControlUnrollMask |
-                                               SpvLoopControlMinIterationsMask |
-                                               SpvLoopControlPeelCountMask,
-                                           23, 0)
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  63,
+                  (uint32_t)spv::LoopControlMask::Unroll |
+                      (uint32_t)spv::LoopControlMask::MinIterations |
+                      (uint32_t)spv::LoopControlMask::PeelCount |
+                      (uint32_t)spv::LoopControlMask::PartialCount,
+                  5, 3)
+                  .IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  63,
+                  (uint32_t)spv::LoopControlMask::Unroll |
+                      (uint32_t)spv::LoopControlMask::MinIterations |
+                      (uint32_t)spv::LoopControlMask::PeelCount,
+                  23, 0)
                   .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationSetLoopControl(
                    63,
-                   SpvLoopControlUnrollMask | SpvLoopControlMinIterationsMask |
-                       SpvLoopControlPeelCountMask,
+                   (uint32_t)spv::LoopControlMask::Unroll |
+                       (uint32_t)spv::LoopControlMask::MinIterations |
+                       (uint32_t)spv::LoopControlMask::PeelCount,
                    2, 23)
                    .IsApplicable(context.get(), transformation_context));
 
-  ASSERT_TRUE(TransformationSetLoopControl(73, SpvLoopControlMaskNone, 0, 0)
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  73, (uint32_t)spv::LoopControlMask::MaskNone, 0, 0)
                   .IsApplicable(context.get(), transformation_context));
-  ASSERT_TRUE(TransformationSetLoopControl(73, SpvLoopControlUnrollMask, 0, 0)
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  73, (uint32_t)spv::LoopControlMask::Unroll, 0, 0)
                   .IsApplicable(context.get(), transformation_context));
-  ASSERT_TRUE(
-      TransformationSetLoopControl(73, SpvLoopControlDontUnrollMask, 0, 0)
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  73, (uint32_t)spv::LoopControlMask::DontUnroll, 0, 0)
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationSetLoopControl(
                    73,
-                   SpvLoopControlUnrollMask | SpvLoopControlMinIterationsMask |
-                       SpvLoopControlPeelCountMask |
-                       SpvLoopControlPartialCountMask,
+                   (uint32_t)spv::LoopControlMask::Unroll |
+                       (uint32_t)spv::LoopControlMask::MinIterations |
+                       (uint32_t)spv::LoopControlMask::PeelCount |
+                       (uint32_t)spv::LoopControlMask::PartialCount,
                    5, 3)
                    .IsApplicable(context.get(), transformation_context));
-  ASSERT_TRUE(TransformationSetLoopControl(73,
-                                           SpvLoopControlUnrollMask |
-                                               SpvLoopControlMaxIterationsMask |
-                                               SpvLoopControlPeelCountMask,
-                                           23, 0)
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  73,
+                  (uint32_t)spv::LoopControlMask::Unroll |
+                      (uint32_t)spv::LoopControlMask::MaxIterations |
+                      (uint32_t)spv::LoopControlMask::PeelCount,
+                  23, 0)
                   .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationSetLoopControl(
                    73,
-                   SpvLoopControlUnrollMask | SpvLoopControlMaxIterationsMask |
-                       SpvLoopControlPeelCountMask,
+                   (uint32_t)spv::LoopControlMask::Unroll |
+                       (uint32_t)spv::LoopControlMask::MaxIterations |
+                       (uint32_t)spv::LoopControlMask::PeelCount,
                    2, 23)
                    .IsApplicable(context.get(), transformation_context));
 
-  ASSERT_TRUE(TransformationSetLoopControl(83, SpvLoopControlMaskNone, 0, 0)
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  83, (uint32_t)spv::LoopControlMask::MaskNone, 0, 0)
                   .IsApplicable(context.get(), transformation_context));
-  ASSERT_TRUE(TransformationSetLoopControl(83, SpvLoopControlUnrollMask, 0, 0)
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  83, (uint32_t)spv::LoopControlMask::Unroll, 0, 0)
                   .IsApplicable(context.get(), transformation_context));
-  ASSERT_TRUE(
-      TransformationSetLoopControl(83, SpvLoopControlDontUnrollMask, 0, 0)
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  83, (uint32_t)spv::LoopControlMask::DontUnroll, 0, 0)
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationSetLoopControl(
                    83,
-                   SpvLoopControlUnrollMask | SpvLoopControlMinIterationsMask |
-                       SpvLoopControlPeelCountMask |
-                       SpvLoopControlPartialCountMask,
+                   (uint32_t)spv::LoopControlMask::Unroll |
+                       (uint32_t)spv::LoopControlMask::MinIterations |
+                       (uint32_t)spv::LoopControlMask::PeelCount |
+                       (uint32_t)spv::LoopControlMask::PartialCount,
                    5, 3)
                    .IsApplicable(context.get(), transformation_context));
-  ASSERT_TRUE(
-      TransformationSetLoopControl(83,
-                                   SpvLoopControlUnrollMask |
-                                       SpvLoopControlIterationMultipleMask |
-                                       SpvLoopControlPeelCountMask,
-                                   23, 0)
-          .IsApplicable(context.get(), transformation_context));
-  ASSERT_FALSE(
-      TransformationSetLoopControl(83,
-                                   SpvLoopControlUnrollMask |
-                                       SpvLoopControlIterationMultipleMask |
-                                       SpvLoopControlPeelCountMask,
-                                   2, 23)
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  83,
+                  (uint32_t)spv::LoopControlMask::Unroll |
+                      (uint32_t)spv::LoopControlMask::IterationMultiple |
+                      (uint32_t)spv::LoopControlMask::PeelCount,
+                  23, 0)
+                  .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationSetLoopControl(
+                   83,
+                   (uint32_t)spv::LoopControlMask::Unroll |
+                       (uint32_t)spv::LoopControlMask::IterationMultiple |
+                       (uint32_t)spv::LoopControlMask::PeelCount,
+                   2, 23)
+                   .IsApplicable(context.get(), transformation_context));
 
-  ASSERT_TRUE(TransformationSetLoopControl(93, SpvLoopControlMaskNone, 0, 0)
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  93, (uint32_t)spv::LoopControlMask::MaskNone, 0, 0)
                   .IsApplicable(context.get(), transformation_context));
-  ASSERT_TRUE(TransformationSetLoopControl(93, SpvLoopControlUnrollMask, 0, 0)
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  93, (uint32_t)spv::LoopControlMask::Unroll, 0, 0)
                   .IsApplicable(context.get(), transformation_context));
-  ASSERT_TRUE(
-      TransformationSetLoopControl(93, SpvLoopControlDontUnrollMask, 0, 0)
-          .IsApplicable(context.get(), transformation_context));
-  ASSERT_TRUE(
-      TransformationSetLoopControl(93, SpvLoopControlPeelCountMask, 8, 0)
-          .IsApplicable(context.get(), transformation_context));
-  ASSERT_FALSE(
-      TransformationSetLoopControl(93, SpvLoopControlPeelCountMask, 8, 8)
-          .IsApplicable(context.get(), transformation_context));
-  ASSERT_TRUE(
-      TransformationSetLoopControl(93, SpvLoopControlPartialCountMask, 0, 8)
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  93, (uint32_t)spv::LoopControlMask::DontUnroll, 0, 0)
+                  .IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  93, (uint32_t)spv::LoopControlMask::PeelCount, 8, 0)
+                  .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationSetLoopControl(
+                   93, (uint32_t)spv::LoopControlMask::PeelCount, 8, 8)
+                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  93, (uint32_t)spv::LoopControlMask::PartialCount, 0, 8)
+                  .IsApplicable(context.get(), transformation_context));
   ASSERT_TRUE(TransformationSetLoopControl(
                   93,
-                  SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask,
+                  (uint32_t)spv::LoopControlMask::PeelCount |
+                      (uint32_t)spv::LoopControlMask::PartialCount,
                   16, 8)
                   .IsApplicable(context.get(), transformation_context));
 
-  ASSERT_TRUE(TransformationSetLoopControl(103, SpvLoopControlMaskNone, 0, 0)
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  103, (uint32_t)spv::LoopControlMask::MaskNone, 0, 0)
                   .IsApplicable(context.get(), transformation_context));
-  ASSERT_TRUE(TransformationSetLoopControl(103, SpvLoopControlUnrollMask, 0, 0)
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  103, (uint32_t)spv::LoopControlMask::Unroll, 0, 0)
                   .IsApplicable(context.get(), transformation_context));
-  ASSERT_TRUE(
-      TransformationSetLoopControl(103, SpvLoopControlDontUnrollMask, 0, 0)
-          .IsApplicable(context.get(), transformation_context));
-  ASSERT_TRUE(
-      TransformationSetLoopControl(103, SpvLoopControlPartialCountMask, 0, 60)
-          .IsApplicable(context.get(), transformation_context));
-  ASSERT_FALSE(TransformationSetLoopControl(103,
-                                            SpvLoopControlDontUnrollMask |
-                                                SpvLoopControlPartialCountMask,
-                                            0, 60)
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  103, (uint32_t)spv::LoopControlMask::DontUnroll, 0, 0)
+                  .IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  103, (uint32_t)spv::LoopControlMask::PartialCount, 0, 60)
+                  .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationSetLoopControl(
+                   103,
+                   (uint32_t)spv::LoopControlMask::DontUnroll |
+                       (uint32_t)spv::LoopControlMask::PartialCount,
+                   0, 60)
                    .IsApplicable(context.get(), transformation_context));
 
-  ASSERT_TRUE(TransformationSetLoopControl(113, SpvLoopControlMaskNone, 0, 0)
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  113, (uint32_t)spv::LoopControlMask::MaskNone, 0, 0)
                   .IsApplicable(context.get(), transformation_context));
-  ASSERT_TRUE(TransformationSetLoopControl(113, SpvLoopControlUnrollMask, 0, 0)
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  113, (uint32_t)spv::LoopControlMask::Unroll, 0, 0)
                   .IsApplicable(context.get(), transformation_context));
-  ASSERT_TRUE(
-      TransformationSetLoopControl(113, SpvLoopControlDontUnrollMask, 0, 0)
-          .IsApplicable(context.get(), transformation_context));
-  ASSERT_TRUE(
-      TransformationSetLoopControl(113, SpvLoopControlPeelCountMask, 12, 0)
-          .IsApplicable(context.get(), transformation_context));
-  ASSERT_FALSE(
-      TransformationSetLoopControl(
-          113,
-          SpvLoopControlIterationMultipleMask | SpvLoopControlPeelCountMask, 12,
-          0)
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  113, (uint32_t)spv::LoopControlMask::DontUnroll, 0, 0)
+                  .IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  113, (uint32_t)spv::LoopControlMask::PeelCount, 12, 0)
+                  .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationSetLoopControl(
+                   113,
+                   (uint32_t)spv::LoopControlMask::IterationMultiple |
+                       (uint32_t)spv::LoopControlMask::PeelCount,
+                   12, 0)
+                   .IsApplicable(context.get(), transformation_context));
 
-  ASSERT_TRUE(TransformationSetLoopControl(123, SpvLoopControlMaskNone, 0, 0)
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  123, (uint32_t)spv::LoopControlMask::MaskNone, 0, 0)
                   .IsApplicable(context.get(), transformation_context));
-  ASSERT_TRUE(TransformationSetLoopControl(123, SpvLoopControlUnrollMask, 0, 0)
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  123, (uint32_t)spv::LoopControlMask::Unroll, 0, 0)
                   .IsApplicable(context.get(), transformation_context));
-  ASSERT_TRUE(
-      TransformationSetLoopControl(123, SpvLoopControlDontUnrollMask, 0, 0)
-          .IsApplicable(context.get(), transformation_context));
-  ASSERT_TRUE(
-      TransformationSetLoopControl(
-          123,
-          SpvLoopControlMinIterationsMask | SpvLoopControlMaxIterationsMask |
-              SpvLoopControlIterationMultipleMask |
-              SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask,
-          7, 8)
-          .IsApplicable(context.get(), transformation_context));
-  ASSERT_TRUE(TransformationSetLoopControl(123,
-                                           SpvLoopControlUnrollMask |
-                                               SpvLoopControlMinIterationsMask |
-                                               SpvLoopControlMaxIterationsMask |
-                                               SpvLoopControlPartialCountMask,
-                                           0, 9)
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  123, (uint32_t)spv::LoopControlMask::DontUnroll, 0, 0)
+                  .IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  123,
+                  (uint32_t)spv::LoopControlMask::MinIterations |
+                      (uint32_t)spv::LoopControlMask::MaxIterations |
+                      (uint32_t)spv::LoopControlMask::IterationMultiple |
+                      (uint32_t)spv::LoopControlMask::PeelCount |
+                      (uint32_t)spv::LoopControlMask::PartialCount,
+                  7, 8)
+                  .IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  123,
+                  (uint32_t)spv::LoopControlMask::Unroll |
+                      (uint32_t)spv::LoopControlMask::MinIterations |
+                      (uint32_t)spv::LoopControlMask::MaxIterations |
+                      (uint32_t)spv::LoopControlMask::PartialCount,
+                  0, 9)
                   .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationSetLoopControl(
                    123,
-                   SpvLoopControlUnrollMask | SpvLoopControlMinIterationsMask |
-                       SpvLoopControlMaxIterationsMask |
-                       SpvLoopControlPartialCountMask,
+                   (uint32_t)spv::LoopControlMask::Unroll |
+                       (uint32_t)spv::LoopControlMask::MinIterations |
+                       (uint32_t)spv::LoopControlMask::MaxIterations |
+                       (uint32_t)spv::LoopControlMask::PartialCount,
                    7, 9)
                    .IsApplicable(context.get(), transformation_context));
-  ASSERT_FALSE(
-      TransformationSetLoopControl(
-          123,
-          SpvLoopControlDontUnrollMask | SpvLoopControlMinIterationsMask |
-              SpvLoopControlMaxIterationsMask | SpvLoopControlPartialCountMask,
-          7, 9)
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationSetLoopControl(
+                   123,
+                   (uint32_t)spv::LoopControlMask::DontUnroll |
+                       (uint32_t)spv::LoopControlMask::MinIterations |
+                       (uint32_t)spv::LoopControlMask::MaxIterations |
+                       (uint32_t)spv::LoopControlMask::PartialCount,
+                   7, 9)
+                   .IsApplicable(context.get(), transformation_context));
 
+  ApplyAndCheckFreshIds(TransformationSetLoopControl(
+                            10,
+                            (uint32_t)spv::LoopControlMask::Unroll |
+                                (uint32_t)spv::LoopControlMask::PeelCount |
+                                (uint32_t)spv::LoopControlMask::PartialCount,
+                            3, 3),
+                        context.get(), &transformation_context);
   ApplyAndCheckFreshIds(
-      TransformationSetLoopControl(10,
-                                   SpvLoopControlUnrollMask |
-                                       SpvLoopControlPeelCountMask |
-                                       SpvLoopControlPartialCountMask,
-                                   3, 3),
+      TransformationSetLoopControl(
+          23, (uint32_t)spv::LoopControlMask::DontUnroll, 0, 0),
       context.get(), &transformation_context);
-  ApplyAndCheckFreshIds(
-      TransformationSetLoopControl(23, SpvLoopControlDontUnrollMask, 0, 0),
-      context.get(), &transformation_context);
-  ApplyAndCheckFreshIds(
-      TransformationSetLoopControl(33, SpvLoopControlUnrollMask, 0, 0),
-      context.get(), &transformation_context);
+  ApplyAndCheckFreshIds(TransformationSetLoopControl(
+                            33, (uint32_t)spv::LoopControlMask::Unroll, 0, 0),
+                        context.get(), &transformation_context);
   ApplyAndCheckFreshIds(
       TransformationSetLoopControl(
           43,
-          SpvLoopControlDontUnrollMask | SpvLoopControlDependencyInfiniteMask,
+          (uint32_t)spv::LoopControlMask::DontUnroll |
+              (uint32_t)spv::LoopControlMask::DependencyInfinite,
           0, 0),
       context.get(), &transformation_context);
+  ApplyAndCheckFreshIds(TransformationSetLoopControl(
+                            53, (uint32_t)spv::LoopControlMask::MaskNone, 0, 0),
+                        context.get(), &transformation_context);
+  ApplyAndCheckFreshIds(TransformationSetLoopControl(
+                            63,
+                            (uint32_t)spv::LoopControlMask::Unroll |
+                                (uint32_t)spv::LoopControlMask::MinIterations |
+                                (uint32_t)spv::LoopControlMask::PeelCount,
+                            23, 0),
+                        context.get(), &transformation_context);
+  ApplyAndCheckFreshIds(TransformationSetLoopControl(
+                            73,
+                            (uint32_t)spv::LoopControlMask::Unroll |
+                                (uint32_t)spv::LoopControlMask::MaxIterations |
+                                (uint32_t)spv::LoopControlMask::PeelCount,
+                            23, 0),
+                        context.get(), &transformation_context);
   ApplyAndCheckFreshIds(
-      TransformationSetLoopControl(53, SpvLoopControlMaskNone, 0, 0),
+      TransformationSetLoopControl(
+          83, (uint32_t)spv::LoopControlMask::DontUnroll, 0, 0),
       context.get(), &transformation_context);
+  ApplyAndCheckFreshIds(TransformationSetLoopControl(
+                            93,
+                            (uint32_t)spv::LoopControlMask::PeelCount |
+                                (uint32_t)spv::LoopControlMask::PartialCount,
+                            16, 8),
+                        context.get(), &transformation_context);
   ApplyAndCheckFreshIds(
-      TransformationSetLoopControl(63,
-                                   SpvLoopControlUnrollMask |
-                                       SpvLoopControlMinIterationsMask |
-                                       SpvLoopControlPeelCountMask,
-                                   23, 0),
-      context.get(), &transformation_context);
-  ApplyAndCheckFreshIds(
-      TransformationSetLoopControl(73,
-                                   SpvLoopControlUnrollMask |
-                                       SpvLoopControlMaxIterationsMask |
-                                       SpvLoopControlPeelCountMask,
-                                   23, 0),
-      context.get(), &transformation_context);
-  ApplyAndCheckFreshIds(
-      TransformationSetLoopControl(83, SpvLoopControlDontUnrollMask, 0, 0),
+      TransformationSetLoopControl(
+          103, (uint32_t)spv::LoopControlMask::PartialCount, 0, 60),
       context.get(), &transformation_context);
   ApplyAndCheckFreshIds(
       TransformationSetLoopControl(
-          93, SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask, 16,
-          8),
+          113, (uint32_t)spv::LoopControlMask::PeelCount, 12, 0),
       context.get(), &transformation_context);
-  ApplyAndCheckFreshIds(
-      TransformationSetLoopControl(103, SpvLoopControlPartialCountMask, 0, 60),
-      context.get(), &transformation_context);
-  ApplyAndCheckFreshIds(
-      TransformationSetLoopControl(113, SpvLoopControlPeelCountMask, 12, 0),
-      context.get(), &transformation_context);
-  ApplyAndCheckFreshIds(
-      TransformationSetLoopControl(
-          123,
-          SpvLoopControlUnrollMask | SpvLoopControlMinIterationsMask |
-              SpvLoopControlMaxIterationsMask | SpvLoopControlPartialCountMask,
-          0, 9),
-      context.get(), &transformation_context);
+  ApplyAndCheckFreshIds(TransformationSetLoopControl(
+                            123,
+                            (uint32_t)spv::LoopControlMask::Unroll |
+                                (uint32_t)spv::LoopControlMask::MinIterations |
+                                (uint32_t)spv::LoopControlMask::MaxIterations |
+                                (uint32_t)spv::LoopControlMask::PartialCount,
+                            0, 9),
+                        context.get(), &transformation_context);
 
   std::string after_transformation = R"(
                OpCapability Shader
@@ -958,10 +1018,10 @@
         context.get(), validator_options, kConsoleMessageConsumer));
     TransformationContext transformation_context(
         MakeUnique<FactManager>(context.get()), validator_options);
-    TransformationSetLoopControl peel_count(10, SpvLoopControlPeelCountMask, 4,
-                                            0);
+    TransformationSetLoopControl peel_count(
+        10, (uint32_t)spv::LoopControlMask::PeelCount, 4, 0);
     TransformationSetLoopControl partial_count(
-        10, SpvLoopControlPartialCountMask, 0, 4);
+        10, (uint32_t)spv::LoopControlMask::PartialCount, 0, 4);
 
     switch (env) {
       case SPV_ENV_UNIVERSAL_1_0:
diff --git a/test/fuzz/transformation_set_memory_operands_mask_test.cpp b/test/fuzz/transformation_set_memory_operands_mask_test.cpp
index 29e57f4..44901f9 100644
--- a/test/fuzz/transformation_set_memory_operands_mask_test.cpp
+++ b/test/fuzz/transformation_set_memory_operands_mask_test.cpp
@@ -106,8 +106,10 @@
     {
       // Not OK: multiple operands are not supported pre SPIR-V 1.4.
       TransformationSetMemoryOperandsMask transformation(
-          MakeInstructionDescriptor(21, SpvOpCopyMemory, 3),
-          SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 1);
+          MakeInstructionDescriptor(21, spv::Op::OpCopyMemory, 3),
+          (uint32_t)spv::MemoryAccessMask::Nontemporal |
+              (uint32_t)spv::MemoryAccessMask::Volatile,
+          1);
       ASSERT_DEATH(
           transformation.IsApplicable(context.get(), transformation_context),
           "Multiple memory operand masks are not supported");
@@ -116,21 +118,24 @@
 
     // Not OK: the instruction is not a memory access.
     ASSERT_FALSE(TransformationSetMemoryOperandsMask(
-                     MakeInstructionDescriptor(21, SpvOpAccessChain, 0),
-                     SpvMemoryAccessMaskNone, 0)
+                     MakeInstructionDescriptor(21, spv::Op::OpAccessChain, 0),
+                     (uint32_t)spv::MemoryAccessMask::MaskNone, 0)
                      .IsApplicable(context.get(), transformation_context));
 
     // Not OK to remove Aligned
-    ASSERT_FALSE(
-        TransformationSetMemoryOperandsMask(
-            MakeInstructionDescriptor(147, SpvOpLoad, 0),
-            SpvMemoryAccessVolatileMask | SpvMemoryAccessNontemporalMask, 0)
-            .IsApplicable(context.get(), transformation_context));
+    ASSERT_FALSE(TransformationSetMemoryOperandsMask(
+                     MakeInstructionDescriptor(147, spv::Op::OpLoad, 0),
+                     (uint32_t)spv::MemoryAccessMask::Volatile |
+                         (uint32_t)spv::MemoryAccessMask::Nontemporal,
+                     0)
+                     .IsApplicable(context.get(), transformation_context));
 
     {
       TransformationSetMemoryOperandsMask transformation(
-          MakeInstructionDescriptor(147, SpvOpLoad, 0),
-          SpvMemoryAccessAlignedMask | SpvMemoryAccessVolatileMask, 0);
+          MakeInstructionDescriptor(147, spv::Op::OpLoad, 0),
+          (uint32_t)spv::MemoryAccessMask::Aligned |
+              (uint32_t)spv::MemoryAccessMask::Volatile,
+          0);
       ASSERT_TRUE(
           transformation.IsApplicable(context.get(), transformation_context));
       ApplyAndCheckFreshIds(transformation, context.get(),
@@ -139,22 +144,23 @@
 
     // Not OK to remove Aligned
     ASSERT_FALSE(TransformationSetMemoryOperandsMask(
-                     MakeInstructionDescriptor(21, SpvOpCopyMemory, 0),
-                     SpvMemoryAccessMaskNone, 0)
+                     MakeInstructionDescriptor(21, spv::Op::OpCopyMemory, 0),
+                     (uint32_t)spv::MemoryAccessMask::MaskNone, 0)
                      .IsApplicable(context.get(), transformation_context));
 
     // OK: leaves the mask as is
     ASSERT_TRUE(TransformationSetMemoryOperandsMask(
-                    MakeInstructionDescriptor(21, SpvOpCopyMemory, 0),
-                    SpvMemoryAccessAlignedMask, 0)
+                    MakeInstructionDescriptor(21, spv::Op::OpCopyMemory, 0),
+                    (uint32_t)spv::MemoryAccessMask::Aligned, 0)
                     .IsApplicable(context.get(), transformation_context));
 
     {
       // OK: adds Nontemporal and Volatile
       TransformationSetMemoryOperandsMask transformation(
-          MakeInstructionDescriptor(21, SpvOpCopyMemory, 0),
-          SpvMemoryAccessAlignedMask | SpvMemoryAccessNontemporalMask |
-              SpvMemoryAccessVolatileMask,
+          MakeInstructionDescriptor(21, spv::Op::OpCopyMemory, 0),
+          (uint32_t)spv::MemoryAccessMask::Aligned |
+              (uint32_t)spv::MemoryAccessMask::Nontemporal |
+              (uint32_t)spv::MemoryAccessMask::Volatile,
           0);
       ASSERT_TRUE(
           transformation.IsApplicable(context.get(), transformation_context));
@@ -164,22 +170,25 @@
 
     // Not OK to remove Volatile
     ASSERT_FALSE(TransformationSetMemoryOperandsMask(
-                     MakeInstructionDescriptor(21, SpvOpCopyMemory, 1),
-                     SpvMemoryAccessNontemporalMask, 0)
+                     MakeInstructionDescriptor(21, spv::Op::OpCopyMemory, 1),
+                     (uint32_t)spv::MemoryAccessMask::Nontemporal, 0)
                      .IsApplicable(context.get(), transformation_context));
 
     // Not OK to add Aligned
     ASSERT_FALSE(TransformationSetMemoryOperandsMask(
-                     MakeInstructionDescriptor(21, SpvOpCopyMemory, 1),
-                     SpvMemoryAccessAlignedMask | SpvMemoryAccessVolatileMask,
+                     MakeInstructionDescriptor(21, spv::Op::OpCopyMemory, 1),
+                     (uint32_t)spv::MemoryAccessMask::Aligned |
+                         (uint32_t)spv::MemoryAccessMask::Volatile,
                      0)
                      .IsApplicable(context.get(), transformation_context));
 
     {
       // OK: adds Nontemporal
       TransformationSetMemoryOperandsMask transformation(
-          MakeInstructionDescriptor(21, SpvOpCopyMemory, 1),
-          SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 0);
+          MakeInstructionDescriptor(21, spv::Op::OpCopyMemory, 1),
+          (uint32_t)spv::MemoryAccessMask::Nontemporal |
+              (uint32_t)spv::MemoryAccessMask::Volatile,
+          0);
       ASSERT_TRUE(
           transformation.IsApplicable(context.get(), transformation_context));
       ApplyAndCheckFreshIds(transformation, context.get(),
@@ -189,8 +198,10 @@
     {
       // OK: adds Nontemporal (creates new operand)
       TransformationSetMemoryOperandsMask transformation(
-          MakeInstructionDescriptor(21, SpvOpCopyMemory, 2),
-          SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 0);
+          MakeInstructionDescriptor(21, spv::Op::OpCopyMemory, 2),
+          (uint32_t)spv::MemoryAccessMask::Nontemporal |
+              (uint32_t)spv::MemoryAccessMask::Volatile,
+          0);
       ASSERT_TRUE(
           transformation.IsApplicable(context.get(), transformation_context));
       ApplyAndCheckFreshIds(transformation, context.get(),
@@ -200,8 +211,10 @@
     {
       // OK: adds Nontemporal and Volatile
       TransformationSetMemoryOperandsMask transformation(
-          MakeInstructionDescriptor(138, SpvOpCopyMemory, 0),
-          SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 0);
+          MakeInstructionDescriptor(138, spv::Op::OpCopyMemory, 0),
+          (uint32_t)spv::MemoryAccessMask::Nontemporal |
+              (uint32_t)spv::MemoryAccessMask::Volatile,
+          0);
       ASSERT_TRUE(
           transformation.IsApplicable(context.get(), transformation_context));
       ApplyAndCheckFreshIds(transformation, context.get(),
@@ -211,8 +224,8 @@
     {
       // OK: removes Nontemporal, adds Volatile
       TransformationSetMemoryOperandsMask transformation(
-          MakeInstructionDescriptor(148, SpvOpStore, 0),
-          SpvMemoryAccessVolatileMask, 0);
+          MakeInstructionDescriptor(148, spv::Op::OpStore, 0),
+          (uint32_t)spv::MemoryAccessMask::Volatile, 0);
       ASSERT_TRUE(
           transformation.IsApplicable(context.get(), transformation_context));
       ApplyAndCheckFreshIds(transformation, context.get(),
@@ -369,12 +382,14 @@
         MakeUnique<FactManager>(context.get()), validator_options);
     {
       TransformationSetMemoryOperandsMask transformation(
-          MakeInstructionDescriptor(21, SpvOpCopyMemory, 0),
-          SpvMemoryAccessAlignedMask | SpvMemoryAccessVolatileMask, 1);
+          MakeInstructionDescriptor(21, spv::Op::OpCopyMemory, 0),
+          (uint32_t)spv::MemoryAccessMask::Aligned |
+              (uint32_t)spv::MemoryAccessMask::Volatile,
+          1);
       // Bad: cannot remove aligned
       ASSERT_FALSE(TransformationSetMemoryOperandsMask(
-                       MakeInstructionDescriptor(21, SpvOpCopyMemory, 0),
-                       SpvMemoryAccessVolatileMask, 1)
+                       MakeInstructionDescriptor(21, spv::Op::OpCopyMemory, 0),
+                       (uint32_t)spv::MemoryAccessMask::Volatile, 1)
                        .IsApplicable(context.get(), transformation_context));
       ASSERT_TRUE(
           transformation.IsApplicable(context.get(), transformation_context));
@@ -384,12 +399,14 @@
 
     {
       TransformationSetMemoryOperandsMask transformation(
-          MakeInstructionDescriptor(21, SpvOpCopyMemory, 1),
-          SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 1);
+          MakeInstructionDescriptor(21, spv::Op::OpCopyMemory, 1),
+          (uint32_t)spv::MemoryAccessMask::Nontemporal |
+              (uint32_t)spv::MemoryAccessMask::Volatile,
+          1);
       // Bad: cannot remove volatile
       ASSERT_FALSE(TransformationSetMemoryOperandsMask(
-                       MakeInstructionDescriptor(21, SpvOpCopyMemory, 1),
-                       SpvMemoryAccessNontemporalMask, 0)
+                       MakeInstructionDescriptor(21, spv::Op::OpCopyMemory, 1),
+                       (uint32_t)spv::MemoryAccessMask::Nontemporal, 0)
                        .IsApplicable(context.get(), transformation_context));
       ASSERT_TRUE(
           transformation.IsApplicable(context.get(), transformation_context));
@@ -400,8 +417,10 @@
     {
       // Creates the first operand.
       TransformationSetMemoryOperandsMask transformation(
-          MakeInstructionDescriptor(21, SpvOpCopyMemory, 2),
-          SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 0);
+          MakeInstructionDescriptor(21, spv::Op::OpCopyMemory, 2),
+          (uint32_t)spv::MemoryAccessMask::Nontemporal |
+              (uint32_t)spv::MemoryAccessMask::Volatile,
+          0);
       ASSERT_TRUE(
           transformation.IsApplicable(context.get(), transformation_context));
       ApplyAndCheckFreshIds(transformation, context.get(),
@@ -411,8 +430,10 @@
     {
       // Creates both operands.
       TransformationSetMemoryOperandsMask transformation(
-          MakeInstructionDescriptor(21, SpvOpCopyMemory, 3),
-          SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 1);
+          MakeInstructionDescriptor(21, spv::Op::OpCopyMemory, 3),
+          (uint32_t)spv::MemoryAccessMask::Nontemporal |
+              (uint32_t)spv::MemoryAccessMask::Volatile,
+          1);
       ASSERT_TRUE(
           transformation.IsApplicable(context.get(), transformation_context));
       ApplyAndCheckFreshIds(transformation, context.get(),
@@ -421,14 +442,17 @@
 
     {
       TransformationSetMemoryOperandsMask transformation(
-          MakeInstructionDescriptor(138, SpvOpCopyMemory, 0),
-          SpvMemoryAccessAlignedMask | SpvMemoryAccessNontemporalMask, 1);
+          MakeInstructionDescriptor(138, spv::Op::OpCopyMemory, 0),
+          (uint32_t)spv::MemoryAccessMask::Aligned |
+              (uint32_t)spv::MemoryAccessMask::Nontemporal,
+          1);
       // Bad: the first mask is None, so Aligned cannot be added to it.
-      ASSERT_FALSE(
-          TransformationSetMemoryOperandsMask(
-              MakeInstructionDescriptor(138, SpvOpCopyMemory, 0),
-              SpvMemoryAccessAlignedMask | SpvMemoryAccessNontemporalMask, 0)
-              .IsApplicable(context.get(), transformation_context));
+      ASSERT_FALSE(TransformationSetMemoryOperandsMask(
+                       MakeInstructionDescriptor(138, spv::Op::OpCopyMemory, 0),
+                       (uint32_t)spv::MemoryAccessMask::Aligned |
+                           (uint32_t)spv::MemoryAccessMask::Nontemporal,
+                       0)
+                       .IsApplicable(context.get(), transformation_context));
       ASSERT_TRUE(
           transformation.IsApplicable(context.get(), transformation_context));
       ApplyAndCheckFreshIds(transformation, context.get(),
@@ -437,8 +461,8 @@
 
     {
       TransformationSetMemoryOperandsMask transformation(
-          MakeInstructionDescriptor(138, SpvOpCopyMemory, 1),
-          SpvMemoryAccessVolatileMask, 1);
+          MakeInstructionDescriptor(138, spv::Op::OpCopyMemory, 1),
+          (uint32_t)spv::MemoryAccessMask::Volatile, 1);
       ASSERT_TRUE(
           transformation.IsApplicable(context.get(), transformation_context));
       ApplyAndCheckFreshIds(transformation, context.get(),
@@ -447,8 +471,10 @@
 
     {
       TransformationSetMemoryOperandsMask transformation(
-          MakeInstructionDescriptor(147, SpvOpLoad, 0),
-          SpvMemoryAccessVolatileMask | SpvMemoryAccessAlignedMask, 0);
+          MakeInstructionDescriptor(147, spv::Op::OpLoad, 0),
+          (uint32_t)spv::MemoryAccessMask::Volatile |
+              (uint32_t)spv::MemoryAccessMask::Aligned,
+          0);
       ASSERT_TRUE(
           transformation.IsApplicable(context.get(), transformation_context));
       ApplyAndCheckFreshIds(transformation, context.get(),
@@ -457,8 +483,8 @@
 
     {
       TransformationSetMemoryOperandsMask transformation(
-          MakeInstructionDescriptor(148, SpvOpStore, 0),
-          SpvMemoryAccessMaskNone, 0);
+          MakeInstructionDescriptor(148, spv::Op::OpStore, 0),
+          (uint32_t)spv::MemoryAccessMask::MaskNone, 0);
       ASSERT_TRUE(
           transformation.IsApplicable(context.get(), transformation_context));
       ApplyAndCheckFreshIds(transformation, context.get(),
diff --git a/test/fuzz/transformation_set_selection_control_test.cpp b/test/fuzz/transformation_set_selection_control_test.cpp
index c584ff1..4cecd23 100644
--- a/test/fuzz/transformation_set_selection_control_test.cpp
+++ b/test/fuzz/transformation_set_selection_control_test.cpp
@@ -109,41 +109,41 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
   // %44 is not a block
-  ASSERT_FALSE(
-      TransformationSetSelectionControl(44, SpvSelectionControlFlattenMask)
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationSetSelectionControl(
+                   44, uint32_t(spv::SelectionControlMask::Flatten))
+                   .IsApplicable(context.get(), transformation_context));
   // %13 does not end with OpSelectionMerge
-  ASSERT_FALSE(
-      TransformationSetSelectionControl(13, SpvSelectionControlMaskNone)
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationSetSelectionControl(
+                   13, uint32_t(spv::SelectionControlMask::MaskNone))
+                   .IsApplicable(context.get(), transformation_context));
   // %10 ends in OpLoopMerge, not OpSelectionMerge
-  ASSERT_FALSE(
-      TransformationSetSelectionControl(10, SpvSelectionControlMaskNone)
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationSetSelectionControl(
+                   10, uint32_t(spv::SelectionControlMask::MaskNone))
+                   .IsApplicable(context.get(), transformation_context));
 
   TransformationSetSelectionControl transformation1(
-      11, SpvSelectionControlDontFlattenMask);
+      11, uint32_t(spv::SelectionControlMask::DontFlatten));
   ASSERT_TRUE(
       transformation1.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation1, context.get(),
                         &transformation_context);
 
   TransformationSetSelectionControl transformation2(
-      23, SpvSelectionControlFlattenMask);
+      23, uint32_t(spv::SelectionControlMask::Flatten));
   ASSERT_TRUE(
       transformation2.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation2, context.get(),
                         &transformation_context);
 
   TransformationSetSelectionControl transformation3(
-      31, SpvSelectionControlMaskNone);
+      31, uint32_t(spv::SelectionControlMask::MaskNone));
   ASSERT_TRUE(
       transformation3.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation3, context.get(),
                         &transformation_context);
 
   TransformationSetSelectionControl transformation4(
-      31, SpvSelectionControlFlattenMask);
+      31, uint32_t(spv::SelectionControlMask::Flatten));
   ASSERT_TRUE(
       transformation4.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation4, context.get(),
diff --git a/test/fuzz/transformation_split_block_test.cpp b/test/fuzz/transformation_split_block_test.cpp
index 55091de..1cef182 100644
--- a/test/fuzz/transformation_split_block_test.cpp
+++ b/test/fuzz/transformation_split_block_test.cpp
@@ -96,53 +96,54 @@
       MakeUnique<FactManager>(context.get()), validator_options);
   // No split before OpVariable
   ASSERT_FALSE(TransformationSplitBlock(
-                   MakeInstructionDescriptor(8, SpvOpVariable, 0), 100)
+                   MakeInstructionDescriptor(8, spv::Op::OpVariable, 0), 100)
                    .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationSplitBlock(
-                   MakeInstructionDescriptor(8, SpvOpVariable, 1), 100)
+                   MakeInstructionDescriptor(8, spv::Op::OpVariable, 1), 100)
                    .IsApplicable(context.get(), transformation_context));
 
   // No split before OpLabel
   ASSERT_FALSE(TransformationSplitBlock(
-                   MakeInstructionDescriptor(14, SpvOpLabel, 0), 100)
+                   MakeInstructionDescriptor(14, spv::Op::OpLabel, 0), 100)
                    .IsApplicable(context.get(), transformation_context));
 
   // No split if base instruction is outside a function
-  ASSERT_FALSE(
-      TransformationSplitBlock(MakeInstructionDescriptor(1, SpvOpLabel, 0), 100)
-          .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationSplitBlock(
-                   MakeInstructionDescriptor(1, SpvOpExecutionMode, 0), 100)
+                   MakeInstructionDescriptor(1, spv::Op::OpLabel, 0), 100)
                    .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationSplitBlock(
+          MakeInstructionDescriptor(1, spv::Op::OpExecutionMode, 0), 100)
+          .IsApplicable(context.get(), transformation_context));
 
   // No split if block is loop header
-  ASSERT_FALSE(
-      TransformationSplitBlock(MakeInstructionDescriptor(27, SpvOpPhi, 0), 100)
-          .IsApplicable(context.get(), transformation_context));
-  ASSERT_FALSE(
-      TransformationSplitBlock(MakeInstructionDescriptor(27, SpvOpPhi, 1), 100)
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationSplitBlock(
+                   MakeInstructionDescriptor(27, spv::Op::OpPhi, 0), 100)
+                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationSplitBlock(
+                   MakeInstructionDescriptor(27, spv::Op::OpPhi, 1), 100)
+                   .IsApplicable(context.get(), transformation_context));
 
   // No split if base instruction does not exist
-  ASSERT_FALSE(
-      TransformationSplitBlock(MakeInstructionDescriptor(88, SpvOpIAdd, 0), 100)
-          .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationSplitBlock(
-                   MakeInstructionDescriptor(88, SpvOpIMul, 22), 100)
+                   MakeInstructionDescriptor(88, spv::Op::OpIAdd, 0), 100)
+                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationSplitBlock(
+                   MakeInstructionDescriptor(88, spv::Op::OpIMul, 22), 100)
                    .IsApplicable(context.get(), transformation_context));
 
   // No split if too many instructions with the desired opcode are skipped
   ASSERT_FALSE(
       TransformationSplitBlock(
-          MakeInstructionDescriptor(18, SpvOpBranchConditional, 1), 100)
+          MakeInstructionDescriptor(18, spv::Op::OpBranchConditional, 1), 100)
           .IsApplicable(context.get(), transformation_context));
 
   // No split if id in use
   ASSERT_FALSE(TransformationSplitBlock(
-                   MakeInstructionDescriptor(18, SpvOpSLessThan, 0), 27)
+                   MakeInstructionDescriptor(18, spv::Op::OpSLessThan, 0), 27)
                    .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationSplitBlock(
-                   MakeInstructionDescriptor(18, SpvOpSLessThan, 0), 14)
+                   MakeInstructionDescriptor(18, spv::Op::OpSLessThan, 0), 14)
                    .IsApplicable(context.get(), transformation_context));
 }
 
@@ -206,7 +207,7 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
   auto split_1 = TransformationSplitBlock(
-      MakeInstructionDescriptor(5, SpvOpStore, 0), 100);
+      MakeInstructionDescriptor(5, spv::Op::OpStore, 0), 100);
   ASSERT_TRUE(split_1.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(split_1, context.get(), &transformation_context);
   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
@@ -255,7 +256,7 @@
   ASSERT_TRUE(IsEqual(env, after_split_1, context.get()));
 
   auto split_2 = TransformationSplitBlock(
-      MakeInstructionDescriptor(11, SpvOpStore, 0), 101);
+      MakeInstructionDescriptor(11, spv::Op::OpStore, 0), 101);
   ASSERT_TRUE(split_2.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(split_2, context.get(), &transformation_context);
   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
@@ -306,7 +307,7 @@
   ASSERT_TRUE(IsEqual(env, after_split_2, context.get()));
 
   auto split_3 = TransformationSplitBlock(
-      MakeInstructionDescriptor(14, SpvOpLoad, 0), 102);
+      MakeInstructionDescriptor(14, spv::Op::OpLoad, 0), 102);
   ASSERT_TRUE(split_3.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(split_3, context.get(), &transformation_context);
   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
@@ -425,15 +426,15 @@
   // Illegal to split between the merge and the conditional branch.
   ASSERT_FALSE(
       TransformationSplitBlock(
-          MakeInstructionDescriptor(14, SpvOpBranchConditional, 0), 100)
+          MakeInstructionDescriptor(14, spv::Op::OpBranchConditional, 0), 100)
           .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       TransformationSplitBlock(
-          MakeInstructionDescriptor(12, SpvOpBranchConditional, 0), 100)
+          MakeInstructionDescriptor(12, spv::Op::OpBranchConditional, 0), 100)
           .IsApplicable(context.get(), transformation_context));
 
   auto split = TransformationSplitBlock(
-      MakeInstructionDescriptor(14, SpvOpSelectionMerge, 0), 100);
+      MakeInstructionDescriptor(14, spv::Op::OpSelectionMerge, 0), 100);
   ASSERT_TRUE(split.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(split, context.get(), &transformation_context);
   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
@@ -555,14 +556,14 @@
       MakeUnique<FactManager>(context.get()), validator_options);
   // Illegal to split between the merge and the conditional branch.
   ASSERT_FALSE(TransformationSplitBlock(
-                   MakeInstructionDescriptor(9, SpvOpSwitch, 0), 100)
+                   MakeInstructionDescriptor(9, spv::Op::OpSwitch, 0), 100)
                    .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(TransformationSplitBlock(
-                   MakeInstructionDescriptor(15, SpvOpSwitch, 0), 100)
+                   MakeInstructionDescriptor(15, spv::Op::OpSwitch, 0), 100)
                    .IsApplicable(context.get(), transformation_context));
 
   auto split = TransformationSplitBlock(
-      MakeInstructionDescriptor(9, SpvOpSelectionMerge, 0), 100);
+      MakeInstructionDescriptor(9, spv::Op::OpSelectionMerge, 0), 100);
   ASSERT_TRUE(split.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(split, context.get(), &transformation_context);
   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
@@ -690,15 +691,15 @@
       MakeUnique<FactManager>(context.get()), validator_options);
   // We cannot split before OpPhi instructions, since the number of incoming
   // blocks may not appropriately match after splitting.
-  ASSERT_FALSE(
-      TransformationSplitBlock(MakeInstructionDescriptor(26, SpvOpPhi, 0), 100)
-          .IsApplicable(context.get(), transformation_context));
-  ASSERT_FALSE(
-      TransformationSplitBlock(MakeInstructionDescriptor(27, SpvOpPhi, 0), 100)
-          .IsApplicable(context.get(), transformation_context));
-  ASSERT_FALSE(
-      TransformationSplitBlock(MakeInstructionDescriptor(27, SpvOpPhi, 1), 100)
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationSplitBlock(
+                   MakeInstructionDescriptor(26, spv::Op::OpPhi, 0), 100)
+                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationSplitBlock(
+                   MakeInstructionDescriptor(27, spv::Op::OpPhi, 0), 100)
+                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationSplitBlock(
+                   MakeInstructionDescriptor(27, spv::Op::OpPhi, 1), 100)
+                   .IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationSplitBlockTest, SplitOpPhiWithSinglePredecessor) {
@@ -741,13 +742,13 @@
   spvtools::ValidatorOptions validator_options;
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
-  ASSERT_TRUE(
-      TransformationSplitBlock(MakeInstructionDescriptor(21, SpvOpPhi, 0), 100)
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(TransformationSplitBlock(
+                  MakeInstructionDescriptor(21, spv::Op::OpPhi, 0), 100)
+                  .IsApplicable(context.get(), transformation_context));
   // An equivalent transformation to the above, just described with respect to a
   // different base instruction.
-  auto split =
-      TransformationSplitBlock(MakeInstructionDescriptor(20, SpvOpPhi, 0), 100);
+  auto split = TransformationSplitBlock(
+      MakeInstructionDescriptor(20, spv::Op::OpPhi, 0), 100);
   ASSERT_TRUE(split.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(split, context.get(), &transformation_context);
   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
@@ -826,7 +827,7 @@
   transformation_context.GetFactManager()->AddFactBlockIsDead(8);
 
   auto split = TransformationSplitBlock(
-      MakeInstructionDescriptor(8, SpvOpBranch, 0), 100);
+      MakeInstructionDescriptor(8, spv::Op::OpBranch, 0), 100);
   ASSERT_TRUE(split.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(split, context.get(), &transformation_context);
   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
@@ -912,7 +913,8 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
   auto split = TransformationSplitBlock(
-      MakeInstructionDescriptor(217, SpvOpImageSampleImplicitLod, 0), 500);
+      MakeInstructionDescriptor(217, spv::Op::OpImageSampleImplicitLod, 0),
+      500);
   ASSERT_FALSE(split.IsApplicable(context.get(), transformation_context));
 }
 
diff --git a/test/fuzz/transformation_store_test.cpp b/test/fuzz/transformation_store_test.cpp
index dd653e2..fe24d74 100644
--- a/test/fuzz/transformation_store_test.cpp
+++ b/test/fuzz/transformation_store_test.cpp
@@ -148,107 +148,107 @@
   //  61 - undefined
 
   // Bad: attempt to store to 11 from outside its function
-  ASSERT_FALSE(
-      TransformationStore(11, false, 0, 0, 80,
-                          MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationStore(
+                   11, false, 0, 0, 80,
+                   MakeInstructionDescriptor(38, spv::Op::OpAccessChain, 0))
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: pointer is not available
-  ASSERT_FALSE(
-      TransformationStore(81, false, 0, 0, 80,
-                          MakeInstructionDescriptor(45, SpvOpCopyObject, 0))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationStore(
+                   81, false, 0, 0, 80,
+                   MakeInstructionDescriptor(45, spv::Op::OpCopyObject, 0))
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: attempt to insert before OpVariable
   ASSERT_FALSE(
       TransformationStore(52, false, 0, 0, 24,
-                          MakeInstructionDescriptor(27, SpvOpVariable, 0))
+                          MakeInstructionDescriptor(27, spv::Op::OpVariable, 0))
           .IsApplicable(context.get(), transformation_context));
 
   // Bad: pointer id does not exist
-  ASSERT_FALSE(
-      TransformationStore(1000, false, 0, 0, 24,
-                          MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationStore(
+                   1000, false, 0, 0, 24,
+                   MakeInstructionDescriptor(38, spv::Op::OpAccessChain, 0))
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: pointer id exists but does not have a type
-  ASSERT_FALSE(
-      TransformationStore(5, false, 0, 0, 24,
-                          MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationStore(
+                   5, false, 0, 0, 24,
+                   MakeInstructionDescriptor(38, spv::Op::OpAccessChain, 0))
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: pointer id exists and has a type, but is not a pointer
-  ASSERT_FALSE(
-      TransformationStore(24, false, 0, 0, 24,
-                          MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationStore(
+                   24, false, 0, 0, 24,
+                   MakeInstructionDescriptor(38, spv::Op::OpAccessChain, 0))
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: attempt to store to a null pointer
-  ASSERT_FALSE(
-      TransformationStore(60, false, 0, 0, 24,
-                          MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationStore(
+                   60, false, 0, 0, 24,
+                   MakeInstructionDescriptor(38, spv::Op::OpAccessChain, 0))
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: attempt to store to an undefined pointer
-  ASSERT_FALSE(
-      TransformationStore(61, false, 0, 0, 21,
-                          MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationStore(
+                   61, false, 0, 0, 21,
+                   MakeInstructionDescriptor(38, spv::Op::OpAccessChain, 0))
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: %82 is not available at the program point
   ASSERT_FALSE(
       TransformationStore(82, false, 0, 0, 80,
-                          MakeInstructionDescriptor(37, SpvOpReturn, 0))
+                          MakeInstructionDescriptor(37, spv::Op::OpReturn, 0))
           .IsApplicable(context.get(), transformation_context));
 
   // Bad: value id does not exist
-  ASSERT_FALSE(
-      TransformationStore(27, false, 0, 0, 1000,
-                          MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationStore(
+                   27, false, 0, 0, 1000,
+                   MakeInstructionDescriptor(38, spv::Op::OpAccessChain, 0))
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: value id exists but does not have a type
-  ASSERT_FALSE(
-      TransformationStore(27, false, 0, 0, 15,
-                          MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationStore(
+                   27, false, 0, 0, 15,
+                   MakeInstructionDescriptor(38, spv::Op::OpAccessChain, 0))
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: value id exists but has the wrong type
-  ASSERT_FALSE(
-      TransformationStore(27, false, 0, 0, 14,
-                          MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationStore(
+                   27, false, 0, 0, 14,
+                   MakeInstructionDescriptor(38, spv::Op::OpAccessChain, 0))
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: attempt to store to read-only variable
-  ASSERT_FALSE(
-      TransformationStore(92, false, 0, 0, 93,
-                          MakeInstructionDescriptor(40, SpvOpAccessChain, 0))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationStore(
+                   92, false, 0, 0, 93,
+                   MakeInstructionDescriptor(40, spv::Op::OpAccessChain, 0))
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: value is not available
-  ASSERT_FALSE(
-      TransformationStore(27, false, 0, 0, 95,
-                          MakeInstructionDescriptor(40, SpvOpAccessChain, 0))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationStore(
+                   27, false, 0, 0, 95,
+                   MakeInstructionDescriptor(40, spv::Op::OpAccessChain, 0))
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: variable being stored to does not have an irrelevant pointee value,
   // and the store is not in a dead block.
-  ASSERT_FALSE(
-      TransformationStore(20, false, 0, 0, 95,
-                          MakeInstructionDescriptor(45, SpvOpCopyObject, 0))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationStore(
+                   20, false, 0, 0, 95,
+                   MakeInstructionDescriptor(45, spv::Op::OpCopyObject, 0))
+                   .IsApplicable(context.get(), transformation_context));
 
   // The described instruction does not exist.
-  ASSERT_FALSE(
-      TransformationStore(27, false, 0, 0, 80,
-                          MakeInstructionDescriptor(1000, SpvOpAccessChain, 0))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationStore(
+                   27, false, 0, 0, 80,
+                   MakeInstructionDescriptor(1000, spv::Op::OpAccessChain, 0))
+                   .IsApplicable(context.get(), transformation_context));
 
   {
     // Store to irrelevant variable from dead block.
     TransformationStore transformation(
         27, false, 0, 0, 80,
-        MakeInstructionDescriptor(38, SpvOpAccessChain, 0));
+        MakeInstructionDescriptor(38, spv::Op::OpAccessChain, 0));
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
@@ -261,7 +261,7 @@
     // Store to irrelevant variable from live block.
     TransformationStore transformation(
         11, false, 0, 0, 95,
-        MakeInstructionDescriptor(95, SpvOpReturnValue, 0));
+        MakeInstructionDescriptor(95, spv::Op::OpReturnValue, 0));
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
@@ -274,7 +274,7 @@
     // Store to irrelevant variable from live block.
     TransformationStore transformation(
         46, false, 0, 0, 80,
-        MakeInstructionDescriptor(95, SpvOpReturnValue, 0));
+        MakeInstructionDescriptor(95, spv::Op::OpReturnValue, 0));
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
@@ -287,7 +287,7 @@
     // Store to irrelevant variable from live block.
     TransformationStore transformation(
         16, false, 0, 0, 21,
-        MakeInstructionDescriptor(95, SpvOpReturnValue, 0));
+        MakeInstructionDescriptor(95, spv::Op::OpReturnValue, 0));
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
@@ -300,7 +300,7 @@
     // Store to non-irrelevant variable from dead block.
     TransformationStore transformation(
         53, false, 0, 0, 21,
-        MakeInstructionDescriptor(38, SpvOpAccessChain, 0));
+        MakeInstructionDescriptor(38, spv::Op::OpAccessChain, 0));
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
@@ -436,15 +436,15 @@
 
   ASSERT_FALSE(
       TransformationStore(15, false, 0, 0, 13,
-                          MakeInstructionDescriptor(27, SpvOpReturn, 0))
+                          MakeInstructionDescriptor(27, spv::Op::OpReturn, 0))
           .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       TransformationStore(19, false, 0, 0, 50,
-                          MakeInstructionDescriptor(27, SpvOpReturn, 0))
+                          MakeInstructionDescriptor(27, spv::Op::OpReturn, 0))
           .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       TransformationStore(27, false, 0, 0, 50,
-                          MakeInstructionDescriptor(27, SpvOpReturn, 0))
+                          MakeInstructionDescriptor(27, spv::Op::OpReturn, 0))
           .IsApplicable(context.get(), transformation_context));
 }
 
@@ -495,84 +495,85 @@
       14);
 
   // Bad: id 100 of memory scope instruction does not exist.
-  ASSERT_FALSE(
-      TransformationStore(14, true, 100, 20, 21,
-                          MakeInstructionDescriptor(24, SpvOpAccessChain, 0))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationStore(
+                   14, true, 100, 20, 21,
+                   MakeInstructionDescriptor(24, spv::Op::OpAccessChain, 0))
+                   .IsApplicable(context.get(), transformation_context));
   // Bad: id 100 of memory semantics instruction does not exist.
-  ASSERT_FALSE(
-      TransformationStore(14, true, 15, 100, 21,
-                          MakeInstructionDescriptor(24, SpvOpAccessChain, 0))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationStore(
+                   14, true, 15, 100, 21,
+                   MakeInstructionDescriptor(24, spv::Op::OpAccessChain, 0))
+                   .IsApplicable(context.get(), transformation_context));
   // Bad: memory scope should be |OpConstant| opcode.
-  ASSERT_FALSE(
-      TransformationStore(14, true, 5, 20, 21,
-                          MakeInstructionDescriptor(24, SpvOpAccessChain, 0))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationStore(
+                   14, true, 5, 20, 21,
+                   MakeInstructionDescriptor(24, spv::Op::OpAccessChain, 0))
+                   .IsApplicable(context.get(), transformation_context));
   // Bad: memory semantics should be |OpConstant| opcode.
-  ASSERT_FALSE(
-      TransformationStore(14, true, 15, 5, 21,
-                          MakeInstructionDescriptor(24, SpvOpAccessChain, 0))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationStore(
+                   14, true, 15, 5, 21,
+                   MakeInstructionDescriptor(24, spv::Op::OpAccessChain, 0))
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: The memory scope instruction must have an Integer operand.
-  ASSERT_FALSE(
-      TransformationStore(14, true, 15, 19, 21,
-                          MakeInstructionDescriptor(24, SpvOpAccessChain, 0))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationStore(
+                   14, true, 15, 19, 21,
+                   MakeInstructionDescriptor(24, spv::Op::OpAccessChain, 0))
+                   .IsApplicable(context.get(), transformation_context));
   // Bad: The memory memory semantics instruction must have an Integer operand.
-  ASSERT_FALSE(
-      TransformationStore(14, true, 19, 20, 21,
-                          MakeInstructionDescriptor(24, SpvOpAccessChain, 0))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationStore(
+                   14, true, 19, 20, 21,
+                   MakeInstructionDescriptor(24, spv::Op::OpAccessChain, 0))
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: Integer size of the memory scope must be equal to 32 bits.
-  ASSERT_FALSE(
-      TransformationStore(14, true, 17, 20, 21,
-                          MakeInstructionDescriptor(24, SpvOpAccessChain, 0))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationStore(
+                   14, true, 17, 20, 21,
+                   MakeInstructionDescriptor(24, spv::Op::OpAccessChain, 0))
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: Integer size of memory semantics must be equal to 32 bits.
-  ASSERT_FALSE(
-      TransformationStore(14, true, 15, 17, 21,
-                          MakeInstructionDescriptor(24, SpvOpAccessChain, 0))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationStore(
+                   14, true, 15, 17, 21,
+                   MakeInstructionDescriptor(24, spv::Op::OpAccessChain, 0))
+                   .IsApplicable(context.get(), transformation_context));
 
-  // Bad: memory scope value must be 4 (SpvScopeInvocation).
-  ASSERT_FALSE(
-      TransformationStore(14, true, 16, 20, 21,
-                          MakeInstructionDescriptor(24, SpvOpAccessChain, 0))
-          .IsApplicable(context.get(), transformation_context));
+  // Bad: memory scope value must be 4 (spv::Scope::Invocation).
+  ASSERT_FALSE(TransformationStore(
+                   14, true, 16, 20, 21,
+                   MakeInstructionDescriptor(24, spv::Op::OpAccessChain, 0))
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: memory semantics value must be either:
   // 64 (SpvMemorySemanticsUniformMemoryMask)
   // 256 (SpvMemorySemanticsWorkgroupMemoryMask)
-  ASSERT_FALSE(
-      TransformationStore(14, true, 15, 16, 21,
-                          MakeInstructionDescriptor(24, SpvOpAccessChain, 0))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationStore(
+                   14, true, 15, 16, 21,
+                   MakeInstructionDescriptor(24, spv::Op::OpAccessChain, 0))
+                   .IsApplicable(context.get(), transformation_context));
   // Bad: The described instruction does not exist
-  ASSERT_FALSE(
-      TransformationStore(14, true, 15, 20, 21,
-                          MakeInstructionDescriptor(150, SpvOpAccessChain, 0))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationStore(
+                   14, true, 15, 20, 21,
+                   MakeInstructionDescriptor(150, spv::Op::OpAccessChain, 0))
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: Can't insert OpAccessChain before the id 15 of memory scope.
-  ASSERT_FALSE(
-      TransformationStore(14, true, 15, 20, 21,
-                          MakeInstructionDescriptor(15, SpvOpAccessChain, 0))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationStore(
+                   14, true, 15, 20, 21,
+                   MakeInstructionDescriptor(15, spv::Op::OpAccessChain, 0))
+                   .IsApplicable(context.get(), transformation_context));
 
   // Bad: Can't insert OpAccessChain before the id 20 of memory semantics.
-  ASSERT_FALSE(
-      TransformationStore(14, true, 15, 20, 21,
-                          MakeInstructionDescriptor(20, SpvOpAccessChain, 0))
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationStore(
+                   14, true, 15, 20, 21,
+                   MakeInstructionDescriptor(20, spv::Op::OpAccessChain, 0))
+                   .IsApplicable(context.get(), transformation_context));
 
   // Successful transformations.
   {
     TransformationStore transformation(
-        14, true, 15, 20, 21, MakeInstructionDescriptor(24, SpvOpReturn, 0));
+        14, true, 15, 20, 21,
+        MakeInstructionDescriptor(24, spv::Op::OpReturn, 0));
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
diff --git a/test/fuzz/transformation_swap_commutable_operands_test.cpp b/test/fuzz/transformation_swap_commutable_operands_test.cpp
index 0731529..789dd09 100644
--- a/test/fuzz/transformation_swap_commutable_operands_test.cpp
+++ b/test/fuzz/transformation_swap_commutable_operands_test.cpp
@@ -118,103 +118,108 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
   // Tests existing commutative instructions
-  auto instructionDescriptor = MakeInstructionDescriptor(22, SpvOpIAdd, 0);
+  auto instructionDescriptor =
+      MakeInstructionDescriptor(22, spv::Op::OpIAdd, 0);
   auto transformation =
       TransformationSwapCommutableOperands(instructionDescriptor);
   ASSERT_TRUE(
       transformation.IsApplicable(context.get(), transformation_context));
 
-  instructionDescriptor = MakeInstructionDescriptor(28, SpvOpIMul, 0);
+  instructionDescriptor = MakeInstructionDescriptor(28, spv::Op::OpIMul, 0);
   transformation = TransformationSwapCommutableOperands(instructionDescriptor);
   ASSERT_TRUE(
       transformation.IsApplicable(context.get(), transformation_context));
 
-  instructionDescriptor = MakeInstructionDescriptor(42, SpvOpFAdd, 0);
+  instructionDescriptor = MakeInstructionDescriptor(42, spv::Op::OpFAdd, 0);
   transformation = TransformationSwapCommutableOperands(instructionDescriptor);
   ASSERT_TRUE(
       transformation.IsApplicable(context.get(), transformation_context));
 
-  instructionDescriptor = MakeInstructionDescriptor(48, SpvOpFMul, 0);
+  instructionDescriptor = MakeInstructionDescriptor(48, spv::Op::OpFMul, 0);
   transformation = TransformationSwapCommutableOperands(instructionDescriptor);
   ASSERT_TRUE(
       transformation.IsApplicable(context.get(), transformation_context));
 
-  instructionDescriptor = MakeInstructionDescriptor(66, SpvOpDot, 0);
+  instructionDescriptor = MakeInstructionDescriptor(66, spv::Op::OpDot, 0);
   transformation = TransformationSwapCommutableOperands(instructionDescriptor);
   ASSERT_TRUE(
       transformation.IsApplicable(context.get(), transformation_context));
 
   // Tests existing non-commutative instructions
-  instructionDescriptor = MakeInstructionDescriptor(1, SpvOpExtInstImport, 0);
+  instructionDescriptor =
+      MakeInstructionDescriptor(1, spv::Op::OpExtInstImport, 0);
   transformation = TransformationSwapCommutableOperands(instructionDescriptor);
   ASSERT_FALSE(
       transformation.IsApplicable(context.get(), transformation_context));
 
-  instructionDescriptor = MakeInstructionDescriptor(5, SpvOpLabel, 0);
+  instructionDescriptor = MakeInstructionDescriptor(5, spv::Op::OpLabel, 0);
   transformation = TransformationSwapCommutableOperands(instructionDescriptor);
   ASSERT_FALSE(
       transformation.IsApplicable(context.get(), transformation_context));
 
-  instructionDescriptor = MakeInstructionDescriptor(8, SpvOpConstant, 0);
+  instructionDescriptor = MakeInstructionDescriptor(8, spv::Op::OpConstant, 0);
   transformation = TransformationSwapCommutableOperands(instructionDescriptor);
   ASSERT_FALSE(
       transformation.IsApplicable(context.get(), transformation_context));
 
-  instructionDescriptor = MakeInstructionDescriptor(11, SpvOpVariable, 0);
+  instructionDescriptor = MakeInstructionDescriptor(11, spv::Op::OpVariable, 0);
   transformation = TransformationSwapCommutableOperands(instructionDescriptor);
   ASSERT_FALSE(
       transformation.IsApplicable(context.get(), transformation_context));
 
   instructionDescriptor =
-      MakeInstructionDescriptor(14, SpvOpConstantComposite, 0);
+      MakeInstructionDescriptor(14, spv::Op::OpConstantComposite, 0);
   transformation = TransformationSwapCommutableOperands(instructionDescriptor);
   ASSERT_FALSE(
       transformation.IsApplicable(context.get(), transformation_context));
 
   // Tests the base instruction id not existing
-  instructionDescriptor = MakeInstructionDescriptor(67, SpvOpIAddCarry, 0);
+  instructionDescriptor =
+      MakeInstructionDescriptor(67, spv::Op::OpIAddCarry, 0);
   transformation = TransformationSwapCommutableOperands(instructionDescriptor);
   ASSERT_FALSE(
       transformation.IsApplicable(context.get(), transformation_context));
 
-  instructionDescriptor = MakeInstructionDescriptor(68, SpvOpIEqual, 0);
+  instructionDescriptor = MakeInstructionDescriptor(68, spv::Op::OpIEqual, 0);
   transformation = TransformationSwapCommutableOperands(instructionDescriptor);
   ASSERT_FALSE(
       transformation.IsApplicable(context.get(), transformation_context));
 
-  instructionDescriptor = MakeInstructionDescriptor(69, SpvOpINotEqual, 0);
+  instructionDescriptor =
+      MakeInstructionDescriptor(69, spv::Op::OpINotEqual, 0);
   transformation = TransformationSwapCommutableOperands(instructionDescriptor);
   ASSERT_FALSE(
       transformation.IsApplicable(context.get(), transformation_context));
 
-  instructionDescriptor = MakeInstructionDescriptor(70, SpvOpFOrdEqual, 0);
+  instructionDescriptor =
+      MakeInstructionDescriptor(70, spv::Op::OpFOrdEqual, 0);
   transformation = TransformationSwapCommutableOperands(instructionDescriptor);
   ASSERT_FALSE(
       transformation.IsApplicable(context.get(), transformation_context));
 
-  instructionDescriptor = MakeInstructionDescriptor(71, SpvOpPtrEqual, 0);
+  instructionDescriptor = MakeInstructionDescriptor(71, spv::Op::OpPtrEqual, 0);
   transformation = TransformationSwapCommutableOperands(instructionDescriptor);
   ASSERT_FALSE(
       transformation.IsApplicable(context.get(), transformation_context));
 
   // Tests there being no instruction with the desired opcode after the base
   // instruction id
-  instructionDescriptor = MakeInstructionDescriptor(24, SpvOpIAdd, 0);
+  instructionDescriptor = MakeInstructionDescriptor(24, spv::Op::OpIAdd, 0);
   transformation = TransformationSwapCommutableOperands(instructionDescriptor);
   ASSERT_FALSE(
       transformation.IsApplicable(context.get(), transformation_context));
 
-  instructionDescriptor = MakeInstructionDescriptor(38, SpvOpIMul, 0);
+  instructionDescriptor = MakeInstructionDescriptor(38, spv::Op::OpIMul, 0);
   transformation = TransformationSwapCommutableOperands(instructionDescriptor);
   ASSERT_FALSE(
       transformation.IsApplicable(context.get(), transformation_context));
 
-  instructionDescriptor = MakeInstructionDescriptor(45, SpvOpFAdd, 0);
+  instructionDescriptor = MakeInstructionDescriptor(45, spv::Op::OpFAdd, 0);
   transformation = TransformationSwapCommutableOperands(instructionDescriptor);
   ASSERT_FALSE(
       transformation.IsApplicable(context.get(), transformation_context));
 
-  instructionDescriptor = MakeInstructionDescriptor(66, SpvOpFMul, 0);
+  instructionDescriptor = MakeInstructionDescriptor(66, spv::Op::OpFMul, 0);
   transformation = TransformationSwapCommutableOperands(instructionDescriptor);
   ASSERT_FALSE(
       transformation.IsApplicable(context.get(), transformation_context));
@@ -222,27 +227,27 @@
   // Tests there being an instruction with the desired opcode after the base
   // instruction id, but the skip count associated with the instruction
   // descriptor being so high.
-  instructionDescriptor = MakeInstructionDescriptor(11, SpvOpIAdd, 100);
+  instructionDescriptor = MakeInstructionDescriptor(11, spv::Op::OpIAdd, 100);
   transformation = TransformationSwapCommutableOperands(instructionDescriptor);
   ASSERT_FALSE(
       transformation.IsApplicable(context.get(), transformation_context));
 
-  instructionDescriptor = MakeInstructionDescriptor(16, SpvOpIMul, 100);
+  instructionDescriptor = MakeInstructionDescriptor(16, spv::Op::OpIMul, 100);
   transformation = TransformationSwapCommutableOperands(instructionDescriptor);
   ASSERT_FALSE(
       transformation.IsApplicable(context.get(), transformation_context));
 
-  instructionDescriptor = MakeInstructionDescriptor(23, SpvOpFAdd, 100);
+  instructionDescriptor = MakeInstructionDescriptor(23, spv::Op::OpFAdd, 100);
   transformation = TransformationSwapCommutableOperands(instructionDescriptor);
   ASSERT_FALSE(
       transformation.IsApplicable(context.get(), transformation_context));
 
-  instructionDescriptor = MakeInstructionDescriptor(32, SpvOpFMul, 100);
+  instructionDescriptor = MakeInstructionDescriptor(32, spv::Op::OpFMul, 100);
   transformation = TransformationSwapCommutableOperands(instructionDescriptor);
   ASSERT_FALSE(
       transformation.IsApplicable(context.get(), transformation_context));
 
-  instructionDescriptor = MakeInstructionDescriptor(37, SpvOpDot, 100);
+  instructionDescriptor = MakeInstructionDescriptor(37, spv::Op::OpDot, 100);
   transformation = TransformationSwapCommutableOperands(instructionDescriptor);
   ASSERT_FALSE(
       transformation.IsApplicable(context.get(), transformation_context));
@@ -342,24 +347,25 @@
                                                kConsoleMessageConsumer));
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
-  auto instructionDescriptor = MakeInstructionDescriptor(22, SpvOpIAdd, 0);
+  auto instructionDescriptor =
+      MakeInstructionDescriptor(22, spv::Op::OpIAdd, 0);
   auto transformation =
       TransformationSwapCommutableOperands(instructionDescriptor);
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
 
-  instructionDescriptor = MakeInstructionDescriptor(28, SpvOpIMul, 0);
+  instructionDescriptor = MakeInstructionDescriptor(28, spv::Op::OpIMul, 0);
   transformation = TransformationSwapCommutableOperands(instructionDescriptor);
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
 
-  instructionDescriptor = MakeInstructionDescriptor(42, SpvOpFAdd, 0);
+  instructionDescriptor = MakeInstructionDescriptor(42, spv::Op::OpFAdd, 0);
   transformation = TransformationSwapCommutableOperands(instructionDescriptor);
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
 
-  instructionDescriptor = MakeInstructionDescriptor(48, SpvOpFMul, 0);
+  instructionDescriptor = MakeInstructionDescriptor(48, spv::Op::OpFMul, 0);
   transformation = TransformationSwapCommutableOperands(instructionDescriptor);
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
 
-  instructionDescriptor = MakeInstructionDescriptor(66, SpvOpDot, 0);
+  instructionDescriptor = MakeInstructionDescriptor(66, spv::Op::OpDot, 0);
   transformation = TransformationSwapCommutableOperands(instructionDescriptor);
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
 
diff --git a/test/fuzz/transformation_swap_conditional_branch_operands_test.cpp b/test/fuzz/transformation_swap_conditional_branch_operands_test.cpp
index 6133a7a..5f7ffe4 100644
--- a/test/fuzz/transformation_swap_conditional_branch_operands_test.cpp
+++ b/test/fuzz/transformation_swap_conditional_branch_operands_test.cpp
@@ -76,27 +76,29 @@
       MakeUnique<FactManager>(context.get()), validator_options);
   // Invalid instruction descriptor.
   ASSERT_FALSE(TransformationSwapConditionalBranchOperands(
-                   MakeInstructionDescriptor(26, SpvOpPhi, 0), 26)
+                   MakeInstructionDescriptor(26, spv::Op::OpPhi, 0), 26)
                    .IsApplicable(context.get(), transformation_context));
 
   // Descriptor for a wrong instruction.
   ASSERT_FALSE(TransformationSwapConditionalBranchOperands(
-                   MakeInstructionDescriptor(25, SpvOpPhi, 0), 26)
+                   MakeInstructionDescriptor(25, spv::Op::OpPhi, 0), 26)
                    .IsApplicable(context.get(), transformation_context));
 
   // Fresh id is not fresh.
-  ASSERT_FALSE(TransformationSwapConditionalBranchOperands(
-                   MakeInstructionDescriptor(15, SpvOpBranchConditional, 0), 25)
-                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationSwapConditionalBranchOperands(
+          MakeInstructionDescriptor(15, spv::Op::OpBranchConditional, 0), 25)
+          .IsApplicable(context.get(), transformation_context));
 
   TransformationSwapConditionalBranchOperands transformation(
-      MakeInstructionDescriptor(15, SpvOpBranchConditional, 0), 26);
+      MakeInstructionDescriptor(15, spv::Op::OpBranchConditional, 0), 26);
   ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(26));
   ASSERT_EQ(nullptr, context->get_instr_block(26));
   ASSERT_TRUE(
       transformation.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
-  ASSERT_EQ(SpvOpLogicalNot, context->get_def_use_mgr()->GetDef(26)->opcode());
+  ASSERT_EQ(spv::Op::OpLogicalNot,
+            context->get_def_use_mgr()->GetDef(26)->opcode());
   ASSERT_EQ(5, context->get_instr_block(26)->id());
   ASSERT_EQ(1, context->get_def_use_mgr()->NumUses(26));
 
@@ -109,7 +111,7 @@
     context->get_def_use_mgr()->WhileEachUse(
         entry.first,
         [&entry](opt::Instruction* inst, uint32_t operand_index) -> bool {
-          if (inst->opcode() == SpvOpBranchConditional) {
+          if (inst->opcode() == spv::Op::OpBranchConditional) {
             EXPECT_EQ(entry.second, operand_index);
             return false;
           }
diff --git a/test/fuzz/transformation_toggle_access_chain_instruction_test.cpp b/test/fuzz/transformation_toggle_access_chain_instruction_test.cpp
index 84ed20d..94ca804 100644
--- a/test/fuzz/transformation_toggle_access_chain_instruction_test.cpp
+++ b/test/fuzz/transformation_toggle_access_chain_instruction_test.cpp
@@ -119,67 +119,71 @@
       MakeUnique<FactManager>(context.get()), validator_options);
   // Tests existing access chain instructions
   auto instructionDescriptor =
-      MakeInstructionDescriptor(18, SpvOpAccessChain, 0);
+      MakeInstructionDescriptor(18, spv::Op::OpAccessChain, 0);
   auto transformation =
       TransformationToggleAccessChainInstruction(instructionDescriptor);
   ASSERT_TRUE(
       transformation.IsApplicable(context.get(), transformation_context));
 
   instructionDescriptor =
-      MakeInstructionDescriptor(20, SpvOpInBoundsAccessChain, 0);
-  transformation =
-      TransformationToggleAccessChainInstruction(instructionDescriptor);
-  ASSERT_TRUE(
-      transformation.IsApplicable(context.get(), transformation_context));
-
-  instructionDescriptor = MakeInstructionDescriptor(24, SpvOpAccessChain, 0);
+      MakeInstructionDescriptor(20, spv::Op::OpInBoundsAccessChain, 0);
   transformation =
       TransformationToggleAccessChainInstruction(instructionDescriptor);
   ASSERT_TRUE(
       transformation.IsApplicable(context.get(), transformation_context));
 
   instructionDescriptor =
-      MakeInstructionDescriptor(26, SpvOpInBoundsAccessChain, 0);
+      MakeInstructionDescriptor(24, spv::Op::OpAccessChain, 0);
+  transformation =
+      TransformationToggleAccessChainInstruction(instructionDescriptor);
+  ASSERT_TRUE(
+      transformation.IsApplicable(context.get(), transformation_context));
+
+  instructionDescriptor =
+      MakeInstructionDescriptor(26, spv::Op::OpInBoundsAccessChain, 0);
   transformation =
       TransformationToggleAccessChainInstruction(instructionDescriptor);
   ASSERT_TRUE(
       transformation.IsApplicable(context.get(), transformation_context));
 
   // Tests existing non-access chain instructions
-  instructionDescriptor = MakeInstructionDescriptor(1, SpvOpExtInstImport, 0);
+  instructionDescriptor =
+      MakeInstructionDescriptor(1, spv::Op::OpExtInstImport, 0);
   transformation =
       TransformationToggleAccessChainInstruction(instructionDescriptor);
   ASSERT_FALSE(
       transformation.IsApplicable(context.get(), transformation_context));
 
-  instructionDescriptor = MakeInstructionDescriptor(5, SpvOpLabel, 0);
+  instructionDescriptor = MakeInstructionDescriptor(5, spv::Op::OpLabel, 0);
   transformation =
       TransformationToggleAccessChainInstruction(instructionDescriptor);
   ASSERT_FALSE(
       transformation.IsApplicable(context.get(), transformation_context));
 
   instructionDescriptor =
-      MakeInstructionDescriptor(14, SpvOpConstantComposite, 0);
+      MakeInstructionDescriptor(14, spv::Op::OpConstantComposite, 0);
   transformation =
       TransformationToggleAccessChainInstruction(instructionDescriptor);
   ASSERT_FALSE(
       transformation.IsApplicable(context.get(), transformation_context));
 
   // Tests the base instruction id not existing
-  instructionDescriptor = MakeInstructionDescriptor(67, SpvOpAccessChain, 0);
-  transformation =
-      TransformationToggleAccessChainInstruction(instructionDescriptor);
-  ASSERT_FALSE(
-      transformation.IsApplicable(context.get(), transformation_context));
-
-  instructionDescriptor = MakeInstructionDescriptor(68, SpvOpAccessChain, 0);
+  instructionDescriptor =
+      MakeInstructionDescriptor(67, spv::Op::OpAccessChain, 0);
   transformation =
       TransformationToggleAccessChainInstruction(instructionDescriptor);
   ASSERT_FALSE(
       transformation.IsApplicable(context.get(), transformation_context));
 
   instructionDescriptor =
-      MakeInstructionDescriptor(69, SpvOpInBoundsAccessChain, 0);
+      MakeInstructionDescriptor(68, spv::Op::OpAccessChain, 0);
+  transformation =
+      TransformationToggleAccessChainInstruction(instructionDescriptor);
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
+
+  instructionDescriptor =
+      MakeInstructionDescriptor(69, spv::Op::OpInBoundsAccessChain, 0);
   transformation =
       TransformationToggleAccessChainInstruction(instructionDescriptor);
   ASSERT_FALSE(
@@ -187,14 +191,15 @@
 
   // Tests there being no instruction with the desired opcode after the base
   // instruction id
-  instructionDescriptor = MakeInstructionDescriptor(65, SpvOpAccessChain, 0);
+  instructionDescriptor =
+      MakeInstructionDescriptor(65, spv::Op::OpAccessChain, 0);
   transformation =
       TransformationToggleAccessChainInstruction(instructionDescriptor);
   ASSERT_FALSE(
       transformation.IsApplicable(context.get(), transformation_context));
 
   instructionDescriptor =
-      MakeInstructionDescriptor(66, SpvOpInBoundsAccessChain, 0);
+      MakeInstructionDescriptor(66, spv::Op::OpInBoundsAccessChain, 0);
   transformation =
       TransformationToggleAccessChainInstruction(instructionDescriptor);
   ASSERT_FALSE(
@@ -203,14 +208,15 @@
   // Tests there being an instruction with the desired opcode after the base
   // instruction id, but the skip count associated with the instruction
   // descriptor being so high.
-  instructionDescriptor = MakeInstructionDescriptor(11, SpvOpAccessChain, 100);
+  instructionDescriptor =
+      MakeInstructionDescriptor(11, spv::Op::OpAccessChain, 100);
   transformation =
       TransformationToggleAccessChainInstruction(instructionDescriptor);
   ASSERT_FALSE(
       transformation.IsApplicable(context.get(), transformation_context));
 
   instructionDescriptor =
-      MakeInstructionDescriptor(16, SpvOpInBoundsAccessChain, 100);
+      MakeInstructionDescriptor(16, spv::Op::OpInBoundsAccessChain, 100);
   transformation =
       TransformationToggleAccessChainInstruction(instructionDescriptor);
   ASSERT_FALSE(
@@ -312,29 +318,31 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
   auto instructionDescriptor =
-      MakeInstructionDescriptor(18, SpvOpAccessChain, 0);
+      MakeInstructionDescriptor(18, spv::Op::OpAccessChain, 0);
   auto transformation =
       TransformationToggleAccessChainInstruction(instructionDescriptor);
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
 
   instructionDescriptor =
-      MakeInstructionDescriptor(20, SpvOpInBoundsAccessChain, 0);
-  transformation =
-      TransformationToggleAccessChainInstruction(instructionDescriptor);
-  ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
-
-  instructionDescriptor = MakeInstructionDescriptor(24, SpvOpAccessChain, 0);
+      MakeInstructionDescriptor(20, spv::Op::OpInBoundsAccessChain, 0);
   transformation =
       TransformationToggleAccessChainInstruction(instructionDescriptor);
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
 
   instructionDescriptor =
-      MakeInstructionDescriptor(26, SpvOpInBoundsAccessChain, 0);
+      MakeInstructionDescriptor(24, spv::Op::OpAccessChain, 0);
   transformation =
       TransformationToggleAccessChainInstruction(instructionDescriptor);
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
 
-  instructionDescriptor = MakeInstructionDescriptor(38, SpvOpAccessChain, 0);
+  instructionDescriptor =
+      MakeInstructionDescriptor(26, spv::Op::OpInBoundsAccessChain, 0);
+  transformation =
+      TransformationToggleAccessChainInstruction(instructionDescriptor);
+  ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
+
+  instructionDescriptor =
+      MakeInstructionDescriptor(38, spv::Op::OpAccessChain, 0);
   transformation =
       TransformationToggleAccessChainInstruction(instructionDescriptor);
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
diff --git a/test/fuzz/transformation_vector_shuffle_test.cpp b/test/fuzz/transformation_vector_shuffle_test.cpp
index e3dc0a7..4d8f985 100644
--- a/test/fuzz/transformation_vector_shuffle_test.cpp
+++ b/test/fuzz/transformation_vector_shuffle_test.cpp
@@ -172,80 +172,82 @@
 
   // %103 does not dominate the return instruction.
   ASSERT_FALSE(TransformationVectorShuffle(
-                   MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 103, 65,
-                   {3, 5, 7})
+                   MakeInstructionDescriptor(100, spv::Op::OpReturn, 0), 200,
+                   103, 65, {3, 5, 7})
                    .IsApplicable(context.get(), transformation_context));
 
   // Illegal to shuffle a bvec2 and a vec3
   ASSERT_FALSE(TransformationVectorShuffle(
-                   MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 112, 61,
-                   {0, 2, 4})
+                   MakeInstructionDescriptor(100, spv::Op::OpReturn, 0), 200,
+                   112, 61, {0, 2, 4})
                    .IsApplicable(context.get(), transformation_context));
 
   // Illegal to shuffle an ivec2 and a uvec4
   ASSERT_FALSE(TransformationVectorShuffle(
-                   MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 27, 50,
-                   {1, 3, 5})
+                   MakeInstructionDescriptor(100, spv::Op::OpReturn, 0), 200,
+                   27, 50, {1, 3, 5})
                    .IsApplicable(context.get(), transformation_context));
 
   // Vector 1 does not exist
   ASSERT_FALSE(TransformationVectorShuffle(
-                   MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 300, 50,
-                   {1, 3, 5})
+                   MakeInstructionDescriptor(100, spv::Op::OpReturn, 0), 200,
+                   300, 50, {1, 3, 5})
                    .IsApplicable(context.get(), transformation_context));
 
   // Vector 2 does not exist
   ASSERT_FALSE(TransformationVectorShuffle(
-                   MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 27, 300,
-                   {1, 3, 5})
+                   MakeInstructionDescriptor(100, spv::Op::OpReturn, 0), 200,
+                   27, 300, {1, 3, 5})
                    .IsApplicable(context.get(), transformation_context));
 
   // Index out of range
-  ASSERT_FALSE(
-      TransformationVectorShuffle(
-          MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 12, 112, {0, 20})
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationVectorShuffle(
+                   MakeInstructionDescriptor(100, spv::Op::OpReturn, 0), 200,
+                   12, 112, {0, 20})
+                   .IsApplicable(context.get(), transformation_context));
 
   // Too many indices
   ASSERT_FALSE(TransformationVectorShuffle(
-                   MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 12, 112,
-                   {0, 1, 0, 1, 0, 1, 0, 1})
+                   MakeInstructionDescriptor(100, spv::Op::OpReturn, 0), 200,
+                   12, 112, {0, 1, 0, 1, 0, 1, 0, 1})
                    .IsApplicable(context.get(), transformation_context));
 
   // Too few indices
-  ASSERT_FALSE(
-      TransformationVectorShuffle(
-          MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 12, 112, {})
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationVectorShuffle(
+                   MakeInstructionDescriptor(100, spv::Op::OpReturn, 0), 200,
+                   12, 112, {})
+                   .IsApplicable(context.get(), transformation_context));
 
   // Too few indices again
-  ASSERT_FALSE(
-      TransformationVectorShuffle(
-          MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 12, 112, {0})
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationVectorShuffle(
+                   MakeInstructionDescriptor(100, spv::Op::OpReturn, 0), 200,
+                   12, 112, {0})
+                   .IsApplicable(context.get(), transformation_context));
 
   // Indices define unknown type: we do not have vec2
-  ASSERT_FALSE(
-      TransformationVectorShuffle(
-          MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 65, 65, {0, 1})
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationVectorShuffle(
+                   MakeInstructionDescriptor(100, spv::Op::OpReturn, 0), 200,
+                   65, 65, {0, 1})
+                   .IsApplicable(context.get(), transformation_context));
 
   // The instruction to insert before does not exist
-  ASSERT_FALSE(TransformationVectorShuffle(
-                   MakeInstructionDescriptor(100, SpvOpCompositeConstruct, 1),
-                   201, 20, 12, {0xFFFFFFFF, 3, 5})
-                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationVectorShuffle(
+          MakeInstructionDescriptor(100, spv::Op::OpCompositeConstruct, 1), 201,
+          20, 12, {0xFFFFFFFF, 3, 5})
+          .IsApplicable(context.get(), transformation_context));
 
   // The 'fresh' id is already in use
   ASSERT_FALSE(
       TransformationVectorShuffle(
-          MakeInstructionDescriptor(100, SpvOpReturn, 0), 12, 12, 112, {})
+          MakeInstructionDescriptor(100, spv::Op::OpReturn, 0), 12, 12, 112, {})
           .IsApplicable(context.get(), transformation_context));
 
   protobufs::DataDescriptor temp_dd;
 
   TransformationVectorShuffle transformation1(
-      MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 12, 112, {1, 0});
+      MakeInstructionDescriptor(100, spv::Op::OpReturn, 0), 200, 12, 112,
+      {1, 0});
   ASSERT_TRUE(
       transformation1.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation1, context.get(),
@@ -258,7 +260,7 @@
       MakeDataDescriptor(10, {}), temp_dd));
 
   TransformationVectorShuffle transformation2(
-      MakeInstructionDescriptor(100, SpvOpReturn, 0), 201, 20, 12,
+      MakeInstructionDescriptor(100, spv::Op::OpReturn, 0), 201, 20, 12,
       {0xFFFFFFFF, 3, 5});
   ASSERT_TRUE(
       transformation2.IsApplicable(context.get(), transformation_context));
@@ -272,7 +274,8 @@
       MakeDataDescriptor(11, {}), temp_dd));
 
   TransformationVectorShuffle transformation3(
-      MakeInstructionDescriptor(100, SpvOpReturn, 0), 202, 27, 35, {5, 4, 1});
+      MakeInstructionDescriptor(100, spv::Op::OpReturn, 0), 202, 27, 35,
+      {5, 4, 1});
   ASSERT_TRUE(
       transformation3.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation3, context.get(),
@@ -288,7 +291,8 @@
       MakeDataDescriptor(26, {}), temp_dd));
 
   TransformationVectorShuffle transformation4(
-      MakeInstructionDescriptor(100, SpvOpReturn, 0), 203, 42, 46, {0, 1});
+      MakeInstructionDescriptor(100, spv::Op::OpReturn, 0), 203, 42, 46,
+      {0, 1});
   ASSERT_TRUE(
       transformation4.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation4, context.get(),
@@ -301,7 +305,8 @@
       MakeDataDescriptor(41, {}), temp_dd));
 
   TransformationVectorShuffle transformation5(
-      MakeInstructionDescriptor(100, SpvOpReturn, 0), 204, 42, 46, {2, 3, 4});
+      MakeInstructionDescriptor(100, spv::Op::OpReturn, 0), 204, 42, 46,
+      {2, 3, 4});
   ASSERT_TRUE(
       transformation5.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation5, context.get(),
@@ -317,7 +322,7 @@
       MakeDataDescriptor(40, {}), temp_dd));
 
   TransformationVectorShuffle transformation6(
-      MakeInstructionDescriptor(100, SpvOpReturn, 0), 205, 42, 42,
+      MakeInstructionDescriptor(100, spv::Op::OpReturn, 0), 205, 42, 42,
       {0, 1, 2, 3});
   ASSERT_TRUE(
       transformation6.IsApplicable(context.get(), transformation_context));
@@ -338,7 +343,7 @@
 
   // swizzle vec4 from vec4 and vec4 using some undefs
   TransformationVectorShuffle transformation7(
-      MakeInstructionDescriptor(100, SpvOpReturn, 0), 206, 65, 65,
+      MakeInstructionDescriptor(100, spv::Op::OpReturn, 0), 206, 65, 65,
       {0xFFFFFFFF, 3, 6, 0xFFFFFFFF});
   ASSERT_TRUE(
       transformation7.IsApplicable(context.get(), transformation_context));
@@ -500,50 +505,52 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
   // Cannot insert before the OpVariables of a function.
-  ASSERT_FALSE(
-      TransformationVectorShuffle(
-          MakeInstructionDescriptor(101, SpvOpVariable, 0), 200, 14, 14, {0, 1})
-          .IsApplicable(context.get(), transformation_context));
-  ASSERT_FALSE(
-      TransformationVectorShuffle(
-          MakeInstructionDescriptor(101, SpvOpVariable, 1), 200, 14, 14, {1, 2})
-          .IsApplicable(context.get(), transformation_context));
-  ASSERT_FALSE(
-      TransformationVectorShuffle(
-          MakeInstructionDescriptor(102, SpvOpVariable, 0), 200, 14, 14, {1, 2})
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationVectorShuffle(
+                   MakeInstructionDescriptor(101, spv::Op::OpVariable, 0), 200,
+                   14, 14, {0, 1})
+                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationVectorShuffle(
+                   MakeInstructionDescriptor(101, spv::Op::OpVariable, 1), 200,
+                   14, 14, {1, 2})
+                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationVectorShuffle(
+                   MakeInstructionDescriptor(102, spv::Op::OpVariable, 0), 200,
+                   14, 14, {1, 2})
+                   .IsApplicable(context.get(), transformation_context));
   // OK to insert right after the OpVariables.
-  ASSERT_FALSE(
-      TransformationVectorShuffle(
-          MakeInstructionDescriptor(102, SpvOpBranch, 1), 200, 14, 14, {1, 1})
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationVectorShuffle(
+                   MakeInstructionDescriptor(102, spv::Op::OpBranch, 1), 200,
+                   14, 14, {1, 1})
+                   .IsApplicable(context.get(), transformation_context));
 
   // Cannot insert before the OpPhis of a block.
   ASSERT_FALSE(
-      TransformationVectorShuffle(MakeInstructionDescriptor(60, SpvOpPhi, 0),
-                                  200, 14, 14, {2, 0})
+      TransformationVectorShuffle(
+          MakeInstructionDescriptor(60, spv::Op::OpPhi, 0), 200, 14, 14, {2, 0})
           .IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
-      TransformationVectorShuffle(MakeInstructionDescriptor(59, SpvOpPhi, 0),
-                                  200, 14, 14, {3, 0})
+      TransformationVectorShuffle(
+          MakeInstructionDescriptor(59, spv::Op::OpPhi, 0), 200, 14, 14, {3, 0})
           .IsApplicable(context.get(), transformation_context));
   // OK to insert after the OpPhis.
   ASSERT_TRUE(TransformationVectorShuffle(
-                  MakeInstructionDescriptor(59, SpvOpAccessChain, 0), 200, 14,
-                  14, {3, 4})
+                  MakeInstructionDescriptor(59, spv::Op::OpAccessChain, 0), 200,
+                  14, 14, {3, 4})
                   .IsApplicable(context.get(), transformation_context));
 
   // Cannot insert before OpLoopMerge
-  ASSERT_FALSE(TransformationVectorShuffle(
-                   MakeInstructionDescriptor(33, SpvOpBranchConditional, 0),
-                   200, 14, 14, {3})
-                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationVectorShuffle(
+          MakeInstructionDescriptor(33, spv::Op::OpBranchConditional, 0), 200,
+          14, 14, {3})
+          .IsApplicable(context.get(), transformation_context));
 
   // Cannot insert before OpSelectionMerge
-  ASSERT_FALSE(TransformationVectorShuffle(
-                   MakeInstructionDescriptor(21, SpvOpBranchConditional, 0),
-                   200, 14, 14, {2})
-                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationVectorShuffle(
+          MakeInstructionDescriptor(21, spv::Op::OpBranchConditional, 0), 200,
+          14, 14, {2})
+          .IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationVectorShuffleTest, HandlesIrrelevantIds1) {
@@ -615,7 +622,8 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
   TransformationVectorShuffle transformation(
-      MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 12, 112, {2, 0});
+      MakeInstructionDescriptor(100, spv::Op::OpReturn, 0), 200, 12, 112,
+      {2, 0});
   ASSERT_TRUE(
       transformation.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
@@ -697,7 +705,8 @@
       MakeUnique<FactManager>(context.get()), validator_options);
   transformation_context.GetFactManager()->AddFactIdIsIrrelevant(112);
   TransformationVectorShuffle transformation(
-      MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 12, 112, {2, 0});
+      MakeInstructionDescriptor(100, spv::Op::OpReturn, 0), 200, 12, 112,
+      {2, 0});
   ASSERT_TRUE(
       transformation.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
@@ -755,7 +764,7 @@
   transformation_context.GetFactManager()->AddFactBlockIsDead(15);
 
   TransformationVectorShuffle transformation1(
-      MakeInstructionDescriptor(15, SpvOpBranch, 0), 200, 12, 12, {0, 3});
+      MakeInstructionDescriptor(15, spv::Op::OpBranch, 0), 200, 12, 12, {0, 3});
   ASSERT_TRUE(
       transformation1.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation1, context.get(),
@@ -766,7 +775,7 @@
       MakeDataDescriptor(200, {1}), MakeDataDescriptor(12, {1})));
 
   TransformationVectorShuffle transformation2(
-      MakeInstructionDescriptor(16, SpvOpReturn, 0), 201, 12, 40, {0, 1});
+      MakeInstructionDescriptor(16, spv::Op::OpReturn, 0), 201, 12, 40, {0, 1});
   ASSERT_TRUE(
       transformation2.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation2, context.get(),
@@ -777,7 +786,7 @@
       MakeDataDescriptor(201, {1}), MakeDataDescriptor(12, {1})));
 
   TransformationVectorShuffle transformation3(
-      MakeInstructionDescriptor(16, SpvOpReturn, 0), 202, 40, 12, {2, 3});
+      MakeInstructionDescriptor(16, spv::Op::OpReturn, 0), 202, 40, 12, {2, 3});
   ASSERT_TRUE(
       transformation3.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation3, context.get(),
@@ -788,7 +797,7 @@
       MakeDataDescriptor(202, {1}), MakeDataDescriptor(12, {1})));
 
   TransformationVectorShuffle transformation4(
-      MakeInstructionDescriptor(16, SpvOpReturn, 0), 203, 40, 12, {0, 3});
+      MakeInstructionDescriptor(16, spv::Op::OpReturn, 0), 203, 40, 12, {0, 3});
   ASSERT_TRUE(
       transformation4.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation4, context.get(),
diff --git a/test/fuzz/transformation_wrap_early_terminator_in_function_test.cpp b/test/fuzz/transformation_wrap_early_terminator_in_function_test.cpp
index 7b4e487..fbbf57b 100644
--- a/test/fuzz/transformation_wrap_early_terminator_in_function_test.cpp
+++ b/test/fuzz/transformation_wrap_early_terminator_in_function_test.cpp
@@ -98,48 +98,50 @@
 
   // Bad: id is not fresh
   ASSERT_FALSE(TransformationWrapEarlyTerminatorInFunction(
-                   61, MakeInstructionDescriptor(8, SpvOpKill, 0), 0)
+                   61, MakeInstructionDescriptor(8, spv::Op::OpKill, 0), 0)
                    .IsApplicable(context.get(), transformation_context));
 
   // Bad: early terminator instruction descriptor does not exist
   ASSERT_FALSE(TransformationWrapEarlyTerminatorInFunction(
-                   100, MakeInstructionDescriptor(82, SpvOpKill, 0), 0)
+                   100, MakeInstructionDescriptor(82, spv::Op::OpKill, 0), 0)
                    .IsApplicable(context.get(), transformation_context));
 
   // Bad: early terminator instruction does not identify an early terminator
-  ASSERT_FALSE(TransformationWrapEarlyTerminatorInFunction(
-                   100, MakeInstructionDescriptor(5, SpvOpSelectionMerge, 0), 0)
-                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationWrapEarlyTerminatorInFunction(
+          100, MakeInstructionDescriptor(5, spv::Op::OpSelectionMerge, 0), 0)
+          .IsApplicable(context.get(), transformation_context));
 
   // Bad: no wrapper function is available
-  ASSERT_FALSE(TransformationWrapEarlyTerminatorInFunction(
-                   100, MakeInstructionDescriptor(9, SpvOpUnreachable, 0), 0)
-                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationWrapEarlyTerminatorInFunction(
+          100, MakeInstructionDescriptor(9, spv::Op::OpUnreachable, 0), 0)
+          .IsApplicable(context.get(), transformation_context));
 
   // Bad: returned value does not exist
   ASSERT_FALSE(TransformationWrapEarlyTerminatorInFunction(
-                   100, MakeInstructionDescriptor(62, SpvOpKill, 0), 1000)
+                   100, MakeInstructionDescriptor(62, spv::Op::OpKill, 0), 1000)
                    .IsApplicable(context.get(), transformation_context));
 
   // Bad: returned value does not have a type
   ASSERT_FALSE(TransformationWrapEarlyTerminatorInFunction(
-                   100, MakeInstructionDescriptor(62, SpvOpKill, 0), 61)
+                   100, MakeInstructionDescriptor(62, spv::Op::OpKill, 0), 61)
                    .IsApplicable(context.get(), transformation_context));
 
   // Bad: returned value type does not match
   ASSERT_FALSE(TransformationWrapEarlyTerminatorInFunction(
-                   100, MakeInstructionDescriptor(62, SpvOpKill, 0), 91)
+                   100, MakeInstructionDescriptor(62, spv::Op::OpKill, 0), 91)
                    .IsApplicable(context.get(), transformation_context));
 
   // Bad: returned value is not available
   ASSERT_FALSE(TransformationWrapEarlyTerminatorInFunction(
-                   100, MakeInstructionDescriptor(62, SpvOpKill, 0), 81)
+                   100, MakeInstructionDescriptor(62, spv::Op::OpKill, 0), 81)
                    .IsApplicable(context.get(), transformation_context));
 
   // Bad: the OpKill being targeted is in the only available wrapper; we cannot
   // have the wrapper call itself.
   ASSERT_FALSE(TransformationWrapEarlyTerminatorInFunction(
-                   100, MakeInstructionDescriptor(31, SpvOpKill, 0), 0)
+                   100, MakeInstructionDescriptor(31, spv::Op::OpKill, 0), 0)
                    .IsApplicable(context.get(), transformation_context));
 }
 
@@ -220,17 +222,20 @@
 
   for (auto& transformation :
        {TransformationWrapEarlyTerminatorInFunction(
-            100, MakeInstructionDescriptor(8, SpvOpKill, 0), 0),
+            100, MakeInstructionDescriptor(8, spv::Op::OpKill, 0), 0),
         TransformationWrapEarlyTerminatorInFunction(
-            101, MakeInstructionDescriptor(9, SpvOpUnreachable, 0), 0),
+            101, MakeInstructionDescriptor(9, spv::Op::OpUnreachable, 0), 0),
         TransformationWrapEarlyTerminatorInFunction(
-            102, MakeInstructionDescriptor(10, SpvOpTerminateInvocation, 0), 0),
+            102,
+            MakeInstructionDescriptor(10, spv::Op::OpTerminateInvocation, 0),
+            0),
         TransformationWrapEarlyTerminatorInFunction(
-            103, MakeInstructionDescriptor(62, SpvOpKill, 0), 0),
+            103, MakeInstructionDescriptor(62, spv::Op::OpKill, 0), 0),
         TransformationWrapEarlyTerminatorInFunction(
-            104, MakeInstructionDescriptor(71, SpvOpUnreachable, 0), 7),
+            104, MakeInstructionDescriptor(71, spv::Op::OpUnreachable, 0), 7),
         TransformationWrapEarlyTerminatorInFunction(
-            105, MakeInstructionDescriptor(82, SpvOpTerminateInvocation, 0),
+            105,
+            MakeInstructionDescriptor(82, spv::Op::OpTerminateInvocation, 0),
             0)}) {
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
diff --git a/test/hex_float_test.cpp b/test/hex_float_test.cpp
index 25d3c70..a44d9ec 100644
--- a/test/hex_float_test.cpp
+++ b/test/hex_float_test.cpp
@@ -1348,9 +1348,11 @@
   return os;
 }
 
-using FloatStreamParseTest = ::testing::TestWithParam<StreamParseCase<float>>;
+using Float32StreamParseTest = ::testing::TestWithParam<StreamParseCase<float>>;
+using Float16StreamParseTest =
+    ::testing::TestWithParam<StreamParseCase<Float16>>;
 
-TEST_P(FloatStreamParseTest, Samples) {
+TEST_P(Float32StreamParseTest, Samples) {
   std::stringstream input(GetParam().literal);
   HexFloat<FloatProxy<float>> parsed_value(0.0f);
   // Hex floats must be read with the stream input operator.
@@ -1367,8 +1369,87 @@
   }
 }
 
+// Returns a Float16 constructed from its sign bit, unbiased exponent, and
+// mantissa.
+Float16 makeF16(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);   // 16 means infinity or NaN
+  EXPECT_LE(0, mantissa);
+  EXPECT_LE(mantissa, 0x3ff);
+  const unsigned biased_exp = 15 + unbiased_exp;
+  const uint32_t as_bits = sign_bit << 15 | (biased_exp << 10) | mantissa;
+  EXPECT_LE(as_bits, 0xffffu);
+  return Float16(static_cast<uint16_t>(as_bits));
+}
+
+TEST_P(Float16StreamParseTest, Samples) {
+  std::stringstream input(GetParam().literal);
+  HexFloat<FloatProxy<Float16>> parsed_value(makeF16(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(
-    HexFloatExponentMissingDigits, FloatStreamParseTest,
+    HexFloat32FillSignificantDigits, Float32StreamParseTest,
+    ::testing::ValuesIn(std::vector<StreamParseCase<float>>{
+        {"0x123456p0", true, "", ldexpf(0x123456, 0)},
+        // Patterns that fill all mantissa bits
+        {"0x1.fffffep+23", true, "", ldexpf(0x1fffffe, -1)},
+        {"0x1f.ffffep+19", true, "", ldexpf(0x1fffffe, -1)},
+        {"0x1ff.fffep+15", true, "", ldexpf(0x1fffffe, -1)},
+        {"0x1fff.ffep+11", true, "", ldexpf(0x1fffffe, -1)},
+        {"0x1ffff.fep+7", true, "", ldexpf(0x1fffffe, -1)},
+        {"0x1fffff.ep+3", true, "", ldexpf(0x1fffffe, -1)},
+        {"0x1fffffe.p-1", true, "", ldexpf(0x1fffffe, -1)},
+        {"0xffffff.p+0", true, "", ldexpf(0x1fffffe, -1)},
+        {"0xffffff.p+0", true, "", ldexpf(0xffffff, 0)},
+        // Now drop some bits in the middle
+        {"0xa5a5a5.p+0", true, "", ldexpf(0xa5a5a5, 0)},
+        {"0x5a5a5a.p+0", true, "", ldexpf(0x5a5a5a, 0)}}));
+
+INSTANTIATE_TEST_SUITE_P(
+    HexFloat32ExcessSignificantDigits, Float32StreamParseTest,
+    ::testing::ValuesIn(std::vector<StreamParseCase<float>>{
+        // Base cases
+        {"0x1.fffffep0", true, "", ldexpf(0xffffff, -23)},
+        {"0xa5a5a5p0", true, "", ldexpf(0xa5a5a5, 0)},
+        {"0xa.5a5a5p+9", true, "", ldexpf(0xa5a5a5, -11)},
+        {"0x5a5a5ap0", true, "", ldexpf(0x5a5a5a, 0)},
+        {"0x5.a5a5ap+9", true, "", ldexpf(0x5a5a5a, -11)},
+        // Truncate extra bits: zeroes
+        {"0x1.fffffe0p0", true, "", ldexpf(0xffffff, -23)},
+        {"0xa5a5a5000p0", true, "", ldexpf(0xa5a5a5, 12)},
+        {"0xa.5a5a5000p+9", true, "", ldexpf(0xa5a5a5, -11)},
+        {"0x5a5a5a000p0", true, "", ldexpf(0x5a5a5a, 12)},
+        {"0x5.a5a5a000p+9", true, "", ldexpf(0x5a5a5a, -11)},
+        // Truncate extra bits: ones
+        {"0x1.ffffffp0",  // Extra bits in the last nibble
+         true, "", ldexpf(0xffffff, -23)},
+        {"0x1.fffffffp0", true, "", ldexpf(0xffffff, -23)},
+        {"0xa5a5a5fffp0", true, "", ldexpf(0xa5a5a5, 12)},
+        {"0xa.5a5a5fffp+9", true, "", ldexpf(0xa5a5a5, -11)},
+        {"0x5a5a5afffp0",
+         // The 5 nibble (0101), leads with 0, so the result can fit a leading
+         // 1 bit , yielding 8 (1000).
+         true, "", ldexpf(0x5a5a5a8, 8)},
+        {"0x5.a5a5afffp+9", true, "", ldexpf(0x5a5a5a8, 8 - 32 + 9)}}));
+
+INSTANTIATE_TEST_SUITE_P(
+    HexFloat32ExponentMissingDigits, Float32StreamParseTest,
     ::testing::ValuesIn(std::vector<StreamParseCase<float>>{
         {"0x1.0p1", true, "", 2.0f},
         {"0x1.0p1a", true, "a", 2.0f},
@@ -1388,7 +1469,7 @@
         {"0x1.0p--", false, "", 0.0f}}));
 
 INSTANTIATE_TEST_SUITE_P(
-    HexFloatExponentTrailingSign, FloatStreamParseTest,
+    HexFloat32ExponentTrailingSign, Float32StreamParseTest,
     ::testing::ValuesIn(std::vector<StreamParseCase<float>>{
         // Don't consume a sign after the binary exponent digits.
         {"0x1.0p1", true, "", 2.0f},
@@ -1396,7 +1477,7 @@
         {"0x1.0p1-", true, "-", 2.0f}}));
 
 INSTANTIATE_TEST_SUITE_P(
-    HexFloatPositiveExponentOverflow, FloatStreamParseTest,
+    HexFloat32PositiveExponentOverflow, Float32StreamParseTest,
     ::testing::ValuesIn(std::vector<StreamParseCase<float>>{
         // Positive exponents
         {"0x1.0p1", true, "", 2.0f},       // fine, a normal number
@@ -1412,7 +1493,7 @@
     }));
 
 INSTANTIATE_TEST_SUITE_P(
-    HexFloatNegativeExponentOverflow, FloatStreamParseTest,
+    HexFloat32NegativeExponentOverflow, Float32StreamParseTest,
     ::testing::ValuesIn(std::vector<StreamParseCase<float>>{
         // Positive results, digits before '.'
         {"0x1.0p-126", true, "",
@@ -1436,7 +1517,109 @@
         {"0x0.0p-5000000000", true, "", 0.0f},  // zero mantissa, zero result
     }));
 
-// TODO(awoloszyn): Add fp16 tests and HexFloatTraits.
+INSTANTIATE_TEST_SUITE_P(
+    HexFloat16ExcessSignificantDigits, Float16StreamParseTest,
+    ::testing::ValuesIn(std::vector<StreamParseCase<Float16>>{
+        // Zero
+        {"0x1.c00p0", true, "", makeF16(0, 0, 0x300)},
+        {"0x0p0", true, "", makeF16(0, -15, 0x0)},
+        {"0x000.0000p0", true, "", makeF16(0, -15, 0x0)},
+        // All leading 1s
+        {"0x1p0", true, "", makeF16(0, 0, 0x0)},
+        {"0x1.8p0", true, "", makeF16(0, 0, 0x200)},
+        {"0x1.cp0", true, "", makeF16(0, 0, 0x300)},
+        {"0x1.ep0", true, "", makeF16(0, 0, 0x380)},
+        {"0x1.fp0", true, "", makeF16(0, 0, 0x3c0)},
+        {"0x1.f8p0", true, "", makeF16(0, 0, 0x3e0)},
+        {"0x1.fcp0", true, "", makeF16(0, 0, 0x3f0)},
+        {"0x1.fep0", true, "", makeF16(0, 0, 0x3f8)},
+        {"0x1.ffp0", true, "", makeF16(0, 0, 0x3fc)},
+        // Fill trailing zeros to all significant places
+        // that might be used for significant digits.
+        {"0x1.ff8p0", true, "", makeF16(0, 0, 0x3fe)},
+        {"0x1.ffcp0", true, "", makeF16(0, 0, 0x3ff)},
+        {"0x1.800p0", true, "", makeF16(0, 0, 0x200)},
+        {"0x1.c00p0", true, "", makeF16(0, 0, 0x300)},
+        {"0x1.e00p0", true, "", makeF16(0, 0, 0x380)},
+        {"0x1.f00p0", true, "", makeF16(0, 0, 0x3c0)},
+        {"0x1.f80p0", true, "", makeF16(0, 0, 0x3e0)},
+        {"0x1.fc0p0", true, "", makeF16(0, 0, 0x3f0)},
+        {"0x1.fe0p0", true, "", makeF16(0, 0, 0x3f8)},
+        {"0x1.ff0p0", true, "", makeF16(0, 0, 0x3fc)},
+        {"0x1.ff8p0", true, "", makeF16(0, 0, 0x3fe)},
+        {"0x1.ffcp0", true, "", makeF16(0, 0, 0x3ff)},
+        // Add several trailing zeros
+        {"0x1.c00000p0", true, "", makeF16(0, 0, 0x300)},
+        {"0x1.e00000p0", true, "", makeF16(0, 0, 0x380)},
+        {"0x1.f00000p0", true, "", makeF16(0, 0, 0x3c0)},
+        {"0x1.f80000p0", true, "", makeF16(0, 0, 0x3e0)},
+        {"0x1.fc0000p0", true, "", makeF16(0, 0, 0x3f0)},
+        {"0x1.fe0000p0", true, "", makeF16(0, 0, 0x3f8)},
+        {"0x1.ff0000p0", true, "", makeF16(0, 0, 0x3fc)},
+        {"0x1.ff8000p0", true, "", makeF16(0, 0, 0x3fe)},
+        {"0x1.ffcp0000", true, "", makeF16(0, 0, 0x3ff)},
+        // Samples that drop out bits in the middle.
+        //   5 = 0101    4 = 0100
+        //   a = 1010    8 = 1000
+        {"0x1.5a4p0", true, "", makeF16(0, 0, 0x169)},
+        {"0x1.a58p0", true, "", makeF16(0, 0, 0x296)},
+        // Samples that drop out bits *and* truncate significant bits
+        // that can't be represented.
+        {"0x1.5a40000p0", true, "", makeF16(0, 0, 0x169)},
+        {"0x1.5a7ffffp0", true, "", makeF16(0, 0, 0x169)},
+        {"0x1.a580000p0", true, "", makeF16(0, 0, 0x296)},
+        {"0x1.a5bffffp0", true, "", makeF16(0, 0, 0x296)},
+        // Try some negations.
+        {"-0x0p0", true, "", makeF16(1, -15, 0x0)},
+        {"-0x000.0000p0", true, "", makeF16(1, -15, 0x0)},
+        {"-0x1.5a40000p0", true, "", makeF16(1, 0, 0x169)},
+        {"-0x1.5a7ffffp0", true, "", makeF16(1, 0, 0x169)},
+        {"-0x1.a580000p0", true, "", makeF16(1, 0, 0x296)},
+        {"-0x1.a5bffffp0", true, "", makeF16(1, 0, 0x296)}}));
+
+INSTANTIATE_TEST_SUITE_P(
+    HexFloat16IncreasingExponentsAndMantissa, Float16StreamParseTest,
+    ::testing::ValuesIn(std::vector<StreamParseCase<Float16>>{
+        // Zero
+        {"0x0p0", true, "", makeF16(0, -15, 0x0)},
+        {"0x0p5000000000000", true, "", makeF16(0, -15, 0x0)},
+        {"-0x0p5000000000000", true, "", makeF16(1, -15, 0x0)},
+        // Leading 1
+        {"0x1p0", true, "", makeF16(0, 0, 0x0)},
+        {"0x1p1", true, "", makeF16(0, 1, 0x0)},
+        {"0x1p16", true, "", makeF16(0, 16, 0x0)},
+        {"0x1p-1", true, "", makeF16(0, -1, 0x0)},
+        {"0x1p-14", true, "", makeF16(0, -14, 0x0)},
+        // Leading 2
+        {"0x2p0", true, "", makeF16(0, 1, 0x0)},
+        {"0x2p1", true, "", makeF16(0, 2, 0x0)},
+        {"0x2p15", true, "", makeF16(0, 16, 0x0)},
+        {"0x2p-1", true, "", makeF16(0, 0, 0x0)},
+        {"0x2p-15", true, "", makeF16(0, -14, 0x0)},
+        // Leading 8
+        {"0x8p0", true, "", makeF16(0, 3, 0x0)},
+        {"0x8p1", true, "", makeF16(0, 4, 0x0)},
+        {"0x8p13", true, "", makeF16(0, 16, 0x0)},
+        {"0x8p-3", true, "", makeF16(0, 0, 0x0)},
+        {"0x8p-17", true, "", makeF16(0, -14, 0x0)},
+        // Leading 10
+        {"0x10.0p0", true, "", makeF16(0, 4, 0x0)},
+        {"0x10.0p1", true, "", makeF16(0, 5, 0x0)},
+        {"0x10.0p12", true, "", makeF16(0, 16, 0x0)},
+        {"0x10.0p-5", true, "", makeF16(0, -1, 0x0)},
+        {"0x10.0p-18", true, "", makeF16(0, -14, 0x0)},
+        // Samples that drop out bits *and* truncate significant bits
+        // that can't be represented.
+        // Progressively increase the leading digit.
+        {"0x1.5a40000p0", true, "", makeF16(0, 0, 0x169)},
+        {"0x1.5a7ffffp0", true, "", makeF16(0, 0, 0x169)},
+        {"0x2.5a40000p0", true, "", makeF16(0, 1, 0x0b4)},
+        {"0x2.5a7ffffp0", true, "", makeF16(0, 1, 0x0b4)},
+        {"0x4.5a40000p0", true, "", makeF16(0, 2, 0x05a)},
+        {"0x4.5a7ffffp0", true, "", makeF16(0, 2, 0x05a)},
+        {"0x8.5a40000p0", true, "", makeF16(0, 3, 0x02d)},
+        {"0x8.5a7ffffp0", true, "", makeF16(0, 3, 0x02d)}}));
+
 }  // namespace
 }  // namespace utils
 }  // namespace spvtools
diff --git a/test/immediate_int_test.cpp b/test/immediate_int_test.cpp
index 393075a..8e7a8fd 100644
--- a/test/immediate_int_test.cpp
+++ b/test/immediate_int_test.cpp
@@ -57,34 +57,34 @@
 
 TEST_F(ImmediateIntTest, AnyWordInSimpleStatement) {
   EXPECT_THAT(CompiledInstructions("!0x00040018 %a %b %123"),
-              Eq(MakeInstruction(SpvOpTypeMatrix, {1, 2, 3})));
+              Eq(MakeInstruction(spv::Op::OpTypeMatrix, {1, 2, 3})));
   EXPECT_THAT(CompiledInstructions("!0x00040018 !1 %b %123"),
-              Eq(MakeInstruction(SpvOpTypeMatrix, {1, 1, 2})));
+              Eq(MakeInstruction(spv::Op::OpTypeMatrix, {1, 1, 2})));
   EXPECT_THAT(CompiledInstructions("%a = OpTypeMatrix !2 %123"),
-              Eq(MakeInstruction(SpvOpTypeMatrix, {1, 2, 2})));
+              Eq(MakeInstruction(spv::Op::OpTypeMatrix, {1, 2, 2})));
   EXPECT_THAT(CompiledInstructions("%a = OpTypeMatrix  %b !123"),
-              Eq(MakeInstruction(SpvOpTypeMatrix, {1, 2, 123})));
+              Eq(MakeInstruction(spv::Op::OpTypeMatrix, {1, 2, 123})));
   EXPECT_THAT(CompiledInstructions("!0x00040018 %a !2 %123"),
-              Eq(MakeInstruction(SpvOpTypeMatrix, {1, 2, 2})));
+              Eq(MakeInstruction(spv::Op::OpTypeMatrix, {1, 2, 2})));
   EXPECT_THAT(CompiledInstructions("!0x00040018 !1 %b !123"),
-              Eq(MakeInstruction(SpvOpTypeMatrix, {1, 1, 123})));
+              Eq(MakeInstruction(spv::Op::OpTypeMatrix, {1, 1, 123})));
   EXPECT_THAT(CompiledInstructions("!0x00040018 !1 !2 !123"),
-              Eq(MakeInstruction(SpvOpTypeMatrix, {1, 2, 123})));
+              Eq(MakeInstruction(spv::Op::OpTypeMatrix, {1, 2, 123})));
 }
 
 TEST_F(ImmediateIntTest, AnyWordAfterEqualsAndOpCode) {
   EXPECT_THAT(CompiledInstructions("%a = OpArrayLength !2 %c 123"),
-              Eq(MakeInstruction(SpvOpArrayLength, {2, 1, 2, 123})));
+              Eq(MakeInstruction(spv::Op::OpArrayLength, {2, 1, 2, 123})));
   EXPECT_THAT(CompiledInstructions("%a = OpArrayLength %b !3 123"),
-              Eq(MakeInstruction(SpvOpArrayLength, {1, 2, 3, 123})));
+              Eq(MakeInstruction(spv::Op::OpArrayLength, {1, 2, 3, 123})));
   EXPECT_THAT(CompiledInstructions("%a = OpArrayLength %b %c !123"),
-              Eq(MakeInstruction(SpvOpArrayLength, {1, 2, 3, 123})));
+              Eq(MakeInstruction(spv::Op::OpArrayLength, {1, 2, 3, 123})));
   EXPECT_THAT(CompiledInstructions("%a = OpArrayLength %b !3 !123"),
-              Eq(MakeInstruction(SpvOpArrayLength, {1, 2, 3, 123})));
+              Eq(MakeInstruction(spv::Op::OpArrayLength, {1, 2, 3, 123})));
   EXPECT_THAT(CompiledInstructions("%a = OpArrayLength !2 !3 123"),
-              Eq(MakeInstruction(SpvOpArrayLength, {2, 1, 3, 123})));
+              Eq(MakeInstruction(spv::Op::OpArrayLength, {2, 1, 3, 123})));
   EXPECT_THAT(CompiledInstructions("%a = OpArrayLength !2 !3 !123"),
-              Eq(MakeInstruction(SpvOpArrayLength, {2, 1, 3, 123})));
+              Eq(MakeInstruction(spv::Op::OpArrayLength, {2, 1, 3, 123})));
 }
 
 TEST_F(ImmediateIntTest, ResultIdInAssignment) {
@@ -108,8 +108,8 @@
   // With !<integer>, we can (and can only) accept 32-bit number literals,
   // even when we declare the return type is 64-bit.
   EXPECT_EQ(Concatenate({
-                MakeInstruction(SpvOpTypeInt, {1, 64, 0}),
-                MakeInstruction(SpvOpConstant, {1, 2, 4294967295}),
+                MakeInstruction(spv::Op::OpTypeInt, {1, 64, 0}),
+                MakeInstruction(spv::Op::OpConstant, {1, 2, 4294967295}),
             }),
             CompiledInstructions("%i64 = OpTypeInt 64 0\n"
                                  "!0x0004002b %i64 !2 4294967295"));
@@ -151,9 +151,9 @@
       CompiledInstructions("%1 = OpTypeFloat 32\n!0x0004002b %1 %2 -0.5"));
 
   EXPECT_EQ(Concatenate({
-                MakeInstruction(SpvOpTypeInt, {1, 64, 0}),
-                MakeInstruction(SpvOpConstant, {1, 2, 0xb, 0xa}),
-                MakeInstruction(SpvOpSwitch,
+                MakeInstruction(spv::Op::OpTypeInt, {1, 64, 0}),
+                MakeInstruction(spv::Op::OpConstant, {1, 2, 0xb, 0xa}),
+                MakeInstruction(spv::Op::OpSwitch,
                                 {2, 1234, BitwiseCast<uint32_t>(2.5f), 3}),
             }),
             CompiledInstructions("%i64 = OpTypeInt 64 0\n"
@@ -174,7 +174,7 @@
               CompiledInstructions("OpMemberName !1 !4 \"" + name + "\""))
         << name;
     const uint16_t wordCount = static_cast<uint16_t>(4 + name.size() / 4);
-    const uint32_t firstWord = spvOpcodeMake(wordCount, SpvOpMemberName);
+    const uint32_t firstWord = spvOpcodeMake(wordCount, spv::Op::OpMemberName);
     EXPECT_EQ(original, CompiledInstructions("!" + std::to_string(firstWord) +
                                              " %10 !4 \"" + name + "\""))
         << name;
@@ -205,8 +205,8 @@
   EXPECT_THAT(Subvector(CompileSuccessfully(
                             "%10 = OpTypeFloat 32 !5 !6 !7 OpEmitVertex"),
                         kFirstInstruction),
-              ElementsAre(spvOpcodeMake(3, SpvOpTypeFloat), 1, 32, 5, 6, 7,
-                          spvOpcodeMake(1, SpvOpEmitVertex)));
+              ElementsAre(spvOpcodeMake(3, spv::Op::OpTypeFloat), 1, 32, 5, 6,
+                          7, spvOpcodeMake(1, spv::Op::OpEmitVertex)));
 }
 
 TEST_F(ImmediateIntTest, NextOpcodeRecognized) {
diff --git a/test/link/binary_version_test.cpp b/test/link/binary_version_test.cpp
index a56030f..384255a 100644
--- a/test/link/binary_version_test.cpp
+++ b/test/link/binary_version_test.cpp
@@ -27,20 +27,20 @@
   return {
       // clang-format off
       // Header
-      SpvMagicNumber,
+      static_cast<uint32_t>(spv::MagicNumber),
       version,
       SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS, 0),
       1u,  // NOTE: Bound
       0u,  // NOTE: Schema; reserved
 
       // OpCapability Shader
-      SpvOpCapability | 2u << SpvWordCountShift,
-      SpvCapabilityShader,
+      static_cast<uint32_t>(spv::Op::OpCapability) | 2u << spv::WordCountShift,
+      static_cast<uint32_t>(spv::Capability::Shader),
 
       // OpMemoryModel Logical Simple
-      SpvOpMemoryModel | 3u << SpvWordCountShift,
-      SpvAddressingModelLogical,
-      SpvMemoryModelSimple
+      static_cast<uint32_t>(spv::Op::OpMemoryModel) | 3u << spv::WordCountShift,
+      static_cast<uint32_t>(spv::AddressingModel::Logical),
+      static_cast<uint32_t>(spv::MemoryModel::Simple)
       // clang-format on
   };
 }
@@ -73,5 +73,21 @@
                         "through 1) vs 1.5 (input module 2)."));
 }
 
+TEST_F(BinaryVersion, UseHighest) {
+  // clang-format off
+  spvtest::Binaries binaries = {
+      CreateBinary(SPV_SPIRV_VERSION_WORD(1, 3)),
+      CreateBinary(SPV_SPIRV_VERSION_WORD(1, 5)),
+  };
+  // clang-format on
+  LinkerOptions options;
+  options.SetUseHighestVersion(true);
+  spvtest::Binary linked_binary;
+  ASSERT_EQ(SPV_SUCCESS, Link(binaries, &linked_binary, options))
+      << GetErrorMessage();
+  EXPECT_THAT(GetErrorMessage(), std::string());
+  EXPECT_EQ(SPV_SPIRV_VERSION_WORD(1, 5), linked_binary[1]);
+}
+
 }  // namespace
 }  // namespace spvtools
diff --git a/test/link/global_values_amount_test.cpp b/test/link/global_values_amount_test.cpp
index 3158b7e..fda55d6 100644
--- a/test/link/global_values_amount_test.cpp
+++ b/test/link/global_values_amount_test.cpp
@@ -34,26 +34,26 @@
 
     spvtest::Binary common_binary = {
         // clang-format off
-        SpvMagicNumber,
-        SpvVersion,
+        static_cast<uint32_t>(spv::MagicNumber),
+        static_cast<uint32_t>(spv::Version),
         SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS, 0),
         3u + global_variable_count_per_binary,  // NOTE: Bound
         0u,                                     // NOTE: Schema; reserved
 
-        SpvOpCapability | 2u << SpvWordCountShift,
-        SpvCapabilityShader,
+        static_cast<uint32_t>(spv::Op::OpCapability) | 2u << spv::WordCountShift,
+        static_cast<uint32_t>(spv::Capability::Shader),
 
-        SpvOpMemoryModel | 3u << SpvWordCountShift,
-        SpvAddressingModelLogical,
-        SpvMemoryModelSimple,
+        static_cast<uint32_t>(spv::Op::OpMemoryModel) | 3u << spv::WordCountShift,
+        static_cast<uint32_t>(spv::AddressingModel::Logical),
+        static_cast<uint32_t>(spv::MemoryModel::Simple),
 
-        SpvOpTypeFloat | 3u << SpvWordCountShift,
+        static_cast<uint32_t>(spv::Op::OpTypeFloat) | 3u << spv::WordCountShift,
         1u,   // NOTE: Result ID
         32u,  // NOTE: Width
 
-        SpvOpTypePointer | 4u << SpvWordCountShift,
+        static_cast<uint32_t>(spv::Op::OpTypePointer) | 4u << spv::WordCountShift,
         2u,  // NOTE: Result ID
-        SpvStorageClassInput,
+        static_cast<uint32_t>(spv::StorageClass::Input),
         1u  // NOTE: Type ID
         // clang-format on
     };
@@ -64,10 +64,11 @@
     binary.insert(binary.end(), common_binary.cbegin(), common_binary.cend());
 
     for (uint32_t i = 0u; i < global_variable_count_per_binary; ++i) {
-      binary.push_back(SpvOpVariable | 4u << SpvWordCountShift);
+      binary.push_back(static_cast<uint32_t>(spv::Op::OpVariable) |
+                       4u << spv::WordCountShift);
       binary.push_back(2u);      // NOTE: Type ID
       binary.push_back(3u + i);  // NOTE: Result ID
-      binary.push_back(SpvStorageClassInput);
+      binary.push_back(static_cast<uint32_t>(spv::StorageClass::Input));
     }
 
     for (uint32_t i = 0u; i < binary_count - 1u; ++i) {
@@ -89,37 +90,37 @@
 TEST_F(EntryPointsAmountTest, OverLimit) {
   binaries.push_back({
       // clang-format off
-      SpvMagicNumber,
-      SpvVersion,
+      static_cast<uint32_t>(spv::MagicNumber),
+      static_cast<uint32_t>(spv::Version),
       SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS, 0),
       5u,  // NOTE: Bound
       0u,  // NOTE: Schema; reserved
 
-      SpvOpCapability | 2u << SpvWordCountShift,
-      SpvCapabilityShader,
+      static_cast<uint32_t>(spv::Op::OpCapability) | 2u << spv::WordCountShift,
+      static_cast<uint32_t>(spv::Capability::Shader),
 
-      SpvOpMemoryModel | 3u << SpvWordCountShift,
-      SpvAddressingModelLogical,
-      SpvMemoryModelSimple,
+      static_cast<uint32_t>(spv::Op::OpMemoryModel) | 3u << spv::WordCountShift,
+      static_cast<uint32_t>(spv::AddressingModel::Logical),
+      static_cast<uint32_t>(spv::MemoryModel::Simple),
 
-      SpvOpTypeFloat | 3u << SpvWordCountShift,
+      static_cast<uint32_t>(spv::Op::OpTypeFloat) | 3u << spv::WordCountShift,
       1u,   // NOTE: Result ID
       32u,  // NOTE: Width
 
-      SpvOpTypePointer | 4u << SpvWordCountShift,
+      static_cast<uint32_t>(spv::Op::OpTypePointer) | 4u << spv::WordCountShift,
       2u,  // NOTE: Result ID
-      SpvStorageClassInput,
+      static_cast<uint32_t>(spv::StorageClass::Input),
       1u,  // NOTE: Type ID
 
-      SpvOpVariable | 4u << SpvWordCountShift,
+      static_cast<uint32_t>(spv::Op::OpVariable) | 4u << spv::WordCountShift,
       2u,  // NOTE: Type ID
       3u,  // NOTE: Result ID
-      SpvStorageClassInput,
+      static_cast<uint32_t>(spv::StorageClass::Input),
 
-      SpvOpVariable | 4u << SpvWordCountShift,
+      static_cast<uint32_t>(spv::Op::OpVariable) | 4u << spv::WordCountShift,
       2u,  // NOTE: Type ID
       4u,  // NOTE: Result ID
-      SpvStorageClassInput
+      static_cast<uint32_t>(spv::StorageClass::Input)
       // clang-format on
   });
 
diff --git a/test/link/ids_limit_test.cpp b/test/link/ids_limit_test.cpp
index 846fbef..8182e5d 100644
--- a/test/link/ids_limit_test.cpp
+++ b/test/link/ids_limit_test.cpp
@@ -36,20 +36,20 @@
     // running the RemoveDuplicates pass.
     spvtest::Binary common_binary = {
         // clang-format off
-        SpvMagicNumber,
-        SpvVersion,
+        spv::MagicNumber,
+        spv::Version,
         SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS, 0),
         id_bound,  // NOTE: Bound
         0u,        // NOTE: Schema; reserved
 
-        SpvOpCapability | 2u << SpvWordCountShift,
-        SpvCapabilityShader,
+        static_cast<uint32_t>(spv::Op::OpCapability) | 2u << spv::WordCountShift,
+        static_cast<uint32_t>(spv::Capability::Shader),
 
-        SpvOpMemoryModel | 3u << SpvWordCountShift,
-        SpvAddressingModelLogical,
-        SpvMemoryModelSimple,
+        static_cast<uint32_t>(spv::Op::OpMemoryModel) | 3u << spv::WordCountShift,
+        static_cast<uint32_t>(spv::AddressingModel::Logical),
+        static_cast<uint32_t>(spv::MemoryModel::Simple),
 
-        SpvOpTypeBool | 2u << SpvWordCountShift,
+        static_cast<uint32_t>(spv::Op::OpTypeBool) | 2u << spv::WordCountShift,
         1u    // NOTE: Result ID
         // clang-format on
     };
@@ -60,7 +60,8 @@
     binary.insert(binary.end(), common_binary.cbegin(), common_binary.cend());
 
     for (uint32_t i = 0u; i < constant_count; ++i) {
-      binary.push_back(SpvOpConstantTrue | 3u << SpvWordCountShift);
+      binary.push_back(static_cast<uint32_t>(spv::Op::OpConstantTrue) |
+                       3u << spv::WordCountShift);
       binary.push_back(1u);      // NOTE: Type ID
       binary.push_back(2u + i);  // NOTE: Result ID
     }
@@ -74,20 +75,20 @@
   return {
       // clang-format off
       // Header
-      SpvMagicNumber,
-      SpvVersion,
+      spv::MagicNumber,
+      spv::Version,
       SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS, 0),
       id_bound,  // NOTE: Bound
       0u,        // NOTE: Schema; reserved
 
       // OpCapability Shader
-      SpvOpCapability | 2u << SpvWordCountShift,
-      SpvCapabilityShader,
+      static_cast<uint32_t>(spv::Op::OpCapability) | 2u << spv::WordCountShift,
+      static_cast<uint32_t>(spv::Capability::Shader),
 
       // OpMemoryModel Logical Simple
-      SpvOpMemoryModel | 3u << SpvWordCountShift,
-      SpvAddressingModelLogical,
-      SpvMemoryModelSimple
+      static_cast<uint32_t>(spv::Op::OpMemoryModel) | 3u << spv::WordCountShift,
+      static_cast<uint32_t>(spv::AddressingModel::Logical),
+      static_cast<uint32_t>(spv::MemoryModel::Simple)
       // clang-format on
   };
 }
@@ -105,7 +106,8 @@
   const uint32_t id_bound = binary[3];
   binary[3] = id_bound + 1u;
 
-  binary.push_back(SpvOpConstantFalse | 3u << SpvWordCountShift);
+  binary.push_back(static_cast<uint32_t>(spv::Op::OpConstantFalse) |
+                   3u << spv::WordCountShift);
   binary.push_back(1u);        // NOTE: Type ID
   binary.push_back(id_bound);  // NOTE: Result ID
 
diff --git a/test/link/memory_model_test.cpp b/test/link/memory_model_test.cpp
index 280a776..ee63c59 100644
--- a/test/link/memory_model_test.cpp
+++ b/test/link/memory_model_test.cpp
@@ -35,8 +35,10 @@
   ASSERT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary));
   EXPECT_THAT(GetErrorMessage(), std::string());
 
-  EXPECT_EQ(SpvAddressingModelLogical, linked_binary[6]);
-  EXPECT_EQ(SpvMemoryModelSimple, linked_binary[7]);
+  EXPECT_EQ(spv::AddressingModel::Logical,
+            static_cast<spv::AddressingModel>(linked_binary[6]));
+  EXPECT_EQ(spv::MemoryModel::Simple,
+            static_cast<spv::MemoryModel>(linked_binary[7]));
 }
 
 TEST_F(MemoryModel, AddressingMismatch) {
diff --git a/test/opcode_make_test.cpp b/test/opcode_make_test.cpp
index 6481ef3..6c1dab6 100644
--- a/test/opcode_make_test.cpp
+++ b/test/opcode_make_test.cpp
@@ -35,7 +35,7 @@
       uint32_t word = 0;
       word |= uint32_t(opcode);
       word |= uint32_t(wordCount) << 16;
-      EXPECT_EQ(word, spvOpcodeMake(wordCount, SpvOp(opcode)));
+      EXPECT_EQ(word, spvOpcodeMake(wordCount, spv::Op(opcode)));
     }
   }
 }
diff --git a/test/opcode_require_capabilities_test.cpp b/test/opcode_require_capabilities_test.cpp
index 07e86f8..37097c6 100644
--- a/test/opcode_require_capabilities_test.cpp
+++ b/test/opcode_require_capabilities_test.cpp
@@ -23,7 +23,7 @@
 
 // Capabilities required by an Opcode.
 struct ExpectedOpCodeCapabilities {
-  SpvOp opcode;
+  spv::Op opcode;
   CapabilitySet capabilities;
 };
 
@@ -46,33 +46,36 @@
     TableRowTest, OpcodeTableCapabilitiesTest,
     // Spot-check a few opcodes.
     ::testing::Values(
+        ExpectedOpCodeCapabilities{spv::Op::OpImageQuerySize,
+                                   CapabilitySet{spv::Capability::Kernel,
+                                                 spv::Capability::ImageQuery}},
+        ExpectedOpCodeCapabilities{spv::Op::OpImageQuerySizeLod,
+                                   CapabilitySet{spv::Capability::Kernel,
+                                                 spv::Capability::ImageQuery}},
+        ExpectedOpCodeCapabilities{spv::Op::OpImageQueryLevels,
+                                   CapabilitySet{spv::Capability::Kernel,
+                                                 spv::Capability::ImageQuery}},
+        ExpectedOpCodeCapabilities{spv::Op::OpImageQuerySamples,
+                                   CapabilitySet{spv::Capability::Kernel,
+                                                 spv::Capability::ImageQuery}},
         ExpectedOpCodeCapabilities{
-            SpvOpImageQuerySize,
-            CapabilitySet{SpvCapabilityKernel, SpvCapabilityImageQuery}},
+            spv::Op::OpImageSparseSampleImplicitLod,
+            CapabilitySet{spv::Capability::SparseResidency}},
+        ExpectedOpCodeCapabilities{spv::Op::OpCopyMemorySized,
+                                   CapabilitySet{spv::Capability::Addresses}},
+        ExpectedOpCodeCapabilities{spv::Op::OpArrayLength,
+                                   CapabilitySet{spv::Capability::Shader}},
+        ExpectedOpCodeCapabilities{spv::Op::OpFunction, CapabilitySet()},
+        ExpectedOpCodeCapabilities{spv::Op::OpConvertFToS, CapabilitySet()},
         ExpectedOpCodeCapabilities{
-            SpvOpImageQuerySizeLod,
-            CapabilitySet{SpvCapabilityKernel, SpvCapabilityImageQuery}},
+            spv::Op::OpEmitStreamVertex,
+            CapabilitySet{spv::Capability::GeometryStreams}},
         ExpectedOpCodeCapabilities{
-            SpvOpImageQueryLevels,
-            CapabilitySet{SpvCapabilityKernel, SpvCapabilityImageQuery}},
+            spv::Op::OpTypeNamedBarrier,
+            CapabilitySet{spv::Capability::NamedBarrier}},
         ExpectedOpCodeCapabilities{
-            SpvOpImageQuerySamples,
-            CapabilitySet{SpvCapabilityKernel, SpvCapabilityImageQuery}},
-        ExpectedOpCodeCapabilities{SpvOpImageSparseSampleImplicitLod,
-                                   CapabilitySet{SpvCapabilitySparseResidency}},
-        ExpectedOpCodeCapabilities{SpvOpCopyMemorySized,
-                                   CapabilitySet{SpvCapabilityAddresses}},
-        ExpectedOpCodeCapabilities{SpvOpArrayLength,
-                                   CapabilitySet{SpvCapabilityShader}},
-        ExpectedOpCodeCapabilities{SpvOpFunction, CapabilitySet()},
-        ExpectedOpCodeCapabilities{SpvOpConvertFToS, CapabilitySet()},
-        ExpectedOpCodeCapabilities{SpvOpEmitStreamVertex,
-                                   CapabilitySet{SpvCapabilityGeometryStreams}},
-        ExpectedOpCodeCapabilities{SpvOpTypeNamedBarrier,
-                                   CapabilitySet{SpvCapabilityNamedBarrier}},
-        ExpectedOpCodeCapabilities{
-            SpvOpGetKernelMaxNumSubgroups,
-            CapabilitySet{SpvCapabilitySubgroupDispatch}}));
+            spv::Op::OpGetKernelMaxNumSubgroups,
+            CapabilitySet{spv::Capability::SubgroupDispatch}}));
 
 }  // namespace
 }  // namespace spvtools
diff --git a/test/opcode_split_test.cpp b/test/opcode_split_test.cpp
index 43fedb3..e8a67b6 100644
--- a/test/opcode_split_test.cpp
+++ b/test/opcode_split_test.cpp
@@ -18,7 +18,7 @@
 namespace {
 
 TEST(OpcodeSplit, Default) {
-  uint32_t word = spvOpcodeMake(42, (SpvOp)23);
+  uint32_t word = spvOpcodeMake(42, (spv::Op)23);
   uint16_t wordCount = 0;
   uint16_t opcode;
   spvOpcodeSplit(word, &wordCount, &opcode);
diff --git a/test/operand_capabilities_test.cpp b/test/operand_capabilities_test.cpp
index 6050346..4872228 100644
--- a/test/operand_capabilities_test.cpp
+++ b/test/operand_capabilities_test.cpp
@@ -18,7 +18,9 @@
 #include <vector>
 
 #include "gmock/gmock.h"
+#include "source/assembly_grammar.h"
 #include "source/enum_set.h"
+#include "source/operand.h"
 #include "test/unit_spirv.h"
 
 namespace spvtools {
@@ -31,12 +33,39 @@
 using ::testing::Values;
 using ::testing::ValuesIn;
 
+// Emits a CapabilitySet to the given ostream, returning the ostream.
+inline std::ostream& operator<<(std::ostream& out, const CapabilitySet& cs) {
+  out << "CapabilitySet{";
+  auto ctx = spvContextCreate(SPV_ENV_UNIVERSAL_1_0);
+  spvtools::AssemblyGrammar grammar(ctx);
+  bool first = true;
+  for (auto c : cs) {
+    if (!first) {
+      out << " ";
+      first = false;
+    }
+    out << grammar.lookupOperandName(SPV_OPERAND_TYPE_CAPABILITY, uint32_t(c))
+        << "(" << uint32_t(c) << ")";
+  }
+  spvContextDestroy(ctx);
+  out << "}";
+  return out;
+}
+
 // A test case for mapping an enum to a capability mask.
 struct EnumCapabilityCase {
   spv_operand_type_t type;
   uint32_t value;
   CapabilitySet expected_capabilities;
 };
+// Emits an EnumCapabilityCase to the ostream, returning the ostream.
+inline std::ostream& operator<<(std::ostream& out,
+                                const EnumCapabilityCase& ecc) {
+  out << "EnumCapabilityCase{ " << spvOperandTypeStr(ecc.type) << "("
+      << unsigned(ecc.type) << "), " << ecc.value << ", "
+      << ecc.expected_capabilities << "}";
+  return out;
+}
 
 // Test fixture for testing EnumCapabilityCases.
 using EnumCapabilityTest =
@@ -56,53 +85,53 @@
 
   EXPECT_THAT(ElementsIn(cap_set),
               Eq(ElementsIn(std::get<1>(GetParam()).expected_capabilities)))
-      << " capability value " << std::get<1>(GetParam()).value;
+      << " enum value " << std::get<1>(GetParam()).value;
   spvContextDestroy(context);
 }
 
 #define CASE0(TYPE, VALUE)                            \
   {                                                   \
-    SPV_OPERAND_TYPE_##TYPE, uint32_t(Spv##VALUE), {} \
+    SPV_OPERAND_TYPE_##TYPE, uint32_t(spv::VALUE), {} \
   }
 #define CASE1(TYPE, VALUE, CAP)                                    \
   {                                                                \
-    SPV_OPERAND_TYPE_##TYPE, uint32_t(Spv##VALUE), CapabilitySet { \
-      SpvCapability##CAP                                           \
+    SPV_OPERAND_TYPE_##TYPE, uint32_t(spv::VALUE), CapabilitySet { \
+      spv::Capability::CAP                                         \
     }                                                              \
   }
 #define CASE2(TYPE, VALUE, CAP1, CAP2)                             \
   {                                                                \
-    SPV_OPERAND_TYPE_##TYPE, uint32_t(Spv##VALUE), CapabilitySet { \
-      SpvCapability##CAP1, SpvCapability##CAP2                     \
+    SPV_OPERAND_TYPE_##TYPE, uint32_t(spv::VALUE), CapabilitySet { \
+      spv::Capability::CAP1, spv::Capability::CAP2                 \
     }                                                              \
   }
-#define CASE3(TYPE, VALUE, CAP1, CAP2, CAP3)                        \
-  {                                                                 \
-    SPV_OPERAND_TYPE_##TYPE, uint32_t(Spv##VALUE), CapabilitySet {  \
-      SpvCapability##CAP1, SpvCapability##CAP2, SpvCapability##CAP3 \
-    }                                                               \
+#define CASE3(TYPE, VALUE, CAP1, CAP2, CAP3)                              \
+  {                                                                       \
+    SPV_OPERAND_TYPE_##TYPE, uint32_t(spv::VALUE), CapabilitySet {        \
+      spv::Capability::CAP1, spv::Capability::CAP2, spv::Capability::CAP3 \
+    }                                                                     \
   }
-#define CASE4(TYPE, VALUE, CAP1, CAP2, CAP3, CAP4)                   \
-  {                                                                  \
-    SPV_OPERAND_TYPE_##TYPE, uint32_t(Spv##VALUE), CapabilitySet {   \
-      SpvCapability##CAP1, SpvCapability##CAP2, SpvCapability##CAP3, \
-          SpvCapability##CAP4                                        \
-    }                                                                \
+#define CASE4(TYPE, VALUE, CAP1, CAP2, CAP3, CAP4)                         \
+  {                                                                        \
+    SPV_OPERAND_TYPE_##TYPE, uint32_t(spv::VALUE), CapabilitySet {         \
+      spv::Capability::CAP1, spv::Capability::CAP2, spv::Capability::CAP3, \
+          spv::Capability::CAP4                                            \
+    }                                                                      \
   }
-#define CASE5(TYPE, VALUE, CAP1, CAP2, CAP3, CAP4, CAP5)             \
-  {                                                                  \
-    SPV_OPERAND_TYPE_##TYPE, uint32_t(Spv##VALUE), CapabilitySet {   \
-      SpvCapability##CAP1, SpvCapability##CAP2, SpvCapability##CAP3, \
-          SpvCapability##CAP4, SpvCapability##CAP5                   \
-    }                                                                \
+#define CASE5(TYPE, VALUE, CAP1, CAP2, CAP3, CAP4, CAP5)                   \
+  {                                                                        \
+    SPV_OPERAND_TYPE_##TYPE, uint32_t(spv::VALUE), CapabilitySet {         \
+      spv::Capability::CAP1, spv::Capability::CAP2, spv::Capability::CAP3, \
+          spv::Capability::CAP4, spv::Capability::CAP5                     \
+    }                                                                      \
   }
 
-#define CASE6(TYPE, VALUE, CAP1, CAP2, CAP3, CAP4, CAP5, CAP6)          \
-  {                                                                     \
-    SPV_OPERAND_TYPE_##TYPE, uint32_t(Spv##VALUE), CapabilitySet {      \
-      SpvCapability##CAP1, SpvCapability##CAP2, SpvCapability##CAP3,    \
-          SpvCapability##CAP4, SpvCapability##CAP5, SpvCapability##CAP6 \
-    }                                                                   \
+#define CASE6(TYPE, VALUE, CAP1, CAP2, CAP3, CAP4, CAP5, CAP6)                \
+  {                                                                           \
+    SPV_OPERAND_TYPE_##TYPE, uint32_t(spv::VALUE), CapabilitySet {            \
+      spv::Capability::CAP1, spv::Capability::CAP2, spv::Capability::CAP3,    \
+          spv::Capability::CAP4, spv::Capability::CAP5, spv::Capability::CAP6 \
+    }                                                                         \
   }
 
 // See SPIR-V Section 3.3 Execution Model
@@ -110,15 +139,15 @@
     ExecutionModel, EnumCapabilityTest,
     Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
             ValuesIn(std::vector<EnumCapabilityCase>{
-                CASE1(EXECUTION_MODEL, ExecutionModelVertex, Shader),
-                CASE1(EXECUTION_MODEL, ExecutionModelTessellationControl,
+                CASE1(EXECUTION_MODEL, ExecutionModel::Vertex, Shader),
+                CASE1(EXECUTION_MODEL, ExecutionModel::TessellationControl,
                       Tessellation),
-                CASE1(EXECUTION_MODEL, ExecutionModelTessellationEvaluation,
+                CASE1(EXECUTION_MODEL, ExecutionModel::TessellationEvaluation,
                       Tessellation),
-                CASE1(EXECUTION_MODEL, ExecutionModelGeometry, Geometry),
-                CASE1(EXECUTION_MODEL, ExecutionModelFragment, Shader),
-                CASE1(EXECUTION_MODEL, ExecutionModelGLCompute, Shader),
-                CASE1(EXECUTION_MODEL, ExecutionModelKernel, Kernel),
+                CASE1(EXECUTION_MODEL, ExecutionModel::Geometry, Geometry),
+                CASE1(EXECUTION_MODEL, ExecutionModel::Fragment, Shader),
+                CASE1(EXECUTION_MODEL, ExecutionModel::GLCompute, Shader),
+                CASE1(EXECUTION_MODEL, ExecutionModel::Kernel, Kernel),
             })));
 
 // See SPIR-V Section 3.4 Addressing Model
@@ -126,9 +155,9 @@
     AddressingModel, EnumCapabilityTest,
     Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
             ValuesIn(std::vector<EnumCapabilityCase>{
-                CASE0(ADDRESSING_MODEL, AddressingModelLogical),
-                CASE1(ADDRESSING_MODEL, AddressingModelPhysical32, Addresses),
-                CASE1(ADDRESSING_MODEL, AddressingModelPhysical64, Addresses),
+                CASE0(ADDRESSING_MODEL, AddressingModel::Logical),
+                CASE1(ADDRESSING_MODEL, AddressingModel::Physical32, Addresses),
+                CASE1(ADDRESSING_MODEL, AddressingModel::Physical64, Addresses),
             })));
 
 // See SPIR-V Section 3.5 Memory Model
@@ -136,9 +165,9 @@
     MemoryModel, EnumCapabilityTest,
     Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
             ValuesIn(std::vector<EnumCapabilityCase>{
-                CASE1(MEMORY_MODEL, MemoryModelSimple, Shader),
-                CASE1(MEMORY_MODEL, MemoryModelGLSL450, Shader),
-                CASE1(MEMORY_MODEL, MemoryModelOpenCL, Kernel),
+                CASE1(MEMORY_MODEL, MemoryModel::Simple, Shader),
+                CASE1(MEMORY_MODEL, MemoryModel::GLSL450, Shader),
+                CASE1(MEMORY_MODEL, MemoryModel::OpenCL, Kernel),
             })));
 
 // See SPIR-V Section 3.6 Execution Mode
@@ -147,54 +176,54 @@
     Combine(
         Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
         ValuesIn(std::vector<EnumCapabilityCase>{
-            CASE1(EXECUTION_MODE, ExecutionModeInvocations, Geometry),
-            CASE1(EXECUTION_MODE, ExecutionModeSpacingEqual, Tessellation),
-            CASE1(EXECUTION_MODE, ExecutionModeSpacingFractionalEven,
+            CASE1(EXECUTION_MODE, ExecutionMode::Invocations, Geometry),
+            CASE1(EXECUTION_MODE, ExecutionMode::SpacingEqual, Tessellation),
+            CASE1(EXECUTION_MODE, ExecutionMode::SpacingFractionalEven,
                   Tessellation),
-            CASE1(EXECUTION_MODE, ExecutionModeSpacingFractionalOdd,
+            CASE1(EXECUTION_MODE, ExecutionMode::SpacingFractionalOdd,
                   Tessellation),
-            CASE1(EXECUTION_MODE, ExecutionModeVertexOrderCw, Tessellation),
-            CASE1(EXECUTION_MODE, ExecutionModeVertexOrderCcw, Tessellation),
-            CASE1(EXECUTION_MODE, ExecutionModePixelCenterInteger, Shader),
-            CASE1(EXECUTION_MODE, ExecutionModeOriginUpperLeft, Shader),
-            CASE1(EXECUTION_MODE, ExecutionModeOriginLowerLeft, Shader),
-            CASE1(EXECUTION_MODE, ExecutionModeEarlyFragmentTests, Shader),
-            CASE1(EXECUTION_MODE, ExecutionModePointMode, Tessellation),
-            CASE1(EXECUTION_MODE, ExecutionModeXfb, TransformFeedback),
-            CASE1(EXECUTION_MODE, ExecutionModeDepthReplacing, Shader),
-            CASE1(EXECUTION_MODE, ExecutionModeDepthGreater, Shader),
-            CASE1(EXECUTION_MODE, ExecutionModeDepthLess, Shader),
-            CASE1(EXECUTION_MODE, ExecutionModeDepthUnchanged, Shader),
-            CASE0(EXECUTION_MODE, ExecutionModeLocalSize),
-            CASE1(EXECUTION_MODE, ExecutionModeLocalSizeHint, Kernel),
-            CASE1(EXECUTION_MODE, ExecutionModeInputPoints, Geometry),
-            CASE1(EXECUTION_MODE, ExecutionModeInputLines, Geometry),
-            CASE1(EXECUTION_MODE, ExecutionModeInputLinesAdjacency, Geometry),
-            CASE2(EXECUTION_MODE, ExecutionModeTriangles, Geometry,
+            CASE1(EXECUTION_MODE, ExecutionMode::VertexOrderCw, Tessellation),
+            CASE1(EXECUTION_MODE, ExecutionMode::VertexOrderCcw, Tessellation),
+            CASE1(EXECUTION_MODE, ExecutionMode::PixelCenterInteger, Shader),
+            CASE1(EXECUTION_MODE, ExecutionMode::OriginUpperLeft, Shader),
+            CASE1(EXECUTION_MODE, ExecutionMode::OriginLowerLeft, Shader),
+            CASE1(EXECUTION_MODE, ExecutionMode::EarlyFragmentTests, Shader),
+            CASE1(EXECUTION_MODE, ExecutionMode::PointMode, Tessellation),
+            CASE1(EXECUTION_MODE, ExecutionMode::Xfb, TransformFeedback),
+            CASE1(EXECUTION_MODE, ExecutionMode::DepthReplacing, Shader),
+            CASE1(EXECUTION_MODE, ExecutionMode::DepthGreater, Shader),
+            CASE1(EXECUTION_MODE, ExecutionMode::DepthLess, Shader),
+            CASE1(EXECUTION_MODE, ExecutionMode::DepthUnchanged, Shader),
+            CASE0(EXECUTION_MODE, ExecutionMode::LocalSize),
+            CASE1(EXECUTION_MODE, ExecutionMode::LocalSizeHint, Kernel),
+            CASE1(EXECUTION_MODE, ExecutionMode::InputPoints, Geometry),
+            CASE1(EXECUTION_MODE, ExecutionMode::InputLines, Geometry),
+            CASE1(EXECUTION_MODE, ExecutionMode::InputLinesAdjacency, Geometry),
+            CASE2(EXECUTION_MODE, ExecutionMode::Triangles, Geometry,
                   Tessellation),
-            CASE1(EXECUTION_MODE, ExecutionModeInputTrianglesAdjacency,
+            CASE1(EXECUTION_MODE, ExecutionMode::InputTrianglesAdjacency,
                   Geometry),
-            CASE1(EXECUTION_MODE, ExecutionModeQuads, Tessellation),
-            CASE1(EXECUTION_MODE, ExecutionModeIsolines, Tessellation),
-            CASE4(EXECUTION_MODE, ExecutionModeOutputVertices, Geometry,
+            CASE1(EXECUTION_MODE, ExecutionMode::Quads, Tessellation),
+            CASE1(EXECUTION_MODE, ExecutionMode::Isolines, Tessellation),
+            CASE4(EXECUTION_MODE, ExecutionMode::OutputVertices, Geometry,
                   Tessellation, MeshShadingNV, MeshShadingEXT),
-            CASE3(EXECUTION_MODE, ExecutionModeOutputPoints, Geometry,
+            CASE3(EXECUTION_MODE, ExecutionMode::OutputPoints, Geometry,
                   MeshShadingNV, MeshShadingEXT),
-            CASE1(EXECUTION_MODE, ExecutionModeOutputLineStrip, Geometry),
-            CASE1(EXECUTION_MODE, ExecutionModeOutputTriangleStrip, Geometry),
-            CASE1(EXECUTION_MODE, ExecutionModeVecTypeHint, Kernel),
-            CASE1(EXECUTION_MODE, ExecutionModeContractionOff, Kernel),
+            CASE1(EXECUTION_MODE, ExecutionMode::OutputLineStrip, Geometry),
+            CASE1(EXECUTION_MODE, ExecutionMode::OutputTriangleStrip, Geometry),
+            CASE1(EXECUTION_MODE, ExecutionMode::VecTypeHint, Kernel),
+            CASE1(EXECUTION_MODE, ExecutionMode::ContractionOff, Kernel),
         })));
 
 INSTANTIATE_TEST_SUITE_P(
     ExecutionModeV11, EnumCapabilityTest,
     Combine(Values(SPV_ENV_UNIVERSAL_1_1),
             ValuesIn(std::vector<EnumCapabilityCase>{
-                CASE1(EXECUTION_MODE, ExecutionModeInitializer, Kernel),
-                CASE1(EXECUTION_MODE, ExecutionModeFinalizer, Kernel),
-                CASE1(EXECUTION_MODE, ExecutionModeSubgroupSize,
+                CASE1(EXECUTION_MODE, ExecutionMode::Initializer, Kernel),
+                CASE1(EXECUTION_MODE, ExecutionMode::Finalizer, Kernel),
+                CASE1(EXECUTION_MODE, ExecutionMode::SubgroupSize,
                       SubgroupDispatch),
-                CASE1(EXECUTION_MODE, ExecutionModeSubgroupsPerWorkgroup,
+                CASE1(EXECUTION_MODE, ExecutionMode::SubgroupsPerWorkgroup,
                       SubgroupDispatch)})));
 
 // See SPIR-V Section 3.7 Storage Class
@@ -202,19 +231,20 @@
     StorageClass, EnumCapabilityTest,
     Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
             ValuesIn(std::vector<EnumCapabilityCase>{
-                CASE0(STORAGE_CLASS, StorageClassUniformConstant),
-                CASE1(STORAGE_CLASS, StorageClassUniform, Shader),
-                CASE1(STORAGE_CLASS, StorageClassOutput, Shader),
-                CASE0(STORAGE_CLASS, StorageClassWorkgroup),
-                CASE0(STORAGE_CLASS, StorageClassCrossWorkgroup),
-                CASE2(STORAGE_CLASS, StorageClassPrivate, Shader,
+                CASE0(STORAGE_CLASS, StorageClass::UniformConstant),
+                CASE1(STORAGE_CLASS, StorageClass::Uniform, Shader),
+                CASE1(STORAGE_CLASS, StorageClass::Output, Shader),
+                CASE0(STORAGE_CLASS, StorageClass::Workgroup),
+                CASE0(STORAGE_CLASS, StorageClass::CrossWorkgroup),
+                CASE2(STORAGE_CLASS, StorageClass::Private, Shader,
                       VectorComputeINTEL),
-                CASE0(STORAGE_CLASS, StorageClassFunction),
-                CASE1(STORAGE_CLASS, StorageClassGeneric,
+                CASE0(STORAGE_CLASS, StorageClass::Function),
+                CASE1(STORAGE_CLASS, StorageClass::Generic,
                       GenericPointer),  // Bug 14287
-                CASE1(STORAGE_CLASS, StorageClassPushConstant, Shader),
-                CASE1(STORAGE_CLASS, StorageClassAtomicCounter, AtomicStorage),
-                CASE0(STORAGE_CLASS, StorageClassImage),
+                CASE1(STORAGE_CLASS, StorageClass::PushConstant, Shader),
+                CASE1(STORAGE_CLASS, StorageClass::AtomicCounter,
+                      AtomicStorage),
+                CASE0(STORAGE_CLASS, StorageClass::Image),
             })));
 
 // See SPIR-V Section 3.8 Dim
@@ -222,37 +252,35 @@
     Dim, EnumCapabilityTest,
     Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
             ValuesIn(std::vector<EnumCapabilityCase>{
-                CASE2(DIMENSIONALITY, Dim1D, Sampled1D, Image1D),
-                CASE3(DIMENSIONALITY, Dim2D, Kernel, Shader, ImageMSArray),
-                CASE0(DIMENSIONALITY, Dim3D),
-                CASE2(DIMENSIONALITY, DimCube, Shader, ImageCubeArray),
-                CASE2(DIMENSIONALITY, DimRect, SampledRect, ImageRect),
-                CASE2(DIMENSIONALITY, DimBuffer, SampledBuffer, ImageBuffer),
-                CASE1(DIMENSIONALITY, DimSubpassData, InputAttachment),
+                CASE1(DIMENSIONALITY, Dim::Dim1D, Sampled1D),
+                CASE0(DIMENSIONALITY, Dim::Dim2D),
+                CASE0(DIMENSIONALITY, Dim::Dim3D),
+                CASE1(DIMENSIONALITY, Dim::Cube, Shader),
+                CASE1(DIMENSIONALITY, Dim::Rect, SampledRect),
+                CASE1(DIMENSIONALITY, Dim::Buffer, SampledBuffer),
+                CASE1(DIMENSIONALITY, Dim::SubpassData, InputAttachment),
             })));
 
 // See SPIR-V Section 3.9 Sampler Addressing Mode
 INSTANTIATE_TEST_SUITE_P(
     SamplerAddressingMode, EnumCapabilityTest,
-    Combine(
-        Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
-        ValuesIn(std::vector<EnumCapabilityCase>{
-            CASE1(SAMPLER_ADDRESSING_MODE, SamplerAddressingModeNone, Kernel),
-            CASE1(SAMPLER_ADDRESSING_MODE, SamplerAddressingModeClampToEdge,
-                  Kernel),
-            CASE1(SAMPLER_ADDRESSING_MODE, SamplerAddressingModeClamp, Kernel),
-            CASE1(SAMPLER_ADDRESSING_MODE, SamplerAddressingModeRepeat, Kernel),
-            CASE1(SAMPLER_ADDRESSING_MODE, SamplerAddressingModeRepeatMirrored,
-                  Kernel),
-        })));
+    Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
+            ValuesIn(std::vector<EnumCapabilityCase>{
+                CASE0(SAMPLER_ADDRESSING_MODE, SamplerAddressingMode::None),
+                CASE0(SAMPLER_ADDRESSING_MODE,
+                      SamplerAddressingMode::ClampToEdge),
+                CASE0(SAMPLER_ADDRESSING_MODE, SamplerAddressingMode::Clamp),
+                CASE0(SAMPLER_ADDRESSING_MODE, SamplerAddressingMode::Repeat),
+                CASE0(SAMPLER_ADDRESSING_MODE,
+                      SamplerAddressingMode::RepeatMirrored)})));
 
 // See SPIR-V Section 3.10 Sampler Filter Mode
 INSTANTIATE_TEST_SUITE_P(
     SamplerFilterMode, EnumCapabilityTest,
     Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
             ValuesIn(std::vector<EnumCapabilityCase>{
-                CASE1(SAMPLER_FILTER_MODE, SamplerFilterModeNearest, Kernel),
-                CASE1(SAMPLER_FILTER_MODE, SamplerFilterModeLinear, Kernel),
+                CASE0(SAMPLER_FILTER_MODE, SamplerFilterMode::Nearest),
+                CASE0(SAMPLER_FILTER_MODE, SamplerFilterMode::Linear),
             })));
 
 // See SPIR-V Section 3.11 Image Format
@@ -261,76 +289,76 @@
     Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
             ValuesIn(std::vector<EnumCapabilityCase>{
                 // clang-format off
-        CASE0(SAMPLER_IMAGE_FORMAT, ImageFormatUnknown),
-        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRgba32f, Shader),
-        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRgba16f, Shader),
-        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatR32f, Shader),
-        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRgba8, Shader),
-        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRgba8Snorm, Shader),
-        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRg32f, StorageImageExtendedFormats),
-        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRg16f, StorageImageExtendedFormats),
-        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatR11fG11fB10f, StorageImageExtendedFormats),
-        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatR16f, StorageImageExtendedFormats),
-        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRgba16, StorageImageExtendedFormats),
-        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRgb10A2, StorageImageExtendedFormats),
-        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRg16, StorageImageExtendedFormats),
-        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRg8, StorageImageExtendedFormats),
-        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatR16, StorageImageExtendedFormats),
-        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatR8, StorageImageExtendedFormats),
-        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRgba16Snorm, StorageImageExtendedFormats),
-        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRg16Snorm, StorageImageExtendedFormats),
-        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRg8Snorm, StorageImageExtendedFormats),
-        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatR16Snorm, StorageImageExtendedFormats),
-        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatR8Snorm, StorageImageExtendedFormats),
-        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRgba32i, Shader),
-        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRgba16i, Shader),
-        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRgba8i, Shader),
-        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatR32i, Shader),
-        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRg32i, StorageImageExtendedFormats),
-        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRg16i, StorageImageExtendedFormats),
-        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRg8i, StorageImageExtendedFormats),
-        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatR16i, StorageImageExtendedFormats),
-        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatR8i, StorageImageExtendedFormats),
-        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRgba32ui, Shader),
-        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRgba16ui, Shader),
-        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRgba8ui, Shader),
-        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRgba8ui, Shader),
-        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRgb10a2ui, StorageImageExtendedFormats),
-        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRg32ui, StorageImageExtendedFormats),
-        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRg16ui, StorageImageExtendedFormats),
-        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRg8ui, StorageImageExtendedFormats),
-        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatR16ui, StorageImageExtendedFormats),
-        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatR8ui, StorageImageExtendedFormats),
+        CASE0(SAMPLER_IMAGE_FORMAT, ImageFormat::Unknown),
+        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormat::Rgba32f, Shader),
+        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormat::Rgba16f, Shader),
+        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormat::R32f, Shader),
+        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormat::Rgba8, Shader),
+        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormat::Rgba8Snorm, Shader),
+        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormat::Rg32f, StorageImageExtendedFormats),
+        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormat::Rg16f, StorageImageExtendedFormats),
+        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormat::R11fG11fB10f, StorageImageExtendedFormats),
+        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormat::R16f, StorageImageExtendedFormats),
+        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormat::Rgba16, StorageImageExtendedFormats),
+        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormat::Rgb10A2, StorageImageExtendedFormats),
+        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormat::Rg16, StorageImageExtendedFormats),
+        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormat::Rg8, StorageImageExtendedFormats),
+        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormat::R16, StorageImageExtendedFormats),
+        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormat::R8, StorageImageExtendedFormats),
+        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormat::Rgba16Snorm, StorageImageExtendedFormats),
+        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormat::Rg16Snorm, StorageImageExtendedFormats),
+        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormat::Rg8Snorm, StorageImageExtendedFormats),
+        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormat::R16Snorm, StorageImageExtendedFormats),
+        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormat::R8Snorm, StorageImageExtendedFormats),
+        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormat::Rgba32i, Shader),
+        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormat::Rgba16i, Shader),
+        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormat::Rgba8i, Shader),
+        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormat::R32i, Shader),
+        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormat::Rg32i, StorageImageExtendedFormats),
+        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormat::Rg16i, StorageImageExtendedFormats),
+        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormat::Rg8i, StorageImageExtendedFormats),
+        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormat::R16i, StorageImageExtendedFormats),
+        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormat::R8i, StorageImageExtendedFormats),
+        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormat::Rgba32ui, Shader),
+        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormat::Rgba16ui, Shader),
+        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormat::Rgba8ui, Shader),
+        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormat::Rgba8ui, Shader),
+        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormat::Rgb10a2ui, StorageImageExtendedFormats),
+        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormat::Rg32ui, StorageImageExtendedFormats),
+        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormat::Rg16ui, StorageImageExtendedFormats),
+        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormat::Rg8ui, StorageImageExtendedFormats),
+        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormat::R16ui, StorageImageExtendedFormats),
+        CASE1(SAMPLER_IMAGE_FORMAT, ImageFormat::R8ui, StorageImageExtendedFormats),
                 // clang-format on
             })));
 
 // See SPIR-V Section 3.12 Image Channel Order
 INSTANTIATE_TEST_SUITE_P(
     ImageChannelOrder, EnumCapabilityTest,
-    Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
-            ValuesIn(std::vector<EnumCapabilityCase>{
-                CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderR, Kernel),
-                CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderA, Kernel),
-                CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderRG, Kernel),
-                CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderRA, Kernel),
-                CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderRGB, Kernel),
-                CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderRGBA, Kernel),
-                CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderBGRA, Kernel),
-                CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderARGB, Kernel),
-                CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderIntensity, Kernel),
-                CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderLuminance, Kernel),
-                CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderRx, Kernel),
-                CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderRGx, Kernel),
-                CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderRGBx, Kernel),
-                CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderDepth, Kernel),
-                CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderDepthStencil,
-                      Kernel),
-                CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrdersRGB, Kernel),
-                CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrdersRGBx, Kernel),
-                CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrdersRGBA, Kernel),
-                CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrdersBGRA, Kernel),
-                CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderABGR, Kernel),
-            })));
+    Combine(
+        Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
+        ValuesIn(std::vector<EnumCapabilityCase>{
+            CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrder::R, Kernel),
+            CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrder::A, Kernel),
+            CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrder::RG, Kernel),
+            CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrder::RA, Kernel),
+            CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrder::RGB, Kernel),
+            CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrder::RGBA, Kernel),
+            CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrder::BGRA, Kernel),
+            CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrder::ARGB, Kernel),
+            CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrder::Intensity, Kernel),
+            CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrder::Luminance, Kernel),
+            CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrder::Rx, Kernel),
+            CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrder::RGx, Kernel),
+            CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrder::RGBx, Kernel),
+            CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrder::Depth, Kernel),
+            CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrder::DepthStencil, Kernel),
+            CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrder::sRGB, Kernel),
+            CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrder::sRGBx, Kernel),
+            CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrder::sRGBA, Kernel),
+            CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrder::sBGRA, Kernel),
+            CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrder::ABGR, Kernel),
+        })));
 
 // See SPIR-V Section 3.13 Image Channel Data Type
 INSTANTIATE_TEST_SUITE_P(
@@ -338,23 +366,23 @@
     Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
             ValuesIn(std::vector<EnumCapabilityCase>{
                 // clang-format off
-                CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataTypeSnormInt8, Kernel),
-                CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataTypeSnormInt16, Kernel),
-                CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataTypeUnormInt8, Kernel),
-                CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataTypeUnormInt16, Kernel),
-                CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataTypeUnormShort565, Kernel),
-                CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataTypeUnormShort555, Kernel),
-                CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataTypeUnormInt101010, Kernel),
-                CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataTypeSignedInt8, Kernel),
-                CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataTypeSignedInt16, Kernel),
-                CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataTypeSignedInt32, Kernel),
-                CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataTypeUnsignedInt8, Kernel),
-                CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataTypeUnsignedInt16, Kernel),
-                CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataTypeUnsignedInt32, Kernel),
-                CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataTypeHalfFloat, Kernel),
-                CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataTypeFloat, Kernel),
-                CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataTypeUnormInt24, Kernel),
-                CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataTypeUnormInt101010_2, Kernel),
+                CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataType::SnormInt8, Kernel),
+                CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataType::SnormInt16, Kernel),
+                CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataType::UnormInt8, Kernel),
+                CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataType::UnormInt16, Kernel),
+                CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataType::UnormShort565, Kernel),
+                CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataType::UnormShort555, Kernel),
+                CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataType::UnormInt101010, Kernel),
+                CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataType::SignedInt8, Kernel),
+                CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataType::SignedInt16, Kernel),
+                CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataType::SignedInt32, Kernel),
+                CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataType::UnsignedInt8, Kernel),
+                CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataType::UnsignedInt16, Kernel),
+                CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataType::UnsignedInt32, Kernel),
+                CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataType::HalfFloat, Kernel),
+                CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataType::Float, Kernel),
+                CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataType::UnormInt24, Kernel),
+                CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataType::UnormInt101010_2, Kernel),
                 // clang-format on
             })));
 
@@ -364,15 +392,15 @@
     Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
             ValuesIn(std::vector<EnumCapabilityCase>{
                 // clang-format off
-                CASE0(OPTIONAL_IMAGE, ImageOperandsMaskNone),
-                CASE1(OPTIONAL_IMAGE, ImageOperandsBiasMask, Shader),
-                CASE0(OPTIONAL_IMAGE, ImageOperandsLodMask),
-                CASE0(OPTIONAL_IMAGE, ImageOperandsGradMask),
-                CASE0(OPTIONAL_IMAGE, ImageOperandsConstOffsetMask),
-                CASE1(OPTIONAL_IMAGE, ImageOperandsOffsetMask, ImageGatherExtended),
-                CASE1(OPTIONAL_IMAGE, ImageOperandsConstOffsetsMask, ImageGatherExtended),
-                CASE0(OPTIONAL_IMAGE, ImageOperandsSampleMask),
-                CASE1(OPTIONAL_IMAGE, ImageOperandsMinLodMask, MinLod),
+                CASE0(OPTIONAL_IMAGE, ImageOperandsMask::MaskNone),
+                CASE1(OPTIONAL_IMAGE, ImageOperandsMask::Bias, Shader),
+                CASE0(OPTIONAL_IMAGE, ImageOperandsMask::Lod),
+                CASE0(OPTIONAL_IMAGE, ImageOperandsMask::Grad),
+                CASE0(OPTIONAL_IMAGE, ImageOperandsMask::ConstOffset),
+                CASE1(OPTIONAL_IMAGE, ImageOperandsMask::Offset, ImageGatherExtended),
+                CASE1(OPTIONAL_IMAGE, ImageOperandsMask::ConstOffsets, ImageGatherExtended),
+                CASE0(OPTIONAL_IMAGE, ImageOperandsMask::Sample),
+                CASE1(OPTIONAL_IMAGE, ImageOperandsMask::MinLod, MinLod),
                 // clang-format on
             })));
 
@@ -381,8 +409,8 @@
     LinkageType, EnumCapabilityTest,
     Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
             ValuesIn(std::vector<EnumCapabilityCase>{
-                CASE1(LINKAGE_TYPE, LinkageTypeExport, Linkage),
-                CASE1(LINKAGE_TYPE, LinkageTypeImport, Linkage),
+                CASE1(LINKAGE_TYPE, LinkageType::Export, Linkage),
+                CASE1(LINKAGE_TYPE, LinkageType::Import, Linkage),
             })));
 
 // See SPIR-V Section 3.18 Access Qualifier
@@ -390,9 +418,9 @@
     AccessQualifier, EnumCapabilityTest,
     Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
             ValuesIn(std::vector<EnumCapabilityCase>{
-                CASE1(ACCESS_QUALIFIER, AccessQualifierReadOnly, Kernel),
-                CASE1(ACCESS_QUALIFIER, AccessQualifierWriteOnly, Kernel),
-                CASE1(ACCESS_QUALIFIER, AccessQualifierReadWrite, Kernel),
+                CASE1(ACCESS_QUALIFIER, AccessQualifier::ReadOnly, Kernel),
+                CASE1(ACCESS_QUALIFIER, AccessQualifier::WriteOnly, Kernel),
+                CASE1(ACCESS_QUALIFIER, AccessQualifier::ReadWrite, Kernel),
             })));
 
 // See SPIR-V Section 3.19 Function Parameter Attribute
@@ -401,70 +429,78 @@
     Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
             ValuesIn(std::vector<EnumCapabilityCase>{
                 // clang-format off
-                CASE1(FUNCTION_PARAMETER_ATTRIBUTE, FunctionParameterAttributeZext, Kernel),
-                CASE1(FUNCTION_PARAMETER_ATTRIBUTE, FunctionParameterAttributeSext, Kernel),
-                CASE1(FUNCTION_PARAMETER_ATTRIBUTE, FunctionParameterAttributeByVal, Kernel),
-                CASE1(FUNCTION_PARAMETER_ATTRIBUTE, FunctionParameterAttributeSret, Kernel),
-                CASE1(FUNCTION_PARAMETER_ATTRIBUTE, FunctionParameterAttributeNoAlias, Kernel),
-                CASE1(FUNCTION_PARAMETER_ATTRIBUTE, FunctionParameterAttributeNoCapture, Kernel),
-                CASE1(FUNCTION_PARAMETER_ATTRIBUTE, FunctionParameterAttributeNoWrite, Kernel),
-                CASE1(FUNCTION_PARAMETER_ATTRIBUTE, FunctionParameterAttributeNoReadWrite, Kernel),
+                CASE1(FUNCTION_PARAMETER_ATTRIBUTE, FunctionParameterAttribute::Zext, Kernel),
+                CASE1(FUNCTION_PARAMETER_ATTRIBUTE, FunctionParameterAttribute::Sext, Kernel),
+                CASE1(FUNCTION_PARAMETER_ATTRIBUTE, FunctionParameterAttribute::ByVal, Kernel),
+                CASE1(FUNCTION_PARAMETER_ATTRIBUTE, FunctionParameterAttribute::Sret, Kernel),
+                CASE1(FUNCTION_PARAMETER_ATTRIBUTE, FunctionParameterAttribute::NoAlias, Kernel),
+                CASE1(FUNCTION_PARAMETER_ATTRIBUTE, FunctionParameterAttribute::NoCapture, Kernel),
+                CASE1(FUNCTION_PARAMETER_ATTRIBUTE, FunctionParameterAttribute::NoWrite, Kernel),
+                CASE1(FUNCTION_PARAMETER_ATTRIBUTE, FunctionParameterAttribute::NoReadWrite, Kernel),
                 // clang-format on
             })));
 
 // See SPIR-V Section 3.20 Decoration
 INSTANTIATE_TEST_SUITE_P(
-    Decoration, EnumCapabilityTest,
+    Decoration_1_1, EnumCapabilityTest,
     Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
             ValuesIn(std::vector<EnumCapabilityCase>{
-                CASE1(DECORATION, DecorationRelaxedPrecision, Shader),
+                CASE1(DECORATION, Decoration::RelaxedPrecision, Shader),
                 // DecorationSpecId handled below.
-                CASE1(DECORATION, DecorationBlock, Shader),
-                CASE1(DECORATION, DecorationBufferBlock, Shader),
-                CASE1(DECORATION, DecorationRowMajor, Matrix),
-                CASE1(DECORATION, DecorationColMajor, Matrix),
-                CASE1(DECORATION, DecorationArrayStride, Shader),
-                CASE1(DECORATION, DecorationMatrixStride, Matrix),  // Bug 15234
-                CASE1(DECORATION, DecorationGLSLShared, Shader),
-                CASE1(DECORATION, DecorationGLSLPacked, Shader),
-                CASE1(DECORATION, DecorationCPacked, Kernel),
-                CASE0(DECORATION, DecorationBuiltIn),  // Bug 15248
+                CASE1(DECORATION, Decoration::Block, Shader),
+                CASE1(DECORATION, Decoration::BufferBlock, Shader),
+                CASE1(DECORATION, Decoration::RowMajor, Matrix),
+                CASE1(DECORATION, Decoration::ColMajor, Matrix),
+                CASE1(DECORATION, Decoration::ArrayStride, Shader),
+                CASE1(DECORATION, Decoration::MatrixStride,
+                      Matrix),  // Bug 15234
+                CASE1(DECORATION, Decoration::GLSLShared, Shader),
+                CASE1(DECORATION, Decoration::GLSLPacked, Shader),
+                CASE1(DECORATION, Decoration::CPacked, Kernel),
+                CASE0(DECORATION, Decoration::BuiltIn),  // Bug 15248
                 // Value 12 placeholder
-                CASE1(DECORATION, DecorationNoPerspective, Shader),
-                CASE1(DECORATION, DecorationFlat, Shader),
-                CASE1(DECORATION, DecorationPatch, Tessellation),
-                CASE1(DECORATION, DecorationCentroid, Shader),
-                CASE1(DECORATION, DecorationSample,
+                CASE1(DECORATION, Decoration::NoPerspective, Shader),
+                CASE1(DECORATION, Decoration::Flat, Shader),
+                CASE1(DECORATION, Decoration::Patch, Tessellation),
+                CASE1(DECORATION, Decoration::Centroid, Shader),
+                CASE1(DECORATION, Decoration::Sample,
                       SampleRateShading),  // Bug 15234
-                CASE1(DECORATION, DecorationInvariant, Shader),
-                CASE0(DECORATION, DecorationRestrict),
-                CASE0(DECORATION, DecorationAliased),
-                CASE0(DECORATION, DecorationVolatile),
-                CASE1(DECORATION, DecorationConstant, Kernel),
-                CASE0(DECORATION, DecorationCoherent),
-                CASE0(DECORATION, DecorationNonWritable),
-                CASE0(DECORATION, DecorationNonReadable),
-                CASE1(DECORATION, DecorationUniform, Shader),
+                CASE1(DECORATION, Decoration::Invariant, Shader),
+                CASE0(DECORATION, Decoration::Restrict),
+                CASE0(DECORATION, Decoration::Aliased),
+                CASE0(DECORATION, Decoration::Volatile),
+                CASE1(DECORATION, Decoration::Constant, Kernel),
+                CASE0(DECORATION, Decoration::Coherent),
+                CASE0(DECORATION, Decoration::NonWritable),
+                CASE0(DECORATION, Decoration::NonReadable),
+                CASE1(DECORATION, Decoration::Uniform, Shader),
                 // Value 27 is an intentional gap in the spec numbering.
-                CASE1(DECORATION, DecorationSaturatedConversion, Kernel),
-                CASE1(DECORATION, DecorationStream, GeometryStreams),
-                CASE1(DECORATION, DecorationLocation, Shader),
-                CASE1(DECORATION, DecorationComponent, Shader),
-                CASE1(DECORATION, DecorationIndex, Shader),
-                CASE1(DECORATION, DecorationBinding, Shader),
-                CASE1(DECORATION, DecorationDescriptorSet, Shader),
-                CASE1(DECORATION, DecorationOffset, Shader),  // Bug 15268
-                CASE1(DECORATION, DecorationXfbBuffer, TransformFeedback),
-                CASE1(DECORATION, DecorationXfbStride, TransformFeedback),
-                CASE1(DECORATION, DecorationFuncParamAttr, Kernel),
-                CASE1(DECORATION, DecorationFPFastMathMode, Kernel),
-                CASE1(DECORATION, DecorationLinkageAttributes, Linkage),
-                CASE1(DECORATION, DecorationNoContraction, Shader),
-                CASE1(DECORATION, DecorationInputAttachmentIndex,
+                CASE1(DECORATION, Decoration::SaturatedConversion, Kernel),
+                CASE1(DECORATION, Decoration::Stream, GeometryStreams),
+                CASE1(DECORATION, Decoration::Location, Shader),
+                CASE1(DECORATION, Decoration::Component, Shader),
+                CASE1(DECORATION, Decoration::Index, Shader),
+                CASE1(DECORATION, Decoration::Binding, Shader),
+                CASE1(DECORATION, Decoration::DescriptorSet, Shader),
+                CASE1(DECORATION, Decoration::Offset, Shader),  // Bug 15268
+                CASE1(DECORATION, Decoration::XfbBuffer, TransformFeedback),
+                CASE1(DECORATION, Decoration::XfbStride, TransformFeedback),
+                CASE1(DECORATION, Decoration::FuncParamAttr, Kernel),
+                CASE1(DECORATION, Decoration::FPFastMathMode, Kernel),
+                CASE1(DECORATION, Decoration::LinkageAttributes, Linkage),
+                CASE1(DECORATION, Decoration::NoContraction, Shader),
+                CASE1(DECORATION, Decoration::InputAttachmentIndex,
                       InputAttachment),
-                CASE1(DECORATION, DecorationAlignment, Kernel),
+                CASE1(DECORATION, Decoration::Alignment, Kernel),
             })));
 
+// See SPIR-V Section 3.20 Decoration
+INSTANTIATE_TEST_SUITE_P(Decoration_1_6, EnumCapabilityTest,
+                         Combine(Values(SPV_ENV_UNIVERSAL_1_6),
+                                 ValuesIn(std::vector<EnumCapabilityCase>{
+                                     CASE2(DECORATION, Decoration::Uniform,
+                                           Shader, UniformDecoration)})));
+
 #if 0
 // SpecId has different requirements in v1.0 and v1.1:
 INSTANTIATE_TEST_SUITE_P(DecorationSpecIdV10, EnumCapabilityTest,
@@ -477,8 +513,8 @@
     DecorationV11, EnumCapabilityTest,
     Combine(Values(SPV_ENV_UNIVERSAL_1_1),
             ValuesIn(std::vector<EnumCapabilityCase>{
-                CASE2(DECORATION, DecorationSpecId, Shader, Kernel),
-                CASE1(DECORATION, DecorationMaxByteOffset, Addresses)})));
+                CASE2(DECORATION, Decoration::SpecId, Shader, Kernel),
+                CASE1(DECORATION, Decoration::MaxByteOffset, Addresses)})));
 
 // See SPIR-V Section 3.21 BuiltIn
 INSTANTIATE_TEST_SUITE_P(
@@ -487,51 +523,51 @@
         Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
         ValuesIn(std::vector<EnumCapabilityCase>{
             // clang-format off
-            CASE1(BUILT_IN, BuiltInPosition, Shader),
-            CASE1(BUILT_IN, BuiltInPointSize, Shader),
+            CASE1(BUILT_IN, BuiltIn::Position, Shader),
+            CASE1(BUILT_IN, BuiltIn::PointSize, Shader),
             // 2 is an intentional gap in the spec numbering.
-            CASE1(BUILT_IN, BuiltInClipDistance, ClipDistance),  // Bug 1407, 15234
-            CASE1(BUILT_IN, BuiltInCullDistance, CullDistance),  // Bug 1407, 15234
-            CASE1(BUILT_IN, BuiltInVertexId, Shader),
-            CASE1(BUILT_IN, BuiltInInstanceId, Shader),
-            CASE6(BUILT_IN, BuiltInPrimitiveId, Geometry, Tessellation,
+            CASE1(BUILT_IN, BuiltIn::ClipDistance, ClipDistance),  // Bug 1407, 15234
+            CASE1(BUILT_IN, BuiltIn::CullDistance, CullDistance),  // Bug 1407, 15234
+            CASE1(BUILT_IN, BuiltIn::VertexId, Shader),
+            CASE1(BUILT_IN, BuiltIn::InstanceId, Shader),
+            CASE6(BUILT_IN, BuiltIn::PrimitiveId, Geometry, Tessellation,
                   RayTracingNV, RayTracingKHR, MeshShadingNV, MeshShadingEXT),
-            CASE2(BUILT_IN, BuiltInInvocationId, Geometry, Tessellation),
-            CASE4(BUILT_IN, BuiltInLayer, Geometry, ShaderViewportIndexLayerEXT, MeshShadingNV, MeshShadingEXT),
-            CASE4(BUILT_IN, BuiltInViewportIndex, MultiViewport, ShaderViewportIndexLayerEXT, MeshShadingNV, MeshShadingEXT),  // Bug 15234
-            CASE1(BUILT_IN, BuiltInTessLevelOuter, Tessellation),
-            CASE1(BUILT_IN, BuiltInTessLevelInner, Tessellation),
-            CASE1(BUILT_IN, BuiltInTessCoord, Tessellation),
-            CASE1(BUILT_IN, BuiltInPatchVertices, Tessellation),
-            CASE1(BUILT_IN, BuiltInFragCoord, Shader),
-            CASE1(BUILT_IN, BuiltInPointCoord, Shader),
-            CASE1(BUILT_IN, BuiltInFrontFacing, Shader),
-            CASE1(BUILT_IN, BuiltInSampleId, SampleRateShading),  // Bug 15234
-            CASE1(BUILT_IN, BuiltInSamplePosition, SampleRateShading), // Bug 15234
-            CASE1(BUILT_IN, BuiltInSampleMask, Shader),  // Bug 15234, Issue 182
+            CASE2(BUILT_IN, BuiltIn::InvocationId, Geometry, Tessellation),
+            CASE4(BUILT_IN, BuiltIn::Layer, Geometry, ShaderViewportIndexLayerEXT, MeshShadingNV, MeshShadingEXT),
+            CASE4(BUILT_IN, BuiltIn::ViewportIndex, MultiViewport, ShaderViewportIndexLayerEXT, MeshShadingNV, MeshShadingEXT),  // Bug 15234
+            CASE1(BUILT_IN, BuiltIn::TessLevelOuter, Tessellation),
+            CASE1(BUILT_IN, BuiltIn::TessLevelInner, Tessellation),
+            CASE1(BUILT_IN, BuiltIn::TessCoord, Tessellation),
+            CASE1(BUILT_IN, BuiltIn::PatchVertices, Tessellation),
+            CASE1(BUILT_IN, BuiltIn::FragCoord, Shader),
+            CASE1(BUILT_IN, BuiltIn::PointCoord, Shader),
+            CASE1(BUILT_IN, BuiltIn::FrontFacing, Shader),
+            CASE1(BUILT_IN, BuiltIn::SampleId, SampleRateShading),  // Bug 15234
+            CASE1(BUILT_IN, BuiltIn::SamplePosition, SampleRateShading), // Bug 15234
+            CASE1(BUILT_IN, BuiltIn::SampleMask, Shader),  // Bug 15234, Issue 182
             // Value 21 intentionally missing
-            CASE1(BUILT_IN, BuiltInFragDepth, Shader),
-            CASE1(BUILT_IN, BuiltInHelperInvocation, Shader),
-            CASE0(BUILT_IN, BuiltInNumWorkgroups),
-            CASE0(BUILT_IN, BuiltInWorkgroupSize),
-            CASE0(BUILT_IN, BuiltInWorkgroupId),
-            CASE0(BUILT_IN, BuiltInLocalInvocationId),
-            CASE0(BUILT_IN, BuiltInGlobalInvocationId),
-            CASE0(BUILT_IN, BuiltInLocalInvocationIndex),
-            CASE1(BUILT_IN, BuiltInWorkDim, Kernel),
-            CASE1(BUILT_IN, BuiltInGlobalSize, Kernel),
-            CASE1(BUILT_IN, BuiltInEnqueuedWorkgroupSize, Kernel),
-            CASE1(BUILT_IN, BuiltInGlobalOffset, Kernel),
-            CASE1(BUILT_IN, BuiltInGlobalLinearId, Kernel),
+            CASE1(BUILT_IN, BuiltIn::FragDepth, Shader),
+            CASE1(BUILT_IN, BuiltIn::HelperInvocation, Shader),
+            CASE0(BUILT_IN, BuiltIn::NumWorkgroups),
+            CASE0(BUILT_IN, BuiltIn::WorkgroupSize),
+            CASE0(BUILT_IN, BuiltIn::WorkgroupId),
+            CASE0(BUILT_IN, BuiltIn::LocalInvocationId),
+            CASE0(BUILT_IN, BuiltIn::GlobalInvocationId),
+            CASE0(BUILT_IN, BuiltIn::LocalInvocationIndex),
+            CASE1(BUILT_IN, BuiltIn::WorkDim, Kernel),
+            CASE1(BUILT_IN, BuiltIn::GlobalSize, Kernel),
+            CASE1(BUILT_IN, BuiltIn::EnqueuedWorkgroupSize, Kernel),
+            CASE1(BUILT_IN, BuiltIn::GlobalOffset, Kernel),
+            CASE1(BUILT_IN, BuiltIn::GlobalLinearId, Kernel),
             // Value 35 intentionally missing
-            CASE2(BUILT_IN, BuiltInSubgroupSize, Kernel, SubgroupBallotKHR),
-            CASE1(BUILT_IN, BuiltInSubgroupMaxSize, Kernel),
-            CASE1(BUILT_IN, BuiltInNumSubgroups, Kernel),
-            CASE1(BUILT_IN, BuiltInNumEnqueuedSubgroups, Kernel),
-            CASE1(BUILT_IN, BuiltInSubgroupId, Kernel),
-            CASE2(BUILT_IN, BuiltInSubgroupLocalInvocationId, Kernel, SubgroupBallotKHR),
-            CASE1(BUILT_IN, BuiltInVertexIndex, Shader),
-            CASE1(BUILT_IN, BuiltInInstanceIndex, Shader),
+            CASE2(BUILT_IN, BuiltIn::SubgroupSize, Kernel, SubgroupBallotKHR),
+            CASE1(BUILT_IN, BuiltIn::SubgroupMaxSize, Kernel),
+            CASE1(BUILT_IN, BuiltIn::NumSubgroups, Kernel),
+            CASE1(BUILT_IN, BuiltIn::NumEnqueuedSubgroups, Kernel),
+            CASE1(BUILT_IN, BuiltIn::SubgroupId, Kernel),
+            CASE2(BUILT_IN, BuiltIn::SubgroupLocalInvocationId, Kernel, SubgroupBallotKHR),
+            CASE1(BUILT_IN, BuiltIn::VertexIndex, Shader),
+            CASE1(BUILT_IN, BuiltIn::InstanceIndex, Shader),
             // clang-format on
         })));
 
@@ -541,9 +577,9 @@
         Values(SPV_ENV_UNIVERSAL_1_5),
         ValuesIn(std::vector<EnumCapabilityCase>{
             // SPIR-V 1.5 adds new capabilities to enable these two builtins.
-            CASE5(BUILT_IN, BuiltInLayer, Geometry, ShaderLayer,
+            CASE5(BUILT_IN, BuiltIn::Layer, Geometry, ShaderLayer,
                   ShaderViewportIndexLayerEXT, MeshShadingNV, MeshShadingEXT),
-            CASE5(BUILT_IN, BuiltInViewportIndex, MultiViewport,
+            CASE5(BUILT_IN, BuiltIn::ViewportIndex, MultiViewport,
                   ShaderViewportIndex, ShaderViewportIndexLayerEXT,
                   MeshShadingNV, MeshShadingEXT),
         })));
@@ -553,9 +589,9 @@
     SelectionControl, EnumCapabilityTest,
     Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
             ValuesIn(std::vector<EnumCapabilityCase>{
-                CASE0(SELECTION_CONTROL, SelectionControlMaskNone),
-                CASE0(SELECTION_CONTROL, SelectionControlFlattenMask),
-                CASE0(SELECTION_CONTROL, SelectionControlDontFlattenMask),
+                CASE0(SELECTION_CONTROL, SelectionControlMask::MaskNone),
+                CASE0(SELECTION_CONTROL, SelectionControlMask::Flatten),
+                CASE0(SELECTION_CONTROL, SelectionControlMask::DontFlatten),
             })));
 
 // See SPIR-V Section 3.23 Loop Control
@@ -563,17 +599,17 @@
     LoopControl, EnumCapabilityTest,
     Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
             ValuesIn(std::vector<EnumCapabilityCase>{
-                CASE0(LOOP_CONTROL, LoopControlMaskNone),
-                CASE0(LOOP_CONTROL, LoopControlUnrollMask),
-                CASE0(LOOP_CONTROL, LoopControlDontUnrollMask),
+                CASE0(LOOP_CONTROL, LoopControlMask::MaskNone),
+                CASE0(LOOP_CONTROL, LoopControlMask::Unroll),
+                CASE0(LOOP_CONTROL, LoopControlMask::DontUnroll),
             })));
 
 INSTANTIATE_TEST_SUITE_P(
     LoopControlV11, EnumCapabilityTest,
     Combine(Values(SPV_ENV_UNIVERSAL_1_1),
             ValuesIn(std::vector<EnumCapabilityCase>{
-                CASE0(LOOP_CONTROL, LoopControlDependencyInfiniteMask),
-                CASE0(LOOP_CONTROL, LoopControlDependencyLengthMask),
+                CASE0(LOOP_CONTROL, LoopControlMask::DependencyInfinite),
+                CASE0(LOOP_CONTROL, LoopControlMask::DependencyLength),
             })));
 
 // See SPIR-V Section 3.24 Function Control
@@ -581,11 +617,11 @@
     FunctionControl, EnumCapabilityTest,
     Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
             ValuesIn(std::vector<EnumCapabilityCase>{
-                CASE0(FUNCTION_CONTROL, FunctionControlMaskNone),
-                CASE0(FUNCTION_CONTROL, FunctionControlInlineMask),
-                CASE0(FUNCTION_CONTROL, FunctionControlDontInlineMask),
-                CASE0(FUNCTION_CONTROL, FunctionControlPureMask),
-                CASE0(FUNCTION_CONTROL, FunctionControlConstMask),
+                CASE0(FUNCTION_CONTROL, FunctionControlMask::MaskNone),
+                CASE0(FUNCTION_CONTROL, FunctionControlMask::Inline),
+                CASE0(FUNCTION_CONTROL, FunctionControlMask::DontInline),
+                CASE0(FUNCTION_CONTROL, FunctionControlMask::Pure),
+                CASE0(FUNCTION_CONTROL, FunctionControlMask::Const),
             })));
 
 // See SPIR-V Section 3.25 Memory Semantics <id>
@@ -594,20 +630,21 @@
     Combine(
         Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
         ValuesIn(std::vector<EnumCapabilityCase>{
-            CASE0(MEMORY_SEMANTICS_ID, MemorySemanticsMaskNone),
-            CASE0(MEMORY_SEMANTICS_ID, MemorySemanticsAcquireMask),
-            CASE0(MEMORY_SEMANTICS_ID, MemorySemanticsReleaseMask),
-            CASE0(MEMORY_SEMANTICS_ID, MemorySemanticsAcquireReleaseMask),
+            CASE0(MEMORY_SEMANTICS_ID, MemorySemanticsMask::MaskNone),
+            CASE0(MEMORY_SEMANTICS_ID, MemorySemanticsMask::Acquire),
+            CASE0(MEMORY_SEMANTICS_ID, MemorySemanticsMask::Release),
+            CASE0(MEMORY_SEMANTICS_ID, MemorySemanticsMask::AcquireRelease),
             CASE0(MEMORY_SEMANTICS_ID,
-                  MemorySemanticsSequentiallyConsistentMask),
-            CASE1(MEMORY_SEMANTICS_ID, MemorySemanticsUniformMemoryMask,
+                  MemorySemanticsMask::SequentiallyConsistent),
+            CASE1(MEMORY_SEMANTICS_ID, MemorySemanticsMask::UniformMemory,
                   Shader),
-            CASE0(MEMORY_SEMANTICS_ID, MemorySemanticsSubgroupMemoryMask),
-            CASE0(MEMORY_SEMANTICS_ID, MemorySemanticsWorkgroupMemoryMask),
-            CASE0(MEMORY_SEMANTICS_ID, MemorySemanticsCrossWorkgroupMemoryMask),
-            CASE1(MEMORY_SEMANTICS_ID, MemorySemanticsAtomicCounterMemoryMask,
+            CASE0(MEMORY_SEMANTICS_ID, MemorySemanticsMask::SubgroupMemory),
+            CASE0(MEMORY_SEMANTICS_ID, MemorySemanticsMask::WorkgroupMemory),
+            CASE0(MEMORY_SEMANTICS_ID,
+                  MemorySemanticsMask::CrossWorkgroupMemory),
+            CASE1(MEMORY_SEMANTICS_ID, MemorySemanticsMask::AtomicCounterMemory,
                   AtomicStorage),  // Bug 15234
-            CASE0(MEMORY_SEMANTICS_ID, MemorySemanticsImageMemoryMask),
+            CASE0(MEMORY_SEMANTICS_ID, MemorySemanticsMask::ImageMemory),
         })));
 
 // See SPIR-V Section 3.26 Memory Access
@@ -615,10 +652,10 @@
     MemoryAccess, EnumCapabilityTest,
     Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
             ValuesIn(std::vector<EnumCapabilityCase>{
-                CASE0(OPTIONAL_MEMORY_ACCESS, MemoryAccessMaskNone),
-                CASE0(OPTIONAL_MEMORY_ACCESS, MemoryAccessVolatileMask),
-                CASE0(OPTIONAL_MEMORY_ACCESS, MemoryAccessAlignedMask),
-                CASE0(OPTIONAL_MEMORY_ACCESS, MemoryAccessNontemporalMask),
+                CASE0(OPTIONAL_MEMORY_ACCESS, MemoryAccessMask::MaskNone),
+                CASE0(OPTIONAL_MEMORY_ACCESS, MemoryAccessMask::Volatile),
+                CASE0(OPTIONAL_MEMORY_ACCESS, MemoryAccessMask::Aligned),
+                CASE0(OPTIONAL_MEMORY_ACCESS, MemoryAccessMask::Nontemporal),
             })));
 
 // See SPIR-V Section 3.27 Scope <id>
@@ -627,12 +664,12 @@
     Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1,
                    SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_3),
             ValuesIn(std::vector<EnumCapabilityCase>{
-                CASE0(SCOPE_ID, ScopeCrossDevice),
-                CASE0(SCOPE_ID, ScopeDevice),
-                CASE0(SCOPE_ID, ScopeWorkgroup),
-                CASE0(SCOPE_ID, ScopeSubgroup),
-                CASE0(SCOPE_ID, ScopeInvocation),
-                CASE1(SCOPE_ID, ScopeQueueFamilyKHR, VulkanMemoryModelKHR),
+                CASE0(SCOPE_ID, Scope::CrossDevice),
+                CASE0(SCOPE_ID, Scope::Device),
+                CASE0(SCOPE_ID, Scope::Workgroup),
+                CASE0(SCOPE_ID, Scope::Subgroup),
+                CASE0(SCOPE_ID, Scope::Invocation),
+                CASE1(SCOPE_ID, Scope::QueueFamilyKHR, VulkanMemoryModelKHR),
             })));
 
 // See SPIR-V Section 3.28 Group Operation
@@ -640,11 +677,11 @@
     GroupOperation, EnumCapabilityTest,
     Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
             ValuesIn(std::vector<EnumCapabilityCase>{
-                CASE3(GROUP_OPERATION, GroupOperationReduce, Kernel,
+                CASE3(GROUP_OPERATION, GroupOperation::Reduce, Kernel,
                       GroupNonUniformArithmetic, GroupNonUniformBallot),
-                CASE3(GROUP_OPERATION, GroupOperationInclusiveScan, Kernel,
+                CASE3(GROUP_OPERATION, GroupOperation::InclusiveScan, Kernel,
                       GroupNonUniformArithmetic, GroupNonUniformBallot),
-                CASE3(GROUP_OPERATION, GroupOperationExclusiveScan, Kernel,
+                CASE3(GROUP_OPERATION, GroupOperation::ExclusiveScan, Kernel,
                       GroupNonUniformArithmetic, GroupNonUniformBallot),
             })));
 
@@ -653,9 +690,9 @@
     KernelEnqueueFlags, EnumCapabilityTest,
     Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
             ValuesIn(std::vector<EnumCapabilityCase>{
-                CASE1(KERNEL_ENQ_FLAGS, KernelEnqueueFlagsNoWait, Kernel),
-                CASE1(KERNEL_ENQ_FLAGS, KernelEnqueueFlagsWaitKernel, Kernel),
-                CASE1(KERNEL_ENQ_FLAGS, KernelEnqueueFlagsWaitWorkGroup,
+                CASE1(KERNEL_ENQ_FLAGS, KernelEnqueueFlags::NoWait, Kernel),
+                CASE1(KERNEL_ENQ_FLAGS, KernelEnqueueFlags::WaitKernel, Kernel),
+                CASE1(KERNEL_ENQ_FLAGS, KernelEnqueueFlags::WaitWorkGroup,
                       Kernel),
             })));
 
@@ -664,9 +701,9 @@
     KernelProfilingInfo, EnumCapabilityTest,
     Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
             ValuesIn(std::vector<EnumCapabilityCase>{
-                CASE0(KERNEL_PROFILING_INFO, KernelProfilingInfoMaskNone),
-                CASE1(KERNEL_PROFILING_INFO, KernelProfilingInfoCmdExecTimeMask,
-                      Kernel),
+                CASE0(KERNEL_PROFILING_INFO, KernelProfilingInfoMask::MaskNone),
+                CASE1(KERNEL_PROFILING_INFO,
+                      KernelProfilingInfoMask::CmdExecTime, Kernel),
             })));
 
 // See SPIR-V Section 3.31 Capability
@@ -676,62 +713,62 @@
         Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
         ValuesIn(std::vector<EnumCapabilityCase>{
             // clang-format off
-            CASE0(CAPABILITY, CapabilityMatrix),
-            CASE1(CAPABILITY, CapabilityShader, Matrix),
-            CASE1(CAPABILITY, CapabilityGeometry, Shader),
-            CASE1(CAPABILITY, CapabilityTessellation, Shader),
-            CASE0(CAPABILITY, CapabilityAddresses),
-            CASE0(CAPABILITY, CapabilityLinkage),
-            CASE0(CAPABILITY, CapabilityKernel),
-            CASE1(CAPABILITY, CapabilityVector16, Kernel),
-            CASE1(CAPABILITY, CapabilityFloat16Buffer, Kernel),
-            CASE0(CAPABILITY, CapabilityFloat16),  // Bug 15234
-            CASE0(CAPABILITY, CapabilityFloat64),
-            CASE0(CAPABILITY, CapabilityInt64),
-            CASE1(CAPABILITY, CapabilityInt64Atomics, Int64),
-            CASE1(CAPABILITY, CapabilityImageBasic, Kernel),
-            CASE1(CAPABILITY, CapabilityImageReadWrite, ImageBasic),
-            CASE1(CAPABILITY, CapabilityImageMipmap, ImageBasic),
+            CASE0(CAPABILITY, Capability::Matrix),
+            CASE1(CAPABILITY, Capability::Shader, Matrix),
+            CASE1(CAPABILITY, Capability::Geometry, Shader),
+            CASE1(CAPABILITY, Capability::Tessellation, Shader),
+            CASE0(CAPABILITY, Capability::Addresses),
+            CASE0(CAPABILITY, Capability::Linkage),
+            CASE0(CAPABILITY, Capability::Kernel),
+            CASE1(CAPABILITY, Capability::Vector16, Kernel),
+            CASE1(CAPABILITY, Capability::Float16Buffer, Kernel),
+            CASE0(CAPABILITY, Capability::Float16),  // Bug 15234
+            CASE0(CAPABILITY, Capability::Float64),
+            CASE0(CAPABILITY, Capability::Int64),
+            CASE1(CAPABILITY, Capability::Int64Atomics, Int64),
+            CASE1(CAPABILITY, Capability::ImageBasic, Kernel),
+            CASE1(CAPABILITY, Capability::ImageReadWrite, ImageBasic),
+            CASE1(CAPABILITY, Capability::ImageMipmap, ImageBasic),
             // Value 16 intentionally missing.
-            CASE1(CAPABILITY, CapabilityPipes, Kernel),
-            CASE0(CAPABILITY, CapabilityGroups),
-            CASE1(CAPABILITY, CapabilityDeviceEnqueue, Kernel),
-            CASE1(CAPABILITY, CapabilityLiteralSampler, Kernel),
-            CASE1(CAPABILITY, CapabilityAtomicStorage, Shader),
-            CASE0(CAPABILITY, CapabilityInt16),
-            CASE1(CAPABILITY, CapabilityTessellationPointSize, Tessellation),
-            CASE1(CAPABILITY, CapabilityGeometryPointSize, Geometry),
-            CASE1(CAPABILITY, CapabilityImageGatherExtended, Shader),
+            CASE1(CAPABILITY, Capability::Pipes, Kernel),
+            CASE0(CAPABILITY, Capability::Groups),
+            CASE1(CAPABILITY, Capability::DeviceEnqueue, Kernel),
+            CASE1(CAPABILITY, Capability::LiteralSampler, Kernel),
+            CASE1(CAPABILITY, Capability::AtomicStorage, Shader),
+            CASE0(CAPABILITY, Capability::Int16),
+            CASE1(CAPABILITY, Capability::TessellationPointSize, Tessellation),
+            CASE1(CAPABILITY, Capability::GeometryPointSize, Geometry),
+            CASE1(CAPABILITY, Capability::ImageGatherExtended, Shader),
             // Value 26 intentionally missing.
-            CASE1(CAPABILITY, CapabilityStorageImageMultisample, Shader),
-            CASE1(CAPABILITY, CapabilityUniformBufferArrayDynamicIndexing, Shader),
-            CASE1(CAPABILITY, CapabilitySampledImageArrayDynamicIndexing, Shader),
-            CASE1(CAPABILITY, CapabilityStorageBufferArrayDynamicIndexing, Shader),
-            CASE1(CAPABILITY, CapabilityStorageImageArrayDynamicIndexing, Shader),
-            CASE1(CAPABILITY, CapabilityClipDistance, Shader),
-            CASE1(CAPABILITY, CapabilityCullDistance, Shader),
-            CASE1(CAPABILITY, CapabilityImageCubeArray, SampledCubeArray),
-            CASE1(CAPABILITY, CapabilitySampleRateShading, Shader),
-            CASE1(CAPABILITY, CapabilityImageRect, SampledRect),
-            CASE1(CAPABILITY, CapabilitySampledRect, Shader),
-            CASE1(CAPABILITY, CapabilityGenericPointer, Addresses),
-            CASE0(CAPABILITY, CapabilityInt8),
-            CASE1(CAPABILITY, CapabilityInputAttachment, Shader),
-            CASE1(CAPABILITY, CapabilitySparseResidency, Shader),
-            CASE1(CAPABILITY, CapabilityMinLod, Shader),
-            CASE1(CAPABILITY, CapabilityImage1D, Sampled1D),
-            CASE1(CAPABILITY, CapabilitySampledCubeArray, Shader),
-            CASE1(CAPABILITY, CapabilityImageBuffer, SampledBuffer),
-            CASE1(CAPABILITY, CapabilityImageMSArray, Shader),
-            CASE1(CAPABILITY, CapabilityStorageImageExtendedFormats, Shader),
-            CASE1(CAPABILITY, CapabilityImageQuery, Shader),
-            CASE1(CAPABILITY, CapabilityDerivativeControl, Shader),
-            CASE1(CAPABILITY, CapabilityInterpolationFunction, Shader),
-            CASE1(CAPABILITY, CapabilityTransformFeedback, Shader),
-            CASE1(CAPABILITY, CapabilityGeometryStreams, Geometry),
-            CASE1(CAPABILITY, CapabilityStorageImageReadWithoutFormat, Shader),
-            CASE1(CAPABILITY, CapabilityStorageImageWriteWithoutFormat, Shader),
-            CASE1(CAPABILITY, CapabilityMultiViewport, Geometry),
+            CASE1(CAPABILITY, Capability::StorageImageMultisample, Shader),
+            CASE1(CAPABILITY, Capability::UniformBufferArrayDynamicIndexing, Shader),
+            CASE1(CAPABILITY, Capability::SampledImageArrayDynamicIndexing, Shader),
+            CASE1(CAPABILITY, Capability::StorageBufferArrayDynamicIndexing, Shader),
+            CASE1(CAPABILITY, Capability::StorageImageArrayDynamicIndexing, Shader),
+            CASE1(CAPABILITY, Capability::ClipDistance, Shader),
+            CASE1(CAPABILITY, Capability::CullDistance, Shader),
+            CASE1(CAPABILITY, Capability::ImageCubeArray, SampledCubeArray),
+            CASE1(CAPABILITY, Capability::SampleRateShading, Shader),
+            CASE1(CAPABILITY, Capability::ImageRect, SampledRect),
+            CASE1(CAPABILITY, Capability::SampledRect, Shader),
+            CASE1(CAPABILITY, Capability::GenericPointer, Addresses),
+            CASE0(CAPABILITY, Capability::Int8),
+            CASE1(CAPABILITY, Capability::InputAttachment, Shader),
+            CASE1(CAPABILITY, Capability::SparseResidency, Shader),
+            CASE1(CAPABILITY, Capability::MinLod, Shader),
+            CASE1(CAPABILITY, Capability::Image1D, Sampled1D),
+            CASE1(CAPABILITY, Capability::SampledCubeArray, Shader),
+            CASE1(CAPABILITY, Capability::ImageBuffer, SampledBuffer),
+            CASE1(CAPABILITY, Capability::ImageMSArray, Shader),
+            CASE1(CAPABILITY, Capability::StorageImageExtendedFormats, Shader),
+            CASE1(CAPABILITY, Capability::ImageQuery, Shader),
+            CASE1(CAPABILITY, Capability::DerivativeControl, Shader),
+            CASE1(CAPABILITY, Capability::InterpolationFunction, Shader),
+            CASE1(CAPABILITY, Capability::TransformFeedback, Shader),
+            CASE1(CAPABILITY, Capability::GeometryStreams, Geometry),
+            CASE1(CAPABILITY, Capability::StorageImageReadWithoutFormat, Shader),
+            CASE1(CAPABILITY, Capability::StorageImageWriteWithoutFormat, Shader),
+            CASE1(CAPABILITY, Capability::MultiViewport, Geometry),
             // clang-format on
         })));
 
@@ -739,9 +776,9 @@
     CapabilityDependsOnV11, EnumCapabilityTest,
     Combine(Values(SPV_ENV_UNIVERSAL_1_1),
             ValuesIn(std::vector<EnumCapabilityCase>{
-                CASE1(CAPABILITY, CapabilitySubgroupDispatch, DeviceEnqueue),
-                CASE1(CAPABILITY, CapabilityNamedBarrier, Kernel),
-                CASE1(CAPABILITY, CapabilityPipeStorage, Pipes),
+                CASE1(CAPABILITY, Capability::SubgroupDispatch, DeviceEnqueue),
+                CASE1(CAPABILITY, Capability::NamedBarrier, Kernel),
+                CASE1(CAPABILITY, Capability::PipeStorage, Pipes),
             })));
 
 #undef CASE0
diff --git a/test/operand_pattern_test.cpp b/test/operand_pattern_test.cpp
index a98a9d7..58b8a08 100644
--- a/test/operand_pattern_test.cpp
+++ b/test/operand_pattern_test.cpp
@@ -103,17 +103,18 @@
          {PREFIX1}},
         // Volatile has no operands.
         {SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS,
-         SpvMemoryAccessVolatileMask,
+         uint32_t(spv::MemoryAccessMask::Volatile),
          {PREFIX0},
          {PREFIX0}},
         // Aligned has one literal number operand.
         {SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS,
-         SpvMemoryAccessAlignedMask,
+         uint32_t(spv::MemoryAccessMask::Aligned),
          {PREFIX1},
          {PREFIX1, SPV_OPERAND_TYPE_LITERAL_INTEGER}},
         // Volatile with Aligned still has just one literal number operand.
         {SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS,
-         SpvMemoryAccessVolatileMask | SpvMemoryAccessAlignedMask,
+         uint32_t(spv::MemoryAccessMask::Volatile |
+                  spv::MemoryAccessMask::Aligned),
          {PREFIX1},
          {PREFIX1, SPV_OPERAND_TYPE_LITERAL_INTEGER}},
         // Newer masks are not tested
diff --git a/test/opt/CMakeLists.txt b/test/opt/CMakeLists.txt
index 15966c1..ceada13 100644
--- a/test/opt/CMakeLists.txt
+++ b/test/opt/CMakeLists.txt
@@ -18,8 +18,10 @@
 add_spvtools_unittest(TARGET opt
   SRCS aggressive_dead_code_elim_test.cpp
        amd_ext_to_khr.cpp
+       analyze_live_input_test.cpp
        assembly_builder_test.cpp
        block_merge_test.cpp
+       c_interface_test.cpp
        ccp_test.cpp
        cfg_cleanup_test.cpp
        cfg_test.cpp
@@ -42,8 +44,9 @@
        desc_sroa_test.cpp
        eliminate_dead_const_test.cpp
        eliminate_dead_functions_test.cpp
-       eliminate_dead_input_components_test.cpp
+       eliminate_dead_io_components_test.cpp
        eliminate_dead_member_test.cpp
+       eliminate_dead_output_stores_test.cpp
        feature_manager_test.cpp
        fix_func_call_arguments_test.cpp
        fix_storage_class_test.cpp
@@ -63,6 +66,7 @@
        instruction_list_test.cpp
        instruction_test.cpp
        interface_var_sroa_test.cpp
+       invocation_interlock_placement_test.cpp
        interp_fixup_test.cpp
        ir_builder.cpp
        ir_context_test.cpp
@@ -100,6 +104,8 @@
        strip_debug_info_test.cpp
        strip_nonsemantic_info_test.cpp
        struct_cfg_analysis_test.cpp
+       switch_descriptorset_test.cpp
+       trim_capabilities_pass_test.cpp
        type_manager_test.cpp
        types_test.cpp
        unify_const_test.cpp
@@ -112,3 +118,12 @@
   LIBS SPIRV-Tools-opt
   PCH_FILE pch_test_opt
 )
+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()
+  endif()
+endif()
diff --git a/test/opt/aggressive_dead_code_elim_test.cpp b/test/opt/aggressive_dead_code_elim_test.cpp
index e51098e..845c6a5 100644
--- a/test/opt/aggressive_dead_code_elim_test.cpp
+++ b/test/opt/aggressive_dead_code_elim_test.cpp
@@ -6764,7 +6764,7 @@
          %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 
+         %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
@@ -7777,6 +7777,221 @@
   SinglePassRunAndMatch<AggressiveDCEPass>(text, true);
 }
 
+TEST_F(AggressiveDCETest, RemoveOutputTrue) {
+  // Remove dead n_out output variable from module
+  const std::string text = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main" %c_out %c_in %n_out
+;CHECK: OpEntryPoint Vertex %main "main" %c_out %c_in
+               OpSource GLSL 450
+               OpName %main "main"
+               OpName %c_out "c_out"
+               OpName %c_in "c_in"
+               OpName %n_out "n_out"
+               OpDecorate %c_out Location 0
+               OpDecorate %c_in Location 0
+               OpDecorate %n_out Location 1
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+      %c_out = OpVariable %_ptr_Output_v4float Output
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+       %c_in = OpVariable %_ptr_Input_v4float Input
+    %v3float = OpTypeVector %float 3
+%_ptr_Output_v3float = OpTypePointer Output %v3float
+      %n_out = OpVariable %_ptr_Output_v3float Output
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %12 = OpLoad %v4float %c_in
+               OpStore %c_out %12
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_VULKAN_1_3);
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndMatch<AggressiveDCEPass>(text, true, false, true);
+}
+
+TEST_F(AggressiveDCETest, RemoveOutputFalse) {
+  // Remove dead n_out output variable from module
+  const std::string text = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main" %c_out %c_in %n_out
+;CHECK: OpEntryPoint Vertex %main "main" %c_out %c_in %n_out
+               OpSource GLSL 450
+               OpName %main "main"
+               OpName %c_out "c_out"
+               OpName %c_in "c_in"
+               OpName %n_out "n_out"
+               OpDecorate %c_out Location 0
+               OpDecorate %c_in Location 0
+               OpDecorate %n_out Location 1
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+      %c_out = OpVariable %_ptr_Output_v4float Output
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+       %c_in = OpVariable %_ptr_Input_v4float Input
+    %v3float = OpTypeVector %float 3
+%_ptr_Output_v3float = OpTypePointer Output %v3float
+      %n_out = OpVariable %_ptr_Output_v3float Output
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %12 = OpLoad %v4float %c_in
+               OpStore %c_out %12
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_VULKAN_1_3);
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndMatch<AggressiveDCEPass>(text, true, false, false);
+}
+
+TEST_F(AggressiveDCETest, RemoveWhenUsingPrintfExtension) {
+  // Remove dead n_out output variable from module
+  const std::string text = R"(
+; CHECK: OpExtInstImport "NonSemantic.DebugPrintf"
+; CHECK-NOT: OpVariable
+               OpCapability Shader
+          %1 = OpExtInstImport "NonSemantic.DebugPrintf"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 8 8 1
+               OpSource HLSL 660
+               OpName %main "main"
+       %uint = OpTypeInt 32 0
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+%_ptr_Function_uint = OpTypePointer Function %uint
+       %main = OpFunction %void None %5
+          %7 = OpLabel
+          %8 = OpVariable %_ptr_Function_uint Function
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_VULKAN_1_3);
+  SinglePassRunAndMatch<AggressiveDCEPass>(text, true);
+}
+
+TEST_F(AggressiveDCETest, FunctionReturnPointer) {
+  // Run DCE when a function returning a pointer to a reference is present
+
+  const std::string text = R"(
+               OpCapability Shader
+               OpCapability PhysicalStorageBufferAddresses
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel PhysicalStorageBuffer64 GLSL450
+               OpEntryPoint Vertex %2 "main" %3 %4
+               OpSource GLSL 450
+               OpSourceExtension "GL_EXT_buffer_reference"
+               OpSourceExtension "GL_EXT_scalar_block_layout"
+               OpName %4 "color"
+               OpMemberDecorate %5 0 Offset 0
+               OpDecorate %5 Block
+               OpMemberDecorate %7 0 Offset 0
+               OpDecorate %7 Block
+               OpDecorate %8 AliasedPointer
+               OpDecorate %4 Location 0
+          %9 = OpTypeVoid
+         %10 = OpTypeFunction %9
+               OpTypeForwardPointer %11 PhysicalStorageBuffer
+         %12 = OpTypeInt 32 0
+          %5 = OpTypeStruct %12
+         %11 = OpTypePointer PhysicalStorageBuffer %5
+;CHECK:         [[pt:%\w+]] = OpTypePointer PhysicalStorageBuffer {{%\w+}}
+         %13 = OpTypeFunction %11
+;CHECK:  [[pt_fn:%\w+]] = OpTypeFunction [[pt]]
+          %7 = OpTypeStruct %11
+         %14 = OpTypePointer PushConstant %7
+          %3 = OpVariable %14 PushConstant
+         %15 = OpTypeInt 32 1
+         %16 = OpConstant %15 0
+         %17 = OpTypePointer PushConstant %11
+         %18 = OpTypePointer Function %11
+         %19 = OpTypeFloat 32
+         %20 = OpTypeVector %19 4
+         %21 = OpTypePointer Output %20
+          %4 = OpVariable %21 Output
+         %22 = OpConstant %19 1
+         %23 = OpConstant %19 0
+         %24 = OpConstantComposite %20 %22 %23 %22 %22
+          %6 = OpFunction %11 None %13
+;CHECK:   [[fn:%\w+]] = OpFunction [[pt]] None [[pt_fn]]
+         %27 = OpLabel
+         %28 = OpAccessChain %17 %3 %16
+         %29 = OpLoad %11 %28
+               OpReturnValue %29
+               OpFunctionEnd
+          %2 = OpFunction %9 None %10
+         %25 = OpLabel
+          %8 = OpVariable %18 Function
+         %26 = OpFunctionCall %11 %6
+;CHECK:  {{%\w+}} = OpFunctionCall [[pt]] [[fn]]
+               OpStore %8 %26
+               OpStore %4 %24
+               OpReturn
+               OpFunctionEnd
+)";
+
+  // For physical storage buffer support
+  SetTargetEnv(SPV_ENV_VULKAN_1_2);
+  SinglePassRunAndMatch<AggressiveDCEPass>(text, true);
+}
+
+TEST_F(AggressiveDCETest, KeepBeginEndInvocationInterlock) {
+  // OpBeginInvocationInterlockEXT and OpEndInvocationInterlockEXT delimit a
+  // critical section. As such, they should be treated as if they have side
+  // effects and should not be removed.
+  const std::string test =
+      R"(OpCapability Shader
+OpCapability FragmentShaderSampleInterlockEXT
+OpExtension "SPV_EXT_fragment_shader_interlock"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %1 "main" %gl_FragCoord
+OpExecutionMode %1 OriginUpperLeft
+OpExecutionMode %1 SampleInterlockOrderedEXT
+OpDecorate %gl_FragCoord BuiltIn FragCoord
+%float = OpTypeFloat 32
+%float_0 = OpConstant %float 0
+%v4float = OpTypeVector %float 4
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%void = OpTypeVoid
+%8 = OpTypeFunction %void
+%bool = OpTypeBool
+%gl_FragCoord = OpVariable %_ptr_Input_v4float Input
+%1 = OpFunction %void None %8
+%10 = OpLabel
+%11 = OpLoad %v4float %gl_FragCoord
+%12 = OpCompositeExtract %float %11 0
+%13 = OpFOrdGreaterThan %bool %12 %float_0
+OpSelectionMerge %14 None
+OpBranchConditional %13 %15 %16
+%15 = OpLabel
+OpBeginInvocationInterlockEXT
+OpBranch %14
+%16 = OpLabel
+OpBeginInvocationInterlockEXT
+OpBranch %14
+%14 = OpLabel
+OpEndInvocationInterlockEXT
+OpReturn
+OpFunctionEnd
+)";
+
+  SinglePassRunAndCheck<AggressiveDCEPass>(test, test, true, true);
+}
+
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools
diff --git a/test/opt/amd_ext_to_khr.cpp b/test/opt/amd_ext_to_khr.cpp
index 3340e89..a520d60 100644
--- a/test/opt/amd_ext_to_khr.cpp
+++ b/test/opt/amd_ext_to_khr.cpp
@@ -26,15 +26,17 @@
 
 using ::testing::HasSubstr;
 
-std::string GetTest(std::string op_code, std::string new_op_code) {
+std::string GetTest(std::string op_code, std::string new_op_code,
+                    bool is_float = false) {
   const std::string text = R"(
 ; CHECK: OpCapability Shader
 ; CHECK-NOT: OpExtension "SPV_AMD_shader_ballot"
 ; CHECK: OpFunction
 ; CHECK-NEXT: OpLabel
-; CHECK-NEXT: [[undef:%\w+]] = OpUndef %uint
+; CHECK-NEXT: [[undef:%\w+]] = OpUndef %
 ; CHECK-NEXT: )" + new_op_code +
-                           R"( %uint %uint_3 Reduce [[undef]]
+                           " %" + (is_float ? "float" : "uint") +
+                           R"( %uint_3 Reduce [[undef]]
                OpCapability Shader
                OpCapability Groups
                OpExtension "SPV_AMD_shader_ballot"
@@ -44,12 +46,15 @@
        %void = OpTypeVoid
           %3 = OpTypeFunction %void
        %uint = OpTypeInt 32 0
+      %float = OpTypeFloat 32
      %uint_3 = OpConstant %uint 3
           %1 = OpFunction %void None %3
           %6 = OpLabel
-          %7 = OpUndef %uint
+          %7 = OpUndef %)" +
+                           (is_float ? "float" : "uint") + R"(
           %8 = )" + op_code +
-                           R"( %uint %uint_3 Reduce %7
+                           " %" + (is_float ? "float" : "uint") +
+                           R"( %uint_3 Reduce %7
                OpReturn
                OpFunctionEnd
 
@@ -64,7 +69,7 @@
 }
 TEST_F(AmdExtToKhrTest, ReplaceGroupFAddNonUniformAMD) {
   std::string text =
-      GetTest("OpGroupFAddNonUniformAMD", "OpGroupNonUniformFAdd");
+      GetTest("OpGroupFAddNonUniformAMD", "OpGroupNonUniformFAdd", true);
   SinglePassRunAndMatch<AmdExtensionToKhrPass>(text, true);
 }
 TEST_F(AmdExtToKhrTest, ReplaceGroupUMinNonUniformAMD) {
@@ -79,7 +84,7 @@
 }
 TEST_F(AmdExtToKhrTest, ReplaceGroupFMinNonUniformAMD) {
   std::string text =
-      GetTest("OpGroupFMinNonUniformAMD", "OpGroupNonUniformFMin");
+      GetTest("OpGroupFMinNonUniformAMD", "OpGroupNonUniformFMin", true);
   SinglePassRunAndMatch<AmdExtensionToKhrPass>(text, true);
 }
 TEST_F(AmdExtToKhrTest, ReplaceGroupUMaxNonUniformAMD) {
@@ -94,7 +99,7 @@
 }
 TEST_F(AmdExtToKhrTest, ReplaceGroupFMaxNonUniformAMD) {
   std::string text =
-      GetTest("OpGroupFMaxNonUniformAMD", "OpGroupNonUniformFMax");
+      GetTest("OpGroupFMaxNonUniformAMD", "OpGroupNonUniformFMax", true);
   SinglePassRunAndMatch<AmdExtensionToKhrPass>(text, true);
 }
 
diff --git a/test/opt/analyze_live_input_test.cpp b/test/opt/analyze_live_input_test.cpp
new file mode 100644
index 0000000..7f1ff2e
--- /dev/null
+++ b/test/opt/analyze_live_input_test.cpp
@@ -0,0 +1,909 @@
+// Copyright (c) 2022 The Khronos Group Inc.
+// Copyright (c) 2022 LunarG Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <unordered_set>
+
+#include "test/opt/pass_fixture.h"
+#include "test/opt/pass_utils.h"
+
+namespace spvtools {
+namespace opt {
+namespace {
+
+using AnalyzeLiveInputTest = PassTest<::testing::Test>;
+
+TEST_F(AnalyzeLiveInputTest, FragMultipleLocations) {
+  // Should report locations {2, 5}
+  //
+  // #version 450
+  //
+  // layout(location = 2) in Vertex
+  // {
+  //         vec4 color0;
+  //         vec4 color1;
+  //         vec4 color2[3];
+  // } iVert;
+  //
+  // layout(location = 0) out vec4 oFragColor;
+  //
+  // void main()
+  // {
+  //     oFragColor = iVert.color0 + iVert.color2[1];
+  // }
+  const std::string text = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main" %oFragColor %iVert
+               OpExecutionMode %main OriginUpperLeft
+               OpSource GLSL 450
+               OpName %main "main"
+               OpName %oFragColor "oFragColor"
+               OpName %Vertex "Vertex"
+               OpMemberName %Vertex 0 "color0"
+               OpMemberName %Vertex 1 "color1"
+               OpMemberName %Vertex 2 "color2"
+               OpName %iVert "iVert"
+               OpDecorate %oFragColor Location 0
+               OpDecorate %Vertex Block
+               OpDecorate %iVert Location 2
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+ %oFragColor = OpVariable %_ptr_Output_v4float Output
+       %uint = OpTypeInt 32 0
+     %uint_3 = OpConstant %uint 3
+%_arr_v4float_uint_3 = OpTypeArray %v4float %uint_3
+     %Vertex = OpTypeStruct %v4float %v4float %_arr_v4float_uint_3
+%_ptr_Input_Vertex = OpTypePointer Input %Vertex
+      %iVert = OpVariable %_ptr_Input_Vertex Input
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+      %int_2 = OpConstant %int 2
+      %int_1 = OpConstant %int 1
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %19 = OpAccessChain %_ptr_Input_v4float %iVert %int_0
+         %20 = OpLoad %v4float %19
+         %23 = OpAccessChain %_ptr_Input_v4float %iVert %int_2 %int_1
+         %24 = OpLoad %v4float %23
+         %25 = OpFAdd %v4float %20 %24
+               OpStore %oFragColor %25
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_VULKAN_1_3);
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+  std::unordered_set<uint32_t> live_inputs;
+  std::unordered_set<uint32_t> live_builtins;
+  auto result = SinglePassRunToBinary<AnalyzeLiveInputPass>(
+      text, true, &live_inputs, &live_builtins);
+
+  auto itr0 = live_inputs.find(0);
+  auto itr1 = live_inputs.find(1);
+  auto itr2 = live_inputs.find(2);
+  auto itr3 = live_inputs.find(3);
+  auto itr4 = live_inputs.find(4);
+  auto itr5 = live_inputs.find(5);
+  auto itr6 = live_inputs.find(6);
+
+  // Expect live_inputs == {2, 5}
+  EXPECT_TRUE(itr0 == live_inputs.end());
+  EXPECT_TRUE(itr1 == live_inputs.end());
+  EXPECT_TRUE(itr2 != live_inputs.end());
+  EXPECT_TRUE(itr3 == live_inputs.end());
+  EXPECT_TRUE(itr4 == live_inputs.end());
+  EXPECT_TRUE(itr5 != live_inputs.end());
+  EXPECT_TRUE(itr6 == live_inputs.end());
+}
+
+TEST_F(AnalyzeLiveInputTest, FragMatrix) {
+  // Should report locations {2, 8, 9, 10, 11}
+  //
+  // #version 450
+  //
+  // uniform ui_name {
+  //     int i;
+  // } ui_inst;
+  //
+  // layout(location = 2) in Vertex
+  // {
+  //         vec4 color0;
+  //         vec4 color1;
+  //         mat4 color2;
+  //         mat4 color3;
+  //         mat4 color4;
+  // } iVert;
+  //
+  // // Output variable for the color
+  // layout(location = 0) out vec4 oFragColor;
+  //
+  // void main()
+  // {
+  //     oFragColor = iVert.color0 + iVert.color3[ui_inst.i];
+  // }
+  const std::string text = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main" %oFragColor %iVert %ui_inst
+               OpExecutionMode %main OriginUpperLeft
+               OpSource GLSL 450
+               OpName %main "main"
+               OpName %oFragColor "oFragColor"
+               OpName %Vertex "Vertex"
+               OpMemberName %Vertex 0 "color0"
+               OpMemberName %Vertex 1 "color1"
+               OpMemberName %Vertex 2 "color2"
+               OpMemberName %Vertex 3 "color3"
+               OpMemberName %Vertex 4 "color4"
+               OpName %iVert "iVert"
+               OpName %ui_name "ui_name"
+               OpMemberName %ui_name 0 "i"
+               OpName %ui_inst "ui_inst"
+               OpDecorate %oFragColor Location 0
+               OpDecorate %Vertex Block
+               OpDecorate %iVert Location 2
+               OpMemberDecorate %ui_name 0 Offset 0
+               OpDecorate %ui_name Block
+               OpDecorate %ui_inst DescriptorSet 0
+               OpDecorate %ui_inst Binding 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+ %oFragColor = OpVariable %_ptr_Output_v4float Output
+%mat4v4float = OpTypeMatrix %v4float 4
+     %Vertex = OpTypeStruct %v4float %v4float %mat4v4float %mat4v4float %mat4v4float
+%_ptr_Input_Vertex = OpTypePointer Input %Vertex
+      %iVert = OpVariable %_ptr_Input_Vertex Input
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+      %int_3 = OpConstant %int 3
+    %ui_name = OpTypeStruct %int
+%_ptr_Uniform_ui_name = OpTypePointer Uniform %ui_name
+    %ui_inst = OpVariable %_ptr_Uniform_ui_name Uniform
+%_ptr_Uniform_int = OpTypePointer Uniform %int
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %17 = OpAccessChain %_ptr_Input_v4float %iVert %int_0
+         %18 = OpLoad %v4float %17
+         %24 = OpAccessChain %_ptr_Uniform_int %ui_inst %int_0
+         %25 = OpLoad %int %24
+         %26 = OpAccessChain %_ptr_Input_v4float %iVert %int_3 %25
+         %27 = OpLoad %v4float %26
+         %28 = OpFAdd %v4float %18 %27
+               OpStore %oFragColor %28
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_VULKAN_1_3);
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+  std::unordered_set<uint32_t> live_inputs;
+  std::unordered_set<uint32_t> live_builtins;
+  auto result = SinglePassRunToBinary<AnalyzeLiveInputPass>(
+      text, true, &live_inputs, &live_builtins);
+
+  auto itr0 = live_inputs.find(0);
+  auto itr1 = live_inputs.find(1);
+  auto itr2 = live_inputs.find(2);
+  auto itr3 = live_inputs.find(3);
+  auto itr4 = live_inputs.find(4);
+  auto itr5 = live_inputs.find(5);
+  auto itr6 = live_inputs.find(6);
+  auto itr7 = live_inputs.find(7);
+  auto itr8 = live_inputs.find(8);
+  auto itr9 = live_inputs.find(9);
+  auto itr10 = live_inputs.find(10);
+  auto itr11 = live_inputs.find(11);
+  auto itr12 = live_inputs.find(12);
+  auto itr13 = live_inputs.find(13);
+  auto itr14 = live_inputs.find(14);
+  auto itr15 = live_inputs.find(15);
+
+  // Expect live_inputs == {2, 8, 9, 10, 11}
+  EXPECT_TRUE(itr0 == live_inputs.end());
+  EXPECT_TRUE(itr1 == live_inputs.end());
+  EXPECT_TRUE(itr2 != live_inputs.end());
+  EXPECT_TRUE(itr3 == live_inputs.end());
+  EXPECT_TRUE(itr4 == live_inputs.end());
+  EXPECT_TRUE(itr5 == live_inputs.end());
+  EXPECT_TRUE(itr6 == live_inputs.end());
+  EXPECT_TRUE(itr7 == live_inputs.end());
+  EXPECT_TRUE(itr8 != live_inputs.end());
+  EXPECT_TRUE(itr9 != live_inputs.end());
+  EXPECT_TRUE(itr10 != live_inputs.end());
+  EXPECT_TRUE(itr11 != live_inputs.end());
+  EXPECT_TRUE(itr12 == live_inputs.end());
+  EXPECT_TRUE(itr13 == live_inputs.end());
+  EXPECT_TRUE(itr14 == live_inputs.end());
+  EXPECT_TRUE(itr15 == live_inputs.end());
+}
+
+TEST_F(AnalyzeLiveInputTest, FragMemberLocs) {
+  // Should report location {1}
+  //
+  // #version 450
+  //
+  // in Vertex
+  // {
+  //     layout (location = 1) vec4 Cd;
+  //     layout (location = 0) vec2 uv;
+  // } iVert;
+  //
+  // layout (location = 0) out vec4 fragColor;
+  //
+  // void main()
+  // {
+  //     vec4 color = vec4(iVert.Cd);
+  //     fragColor = color;
+  // }
+  const std::string text = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main" %iVert %fragColor
+               OpExecutionMode %main OriginUpperLeft
+               OpSource GLSL 450
+               OpName %main "main"
+               OpName %color "color"
+               OpName %Vertex "Vertex"
+               OpMemberName %Vertex 0 "Cd"
+               OpMemberName %Vertex 1 "uv"
+               OpName %iVert "iVert"
+               OpName %fragColor "fragColor"
+               OpMemberDecorate %Vertex 0 Location 1
+               OpMemberDecorate %Vertex 1 Location 0
+               OpDecorate %Vertex Block
+               OpDecorate %fragColor Location 0
+               OpDecorate %_struct_27 Block
+               OpMemberDecorate %_struct_27 0 Location 1
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+    %v2float = OpTypeVector %float 2
+     %Vertex = OpTypeStruct %v4float %v2float
+%_ptr_Input_Vertex = OpTypePointer Input %Vertex
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+  %fragColor = OpVariable %_ptr_Output_v4float Output
+ %_struct_27 = OpTypeStruct %v4float
+%_ptr_Input__struct_27 = OpTypePointer Input %_struct_27
+      %iVert = OpVariable %_ptr_Input__struct_27 Input
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+      %color = OpVariable %_ptr_Function_v4float Function
+         %17 = OpAccessChain %_ptr_Input_v4float %iVert %int_0
+         %18 = OpLoad %v4float %17
+         %19 = OpCompositeExtract %float %18 0
+         %20 = OpCompositeExtract %float %18 1
+         %21 = OpCompositeExtract %float %18 2
+         %22 = OpCompositeExtract %float %18 3
+         %23 = OpCompositeConstruct %v4float %19 %20 %21 %22
+               OpStore %color %23
+         %26 = OpLoad %v4float %color
+               OpStore %fragColor %26
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_VULKAN_1_3);
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+  std::unordered_set<uint32_t> live_inputs;
+  std::unordered_set<uint32_t> live_builtins;
+  auto result = SinglePassRunToBinary<AnalyzeLiveInputPass>(
+      text, true, &live_inputs, &live_builtins);
+
+  auto itr0 = live_inputs.find(0);
+  auto itr1 = live_inputs.find(1);
+
+  // Expect live_inputs == {2, 5}
+  EXPECT_TRUE(itr0 == live_inputs.end());
+  EXPECT_TRUE(itr1 != live_inputs.end());
+}
+
+TEST_F(AnalyzeLiveInputTest, ArrayedInput) {
+  // Tests handling of arrayed input seen in Tesc, Tese and Geom shaders.
+  //
+  // Should report location {1, 10}.
+  //
+  // #version 450
+  //
+  // layout (vertices = 4) out;
+  //
+  // layout (location = 1) in Vertex
+  // {
+  //                 vec4 p;
+  //                 vec3 n;
+  //                 vec4 f[100];
+  // } iVert[];
+  //
+  // layout (location = 0) out vec4 position[4];
+  //
+  // void main()
+  // {
+  //                 vec4 pos = iVert[gl_InvocationID].p *
+  //                            iVert[gl_InvocationID].f[7];
+  //                 position[gl_InvocationID] = pos;
+  // }
+  const std::string text = R"(
+               OpCapability Tessellation
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint TessellationControl %main "main" %iVert %gl_InvocationID %position
+               OpExecutionMode %main OutputVertices 4
+               OpSource GLSL 450
+               OpName %main "main"
+               OpName %Vertex "Vertex"
+               OpMemberName %Vertex 0 "p"
+               OpMemberName %Vertex 1 "n"
+               OpMemberName %Vertex 2 "f"
+               OpName %iVert "iVert"
+               OpName %gl_InvocationID "gl_InvocationID"
+               OpName %position "position"
+               OpDecorate %Vertex Block
+               OpDecorate %iVert Location 1
+               OpDecorate %gl_InvocationID BuiltIn InvocationId
+               OpDecorate %position Location 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+    %v3float = OpTypeVector %float 3
+       %uint = OpTypeInt 32 0
+   %uint_100 = OpConstant %uint 100
+%_arr_v4float_uint_100 = OpTypeArray %v4float %uint_100
+     %Vertex = OpTypeStruct %v4float %v3float %_arr_v4float_uint_100
+    %uint_32 = OpConstant %uint 32
+%_arr_Vertex_uint_32 = OpTypeArray %Vertex %uint_32
+%_ptr_Input__arr_Vertex_uint_32 = OpTypePointer Input %_arr_Vertex_uint_32
+      %iVert = OpVariable %_ptr_Input__arr_Vertex_uint_32 Input
+        %int = OpTypeInt 32 1
+%_ptr_Input_int = OpTypePointer Input %int
+%gl_InvocationID = OpVariable %_ptr_Input_int Input
+      %int_0 = OpConstant %int 0
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+      %int_2 = OpConstant %int 2
+      %int_7 = OpConstant %int 7
+     %uint_4 = OpConstant %uint 4
+%_arr_v4float_uint_4 = OpTypeArray %v4float %uint_4
+%_ptr_Output__arr_v4float_uint_4 = OpTypePointer Output %_arr_v4float_uint_4
+   %position = OpVariable %_ptr_Output__arr_v4float_uint_4 Output
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %22 = OpLoad %int %gl_InvocationID
+         %25 = OpAccessChain %_ptr_Input_v4float %iVert %22 %int_0
+         %26 = OpLoad %v4float %25
+         %30 = OpAccessChain %_ptr_Input_v4float %iVert %22 %int_2 %int_7
+         %31 = OpLoad %v4float %30
+         %32 = OpFMul %v4float %26 %31
+         %40 = OpAccessChain %_ptr_Output_v4float %position %22
+               OpStore %40 %32
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_VULKAN_1_3);
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+  std::unordered_set<uint32_t> live_inputs;
+  std::unordered_set<uint32_t> live_builtins;
+  auto result = SinglePassRunToBinary<AnalyzeLiveInputPass>(
+      text, true, &live_inputs, &live_builtins);
+
+  auto itr0 = live_inputs.find(0);
+  auto itr1 = live_inputs.find(1);
+  auto itr2 = live_inputs.find(2);
+  auto itr3 = live_inputs.find(3);
+  auto itr4 = live_inputs.find(4);
+  auto itr5 = live_inputs.find(5);
+  auto itr6 = live_inputs.find(6);
+  auto itr7 = live_inputs.find(7);
+  auto itr8 = live_inputs.find(8);
+  auto itr9 = live_inputs.find(9);
+  auto itr10 = live_inputs.find(10);
+  auto itr11 = live_inputs.find(11);
+
+  // Expect live_inputs == {1, 10}
+  EXPECT_TRUE(itr0 == live_inputs.end());
+  EXPECT_TRUE(itr1 != live_inputs.end());
+  EXPECT_TRUE(itr2 == live_inputs.end());
+  EXPECT_TRUE(itr3 == live_inputs.end());
+  EXPECT_TRUE(itr4 == live_inputs.end());
+  EXPECT_TRUE(itr5 == live_inputs.end());
+  EXPECT_TRUE(itr6 == live_inputs.end());
+  EXPECT_TRUE(itr7 == live_inputs.end());
+  EXPECT_TRUE(itr8 == live_inputs.end());
+  EXPECT_TRUE(itr9 == live_inputs.end());
+  EXPECT_TRUE(itr10 != live_inputs.end());
+  EXPECT_TRUE(itr11 == live_inputs.end());
+}
+
+TEST_F(AnalyzeLiveInputTest, ArrayedInputMemberLocs) {
+  // Tests handling of member locs with arrayed input seen in Tesc, Tese
+  // and Geom shaders.
+  //
+  // Should report location {1, 12}.
+  //
+  // #version 450
+  //
+  // layout (vertices = 4) out;
+  //
+  // in Vertex
+  // {
+  //     layout (location = 1) vec4 p;
+  //     layout (location = 3) vec3 n;
+  //     layout (location = 5) vec4 f[100];
+  // } iVert[];
+  //
+  // layout (location = 0) out vec4 position[4];
+  //
+  // void main()
+  // {
+  //                 vec4 pos = iVert[gl_InvocationID].p *
+  //                            iVert[gl_InvocationID].f[7];
+  //                 position[gl_InvocationID] = pos;
+  // }
+  const std::string text = R"(
+               OpCapability Tessellation
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint TessellationControl %main "main" %iVert %gl_InvocationID %position
+               OpExecutionMode %main OutputVertices 4
+               OpSource GLSL 450
+               OpName %main "main"
+               OpName %Vertex "Vertex"
+               OpMemberName %Vertex 0 "p"
+               OpMemberName %Vertex 1 "n"
+               OpMemberName %Vertex 2 "f"
+               OpName %iVert "iVert"
+               OpName %gl_InvocationID "gl_InvocationID"
+               OpName %position "position"
+               OpMemberDecorate %Vertex 0 Location 1
+               OpMemberDecorate %Vertex 1 Location 3
+               OpMemberDecorate %Vertex 2 Location 5
+               OpDecorate %Vertex Block
+               OpDecorate %gl_InvocationID BuiltIn InvocationId
+               OpDecorate %position Location 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+    %v3float = OpTypeVector %float 3
+       %uint = OpTypeInt 32 0
+   %uint_100 = OpConstant %uint 100
+%_arr_v4float_uint_100 = OpTypeArray %v4float %uint_100
+     %Vertex = OpTypeStruct %v4float %v3float %_arr_v4float_uint_100
+    %uint_32 = OpConstant %uint 32
+%_arr_Vertex_uint_32 = OpTypeArray %Vertex %uint_32
+%_ptr_Input__arr_Vertex_uint_32 = OpTypePointer Input %_arr_Vertex_uint_32
+      %iVert = OpVariable %_ptr_Input__arr_Vertex_uint_32 Input
+        %int = OpTypeInt 32 1
+%_ptr_Input_int = OpTypePointer Input %int
+%gl_InvocationID = OpVariable %_ptr_Input_int Input
+      %int_0 = OpConstant %int 0
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+      %int_2 = OpConstant %int 2
+      %int_7 = OpConstant %int 7
+     %uint_4 = OpConstant %uint 4
+%_arr_v4float_uint_4 = OpTypeArray %v4float %uint_4
+%_ptr_Output__arr_v4float_uint_4 = OpTypePointer Output %_arr_v4float_uint_4
+   %position = OpVariable %_ptr_Output__arr_v4float_uint_4 Output
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %22 = OpLoad %int %gl_InvocationID
+         %25 = OpAccessChain %_ptr_Input_v4float %iVert %22 %int_0
+         %26 = OpLoad %v4float %25
+         %30 = OpAccessChain %_ptr_Input_v4float %iVert %22 %int_2 %int_7
+         %31 = OpLoad %v4float %30
+         %32 = OpFMul %v4float %26 %31
+         %40 = OpAccessChain %_ptr_Output_v4float %position %22
+               OpStore %40 %32
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_VULKAN_1_3);
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+  std::unordered_set<uint32_t> live_inputs;
+  std::unordered_set<uint32_t> live_builtins;
+  auto result = SinglePassRunToBinary<AnalyzeLiveInputPass>(
+      text, true, &live_inputs, &live_builtins);
+
+  auto itr0 = live_inputs.find(0);
+  auto itr1 = live_inputs.find(1);
+  auto itr2 = live_inputs.find(2);
+  auto itr3 = live_inputs.find(3);
+  auto itr4 = live_inputs.find(4);
+  auto itr5 = live_inputs.find(5);
+  auto itr6 = live_inputs.find(6);
+  auto itr7 = live_inputs.find(7);
+  auto itr8 = live_inputs.find(8);
+  auto itr9 = live_inputs.find(9);
+  auto itr10 = live_inputs.find(10);
+  auto itr11 = live_inputs.find(11);
+  auto itr12 = live_inputs.find(12);
+  auto itr13 = live_inputs.find(13);
+
+  // Expect live_inputs == {1, 12}
+  EXPECT_TRUE(itr0 == live_inputs.end());
+  EXPECT_TRUE(itr1 != live_inputs.end());
+  EXPECT_TRUE(itr2 == live_inputs.end());
+  EXPECT_TRUE(itr3 == live_inputs.end());
+  EXPECT_TRUE(itr4 == live_inputs.end());
+  EXPECT_TRUE(itr5 == live_inputs.end());
+  EXPECT_TRUE(itr6 == live_inputs.end());
+  EXPECT_TRUE(itr7 == live_inputs.end());
+  EXPECT_TRUE(itr8 == live_inputs.end());
+  EXPECT_TRUE(itr9 == live_inputs.end());
+  EXPECT_TRUE(itr10 == live_inputs.end());
+  EXPECT_TRUE(itr11 == live_inputs.end());
+  EXPECT_TRUE(itr12 != live_inputs.end());
+  EXPECT_TRUE(itr13 == live_inputs.end());
+}
+
+TEST_F(AnalyzeLiveInputTest, Builtins) {
+  // Tests handling of builtin input seen in Tesc, Tese and Geom shaders.
+  //
+  // Should report builtin gl_PointSize only.
+  //
+  // #version 460
+  //
+  // layout(triangle_strip, max_vertices = 3) out;
+  // layout(triangles) in;
+  //
+  // void main()
+  // {
+  //         for (int i = 0; i < 3; i++)
+  //         {
+  //                 gl_Position = gl_in[i].gl_Position;
+  //                 gl_PointSize = gl_in[i].gl_PointSize;
+  //
+  //                 EmitVertex();
+  //         }
+  //
+  //         EndPrimitive();
+  // }
+  const std::string text = R"(
+               OpCapability Geometry
+               OpCapability GeometryPointSize
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Geometry %main "main" %_ %gl_in
+               OpExecutionMode %main Triangles
+               OpExecutionMode %main Invocations 1
+               OpExecutionMode %main OutputTriangleStrip
+               OpExecutionMode %main OutputVertices 3
+               OpSource GLSL 460
+               OpName %main "main"
+               OpName %i "i"
+               OpName %gl_PerVertex "gl_PerVertex"
+               OpMemberName %gl_PerVertex 0 "gl_Position"
+               OpMemberName %gl_PerVertex 1 "gl_PointSize"
+               OpMemberName %gl_PerVertex 2 "gl_ClipDistance"
+               OpMemberName %gl_PerVertex 3 "gl_CullDistance"
+               OpName %_ ""
+               OpName %gl_PerVertex_0 "gl_PerVertex"
+               OpMemberName %gl_PerVertex_0 0 "gl_Position"
+               OpMemberName %gl_PerVertex_0 1 "gl_PointSize"
+               OpMemberName %gl_PerVertex_0 2 "gl_ClipDistance"
+               OpMemberName %gl_PerVertex_0 3 "gl_CullDistance"
+               OpName %gl_in "gl_in"
+               OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
+               OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize
+               OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance
+               OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance
+               OpDecorate %gl_PerVertex Block
+               OpMemberDecorate %gl_PerVertex_0 0 BuiltIn Position
+               OpMemberDecorate %gl_PerVertex_0 1 BuiltIn PointSize
+               OpDecorate %gl_PerVertex_0 Block
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+        %int = OpTypeInt 32 1
+%_ptr_Function_int = OpTypePointer Function %int
+      %int_0 = OpConstant %int 0
+      %int_3 = OpConstant %int 3
+       %bool = OpTypeBool
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+%_arr_float_uint_1 = OpTypeArray %float %uint_1
+%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1
+%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex
+          %_ = OpVariable %_ptr_Output_gl_PerVertex Output
+%gl_PerVertex_0 = OpTypeStruct %v4float %float
+     %uint_3 = OpConstant %uint 3
+%_arr_gl_PerVertex_0_uint_3 = OpTypeArray %gl_PerVertex_0 %uint_3
+%_ptr_Input__arr_gl_PerVertex_0_uint_3 = OpTypePointer Input %_arr_gl_PerVertex_0_uint_3
+      %gl_in = OpVariable %_ptr_Input__arr_gl_PerVertex_0_uint_3 Input
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+      %int_1 = OpConstant %int 1
+%_ptr_Input_float = OpTypePointer Input %float
+%_ptr_Output_float = OpTypePointer Output %float
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+          %i = OpVariable %_ptr_Function_int Function
+               OpStore %i %int_0
+               OpBranch %10
+         %10 = OpLabel
+               OpLoopMerge %12 %13 None
+               OpBranch %14
+         %14 = OpLabel
+         %15 = OpLoad %int %i
+         %18 = OpSLessThan %bool %15 %int_3
+               OpBranchConditional %18 %11 %12
+         %11 = OpLabel
+         %32 = OpLoad %int %i
+         %34 = OpAccessChain %_ptr_Input_v4float %gl_in %32 %int_0
+         %35 = OpLoad %v4float %34
+         %37 = OpAccessChain %_ptr_Output_v4float %_ %int_0
+               OpStore %37 %35
+         %39 = OpLoad %int %i
+         %41 = OpAccessChain %_ptr_Input_float %gl_in %39 %int_1
+         %42 = OpLoad %float %41
+         %44 = OpAccessChain %_ptr_Output_float %_ %int_1
+               OpStore %44 %42
+               OpEmitVertex
+               OpBranch %13
+         %13 = OpLabel
+         %45 = OpLoad %int %i
+         %46 = OpIAdd %int %45 %int_1
+               OpStore %i %46
+               OpBranch %10
+         %12 = OpLabel
+               OpEndPrimitive
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_VULKAN_1_3);
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+  std::unordered_set<uint32_t> live_inputs;
+  std::unordered_set<uint32_t> live_builtins;
+  auto result = SinglePassRunToBinary<AnalyzeLiveInputPass>(
+      text, true, &live_inputs, &live_builtins);
+
+  auto itr0 = live_builtins.find((uint32_t)spv::BuiltIn::PointSize);
+  auto itr1 = live_builtins.find((uint32_t)spv::BuiltIn::ClipDistance);
+  auto itr2 = live_builtins.find((uint32_t)spv::BuiltIn::CullDistance);
+
+  // Expect live_builtins == { spv::BuiltIn::PointSize }
+  EXPECT_TRUE(itr0 != live_builtins.end());
+  EXPECT_TRUE(itr1 == live_builtins.end());
+  EXPECT_TRUE(itr2 == live_builtins.end());
+}
+
+TEST_F(AnalyzeLiveInputTest, ArrayedInputPatchLocs) {
+  // Tests handling of locs with arrayed input patch seen in Tese
+  //
+  // Should report location {3}.
+  //
+  // #version 450 core
+  //
+  // layout(triangles, ccw) in;
+  //
+  // layout(fractional_odd_spacing) in;
+  //
+  // layout(point_mode) in;
+  //
+  // layout(location=2) patch in float patchIn1[2];
+  //
+  // void main()
+  // {
+  //     vec4 p = gl_in[1].gl_Position;
+  //     gl_Position = p * patchIn1[1];
+  // }
+  const std::string text = R"(
+               OpCapability Tessellation
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint TessellationEvaluation %main "main" %gl_in %_ %patchIn1
+               OpExecutionMode %main Triangles
+               OpExecutionMode %main SpacingFractionalOdd
+               OpExecutionMode %main VertexOrderCcw
+               OpExecutionMode %main PointMode
+               OpSource GLSL 450
+               OpName %main "main"
+               OpName %p "p"
+               OpName %gl_PerVertex "gl_PerVertex"
+               OpMemberName %gl_PerVertex 0 "gl_Position"
+               OpName %gl_in "gl_in"
+               OpName %gl_PerVertex_0 "gl_PerVertex"
+               OpMemberName %gl_PerVertex_0 0 "gl_Position"
+               OpName %_ ""
+               OpName %patchIn1 "patchIn1"
+               OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
+               OpDecorate %gl_PerVertex Block
+               OpMemberDecorate %gl_PerVertex_0 0 BuiltIn Position
+               OpDecorate %gl_PerVertex_0 Block
+               OpDecorate %patchIn1 Patch
+               OpDecorate %patchIn1 Location 2
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+%_arr_float_uint_1 = OpTypeArray %float %uint_1
+%gl_PerVertex = OpTypeStruct %v4float
+    %uint_32 = OpConstant %uint 32
+%_arr_gl_PerVertex_uint_32 = OpTypeArray %gl_PerVertex %uint_32
+%_ptr_Input__arr_gl_PerVertex_uint_32 = OpTypePointer Input %_arr_gl_PerVertex_uint_32
+      %gl_in = OpVariable %_ptr_Input__arr_gl_PerVertex_uint_32 Input
+        %int = OpTypeInt 32 1
+      %int_1 = OpConstant %int 1
+      %int_0 = OpConstant %int 0
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%gl_PerVertex_0 = OpTypeStruct %v4float
+%_ptr_Output_gl_PerVertex_0 = OpTypePointer Output %gl_PerVertex_0
+          %_ = OpVariable %_ptr_Output_gl_PerVertex_0 Output
+     %uint_2 = OpConstant %uint 2
+%_arr_float_uint_2 = OpTypeArray %float %uint_2
+%_ptr_Input__arr_float_uint_2 = OpTypePointer Input %_arr_float_uint_2
+   %patchIn1 = OpVariable %_ptr_Input__arr_float_uint_2 Input
+%_ptr_Input_float = OpTypePointer Input %float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+          %p = OpVariable %_ptr_Function_v4float Function
+         %22 = OpAccessChain %_ptr_Input_v4float %gl_in %int_1 %int_0
+         %23 = OpLoad %v4float %22
+               OpStore %p %23
+         %27 = OpLoad %v4float %p
+         %33 = OpAccessChain %_ptr_Input_float %patchIn1 %int_1
+         %34 = OpLoad %float %33
+         %35 = OpVectorTimesScalar %v4float %27 %34
+         %37 = OpAccessChain %_ptr_Output_v4float %_ %int_0
+               OpStore %37 %35
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_VULKAN_1_3);
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+  std::unordered_set<uint32_t> live_inputs;
+  std::unordered_set<uint32_t> live_builtins;
+  auto result = SinglePassRunToBinary<AnalyzeLiveInputPass>(
+      text, true, &live_inputs, &live_builtins);
+
+  auto itr0 = live_inputs.find(0);
+  auto itr1 = live_inputs.find(1);
+  auto itr2 = live_inputs.find(2);
+  auto itr3 = live_inputs.find(3);
+
+  // Expect live_inputs == {3}
+  EXPECT_TRUE(itr0 == live_inputs.end());
+  EXPECT_TRUE(itr1 == live_inputs.end());
+  EXPECT_TRUE(itr2 == live_inputs.end());
+  EXPECT_TRUE(itr3 != live_inputs.end());
+}
+
+TEST_F(AnalyzeLiveInputTest, FragMultipleLocationsF16) {
+  // Should report locations {2, 5}
+  //
+  // #version 450
+  //
+  // layout(location = 2) in Vertex
+  // {
+  //         f16vec4 color0;
+  //         f16vec4 color1;
+  //         f16vec4 color2[3];
+  // } iVert;
+  //
+  // layout(location = 0) out f16vec4 oFragColor;
+  //
+  // void main()
+  // {
+  //     oFragColor = iVert.color0 + iVert.color2[1];
+  // }
+  const std::string text = R"(
+               OpCapability Shader
+               OpCapability Float16
+               OpCapability StorageInputOutput16
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main" %oFragColor %iVert
+               OpExecutionMode %main OriginUpperLeft
+               OpSource GLSL 450
+               OpName %main "main"
+               OpName %oFragColor "oFragColor"
+               OpName %Vertex "Vertex"
+               OpMemberName %Vertex 0 "color0"
+               OpMemberName %Vertex 1 "color1"
+               OpMemberName %Vertex 2 "color2"
+               OpName %iVert "iVert"
+               OpDecorate %oFragColor Location 0
+               OpDecorate %Vertex Block
+               OpDecorate %iVert Location 2
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+       %half = OpTypeFloat 16
+     %v4half = OpTypeVector %half 4
+%_ptr_Output_v4half = OpTypePointer Output %v4half
+ %oFragColor = OpVariable %_ptr_Output_v4half Output
+       %uint = OpTypeInt 32 0
+     %uint_3 = OpConstant %uint 3
+%_arr_v4half_uint_3 = OpTypeArray %v4half %uint_3
+     %Vertex = OpTypeStruct %v4half %v4half %_arr_v4half_uint_3
+%_ptr_Input_Vertex = OpTypePointer Input %Vertex
+      %iVert = OpVariable %_ptr_Input_Vertex Input
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+%_ptr_Input_v4half = OpTypePointer Input %v4half
+      %int_2 = OpConstant %int 2
+      %int_1 = OpConstant %int 1
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %19 = OpAccessChain %_ptr_Input_v4half %iVert %int_0
+         %20 = OpLoad %v4half %19
+         %23 = OpAccessChain %_ptr_Input_v4half %iVert %int_2 %int_1
+         %24 = OpLoad %v4half %23
+         %25 = OpFAdd %v4half %20 %24
+               OpStore %oFragColor %25
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_VULKAN_1_3);
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+  std::unordered_set<uint32_t> live_inputs;
+  std::unordered_set<uint32_t> live_builtins;
+  auto result = SinglePassRunToBinary<AnalyzeLiveInputPass>(
+      text, true, &live_inputs, &live_builtins);
+
+  auto itr0 = live_inputs.find(0);
+  auto itr1 = live_inputs.find(1);
+  auto itr2 = live_inputs.find(2);
+  auto itr3 = live_inputs.find(3);
+  auto itr4 = live_inputs.find(4);
+  auto itr5 = live_inputs.find(5);
+  auto itr6 = live_inputs.find(6);
+
+  // Expect live_inputs == {2, 5}
+  EXPECT_TRUE(itr0 == live_inputs.end());
+  EXPECT_TRUE(itr1 == live_inputs.end());
+  EXPECT_TRUE(itr2 != live_inputs.end());
+  EXPECT_TRUE(itr3 == live_inputs.end());
+  EXPECT_TRUE(itr4 == live_inputs.end());
+  EXPECT_TRUE(itr5 != live_inputs.end());
+  EXPECT_TRUE(itr6 == live_inputs.end());
+}
+
+}  // namespace
+}  // namespace opt
+}  // namespace spvtools
diff --git a/test/opt/block_merge_test.cpp b/test/opt/block_merge_test.cpp
index 9698fed..57c5061 100644
--- a/test/opt/block_merge_test.cpp
+++ b/test/opt/block_merge_test.cpp
@@ -1201,6 +1201,125 @@
   SinglePassRunAndMatch<BlockMergePass>(text, true);
 }
 
+TEST_F(BlockMergeTest, DontLoseCaseConstruct) {
+  const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %func "func"
+OpExecutionMode %func LocalSize 1 1 1
+OpName %entry "entry";
+OpName %loop "loop"
+OpName %loop_merge "loop_merge"
+OpName %loop_cont "loop_cont"
+OpName %switch "switch"
+OpName %switch_merge "switch_merge"
+%void = OpTypeVoid
+%bool = OpTypeBool
+%int = OpTypeInt 32 0
+%void_fn = OpTypeFunction %void
+%bool_undef = OpUndef %bool
+%int_undef = OpUndef %int
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+OpBranch %loop
+%loop = OpLabel
+OpLoopMerge %loop_merge %loop_cont None
+OpBranch %switch
+%switch = OpLabel
+OpSelectionMerge %switch_merge None
+OpSwitch %int_undef %switch_merge 0 %break 1 %break
+%break = OpLabel
+OpBranch %loop_merge
+%switch_merge = OpLabel
+OpBranch %loop_cont
+%loop_cont = OpLabel
+OpBranch %loop
+%loop_merge = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  auto result = SinglePassRunAndDisassemble<opt::BlockMergePass>(
+      text, /* skip_nop = */ true, /* do_validation = */ true);
+  EXPECT_EQ(opt::Pass::Status::SuccessWithoutChange, std::get<1>(result));
+}
+
+TEST_F(BlockMergeTest, DontLoseDefaultCaseConstruct) {
+  const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %func "func"
+OpExecutionMode %func LocalSize 1 1 1
+OpName %entry "entry";
+OpName %loop "loop"
+OpName %loop_merge "loop_merge"
+OpName %loop_cont "loop_cont"
+OpName %switch "switch"
+OpName %switch_merge "switch_merge"
+%void = OpTypeVoid
+%bool = OpTypeBool
+%int = OpTypeInt 32 0
+%void_fn = OpTypeFunction %void
+%bool_undef = OpUndef %bool
+%int_undef = OpUndef %int
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+OpBranch %loop
+%loop = OpLabel
+OpLoopMerge %loop_merge %loop_cont None
+OpBranch %switch
+%switch = OpLabel
+OpSelectionMerge %switch_merge None
+OpSwitch %int_undef %cont 0 %switch_merge 1 %switch_merge
+%cont = OpLabel
+OpBranch %loop_cont
+%switch_merge = OpLabel
+OpBranch %loop_merge
+%loop_cont = OpLabel
+OpBranch %loop
+%loop_merge = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  auto result = SinglePassRunAndDisassemble<opt::BlockMergePass>(
+      text, /* skip_nop = */ true, /* do_validation = */ true);
+  EXPECT_EQ(opt::Pass::Status::SuccessWithoutChange, std::get<1>(result));
+}
+
+TEST_F(BlockMergeTest, RebuildStructuredCFG) {
+  const std::string text = R"(
+; CHECK: = OpFunction
+; CHECK-NEXT: [[entry:%\w+]] = OpLabel
+; CHECK-NEXT: OpSelectionMerge [[merge:%\w+]] None
+; CHECK-NEXT: OpSwitch {{%\w+}} [[merge]] 0 [[other:%\w+]]
+; CHECK [[other]] = OpLabel
+; CHECK: OpBranch [[merge]]
+; CHECK [[merge]] = OpLabel
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_1 = OpConstant %int 1
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpBranch %switch
+%switch = OpLabel
+OpSelectionMerge %merge None
+OpSwitch %int_1 %merge 0 %other
+%other = OpLabel
+OpBranch %merge
+%merge = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<BlockMergePass>(text, true);
+}
+
 // TODO(greg-lunarg): Add tests to verify handling of these cases:
 //
 //    More complex control flow
diff --git a/test/opt/c_interface_test.cpp b/test/opt/c_interface_test.cpp
new file mode 100644
index 0000000..a172525
--- /dev/null
+++ b/test/opt/c_interface_test.cpp
@@ -0,0 +1,534 @@
+// Copyright (c) 2023 Nintendo
+//
+// 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 <iostream>
+
+#include "gtest/gtest.h"
+#include "spirv-tools/libspirv.h"
+
+namespace spvtools {
+namespace {
+
+TEST(OptimizerCInterface, DefaultConsumerWithValidationNoPassesForInvalidInput) {
+  const uint32_t spirv[] = {
+      0xDEADFEED, // Invalid Magic
+      0x00010100, // Version 1.1
+      0x00000000, // No Generator
+      0x01000000, // Bound
+      0x00000000, // Schema
+      0x00020011, // OpCapability
+      0x00000001, // Shader
+      0x00020011, // OpCapability
+      0x00000005, // Linkage
+      0x0003000E, // OpMemoryModel
+      0x00000000, // Logical
+      0x00000001  // GLSL450
+  };
+
+  auto optimizer = spvOptimizerCreate(SPV_ENV_UNIVERSAL_1_1);
+  ASSERT_NE(optimizer, nullptr);
+
+  // Do not register any passes
+
+  auto options = spvOptimizerOptionsCreate();
+  ASSERT_NE(options, nullptr);
+  spvOptimizerOptionsSetRunValidator(options, true);
+
+  spv_binary binary = nullptr;
+  EXPECT_NE(SPV_SUCCESS,
+            spvOptimizerRun(optimizer, spirv, sizeof(spirv) / sizeof(uint32_t),
+                            &binary, options));
+  ASSERT_EQ(binary, nullptr);
+
+  spvOptimizerOptionsDestroy(options);
+
+  spvOptimizerDestroy(optimizer);
+}
+
+TEST(OptimizerCInterface, SpecifyConsumerWithValidationNoPassesForInvalidInput) {
+  const uint32_t spirv[] = {
+      0xDEADFEED, // Invalid Magic
+      0x00010100, // Version 1.1
+      0x00000000, // No Generator
+      0x01000000, // Bound
+      0x00000000, // Schema
+      0x00020011, // OpCapability
+      0x00000001, // Shader
+      0x00020011, // OpCapability
+      0x00000005, // Linkage
+      0x0003000E, // OpMemoryModel
+      0x00000000, // Logical
+      0x00000001  // GLSL450
+  };
+
+  auto optimizer = spvOptimizerCreate(SPV_ENV_UNIVERSAL_1_1);
+  ASSERT_NE(optimizer, nullptr);
+
+  spvOptimizerSetMessageConsumer(
+      optimizer,
+      [](spv_message_level_t, const char*, const spv_position_t*,
+         const char* message) {
+        std::cout << message << std::endl;
+      });
+
+  // Do not register any passes
+
+  auto options = spvOptimizerOptionsCreate();
+  ASSERT_NE(options, nullptr);
+  spvOptimizerOptionsSetRunValidator(options, true);
+
+  testing::internal::CaptureStdout();
+
+  spv_binary binary = nullptr;
+  EXPECT_NE(SPV_SUCCESS,
+            spvOptimizerRun(optimizer, spirv, sizeof(spirv) / sizeof(uint32_t),
+                            &binary, options));
+  ASSERT_EQ(binary, nullptr);
+
+  auto output = testing::internal::GetCapturedStdout();
+  EXPECT_STRNE(output.c_str(), "");
+
+  spvOptimizerOptionsDestroy(options);
+
+  spvOptimizerDestroy(optimizer);
+}
+
+TEST(OptimizerCInterface, DefaultConsumerWithValidationNoPassesForValidInput) {
+  const uint32_t spirv[] = {
+      0x07230203, // Magic
+      0x00010100, // Version 1.1
+      0x00000000, // No Generator
+      0x00000001, // Bound
+      0x00000000, // Schema
+      0x00020011, // OpCapability
+      0x00000001, // Shader
+      0x00020011, // OpCapability
+      0x00000005, // Linkage
+      0x0003000E, // OpMemoryModel
+      0x00000000, // Logical
+      0x00000001  // GLSL450
+  };
+
+  auto optimizer = spvOptimizerCreate(SPV_ENV_UNIVERSAL_1_1);
+  ASSERT_NE(optimizer, nullptr);
+
+  // Do not register any passes
+
+  auto options = spvOptimizerOptionsCreate();
+  ASSERT_NE(options, nullptr);
+  spvOptimizerOptionsSetRunValidator(options, true);
+
+  spv_binary binary = nullptr;
+  EXPECT_EQ(SPV_SUCCESS,
+            spvOptimizerRun(optimizer, spirv, sizeof(spirv) / sizeof(uint32_t),
+                            &binary, options));
+  ASSERT_NE(binary, nullptr);
+
+  spvOptimizerOptionsDestroy(options);
+
+  // Should remain unchanged
+  EXPECT_EQ(binary->wordCount, sizeof(spirv) / sizeof(uint32_t));
+  EXPECT_EQ(memcmp(binary->code, spirv, sizeof(spirv) / sizeof(uint32_t)), 0);
+
+  spvBinaryDestroy(binary);
+  spvOptimizerDestroy(optimizer);
+}
+
+TEST(OptimizerCInterface, DefaultConsumerNoPassesForValidInput) {
+  const uint32_t spirv[] = {
+      0x07230203, // Magic
+      0x00010100, // Version 1.1
+      0x00000000, // No Generator
+      0x00000003, // Bound
+      0x00000000, // Schema
+      0x00020011, // OpCapability
+      0x00000001, // Shader
+      0x00020011, // OpCapability
+      0x00000005, // Linkage
+      0x0003000E, // OpMemoryModel
+      0x00000000, // Logical
+      0x00000001, // GLSL450
+      0x00040015, // OpTypeInt
+      0x00000001, // %1
+      0x00000020, // 32 Bits
+      0x00000000, // Unsigned
+      0x0004002B, // OpConstant
+      0x00000001, // %1
+      0x00000002, // %2
+      0x00000001  // 1
+  };
+
+  auto optimizer = spvOptimizerCreate(SPV_ENV_UNIVERSAL_1_1);
+  ASSERT_NE(optimizer, nullptr);
+
+  // Do not register any passes
+
+  auto options = spvOptimizerOptionsCreate();
+  ASSERT_NE(options, nullptr);
+  spvOptimizerOptionsSetRunValidator(options, true);
+
+  spv_binary binary = nullptr;
+  EXPECT_EQ(SPV_SUCCESS,
+            spvOptimizerRun(optimizer, spirv, sizeof(spirv) / sizeof(uint32_t),
+                            &binary, options));
+  ASSERT_NE(binary, nullptr);
+
+  spvOptimizerOptionsDestroy(options);
+
+  // Should remain unchanged
+  EXPECT_EQ(binary->wordCount, sizeof(spirv) / sizeof(uint32_t));
+  EXPECT_EQ(memcmp(binary->code, spirv, sizeof(spirv) / sizeof(uint32_t)), 0);
+
+  spvBinaryDestroy(binary);
+  spvOptimizerDestroy(optimizer);
+}
+
+TEST(OptimizerCInterface, DefaultConsumerLegalizationPassesForValidInput) {
+  const uint32_t spirv[] = {
+      0x07230203, // Magic
+      0x00010100, // Version 1.1
+      0x00000000, // No Generator
+      0x00000003, // Bound
+      0x00000000, // Schema
+      0x00020011, // OpCapability
+      0x00000001, // Shader
+      0x00020011, // OpCapability
+      0x00000005, // Linkage
+      0x0003000E, // OpMemoryModel
+      0x00000000, // Logical
+      0x00000001, // GLSL450
+      0x00040015, // OpTypeInt
+      0x00000001, // %1
+      0x00000020, // 32 Bits
+      0x00000000, // Unsigned
+      0x0004002B, // OpConstant
+      0x00000001, // %1
+      0x00000002, // %2
+      0x00000001  // 1
+  };
+
+  auto optimizer = spvOptimizerCreate(SPV_ENV_UNIVERSAL_1_1);
+  ASSERT_NE(optimizer, nullptr);
+
+  spvOptimizerRegisterLegalizationPasses(optimizer);
+
+  auto options = spvOptimizerOptionsCreate();
+  ASSERT_NE(options, nullptr);
+  spvOptimizerOptionsSetRunValidator(options, false);
+
+  spv_binary binary = nullptr;
+  EXPECT_EQ(SPV_SUCCESS,
+            spvOptimizerRun(optimizer, spirv, sizeof(spirv) / sizeof(uint32_t),
+                            &binary, options));
+  ASSERT_NE(binary, nullptr);
+
+  spvOptimizerOptionsDestroy(options);
+
+  // Only check that SPV_SUCCESS is returned, do not verify output
+
+  spvBinaryDestroy(binary);
+  spvOptimizerDestroy(optimizer);
+}
+
+TEST(OptimizerCInterface, DefaultConsumerPerformancePassesForValidInput) {
+  const uint32_t spirv[] = {
+      0x07230203, // Magic
+      0x00010100, // Version 1.1
+      0x00000000, // No Generator
+      0x00000003, // Bound
+      0x00000000, // Schema
+      0x00020011, // OpCapability
+      0x00000001, // Shader
+      0x00020011, // OpCapability
+      0x00000005, // Linkage
+      0x0003000E, // OpMemoryModel
+      0x00000000, // Logical
+      0x00000001, // GLSL450
+      0x00040015, // OpTypeInt
+      0x00000001, // %1
+      0x00000020, // 32 Bits
+      0x00000000, // Unsigned
+      0x0004002B, // OpConstant
+      0x00000001, // %1
+      0x00000002, // %2
+      0x00000001  // 1
+  };
+  const uint32_t expected_spirv[] = {
+      0x07230203, // Magic
+      0x00010100, // Version 1.1
+      0x00000000, // No Generator
+      0x00000001, // Bound
+      0x00000000, // Schema
+      0x00020011, // OpCapability
+      0x00000001, // Shader
+      0x00020011, // OpCapability
+      0x00000005, // Linkage
+      0x0003000E, // OpMemoryModel
+      0x00000000, // Logical
+      0x00000001  // GLSL450
+  };
+
+  auto optimizer = spvOptimizerCreate(SPV_ENV_UNIVERSAL_1_1);
+  ASSERT_NE(optimizer, nullptr);
+
+  spvOptimizerRegisterPerformancePasses(optimizer);
+
+  auto options = spvOptimizerOptionsCreate();
+  ASSERT_NE(options, nullptr);
+  spvOptimizerOptionsSetRunValidator(options, false);
+
+  spv_binary binary = nullptr;
+  EXPECT_EQ(SPV_SUCCESS,
+            spvOptimizerRun(optimizer, spirv, sizeof(spirv) / sizeof(uint32_t),
+                            &binary, options));
+  ASSERT_NE(binary, nullptr);
+
+  spvOptimizerOptionsDestroy(options);
+
+  // Unreferenced OpTypeInt and OpConstant should be removed
+  EXPECT_EQ(binary->wordCount, sizeof(expected_spirv) / sizeof(uint32_t));
+  EXPECT_EQ(memcmp(binary->code, expected_spirv,
+                   sizeof(expected_spirv) / sizeof(uint32_t)), 0);
+
+  spvBinaryDestroy(binary);
+  spvOptimizerDestroy(optimizer);
+}
+
+TEST(OptimizerCInterface, DefaultConsumerSizePassesForValidInput) {
+  const uint32_t spirv[] = {
+      0x07230203, // Magic
+      0x00010100, // Version 1.1
+      0x00000000, // No Generator
+      0x00000003, // Bound
+      0x00000000, // Schema
+      0x00020011, // OpCapability
+      0x00000001, // Shader
+      0x00020011, // OpCapability
+      0x00000005, // Linkage
+      0x0003000E, // OpMemoryModel
+      0x00000000, // Logical
+      0x00000001, // GLSL450
+      0x00040015, // OpTypeInt
+      0x00000001, // %1
+      0x00000020, // 32 Bits
+      0x00000000, // Unsigned
+      0x0004002B, // OpConstant
+      0x00000001, // %1
+      0x00000002, // %2
+      0x00000001  // 1
+  };
+  const uint32_t expected_spirv[] = {
+      0x07230203, // Magic
+      0x00010100, // Version 1.1
+      0x00000000, // No Generator
+      0x00000001, // Bound
+      0x00000000, // Schema
+      0x00020011, // OpCapability
+      0x00000001, // Shader
+      0x00020011, // OpCapability
+      0x00000005, // Linkage
+      0x0003000E, // OpMemoryModel
+      0x00000000, // Logical
+      0x00000001  // GLSL450
+  };
+
+  auto optimizer = spvOptimizerCreate(SPV_ENV_UNIVERSAL_1_1);
+  ASSERT_NE(optimizer, nullptr);
+
+  spvOptimizerRegisterSizePasses(optimizer);
+
+  auto options = spvOptimizerOptionsCreate();
+  ASSERT_NE(options, nullptr);
+  spvOptimizerOptionsSetRunValidator(options, false);
+
+  spv_binary binary = nullptr;
+  EXPECT_EQ(SPV_SUCCESS,
+            spvOptimizerRun(optimizer, spirv, sizeof(spirv) / sizeof(uint32_t),
+                            &binary, options));
+  ASSERT_NE(binary, nullptr);
+
+  spvOptimizerOptionsDestroy(options);
+
+  // Unreferenced OpTypeInt and OpConstant should be removed
+  EXPECT_EQ(binary->wordCount, sizeof(expected_spirv) / sizeof(uint32_t));
+  EXPECT_EQ(memcmp(binary->code, expected_spirv,
+                   sizeof(expected_spirv) / sizeof(uint32_t)), 0);
+
+  spvBinaryDestroy(binary);
+  spvOptimizerDestroy(optimizer);
+}
+
+TEST(OptimizerCInterface, DefaultConsumerPassFromFlagForValidInput) {
+  const uint32_t spirv[] = {
+      0x07230203, // Magic
+      0x00010100, // Version 1.1
+      0x00000000, // No Generator
+      0x00000003, // Bound
+      0x00000000, // Schema
+      0x00020011, // OpCapability
+      0x00000001, // Shader
+      0x00020011, // OpCapability
+      0x00000005, // Linkage
+      0x0003000E, // OpMemoryModel
+      0x00000000, // Logical
+      0x00000001, // GLSL450
+      0x00040015, // OpTypeInt
+      0x00000001, // %1
+      0x00000020, // 32 Bits
+      0x00000000, // Unsigned
+      0x0004002B, // OpConstant
+      0x00000001, // %1
+      0x00000002, // %2
+      0x00000001  // 1
+  };
+  const uint32_t expected_spirv[] = {
+      0x07230203, // Magic
+      0x00010100, // Version 1.1
+      0x00000000, // No Generator
+      0x00000001, // Bound
+      0x00000000, // Schema
+      0x00020011, // OpCapability
+      0x00000001, // Shader
+      0x00020011, // OpCapability
+      0x00000005, // Linkage
+      0x0003000E, // OpMemoryModel
+      0x00000000, // Logical
+      0x00000001  // GLSL450
+  };
+
+  auto optimizer = spvOptimizerCreate(SPV_ENV_UNIVERSAL_1_1);
+  ASSERT_NE(optimizer, nullptr);
+
+  EXPECT_TRUE(spvOptimizerRegisterPassFromFlag(
+      optimizer, "--eliminate-dead-code-aggressive"));
+
+  auto options = spvOptimizerOptionsCreate();
+  ASSERT_NE(options, nullptr);
+  spvOptimizerOptionsSetRunValidator(options, false);
+
+  spv_binary binary = nullptr;
+  EXPECT_EQ(SPV_SUCCESS,
+            spvOptimizerRun(optimizer, spirv, sizeof(spirv) / sizeof(uint32_t),
+                            &binary, options));
+  ASSERT_NE(binary, nullptr);
+
+  spvOptimizerOptionsDestroy(options);
+
+  // Unreferenced OpTypeInt and OpConstant should be removed
+  EXPECT_EQ(binary->wordCount, sizeof(expected_spirv) / sizeof(uint32_t));
+  EXPECT_EQ(memcmp(binary->code, expected_spirv,
+                   sizeof(expected_spirv) / sizeof(uint32_t)), 0);
+
+  spvBinaryDestroy(binary);
+  spvOptimizerDestroy(optimizer);
+}
+
+TEST(OptimizerCInterface, DefaultConsumerPassesFromFlagsForValidInput) {
+  const uint32_t spirv[] = {
+      0x07230203, // Magic
+      0x00010100, // Version 1.1
+      0x00000000, // No Generator
+      0x00000003, // Bound
+      0x00000000, // Schema
+      0x00020011, // OpCapability
+      0x00000001, // Shader
+      0x00020011, // OpCapability
+      0x00000005, // Linkage
+      0x0003000E, // OpMemoryModel
+      0x00000000, // Logical
+      0x00000001, // GLSL450
+      0x00040015, // OpTypeInt
+      0x00000001, // %1
+      0x00000020, // 32 Bits
+      0x00000000, // Unsigned
+      0x0004002B, // OpConstant
+      0x00000001, // %1
+      0x00000002, // %2
+      0x00000001  // 1
+  };
+  const uint32_t expected_spirv[] = {
+      0x07230203, // Magic
+      0x00010100, // Version 1.1
+      0x00000000, // No Generator
+      0x00000001, // Bound
+      0x00000000, // Schema
+      0x00020011, // OpCapability
+      0x00000001, // Shader
+      0x00020011, // OpCapability
+      0x00000005, // Linkage
+      0x0003000E, // OpMemoryModel
+      0x00000000, // Logical
+      0x00000001  // GLSL450
+  };
+
+  auto optimizer = spvOptimizerCreate(SPV_ENV_UNIVERSAL_1_1);
+  ASSERT_NE(optimizer, nullptr);
+
+  const char* flags[2] = {
+      "--eliminate-dead-const",
+      "--eliminate-dead-code-aggressive"
+  };
+
+  EXPECT_TRUE(spvOptimizerRegisterPassesFromFlags(
+      optimizer, flags, sizeof(flags) / sizeof(const char*)));
+
+  auto options = spvOptimizerOptionsCreate();
+  ASSERT_NE(options, nullptr);
+  spvOptimizerOptionsSetRunValidator(options, false);
+
+  spv_binary binary = nullptr;
+  EXPECT_EQ(SPV_SUCCESS,
+            spvOptimizerRun(optimizer, spirv, sizeof(spirv) / sizeof(uint32_t),
+                            &binary, options));
+  ASSERT_NE(binary, nullptr);
+
+  spvOptimizerOptionsDestroy(options);
+
+  // Unreferenced OpTypeInt and OpConstant should be removed
+  EXPECT_EQ(binary->wordCount, sizeof(expected_spirv) / sizeof(uint32_t));
+  EXPECT_EQ(memcmp(binary->code, expected_spirv,
+                   sizeof(expected_spirv) / sizeof(uint32_t)), 0);
+
+  spvBinaryDestroy(binary);
+  spvOptimizerDestroy(optimizer);
+}
+
+TEST(OptimizerCInterface, DefaultConsumerInvalidPassFromFlag) {
+  auto optimizer = spvOptimizerCreate(SPV_ENV_UNIVERSAL_1_1);
+  ASSERT_NE(optimizer, nullptr);
+
+  EXPECT_FALSE(spvOptimizerRegisterPassFromFlag(
+      optimizer, "--this-is-not-a-valid-pass"));
+
+  spvOptimizerDestroy(optimizer);
+}
+
+TEST(OptimizerCInterface, DefaultConsumerInvalidPassesFromFlags) {
+  auto optimizer = spvOptimizerCreate(SPV_ENV_UNIVERSAL_1_1);
+  ASSERT_NE(optimizer, nullptr);
+
+  const char* flags[2] = {
+      "--eliminate-dead-const",
+      "--this-is-not-a-valid-pass"
+  };
+
+  EXPECT_FALSE(spvOptimizerRegisterPassesFromFlags(
+      optimizer, flags, sizeof(flags) / sizeof(const char*)));
+
+  spvOptimizerDestroy(optimizer);
+}
+
+}  // namespace
+}  // namespace spvtools
diff --git a/test/opt/ccp_test.cpp b/test/opt/ccp_test.cpp
index f0f2436..a8e9557 100644
--- a/test/opt/ccp_test.cpp
+++ b/test/opt/ccp_test.cpp
@@ -14,7 +14,6 @@
 
 #include <string>
 
-#include "gmock/gmock.h"
 #include "gtest/gtest.h"
 #include "source/opt/ccp_pass.h"
 #include "test/opt/pass_fixture.h"
diff --git a/test/opt/code_sink_test.cpp b/test/opt/code_sink_test.cpp
index bf5029b..98033fb 100644
--- a/test/opt/code_sink_test.cpp
+++ b/test/opt/code_sink_test.cpp
@@ -14,7 +14,6 @@
 
 #include <string>
 
-#include "gmock/gmock.h"
 #include "test/opt/assembly_builder.h"
 #include "test/opt/pass_fixture.h"
 #include "test/opt/pass_utils.h"
diff --git a/test/opt/combine_access_chains_test.cpp b/test/opt/combine_access_chains_test.cpp
index 5be3ba6..ef7addc 100644
--- a/test/opt/combine_access_chains_test.cpp
+++ b/test/opt/combine_access_chains_test.cpp
@@ -14,8 +14,6 @@
 
 #include <string>
 
-#include "gmock/gmock.h"
-#include "test/opt/assembly_builder.h"
 #include "test/opt/pass_fixture.h"
 #include "test/opt/pass_utils.h"
 
diff --git a/test/opt/compact_ids_test.cpp b/test/opt/compact_ids_test.cpp
index 7c232fe..42f2351 100644
--- a/test/opt/compact_ids_test.cpp
+++ b/test/opt/compact_ids_test.cpp
@@ -13,7 +13,6 @@
 // limitations under the License.
 
 #include <memory>
-#include <string>
 #include <vector>
 
 #include "gmock/gmock.h"
diff --git a/test/opt/constant_manager_test.cpp b/test/opt/constant_manager_test.cpp
index 14e14ec..54c8652 100644
--- a/test/opt/constant_manager_test.cpp
+++ b/test/opt/constant_manager_test.cpp
@@ -13,9 +13,7 @@
 // limitations under the License.
 
 #include <memory>
-#include <string>
 
-#include "gmock/gmock.h"
 #include "gtest/gtest.h"
 #include "source/opt/build_module.h"
 #include "source/opt/constants.h"
diff --git a/test/opt/constants_test.cpp b/test/opt/constants_test.cpp
index 55c92a5..1d4c738 100644
--- a/test/opt/constants_test.cpp
+++ b/test/opt/constants_test.cpp
@@ -16,7 +16,6 @@
 
 #include <gtest/gtest-param-test.h>
 
-#include "gmock/gmock.h"
 #include "gtest/gtest.h"
 #include "source/opt/types.h"
 
diff --git a/test/opt/convert_relaxed_to_half_test.cpp b/test/opt/convert_relaxed_to_half_test.cpp
index 6a06de8..62b9ae4 100644
--- a/test/opt/convert_relaxed_to_half_test.cpp
+++ b/test/opt/convert_relaxed_to_half_test.cpp
@@ -1570,6 +1570,149 @@
   EXPECT_EQ(Pass::Status::SuccessWithChange, std::get<1>(result));
 }
 
+TEST_F(ConvertToHalfTest, DoNotReplaceStructMember) {
+  // See https://github.com/KhronosGroup/SPIRV-Tools/issues/4814
+
+  // This test is a case with a non-relaxed phi with a relaxed operand.
+  // A convert must be inserted at the end of the block associated with
+  // the operand.
+  const std::string test =
+      R"(OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %PSMain "PSMain" %out_var_SV_TARGET %MyConstants
+OpExecutionMode %PSMain OriginUpperLeft
+OpSource HLSL 600
+OpName %type_ConstantBuffer_myStruct "type.ConstantBuffer.myStruct"
+OpMemberName %type_ConstantBuffer_myStruct 0 "f"
+OpName %MyConstants "MyConstants"
+OpName %out_var_SV_TARGET "out.var.SV_TARGET"
+OpName %PSMain "PSMain"
+OpDecorate %out_var_SV_TARGET Location 0
+OpDecorate %MyConstants DescriptorSet 1
+OpDecorate %MyConstants Binding 2
+OpMemberDecorate %type_ConstantBuffer_myStruct 0 Offset 0
+OpDecorate %type_ConstantBuffer_myStruct Block
+%float = OpTypeFloat 32
+%type_ConstantBuffer_myStruct = OpTypeStruct %float
+%_ptr_Uniform_type_ConstantBuffer_myStruct = OpTypePointer Uniform %type_ConstantBuffer_myStruct
+%_ptr_Output_float = OpTypePointer Output %float
+%void = OpTypeVoid
+%9 = OpTypeFunction %void
+%MyConstants = OpVariable %_ptr_Uniform_type_ConstantBuffer_myStruct Uniform
+%out_var_SV_TARGET = OpVariable %_ptr_Output_float Output
+%PSMain = OpFunction %void None %9
+%10 = OpLabel
+%11 = OpLoad %type_ConstantBuffer_myStruct %MyConstants
+%12 = OpCompositeExtract %float %11 0
+OpStore %out_var_SV_TARGET %12
+OpReturn
+OpFunctionEnd
+)";
+
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndCheck<ConvertToHalfPass>(test, test, true);
+}
+
+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.
+
+  // Also ensure that a relaxed local variable does get converted to half
+  // precision before being passed to an image opeartor.
+
+  // #version 310 es
+  //
+  // precision mediump float;
+  //
+  // layout(location = 10) in highp vec4 vertex_uv01;
+  // layout(binding = 0, set = 3) uniform sampler2D materialParams_baseColorMap;
+  //
+  // layout(location = 0) out vec4 fragColor;
+  //
+  // void main() {
+  //   vec4 uv = vec4(2.0);
+  //   fragColor = texture(materialParams_baseColorMap, uv.xy);
+  //   fragColor = texture(materialParams_baseColorMap, vertex_uv01.xy);
+  // }
+  const std::string test = R"(
+               OpCapability Shader
+               OpCapability Float16
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %13 %25
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpDecorate %9 RelaxedPrecision
+;CHECK: OpDecorate [[uv:%\w+]] RelaxedPrecision
+               OpDecorate %13 Location 0
+               OpDecorate %17 DescriptorSet 3
+               OpDecorate %17 Binding 0
+               OpDecorate %18 RelaxedPrecision
+               OpDecorate %23 RelaxedPrecision
+               OpDecorate %25 Location 10
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+;CHECK: [[float32_t:%\w+]] = OpTypeFloat 32
+          %7 = OpTypeVector %6 4
+;CHECK: [[vec4_t:%\w+]] = OpTypeVector [[float32_t]] 4
+          %8 = OpTypePointer Function %7
+         %10 = OpConstant %6 2
+         %11 = OpConstantComposite %7 %10 %10 %10 %10
+         %12 = OpTypePointer Output %7
+;CHECK: [[output_ptr_t:%\w+]] = OpTypePointer Output [[vec4_t]]
+         %13 = OpVariable %12 Output
+;CHECK: [[output:%\w+]] = OpVariable [[output_ptr_t]] Output
+         %14 = OpTypeImage %6 2D 0 0 0 1 Unknown
+         %15 = OpTypeSampledImage %14
+         %16 = OpTypePointer UniformConstant %15
+         %17 = OpVariable %16 UniformConstant
+         %19 = OpTypeVector %6 2
+;CHECK: [[vec2_t:%\w+]] = OpTypeVector [[float32_t]] 2
+         %24 = OpTypePointer Input %7
+;CHECK: [[input_ptr_t:%\w+]] = OpTypePointer Input [[vec4_t]]
+         %25 = OpVariable %24 Input
+         %29 = OpTypeFloat 16
+;CHECK: [[float16_t:%\w+]] = OpTypeFloat 16
+         %30 = OpTypeVector %29 4
+         %33 = OpTypeVector %29 2
+;CHECK: [[vec2_16b_t:%\w+]] = OpTypeVector [[float16_t]] 2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+
+; The only Function storage variable is marked as relaxed
+          %9 = OpVariable %8 Function
+;CHECK: [[uv]] = OpVariable {{%\w+}} Function
+               OpStore %9 %11
+         %18 = OpLoad %15 %17
+         %20 = OpLoad %7 %9
+         %31 = OpFConvert %30 %20
+         %32 = OpFConvert %30 %20
+
+; The first sample op should get a 16b coordinate
+         %21 = OpVectorShuffle %33 %31 %32 0 1
+;CHECK: [[uv_16b:%\w+]] = OpVectorShuffle [[vec2_16b_t]]
+         %22 = OpImageSampleImplicitLod %7 %18 %21
+;CHECK: OpImageSampleImplicitLod [[vec4_t]] {{%\w+}} [[uv_16b]]
+
+               OpStore %13 %22
+         %23 = OpLoad %15 %17
+         %26 = OpLoad %7 %25
+
+; The second sample op should get a 32b coordinate
+         %27 = OpVectorShuffle %19 %26 %26 0 1
+;CHECK: [[uv_32b:%\w+]] = OpVectorShuffle [[vec2_t]]
+         %28 = OpImageSampleImplicitLod %7 %23 %27
+;CHECK: OpImageSampleImplicitLod [[vec4_t]] {{%\w+}} [[uv_32b]]
+
+               OpStore %13 %28
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  SinglePassRunAndMatch<ConvertToHalfPass>(test, true);
+}
+
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools
diff --git a/test/opt/copy_prop_array_test.cpp b/test/opt/copy_prop_array_test.cpp
index d6e376e..2d4b7a3 100644
--- a/test/opt/copy_prop_array_test.cpp
+++ b/test/opt/copy_prop_array_test.cpp
@@ -12,7 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <iostream>
 #include <string>
 
 #include "gmock/gmock.h"
diff --git a/test/opt/dataflow.cpp b/test/opt/dataflow.cpp
index 4742015..dcb6bc6 100644
--- a/test/opt/dataflow.cpp
+++ b/test/opt/dataflow.cpp
@@ -17,7 +17,6 @@
 #include <map>
 #include <set>
 
-#include "gmock/gmock.h"
 #include "gtest/gtest.h"
 #include "opt/function_utils.h"
 #include "source/opt/build_module.h"
@@ -66,7 +65,7 @@
 
   VisitResult Visit(Instruction* inst) override {
     // Conditional branches can be enqueued from labels, so skip them.
-    if (inst->opcode() != SpvOpLabel)
+    if (inst->opcode() != spv::Op::OpLabel)
       return DataFlowAnalysis::VisitResult::kResultFixed;
     uint32_t id = inst->result_id();
     VisitResult ret = DataFlowAnalysis::VisitResult::kResultFixed;
diff --git a/test/opt/debug_info_manager_test.cpp b/test/opt/debug_info_manager_test.cpp
index e87d0be..3df26a9 100644
--- a/test/opt/debug_info_manager_test.cpp
+++ b/test/opt/debug_info_manager_test.cpp
@@ -15,11 +15,8 @@
 #include "source/opt/debug_info_manager.h"
 
 #include <memory>
-#include <string>
 #include <vector>
 
-#include "effcee/effcee.h"
-#include "gmock/gmock.h"
 #include "gtest/gtest.h"
 #include "source/opt/build_module.h"
 #include "source/opt/instruction.h"
@@ -155,7 +152,7 @@
   EXPECT_EQ(inlined_at->NumOperands(), kDebugInlinedAtOperandScopeIndex + 1);
 
   const uint32_t line_number = 77U;
-  Instruction line(context.get(), SpvOpLine);
+  Instruction line(context.get(), spv::Op::OpLine);
   line.SetInOperands({
       {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {5U}},
       {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {line_number}},
@@ -278,7 +275,7 @@
                   SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
 
   const uint32_t line_number = 7U;
-  Instruction line(context.get(), SpvOpLine);
+  Instruction line(context.get(), spv::Op::OpLine);
   line.SetInOperands({
       {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {5U}},
       {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {line_number}},
diff --git a/test/opt/decoration_manager_test.cpp b/test/opt/decoration_manager_test.cpp
index c9fabe7..b287d5e 100644
--- a/test/opt/decoration_manager_test.cpp
+++ b/test/opt/decoration_manager_test.cpp
@@ -12,7 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <iostream>
 #include <memory>
 #include <string>
 #include <vector>
@@ -88,7 +87,7 @@
   std::string GetErrorMessage() const { return error_message_; }
 
   std::string ToText(const std::vector<Instruction*>& inst) {
-    std::vector<uint32_t> binary = {SpvMagicNumber, 0x10200, 0u, 2u, 0u};
+    std::vector<uint32_t> binary = {spv::MagicNumber, 0x10200, 0u, 2u, 0u};
     for (const Instruction* i : inst)
       i->ToBinaryWithoutAttachedDebugInsts(&binary);
     std::string text;
@@ -118,16 +117,17 @@
 TEST_F(DecorationManagerTest,
        ComparingDecorationsWithDiffOpcodesDecorateDecorateId) {
   IRContext ir_context(SPV_ENV_UNIVERSAL_1_2, GetConsumer());
-  // This parameter can be interpreted both as { SpvDecorationConstant }
+  // This parameter can be interpreted both as { spv::Decoration::Constant }
   // and also as a list of IDs:  { 22 }
-  const std::vector<uint32_t> param{SpvDecorationConstant};
+  const std::vector<uint32_t> param{
+      static_cast<uint32_t>(spv::Decoration::Constant)};
   // OpDecorate %1 Constant
   Instruction inst1(
-      &ir_context, SpvOpDecorate, 0u, 0u,
+      &ir_context, spv::Op::OpDecorate, 0u, 0u,
       {{SPV_OPERAND_TYPE_ID, {1u}}, {SPV_OPERAND_TYPE_DECORATION, param}});
   // OpDecorateId %1 %22   ; 'Constant' is decoration number 22
   Instruction inst2(
-      &ir_context, SpvOpDecorateId, 0u, 0u,
+      &ir_context, spv::Op::OpDecorateId, 0u, 0u,
       {{SPV_OPERAND_TYPE_ID, {1u}}, {SPV_OPERAND_TYPE_ID, param}});
   DecorationManager* decoManager = ir_context.get_decoration_mgr();
   EXPECT_THAT(GetErrorMessage(), "");
@@ -137,16 +137,17 @@
 TEST_F(DecorationManagerTest,
        ComparingDecorationsWithDiffOpcodesDecorateDecorateString) {
   IRContext ir_context(SPV_ENV_UNIVERSAL_1_2, GetConsumer());
-  // This parameter can be interpreted both as { SpvDecorationConstant }
+  // This parameter can be interpreted both as { spv::Decoration::Constant }
   // and also as a null-terminated string with a single character with value 22.
-  const std::vector<uint32_t> param{SpvDecorationConstant};
+  const std::vector<uint32_t> param{
+      static_cast<uint32_t>(spv::Decoration::Constant)};
   // OpDecorate %1 Constant
   Instruction inst1(
-      &ir_context, SpvOpDecorate, 0u, 0u,
+      &ir_context, spv::Op::OpDecorate, 0u, 0u,
       {{SPV_OPERAND_TYPE_ID, {1u}}, {SPV_OPERAND_TYPE_DECORATION, param}});
   // OpDecorateStringGOOGLE %1 !22
   Instruction inst2(
-      &ir_context, SpvOpDecorateStringGOOGLE, 0u, 0u,
+      &ir_context, spv::Op::OpDecorateStringGOOGLE, 0u, 0u,
       {{SPV_OPERAND_TYPE_ID, {1u}}, {SPV_OPERAND_TYPE_LITERAL_STRING, param}});
   DecorationManager* decoManager = ir_context.get_decoration_mgr();
   EXPECT_THAT(GetErrorMessage(), "");
@@ -156,13 +157,15 @@
 TEST_F(DecorationManagerTest, ComparingDecorationsWithDiffDecorateParam) {
   IRContext ir_context(SPV_ENV_UNIVERSAL_1_2, GetConsumer());
   // OpDecorate %1 Constant
-  Instruction inst1(&ir_context, SpvOpDecorate, 0u, 0u,
-                    {{SPV_OPERAND_TYPE_ID, {1u}},
-                     {SPV_OPERAND_TYPE_DECORATION, {SpvDecorationConstant}}});
+  Instruction inst1(
+      &ir_context, spv::Op::OpDecorate, 0u, 0u,
+      {{SPV_OPERAND_TYPE_ID, {1u}},
+       {SPV_OPERAND_TYPE_DECORATION, {uint32_t(spv::Decoration::Constant)}}});
   // OpDecorate %1 Restrict
-  Instruction inst2(&ir_context, SpvOpDecorate, 0u, 0u,
-                    {{SPV_OPERAND_TYPE_ID, {1u}},
-                     {SPV_OPERAND_TYPE_DECORATION, {SpvDecorationRestrict}}});
+  Instruction inst2(
+      &ir_context, spv::Op::OpDecorate, 0u, 0u,
+      {{SPV_OPERAND_TYPE_ID, {1u}},
+       {SPV_OPERAND_TYPE_DECORATION, {uint32_t(spv::Decoration::Restrict)}}});
   DecorationManager* decoManager = ir_context.get_decoration_mgr();
   EXPECT_THAT(GetErrorMessage(), "");
   EXPECT_FALSE(decoManager->AreDecorationsTheSame(&inst1, &inst2, true));
@@ -172,11 +175,11 @@
   IRContext ir_context(SPV_ENV_UNIVERSAL_1_2, GetConsumer());
   // OpDecorate %1 Constant
   Instruction inst1(
-      &ir_context, SpvOpDecorateId, 0u, 0u,
+      &ir_context, spv::Op::OpDecorateId, 0u, 0u,
       {{SPV_OPERAND_TYPE_ID, {1u}}, {SPV_OPERAND_TYPE_ID, {555}}});
   // OpDecorate %1 Restrict
   Instruction inst2(
-      &ir_context, SpvOpDecorateId, 0u, 0u,
+      &ir_context, spv::Op::OpDecorateId, 0u, 0u,
       {{SPV_OPERAND_TYPE_ID, {1u}}, {SPV_OPERAND_TYPE_ID, {666}}});
   DecorationManager* decoManager = ir_context.get_decoration_mgr();
   EXPECT_THAT(GetErrorMessage(), "");
@@ -186,11 +189,11 @@
 TEST_F(DecorationManagerTest, ComparingDecorationsWithDiffDecorateStringParam) {
   IRContext ir_context(SPV_ENV_UNIVERSAL_1_2, GetConsumer());
   // OpDecorate %1 Constant
-  Instruction inst1(&ir_context, SpvOpDecorateStringGOOGLE, 0u, 0u,
+  Instruction inst1(&ir_context, spv::Op::OpDecorateStringGOOGLE, 0u, 0u,
                     {{SPV_OPERAND_TYPE_ID, {1u}},
                      {SPV_OPERAND_TYPE_LITERAL_STRING, MakeVector("Hello!")}});
   // OpDecorate %1 Restrict
-  Instruction inst2(&ir_context, SpvOpDecorateStringGOOGLE, 0u, 0u,
+  Instruction inst2(&ir_context, spv::Op::OpDecorateStringGOOGLE, 0u, 0u,
                     {{SPV_OPERAND_TYPE_ID, {1u}},
                      {SPV_OPERAND_TYPE_LITERAL_STRING, MakeVector("Hellx")}});
   DecorationManager* decoManager = ir_context.get_decoration_mgr();
@@ -201,13 +204,15 @@
 TEST_F(DecorationManagerTest, ComparingSameDecorationsOnDiffTargetAllowed) {
   IRContext ir_context(SPV_ENV_UNIVERSAL_1_2, GetConsumer());
   // OpDecorate %1 Constant
-  Instruction inst1(&ir_context, SpvOpDecorate, 0u, 0u,
-                    {{SPV_OPERAND_TYPE_ID, {1u}},
-                     {SPV_OPERAND_TYPE_DECORATION, {SpvDecorationConstant}}});
+  Instruction inst1(
+      &ir_context, spv::Op::OpDecorate, 0u, 0u,
+      {{SPV_OPERAND_TYPE_ID, {1u}},
+       {SPV_OPERAND_TYPE_DECORATION, {uint32_t(spv::Decoration::Constant)}}});
   // OpDecorate %2 Constant
-  Instruction inst2(&ir_context, SpvOpDecorate, 0u, 0u,
-                    {{SPV_OPERAND_TYPE_ID, {2u}},
-                     {SPV_OPERAND_TYPE_DECORATION, {SpvDecorationConstant}}});
+  Instruction inst2(
+      &ir_context, spv::Op::OpDecorate, 0u, 0u,
+      {{SPV_OPERAND_TYPE_ID, {2u}},
+       {SPV_OPERAND_TYPE_DECORATION, {uint32_t(spv::Decoration::Constant)}}});
   DecorationManager* decoManager = ir_context.get_decoration_mgr();
   EXPECT_THAT(GetErrorMessage(), "");
   EXPECT_TRUE(decoManager->AreDecorationsTheSame(&inst1, &inst2, true));
@@ -216,10 +221,10 @@
 TEST_F(DecorationManagerTest, ComparingSameDecorationIdsOnDiffTargetAllowed) {
   IRContext ir_context(SPV_ENV_UNIVERSAL_1_2, GetConsumer());
   Instruction inst1(
-      &ir_context, SpvOpDecorateId, 0u, 0u,
+      &ir_context, spv::Op::OpDecorateId, 0u, 0u,
       {{SPV_OPERAND_TYPE_ID, {1u}}, {SPV_OPERAND_TYPE_DECORATION, {44}}});
   Instruction inst2(
-      &ir_context, SpvOpDecorateId, 0u, 0u,
+      &ir_context, spv::Op::OpDecorateId, 0u, 0u,
       {{SPV_OPERAND_TYPE_ID, {2u}}, {SPV_OPERAND_TYPE_DECORATION, {44}}});
   DecorationManager* decoManager = ir_context.get_decoration_mgr();
   EXPECT_THAT(GetErrorMessage(), "");
@@ -229,10 +234,10 @@
 TEST_F(DecorationManagerTest,
        ComparingSameDecorationStringsOnDiffTargetAllowed) {
   IRContext ir_context(SPV_ENV_UNIVERSAL_1_2, GetConsumer());
-  Instruction inst1(&ir_context, SpvOpDecorateStringGOOGLE, 0u, 0u,
+  Instruction inst1(&ir_context, spv::Op::OpDecorateStringGOOGLE, 0u, 0u,
                     {{SPV_OPERAND_TYPE_ID, {1u}},
                      {SPV_OPERAND_TYPE_LITERAL_STRING, MakeVector("hello")}});
-  Instruction inst2(&ir_context, SpvOpDecorateStringGOOGLE, 0u, 0u,
+  Instruction inst2(&ir_context, spv::Op::OpDecorateStringGOOGLE, 0u, 0u,
                     {{SPV_OPERAND_TYPE_ID, {2u}},
                      {SPV_OPERAND_TYPE_LITERAL_STRING, MakeVector("hello")}});
   DecorationManager* decoManager = ir_context.get_decoration_mgr();
@@ -243,13 +248,15 @@
 TEST_F(DecorationManagerTest, ComparingSameDecorationsOnDiffTargetDisallowed) {
   IRContext ir_context(SPV_ENV_UNIVERSAL_1_2, GetConsumer());
   // OpDecorate %1 Constant
-  Instruction inst1(&ir_context, SpvOpDecorate, 0u, 0u,
-                    {{SPV_OPERAND_TYPE_ID, {1u}},
-                     {SPV_OPERAND_TYPE_DECORATION, {SpvDecorationConstant}}});
+  Instruction inst1(
+      &ir_context, spv::Op::OpDecorate, 0u, 0u,
+      {{SPV_OPERAND_TYPE_ID, {1u}},
+       {SPV_OPERAND_TYPE_DECORATION, {uint32_t(spv::Decoration::Constant)}}});
   // OpDecorate %2 Constant
-  Instruction inst2(&ir_context, SpvOpDecorate, 0u, 0u,
-                    {{SPV_OPERAND_TYPE_ID, {2u}},
-                     {SPV_OPERAND_TYPE_DECORATION, {SpvDecorationConstant}}});
+  Instruction inst2(
+      &ir_context, spv::Op::OpDecorate, 0u, 0u,
+      {{SPV_OPERAND_TYPE_ID, {2u}},
+       {SPV_OPERAND_TYPE_DECORATION, {uint32_t(spv::Decoration::Constant)}}});
   DecorationManager* decoManager = ir_context.get_decoration_mgr();
   EXPECT_THAT(GetErrorMessage(), "");
   EXPECT_FALSE(decoManager->AreDecorationsTheSame(&inst1, &inst2, false));
@@ -258,15 +265,17 @@
 TEST_F(DecorationManagerTest, ComparingMemberDecorationsOnSameTypeDiffMember) {
   IRContext ir_context(SPV_ENV_UNIVERSAL_1_2, GetConsumer());
   // OpMemberDecorate %1 0 Constant
-  Instruction inst1(&ir_context, SpvOpMemberDecorate, 0u, 0u,
-                    {{SPV_OPERAND_TYPE_ID, {1u}},
-                     {SPV_OPERAND_TYPE_LITERAL_INTEGER, {0u}},
-                     {SPV_OPERAND_TYPE_DECORATION, {SpvDecorationConstant}}});
+  Instruction inst1(
+      &ir_context, spv::Op::OpMemberDecorate, 0u, 0u,
+      {{SPV_OPERAND_TYPE_ID, {1u}},
+       {SPV_OPERAND_TYPE_LITERAL_INTEGER, {0u}},
+       {SPV_OPERAND_TYPE_DECORATION, {uint32_t(spv::Decoration::Constant)}}});
   // OpMemberDecorate %1 1 Constant
-  Instruction inst2(&ir_context, SpvOpMemberDecorate, 0u, 0u,
-                    {{SPV_OPERAND_TYPE_ID, {1u}},
-                     {SPV_OPERAND_TYPE_LITERAL_INTEGER, {1u}},
-                     {SPV_OPERAND_TYPE_DECORATION, {SpvDecorationConstant}}});
+  Instruction inst2(
+      &ir_context, spv::Op::OpMemberDecorate, 0u, 0u,
+      {{SPV_OPERAND_TYPE_ID, {1u}},
+       {SPV_OPERAND_TYPE_LITERAL_INTEGER, {1u}},
+       {SPV_OPERAND_TYPE_DECORATION, {uint32_t(spv::Decoration::Constant)}}});
   DecorationManager* decoManager = ir_context.get_decoration_mgr();
   EXPECT_THAT(GetErrorMessage(), "");
   EXPECT_FALSE(decoManager->AreDecorationsTheSame(&inst1, &inst2, true));
@@ -276,15 +285,17 @@
        ComparingSameMemberDecorationsOnDiffTargetAllowed) {
   IRContext ir_context(SPV_ENV_UNIVERSAL_1_2, GetConsumer());
   // OpMemberDecorate %1 0 Constant
-  Instruction inst1(&ir_context, SpvOpMemberDecorate, 0u, 0u,
-                    {{SPV_OPERAND_TYPE_ID, {1u}},
-                     {SPV_OPERAND_TYPE_LITERAL_INTEGER, {0u}},
-                     {SPV_OPERAND_TYPE_DECORATION, {SpvDecorationConstant}}});
+  Instruction inst1(
+      &ir_context, spv::Op::OpMemberDecorate, 0u, 0u,
+      {{SPV_OPERAND_TYPE_ID, {1u}},
+       {SPV_OPERAND_TYPE_LITERAL_INTEGER, {0u}},
+       {SPV_OPERAND_TYPE_DECORATION, {uint32_t(spv::Decoration::Constant)}}});
   // OpMemberDecorate %2 0 Constant
-  Instruction inst2(&ir_context, SpvOpMemberDecorate, 0u, 0u,
-                    {{SPV_OPERAND_TYPE_ID, {2u}},
-                     {SPV_OPERAND_TYPE_LITERAL_INTEGER, {0u}},
-                     {SPV_OPERAND_TYPE_DECORATION, {SpvDecorationConstant}}});
+  Instruction inst2(
+      &ir_context, spv::Op::OpMemberDecorate, 0u, 0u,
+      {{SPV_OPERAND_TYPE_ID, {2u}},
+       {SPV_OPERAND_TYPE_LITERAL_INTEGER, {0u}},
+       {SPV_OPERAND_TYPE_DECORATION, {uint32_t(spv::Decoration::Constant)}}});
   DecorationManager* decoManager = ir_context.get_decoration_mgr();
   EXPECT_THAT(GetErrorMessage(), "");
   EXPECT_TRUE(decoManager->AreDecorationsTheSame(&inst1, &inst2, true));
@@ -294,15 +305,17 @@
        ComparingSameMemberDecorationsOnDiffTargetDisallowed) {
   IRContext ir_context(SPV_ENV_UNIVERSAL_1_2, GetConsumer());
   // OpMemberDecorate %1 0 Constant
-  Instruction inst1(&ir_context, SpvOpMemberDecorate, 0u, 0u,
-                    {{SPV_OPERAND_TYPE_ID, {1u}},
-                     {SPV_OPERAND_TYPE_LITERAL_INTEGER, {0u}},
-                     {SPV_OPERAND_TYPE_DECORATION, {SpvDecorationConstant}}});
+  Instruction inst1(
+      &ir_context, spv::Op::OpMemberDecorate, 0u, 0u,
+      {{SPV_OPERAND_TYPE_ID, {1u}},
+       {SPV_OPERAND_TYPE_LITERAL_INTEGER, {0u}},
+       {SPV_OPERAND_TYPE_DECORATION, {uint32_t(spv::Decoration::Constant)}}});
   // OpMemberDecorate %2 0 Constant
-  Instruction inst2(&ir_context, SpvOpMemberDecorate, 0u, 0u,
-                    {{SPV_OPERAND_TYPE_ID, {2u}},
-                     {SPV_OPERAND_TYPE_LITERAL_INTEGER, {0u}},
-                     {SPV_OPERAND_TYPE_DECORATION, {SpvDecorationConstant}}});
+  Instruction inst2(
+      &ir_context, spv::Op::OpMemberDecorate, 0u, 0u,
+      {{SPV_OPERAND_TYPE_ID, {2u}},
+       {SPV_OPERAND_TYPE_LITERAL_INTEGER, {0u}},
+       {SPV_OPERAND_TYPE_DECORATION, {uint32_t(spv::Decoration::Constant)}}});
   DecorationManager* decoManager = ir_context.get_decoration_mgr();
   EXPECT_THAT(GetErrorMessage(), "");
   EXPECT_FALSE(decoManager->AreDecorationsTheSame(&inst1, &inst2, false));
@@ -486,7 +499,7 @@
   DecorationManager* decoManager = GetDecorationManager(spirv);
   EXPECT_THAT(GetErrorMessage(), "");
   decoManager->RemoveDecorationsFrom(1u, [](const Instruction& inst) {
-    return inst.opcode() == SpvOpDecorate &&
+    return inst.opcode() == spv::Op::OpDecorate &&
            inst.GetSingleWordInOperand(0u) == 3u;
   });
   auto decorations = decoManager->GetDecorationsFor(1u, false);
@@ -537,9 +550,10 @@
   DecorationManager* decoManager = GetDecorationManager(spirv);
   EXPECT_THAT(GetErrorMessage(), "");
   decoManager->RemoveDecorationsFrom(1u, [](const Instruction& inst) {
-    return inst.opcode() == SpvOpDecorate &&
+    return inst.opcode() == spv::Op::OpDecorate &&
            inst.GetSingleWordInOperand(0u) == 3u &&
-           inst.GetSingleWordInOperand(1u) == SpvDecorationBuiltIn;
+           spv::Decoration(inst.GetSingleWordInOperand(1u)) ==
+               spv::Decoration::BuiltIn;
   });
   auto decorations = decoManager->GetDecorationsFor(1u, false);
   EXPECT_THAT(GetErrorMessage(), "");
@@ -763,7 +777,7 @@
   EXPECT_EQ(GetErrorMessage(), "");
   EXPECT_TRUE(decorations.empty());
 
-  decoManager->CloneDecorations(1u, 8u, {SpvDecorationRelaxedPrecision});
+  decoManager->CloneDecorations(1u, 8u, {spv::Decoration::RelaxedPrecision});
   decorations = decoManager->GetDecorationsFor(8u, false);
   EXPECT_THAT(GetErrorMessage(), "");
 
@@ -822,7 +836,7 @@
   EXPECT_EQ(GetErrorMessage(), "");
   EXPECT_TRUE(decorations.empty());
 
-  decoManager->CloneDecorations(2u, 9u, {SpvDecorationRelaxedPrecision});
+  decoManager->CloneDecorations(2u, 9u, {spv::Decoration::RelaxedPrecision});
   decorations = decoManager->GetDecorationsFor(9u, false);
   EXPECT_THAT(GetErrorMessage(), "");
 
diff --git a/test/opt/def_use_test.cpp b/test/opt/def_use_test.cpp
index 0210095..5f7731b 100644
--- a/test/opt/def_use_test.cpp
+++ b/test/opt/def_use_test.cpp
@@ -13,7 +13,6 @@
 // limitations under the License.
 
 #include <memory>
-#include <string>
 #include <unordered_map>
 #include <unordered_set>
 #include <utility>
@@ -50,9 +49,9 @@
 //
 // If |id| is used multiple times in a single instruction, that instruction's
 // opcode will appear a corresponding number of times.
-std::vector<SpvOp> GetUseOpcodes(const std::unique_ptr<IRContext>& context,
-                                 uint32_t id) {
-  std::vector<SpvOp> opcodes;
+std::vector<spv::Op> GetUseOpcodes(const std::unique_ptr<IRContext>& context,
+                                   uint32_t id) {
+  std::vector<spv::Op> opcodes;
   context->get_def_use_mgr()->ForEachUse(
       id, [&opcodes](Instruction* user, uint32_t) {
         opcodes.push_back(user->opcode());
@@ -99,7 +98,7 @@
     const auto expected_def = expected_defs_uses.defs[i].second;
     ASSERT_EQ(1u, actual_defs.count(id)) << "expected to def id [" << id << "]";
     auto def = actual_defs.at(id);
-    if (def->opcode() != SpvOpConstant) {
+    if (def->opcode() != spv::Op::OpConstant) {
       // Constants don't disassemble properly without a full context.
       EXPECT_EQ(expected_def, DisassembleInst(actual_defs.at(id)));
     }
@@ -116,7 +115,7 @@
   for (uint32_t id = 0; id != idBound; ++id) {
     if (mgr->GetDef(id)) {
       mgr->ForEachUser(id, [id, &userMap](Instruction* user) {
-        if (user->opcode() != SpvOpConstant) {
+        if (user->opcode() != spv::Op::OpConstant) {
           userMap[id].push_back(user);
         }
       });
@@ -1298,21 +1297,23 @@
 
   {
     EXPECT_EQ(2u, NumUses(context, 6));
-    std::vector<SpvOp> opcodes = GetUseOpcodes(context, 6u);
-    EXPECT_THAT(opcodes, UnorderedElementsAre(SpvOpSwitch, SpvOpReturnValue));
+    std::vector<spv::Op> opcodes = GetUseOpcodes(context, 6u);
+    EXPECT_THAT(opcodes, UnorderedElementsAre(spv::Op::OpSwitch,
+                                              spv::Op::OpReturnValue));
   }
   {
     EXPECT_EQ(6u, NumUses(context, 7));
-    std::vector<SpvOp> opcodes = GetUseOpcodes(context, 7u);
+    std::vector<spv::Op> opcodes = GetUseOpcodes(context, 7u);
     // OpSwitch is now a user of %7.
-    EXPECT_THAT(opcodes, UnorderedElementsAre(SpvOpSelectionMerge, SpvOpBranch,
-                                              SpvOpBranch, SpvOpBranch,
-                                              SpvOpBranch, SpvOpSwitch));
+    EXPECT_THAT(opcodes, UnorderedElementsAre(
+                             spv::Op::OpSelectionMerge, spv::Op::OpBranch,
+                             spv::Op::OpBranch, spv::Op::OpBranch,
+                             spv::Op::OpBranch, spv::Op::OpSwitch));
   }
   // Check all ids only used by OpSwitch after replacement.
   for (const auto id : {8u, 10u, 11u}) {
     EXPECT_EQ(1u, NumUses(context, id));
-    EXPECT_EQ(SpvOpSwitch, GetUseOpcodes(context, id).back());
+    EXPECT_EQ(spv::Op::OpSwitch, GetUseOpcodes(context, id).back());
   }
 }
 
@@ -1378,10 +1379,11 @@
   // Analyze the instructions.
   DefUseManager manager(context.module());
 
-  Instruction label(&context, SpvOpLabel, 0, 2, {});
+  Instruction label(&context, spv::Op::OpLabel, 0, 2, {});
   manager.AnalyzeInstDefUse(&label);
 
-  Instruction branch(&context, SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {2}}});
+  Instruction branch(&context, spv::Op::OpBranch, 0, 0,
+                     {{SPV_OPERAND_TYPE_ID, {2}}});
   manager.AnalyzeInstDefUse(&branch);
   context.module()->SetIdBound(3);
 
@@ -1409,7 +1411,7 @@
   // Analyze the instructions.
   DefUseManager manager(context->module());
 
-  Instruction newInst(context.get(), SpvOpConstantTrue, 1, 2, {});
+  Instruction newInst(context.get(), spv::Op::OpConstantTrue, 1, 2, {});
   manager.AnalyzeInstDefUse(&newInst);
 
   InstDefUse expected = {
@@ -1703,7 +1705,7 @@
   DefUseManager* def_use_mgr = context->get_def_use_mgr();
   Instruction* def = def_use_mgr->GetDef(9);
   Instruction* use = def_use_mgr->GetDef(10);
-  def->SetOpcode(SpvOpCopyObject);
+  def->SetOpcode(spv::Op::OpCopyObject);
   def->SetInOperands({{SPV_OPERAND_TYPE_ID, {25}}});
   context->UpdateDefUse(def);
 
diff --git a/test/opt/desc_sroa_test.cpp b/test/opt/desc_sroa_test.cpp
index 91c950e..7a118f9 100644
--- a/test/opt/desc_sroa_test.cpp
+++ b/test/opt/desc_sroa_test.cpp
@@ -14,8 +14,6 @@
 
 #include <string>
 
-#include "gmock/gmock.h"
-#include "test/opt/assembly_builder.h"
 #include "test/opt/pass_fixture.h"
 #include "test/opt/pass_utils.h"
 
diff --git a/test/opt/dominator_tree/common_dominators.cpp b/test/opt/dominator_tree/common_dominators.cpp
index dfa03e9..9573afa 100644
--- a/test/opt/dominator_tree/common_dominators.cpp
+++ b/test/opt/dominator_tree/common_dominators.cpp
@@ -13,9 +13,7 @@
 // limitations under the License.
 
 #include <memory>
-#include <string>
 
-#include "gmock/gmock.h"
 #include "gtest/gtest.h"
 #include "source/opt/build_module.h"
 #include "source/opt/ir_context.h"
diff --git a/test/opt/dominator_tree/generated.cpp b/test/opt/dominator_tree/generated.cpp
index 4fccef0..2a5bc98 100644
--- a/test/opt/dominator_tree/generated.cpp
+++ b/test/opt/dominator_tree/generated.cpp
@@ -14,7 +14,6 @@
 
 #include <array>
 #include <memory>
-#include <set>
 #include <string>
 #include <vector>
 
diff --git a/test/opt/dominator_tree/nested_ifs.cpp b/test/opt/dominator_tree/nested_ifs.cpp
index 0552b75..848296a 100644
--- a/test/opt/dominator_tree/nested_ifs.cpp
+++ b/test/opt/dominator_tree/nested_ifs.cpp
@@ -12,9 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-
 #include <memory>
-#include <string>
 #include <vector>
 
 #include "gmock/gmock.h"
diff --git a/test/opt/dominator_tree/nested_ifs_post.cpp b/test/opt/dominator_tree/nested_ifs_post.cpp
index ad759df..217bdec 100644
--- a/test/opt/dominator_tree/nested_ifs_post.cpp
+++ b/test/opt/dominator_tree/nested_ifs_post.cpp
@@ -13,7 +13,6 @@
 // limitations under the License.
 
 #include <memory>
-#include <string>
 #include <vector>
 
 #include "gmock/gmock.h"
diff --git a/test/opt/dominator_tree/nested_loops.cpp b/test/opt/dominator_tree/nested_loops.cpp
index 7d03937..a82f409 100644
--- a/test/opt/dominator_tree/nested_loops.cpp
+++ b/test/opt/dominator_tree/nested_loops.cpp
@@ -13,7 +13,6 @@
 // limitations under the License.
 
 #include <memory>
-#include <string>
 #include <vector>
 
 #include "gmock/gmock.h"
diff --git a/test/opt/dominator_tree/nested_loops_with_unreachables.cpp b/test/opt/dominator_tree/nested_loops_with_unreachables.cpp
index e87e8dd..2c91bd1 100644
--- a/test/opt/dominator_tree/nested_loops_with_unreachables.cpp
+++ b/test/opt/dominator_tree/nested_loops_with_unreachables.cpp
@@ -13,7 +13,6 @@
 // limitations under the License.
 
 #include <memory>
-#include <string>
 #include <vector>
 
 #include "gmock/gmock.h"
diff --git a/test/opt/dominator_tree/post.cpp b/test/opt/dominator_tree/post.cpp
index bb10fde..acbf012 100644
--- a/test/opt/dominator_tree/post.cpp
+++ b/test/opt/dominator_tree/post.cpp
@@ -13,7 +13,6 @@
 // limitations under the License.
 
 #include <memory>
-#include <string>
 #include <vector>
 
 #include "gmock/gmock.h"
diff --git a/test/opt/dominator_tree/simple.cpp b/test/opt/dominator_tree/simple.cpp
index d11854d..eae2438 100644
--- a/test/opt/dominator_tree/simple.cpp
+++ b/test/opt/dominator_tree/simple.cpp
@@ -13,7 +13,6 @@
 // limitations under the License.
 
 #include <memory>
-#include <string>
 #include <vector>
 
 #include "gmock/gmock.h"
diff --git a/test/opt/dominator_tree/switch_case_fallthrough.cpp b/test/opt/dominator_tree/switch_case_fallthrough.cpp
index d9dd7d1..9eeb410 100644
--- a/test/opt/dominator_tree/switch_case_fallthrough.cpp
+++ b/test/opt/dominator_tree/switch_case_fallthrough.cpp
@@ -13,7 +13,6 @@
 // limitations under the License.
 
 #include <memory>
-#include <string>
 #include <vector>
 
 #include "gmock/gmock.h"
diff --git a/test/opt/dominator_tree/unreachable_for.cpp b/test/opt/dominator_tree/unreachable_for.cpp
index 469e5c1..bf95930 100644
--- a/test/opt/dominator_tree/unreachable_for.cpp
+++ b/test/opt/dominator_tree/unreachable_for.cpp
@@ -13,7 +13,6 @@
 // limitations under the License.
 
 #include <memory>
-#include <string>
 #include <vector>
 
 #include "gmock/gmock.h"
diff --git a/test/opt/dominator_tree/unreachable_for_post.cpp b/test/opt/dominator_tree/unreachable_for_post.cpp
index 8d3e37b..57278f5 100644
--- a/test/opt/dominator_tree/unreachable_for_post.cpp
+++ b/test/opt/dominator_tree/unreachable_for_post.cpp
@@ -13,7 +13,6 @@
 // limitations under the License.
 
 #include <memory>
-#include <string>
 #include <vector>
 
 #include "gmock/gmock.h"
diff --git a/test/opt/eliminate_dead_const_test.cpp b/test/opt/eliminate_dead_const_test.cpp
index 87aab54..ec4c284 100644
--- a/test/opt/eliminate_dead_const_test.cpp
+++ b/test/opt/eliminate_dead_const_test.cpp
@@ -13,9 +13,6 @@
 // limitations under the License.
 
 #include <algorithm>
-#include <cstdarg>
-#include <iostream>
-#include <sstream>
 #include <string>
 #include <unordered_set>
 #include <vector>
diff --git a/test/opt/eliminate_dead_functions_test.cpp b/test/opt/eliminate_dead_functions_test.cpp
index 96deb2a..e9f79a1 100644
--- a/test/opt/eliminate_dead_functions_test.cpp
+++ b/test/opt/eliminate_dead_functions_test.cpp
@@ -517,6 +517,39 @@
   SinglePassRunAndMatch<EliminateDeadFunctionsPass>(text, true);
 }
 
+TEST_F(EliminateDeadFunctionsBasicTest, DependentNonSemanticChain) {
+  const std::string text = R"(
+; CHECK: OpEntryPoint GLCompute [[main:%\w+]]
+; CHECK: [[main]] = OpFunction
+; CHECK-NOT: = OpFunction
+; CHECK: [[ext1:%\w+]] = OpExtInst %void {{%\w+}} 1 [[main]]
+; CHECK: [[ext2:%\w+]] = OpExtInst %void {{%\w+}} 2 [[ext1]]
+; CHECK: [[ext3:%\w+]] = OpExtInst %void {{%\w+}} 3 [[ext1]] [[ext2]]
+OpCapability Shader
+OpExtension "SPV_KHR_non_semantic_info"
+%1 = OpExtInstImport "NonSemantic.Test"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%main_entry = OpLabel
+OpReturn
+OpFunctionEnd
+%dead = OpFunction %void None %void_fn
+%dead_entry = OpLabel
+OpReturn
+OpFunctionEnd
+%2 = OpExtInst %void %1 1 %main
+%3 = OpExtInst %void %1 2 %2
+%4 = OpExtInst %void %1 3 %2 %3
+)";
+
+  SetTargetEnv(SPV_ENV_VULKAN_1_0);
+  SinglePassRunAndMatch<EliminateDeadFunctionsPass>(text, true);
+}
+
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools
diff --git a/test/opt/eliminate_dead_input_components_test.cpp b/test/opt/eliminate_dead_input_components_test.cpp
deleted file mode 100644
index 822914a..0000000
--- a/test/opt/eliminate_dead_input_components_test.cpp
+++ /dev/null
@@ -1,468 +0,0 @@
-// Copyright (c) 2022 The Khronos Group Inc.
-// Copyright (c) 2022 LunarG Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <vector>
-
-#include "gmock/gmock.h"
-#include "test/opt/pass_fixture.h"
-#include "test/opt/pass_utils.h"
-
-namespace spvtools {
-namespace opt {
-namespace {
-
-using ElimDeadInputComponentsTest = PassTest<::testing::Test>;
-
-TEST_F(ElimDeadInputComponentsTest, ElimOneConstantIndex) {
-  // Should reduce to uv[2]
-  //
-  // #version 450
-  //
-  // layout(location = 0) in vec4 uv[8];
-  //
-  // out gl_PerVertex {
-  //     vec4 gl_Position;
-  // };
-  //
-  // void main()
-  // {
-  //     gl_Position = uv[1];
-  // }
-  const std::string text = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Vertex %main "main" %_ %uv
-               OpSource GLSL 450
-               OpName %main "main"
-               OpName %gl_PerVertex "gl_PerVertex"
-               OpMemberName %gl_PerVertex 0 "gl_Position"
-               OpName %_ ""
-               OpName %uv "uv"
-               OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
-               OpDecorate %gl_PerVertex Block
-               OpDecorate %uv Location 0
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v4float = OpTypeVector %float 4
-%gl_PerVertex = OpTypeStruct %v4float
-%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex
-          %_ = OpVariable %_ptr_Output_gl_PerVertex Output
-        %int = OpTypeInt 32 1
-      %int_0 = OpConstant %int 0
-       %uint = OpTypeInt 32 0
-     %uint_8 = OpConstant %uint 8
-%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8
-%_ptr_Input__arr_v4float_uint_8 = OpTypePointer Input %_arr_v4float_uint_8
-           %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
-      %int_1 = OpConstant %int 1
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-;CHECK-NOT: %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
-;CHECK:     %uv = OpVariable %_ptr_Input__arr_v4float_uint_2 Input
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-         %20 = OpAccessChain %_ptr_Input_v4float %uv %int_1
-         %21 = OpLoad %v4float %20
-         %23 = OpAccessChain %_ptr_Output_v4float %_ %int_0
-               OpStore %23 %21
-               OpReturn
-               OpFunctionEnd
-)";
-
-  SetTargetEnv(SPV_ENV_VULKAN_1_3);
-  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true);
-}
-
-TEST_F(ElimDeadInputComponentsTest, ElimOneConstantIndexInBounds) {
-  // Same as ElimOneConstantIndex but with OpInBoundsAccessChain
-  const std::string text = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Vertex %main "main" %_ %uv
-               OpSource GLSL 450
-               OpName %main "main"
-               OpName %gl_PerVertex "gl_PerVertex"
-               OpMemberName %gl_PerVertex 0 "gl_Position"
-               OpName %_ ""
-               OpName %uv "uv"
-               OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
-               OpDecorate %gl_PerVertex Block
-               OpDecorate %uv Location 0
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v4float = OpTypeVector %float 4
-%gl_PerVertex = OpTypeStruct %v4float
-%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex
-          %_ = OpVariable %_ptr_Output_gl_PerVertex Output
-        %int = OpTypeInt 32 1
-      %int_0 = OpConstant %int 0
-       %uint = OpTypeInt 32 0
-     %uint_8 = OpConstant %uint 8
-%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8
-%_ptr_Input__arr_v4float_uint_8 = OpTypePointer Input %_arr_v4float_uint_8
-           %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
-      %int_1 = OpConstant %int 1
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-;CHECK-NOT: %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
-;CHECK:     %uv = OpVariable %_ptr_Input__arr_v4float_uint_2 Input
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-         %20 = OpInBoundsAccessChain %_ptr_Input_v4float %uv %int_1
-         %21 = OpLoad %v4float %20
-         %23 = OpAccessChain %_ptr_Output_v4float %_ %int_0
-               OpStore %23 %21
-               OpReturn
-               OpFunctionEnd
-)";
-
-  SetTargetEnv(SPV_ENV_VULKAN_1_3);
-  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true);
-}
-
-TEST_F(ElimDeadInputComponentsTest, ElimTwoConstantIndices) {
-  // Should reduce to uv[4]
-  //
-  // #version 450
-  //
-  // layout(location = 0) in vec4 uv[8];
-  //
-  // out gl_PerVertex {
-  //     vec4 gl_Position;
-  // };
-  //
-  // void main()
-  // {
-  //     gl_Position = uv[1] + uv[3];
-  // }
-  const std::string text = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Vertex %main "main" %_ %uv
-               OpSource GLSL 450
-               OpName %main "main"
-               OpName %gl_PerVertex "gl_PerVertex"
-               OpMemberName %gl_PerVertex 0 "gl_Position"
-               OpName %_ ""
-               OpName %uv "uv"
-               OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
-               OpDecorate %gl_PerVertex Block
-               OpDecorate %uv Location 0
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v4float = OpTypeVector %float 4
-%gl_PerVertex = OpTypeStruct %v4float
-%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex
-          %_ = OpVariable %_ptr_Output_gl_PerVertex Output
-        %int = OpTypeInt 32 1
-      %int_0 = OpConstant %int 0
-       %uint = OpTypeInt 32 0
-     %uint_8 = OpConstant %uint 8
-%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8
-%_ptr_Input__arr_v4float_uint_8 = OpTypePointer Input %_arr_v4float_uint_8
-         %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
-      %int_1 = OpConstant %int 1
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-      %int_3 = OpConstant %int 3
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-;CHECK-NOT: %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
-;CHECK:     %uv = OpVariable %_ptr_Input__arr_v4float_uint_4 Input
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-         %20 = OpAccessChain %_ptr_Input_v4float %uv %int_1
-         %21 = OpLoad %v4float %20
-         %23 = OpAccessChain %_ptr_Input_v4float %uv %int_3
-         %24 = OpLoad %v4float %23
-         %25 = OpFAdd %v4float %21 %24
-         %27 = OpAccessChain %_ptr_Output_v4float %_ %int_0
-               OpStore %27 %25
-               OpReturn
-               OpFunctionEnd
-)";
-
-  SetTargetEnv(SPV_ENV_VULKAN_1_3);
-  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true);
-}
-
-TEST_F(ElimDeadInputComponentsTest, NoElimMaxConstantIndex) {
-  // Should not reduce uv[8] because of max index of 7
-  //
-  // #version 450
-  //
-  // layout(location = 0) in vec4 uv[8];
-  //
-  // out gl_PerVertex {
-  //     vec4 gl_Position;
-  // };
-  //
-  // void main()
-  // {
-  //     gl_Position = uv[1] + uv[7];
-  // }
-  const std::string text = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Vertex %main "main" %_ %uv
-               OpSource GLSL 450
-               OpName %main "main"
-               OpName %gl_PerVertex "gl_PerVertex"
-               OpMemberName %gl_PerVertex 0 "gl_Position"
-               OpName %_ ""
-               OpName %uv "uv"
-               OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
-               OpDecorate %gl_PerVertex Block
-               OpDecorate %uv Location 0
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v4float = OpTypeVector %float 4
-%gl_PerVertex = OpTypeStruct %v4float
-%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex
-          %_ = OpVariable %_ptr_Output_gl_PerVertex Output
-        %int = OpTypeInt 32 1
-      %int_0 = OpConstant %int 0
-       %uint = OpTypeInt 32 0
-     %uint_8 = OpConstant %uint 8
-%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8
-%_ptr_Input__arr_v4float_uint_8 = OpTypePointer Input %_arr_v4float_uint_8
-         %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
-      %int_1 = OpConstant %int 1
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-      %int_7 = OpConstant %int 7
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-;CHECK:     %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-         %20 = OpAccessChain %_ptr_Input_v4float %uv %int_1
-         %21 = OpLoad %v4float %20
-         %23 = OpAccessChain %_ptr_Input_v4float %uv %int_7
-         %24 = OpLoad %v4float %23
-         %25 = OpFAdd %v4float %21 %24
-         %27 = OpAccessChain %_ptr_Output_v4float %_ %int_0
-               OpStore %27 %25
-               OpReturn
-               OpFunctionEnd
-)";
-
-  SetTargetEnv(SPV_ENV_VULKAN_1_3);
-  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true);
-}
-
-TEST_F(ElimDeadInputComponentsTest, NoElimNonConstantIndex) {
-  // Should not reduce uv[8] because of non-constant index of ui
-  //
-  // #version 450
-  //
-  // layout(location = 0) in vec4 uv[8];
-  //
-  // out gl_PerVertex {
-  //     vec4 gl_Position;
-  // };
-  //
-  // uniform ubname {
-  //     int ui;
-  // } ubinst;
-  //
-  // void main()
-  // {
-  //     gl_Position = uv[1] + uv[ubinst.ui];
-  // }
-  const std::string text = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Vertex %main "main" %_ %uv %ubinst
-               OpSource GLSL 450
-               OpName %main "main"
-               OpName %gl_PerVertex "gl_PerVertex"
-               OpMemberName %gl_PerVertex 0 "gl_Position"
-               OpName %_ ""
-               OpName %uv "uv"
-               OpName %ubname "ubname"
-               OpMemberName %ubname 0 "ui"
-               OpName %ubinst "ubinst"
-               OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
-               OpDecorate %gl_PerVertex Block
-               OpDecorate %uv Location 0
-               OpMemberDecorate %ubname 0 Offset 0
-               OpDecorate %ubname Block
-               OpDecorate %ubinst DescriptorSet 0
-               OpDecorate %ubinst Binding 0
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v4float = OpTypeVector %float 4
-%gl_PerVertex = OpTypeStruct %v4float
-%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex
-          %_ = OpVariable %_ptr_Output_gl_PerVertex Output
-        %int = OpTypeInt 32 1
-      %int_0 = OpConstant %int 0
-       %uint = OpTypeInt 32 0
-     %uint_8 = OpConstant %uint 8
-%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8
-%_ptr_Input__arr_v4float_uint_8 = OpTypePointer Input %_arr_v4float_uint_8
-         %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
-      %int_1 = OpConstant %int 1
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-     %ubname = OpTypeStruct %int
-%_ptr_Uniform_ubname = OpTypePointer Uniform %ubname
-     %ubinst = OpVariable %_ptr_Uniform_ubname Uniform
-%_ptr_Uniform_int = OpTypePointer Uniform %int
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-;CHECK:  %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-         %20 = OpAccessChain %_ptr_Input_v4float %uv %int_1
-         %21 = OpLoad %v4float %20
-         %26 = OpAccessChain %_ptr_Uniform_int %ubinst %int_0
-         %27 = OpLoad %int %26
-         %28 = OpAccessChain %_ptr_Input_v4float %uv %27
-         %29 = OpLoad %v4float %28
-         %30 = OpFAdd %v4float %21 %29
-         %32 = OpAccessChain %_ptr_Output_v4float %_ %int_0
-               OpStore %32 %30
-               OpReturn
-               OpFunctionEnd
-)";
-
-  SetTargetEnv(SPV_ENV_VULKAN_1_3);
-  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true);
-}
-
-TEST_F(ElimDeadInputComponentsTest, NoElimNonIndexedAccessChain) {
-  // Should not change due to non-indexed access chain
-  const std::string text = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Vertex %main "main" %_ %uv
-               OpSource GLSL 450
-               OpName %main "main"
-               OpName %gl_PerVertex "gl_PerVertex"
-               OpMemberName %gl_PerVertex 0 "gl_Position"
-               OpName %_ ""
-               OpName %uv "uv"
-               OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
-               OpDecorate %gl_PerVertex Block
-               OpDecorate %uv Location 0
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v4float = OpTypeVector %float 4
-%gl_PerVertex = OpTypeStruct %v4float
-%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex
-          %_ = OpVariable %_ptr_Output_gl_PerVertex Output
-        %int = OpTypeInt 32 1
-      %int_0 = OpConstant %int 0
-       %uint = OpTypeInt 32 0
-     %uint_8 = OpConstant %uint 8
-%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8
-%_ptr_Input__arr_v4float_uint_8 = OpTypePointer Input %_arr_v4float_uint_8
-           %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
-      %int_1 = OpConstant %int 1
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-;CHECK:  %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-         %20 = OpAccessChain %_ptr_Input__arr_v4float_uint_8 %uv
-               OpReturn
-               OpFunctionEnd
-)";
-
-  SetTargetEnv(SPV_ENV_VULKAN_1_3);
-  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true);
-}
-
-TEST_F(ElimDeadInputComponentsTest, ElimStructMember) {
-  // Should eliminate uv
-  //
-  // #version 450
-  //
-  // in Vertex {
-  //   vec4 Cd;
-  //   vec2 uv;
-  // } iVert;
-  //
-  // out vec4 fragColor;
-  //
-  // void main()
-  // {
-  //   vec4 color = vec4(iVert.Cd);
-  //   fragColor = color;
-  // }
-  const std::string text = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %main "main" %iVert %fragColor
-               OpExecutionMode %main OriginUpperLeft
-               OpSource GLSL 450
-               OpName %main "main"
-               OpName %Vertex "Vertex"
-               OpMemberName %Vertex 0 "Cd"
-               OpMemberName %Vertex 1 "uv"
-               OpName %iVert "iVert"
-               OpName %fragColor "fragColor"
-               OpDecorate %Vertex Block
-               OpDecorate %iVert Location 0
-               OpDecorate %fragColor Location 0
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v4float = OpTypeVector %float 4
-    %v2float = OpTypeVector %float 2
-     %Vertex = OpTypeStruct %v4float %v2float
-; CHECK: %Vertex = OpTypeStruct %v4float %v2float
-; CHECK: [[sty:%\w+]] = OpTypeStruct %v4float
-%_ptr_Input_Vertex = OpTypePointer Input %Vertex
-; CHECK: [[pty:%\w+]] = OpTypePointer Input [[sty]]
-      %iVert = OpVariable %_ptr_Input_Vertex Input
-; CHECK: %iVert = OpVariable [[pty]] Input
-        %int = OpTypeInt 32 1
-      %int_0 = OpConstant %int 0
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-  %fragColor = OpVariable %_ptr_Output_v4float Output
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-         %17 = OpAccessChain %_ptr_Input_v4float %iVert %int_0
-         %18 = OpLoad %v4float %17
-               OpStore %fragColor %18
-               OpReturn
-               OpFunctionEnd
-)";
-
-  SetTargetEnv(SPV_ENV_VULKAN_1_3);
-  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true);
-}
-
-}  // namespace
-}  // namespace opt
-}  // namespace spvtools
diff --git a/test/opt/eliminate_dead_io_components_test.cpp b/test/opt/eliminate_dead_io_components_test.cpp
new file mode 100644
index 0000000..b7a2fb5
--- /dev/null
+++ b/test/opt/eliminate_dead_io_components_test.cpp
@@ -0,0 +1,1253 @@
+// Copyright (c) 2022 The Khronos Group Inc.
+// Copyright (c) 2022 LunarG Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <vector>
+
+#include "test/opt/pass_fixture.h"
+#include "test/opt/pass_utils.h"
+
+namespace spvtools {
+namespace opt {
+namespace {
+
+using ElimDeadIOComponentsTest = PassTest<::testing::Test>;
+
+TEST_F(ElimDeadIOComponentsTest, ElimOneConstantIndex) {
+  // Should reduce to uv[2]
+  //
+  // #version 450
+  //
+  // layout(location = 0) in vec4 uv[8];
+  //
+  // out gl_PerVertex {
+  //     vec4 gl_Position;
+  // };
+  //
+  // void main()
+  // {
+  //     gl_Position = uv[1];
+  // }
+  const std::string text = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main" %_ %uv
+               OpSource GLSL 450
+               OpName %main "main"
+               OpName %gl_PerVertex "gl_PerVertex"
+               OpMemberName %gl_PerVertex 0 "gl_Position"
+               OpName %_ ""
+               OpName %uv "uv"
+               OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
+               OpDecorate %gl_PerVertex Block
+               OpDecorate %uv Location 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%gl_PerVertex = OpTypeStruct %v4float
+%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex
+          %_ = OpVariable %_ptr_Output_gl_PerVertex Output
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+       %uint = OpTypeInt 32 0
+     %uint_8 = OpConstant %uint 8
+%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8
+%_ptr_Input__arr_v4float_uint_8 = OpTypePointer Input %_arr_v4float_uint_8
+           %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
+      %int_1 = OpConstant %int 1
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+;CHECK-NOT: %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
+;CHECK:     %uv = OpVariable %_ptr_Input__arr_v4float_uint_2 Input
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %20 = OpAccessChain %_ptr_Input_v4float %uv %int_1
+         %21 = OpLoad %v4float %20
+         %23 = OpAccessChain %_ptr_Output_v4float %_ %int_0
+               OpStore %23 %21
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_VULKAN_1_3);
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndMatch<EliminateDeadIOComponentsPass>(
+      text, true, spv::StorageClass::Input, false);
+}
+
+TEST_F(ElimDeadIOComponentsTest, ElimOneConstantIndexInBounds) {
+  // Same as ElimOneConstantIndex but with OpInBoundsAccessChain
+  const std::string text = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main" %_ %uv
+               OpSource GLSL 450
+               OpName %main "main"
+               OpName %gl_PerVertex "gl_PerVertex"
+               OpMemberName %gl_PerVertex 0 "gl_Position"
+               OpName %_ ""
+               OpName %uv "uv"
+               OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
+               OpDecorate %gl_PerVertex Block
+               OpDecorate %uv Location 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%gl_PerVertex = OpTypeStruct %v4float
+%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex
+          %_ = OpVariable %_ptr_Output_gl_PerVertex Output
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+       %uint = OpTypeInt 32 0
+     %uint_8 = OpConstant %uint 8
+%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8
+%_ptr_Input__arr_v4float_uint_8 = OpTypePointer Input %_arr_v4float_uint_8
+           %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
+      %int_1 = OpConstant %int 1
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+;CHECK-NOT: %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
+;CHECK:     %uv = OpVariable %_ptr_Input__arr_v4float_uint_2 Input
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %20 = OpInBoundsAccessChain %_ptr_Input_v4float %uv %int_1
+         %21 = OpLoad %v4float %20
+         %23 = OpAccessChain %_ptr_Output_v4float %_ %int_0
+               OpStore %23 %21
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_VULKAN_1_3);
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndMatch<EliminateDeadIOComponentsPass>(
+      text, true, spv::StorageClass::Input, false);
+}
+
+TEST_F(ElimDeadIOComponentsTest, ElimTwoConstantIndices) {
+  // Should reduce to uv[4]
+  //
+  // #version 450
+  //
+  // layout(location = 0) in vec4 uv[8];
+  //
+  // out gl_PerVertex {
+  //     vec4 gl_Position;
+  // };
+  //
+  // void main()
+  // {
+  //     gl_Position = uv[1] + uv[3];
+  // }
+  const std::string text = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main" %_ %uv
+               OpSource GLSL 450
+               OpName %main "main"
+               OpName %gl_PerVertex "gl_PerVertex"
+               OpMemberName %gl_PerVertex 0 "gl_Position"
+               OpName %_ ""
+               OpName %uv "uv"
+               OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
+               OpDecorate %gl_PerVertex Block
+               OpDecorate %uv Location 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%gl_PerVertex = OpTypeStruct %v4float
+%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex
+          %_ = OpVariable %_ptr_Output_gl_PerVertex Output
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+       %uint = OpTypeInt 32 0
+     %uint_8 = OpConstant %uint 8
+%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8
+%_ptr_Input__arr_v4float_uint_8 = OpTypePointer Input %_arr_v4float_uint_8
+         %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
+      %int_1 = OpConstant %int 1
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+      %int_3 = OpConstant %int 3
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+;CHECK-NOT: %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
+;CHECK:     %uv = OpVariable %_ptr_Input__arr_v4float_uint_4 Input
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %20 = OpAccessChain %_ptr_Input_v4float %uv %int_1
+         %21 = OpLoad %v4float %20
+         %23 = OpAccessChain %_ptr_Input_v4float %uv %int_3
+         %24 = OpLoad %v4float %23
+         %25 = OpFAdd %v4float %21 %24
+         %27 = OpAccessChain %_ptr_Output_v4float %_ %int_0
+               OpStore %27 %25
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_VULKAN_1_3);
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndMatch<EliminateDeadIOComponentsPass>(
+      text, true, spv::StorageClass::Input, false);
+}
+
+TEST_F(ElimDeadIOComponentsTest, NoElimMaxConstantIndex) {
+  // Should not reduce uv[8] because of max index of 7
+  //
+  // #version 450
+  //
+  // layout(location = 0) in vec4 uv[8];
+  //
+  // out gl_PerVertex {
+  //     vec4 gl_Position;
+  // };
+  //
+  // void main()
+  // {
+  //     gl_Position = uv[1] + uv[7];
+  // }
+  const std::string text = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main" %_ %uv
+               OpSource GLSL 450
+               OpName %main "main"
+               OpName %gl_PerVertex "gl_PerVertex"
+               OpMemberName %gl_PerVertex 0 "gl_Position"
+               OpName %_ ""
+               OpName %uv "uv"
+               OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
+               OpDecorate %gl_PerVertex Block
+               OpDecorate %uv Location 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%gl_PerVertex = OpTypeStruct %v4float
+%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex
+          %_ = OpVariable %_ptr_Output_gl_PerVertex Output
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+       %uint = OpTypeInt 32 0
+     %uint_8 = OpConstant %uint 8
+%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8
+%_ptr_Input__arr_v4float_uint_8 = OpTypePointer Input %_arr_v4float_uint_8
+         %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
+      %int_1 = OpConstant %int 1
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+      %int_7 = OpConstant %int 7
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+;CHECK:     %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %20 = OpAccessChain %_ptr_Input_v4float %uv %int_1
+         %21 = OpLoad %v4float %20
+         %23 = OpAccessChain %_ptr_Input_v4float %uv %int_7
+         %24 = OpLoad %v4float %23
+         %25 = OpFAdd %v4float %21 %24
+         %27 = OpAccessChain %_ptr_Output_v4float %_ %int_0
+               OpStore %27 %25
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_VULKAN_1_3);
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndMatch<EliminateDeadIOComponentsPass>(
+      text, true, spv::StorageClass::Input, false);
+}
+
+TEST_F(ElimDeadIOComponentsTest, NoElimNonConstantIndex) {
+  // Should not reduce uv[8] because of non-constant index of ui
+  //
+  // #version 450
+  //
+  // layout(location = 0) in vec4 uv[8];
+  //
+  // out gl_PerVertex {
+  //     vec4 gl_Position;
+  // };
+  //
+  // uniform ubname {
+  //     int ui;
+  // } ubinst;
+  //
+  // void main()
+  // {
+  //     gl_Position = uv[1] + uv[ubinst.ui];
+  // }
+  const std::string text = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main" %_ %uv %ubinst
+               OpSource GLSL 450
+               OpName %main "main"
+               OpName %gl_PerVertex "gl_PerVertex"
+               OpMemberName %gl_PerVertex 0 "gl_Position"
+               OpName %_ ""
+               OpName %uv "uv"
+               OpName %ubname "ubname"
+               OpMemberName %ubname 0 "ui"
+               OpName %ubinst "ubinst"
+               OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
+               OpDecorate %gl_PerVertex Block
+               OpDecorate %uv Location 0
+               OpMemberDecorate %ubname 0 Offset 0
+               OpDecorate %ubname Block
+               OpDecorate %ubinst DescriptorSet 0
+               OpDecorate %ubinst Binding 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%gl_PerVertex = OpTypeStruct %v4float
+%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex
+          %_ = OpVariable %_ptr_Output_gl_PerVertex Output
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+       %uint = OpTypeInt 32 0
+     %uint_8 = OpConstant %uint 8
+%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8
+%_ptr_Input__arr_v4float_uint_8 = OpTypePointer Input %_arr_v4float_uint_8
+         %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
+      %int_1 = OpConstant %int 1
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+     %ubname = OpTypeStruct %int
+%_ptr_Uniform_ubname = OpTypePointer Uniform %ubname
+     %ubinst = OpVariable %_ptr_Uniform_ubname Uniform
+%_ptr_Uniform_int = OpTypePointer Uniform %int
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+;CHECK:  %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %20 = OpAccessChain %_ptr_Input_v4float %uv %int_1
+         %21 = OpLoad %v4float %20
+         %26 = OpAccessChain %_ptr_Uniform_int %ubinst %int_0
+         %27 = OpLoad %int %26
+         %28 = OpAccessChain %_ptr_Input_v4float %uv %27
+         %29 = OpLoad %v4float %28
+         %30 = OpFAdd %v4float %21 %29
+         %32 = OpAccessChain %_ptr_Output_v4float %_ %int_0
+               OpStore %32 %30
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_VULKAN_1_3);
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndMatch<EliminateDeadIOComponentsPass>(
+      text, true, spv::StorageClass::Input, false);
+}
+
+TEST_F(ElimDeadIOComponentsTest, NoElimNonIndexedAccessChain) {
+  // Should not change due to non-indexed access chain
+  const std::string text = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main" %_ %uv
+               OpSource GLSL 450
+               OpName %main "main"
+               OpName %gl_PerVertex "gl_PerVertex"
+               OpMemberName %gl_PerVertex 0 "gl_Position"
+               OpName %_ ""
+               OpName %uv "uv"
+               OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
+               OpDecorate %gl_PerVertex Block
+               OpDecorate %uv Location 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%gl_PerVertex = OpTypeStruct %v4float
+%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex
+          %_ = OpVariable %_ptr_Output_gl_PerVertex Output
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+       %uint = OpTypeInt 32 0
+     %uint_8 = OpConstant %uint 8
+%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8
+%_ptr_Input__arr_v4float_uint_8 = OpTypePointer Input %_arr_v4float_uint_8
+           %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
+      %int_1 = OpConstant %int 1
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+;CHECK:  %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %20 = OpAccessChain %_ptr_Input__arr_v4float_uint_8 %uv
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_VULKAN_1_3);
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndMatch<EliminateDeadIOComponentsPass>(
+      text, true, spv::StorageClass::Input, false);
+}
+
+TEST_F(ElimDeadIOComponentsTest, ElimStructMember) {
+  // Should eliminate uv
+  //
+  // #version 450
+  //
+  // in Vertex {
+  //   vec4 Cd;
+  //   vec2 uv;
+  // } iVert;
+  //
+  // out vec4 fragColor;
+  //
+  // void main()
+  // {
+  //   vec4 color = vec4(iVert.Cd);
+  //   fragColor = color;
+  // }
+  const std::string text = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main" %iVert %fragColor
+               OpExecutionMode %main OriginUpperLeft
+               OpSource GLSL 450
+               OpName %main "main"
+               OpName %Vertex "Vertex"
+               OpMemberName %Vertex 0 "Cd"
+               OpMemberName %Vertex 1 "uv"
+               OpName %iVert "iVert"
+               OpName %fragColor "fragColor"
+               OpDecorate %Vertex Block
+               OpDecorate %iVert Location 0
+               OpDecorate %fragColor Location 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+    %v2float = OpTypeVector %float 2
+     %Vertex = OpTypeStruct %v4float %v2float
+; CHECK: %Vertex = OpTypeStruct %v4float %v2float
+; CHECK: %Vertex_0 = OpTypeStruct %v4float
+%_ptr_Input_Vertex = OpTypePointer Input %Vertex
+; CHECK: [[pty:%\w+]] = OpTypePointer Input %Vertex_0
+      %iVert = OpVariable %_ptr_Input_Vertex Input
+; CHECK: %iVert = OpVariable [[pty]] Input
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+  %fragColor = OpVariable %_ptr_Output_v4float Output
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %17 = OpAccessChain %_ptr_Input_v4float %iVert %int_0
+         %18 = OpLoad %v4float %17
+               OpStore %fragColor %18
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_VULKAN_1_3);
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndMatch<EliminateDeadIOComponentsPass>(
+      text, true, spv::StorageClass::Input, false);
+}
+
+TEST_F(ElimDeadIOComponentsTest, ElimOutputStructMember) {
+  // Should eliminate uv from Vertex and all but gl_Position from gl_PerVertex
+  //
+  // #version 450
+  //
+  // out Vertex {
+  //   vec4 Cd;
+  //   vec2 uv;
+  // } oVert;
+  //
+  // in vec3 P;
+  //
+  // void main()
+  // {
+  //   vec4 worldSpacePos = vec4(P, 1);
+  //   oVert.Cd = vec4(1, 0.5, 0, 1);
+  //   gl_Position = worldSpacePos;
+  // }
+
+  const std::string text = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main" %P %oVert %_
+               OpSource GLSL 450
+               OpName %main "main"
+               OpName %P "P"
+               OpName %Vertex "Vertex"
+               OpMemberName %Vertex 0 "Cd"
+               OpMemberName %Vertex 1 "uv"
+               OpName %oVert "oVert"
+               OpName %gl_PerVertex "gl_PerVertex"
+               OpMemberName %gl_PerVertex 0 "gl_Position"
+               OpMemberName %gl_PerVertex 1 "gl_PointSize"
+               OpMemberName %gl_PerVertex 2 "gl_ClipDistance"
+               OpMemberName %gl_PerVertex 3 "gl_CullDistance"
+               OpName %_ ""
+               OpDecorate %P Location 0
+               OpDecorate %Vertex Block
+               OpDecorate %oVert Location 0
+               OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
+               OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize
+               OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance
+               OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance
+               OpDecorate %gl_PerVertex Block
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+    %v3float = OpTypeVector %float 3
+%_ptr_Input_v3float = OpTypePointer Input %v3float
+          %P = OpVariable %_ptr_Input_v3float Input
+    %float_1 = OpConstant %float 1
+    %v2float = OpTypeVector %float 2
+     %Vertex = OpTypeStruct %v4float %v2float
+%_ptr_Output_Vertex = OpTypePointer Output %Vertex
+      %oVert = OpVariable %_ptr_Output_Vertex Output
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+  %float_0_5 = OpConstant %float 0.5
+    %float_0 = OpConstant %float 0
+         %27 = OpConstantComposite %v4float %float_1 %float_0_5 %float_0 %float_1
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+%_arr_float_uint_1 = OpTypeArray %float %uint_1
+%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1
+%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex
+          %_ = OpVariable %_ptr_Output_gl_PerVertex Output
+; CHECK: %Vertex = OpTypeStruct %v4float %v2float
+; CHECK: %gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1
+; CHECK: %_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex
+; CHECK: [[sty:%\w+]] = OpTypeStruct %v4float
+; CHECK: [[pty:%\w+]] = OpTypePointer Output [[sty]]
+; CHECK: %oVert = OpVariable [[pty]] Output
+; CHECK: [[sty2:%\w+]] = OpTypeStruct %v4float
+; CHECK: [[pty2:%\w+]] = OpTypePointer Output [[sty2]]
+; CHECK: %_ = OpVariable [[pty2]] Output
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %13 = OpLoad %v3float %P
+         %15 = OpCompositeExtract %float %13 0
+         %16 = OpCompositeExtract %float %13 1
+         %17 = OpCompositeExtract %float %13 2
+         %18 = OpCompositeConstruct %v4float %15 %16 %17 %float_1
+         %29 = OpAccessChain %_ptr_Output_v4float %oVert %int_0
+               OpStore %29 %27
+         %37 = OpAccessChain %_ptr_Output_v4float %_ %int_0
+               OpStore %37 %18
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_VULKAN_1_3);
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndMatch<EliminateDeadIOComponentsPass>(
+      text, true, spv::StorageClass::Output, false);
+}
+
+TEST_F(ElimDeadIOComponentsTest, ElimOutputArrayMembers) {
+  // Should reduce to uv[2]
+  //
+  // #version 450
+  //
+  // layout(location = 0) out vec2 uv[8];
+  //
+  // void main()
+  // {
+  //     uv[1] = vec2(1, 0.5);
+  // }
+
+  const std::string text = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main" %uv
+               OpExecutionMode %main OriginUpperLeft
+               OpSource GLSL 450
+               OpName %main "main"
+               OpName %uv "uv"
+               OpDecorate %uv Location 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v2float = OpTypeVector %float 2
+       %uint = OpTypeInt 32 0
+     %uint_8 = OpConstant %uint 8
+%_arr_v2float_uint_8 = OpTypeArray %v2float %uint_8
+%_ptr_Output__arr_v2float_uint_8 = OpTypePointer Output %_arr_v2float_uint_8
+         %uv = OpVariable %_ptr_Output__arr_v2float_uint_8 Output
+;CHECK-NOT:         %uv = OpVariable %_ptr_Output__arr_v2float_uint_8 Output
+;CHECK:             %uv = OpVariable %_ptr_Output__arr_v2float_uint_2 Output
+        %int = OpTypeInt 32 1
+      %int_1 = OpConstant %int 1
+    %float_1 = OpConstant %float 1
+  %float_0_5 = OpConstant %float 0.5
+         %17 = OpConstantComposite %v2float %float_1 %float_0_5
+%_ptr_Output_v2float = OpTypePointer Output %v2float
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %19 = OpAccessChain %_ptr_Output_v2float %uv %int_1
+               OpStore %19 %17
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_VULKAN_1_3);
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndMatch<EliminateDeadIOComponentsPass>(
+      text, true, spv::StorageClass::Output, false);
+}
+
+TEST_F(ElimDeadIOComponentsTest, VertexOnly) {
+  // Should NOT eliminate uv
+  //
+  // #version 450
+  //
+  // in Vertex {
+  //   vec4 Cd;
+  //   vec2 uv;
+  // } iVert;
+  //
+  // out vec4 fragColor;
+  //
+  // void main()
+  // {
+  //   vec4 color = vec4(iVert.Cd);
+  //   fragColor = color;
+  // }
+  const std::string text = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main" %iVert %fragColor
+               OpExecutionMode %main OriginUpperLeft
+               OpSource GLSL 450
+               OpName %main "main"
+               OpName %Vertex "Vertex"
+               OpMemberName %Vertex 0 "Cd"
+               OpMemberName %Vertex 1 "uv"
+               OpName %iVert "iVert"
+               OpName %fragColor "fragColor"
+               OpDecorate %Vertex Block
+               OpDecorate %iVert Location 0
+               OpDecorate %fragColor Location 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+    %v2float = OpTypeVector %float 2
+     %Vertex = OpTypeStruct %v4float %v2float
+; CHECK: %Vertex = OpTypeStruct %v4float %v2float
+%_ptr_Input_Vertex = OpTypePointer Input %Vertex
+; CHECK: %_ptr_Input_Vertex = OpTypePointer Input %Vertex
+      %iVert = OpVariable %_ptr_Input_Vertex Input
+; CHECK: %iVert = OpVariable %_ptr_Input_Vertex Input
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+  %fragColor = OpVariable %_ptr_Output_v4float Output
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %17 = OpAccessChain %_ptr_Input_v4float %iVert %int_0
+         %18 = OpLoad %v4float %17
+               OpStore %fragColor %18
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_VULKAN_1_3);
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndMatch<EliminateDeadIOComponentsPass>(
+      text, true, spv::StorageClass::Input, true);
+}
+
+TEST_F(ElimDeadIOComponentsTest, TescInput) {
+  // Eliminate PointSize, ClipDistance, CullDistance from gl_in[]
+  //
+  // #version 450
+  //
+  // layout (vertices = 4) out;
+  //
+  // void main()
+  // {
+  //     vec4 pos = gl_in[gl_InvocationID].gl_Position;
+  //     gl_out[gl_InvocationID].gl_Position = pos;
+  // }
+  const std::string text = R"(
+               OpCapability Tessellation
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint TessellationControl %main "main" %gl_in %gl_InvocationID %gl_out
+               OpExecutionMode %main OutputVertices 4
+               OpSource GLSL 450
+               OpName %main "main"
+               OpName %pos "pos"
+               OpName %gl_PerVertex "gl_PerVertex"
+               OpMemberName %gl_PerVertex 0 "gl_Position"
+               OpMemberName %gl_PerVertex 1 "gl_PointSize"
+               OpMemberName %gl_PerVertex 2 "gl_ClipDistance"
+               OpMemberName %gl_PerVertex 3 "gl_CullDistance"
+               OpName %gl_in "gl_in"
+               OpName %gl_InvocationID "gl_InvocationID"
+               OpName %gl_PerVertex_0 "gl_PerVertex"
+               OpMemberName %gl_PerVertex_0 0 "gl_Position"
+               OpName %gl_out "gl_out"
+               OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
+               OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize
+               OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance
+               OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance
+               OpDecorate %gl_PerVertex Block
+               OpDecorate %gl_InvocationID BuiltIn InvocationId
+               OpMemberDecorate %gl_PerVertex_0 0 BuiltIn Position
+               OpDecorate %gl_PerVertex_0 Block
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+%_arr_float_uint_1 = OpTypeArray %float %uint_1
+%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1
+    %uint_32 = OpConstant %uint 32
+%_arr_gl_PerVertex_uint_32 = OpTypeArray %gl_PerVertex %uint_32
+%_ptr_Input__arr_gl_PerVertex_uint_32 = OpTypePointer Input %_arr_gl_PerVertex_uint_32
+      %gl_in = OpVariable %_ptr_Input__arr_gl_PerVertex_uint_32 Input
+        %int = OpTypeInt 32 1
+%_ptr_Input_int = OpTypePointer Input %int
+%gl_InvocationID = OpVariable %_ptr_Input_int Input
+      %int_0 = OpConstant %int 0
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%gl_PerVertex_0 = OpTypeStruct %v4float
+     %uint_4 = OpConstant %uint 4
+%_arr_gl_PerVertex_0_uint_4 = OpTypeArray %gl_PerVertex_0 %uint_4
+%_ptr_Output__arr_gl_PerVertex_0_uint_4 = OpTypePointer Output %_arr_gl_PerVertex_0_uint_4
+     %gl_out = OpVariable %_ptr_Output__arr_gl_PerVertex_0_uint_4 Output
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+; CHECK: %gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1
+; CHECK: [[sty:%\w+]] = OpTypeStruct %v4float
+; CHECK: [[asty:%\w+]] = OpTypeArray [[sty]] %uint_32
+; CHECK: [[pasty:%\w+]] = OpTypePointer Input [[asty]]
+; CHECK: %gl_in = OpVariable [[pasty]] Input
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+        %pos = OpVariable %_ptr_Function_v4float Function
+         %21 = OpLoad %int %gl_InvocationID
+         %24 = OpAccessChain %_ptr_Input_v4float %gl_in %21 %int_0
+         %25 = OpLoad %v4float %24
+               OpStore %pos %25
+         %31 = OpLoad %int %gl_InvocationID
+         %32 = OpLoad %v4float %pos
+         %34 = OpAccessChain %_ptr_Output_v4float %gl_out %31 %int_0
+               OpStore %34 %32
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_VULKAN_1_3);
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndMatch<EliminateDeadIOComponentsPass>(
+      text, true, spv::StorageClass::Input, false);
+}
+
+TEST_F(ElimDeadIOComponentsTest, TescOutput) {
+  // Eliminate PointSize, ClipDistance, CullDistance from gl_out[]
+  //
+  // #version 450
+  //
+  // layout (vertices = 4) out;
+  //
+  // void main()
+  // {
+  //     vec4 pos = gl_in[gl_InvocationID].gl_Position;
+  //     gl_out[gl_InvocationID].gl_Position = pos;
+  // }
+  const std::string text = R"(
+               OpCapability Tessellation
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint TessellationControl %main "main" %gl_in %gl_InvocationID %gl_out
+               OpExecutionMode %main OutputVertices 4
+               OpSource GLSL 450
+               OpName %main "main"
+               OpName %pos "pos"
+               OpName %gl_PerVertex "gl_PerVertex"
+               OpMemberName %gl_PerVertex 0 "gl_Position"
+               OpName %gl_in "gl_in"
+               OpName %gl_InvocationID "gl_InvocationID"
+               OpName %gl_PerVertex_0 "gl_PerVertex"
+               OpMemberName %gl_PerVertex_0 0 "gl_Position"
+               OpMemberName %gl_PerVertex_0 1 "gl_PointSize"
+               OpMemberName %gl_PerVertex_0 2 "gl_ClipDistance"
+               OpMemberName %gl_PerVertex_0 3 "gl_CullDistance"
+               OpName %gl_out "gl_out"
+               OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
+               OpDecorate %gl_PerVertex Block
+               OpDecorate %gl_InvocationID BuiltIn InvocationId
+               OpMemberDecorate %gl_PerVertex_0 0 BuiltIn Position
+               OpMemberDecorate %gl_PerVertex_0 1 BuiltIn PointSize
+               OpMemberDecorate %gl_PerVertex_0 2 BuiltIn ClipDistance
+               OpMemberDecorate %gl_PerVertex_0 3 BuiltIn CullDistance
+               OpDecorate %gl_PerVertex_0 Block
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+%_arr_float_uint_1 = OpTypeArray %float %uint_1
+%gl_PerVertex = OpTypeStruct %v4float 
+    %uint_32 = OpConstant %uint 32
+%_arr_gl_PerVertex_uint_32 = OpTypeArray %gl_PerVertex %uint_32
+%_ptr_Input__arr_gl_PerVertex_uint_32 = OpTypePointer Input %_arr_gl_PerVertex_uint_32
+      %gl_in = OpVariable %_ptr_Input__arr_gl_PerVertex_uint_32 Input
+        %int = OpTypeInt 32 1
+%_ptr_Input_int = OpTypePointer Input %int
+%gl_InvocationID = OpVariable %_ptr_Input_int Input
+      %int_0 = OpConstant %int 0
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%gl_PerVertex_0 = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1
+     %uint_4 = OpConstant %uint 4
+%_arr_gl_PerVertex_0_uint_4 = OpTypeArray %gl_PerVertex_0 %uint_4
+%_ptr_Output__arr_gl_PerVertex_0_uint_4 = OpTypePointer Output %_arr_gl_PerVertex_0_uint_4
+     %gl_out = OpVariable %_ptr_Output__arr_gl_PerVertex_0_uint_4 Output
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+; CHECK: [[sty:%\w+]] = OpTypeStruct %v4float
+; CHECK: [[asty:%\w+]] = OpTypeArray [[sty]] %uint_4
+; CHECK: [[pasty:%\w+]] = OpTypePointer Output [[asty]]
+; CHECK: %gl_out = OpVariable [[pasty]] Output
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+        %pos = OpVariable %_ptr_Function_v4float Function
+         %21 = OpLoad %int %gl_InvocationID
+         %24 = OpAccessChain %_ptr_Input_v4float %gl_in %21 %int_0
+         %25 = OpLoad %v4float %24
+               OpStore %pos %25
+         %31 = OpLoad %int %gl_InvocationID
+         %32 = OpLoad %v4float %pos
+         %34 = OpAccessChain %_ptr_Output_v4float %gl_out %31 %int_0
+               OpStore %34 %32
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_VULKAN_1_3);
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndMatch<EliminateDeadIOComponentsPass>(
+      text, true, spv::StorageClass::Output, false);
+}
+
+TEST_F(ElimDeadIOComponentsTest, TeseInput) {
+  // Eliminate PointSize, ClipDistance, CullDistance from gl_in[]
+  //
+  // #version 450
+  //
+  // layout(triangles, ccw) in;
+  // layout(fractional_odd_spacing) in;
+  // layout(point_mode) in;
+  //
+  // void main()
+  // {
+  //     vec4 p = gl_in[1].gl_Position;
+  //     gl_Position = p;
+  // }
+  const std::string text = R"(
+               OpCapability Tessellation
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint TessellationEvaluation %main "main" %gl_in %_
+               OpExecutionMode %main Triangles
+               OpExecutionMode %main SpacingFractionalOdd
+               OpExecutionMode %main VertexOrderCcw
+               OpExecutionMode %main PointMode
+               OpSource GLSL 450
+               OpName %main "main"
+               OpName %p "p"
+               OpName %gl_PerVertex "gl_PerVertex"
+               OpMemberName %gl_PerVertex 0 "gl_Position"
+               OpMemberName %gl_PerVertex 1 "gl_PointSize"
+               OpMemberName %gl_PerVertex 2 "gl_ClipDistance"
+               OpMemberName %gl_PerVertex 3 "gl_CullDistance"
+               OpName %gl_in "gl_in"
+               OpName %gl_PerVertex_0 "gl_PerVertex"
+               OpMemberName %gl_PerVertex_0 0 "gl_Position"
+               OpName %_ ""
+               OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
+               OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize
+               OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance
+               OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance
+               OpDecorate %gl_PerVertex Block
+               OpMemberDecorate %gl_PerVertex_0 0 BuiltIn Position
+               OpDecorate %gl_PerVertex_0 Block
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+%_arr_float_uint_1 = OpTypeArray %float %uint_1
+%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1
+    %uint_32 = OpConstant %uint 32
+%_arr_gl_PerVertex_uint_32 = OpTypeArray %gl_PerVertex %uint_32
+%_ptr_Input__arr_gl_PerVertex_uint_32 = OpTypePointer Input %_arr_gl_PerVertex_uint_32
+      %gl_in = OpVariable %_ptr_Input__arr_gl_PerVertex_uint_32 Input
+        %int = OpTypeInt 32 1
+      %int_1 = OpConstant %int 1
+      %int_0 = OpConstant %int 0
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%gl_PerVertex_0 = OpTypeStruct %v4float
+%_ptr_Output_gl_PerVertex_0 = OpTypePointer Output %gl_PerVertex_0
+          %_ = OpVariable %_ptr_Output_gl_PerVertex_0 Output
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+; CHECK: %gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1
+; CHECK: [[sty:%\w+]] = OpTypeStruct %v4float
+; CHECK: [[asty:%\w+]] = OpTypeArray [[sty]] %uint_32
+; CHECK: [[pasty:%\w+]] = OpTypePointer Input [[asty]]
+; CHECK: %gl_in = OpVariable [[pasty]] Input
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+          %p = OpVariable %_ptr_Function_v4float Function
+         %22 = OpAccessChain %_ptr_Input_v4float %gl_in %int_1 %int_0
+         %23 = OpLoad %v4float %22
+               OpStore %p %23
+         %27 = OpLoad %v4float %p
+         %29 = OpAccessChain %_ptr_Output_v4float %_ %int_0
+               OpStore %29 %27
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_VULKAN_1_3);
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndMatch<EliminateDeadIOComponentsPass>(
+      text, true, spv::StorageClass::Input, false);
+}
+
+TEST_F(ElimDeadIOComponentsTest, TeseOutput) {
+  // Eliminate PointSize, ClipDistance, CullDistance from gl_out
+  //
+  // #version 450
+  //
+  // layout(triangles, ccw) in;
+  // layout(fractional_odd_spacing) in;
+  // layout(point_mode) in;
+  //
+  // void main()
+  // {
+  //     vec4 p = gl_in[1].gl_Position;
+  //     gl_Position = p;
+  // }
+  const std::string text = R"(
+               OpCapability Tessellation
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint TessellationEvaluation %main "main" %gl_in %_
+               OpExecutionMode %main Triangles
+               OpExecutionMode %main SpacingFractionalOdd
+               OpExecutionMode %main VertexOrderCcw
+               OpExecutionMode %main PointMode
+               OpSource GLSL 450
+               OpName %main "main"
+               OpName %p "p"
+               OpName %gl_PerVertex "gl_PerVertex"
+               OpMemberName %gl_PerVertex 0 "gl_Position"
+               OpName %gl_in "gl_in"
+               OpName %gl_PerVertex_0 "gl_PerVertex"
+               OpMemberName %gl_PerVertex_0 0 "gl_Position"
+               OpMemberName %gl_PerVertex_0 1 "gl_PointSize"
+               OpMemberName %gl_PerVertex_0 2 "gl_ClipDistance"
+               OpMemberName %gl_PerVertex_0 3 "gl_CullDistance"
+               OpName %_ ""
+               OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
+               OpDecorate %gl_PerVertex Block
+               OpMemberDecorate %gl_PerVertex_0 0 BuiltIn Position
+               OpMemberDecorate %gl_PerVertex_0 1 BuiltIn PointSize
+               OpMemberDecorate %gl_PerVertex_0 2 BuiltIn ClipDistance
+               OpMemberDecorate %gl_PerVertex_0 3 BuiltIn CullDistance
+               OpDecorate %gl_PerVertex_0 Block
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+%_arr_float_uint_1 = OpTypeArray %float %uint_1
+%gl_PerVertex = OpTypeStruct %v4float
+    %uint_32 = OpConstant %uint 32
+%_arr_gl_PerVertex_uint_32 = OpTypeArray %gl_PerVertex %uint_32
+%_ptr_Input__arr_gl_PerVertex_uint_32 = OpTypePointer Input %_arr_gl_PerVertex_uint_32
+      %gl_in = OpVariable %_ptr_Input__arr_gl_PerVertex_uint_32 Input
+        %int = OpTypeInt 32 1
+      %int_1 = OpConstant %int 1
+      %int_0 = OpConstant %int 0
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%gl_PerVertex_0 = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1
+%_ptr_Output_gl_PerVertex_0 = OpTypePointer Output %gl_PerVertex_0
+          %_ = OpVariable %_ptr_Output_gl_PerVertex_0 Output
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+; CHECK: %_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex
+; CHECK: %_ = OpVariable %_ptr_Output_gl_PerVertex Output
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+          %p = OpVariable %_ptr_Function_v4float Function
+         %22 = OpAccessChain %_ptr_Input_v4float %gl_in %int_1 %int_0
+         %23 = OpLoad %v4float %22
+               OpStore %p %23
+         %27 = OpLoad %v4float %p
+         %29 = OpAccessChain %_ptr_Output_v4float %_ %int_0
+               OpStore %29 %27
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_VULKAN_1_3);
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndMatch<EliminateDeadIOComponentsPass>(
+      text, true, spv::StorageClass::Output, false);
+}
+
+TEST_F(ElimDeadIOComponentsTest, GeomInput) {
+  // Eliminate PointSize, ClipDistance, CullDistance from gl_in[]
+  //
+  // #version 450
+  //
+  // layout(triangle_strip, max_vertices = 3) out;
+  // layout(triangles) in;
+  //
+  // void main()
+  // {
+  //         for (int i = 0; i < 3; i++)
+  //         {
+  //                 gl_Position = gl_in[i].gl_Position;
+  //                 EmitVertex();
+  //         }
+  //         EndPrimitive();
+  // }
+  const std::string text = R"(
+               OpCapability Geometry
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Geometry %main "main" %_ %gl_in
+               OpExecutionMode %main Triangles
+               OpExecutionMode %main Invocations 1
+               OpExecutionMode %main OutputTriangleStrip
+               OpExecutionMode %main OutputVertices 3
+               OpSource GLSL 460
+               OpName %main "main"
+               OpName %i "i"
+               OpName %gl_PerVertex "gl_PerVertex"
+               OpMemberName %gl_PerVertex 0 "gl_Position"
+               OpName %_ ""
+               OpName %gl_PerVertex_0 "gl_PerVertex"
+               OpMemberName %gl_PerVertex_0 0 "gl_Position"
+               OpMemberName %gl_PerVertex_0 1 "gl_PointSize"
+               OpMemberName %gl_PerVertex_0 2 "gl_ClipDistance"
+               OpMemberName %gl_PerVertex_0 3 "gl_CullDistance"
+               OpName %gl_in "gl_in"
+               OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
+               OpDecorate %gl_PerVertex Block
+               OpMemberDecorate %gl_PerVertex_0 0 BuiltIn Position
+               OpMemberDecorate %gl_PerVertex_0 1 BuiltIn PointSize
+               OpMemberDecorate %gl_PerVertex_0 2 BuiltIn ClipDistance
+               OpMemberDecorate %gl_PerVertex_0 3 BuiltIn CullDistance
+               OpDecorate %gl_PerVertex_0 Block
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+        %int = OpTypeInt 32 1
+%_ptr_Function_int = OpTypePointer Function %int
+      %int_0 = OpConstant %int 0
+      %int_3 = OpConstant %int 3
+       %bool = OpTypeBool
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+%_arr_float_uint_1 = OpTypeArray %float %uint_1
+%gl_PerVertex = OpTypeStruct %v4float
+%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex
+          %_ = OpVariable %_ptr_Output_gl_PerVertex Output
+%gl_PerVertex_0 = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1
+     %uint_3 = OpConstant %uint 3
+%_arr_gl_PerVertex_0_uint_3 = OpTypeArray %gl_PerVertex_0 %uint_3
+%_ptr_Input__arr_gl_PerVertex_0_uint_3 = OpTypePointer Input %_arr_gl_PerVertex_0_uint_3
+      %gl_in = OpVariable %_ptr_Input__arr_gl_PerVertex_0_uint_3 Input
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+      %int_1 = OpConstant %int 1
+; CHECK: [[asty:%\w+]] = OpTypeArray %gl_PerVertex %uint_3
+; CHECK: [[pasty:%\w+]] = OpTypePointer Input [[asty]]
+; CHECK: %gl_in = OpVariable [[pasty]] Input
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+          %i = OpVariable %_ptr_Function_int Function
+               OpStore %i %int_0
+               OpBranch %10
+         %10 = OpLabel
+               OpLoopMerge %12 %13 None
+               OpBranch %14
+         %14 = OpLabel
+         %15 = OpLoad %int %i
+         %18 = OpSLessThan %bool %15 %int_3
+               OpBranchConditional %18 %11 %12
+         %11 = OpLabel
+         %32 = OpLoad %int %i
+         %34 = OpAccessChain %_ptr_Input_v4float %gl_in %32 %int_0
+         %35 = OpLoad %v4float %34
+         %37 = OpAccessChain %_ptr_Output_v4float %_ %int_0
+               OpStore %37 %35
+               OpEmitVertex
+               OpBranch %13
+         %13 = OpLabel
+         %38 = OpLoad %int %i
+         %40 = OpIAdd %int %38 %int_1
+               OpStore %i %40
+               OpBranch %10
+         %12 = OpLabel
+               OpEndPrimitive
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_VULKAN_1_3);
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndMatch<EliminateDeadIOComponentsPass>(
+      text, true, spv::StorageClass::Input, false);
+}
+
+TEST_F(ElimDeadIOComponentsTest, GeomOutput) {
+  // Eliminate PointSize, ClipDistance, CullDistance from gl_out
+  //
+  // #version 450
+  //
+  // layout(triangle_strip, max_vertices = 3) out;
+  // layout(triangles) in;
+  //
+  // void main()
+  // {
+  //         for (int i = 0; i < 3; i++)
+  //         {
+  //                 gl_Position = gl_in[i].gl_Position;
+  //                 EmitVertex();
+  //         }
+  //         EndPrimitive();
+  // }
+  const std::string text = R"(
+               OpCapability Geometry
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Geometry %main "main" %_ %gl_in
+               OpExecutionMode %main Triangles
+               OpExecutionMode %main Invocations 1
+               OpExecutionMode %main OutputTriangleStrip
+               OpExecutionMode %main OutputVertices 3
+               OpSource GLSL 460
+               OpName %main "main"
+               OpName %i "i"
+               OpName %gl_PerVertex "gl_PerVertex"
+               OpMemberName %gl_PerVertex 0 "gl_Position"
+               OpMemberName %gl_PerVertex 1 "gl_PointSize"
+               OpMemberName %gl_PerVertex 2 "gl_ClipDistance"
+               OpMemberName %gl_PerVertex 3 "gl_CullDistance"
+               OpName %_ ""
+               OpName %gl_PerVertex_0 "gl_PerVertex"
+               OpMemberName %gl_PerVertex_0 0 "gl_Position"
+               OpName %gl_in "gl_in"
+               OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
+               OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize
+               OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance
+               OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance
+               OpDecorate %gl_PerVertex Block
+               OpMemberDecorate %gl_PerVertex_0 0 BuiltIn Position
+               OpDecorate %gl_PerVertex_0 Block
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+        %int = OpTypeInt 32 1
+%_ptr_Function_int = OpTypePointer Function %int
+      %int_0 = OpConstant %int 0
+      %int_3 = OpConstant %int 3
+       %bool = OpTypeBool
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+%_arr_float_uint_1 = OpTypeArray %float %uint_1
+%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1
+%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex
+          %_ = OpVariable %_ptr_Output_gl_PerVertex Output
+%gl_PerVertex_0 = OpTypeStruct %v4float
+     %uint_3 = OpConstant %uint 3
+%_arr_gl_PerVertex_0_uint_3 = OpTypeArray %gl_PerVertex_0 %uint_3
+%_ptr_Input__arr_gl_PerVertex_0_uint_3 = OpTypePointer Input %_arr_gl_PerVertex_0_uint_3
+      %gl_in = OpVariable %_ptr_Input__arr_gl_PerVertex_0_uint_3 Input
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+      %int_1 = OpConstant %int 1
+; CHECK: %_ptr_Output_gl_PerVertex_0 = OpTypePointer Output %gl_PerVertex_0
+; CHECK: %_ = OpVariable %_ptr_Output_gl_PerVertex_0 Output
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+          %i = OpVariable %_ptr_Function_int Function
+               OpStore %i %int_0
+               OpBranch %10
+         %10 = OpLabel
+               OpLoopMerge %12 %13 None
+               OpBranch %14
+         %14 = OpLabel
+         %15 = OpLoad %int %i
+         %18 = OpSLessThan %bool %15 %int_3
+               OpBranchConditional %18 %11 %12
+         %11 = OpLabel
+         %32 = OpLoad %int %i
+         %34 = OpAccessChain %_ptr_Input_v4float %gl_in %32 %int_0
+         %35 = OpLoad %v4float %34
+         %37 = OpAccessChain %_ptr_Output_v4float %_ %int_0
+               OpStore %37 %35
+               OpEmitVertex
+               OpBranch %13
+         %13 = OpLabel
+         %38 = OpLoad %int %i
+         %40 = OpIAdd %int %38 %int_1
+               OpStore %i %40
+               OpBranch %10
+         %12 = OpLabel
+               OpEndPrimitive
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_VULKAN_1_3);
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndMatch<EliminateDeadIOComponentsPass>(
+      text, true, spv::StorageClass::Output, false);
+}
+
+}  // namespace
+}  // namespace opt
+}  // namespace spvtools
diff --git a/test/opt/eliminate_dead_member_test.cpp b/test/opt/eliminate_dead_member_test.cpp
index e277999..bb0ec03 100644
--- a/test/opt/eliminate_dead_member_test.cpp
+++ b/test/opt/eliminate_dead_member_test.cpp
@@ -13,7 +13,6 @@
 // limitations under the License.
 
 #include "assembly_builder.h"
-#include "gmock/gmock.h"
 #include "pass_fixture.h"
 #include "pass_utils.h"
 
@@ -978,6 +977,7 @@
                OpMemberDecorate %type__Globals 1 Offset 4
                OpMemberDecorate %type__Globals 2 Offset 16
                OpDecorate %type__Globals Block
+               OpDecorate %_ptr_Uniform_type__Globals ArrayStride 8
        %uint = OpTypeInt 32 0
      %uint_0 = OpConstant %uint 0
      %uint_1 = OpConstant %uint 1
diff --git a/test/opt/eliminate_dead_output_stores_test.cpp b/test/opt/eliminate_dead_output_stores_test.cpp
new file mode 100644
index 0000000..4c2e44c
--- /dev/null
+++ b/test/opt/eliminate_dead_output_stores_test.cpp
@@ -0,0 +1,951 @@
+// Copyright (c) 2022 The Khronos Group Inc.
+// Copyright (c) 2022 LunarG Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <unordered_set>
+
+#include "test/opt/pass_fixture.h"
+#include "test/opt/pass_utils.h"
+
+namespace spvtools {
+namespace opt {
+namespace {
+
+using ElimDeadOutputStoresTest = PassTest<::testing::Test>;
+
+TEST_F(ElimDeadOutputStoresTest, VertMultipleLocations) {
+  // #version 450
+  //
+  // layout(location = 2) out Vertex
+  // {
+  //         vec4 color0;
+  //         vec4 color1;
+  //         vec4 color2[3];
+  // } oVert;
+  //
+  // void main()
+  // {
+  //     oVert.color0 = vec4(0.0,0.0,0.0,0.0);
+  //     oVert.color1 = vec4(0.1,0.0,0.0,0.0);
+  //     oVert.color2[0] = vec4(0.2,0.0,0.0,0.0);
+  //     oVert.color2[1] = vec4(0.3,0.0,0.0,0.0);
+  //     oVert.color2[2] = vec4(0.4,0.0,0.0,0.0);
+  // }
+  const std::string text = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main" %oVert
+               OpSource GLSL 450
+               OpName %main "main"
+               OpName %Vertex "Vertex"
+               OpMemberName %Vertex 0 "color0"
+               OpMemberName %Vertex 1 "color1"
+               OpMemberName %Vertex 2 "color2"
+               OpName %oVert "oVert"
+               OpDecorate %Vertex Block
+               OpDecorate %oVert Location 2
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+       %uint = OpTypeInt 32 0
+     %uint_3 = OpConstant %uint 3
+%_arr_v4float_uint_3 = OpTypeArray %v4float %uint_3
+     %Vertex = OpTypeStruct %v4float %v4float %_arr_v4float_uint_3
+%_ptr_Output_Vertex = OpTypePointer Output %Vertex
+      %oVert = OpVariable %_ptr_Output_Vertex Output
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+    %float_0 = OpConstant %float 0
+         %17 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+      %int_1 = OpConstant %int 1
+%float_0_100000001 = OpConstant %float 0.100000001
+         %22 = OpConstantComposite %v4float %float_0_100000001 %float_0 %float_0 %float_0
+      %int_2 = OpConstant %int 2
+%float_0_200000003 = OpConstant %float 0.200000003
+         %26 = OpConstantComposite %v4float %float_0_200000003 %float_0 %float_0 %float_0
+%float_0_300000012 = OpConstant %float 0.300000012
+         %29 = OpConstantComposite %v4float %float_0_300000012 %float_0 %float_0 %float_0
+%float_0_400000006 = OpConstant %float 0.400000006
+         %32 = OpConstantComposite %v4float %float_0_400000006 %float_0 %float_0 %float_0
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %19 = OpAccessChain %_ptr_Output_v4float %oVert %int_0
+               OpStore %19 %17
+;CHECK:            OpStore %19 %17
+         %23 = OpAccessChain %_ptr_Output_v4float %oVert %int_1
+               OpStore %23 %22
+;CHECK-NOT:        OpStore %23 %22
+         %27 = OpAccessChain %_ptr_Output_v4float %oVert %int_2 %int_0
+               OpStore %27 %26
+;CHECK-NOT:        OpStore %27 %26
+         %30 = OpAccessChain %_ptr_Output_v4float %oVert %int_2 %int_1
+               OpStore %30 %29
+;CHECK:            OpStore %30 %29
+         %33 = OpAccessChain %_ptr_Output_v4float %oVert %int_2 %int_2
+               OpStore %33 %32
+;CHECK-NOT:        OpStore %33 %32
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_VULKAN_1_3);
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+  std::unordered_set<uint32_t> live_inputs;
+  std::unordered_set<uint32_t> live_builtins;
+  live_inputs.insert(2);
+  live_inputs.insert(5);
+  SinglePassRunAndMatch<EliminateDeadOutputStoresPass>(text, true, &live_inputs,
+                                                       &live_builtins);
+}
+
+TEST_F(ElimDeadOutputStoresTest, VertMatrix) {
+  // #version 450
+  //
+  // layout(location = 2) out Vertex
+  // {
+  //         vec4 color0;
+  //         vec4 color1;
+  //         mat4 color2;
+  //         mat4 color3;
+  //         mat4 color4;
+  // } oVert;
+  //
+  // void main()
+  // {
+  //     oVert.color0 = vec4(0.0,0.0,0.0,0.0);
+  //     oVert.color1 = vec4(0.1,0.0,0.0,0.0);
+  //     oVert.color2[2] = vec4(0.2,0.0,0.0,0.0);
+  //     oVert.color3[1] = vec4(0.3,0.0,0.0,0.0);
+  //     oVert.color4[0] = vec4(0.4,0.0,0.0,0.0);
+  // }
+  const std::string text = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main" %oVert
+               OpSource GLSL 450
+               OpName %main "main"
+               OpName %Vertex "Vertex"
+               OpMemberName %Vertex 0 "color0"
+               OpMemberName %Vertex 1 "color1"
+               OpMemberName %Vertex 2 "color2"
+               OpMemberName %Vertex 3 "color3"
+               OpMemberName %Vertex 4 "color4"
+               OpName %oVert "oVert"
+               OpDecorate %Vertex Block
+               OpDecorate %oVert Location 2
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%mat4v4float = OpTypeMatrix %v4float 4
+     %Vertex = OpTypeStruct %v4float %v4float %mat4v4float %mat4v4float %mat4v4float
+%_ptr_Output_Vertex = OpTypePointer Output %Vertex
+      %oVert = OpVariable %_ptr_Output_Vertex Output
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+    %float_0 = OpConstant %float 0
+         %15 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+      %int_1 = OpConstant %int 1
+%float_0_100000001 = OpConstant %float 0.100000001
+         %20 = OpConstantComposite %v4float %float_0_100000001 %float_0 %float_0 %float_0
+      %int_2 = OpConstant %int 2
+%float_0_200000003 = OpConstant %float 0.200000003
+         %24 = OpConstantComposite %v4float %float_0_200000003 %float_0 %float_0 %float_0
+      %int_3 = OpConstant %int 3
+%float_0_300000012 = OpConstant %float 0.300000012
+         %28 = OpConstantComposite %v4float %float_0_300000012 %float_0 %float_0 %float_0
+      %int_4 = OpConstant %int 4
+%float_0_400000006 = OpConstant %float 0.400000006
+         %32 = OpConstantComposite %v4float %float_0_400000006 %float_0 %float_0 %float_0
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %17 = OpAccessChain %_ptr_Output_v4float %oVert %int_0
+               OpStore %17 %15
+; CHECK:           OpStore %17 %15
+         %21 = OpAccessChain %_ptr_Output_v4float %oVert %int_1
+               OpStore %21 %20
+; CHECK-NOT:       OpStore %21 %20
+         %25 = OpAccessChain %_ptr_Output_v4float %oVert %int_2 %int_2
+               OpStore %25 %24
+; CHECK-NOT:       OpStore %25 %24
+         %29 = OpAccessChain %_ptr_Output_v4float %oVert %int_3 %int_1
+               OpStore %29 %28
+; CHECK:           OpStore %29 %28
+         %33 = OpAccessChain %_ptr_Output_v4float %oVert %int_4 %int_0
+               OpStore %33 %32
+; CHECK-NOT:       OpStore %33 %32
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_VULKAN_1_3);
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+  std::unordered_set<uint32_t> live_inputs;
+  std::unordered_set<uint32_t> live_builtins;
+  live_inputs.insert(2);
+  live_inputs.insert(8);
+  live_inputs.insert(9);
+  live_inputs.insert(10);
+  live_inputs.insert(11);
+  SinglePassRunAndMatch<EliminateDeadOutputStoresPass>(text, true, &live_inputs,
+                                                       &live_builtins);
+}
+
+TEST_F(ElimDeadOutputStoresTest, VertMemberLocs) {
+  // #version 450
+  //
+  // out Vertex
+  // {
+  //     layout (location = 1) vec4 Cd;
+  //     layout (location = 0) vec2 uv;
+  // } oVert;
+  //
+  // layout (location  = 0) in vec3 P;
+  //
+  // void main()
+  // {
+  //     oVert.uv = vec2(0.1, 0.7);
+  //     oVert.Cd = vec4(1, 0.5, 0, 1);
+  //     gl_Position = vec4(P, 1);
+  // }
+  const std::string text = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main" %oVert %_ %P
+               OpSource GLSL 450
+               OpName %main "main"
+               OpName %Vertex "Vertex"
+               OpMemberName %Vertex 0 "Cd"
+               OpMemberName %Vertex 1 "uv"
+               OpName %oVert "oVert"
+               OpName %gl_PerVertex "gl_PerVertex"
+               OpMemberName %gl_PerVertex 0 "gl_Position"
+               OpMemberName %gl_PerVertex 1 "gl_PointSize"
+               OpMemberName %gl_PerVertex 2 "gl_ClipDistance"
+               OpMemberName %gl_PerVertex 3 "gl_CullDistance"
+               OpName %_ ""
+               OpName %P "P"
+               OpMemberDecorate %Vertex 0 Location 1
+               OpMemberDecorate %Vertex 1 Location 0
+               OpDecorate %Vertex Block
+               OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
+               OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize
+               OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance
+               OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance
+               OpDecorate %gl_PerVertex Block
+               OpDecorate %P Location 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+    %v2float = OpTypeVector %float 2
+     %Vertex = OpTypeStruct %v4float %v2float
+%_ptr_Output_Vertex = OpTypePointer Output %Vertex
+      %oVert = OpVariable %_ptr_Output_Vertex Output
+        %int = OpTypeInt 32 1
+      %int_1 = OpConstant %int 1
+%float_0_100000001 = OpConstant %float 0.100000001
+%float_0_699999988 = OpConstant %float 0.699999988
+         %16 = OpConstantComposite %v2float %float_0_100000001 %float_0_699999988
+%_ptr_Output_v2float = OpTypePointer Output %v2float
+      %int_0 = OpConstant %int 0
+    %float_1 = OpConstant %float 1
+  %float_0_5 = OpConstant %float 0.5
+    %float_0 = OpConstant %float 0
+         %23 = OpConstantComposite %v4float %float_1 %float_0_5 %float_0 %float_1
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+%_arr_float_uint_1 = OpTypeArray %float %uint_1
+%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1
+%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex
+          %_ = OpVariable %_ptr_Output_gl_PerVertex Output
+    %v3float = OpTypeVector %float 3
+%_ptr_Input_v3float = OpTypePointer Input %v3float
+          %P = OpVariable %_ptr_Input_v3float Input
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %18 = OpAccessChain %_ptr_Output_v2float %oVert %int_1
+               OpStore %18 %16
+; CHECK-NOT:       OpStore %18 %16
+         %25 = OpAccessChain %_ptr_Output_v4float %oVert %int_0
+               OpStore %25 %23
+; CHECK:           OpStore %25 %23
+         %35 = OpLoad %v3float %P
+         %36 = OpCompositeExtract %float %35 0
+         %37 = OpCompositeExtract %float %35 1
+         %38 = OpCompositeExtract %float %35 2
+         %39 = OpCompositeConstruct %v4float %36 %37 %38 %float_1
+         %40 = OpAccessChain %_ptr_Output_v4float %_ %int_0
+               OpStore %40 %39
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_VULKAN_1_3);
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+  std::unordered_set<uint32_t> live_inputs;
+  std::unordered_set<uint32_t> live_builtins;
+  live_inputs.insert(1);
+  SinglePassRunAndMatch<EliminateDeadOutputStoresPass>(text, true, &live_inputs,
+                                                       &live_builtins);
+}
+
+TEST_F(ElimDeadOutputStoresTest, ArrayedOutput) {
+  // Tests elimination of arrayed output as seen in Tesc shaders.
+  //
+  // #version 450
+  //
+  // layout (vertices = 4) out;
+  //
+  // layout (location = 0) in vec3 N[];
+  // layout (location = 1) in vec3 P[];
+  //
+  // layout (location = 5) out Vertex
+  // {
+  //                 vec4 c;
+  //                 vec3 n;
+  //                 vec3 f[10];
+  // } oVert[];
+  //
+  // void main()
+  // {
+  //                 oVert[gl_InvocationID].c = vec4(1, 0, 0, 1);
+  //                 oVert[gl_InvocationID].n = N[gl_InvocationID];
+  //                 oVert[gl_InvocationID].f[3] = vec3(0, 1, 0);
+  //                 vec4 worldSpacePos = vec4(P[gl_InvocationID], 1);
+  //                 gl_out[gl_InvocationID].gl_Position = worldSpacePos;
+  // }
+  const std::string text = R"(
+               OpCapability Tessellation
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint TessellationControl %main "main" %oVert %gl_InvocationID %N %P %gl_out
+               OpExecutionMode %main OutputVertices 4
+               OpSource GLSL 450
+               OpName %main "main"
+               OpName %Vertex "Vertex"
+               OpMemberName %Vertex 0 "c"
+               OpMemberName %Vertex 1 "n"
+               OpMemberName %Vertex 2 "f"
+               OpName %oVert "oVert"
+               OpName %gl_InvocationID "gl_InvocationID"
+               OpName %N "N"
+               OpName %P "P"
+               OpName %gl_PerVertex "gl_PerVertex"
+               OpMemberName %gl_PerVertex 0 "gl_Position"
+               OpMemberName %gl_PerVertex 1 "gl_PointSize"
+               OpMemberName %gl_PerVertex 2 "gl_ClipDistance"
+               OpMemberName %gl_PerVertex 3 "gl_CullDistance"
+               OpName %gl_out "gl_out"
+               OpDecorate %Vertex Block
+               OpDecorate %oVert Location 5
+               OpDecorate %gl_InvocationID BuiltIn InvocationId
+               OpDecorate %N Location 0
+               OpDecorate %P Location 1
+               OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
+               OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize
+               OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance
+               OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance
+               OpDecorate %gl_PerVertex Block
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+    %v3float = OpTypeVector %float 3
+       %uint = OpTypeInt 32 0
+    %uint_10 = OpConstant %uint 10
+%_arr_v3float_uint_10 = OpTypeArray %v3float %uint_10
+     %Vertex = OpTypeStruct %v4float %v3float %_arr_v3float_uint_10
+     %uint_4 = OpConstant %uint 4
+%_arr_Vertex_uint_4 = OpTypeArray %Vertex %uint_4
+%_ptr_Output__arr_Vertex_uint_4 = OpTypePointer Output %_arr_Vertex_uint_4
+      %oVert = OpVariable %_ptr_Output__arr_Vertex_uint_4 Output
+        %int = OpTypeInt 32 1
+%_ptr_Input_int = OpTypePointer Input %int
+%gl_InvocationID = OpVariable %_ptr_Input_int Input
+      %int_0 = OpConstant %int 0
+    %float_1 = OpConstant %float 1
+    %float_0 = OpConstant %float 0
+         %24 = OpConstantComposite %v4float %float_1 %float_0 %float_0 %float_1
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+      %int_1 = OpConstant %int 1
+    %uint_32 = OpConstant %uint 32
+%_arr_v3float_uint_32 = OpTypeArray %v3float %uint_32
+%_ptr_Input__arr_v3float_uint_32 = OpTypePointer Input %_arr_v3float_uint_32
+          %N = OpVariable %_ptr_Input__arr_v3float_uint_32 Input
+%_ptr_Input_v3float = OpTypePointer Input %v3float
+%_ptr_Output_v3float = OpTypePointer Output %v3float
+      %int_2 = OpConstant %int 2
+      %int_3 = OpConstant %int 3
+         %42 = OpConstantComposite %v3float %float_0 %float_1 %float_0
+          %P = OpVariable %_ptr_Input__arr_v3float_uint_32 Input
+     %uint_1 = OpConstant %uint 1
+%_arr_float_uint_1 = OpTypeArray %float %uint_1
+%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1
+%_arr_gl_PerVertex_uint_4 = OpTypeArray %gl_PerVertex %uint_4
+%_ptr_Output__arr_gl_PerVertex_uint_4 = OpTypePointer Output %_arr_gl_PerVertex_uint_4
+     %gl_out = OpVariable %_ptr_Output__arr_gl_PerVertex_uint_4 Output
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %20 = OpLoad %int %gl_InvocationID
+         %26 = OpAccessChain %_ptr_Output_v4float %oVert %20 %int_0
+               OpStore %26 %24
+; CHECK:           OpStore %26 %24
+         %35 = OpAccessChain %_ptr_Input_v3float %N %20
+         %36 = OpLoad %v3float %35
+         %38 = OpAccessChain %_ptr_Output_v3float %oVert %20 %int_1
+               OpStore %38 %36
+; CHECK-NOT:       OpStore %38 %36
+         %43 = OpAccessChain %_ptr_Output_v3float %oVert %20 %int_2 %int_3
+               OpStore %43 %42
+; CHECK:           OpStore %43 %42
+         %48 = OpAccessChain %_ptr_Input_v3float %P %20
+         %49 = OpLoad %v3float %48
+         %50 = OpCompositeExtract %float %49 0
+         %51 = OpCompositeExtract %float %49 1
+         %52 = OpCompositeExtract %float %49 2
+         %53 = OpCompositeConstruct %v4float %50 %51 %52 %float_1
+         %62 = OpAccessChain %_ptr_Output_v4float %gl_out %20 %int_0
+               OpStore %62 %53
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_VULKAN_1_3);
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+  std::unordered_set<uint32_t> live_inputs;
+  std::unordered_set<uint32_t> live_builtins;
+  live_inputs.insert(5);
+  live_inputs.insert(10);
+  SinglePassRunAndMatch<EliminateDeadOutputStoresPass>(text, true, &live_inputs,
+                                                       &live_builtins);
+}
+
+TEST_F(ElimDeadOutputStoresTest, ArrayedOutputMemberLocs) {
+  // Tests elimination of member location with arrayed output as seen in
+  // Tesc shaders.
+  //
+  // #version 450
+  //
+  // layout (vertices = 4) out;
+  //
+  // layout (location = 0) in vec3 N[];
+  // layout (location = 1) in vec3 P[];
+  //
+  // out Vertex
+  // {
+  //                 layout (location = 1) vec4 c;
+  //                 layout (location = 3) vec3 n;
+  //                 layout (location = 5) vec3 f[10];
+  // } oVert[];
+  //
+  // void main()
+  // {
+  //                 oVert[gl_InvocationID].c = vec4(1, 0, 0, 1);
+  //                 oVert[gl_InvocationID].n = N[gl_InvocationID];
+  //                 oVert[gl_InvocationID].f[3] = vec3(0, 1, 0);
+  //                 vec4 worldSpacePos = vec4(P[gl_InvocationID], 1);
+  //                 gl_out[gl_InvocationID].gl_Position = worldSpacePos;
+  // }
+  const std::string text = R"(
+               OpCapability Tessellation
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint TessellationControl %main "main" %oVert %gl_InvocationID %N %P %gl_out
+               OpExecutionMode %main OutputVertices 4
+               OpSource GLSL 450
+               OpName %main "main"
+               OpName %Vertex "Vertex"
+               OpMemberName %Vertex 0 "c"
+               OpMemberName %Vertex 1 "n"
+               OpMemberName %Vertex 2 "f"
+               OpName %oVert "oVert"
+               OpName %gl_InvocationID "gl_InvocationID"
+               OpName %N "N"
+               OpName %P "P"
+               OpName %gl_PerVertex "gl_PerVertex"
+               OpMemberName %gl_PerVertex 0 "gl_Position"
+               OpMemberName %gl_PerVertex 1 "gl_PointSize"
+               OpMemberName %gl_PerVertex 2 "gl_ClipDistance"
+               OpMemberName %gl_PerVertex 3 "gl_CullDistance"
+               OpName %gl_out "gl_out"
+               OpMemberDecorate %Vertex 0 Location 1
+               OpMemberDecorate %Vertex 1 Location 3
+               OpMemberDecorate %Vertex 2 Location 5
+               OpDecorate %Vertex Block
+               OpDecorate %gl_InvocationID BuiltIn InvocationId
+               OpDecorate %N Location 0
+               OpDecorate %P Location 1
+               OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
+               OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize
+               OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance
+               OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance
+               OpDecorate %gl_PerVertex Block
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+    %v3float = OpTypeVector %float 3
+       %uint = OpTypeInt 32 0
+    %uint_10 = OpConstant %uint 10
+%_arr_v3float_uint_10 = OpTypeArray %v3float %uint_10
+     %Vertex = OpTypeStruct %v4float %v3float %_arr_v3float_uint_10
+     %uint_4 = OpConstant %uint 4
+%_arr_Vertex_uint_4 = OpTypeArray %Vertex %uint_4
+%_ptr_Output__arr_Vertex_uint_4 = OpTypePointer Output %_arr_Vertex_uint_4
+      %oVert = OpVariable %_ptr_Output__arr_Vertex_uint_4 Output
+        %int = OpTypeInt 32 1
+%_ptr_Input_int = OpTypePointer Input %int
+%gl_InvocationID = OpVariable %_ptr_Input_int Input
+      %int_0 = OpConstant %int 0
+    %float_1 = OpConstant %float 1
+    %float_0 = OpConstant %float 0
+         %24 = OpConstantComposite %v4float %float_1 %float_0 %float_0 %float_1
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+      %int_1 = OpConstant %int 1
+    %uint_32 = OpConstant %uint 32
+%_arr_v3float_uint_32 = OpTypeArray %v3float %uint_32
+%_ptr_Input__arr_v3float_uint_32 = OpTypePointer Input %_arr_v3float_uint_32
+          %N = OpVariable %_ptr_Input__arr_v3float_uint_32 Input
+%_ptr_Input_v3float = OpTypePointer Input %v3float
+%_ptr_Output_v3float = OpTypePointer Output %v3float
+      %int_2 = OpConstant %int 2
+      %int_3 = OpConstant %int 3
+         %42 = OpConstantComposite %v3float %float_0 %float_1 %float_0
+          %P = OpVariable %_ptr_Input__arr_v3float_uint_32 Input
+     %uint_1 = OpConstant %uint 1
+%_arr_float_uint_1 = OpTypeArray %float %uint_1
+%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1
+%_arr_gl_PerVertex_uint_4 = OpTypeArray %gl_PerVertex %uint_4
+%_ptr_Output__arr_gl_PerVertex_uint_4 = OpTypePointer Output %_arr_gl_PerVertex_uint_4
+     %gl_out = OpVariable %_ptr_Output__arr_gl_PerVertex_uint_4 Output
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %20 = OpLoad %int %gl_InvocationID
+         %26 = OpAccessChain %_ptr_Output_v4float %oVert %20 %int_0
+               OpStore %26 %24
+;CHECK:            OpStore %26 %24
+         %35 = OpAccessChain %_ptr_Input_v3float %N %20
+         %36 = OpLoad %v3float %35
+         %38 = OpAccessChain %_ptr_Output_v3float %oVert %20 %int_1
+               OpStore %38 %36
+;CHECK-NOT:        OpStore %38 %36
+         %43 = OpAccessChain %_ptr_Output_v3float %oVert %20 %int_2 %int_3
+               OpStore %43 %42
+;CHECK:            OpStore %43 %42
+         %48 = OpAccessChain %_ptr_Input_v3float %P %20
+         %49 = OpLoad %v3float %48
+         %50 = OpCompositeExtract %float %49 0
+         %51 = OpCompositeExtract %float %49 1
+         %52 = OpCompositeExtract %float %49 2
+         %53 = OpCompositeConstruct %v4float %50 %51 %52 %float_1
+         %62 = OpAccessChain %_ptr_Output_v4float %gl_out %20 %int_0
+               OpStore %62 %53
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_VULKAN_1_3);
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+  std::unordered_set<uint32_t> live_inputs;
+  std::unordered_set<uint32_t> live_builtins;
+  live_inputs.insert(1);
+  live_inputs.insert(8);
+  SinglePassRunAndMatch<EliminateDeadOutputStoresPass>(text, true, &live_inputs,
+                                                       &live_builtins);
+}
+
+TEST_F(ElimDeadOutputStoresTest, ScalarBuiltins) {
+  // Tests elimination of scalar builtins as seen in vert shaders.
+  //
+  // #version 460
+  //
+  // layout (location = 0) in vec3 P;
+  //
+  // void main()
+  // {
+  //         gl_Position = vec4(P, 1.0);
+  //         gl_PointSize = 1.0;
+  // }
+  const std::string text = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main" %_ %P
+               OpSource GLSL 460
+               OpName %main "main"
+               OpName %gl_PerVertex "gl_PerVertex"
+               OpMemberName %gl_PerVertex 0 "gl_Position"
+               OpMemberName %gl_PerVertex 1 "gl_PointSize"
+               OpMemberName %gl_PerVertex 2 "gl_ClipDistance"
+               OpMemberName %gl_PerVertex 3 "gl_CullDistance"
+               OpName %_ ""
+               OpName %P "P"
+               OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
+               OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize
+               OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance
+               OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance
+               OpDecorate %gl_PerVertex Block
+               OpDecorate %P Location 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+%_arr_float_uint_1 = OpTypeArray %float %uint_1
+%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1
+%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex
+          %_ = OpVariable %_ptr_Output_gl_PerVertex Output
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+    %v3float = OpTypeVector %float 3
+%_ptr_Input_v3float = OpTypePointer Input %v3float
+          %P = OpVariable %_ptr_Input_v3float Input
+    %float_1 = OpConstant %float 1
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+      %int_1 = OpConstant %int 1
+%_ptr_Output_float = OpTypePointer Output %float
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %19 = OpLoad %v3float %P
+         %21 = OpCompositeExtract %float %19 0
+         %22 = OpCompositeExtract %float %19 1
+         %23 = OpCompositeExtract %float %19 2
+         %24 = OpCompositeConstruct %v4float %21 %22 %23 %float_1
+         %26 = OpAccessChain %_ptr_Output_v4float %_ %int_0
+               OpStore %26 %24
+;CHECK:                   OpStore %26 %24
+         %29 = OpAccessChain %_ptr_Output_float %_ %int_1
+               OpStore %29 %float_1
+;CHECK-NOT:               OpStore %29 %float_1
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_VULKAN_1_3);
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+  std::unordered_set<uint32_t> live_inputs;
+  std::unordered_set<uint32_t> live_builtins;
+  // Omit spv::BuiltIn::PointSize
+  live_builtins.insert((uint32_t)spv::BuiltIn::ClipDistance);
+  live_builtins.insert((uint32_t)spv::BuiltIn::CullDistance);
+  SinglePassRunAndMatch<EliminateDeadOutputStoresPass>(text, true, &live_inputs,
+                                                       &live_builtins);
+}
+
+TEST_F(ElimDeadOutputStoresTest, ArrayedBuiltins) {
+  // Tests elimination of arrayed builtins as seen in geom, tesc, and tese
+  // shaders.
+  //
+  // #version 460
+  //
+  // layout(triangle_strip, max_vertices = 3) out;
+  // layout(triangles) in;
+  //
+  // void main()
+  // {
+  //         for (int i = 0; i < 3; i++)
+  //         {
+  //                 gl_Position = gl_in[i].gl_Position;
+  //                 gl_PointSize = gl_in[i].gl_PointSize;
+  //
+  //                 EmitVertex();
+  //         }
+  //
+  //         EndPrimitive();
+  // }
+  const std::string text = R"(
+               OpCapability Geometry
+               OpCapability GeometryPointSize
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Geometry %main "main" %_ %gl_in
+               OpExecutionMode %main Triangles
+               OpExecutionMode %main Invocations 1
+               OpExecutionMode %main OutputTriangleStrip
+               OpExecutionMode %main OutputVertices 3
+               OpSource GLSL 460
+               OpName %main "main"
+               OpName %i "i"
+               OpName %gl_PerVertex "gl_PerVertex"
+               OpMemberName %gl_PerVertex 0 "gl_Position"
+               OpMemberName %gl_PerVertex 1 "gl_PointSize"
+               OpMemberName %gl_PerVertex 2 "gl_ClipDistance"
+               OpMemberName %gl_PerVertex 3 "gl_CullDistance"
+               OpName %_ ""
+               OpName %gl_PerVertex_0 "gl_PerVertex"
+               OpMemberName %gl_PerVertex_0 0 "gl_Position"
+               OpMemberName %gl_PerVertex_0 1 "gl_PointSize"
+               OpMemberName %gl_PerVertex_0 2 "gl_ClipDistance"
+               OpMemberName %gl_PerVertex_0 3 "gl_CullDistance"
+               OpName %gl_in "gl_in"
+               OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
+               OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize
+               OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance
+               OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance
+               OpDecorate %gl_PerVertex Block
+               OpMemberDecorate %gl_PerVertex_0 0 BuiltIn Position
+               OpMemberDecorate %gl_PerVertex_0 1 BuiltIn PointSize
+               OpMemberDecorate %gl_PerVertex_0 2 BuiltIn ClipDistance
+               OpMemberDecorate %gl_PerVertex_0 3 BuiltIn CullDistance
+               OpDecorate %gl_PerVertex_0 Block
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+        %int = OpTypeInt 32 1
+%_ptr_Function_int = OpTypePointer Function %int
+      %int_0 = OpConstant %int 0
+      %int_3 = OpConstant %int 3
+       %bool = OpTypeBool
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+%_arr_float_uint_1 = OpTypeArray %float %uint_1
+%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1
+%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex
+          %_ = OpVariable %_ptr_Output_gl_PerVertex Output
+%gl_PerVertex_0 = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1
+     %uint_3 = OpConstant %uint 3
+%_arr_gl_PerVertex_0_uint_3 = OpTypeArray %gl_PerVertex_0 %uint_3
+%_ptr_Input__arr_gl_PerVertex_0_uint_3 = OpTypePointer Input %_arr_gl_PerVertex_0_uint_3
+      %gl_in = OpVariable %_ptr_Input__arr_gl_PerVertex_0_uint_3 Input
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+      %int_1 = OpConstant %int 1
+%_ptr_Input_float = OpTypePointer Input %float
+%_ptr_Output_float = OpTypePointer Output %float
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+          %i = OpVariable %_ptr_Function_int Function
+               OpStore %i %int_0
+               OpBranch %10
+         %10 = OpLabel
+               OpLoopMerge %12 %13 None
+               OpBranch %14
+         %14 = OpLabel
+         %15 = OpLoad %int %i
+         %18 = OpSLessThan %bool %15 %int_3
+               OpBranchConditional %18 %11 %12
+         %11 = OpLabel
+         %32 = OpLoad %int %i
+         %34 = OpAccessChain %_ptr_Input_v4float %gl_in %32 %int_0
+         %35 = OpLoad %v4float %34
+         %37 = OpAccessChain %_ptr_Output_v4float %_ %int_0
+               OpStore %37 %35
+;CHECK:                   OpStore %37 %35
+         %39 = OpLoad %int %i
+         %41 = OpAccessChain %_ptr_Input_float %gl_in %39 %int_1
+         %42 = OpLoad %float %41
+         %44 = OpAccessChain %_ptr_Output_float %_ %int_1
+               OpStore %44 %42
+;CHECK-NOT:               OpStore %44 %42
+               OpEmitVertex
+               OpBranch %13
+         %13 = OpLabel
+         %45 = OpLoad %int %i
+         %46 = OpIAdd %int %45 %int_1
+               OpStore %i %46
+               OpBranch %10
+         %12 = OpLabel
+               OpEndPrimitive
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_VULKAN_1_3);
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+  std::unordered_set<uint32_t> live_inputs;
+  std::unordered_set<uint32_t> live_builtins;
+  // Omit spv::BuiltIn::PointSize
+  live_builtins.insert((uint32_t)spv::BuiltIn::ClipDistance);
+  live_builtins.insert((uint32_t)spv::BuiltIn::CullDistance);
+  SinglePassRunAndMatch<EliminateDeadOutputStoresPass>(text, true, &live_inputs,
+                                                       &live_builtins);
+}
+
+TEST_F(ElimDeadOutputStoresTest, ArrayedOutputPatchLocs) {
+  // Tests elimination of location with arrayed patch output as seen in
+  // Tesc shaders.
+  //
+  // #version 450 core
+  //
+  // layout(vertices = 4) out;
+  //
+  // layout(location=0) patch out float patchOut0[2];
+  // layout(location=2) patch out float patchOut1[2];
+  //
+  // void main()
+  // {
+  //     patchOut0[1] = 0.0;  // Dead loc 1
+  //     patchOut1[1] = 1.0;  // Live loc 3
+  // }
+  const std::string text = R"(
+               OpCapability Tessellation
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint TessellationControl %main "main" %patchOut0 %patchOut1
+               OpExecutionMode %main OutputVertices 4
+               OpSource GLSL 450
+               OpName %main "main"
+               OpName %patchOut0 "patchOut0"
+               OpName %patchOut1 "patchOut1"
+               OpDecorate %patchOut0 Patch
+               OpDecorate %patchOut0 Location 0
+               OpDecorate %patchOut1 Patch
+               OpDecorate %patchOut1 Location 2
+       %void = OpTypeVoid
+          %3 = 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
+  %patchOut0 = OpVariable %_ptr_Output__arr_float_uint_2 Output
+        %int = OpTypeInt 32 1
+      %int_1 = OpConstant %int 1
+    %float_0 = OpConstant %float 0
+%_ptr_Output_float = OpTypePointer Output %float
+  %patchOut1 = OpVariable %_ptr_Output__arr_float_uint_2 Output
+    %float_1 = OpConstant %float 1
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %16 = OpAccessChain %_ptr_Output_float %patchOut0 %int_1
+               OpStore %16 %float_0
+;CHECK-NOT:               OpStore %16 %float_0
+         %19 = OpAccessChain %_ptr_Output_float %patchOut1 %int_1
+               OpStore %19 %float_1
+;CHECK:                   OpStore %19 %float_1
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_VULKAN_1_3);
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+  std::unordered_set<uint32_t> live_inputs;
+  std::unordered_set<uint32_t> live_builtins;
+  live_inputs.insert(3);
+  SinglePassRunAndMatch<EliminateDeadOutputStoresPass>(text, true, &live_inputs,
+                                                       &live_builtins);
+}
+
+TEST_F(ElimDeadOutputStoresTest, VertMultipleLocationsF16) {
+  // #version 450
+  //
+  // layout(location = 2) out Vertex
+  // {
+  //         f16vec4 color0;
+  //         f16vec4 color1;
+  //         f16vec4 color2[3];
+  // } oVert;
+  //
+  // void main()
+  // {
+  //     oVert.color0 = f16vec4(0.0,0.0,0.0,0.0);
+  //     oVert.color1 = f16vec4(0.1,0.0,0.0,0.0);
+  //     oVert.color2[0] = f16vec4(0.2,0.0,0.0,0.0);
+  //     oVert.color2[1] = f16vec4(0.3,0.0,0.0,0.0);
+  //     oVert.color2[2] = f16vec4(0.4,0.0,0.0,0.0);
+  // }
+  const std::string text = R"(
+               OpCapability Shader
+               OpCapability Float16
+               OpCapability StorageInputOutput16
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main" %oVert
+               OpSource GLSL 450
+               OpName %main "main"
+               OpName %Vertex "Vertex"
+               OpMemberName %Vertex 0 "color0"
+               OpMemberName %Vertex 1 "color1"
+               OpMemberName %Vertex 2 "color2"
+               OpName %oVert "oVert"
+               OpDecorate %Vertex Block
+               OpDecorate %oVert Location 2
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %half = OpTypeFloat 32
+    %v4half = OpTypeVector %half 4
+       %uint = OpTypeInt 32 0
+     %uint_3 = OpConstant %uint 3
+%_arr_v4half_uint_3 = OpTypeArray %v4half %uint_3
+     %Vertex = OpTypeStruct %v4half %v4half %_arr_v4half_uint_3
+%_ptr_Output_Vertex = OpTypePointer Output %Vertex
+      %oVert = OpVariable %_ptr_Output_Vertex Output
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+    %half_0 = OpConstant %half 0
+         %17 = OpConstantComposite %v4half %half_0 %half_0 %half_0 %half_0
+%_ptr_Output_v4half = OpTypePointer Output %v4half
+      %int_1 = OpConstant %int 1
+%half_0_100000001 = OpConstant %half 0.100000001
+         %22 = OpConstantComposite %v4half %half_0_100000001 %half_0 %half_0 %half_0
+      %int_2 = OpConstant %int 2
+%half_0_200000003 = OpConstant %half 0.200000003
+         %26 = OpConstantComposite %v4half %half_0_200000003 %half_0 %half_0 %half_0
+%half_0_300000012 = OpConstant %half 0.300000012
+         %29 = OpConstantComposite %v4half %half_0_300000012 %half_0 %half_0 %half_0
+%half_0_400000006 = OpConstant %half 0.400000006
+         %32 = OpConstantComposite %v4half %half_0_400000006 %half_0 %half_0 %half_0
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %19 = OpAccessChain %_ptr_Output_v4half %oVert %int_0
+               OpStore %19 %17
+;CHECK:            OpStore %19 %17
+         %23 = OpAccessChain %_ptr_Output_v4half %oVert %int_1
+               OpStore %23 %22
+;CHECK-NOT:        OpStore %23 %22
+         %27 = OpAccessChain %_ptr_Output_v4half %oVert %int_2 %int_0
+               OpStore %27 %26
+;CHECK-NOT:        OpStore %27 %26
+         %30 = OpAccessChain %_ptr_Output_v4half %oVert %int_2 %int_1
+               OpStore %30 %29
+;CHECK:            OpStore %30 %29
+         %33 = OpAccessChain %_ptr_Output_v4half %oVert %int_2 %int_2
+               OpStore %33 %32
+;CHECK-NOT:        OpStore %33 %32
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_VULKAN_1_3);
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+  std::unordered_set<uint32_t> live_inputs;
+  std::unordered_set<uint32_t> live_builtins;
+  live_inputs.insert(2);
+  live_inputs.insert(5);
+  SinglePassRunAndMatch<EliminateDeadOutputStoresPass>(text, true, &live_inputs,
+                                                       &live_builtins);
+}
+
+}  // namespace
+}  // namespace opt
+}  // namespace spvtools
diff --git a/test/opt/feature_manager_test.cpp b/test/opt/feature_manager_test.cpp
index 767376c..7e8f92c 100644
--- a/test/opt/feature_manager_test.cpp
+++ b/test/opt/feature_manager_test.cpp
@@ -14,9 +14,7 @@
 
 #include <algorithm>
 #include <memory>
-#include <string>
 
-#include "gmock/gmock.h"
 #include "gtest/gtest.h"
 #include "source/opt/build_module.h"
 #include "source/opt/ir_context.h"
@@ -89,6 +87,25 @@
       Extension::kSPV_KHR_storage_buffer_storage_class));
 }
 
+TEST_F(FeatureManagerTest, GetExtensionsReturnsExtensions) {
+  const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpExtension "SPV_KHR_variable_pointers"
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+  )";
+
+  std::unique_ptr<IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text);
+  ASSERT_NE(context, nullptr);
+
+  const auto& extensions = context->get_feature_mgr()->GetExtensions();
+  EXPECT_EQ(extensions.size(), 2);
+  EXPECT_TRUE(extensions.contains(Extension::kSPV_KHR_variable_pointers));
+  EXPECT_TRUE(
+      extensions.contains(Extension::kSPV_KHR_storage_buffer_storage_class));
+}
+
 // Test capability checks.
 TEST_F(FeatureManagerTest, ExplicitlyPresent1) {
   const std::string text = R"(
@@ -100,8 +117,10 @@
       BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text);
   ASSERT_NE(context, nullptr);
 
-  EXPECT_TRUE(context->get_feature_mgr()->HasCapability(SpvCapabilityShader));
-  EXPECT_FALSE(context->get_feature_mgr()->HasCapability(SpvCapabilityKernel));
+  EXPECT_TRUE(
+      context->get_feature_mgr()->HasCapability(spv::Capability::Shader));
+  EXPECT_FALSE(
+      context->get_feature_mgr()->HasCapability(spv::Capability::Kernel));
 }
 
 TEST_F(FeatureManagerTest, ExplicitlyPresent2) {
@@ -114,8 +133,10 @@
       BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text);
   ASSERT_NE(context, nullptr);
 
-  EXPECT_FALSE(context->get_feature_mgr()->HasCapability(SpvCapabilityShader));
-  EXPECT_TRUE(context->get_feature_mgr()->HasCapability(SpvCapabilityKernel));
+  EXPECT_FALSE(
+      context->get_feature_mgr()->HasCapability(spv::Capability::Shader));
+  EXPECT_TRUE(
+      context->get_feature_mgr()->HasCapability(spv::Capability::Kernel));
 }
 
 TEST_F(FeatureManagerTest, ImplicitlyPresent) {
@@ -131,10 +152,31 @@
   // Check multiple levels of indirection.  Tessellation implies Shader, which
   // implies Matrix.
   EXPECT_TRUE(
-      context->get_feature_mgr()->HasCapability(SpvCapabilityTessellation));
-  EXPECT_TRUE(context->get_feature_mgr()->HasCapability(SpvCapabilityShader));
-  EXPECT_TRUE(context->get_feature_mgr()->HasCapability(SpvCapabilityMatrix));
-  EXPECT_FALSE(context->get_feature_mgr()->HasCapability(SpvCapabilityKernel));
+      context->get_feature_mgr()->HasCapability(spv::Capability::Tessellation));
+  EXPECT_TRUE(
+      context->get_feature_mgr()->HasCapability(spv::Capability::Shader));
+  EXPECT_TRUE(
+      context->get_feature_mgr()->HasCapability(spv::Capability::Matrix));
+  EXPECT_FALSE(
+      context->get_feature_mgr()->HasCapability(spv::Capability::Kernel));
+}
+
+TEST_F(FeatureManagerTest, GetCapabilitiesReturnsImplicitCapabilities) {
+  const std::string text = R"(
+OpCapability Tessellation
+OpMemoryModel Logical GLSL450
+  )";
+
+  std::unique_ptr<IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text);
+  ASSERT_NE(context, nullptr);
+
+  const auto& capabilities = context->get_feature_mgr()->GetCapabilities();
+  // Tesselation implies Shader, which implies Matrix.
+  EXPECT_EQ(capabilities.size(), 3);
+  EXPECT_TRUE(capabilities.contains(spv::Capability::Tessellation));
+  EXPECT_TRUE(capabilities.contains(spv::Capability::Shader));
+  EXPECT_TRUE(capabilities.contains(spv::Capability::Matrix));
 }
 
 }  // namespace
diff --git a/test/opt/fix_func_call_arguments_test.cpp b/test/opt/fix_func_call_arguments_test.cpp
index ecd13a8..606ed26 100644
--- a/test/opt/fix_func_call_arguments_test.cpp
+++ b/test/opt/fix_func_call_arguments_test.cpp
@@ -12,7 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "gmock/gmock.h"
 #include "test/opt/pass_fixture.h"
 #include "test/opt/pass_utils.h"
 
diff --git a/test/opt/fix_storage_class_test.cpp b/test/opt/fix_storage_class_test.cpp
index 1c0101a..93ce873 100644
--- a/test/opt/fix_storage_class_test.cpp
+++ b/test/opt/fix_storage_class_test.cpp
@@ -14,8 +14,6 @@
 
 #include <string>
 
-#include "gmock/gmock.h"
-#include "test/opt/assembly_builder.h"
 #include "test/opt/pass_fixture.h"
 #include "test/opt/pass_utils.h"
 
diff --git a/test/opt/flatten_decoration_test.cpp b/test/opt/flatten_decoration_test.cpp
index 63207fd..d7ac2ab 100644
--- a/test/opt/flatten_decoration_test.cpp
+++ b/test/opt/flatten_decoration_test.cpp
@@ -16,7 +16,6 @@
 #include <string>
 #include <vector>
 
-#include "gmock/gmock.h"
 #include "test/opt/pass_fixture.h"
 #include "test/opt/pass_utils.h"
 
diff --git a/test/opt/fold_spec_const_op_composite_test.cpp b/test/opt/fold_spec_const_op_composite_test.cpp
index c98a44c..f83e86e 100644
--- a/test/opt/fold_spec_const_op_composite_test.cpp
+++ b/test/opt/fold_spec_const_op_composite_test.cpp
@@ -308,6 +308,372 @@
       builder.GetCode(), JoinAllInsts(expected), /* skip_nop = */ true);
 }
 
+TEST_F(FoldSpecConstantOpAndCompositePassBasicTest, CompositeInsertVector) {
+  const std::string test =
+      R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "main"
+               OpExecutionMode %1 LocalSize 1 1 1
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+     %v3uint = OpTypeVector %uint 3
+     %uint_2 = OpConstant %uint 2
+     %uint_3 = OpConstant %uint 3
+          %8 = OpConstantNull %uint
+          %9 = OpSpecConstantComposite %v3uint %uint_2 %uint_2 %uint_2
+ ; CHECK: %15 = OpConstantComposite %v3uint %uint_3 %uint_2 %uint_2
+ ; CHECK: %uint_3_0 = OpConstant %uint 3
+ ; CHECK: %17 = OpConstantComposite %v3uint %8 %uint_2 %uint_2
+ ; CHECK: %18 = OpConstantNull %uint
+         %10 = OpSpecConstantOp %v3uint CompositeInsert %uint_3 %9 0
+         %11 = OpSpecConstantOp %uint CompositeExtract %10 0
+         %12 = OpSpecConstantOp %v3uint CompositeInsert %8 %9 0
+         %13 = OpSpecConstantOp %uint CompositeExtract %12 0
+          %1 = OpFunction %void None %3
+         %14 = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<FoldSpecConstantOpAndCompositePass>(test, false);
+}
+
+TEST_F(FoldSpecConstantOpAndCompositePassBasicTest,
+       CompositeInsertVectorIntoMatrix) {
+  const std::string test =
+      R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "main"
+               OpExecutionMode %1 LocalSize 1 1 1
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v2float = OpTypeVector %float 2
+ %mat2v2float = OpTypeMatrix %v2float 2
+    %float_0 = OpConstant %float 0
+    %float_1 = OpConstant %float 1
+    %float_2 = OpConstant %float 2
+ %v2float_01 = OpConstantComposite %v2float %float_0 %float_1
+ %v2float_12 = OpConstantComposite %v2float %float_1 %float_2
+
+; CHECK: %10 = OpConstantComposite %v2float %float_0 %float_1
+; CHECK: %11 = OpConstantComposite %v2float %float_1 %float_2
+; CHECK: %12 = OpConstantComposite %mat2v2float %11 %11
+%mat2v2float_1212 = OpConstantComposite %mat2v2float %v2float_12 %v2float_12
+
+; CHECK: %15 = OpConstantComposite %mat2v2float %10 %11
+     %spec_0 = OpSpecConstantOp %mat2v2float CompositeInsert %v2float_01 %mat2v2float_1212 0
+          %1 = OpFunction %void None %3
+      %label = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<FoldSpecConstantOpAndCompositePass>(test, false);
+}
+
+TEST_F(FoldSpecConstantOpAndCompositePassBasicTest, CompositeInsertMatrix) {
+  const std::string test =
+      R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "main"
+               OpExecutionMode %1 LocalSize 1 1 1
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v3float = OpTypeVector %float 3
+%mat3v3float = OpTypeMatrix %v3float 3
+    %float_1 = OpConstant %float 1
+    %float_2 = OpConstant %float 2
+          %9 = OpSpecConstantComposite %v3float %float_1 %float_1 %float_1
+         %10 = OpSpecConstantComposite %v3float %float_1 %float_1 %float_1
+         %11 = OpSpecConstantComposite %v3float %float_1 %float_2 %float_1
+         %12 = OpSpecConstantComposite %mat3v3float %9 %10 %11
+ ; CHECK: %float_2_0 = OpConstant %float 2
+ ; CHECK: %18 = OpConstantComposite %v3float %float_1 %float_1 %float_2
+ ; CHECK: %19 = OpConstantComposite %mat3v3float %9 %18 %11
+ ; CHECK: %float_2_1 = OpConstant %float 2
+         %13 = OpSpecConstantOp %float CompositeExtract %12 2 1
+         %14 = OpSpecConstantOp %mat3v3float CompositeInsert %13 %12 1 2
+         %15 = OpSpecConstantOp %float CompositeExtract %14 1 2
+          %1 = OpFunction %void None %3
+         %16 = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<FoldSpecConstantOpAndCompositePass>(test, false);
+}
+
+TEST_F(FoldSpecConstantOpAndCompositePassBasicTest, CompositeInsertFloatNull) {
+  const std::string test =
+      R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "main"
+               OpExecutionMode %1 LocalSize 1 1 1
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v3float = OpTypeVector %float 3
+    %float_1 = OpConstant %float 1
+
+; CHECK: %7 = OpConstantNull %float
+; CHECK: %8 = OpConstantComposite %v3float %7 %7 %7
+; CHECK: %12 = OpConstantComposite %v3float %7 %7 %float_1
+       %null = OpConstantNull %float
+     %spec_0 = OpConstantComposite %v3float %null %null %null
+     %spec_1 = OpSpecConstantOp %v3float CompositeInsert %float_1 %spec_0 2
+
+; CHECK: %float_1_0 = OpConstant %float 1
+     %spec_2 = OpSpecConstantOp %float CompositeExtract %spec_1 2
+          %1 = OpFunction %void None %3
+      %label = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<FoldSpecConstantOpAndCompositePass>(test, false);
+}
+
+TEST_F(FoldSpecConstantOpAndCompositePassBasicTest,
+       CompositeInsertFloatSetNull) {
+  const std::string test =
+      R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "main"
+               OpExecutionMode %1 LocalSize 1 1 1
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v3float = OpTypeVector %float 3
+    %float_1 = OpConstant %float 1
+
+; CHECK: %7 = OpConstantNull %float
+; CHECK: %8 = OpConstantComposite %v3float %7 %7 %float_1
+; CHECK: %12 = OpConstantComposite %v3float %7 %7 %7
+       %null = OpConstantNull %float
+     %spec_0 = OpConstantComposite %v3float %null %null %float_1
+     %spec_1 = OpSpecConstantOp %v3float CompositeInsert %null %spec_0 2
+
+; CHECK: %13 = OpConstantNull %float
+     %spec_2 = OpSpecConstantOp %float CompositeExtract %spec_1 2
+          %1 = OpFunction %void None %3
+      %label = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<FoldSpecConstantOpAndCompositePass>(test, false);
+}
+
+TEST_F(FoldSpecConstantOpAndCompositePassBasicTest, CompositeInsertVectorNull) {
+  const std::string test =
+      R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "main"
+               OpExecutionMode %1 LocalSize 1 1 1
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v3float = OpTypeVector %float 3
+    %float_1 = OpConstant %float 1
+       %null = OpConstantNull %v3float
+
+; CHECK: %11 = OpConstantNull %float
+; CHECK: %12 = OpConstantComposite %v3float %11 %11 %float_1
+     %spec_0 = OpSpecConstantOp %v3float CompositeInsert %float_1 %null 2
+
+
+; CHECK: %float_1_0 = OpConstant %float 1
+     %spec_1 = OpSpecConstantOp %float CompositeExtract %spec_0 2
+          %1 = OpFunction %void None %3
+      %label = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<FoldSpecConstantOpAndCompositePass>(test, false);
+}
+
+TEST_F(FoldSpecConstantOpAndCompositePassBasicTest,
+       CompositeInsertNullVectorIntoMatrix) {
+  const std::string test =
+      R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "main"
+               OpExecutionMode %1 LocalSize 1 1 1
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v2float = OpTypeVector %float 2
+ %mat2v2float = OpTypeMatrix %v2float 2
+       %null = OpConstantNull %mat2v2float
+    %float_1 = OpConstant %float 1
+    %float_2 = OpConstant %float 2
+ %v2float_12 = OpConstantComposite %v2float %float_1 %float_2
+
+; CHECK: %13 = OpConstantNull %v2float
+; CHECK: %14 = OpConstantComposite %mat2v2float %10 %13
+     %spec_0 = OpSpecConstantOp %mat2v2float CompositeInsert %v2float_12 %null 0
+          %1 = OpFunction %void None %3
+      %label = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<FoldSpecConstantOpAndCompositePass>(test, false);
+}
+
+TEST_F(FoldSpecConstantOpAndCompositePassBasicTest,
+       CompositeInsertVectorKeepNull) {
+  const std::string test =
+      R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "main"
+               OpExecutionMode %1 LocalSize 1 1 1
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v3float = OpTypeVector %float 3
+    %float_0 = OpConstant %float 0
+ %null_float = OpConstantNull %float
+   %null_vec = OpConstantNull %v3float
+
+; CHECK: %15 = OpConstantComposite %v3float %7 %7 %float_0
+     %spec_0 = OpSpecConstantOp %v3float CompositeInsert %float_0 %null_vec 2
+
+; CHECK: %float_0_0 = OpConstant %float 0
+     %spec_1 = OpSpecConstantOp %float CompositeExtract %spec_0 2
+
+; CHECK: %17 = OpConstantComposite %v3float %7 %7 %7
+     %spec_2 = OpSpecConstantOp %v3float CompositeInsert %null_float %null_vec 2
+
+; CHECK: %18 = OpConstantNull %float
+     %spec_3 = OpSpecConstantOp %float CompositeExtract %spec_2 2
+          %1 = OpFunction %void None %3
+      %label = OpLabel
+        %add = OpFAdd %float %spec_3 %spec_3
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<FoldSpecConstantOpAndCompositePass>(test, false);
+}
+
+TEST_F(FoldSpecConstantOpAndCompositePassBasicTest,
+       CompositeInsertVectorChainNull) {
+  const std::string test =
+      R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "main"
+               OpExecutionMode %1 LocalSize 1 1 1
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v3float = OpTypeVector %float 3
+    %float_1 = OpConstant %float 1
+       %null = OpConstantNull %v3float
+
+; CHECK: %15 = OpConstantNull %float
+; CHECK: %16 = OpConstantComposite %v3float %15 %15 %float_1
+; CHECK: %17 = OpConstantComposite %v3float %15 %float_1 %float_1
+; CHECK: %18 = OpConstantComposite %v3float %float_1 %float_1 %float_1
+     %spec_0 = OpSpecConstantOp %v3float CompositeInsert %float_1 %null 2
+     %spec_1 = OpSpecConstantOp %v3float CompositeInsert %float_1 %spec_0 1
+     %spec_2 = OpSpecConstantOp %v3float CompositeInsert %float_1 %spec_1 0
+
+; CHECK: %float_1_0 = OpConstant %float 1
+; CHECK: %float_1_1 = OpConstant %float 1
+; CHECK: %float_1_2 = OpConstant %float 1
+     %spec_3 = OpSpecConstantOp %float CompositeExtract %spec_2 0
+     %spec_4 = OpSpecConstantOp %float CompositeExtract %spec_2 1
+     %spec_5 = OpSpecConstantOp %float CompositeExtract %spec_2 2
+          %1 = OpFunction %void None %3
+      %label = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<FoldSpecConstantOpAndCompositePass>(test, false);
+}
+
+TEST_F(FoldSpecConstantOpAndCompositePassBasicTest,
+       CompositeInsertVectorChainReset) {
+  const std::string test =
+      R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "main"
+               OpExecutionMode %1 LocalSize 1 1 1
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v3float = OpTypeVector %float 3
+    %float_1 = OpConstant %float 1
+       %null = OpConstantNull %float
+; CHECK: %8 = OpConstantComposite %v3float %7 %7 %float_1
+     %spec_0 = OpConstantComposite %v3float %null %null %float_1
+
+            ; set to null
+; CHECK: %13 = OpConstantComposite %v3float %7 %7 %7
+     %spec_1 = OpSpecConstantOp %v3float CompositeInsert %null %spec_0 2
+
+            ; set to back to original value
+; CHECK: %14 = OpConstantComposite %v3float %7 %7 %float_1
+     %spec_2 = OpSpecConstantOp %v3float CompositeInsert %float_1 %spec_1 2
+
+; CHECK: %float_1_0 = OpConstant %float 1
+     %spec_3 = OpSpecConstantOp %float CompositeExtract %spec_2 2
+          %1 = OpFunction %void None %3
+      %label = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<FoldSpecConstantOpAndCompositePass>(test, false);
+}
+
+TEST_F(FoldSpecConstantOpAndCompositePassBasicTest, CompositeInsertMatrixNull) {
+  const std::string test =
+      R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+       %void = OpTypeVoid
+       %func = OpTypeFunction %void
+      %float = OpTypeFloat 32
+        %int = OpTypeInt 32 0
+%v2float = OpTypeVector %float 2
+%mat2v2float = OpTypeMatrix %v2float 2
+%null = OpConstantNull %mat2v2float
+    %float_1 = OpConstant %float 1
+ ; CHECK: %13 = OpConstantNull %v2float
+ ; CHECK: %14 = OpConstantNull %float
+ ; CHECK: %15 = OpConstantComposite %v2float %float_1 %14
+ ; CHECK: %16 = OpConstantComposite %mat2v2float %13 %15
+       %spec = OpSpecConstantOp %mat2v2float CompositeInsert %float_1 %null 1 0
+; extra type def to make sure new type def are not just thrown at end
+      %v2int = OpTypeVector %int 2
+       %main = OpFunction %void None %func
+      %label = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<FoldSpecConstantOpAndCompositePass>(test, false);
+}
+
 // All types and some common constants that are potentially required in
 // FoldSpecConstantOpAndCompositeTest.
 std::vector<std::string> CommonTypesAndConstants() {
diff --git a/test/opt/fold_test.cpp b/test/opt/fold_test.cpp
index b324803..c5adf6d 100644
--- a/test/opt/fold_test.cpp
+++ b/test/opt/fold_test.cpp
@@ -27,7 +27,6 @@
 #include "source/opt/ir_context.h"
 #include "source/opt/module.h"
 #include "spirv-tools/libspirv.hpp"
-#include "test/opt/pass_utils.h"
 
 namespace spvtools {
 namespace opt {
@@ -88,9 +87,9 @@
   // Make sure the instruction folded as expected.
   EXPECT_TRUE(succeeded);
   if (inst != nullptr) {
-    EXPECT_EQ(inst->opcode(), SpvOpCopyObject);
+    EXPECT_EQ(inst->opcode(), spv::Op::OpCopyObject);
     inst = def_use_mgr->GetDef(inst->GetSingleWordInOperand(0));
-    EXPECT_EQ(inst->opcode(), SpvOpConstant);
+    EXPECT_EQ(inst->opcode(), spv::Op::OpConstant);
     analysis::ConstantManager* const_mrg = context->get_constant_mgr();
     const analysis::Constant* constant = const_mrg->GetConstantFromInst(inst);
     // We expect to see either integer types or 16-bit float types here.
@@ -157,6 +156,8 @@
 %v2half = OpTypeVector %half 2
 %v2bool = OpTypeVector %bool 2
 %m2x2int = OpTypeMatrix %v2int 2
+%mat4v2float = OpTypeMatrix %v2float 4
+%mat2v4float = OpTypeMatrix %v4float 2
 %mat4v4float = OpTypeMatrix %v4float 4
 %mat4v4double = OpTypeMatrix %v4double 4
 %struct_v2int_int_int = OpTypeStruct %v2int %int %int
@@ -224,6 +225,7 @@
 %v2int_2_2 = OpConstantComposite %v2int %int_2 %int_2
 %v2int_2_3 = OpConstantComposite %v2int %int_2 %int_3
 %v2int_3_2 = OpConstantComposite %v2int %int_3 %int_2
+%v2int_n1_n24 = OpConstantComposite %v2int %int_n1 %int_n24
 %v2int_4_4 = OpConstantComposite %v2int %int_4 %int_4
 %v2int_min_max = OpConstantComposite %v2int %int_min %int_max
 %v2bool_null = OpConstantNull %v2bool
@@ -283,6 +285,7 @@
 %v2double_null = OpConstantNull %v2double
 %108 = OpConstant %half 0
 %half_1 = OpConstant %half 1
+%half_2 = OpConstant %half 2
 %half_0_1 = OpConstantComposite %v2half %108 %half_1
 %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
@@ -291,8 +294,10 @@
 %v4float_1_1_1_1 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1
 %v4float_1_2_3_4 = OpConstantComposite %v4float %float_1 %float_2 %float_3 %float_4
 %v4float_null = OpConstantNull %v4float
-%mat4v4float_null = OpConstantComposite %mat4v4float %v4float_null %v4float_null %v4float_null %v4float_null
+%mat2v4float_null = OpConstantNull %mat2v4float
+%mat4v4float_null = OpConstantNull %mat4v4float
 %mat4v4float_1_2_3_4 = OpConstantComposite %mat4v4float %v4float_1_2_3_4 %v4float_1_2_3_4 %v4float_1_2_3_4 %v4float_1_2_3_4
+%mat4v4float_1_2_3_4_null = OpConstantComposite %mat4v4float %v4float_1_2_3_4 %v4float_null %v4float_1_2_3_4 %v4float_null
 %107 = OpConstantComposite %v4double %double_0 %double_0 %double_0 %double_0
 %v4double_0_0_0_0 = OpConstantComposite %v4double %double_0 %double_0 %double_0 %double_0
 %v4double_0_0_0_1 = OpConstantComposite %v4double %double_0 %double_0 %double_0 %double_1
@@ -301,8 +306,9 @@
 %v4double_1_2_3_4 = OpConstantComposite %v4double %double_1 %double_2 %double_3 %double_4
 %v4double_1_1_1_0p5 = OpConstantComposite %v4double %double_1 %double_1 %double_1 %double_0p5
 %v4double_null = OpConstantNull %v4double
-%mat4v4double_null = OpConstantComposite %mat4v4double %v4double_null %v4double_null %v4double_null %v4double_null
+%mat4v4double_null = OpConstantNull %mat4v4double
 %mat4v4double_1_2_3_4 = OpConstantComposite %mat4v4double %v4double_1_2_3_4 %v4double_1_2_3_4 %v4double_1_2_3_4 %v4double_1_2_3_4
+%mat4v4double_1_2_3_4_null = OpConstantComposite %mat4v4double %v4double_1_2_3_4 %v4double_null %v4double_1_2_3_4 %v4double_null
 %v4float_n1_2_1_3 = OpConstantComposite %v4float %float_n1 %float_2 %float_1 %float_3
 %uint_0x3f800000 = OpConstant %uint 0x3f800000
 %uint_0xbf800000 = OpConstant %uint 0xbf800000
@@ -313,6 +319,8 @@
 %int_0xC05FD666 = OpConstant %int 0xC05FD666
 %int_0x66666666 = OpConstant %int 0x66666666
 %v4int_0x3FF00000_0x00000000_0xC05FD666_0x66666666 = OpConstantComposite %v4int %int_0x00000000 %int_0x3FF00000 %int_0x66666666 %int_0xC05FD666
+%ushort_0x4400 = OpConstant %ushort 0x4400
+%short_0x4400 = OpConstant %short 0x4400
 %ushort_0xBC00 = OpConstant %ushort 0xBC00
 %short_0xBC00 = OpConstant %short 0xBC00
 )";
@@ -748,7 +756,7 @@
           "OpReturn\n" +
           "OpFunctionEnd",
       2, 1),
-  // Test case 44: UClamp 1 2 x
+  // Test case 46: UClamp 1 2 x
   InstructionFoldingCase<uint32_t>(
       Header() + "%main = OpFunction %void None %void_func\n" +
           "%main_lab = OpLabel\n" +
@@ -757,7 +765,7 @@
           "OpReturn\n" +
           "OpFunctionEnd",
       2, 2),
-  // Test case 45: UClamp 2 x 1
+  // Test case 47: UClamp 2 x 1
   InstructionFoldingCase<uint32_t>(
       Header() + "%main = OpFunction %void None %void_func\n" +
           "%main_lab = OpLabel\n" +
@@ -766,7 +774,7 @@
           "OpReturn\n" +
           "OpFunctionEnd",
       2, 1),
-    // Test case 46: Bit-cast int 0 to unsigned int
+    // Test case 48: Bit-cast int 0 to unsigned int
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -774,7 +782,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, 0),
-    // Test case 47: Bit-cast int -24 to unsigned int
+    // Test case 49: Bit-cast int -24 to unsigned int
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -782,7 +790,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, static_cast<uint32_t>(-24)),
-    // Test case 48: Bit-cast float 1.0f to unsigned int
+    // Test case 50: Bit-cast float 1.0f to unsigned int
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -790,7 +798,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, static_cast<uint32_t>(0x3f800000)),
-    // Test case 49: Bit-cast ushort 0xBC00 to ushort
+    // Test case 51: Bit-cast ushort 0xBC00 to ushort
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -798,7 +806,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, 0xBC00),
-    // Test case 50: Bit-cast short 0xBC00 to ushort
+    // Test case 52: Bit-cast short 0xBC00 to ushort
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -806,7 +814,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, 0xFFFFBC00),
-    // Test case 51: Bit-cast half 1 to ushort
+    // Test case 53: Bit-cast half 1 to ushort
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -814,7 +822,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, 0x3C00),
-    // Test case 52: Bit-cast ushort 0xBC00 to short
+    // Test case 54: Bit-cast ushort 0xBC00 to short
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -822,7 +830,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, 0xBC00),
-    // Test case 53: Bit-cast short 0xBC00 to short
+    // Test case 55: Bit-cast short 0xBC00 to short
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -830,7 +838,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, 0xFFFFBC00),
-    // Test case 54: Bit-cast half 1 to short
+    // Test case 56: Bit-cast half 1 to short
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -838,7 +846,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, 0x3C00),
-    // Test case 55: Bit-cast ushort 0xBC00 to half
+    // Test case 57: Bit-cast ushort 0xBC00 to half
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -846,7 +854,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, 0xBC00),
-    // Test case 56: Bit-cast short 0xBC00 to half
+    // Test case 58: Bit-cast short 0xBC00 to half
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -854,7 +862,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, 0xFFFFBC00),
-    // Test case 57: Bit-cast half 1 to half
+    // Test case 59: Bit-cast half 1 to half
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -862,7 +870,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, 0x3C00),
-    // Test case 58: Bit-cast ubyte 1 to byte
+    // Test case 60: Bit-cast ubyte 1 to byte
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -870,21 +878,61 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, 1),
-    // Test case 59: Bit-cast byte -1 to ubyte
+    // Test case 61: Bit-cast byte -1 to ubyte
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
             "%2 = OpBitcast %ubyte %byte_n1\n" +
             "OpReturn\n" +
             "OpFunctionEnd",
-        2, 0xFFFFFFFF)
+        2, 0xFFFFFFFF),
+    // Test case 62: Negate 2.
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpSNegate %int %int_2\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, -2),
+    // Test case 63: Negate negative short.
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpSNegate %short %short_0xBC00\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 0x4400 /* expected to be sign extended. */),
+    // Test case 64: Negate positive short.
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpSNegate %short %short_0x4400\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 0xFFFFBC00 /* expected to be sign extended. */),
+    // Test case 65: Negate a negative short.
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpSNegate %ushort %ushort_0xBC00\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 0x4400 /* expected to be zero extended. */),
+    // Test case 66: Negate positive short.
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpSNegate %ushort %ushort_0x4400\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 0xBC00 /* expected to be zero extended. */)
 ));
 // clang-format on
 
-using IntVectorInstructionFoldingTest =
+using UIntVectorInstructionFoldingTest =
     ::testing::TestWithParam<InstructionFoldingCase<std::vector<uint32_t>>>;
 
-TEST_P(IntVectorInstructionFoldingTest, Case) {
+TEST_P(UIntVectorInstructionFoldingTest, Case) {
   const auto& tc = GetParam();
 
   // Build module.
@@ -896,15 +944,15 @@
   // Fold the instruction to test.
   analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
   Instruction* inst = def_use_mgr->GetDef(tc.id_to_fold);
-  SpvOp original_opcode = inst->opcode();
+  spv::Op original_opcode = inst->opcode();
   bool succeeded = context->get_instruction_folder().FoldInstruction(inst);
 
   // Make sure the instruction folded as expected.
   EXPECT_EQ(succeeded, inst == nullptr || inst->opcode() != original_opcode);
   if (succeeded && inst != nullptr) {
-    EXPECT_EQ(inst->opcode(), SpvOpCopyObject);
+    EXPECT_EQ(inst->opcode(), spv::Op::OpCopyObject);
     inst = def_use_mgr->GetDef(inst->GetSingleWordInOperand(0));
-    std::vector<SpvOp> opcodes = {SpvOpConstantComposite};
+    std::vector<spv::Op> opcodes = {spv::Op::OpConstantComposite};
     EXPECT_THAT(opcodes, Contains(inst->opcode()));
     analysis::ConstantManager* const_mrg = context->get_constant_mgr();
     const analysis::Constant* result = const_mrg->GetConstantFromInst(inst);
@@ -921,7 +969,7 @@
 }
 
 // clang-format off
-INSTANTIATE_TEST_SUITE_P(TestCase, IntVectorInstructionFoldingTest,
+INSTANTIATE_TEST_SUITE_P(TestCase, UIntVectorInstructionFoldingTest,
 ::testing::Values(
     // Test case 0: fold 0*n
     InstructionFoldingCase<std::vector<uint32_t>>(
@@ -969,7 +1017,101 @@
           "%2 = OpBitcast %v2uint %v2int_min_max\n" +
           "OpReturn\n" +
           "OpFunctionEnd",
-      2, {2147483648, 2147483647})
+      2, {2147483648, 2147483647}),
+    // Test case 5: fold SNegate vector of uint
+    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 = 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 (incuding integer overflow)
+    InstructionFoldingCase<std::vector<uint32_t>>(
+      Header() + "%main = OpFunction %void None %void_func\n" +
+          "%main_lab = OpLabel\n" +
+          "%2 = OpIAdd %v2uint %v2uint_0x3f800000_0xbf800000 %v2uint_0x3f800000_0xbf800000\n" +
+          "OpReturn\n" +
+          "OpFunctionEnd",
+      2, {0x7f000000u, 0x7f000000u})
+));
+// clang-format on
+
+using IntVectorInstructionFoldingTest =
+    ::testing::TestWithParam<InstructionFoldingCase<std::vector<int32_t>>>;
+
+TEST_P(IntVectorInstructionFoldingTest, Case) {
+  const auto& tc = GetParam();
+
+  // Build module.
+  std::unique_ptr<IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, tc.test_body,
+                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  ASSERT_NE(nullptr, context);
+
+  // Fold the instruction to test.
+  analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
+  Instruction* inst = def_use_mgr->GetDef(tc.id_to_fold);
+  bool succeeded = context->get_instruction_folder().FoldInstruction(inst);
+
+  // Make sure the instruction folded as expected.
+  EXPECT_TRUE(succeeded);
+  if (succeeded && inst != nullptr) {
+    EXPECT_EQ(inst->opcode(), spv::Op::OpCopyObject);
+    inst = def_use_mgr->GetDef(inst->GetSingleWordInOperand(0));
+    std::vector<spv::Op> opcodes = {spv::Op::OpConstantComposite};
+    EXPECT_THAT(opcodes, Contains(inst->opcode()));
+    analysis::ConstantManager* const_mrg = context->get_constant_mgr();
+    const analysis::Constant* result = const_mrg->GetConstantFromInst(inst);
+    EXPECT_NE(result, nullptr);
+    if (result != nullptr) {
+      const std::vector<const analysis::Constant*>& componenets =
+          result->AsVectorConstant()->GetComponents();
+      EXPECT_EQ(componenets.size(), tc.expected_result.size());
+      for (size_t i = 0; i < componenets.size(); i++) {
+        EXPECT_EQ(tc.expected_result[i], componenets[i]->GetS32());
+      }
+    }
+  }
+}
+
+// clang-format off
+INSTANTIATE_TEST_SUITE_P(TestCase, IntVectorInstructionFoldingTest,
+::testing::Values(
+    // Test case 0: fold negate of a vector
+    InstructionFoldingCase<std::vector<int32_t>>(
+      Header() + "%main = OpFunction %void None %void_func\n" +
+          "%main_lab = OpLabel\n" +
+          "%2 = OpSNegate %v2int %v2int_2_3\n" +
+          "OpReturn\n" +
+          "OpFunctionEnd",
+      2, {-2, -3}),
+    // Test case 1: fold negate of a vector containing negative values.
+    InstructionFoldingCase<std::vector<int32_t>>(
+      Header() + "%main = OpFunction %void None %void_func\n" +
+          "%main_lab = OpLabel\n" +
+          "%2 = OpSNegate %v2int %v2int_n1_n24\n" +
+          "OpReturn\n" +
+          "OpFunctionEnd",
+      2, {1, 24}),
+    // Test case 2: fold negate of a vector at the limits
+    InstructionFoldingCase<std::vector<int32_t>>(
+      Header() + "%main = OpFunction %void None %void_func\n" +
+          "%main_lab = OpLabel\n" +
+          "%2 = OpSNegate %v2int %v2int_min_max\n" +
+          "OpReturn\n" +
+          "OpFunctionEnd",
+      2, {INT_MIN, -INT_MAX}),
+    // Test case 3: fold vector components of int
+    InstructionFoldingCase<std::vector<int32_t>>(
+      Header() + "%main = OpFunction %void None %void_func\n" +
+          "%main_lab = OpLabel\n" +
+          "%2 = OpIMul %v2int %v2int_2_3 %v2int_2_3\n" +
+          "OpReturn\n" +
+          "OpFunctionEnd",
+      2, {4,9})
 ));
 // clang-format on
 
@@ -993,9 +1135,9 @@
   // Make sure the instruction folded as expected.
   EXPECT_TRUE(succeeded);
   if (succeeded && inst != nullptr) {
-    EXPECT_EQ(inst->opcode(), SpvOpCopyObject);
+    EXPECT_EQ(inst->opcode(), spv::Op::OpCopyObject);
     inst = def_use_mgr->GetDef(inst->GetSingleWordInOperand(0));
-    std::vector<SpvOp> opcodes = {SpvOpConstantComposite};
+    std::vector<spv::Op> opcodes = {spv::Op::OpConstantComposite};
     EXPECT_THAT(opcodes, Contains(inst->opcode()));
     analysis::ConstantManager* const_mrg = context->get_constant_mgr();
     const analysis::Constant* result = const_mrg->GetConstantFromInst(inst);
@@ -1050,7 +1192,16 @@
        "OpReturn\n" +
        "OpFunctionEnd",
        2, {30.0,30.0,30.0,30.0}),
-   // Test case 4: OpMatrixTimesVector Zero Non-Zero {1.0, 2.0, 3.0, 4.0} {{0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}} {0.0, 0.0, 0.0, 0.0}
+   // Test case 4: OpVectorTimesMatrix Non-Zero Non-Zero {{1.0, 2.0, 3.0, 4.0}, Null, {1.0, 2.0, 3.0, 4.0}, Null} {1.0, 2.0, 3.0, 4.0} {30.0, 0.0, 30.0, 0.0}
+   InstructionFoldingCase<std::vector<double>>(
+       Header() +
+       "%main = OpFunction %void None %void_func\n" +
+       "%main_lab = OpLabel\n" +
+       "%2 = OpVectorTimesMatrix %v4double %v4double_1_2_3_4 %mat4v4double_1_2_3_4_null\n" +
+       "OpReturn\n" +
+       "OpFunctionEnd",
+       2, {30.0,0.0,30.0,0.0}),
+   // Test case 5: OpMatrixTimesVector Zero Non-Zero {1.0, 2.0, 3.0, 4.0} {{0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}} {0.0, 0.0, 0.0, 0.0}
    InstructionFoldingCase<std::vector<double>>(
        Header() +
        "%main = OpFunction %void None %void_func\n" +
@@ -1059,7 +1210,7 @@
        "OpReturn\n" +
        "OpFunctionEnd",
        2, {0.0,0.0,0.0,0.0}),
-   // Test case 5: OpMatrixTimesVector Non-Zero Zero {{1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}} {0.0, 0.0, 0.0, 0.0} {0.0, 0.0, 0.0, 0.0}
+   // Test case 6: OpMatrixTimesVector Non-Zero Zero {{1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}} {0.0, 0.0, 0.0, 0.0} {0.0, 0.0, 0.0, 0.0}
    InstructionFoldingCase<std::vector<double>>(
        Header() +
        "%main = OpFunction %void None %void_func\n" +
@@ -1068,7 +1219,7 @@
        "OpReturn\n" +
        "OpFunctionEnd",
        2, {0.0,0.0,0.0,0.0}),
-   // Test case 6: OpMatrixTimesVector Non-Zero Non-Zero {1.0, 2.0, 3.0, 4.0} {{1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}} {10.0, 20.0, 30.0, 40.0}
+   // Test case 7: OpMatrixTimesVector Non-Zero Non-Zero {1.0, 2.0, 3.0, 4.0} {{1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}} {10.0, 20.0, 30.0, 40.0}
    InstructionFoldingCase<std::vector<double>>(
        Header() +
        "%main = OpFunction %void None %void_func\n" +
@@ -1076,7 +1227,16 @@
        "%2 = OpMatrixTimesVector %v4double %mat4v4double_1_2_3_4 %v4double_1_2_3_4\n" +
        "OpReturn\n" +
        "OpFunctionEnd",
-       2, {10.0,20.0,30.0,40.0})
+       2, {10.0,20.0,30.0,40.0}),
+   // Test case 8: OpMatrixTimesVector Non-Zero Non-Zero {1.0, 2.0, 3.0, 4.0} {{1.0, 2.0, 3.0, 4.0}, Null, {1.0, 2.0, 3.0, 4.0}, Null} {10.0, 20.0, 30.0, 40.0}
+   InstructionFoldingCase<std::vector<double>>(
+       Header() +
+       "%main = OpFunction %void None %void_func\n" +
+       "%main_lab = OpLabel\n" +
+       "%2 = OpMatrixTimesVector %v4double %mat4v4double_1_2_3_4_null %v4double_1_2_3_4\n" +
+       "OpReturn\n" +
+       "OpFunctionEnd",
+       2, {4.0,8.0,12.0,16.0})
 ));
 
 using FloatVectorInstructionFoldingTest =
@@ -1094,15 +1254,15 @@
   // Fold the instruction to test.
   analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
   Instruction* inst = def_use_mgr->GetDef(tc.id_to_fold);
-  SpvOp original_opcode = inst->opcode();
+  spv::Op original_opcode = inst->opcode();
   bool succeeded = context->get_instruction_folder().FoldInstruction(inst);
 
   // Make sure the instruction folded as expected.
   EXPECT_EQ(succeeded, inst == nullptr || inst->opcode() != original_opcode);
   if (succeeded && inst != nullptr) {
-    EXPECT_EQ(inst->opcode(), SpvOpCopyObject);
+    EXPECT_EQ(inst->opcode(), spv::Op::OpCopyObject);
     inst = def_use_mgr->GetDef(inst->GetSingleWordInOperand(0));
-    std::vector<SpvOp> opcodes = {SpvOpConstantComposite};
+    std::vector<spv::Op> opcodes = {spv::Op::OpConstantComposite};
     EXPECT_THAT(opcodes, Contains(inst->opcode()));
     analysis::ConstantManager* const_mrg = context->get_constant_mgr();
     const analysis::Constant* result = const_mrg->GetConstantFromInst(inst);
@@ -1155,7 +1315,16 @@
        "OpReturn\n" +
        "OpFunctionEnd",
        2, {0.0f,0.0f,0.0f,0.0f}),
-   // Test case 4: OpVectorTimesMatrix Zero Non-Zero {{1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}} {0.0, 0.0, 0.0, 0.0} {0.0, 0.0, 0.0, 0.0}
+   // Test case 4: OpVectorTimesMatrix Non-Zero Non-Zero {{1.0, 2.0, 3.0, 4.0}, Null, {1.0, 2.0, 3.0, 4.0}, Null} {1.0, 2.0, 3.0, 4.0} {30.0, 0.0, 30.0, 0.0}
+   InstructionFoldingCase<std::vector<float>>(
+       Header() +
+       "%main = OpFunction %void None %void_func\n" +
+       "%main_lab = OpLabel\n" +
+       "%2 = OpVectorTimesMatrix %v4float %v4float_1_2_3_4 %mat4v4float_1_2_3_4_null\n" +
+       "OpReturn\n" +
+       "OpFunctionEnd",
+       2, {30.0,0.0,30.0,0.0}),
+   // Test case 5: OpVectorTimesMatrix Zero Non-Zero {{1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}} {0.0, 0.0, 0.0, 0.0} {0.0, 0.0, 0.0, 0.0}
    InstructionFoldingCase<std::vector<float>>(
        Header() +
        "%main = OpFunction %void None %void_func\n" +
@@ -1164,7 +1333,7 @@
        "OpReturn\n" +
        "OpFunctionEnd",
        2, {0.0f,0.0f,0.0f,0.0f}),
-   // Test case 5: OpVectorTimesMatrix Non-Zero Non-Zero {{1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}} {1.0, 2.0, 3.0, 4.0} {30.0, 30.0, 30.0, 30.0}
+   // Test case 6: OpVectorTimesMatrix Non-Zero Non-Zero {{1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}} {1.0, 2.0, 3.0, 4.0} {30.0, 30.0, 30.0, 30.0}
    InstructionFoldingCase<std::vector<float>>(
        Header() +
        "%main = OpFunction %void None %void_func\n" +
@@ -1173,7 +1342,7 @@
        "OpReturn\n" +
        "OpFunctionEnd",
        2, {30.0f,30.0f,30.0f,30.0f}),
-   // Test case 6: OpMatrixTimesVector Zero Non-Zero {1.0, 2.0, 3.0, 4.0} {{0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}} {0.0, 0.0, 0.0, 0.0}
+   // Test case 7: OpMatrixTimesVector Zero Non-Zero {1.0, 2.0, 3.0, 4.0} {{0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}} {0.0, 0.0, 0.0, 0.0}
    InstructionFoldingCase<std::vector<float>>(
        Header() +
        "%main = OpFunction %void None %void_func\n" +
@@ -1182,7 +1351,7 @@
        "OpReturn\n" +
        "OpFunctionEnd",
        2, {0.0f,0.0f,0.0f,0.0f}),
-   // Test case 7: OpMatrixTimesVector Non-Zero Zero {{1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}} {0.0, 0.0, 0.0, 0.0} {0.0, 0.0, 0.0, 0.0}
+   // Test case 8: OpMatrixTimesVector Non-Zero Zero {{1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}} {0.0, 0.0, 0.0, 0.0} {0.0, 0.0, 0.0, 0.0}
    InstructionFoldingCase<std::vector<float>>(
        Header() +
        "%main = OpFunction %void None %void_func\n" +
@@ -1191,7 +1360,7 @@
        "OpReturn\n" +
        "OpFunctionEnd",
        2, {0.0f,0.0f,0.0f,0.0f}),
-   // Test case 8: OpMatrixTimesVector Non-Zero Non-Zero {1.0, 2.0, 3.0, 4.0} {{1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}} {10.0, 20.0, 30.0, 40.0}
+   // Test case 9: OpMatrixTimesVector Non-Zero Non-Zero {1.0, 2.0, 3.0, 4.0} {{1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}} {10.0, 20.0, 30.0, 40.0}
    InstructionFoldingCase<std::vector<float>>(
        Header() +
        "%main = OpFunction %void None %void_func\n" +
@@ -1199,9 +1368,96 @@
        "%2 = OpMatrixTimesVector %v4float %mat4v4float_1_2_3_4 %v4float_1_2_3_4\n" +
        "OpReturn\n" +
        "OpFunctionEnd",
-       2, {10.0f,20.0f,30.0f,40.0f})
+       2, {10.0f,20.0f,30.0f,40.0f}),
+   // Test case 10: OpMatrixTimesVector Non-Zero Non-Zero {1.0, 2.0, 3.0, 4.0} {{1.0, 2.0, 3.0, 4.0}, Null, {1.0, 2.0, 3.0, 4.0}, Null} {10.0, 20.0, 30.0, 40.0}
+   InstructionFoldingCase<std::vector<float>>(
+       Header() +
+       "%main = OpFunction %void None %void_func\n" +
+       "%main_lab = OpLabel\n" +
+       "%2 = OpMatrixTimesVector %v4float %mat4v4float_1_2_3_4_null %v4float_1_2_3_4\n" +
+       "OpReturn\n" +
+       "OpFunctionEnd",
+       2, {4.0,8.0,12.0,16.0})
 ));
 // clang-format on
+
+using FloatMatrixInstructionFoldingTest = ::testing::TestWithParam<
+    InstructionFoldingCase<std::vector<std::vector<float>>>>;
+
+TEST_P(FloatMatrixInstructionFoldingTest, Case) {
+  const auto& tc = GetParam();
+
+  // Build module.
+  std::unique_ptr<IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, tc.test_body,
+                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  ASSERT_NE(nullptr, context);
+
+  // Fold the instruction to test.
+  analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
+  Instruction* inst = def_use_mgr->GetDef(tc.id_to_fold);
+  bool succeeded = context->get_instruction_folder().FoldInstruction(inst);
+
+  // Make sure the instruction folded as expected.
+  EXPECT_TRUE(succeeded);
+  EXPECT_EQ(inst->opcode(), spv::Op::OpCopyObject);
+
+  if (inst->opcode() == spv::Op::OpCopyObject) {
+    inst = def_use_mgr->GetDef(inst->GetSingleWordInOperand(0));
+    analysis::ConstantManager* const_mgr = context->get_constant_mgr();
+    const analysis::Constant* result = const_mgr->GetConstantFromInst(inst);
+    EXPECT_NE(result, nullptr);
+    if (result != nullptr) {
+      std::vector<const analysis::Constant*> matrix =
+          result->AsMatrixConstant()->GetComponents();
+      EXPECT_EQ(matrix.size(), tc.expected_result.size());
+      for (size_t c = 0; c < matrix.size(); c++) {
+        if (matrix[c]->AsNullConstant() != nullptr) {
+          matrix[c] = const_mgr->GetNullCompositeConstant(matrix[c]->type());
+        }
+        const analysis::VectorConstant* column_const =
+            matrix[c]->AsVectorConstant();
+        ASSERT_NE(column_const, nullptr);
+        const std::vector<const analysis::Constant*>& column =
+            column_const->GetComponents();
+        EXPECT_EQ(column.size(), tc.expected_result[c].size());
+        for (size_t r = 0; r < column.size(); r++) {
+          EXPECT_EQ(tc.expected_result[c][r], column[r]->GetFloat());
+        }
+      }
+    }
+  }
+}
+
+// clang-format off
+INSTANTIATE_TEST_SUITE_P(TestCase, FloatMatrixInstructionFoldingTest,
+::testing::Values(
+   // Test case 0: OpTranspose square null matrix
+   InstructionFoldingCase<std::vector<std::vector<float>>>(
+       Header() + "%main = OpFunction %void None %void_func\n" +
+           "%main_lab = OpLabel\n" +
+           "%2 = OpTranspose %mat4v4float %mat4v4float_null\n" +
+           "OpReturn\n" +
+           "OpFunctionEnd",
+       2, {{0.0f, 0.0f, 0.0f, 0.0f},{0.0f, 0.0f, 0.0f, 0.0f},{0.0f, 0.0f, 0.0f, 0.0f},{0.0f, 0.0f, 0.0f, 0.0f}}),
+   // Test case 1: OpTranspose rectangular null matrix
+   InstructionFoldingCase<std::vector<std::vector<float>>>(
+       Header() + "%main = OpFunction %void None %void_func\n" +
+           "%main_lab = OpLabel\n" +
+           "%2 = OpTranspose %mat4v2float %mat2v4float_null\n" +
+           "OpReturn\n" +
+           "OpFunctionEnd",
+       2, {{0.0f, 0.0f},{0.0f, 0.0f},{0.0f, 0.0f},{0.0f, 0.0f}}),
+   InstructionFoldingCase<std::vector<std::vector<float>>>(
+       Header() + "%main = OpFunction %void None %void_func\n" +
+           "%main_lab = OpLabel\n" +
+           "%2 = OpTranspose %mat4v4float %mat4v4float_1_2_3_4\n" +
+           "OpReturn\n" +
+           "OpFunctionEnd",
+       2, {{1.0f, 1.0f, 1.0f, 1.0f},{2.0f, 2.0f, 2.0f, 2.0f},{3.0f, 3.0f, 3.0f, 3.0f},{4.0f, 4.0f, 4.0f, 4.0f}})
+));
+// clang-format on
+
 using BooleanInstructionFoldingTest =
     ::testing::TestWithParam<InstructionFoldingCase<bool>>;
 
@@ -1222,9 +1478,10 @@
   // Make sure the instruction folded as expected.
   EXPECT_TRUE(succeeded);
   if (inst != nullptr) {
-    EXPECT_EQ(inst->opcode(), SpvOpCopyObject);
+    EXPECT_EQ(inst->opcode(), spv::Op::OpCopyObject);
     inst = def_use_mgr->GetDef(inst->GetSingleWordInOperand(0));
-    std::vector<SpvOp> bool_opcodes = {SpvOpConstantTrue, SpvOpConstantFalse};
+    std::vector<spv::Op> bool_opcodes = {spv::Op::OpConstantTrue,
+                                         spv::Op::OpConstantFalse};
     EXPECT_THAT(bool_opcodes, Contains(inst->opcode()));
     analysis::ConstantManager* const_mrg = context->get_constant_mgr();
     const analysis::BoolConstant* result =
@@ -1833,9 +2090,9 @@
   // Make sure the instruction folded as expected.
   EXPECT_TRUE(succeeded);
   if (inst != nullptr) {
-    EXPECT_EQ(inst->opcode(), SpvOpCopyObject);
+    EXPECT_EQ(inst->opcode(), spv::Op::OpCopyObject);
     inst = def_use_mgr->GetDef(inst->GetSingleWordInOperand(0));
-    EXPECT_EQ(inst->opcode(), SpvOpConstant);
+    EXPECT_EQ(inst->opcode(), spv::Op::OpConstant);
     analysis::ConstantManager* const_mrg = context->get_constant_mgr();
     const analysis::FloatConstant* result =
         const_mrg->GetConstantFromInst(inst)->AsFloatConstant();
@@ -2266,9 +2523,9 @@
   // Make sure the instruction folded as expected.
   EXPECT_TRUE(succeeded);
   if (inst != nullptr) {
-    EXPECT_EQ(inst->opcode(), SpvOpCopyObject);
+    EXPECT_EQ(inst->opcode(), spv::Op::OpCopyObject);
     inst = def_use_mgr->GetDef(inst->GetSingleWordInOperand(0));
-    EXPECT_EQ(inst->opcode(), SpvOpConstant);
+    EXPECT_EQ(inst->opcode(), spv::Op::OpConstant);
     analysis::ConstantManager* const_mrg = context->get_constant_mgr();
     const analysis::FloatConstant* result =
         const_mrg->GetConstantFromInst(inst)->AsFloatConstant();
@@ -3153,7 +3410,7 @@
   // Make sure the instruction folded as expected.
   EXPECT_NE(inst, nullptr);
   if (inst != nullptr) {
-    EXPECT_EQ(inst->opcode(), SpvOpConstant);
+    EXPECT_EQ(inst->opcode(), spv::Op::OpConstant);
     analysis::ConstantManager* const_mrg = context->get_constant_mgr();
     const analysis::IntConstant* result =
         const_mrg->GetConstantFromInst(inst)->AsIntConstant();
@@ -3201,7 +3458,8 @@
   // Make sure the instruction folded as expected.
   EXPECT_NE(inst, nullptr);
   if (inst != nullptr) {
-    std::vector<SpvOp> bool_opcodes = {SpvOpConstantTrue, SpvOpConstantFalse};
+    std::vector<spv::Op> bool_opcodes = {spv::Op::OpConstantTrue,
+                                         spv::Op::OpConstantFalse};
     EXPECT_THAT(bool_opcodes, Contains(inst->opcode()));
     analysis::ConstantManager* const_mrg = context->get_constant_mgr();
     const analysis::BoolConstant* result =
@@ -3253,7 +3511,7 @@
   EXPECT_EQ(inst->type_id(), original_inst->type_id());
   EXPECT_TRUE((!succeeded) == (tc.expected_result == 0));
   if (succeeded) {
-    EXPECT_EQ(inst->opcode(), SpvOpCopyObject);
+    EXPECT_EQ(inst->opcode(), spv::Op::OpCopyObject);
     EXPECT_EQ(inst->GetSingleWordInOperand(0), tc.expected_result);
   } else {
     EXPECT_EQ(inst->NumInOperands(), original_inst->NumInOperands());
@@ -4260,6 +4518,17 @@
             "%2 = OpDot %half %half_0_1 %half_0_1\n" +
             "OpReturn\n" +
             "OpFunctionEnd",
+        2, 0),
+    // Test case 23: 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" +
+            "%main_lab = OpLabel\n" +
+            "%n = OpVariable %_ptr_half Function\n" +
+            "%3 = OpLoad %half %n\n" +
+            "%2 = OpFDiv %half %half_1 %half_2\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
         2, 0)
 ));
 
@@ -4911,7 +5180,7 @@
   EXPECT_EQ(inst->type_id(), original_inst->type_id());
   EXPECT_TRUE((!succeeded) == (tc.expected_result == 0));
   if (succeeded) {
-    EXPECT_EQ(inst->opcode(), SpvOpFNegate);
+    EXPECT_EQ(inst->opcode(), spv::Op::OpFNegate);
     EXPECT_EQ(inst->GetSingleWordInOperand(0), tc.expected_result);
   } else {
     EXPECT_EQ(inst->NumInOperands(), original_inst->NumInOperands());
@@ -7361,7 +7630,25 @@
             "%5 = OpCompositeConstruct %v2int %3 %4\n" +
             "OpReturn\n" +
             "OpFunctionEnd",
-        5, true)
+        5, true),
+    // Test case 16: Don't fold when type cannot be deduced to a constant.
+    InstructionFoldingCase<bool>(
+        Header() +
+            "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%4 = OpCompositeInsert %struct_v2int_int_int %int_1 %struct_v2int_int_int_null 2\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        4, false),
+    // Test case 17: Don't fold when index into composite is out of bounds.
+    InstructionFoldingCase<bool>(
+	Header() +
+            "%main = OpFunction %void None %void_func\n" +
+	    "%main_lab = OpLabel\n" +
+	    "%4 = OpCompositeExtract %int %struct_v2int_int_int 3\n" +
+	    "OpReturn\n" +
+	    "OpFunctionEnd",
+	4, false)
 ));
 
 INSTANTIATE_TEST_SUITE_P(DotProductMatchingTest, MatchingInstructionFoldingTest,
@@ -8391,6 +8678,7 @@
       %v3int = OpTypeVector %int 3
     %Texture = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
    %gSampler = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
+        %110 = OpConstantComposite %v2int %5 %5
         %101 = OpConstantComposite %v2int %int_n1 %int_n1
          %20 = OpConstantComposite %v2float %float_0 %float_0
        %main = OpFunction %void None %22
@@ -8450,7 +8738,12 @@
     InstructionFoldingCase<bool>(ImageOperandsTestBody(
       "         OpImageWrite %88 %5 %101 Offset %101      \n"
       "; CHECK: OpImageWrite %88 %5 %101 ConstOffset %101 \n")
-      , 0 /* No result-id */, true)
+      , 0 /* No result-id */, true),
+    // Test case 8: OpImageFetch with zero constant Offset
+    InstructionFoldingCase<bool>(ImageOperandsTestBody(
+        "         %89 = OpImageFetch %10 %88 %101 Lod|Offset %5 %110      \n"
+        "; CHECK: %89 = OpImageFetch %10 %88 %101 Lod %5 \n")
+        , 89, true)
 ));
 
 }  // namespace
diff --git a/test/opt/freeze_spec_const_test.cpp b/test/opt/freeze_spec_const_test.cpp
index ad0fc32..1ccaa3e 100644
--- a/test/opt/freeze_spec_const_test.cpp
+++ b/test/opt/freeze_spec_const_test.cpp
@@ -12,7 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <algorithm>
 #include <string>
 #include <tuple>
 #include <utility>
diff --git a/test/opt/function_test.cpp b/test/opt/function_test.cpp
index 34a0387..6a40e93 100644
--- a/test/opt/function_test.cpp
+++ b/test/opt/function_test.cpp
@@ -13,8 +13,6 @@
 // limitations under the License.
 
 #include <memory>
-#include <sstream>
-#include <string>
 #include <vector>
 
 #include "function_utils.h"
@@ -248,7 +246,7 @@
   std::unordered_set<uint32_t> non_semantic_ids;
   func->ForEachInst(
       [&non_semantic_ids](const Instruction* inst) {
-        if (inst->opcode() == SpvOpExtInst) {
+        if (inst->opcode() == spv::Op::OpExtInst) {
           non_semantic_ids.insert(inst->result_id());
         }
       },
@@ -285,7 +283,7 @@
   std::unordered_set<uint32_t> non_semantic_ids;
   func->ForEachInst(
       [&non_semantic_ids](const Instruction* inst) {
-        if (inst->opcode() == SpvOpExtInst) {
+        if (inst->opcode() == spv::Op::OpExtInst) {
           non_semantic_ids.insert(inst->result_id());
         }
       },
diff --git a/test/opt/graphics_robust_access_test.cpp b/test/opt/graphics_robust_access_test.cpp
index 057b909..a1a3b7d 100644
--- a/test/opt/graphics_robust_access_test.cpp
+++ b/test/opt/graphics_robust_access_test.cpp
@@ -12,12 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <array>
 #include <sstream>
 #include <string>
 #include <vector>
 
-#include "gmock/gmock.h"
 #include "pass_fixture.h"
 #include "pass_utils.h"
 #include "source/opt/graphics_robust_access_pass.h"
diff --git a/test/opt/if_conversion_test.cpp b/test/opt/if_conversion_test.cpp
index dc7f831..c1425e8 100644
--- a/test/opt/if_conversion_test.cpp
+++ b/test/opt/if_conversion_test.cpp
@@ -14,8 +14,6 @@
 
 #include <string>
 
-#include "gmock/gmock.h"
-#include "test/opt/assembly_builder.h"
 #include "test/opt/pass_fixture.h"
 #include "test/opt/pass_utils.h"
 
diff --git a/test/opt/inline_test.cpp b/test/opt/inline_test.cpp
index f3ff81c..1e5d9f3 100644
--- a/test/opt/inline_test.cpp
+++ b/test/opt/inline_test.cpp
@@ -4437,7 +4437,7 @@
 //    Callee function returns a value generated outside the callee,
 //      e.g. a constant value. This might exercise some logic not yet
 //      exercised by the current tests: the false branch in the "if"
-//      inside the SpvOpReturnValue case in InlinePass::GenInlineCode?
+//      inside the spv::Op::OpReturnValue case in InlinePass::GenInlineCode?
 //    SampledImage before function call, but callee is only single block.
 //      Then the SampledImage instruction is not cloned. Documents existing
 //      behaviour.
diff --git a/test/opt/inst_bindless_check_test.cpp b/test/opt/inst_bindless_check_test.cpp
index 90f40bc..0deec5c 100644
--- a/test/opt/inst_bindless_check_test.cpp
+++ b/test/opt/inst_bindless_check_test.cpp
@@ -18,7 +18,6 @@
 #include <string>
 #include <vector>
 
-#include "test/opt/assembly_builder.h"
 #include "test/opt/pass_fixture.h"
 #include "test/opt/pass_utils.h"
 
@@ -28,456 +27,18 @@
 
 using InstBindlessTest = PassTest<::testing::Test>;
 
-static const std::string kOutputDecorations = R"(
-; CHECK: OpDecorate [[output_buffer_type:%inst_bindless_OutputBuffer]] Block
-; CHECK: OpMemberDecorate [[output_buffer_type]] 0 Offset 0
-; CHECK: OpMemberDecorate [[output_buffer_type]] 1 Offset 4
-; CHECK: OpDecorate [[output_buffer_var:%\w+]] DescriptorSet 7
-; CHECK: OpDecorate [[output_buffer_var]] Binding 0
+static const std::string kFuncName = "inst_bindless_check_desc";
+
+static const std::string kImportDeco = R"(
+;CHECK: OpDecorate %)" + kFuncName + R"( LinkageAttributes ")" +
+                                       kFuncName + R"(" Import
 )";
 
-static const std::string kOutputGlobals = R"(
-; CHECK: [[output_buffer_type]] = OpTypeStruct %uint %_runtimearr_uint
-; CHECK: [[output_ptr_type:%\w+]] = OpTypePointer StorageBuffer [[output_buffer_type]]
-; CHECK: [[output_buffer_var]] = OpVariable [[output_ptr_type]] StorageBuffer
+static const std::string kImportStub = R"(
+;CHECK: %)" + kFuncName + R"( = OpFunction %bool None {{%\w+}}
+;CHECK: OpFunctionEnd
 )";
 
-static const std::string kStreamWrite4Begin = R"(
-; CHECK: %inst_bindless_stream_write_4 = OpFunction %void None {{%\w+}}
-; CHECK: [[param_1:%\w+]] = OpFunctionParameter %uint
-; CHECK: [[param_2:%\w+]] = OpFunctionParameter %uint
-; CHECK: [[param_3:%\w+]] = OpFunctionParameter %uint
-; CHECK: [[param_4:%\w+]] = OpFunctionParameter %uint
-; CHECK: {{%\w+}} = OpLabel
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_0
-; CHECK: {{%\w+}} = OpAtomicIAdd %uint {{%\w+}} %uint_4 %uint_0 %uint_10
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_10
-; CHECK: {{%\w+}} = OpArrayLength %uint [[output_buffer_var]] 1
-; CHECK: {{%\w+}} = OpULessThanEqual %bool {{%\w+}} {{%\w+}}
-; CHECK: OpSelectionMerge {{%\w+}} None
-; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
-; CHECK: {{%\w+}} = OpLabel
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_0
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
-; CHECK: OpStore {{%\w+}} %uint_10
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_1
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
-; CHECK: OpStore {{%\w+}} %uint_23
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_2
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
-; CHECK: OpStore {{%\w+}} [[param_1]]
-)";
-
-static const std::string kStreamWrite4End = R"(
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_7
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
-; CHECK: OpStore {{%\w+}} [[param_2]]
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_8
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
-; CHECK: OpStore {{%\w+}} [[param_3]]
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_9
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
-; CHECK: OpStore {{%\w+}} [[param_4]]
-; CHECK: OpBranch {{%\w+}}
-; CHECK: {{%\w+}} = OpLabel
-; CHECK: OpReturn
-; CHECK: OpFunctionEnd
-)";
-
-// clang-format off
-static const std::string kStreamWrite4Frag = kStreamWrite4Begin + R"(
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_3
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
-; CHECK: OpStore {{%\w+}} %uint_4
-; CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord
-; CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}}
-; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_4
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
-; CHECK: OpStore {{%\w+}} {{%\w+}}
-; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_5
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
-; CHECK: OpStore {{%\w+}} {{%\w+}}
-)" + kStreamWrite4End;
-
-static const std::string kStreamWrite4Tese = kStreamWrite4Begin + R"(
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_3
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
-; CHECK: OpStore {{%\w+}} %uint_2
-; CHECK: {{%\w+}} = OpLoad %uint %gl_PrimitiveID
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_4
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
-; CHECK: OpStore {{%\w+}} {{%\w+}}
-; CHECK: {{%\w+}} = OpLoad %v3float %gl_TessCoord
-; CHECK: {{%\w+}} = OpBitcast %v3uint {{%\w+}}
-; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
-; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_5
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
-; CHECK: OpStore {{%\w+}} {{%\w+}}
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_6
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
-; CHECK: OpStore {{%\w+}} {{%\w+}}
-)" + kStreamWrite4End;
-
-static const std::string kStreamWrite4Vert = kStreamWrite4Begin + R"(
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_3
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
-; CHECK: OpStore {{%\w+}} %uint_0
-; CHECK: {{%\w+}} = OpLoad %uint %gl_VertexIndex
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_4
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
-; CHECK: OpStore {{%\w+}} {{%\w+}}
-; CHECK: {{%\w+}} = OpLoad %uint %gl_InstanceIndex
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_5
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
-; CHECK: OpStore {{%\w+}} {{%\w+}}
-)" + kStreamWrite4End;
-
-static const std::string kStreamWrite4Compute = kStreamWrite4Begin + R"(
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_3
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
-; CHECK: OpStore {{%\w+}} %uint_5
-; CHECK: {{%\w+}} = OpLoad %v3uint %gl_GlobalInvocationID
-; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
-; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
-; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_4
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
-; CHECK: OpStore {{%\w+}} {{%\w+}}
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_5
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
-; CHECK: OpStore {{%\w+}} {{%\w+}}
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_6
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
-; CHECK: OpStore {{%\w+}} {{%\w+}}
-)" + kStreamWrite4End;
-
-static const std::string kStreamWrite4Ray = kStreamWrite4Begin + R"(
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_3
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
-; CHECK: OpStore {{%\w+}} {{%\w+}}
-; CHECK: {{%\w+}} = OpLoad %v3uint {{%\w+}}
-; CHECK: {{%\w+}} = OpCompositeExtract %uint %90 0
-; CHECK: {{%\w+}} = OpCompositeExtract %uint %90 1
-; CHECK: {{%\w+}} = OpCompositeExtract %uint %90 2
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_4
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
-; CHECK: OpStore {{%\w+}} {{%\w+}}
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_5
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
-; CHECK: OpStore {{%\w+}} {{%\w+}}
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_6
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
-; CHECK: OpStore {{%\w+}} {{%\w+}}
-)" + kStreamWrite4End;
-// clang-format on
-
-static const std::string kStreamWrite5Begin = R"(
-; CHECK: %inst_bindless_stream_write_5 = OpFunction %void None {{%\w+}}
-; CHECK: [[param_1:%\w+]] = OpFunctionParameter %uint
-; CHECK: [[param_2:%\w+]] = OpFunctionParameter %uint
-; CHECK: [[param_3:%\w+]] = OpFunctionParameter %uint
-; CHECK: [[param_4:%\w+]] = OpFunctionParameter %uint
-; CHECK: [[param_5:%\w+]] = OpFunctionParameter %uint
-; CHECK: {{%\w+}} = OpLabel
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_0
-; CHECK: {{%\w+}} = OpAtomicIAdd %uint {{%\w+}} %uint_4 %uint_0 %uint_11
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_11
-; CHECK: {{%\w+}} = OpArrayLength %uint [[output_buffer_var]] 1
-; CHECK: {{%\w+}} = OpULessThanEqual %bool {{%\w+}} {{%\w+}}
-; CHECK: OpSelectionMerge {{%\w+}} None
-; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
-; CHECK: {{%\w+}} = OpLabel
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_0
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
-; CHECK: OpStore {{%\w+}} %uint_11
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_1
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
-; CHECK: OpStore {{%\w+}} %uint_23
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_2
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
-; CHECK: OpStore {{%\w+}} [[param_1]]
-)";
-
-static const std::string kStreamWrite5End = R"(
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_7
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
-; CHECK: OpStore {{%\w+}} [[param_2]]
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_8
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
-; CHECK: OpStore {{%\w+}} [[param_3]]
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_9
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
-; CHECK: OpStore {{%\w+}} [[param_4]]
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_10
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
-; CHECK: OpStore {{%\w+}} [[param_5]]
-; CHECK: OpBranch {{%\w+}}
-; CHECK: {{%\w+}} = OpLabel
-; CHECK: OpReturn
-; CHECK: OpFunctionEnd
-)";
-
-// clang-format off
-static const std::string kStreamWrite5Frag = kStreamWrite5Begin + R"(
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_3
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
-; CHECK: OpStore {{%\w+}} %uint_4
-; CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord
-; CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}}
-; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_4
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
-; CHECK: OpStore {{%\w+}} {{%\w+}}
-; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_5
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
-; CHECK: OpStore {{%\w+}} {{%\w+}}
-)" + kStreamWrite4End;
-
-static const std::string kStreamWrite5Vert = kStreamWrite5Begin + R"(
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_3
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
-; CHECK: OpStore {{%\w+}} %uint_0
-; CHECK: {{%\w+}} = OpLoad %uint %gl_VertexIndex
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_4
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
-; CHECK: OpStore {{%\w+}} {{%\w+}}
-; CHECK: {{%\w+}} = OpLoad %uint %gl_InstanceIndex
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_5
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
-; CHECK: OpStore {{%\w+}} {{%\w+}}
-)" + kStreamWrite5End;
-// clang-format on
-
-static const std::string kInputDecorations = R"(
-; CHECK: OpDecorate [[input_buffer_type:%inst_bindless_InputBuffer]] Block
-; CHECK: OpMemberDecorate [[input_buffer_type]] 0 Offset 0
-; CHECK: OpDecorate [[input_buffer_var:%\w+]] DescriptorSet 7
-; CHECK: OpDecorate [[input_buffer_var]] Binding 1
-)";
-
-static const std::string kInputGlobals = R"(
-; CHECK: [[input_buffer_type]] = OpTypeStruct %_runtimearr_uint
-; CHECK: [[input_ptr_type:%\w+]] = OpTypePointer StorageBuffer [[input_buffer_type]]
-; CHECK: [[input_buffer_var]] = OpVariable [[input_ptr_type]] StorageBuffer
-)";
-
-static const std::string kDirectRead2 = R"(
-; CHECK: %inst_bindless_direct_read_2 = OpFunction %uint None {{%\w+}}
-; CHECK: [[param_1:%\w+]] = OpFunctionParameter %uint
-; CHECK: [[param_2:%\w+]] = OpFunctionParameter %uint
-; CHECK: {{%\w+}} = OpLabel
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[input_buffer_var]] %uint_0 [[param_1]]
-; CHECK: {{%\w+}} = OpLoad %uint {{%\w+}}
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} [[param_2]]
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[input_buffer_var]] %uint_0 {{%\w+}}
-; CHECK: {{%\w+}} = OpLoad %uint {{%\w+}}
-; CHECK: OpReturnValue {{%\w+}}
-; CHECK: OpFunctionEnd
-)";
-
-static const std::string kDirectRead3 = R"(
- ;CHECK: %inst_bindless_direct_read_3 = OpFunction %uint None {{%\w+}}
-; CHECK: [[param_1:%\w+]] = OpFunctionParameter %uint
-; CHECK: [[param_2:%\w+]] = OpFunctionParameter %uint
-; CHECK: [[param_3:%\w+]] = OpFunctionParameter %uint
- ;CHECK: {{%\w+}} = OpLabel
- ;CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[input_buffer_var]] %uint_0 [[param_1]]
- ;CHECK: {{%\w+}} = OpLoad %uint {{%\w+}}
- ;CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} [[param_2]]
- ;CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[input_buffer_var]] %uint_0 {{%\w+}}
- ;CHECK: {{%\w+}} = OpLoad %uint {{%\w+}}
- ;CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} [[param_3]]
- ;CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[input_buffer_var]] %uint_0 {{%\w+}}
- ;CHECK: {{%\w+}} = OpLoad %uint {{%\w+}}
- ;CHECK: OpReturnValue {{%\w+}}
- ;CHECK: OpFunctionEnd
-)";
-
-static const std::string kDirectRead4 = R"(
-; CHECK: %inst_bindless_direct_read_4 = OpFunction %uint None {{%\w+}}
-; CHECK: [[param_1:%\w+]] = OpFunctionParameter %uint
-; CHECK: [[param_2:%\w+]] = OpFunctionParameter %uint
-; CHECK: [[param_3:%\w+]] = OpFunctionParameter %uint
-; CHECK: [[param_4:%\w+]] = OpFunctionParameter %uint
-; CHECK: {{%\w+}} = OpLabel
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[input_buffer_var]] %uint_0 [[param_1]]
-; CHECK: {{%\w+}} = OpLoad %uint {{%\w+}}
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} [[param_2]]
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[input_buffer_var]] %uint_0 {{%\w+}}
-; CHECK: {{%\w+}} = OpLoad %uint {{%\w+}}
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} [[param_3]]
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[input_buffer_var]] %uint_0 {{%\w+}}
-; CHECK: {{%\w+}} = OpLoad %uint {{%\w+}}
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} [[param_4]]
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[input_buffer_var]] %uint_0 {{%\w+}}
-; CHECK: {{%\w+}} = OpLoad %uint {{%\w+}}
-; CHECK: OpReturnValue {{%\w+}}
-; CHECK: OpFunctionEnd
-)";
-
-TEST_F(InstBindlessTest, NoInstrumentConstIndexInbounds) {
-  // Texture2D g_tColor[128];
-  //
-  // SamplerState g_sAniso;
-  //
-  // struct PS_INPUT
-  // {
-  //   float2 vTextureCoords : TEXCOORD2;
-  // };
-  //
-  // struct PS_OUTPUT
-  // {
-  //   float4 vColor : SV_Target0;
-  // };
-  //
-  // PS_OUTPUT MainPs(PS_INPUT i)
-  // {
-  //   PS_OUTPUT ps_output;
-  //
-  //   ps_output.vColor = g_tColor[ 37 ].Sample(g_sAniso, i.vTextureCoords.xy);
-  //   return ps_output;
-  // }
-
-  const std::string before =
-      R"(OpCapability Shader
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor
-OpExecutionMode %MainPs OriginUpperLeft
-OpSource HLSL 500
-OpName %MainPs "MainPs"
-OpName %g_tColor "g_tColor"
-OpName %g_sAniso "g_sAniso"
-OpName %i_vTextureCoords "i.vTextureCoords"
-OpName %_entryPointOutput_vColor "@entryPointOutput.vColor"
-OpDecorate %g_tColor DescriptorSet 3
-OpDecorate %g_tColor Binding 0
-OpDecorate %g_sAniso DescriptorSet 0
-OpDecorate %i_vTextureCoords Location 0
-OpDecorate %_entryPointOutput_vColor Location 0
-%void = OpTypeVoid
-%8 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%v2float = OpTypeVector %float 2
-%v4float = OpTypeVector %float 4
-%int = OpTypeInt 32 1
-%int_0 = OpConstant %int 0
-%int_37 = OpConstant %int 37
-%15 = OpTypeImage %float 2D 0 0 0 1 Unknown
-%uint = OpTypeInt 32 0
-%uint_128 = OpConstant %uint 128
-%_arr_15_uint_128 = OpTypeArray %15 %uint_128
-%_ptr_UniformConstant__arr_15_uint_128 = OpTypePointer UniformConstant %_arr_15_uint_128
-%g_tColor = OpVariable %_ptr_UniformConstant__arr_15_uint_128 UniformConstant
-%_ptr_UniformConstant_15 = OpTypePointer UniformConstant %15
-%21 = OpTypeSampler
-%_ptr_UniformConstant_21 = OpTypePointer UniformConstant %21
-%g_sAniso = OpVariable %_ptr_UniformConstant_21 UniformConstant
-%23 = OpTypeSampledImage %15
-%_ptr_Input_v2float = OpTypePointer Input %v2float
-%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output
-%MainPs = OpFunction %void None %8
-%26 = OpLabel
-%27 = OpLoad %v2float %i_vTextureCoords
-%28 = OpAccessChain %_ptr_UniformConstant_15 %g_tColor %int_37
-%29 = OpLoad %15 %28
-%30 = OpLoad %21 %g_sAniso
-%31 = OpSampledImage %23 %29 %30
-%32 = OpImageSampleImplicitLod %v4float %31 %27
-OpStore %_entryPointOutput_vColor %32
-OpReturn
-OpFunctionEnd
-)";
-
-  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndCheck<InstBindlessCheckPass>(
-      before, before, true, true, 7u, 23u, false, false, false, false, false);
-}
-
-TEST_F(InstBindlessTest, NoInstrumentNonBindless) {
-  // This test verifies that the pass will correctly not instrument vanilla
-  // texture sample.
-  //
-  // Texture2D g_tColor;
-  //
-  // SamplerState g_sAniso;
-  //
-  // struct PS_INPUT
-  // {
-  //   float2 vTextureCoords : TEXCOORD2;
-  // };
-  //
-  // struct PS_OUTPUT
-  // {
-  //   float4 vColor : SV_Target0;
-  // };
-  //
-  // PS_OUTPUT MainPs(PS_INPUT i)
-  // {
-  //   PS_OUTPUT ps_output;
-  //   ps_output.vColor =
-  //       g_tColor.Sample(g_sAniso, i.vTextureCoords.xy);
-  //   return ps_output;
-  // }
-
-  const std::string whole_file =
-      R"(OpCapability Shader
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor
-OpExecutionMode %MainPs OriginUpperLeft
-OpSource HLSL 500
-OpName %MainPs "MainPs"
-OpName %g_tColor "g_tColor"
-OpName %g_sAniso "g_sAniso"
-OpName %i_vTextureCoords "i.vTextureCoords"
-OpName %_entryPointOutput_vColor "@entryPointOutput.vColor"
-OpDecorate %g_tColor DescriptorSet 0
-OpDecorate %g_tColor Binding 0
-OpDecorate %g_sAniso DescriptorSet 0
-OpDecorate %g_sAniso Binding 0
-OpDecorate %i_vTextureCoords Location 0
-OpDecorate %_entryPointOutput_vColor Location 0
-%void = OpTypeVoid
-%8 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%v2float = OpTypeVector %float 2
-%v4float = OpTypeVector %float 4
-%12 = OpTypeImage %float 2D 0 0 0 1 Unknown
-%_ptr_UniformConstant_12 = OpTypePointer UniformConstant %12
-%g_tColor = OpVariable %_ptr_UniformConstant_12 UniformConstant
-%14 = OpTypeSampler
-%_ptr_UniformConstant_14 = OpTypePointer UniformConstant %14
-%g_sAniso = OpVariable %_ptr_UniformConstant_14 UniformConstant
-%16 = OpTypeSampledImage %12
-%_ptr_Input_v2float = OpTypePointer Input %v2float
-%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output
-%MainPs = OpFunction %void None %8
-%19 = OpLabel
-%20 = OpLoad %v2float %i_vTextureCoords
-%21 = OpLoad %12 %g_tColor
-%22 = OpLoad %14 %g_sAniso
-%23 = OpSampledImage %16 %21 %22
-%24 = OpImageSampleImplicitLod %v4float %23 %20
-OpStore %_entryPointOutput_vColor %24
-OpReturn
-OpFunctionEnd
-)";
-
-  // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndCheck<InstBindlessCheckPass>(whole_file, whole_file, true,
-                                               true, 7u, 23u, false, false,
-                                               false, false, false);
-}
-
 TEST_F(InstBindlessTest, Simple) {
   // Texture2D g_tColor[128];
   //
@@ -508,11 +69,11 @@
 
   const std::string entry = R"(
 OpCapability Shader
-; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
+;CHECK: OpCapability Linkage
 %1 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel Logical GLSL450
 OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor
-; CHECK: OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord
+;CHECK: OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord
 OpExecutionMode %MainPs OriginUpperLeft
 OpSource HLSL 500
 )";
@@ -533,10 +94,10 @@
 OpDecorate %PerViewConstantBuffer_t Block
 OpDecorate %g_sAniso DescriptorSet 0
 OpDecorate %i_vTextureCoords Location 0
-OpDecorate %_entryPointOutput_vColor Location 0
-; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
-)" + kOutputDecorations + R"(
-; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
+OpDecorate %_entryPointOutput_vColor Location 0)"
++ kImportDeco +
+R"(
+;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
 )";
 
   const std::string consts_types_vars = R"(
@@ -566,29 +127,10 @@
 %i_vTextureCoords = OpVariable %_ptr_Input_v2float Input
 %_ptr_Output_v4float = OpTypePointer Output %v4float
 %_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output
-; CHECK: %uint_0 = OpConstant %uint 0
-; CHECK: %bool = OpTypeBool
-; CHECK: %48 = OpTypeFunction %void %uint %uint %uint %uint
-; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
-)" + kOutputGlobals + R"(
-; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-; CHECK: %uint_10 = OpConstant %uint 10
-; CHECK: %uint_4 = OpConstant %uint 4
-; CHECK: %uint_1 = OpConstant %uint 1
-; CHECK: %uint_23 = OpConstant %uint 23
-; CHECK: %uint_2 = OpConstant %uint 2
-; CHECK: %uint_3 = OpConstant %uint 3
-; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
-; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
-; CHECK: %v4uint = OpTypeVector %uint 4
-; CHECK: %uint_5 = OpConstant %uint 5
-; CHECK: %uint_7 = OpConstant %uint 7
-; CHECK: %uint_8 = OpConstant %uint 8
-; CHECK: %uint_9 = OpConstant %uint 9
-; CHECK: %uint_56 = OpConstant %uint 56
-; CHECK: %103 = OpConstantNull %v4float
+;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
+;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
+;CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float
 )";
-  // clang-format on
 
   const std::string main_func = R"(
 %MainPs = OpFunction %void None %10
@@ -602,32 +144,30 @@
 %36 = OpSampledImage %26 %34 %35
 %37 = OpImageSampleImplicitLod %v4float %36 %30
 OpStore %_entryPointOutput_vColor %37
-; CHECK-NOT: %37 = OpImageSampleImplicitLod %v4float %36 %30
-; CHECK-NOT: OpStore %_entryPointOutput_vColor %37
-; CHECK: %40 = OpULessThan %bool %32 %uint_128
-; CHECK: OpSelectionMerge %41 None
-; CHECK: OpBranchConditional %40 %42 %43
-; CHECK: %42 = OpLabel
-; CHECK: %44 = OpLoad %16 %33
-; CHECK: %45 = OpSampledImage %26 %44 %35
-; CHECK: %46 = OpImageSampleImplicitLod %v4float %45 %30
-; CHECK: OpBranch %41
-; CHECK: %43 = OpLabel
-; CHECK: %102 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_56 %uint_0 %32 %uint_128
-; CHECK: OpBranch %41
-; CHECK: %41 = OpLabel
-; CHECK: %104 = OpPhi %v4float %46 %42 %103 %43
-; CHECK: OpStore %_entryPointOutput_vColor %104
+;CHECK-NOT: %37 = OpImageSampleImplicitLod %v4float %36 %30
+;CHECK-NOT: OpStore %_entryPointOutput_vColor %37
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_57 {{%\w+}} %uint_3 %uint_0 %32 %uint_0
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpLoad %16 %33
+;CHECK: {{%\w+}} = OpSampledImage %26 {{%\w+}} %35
+;CHECK: {{%\w+}} = OpImageSampleImplicitLod %v4float {{%\w+}} %30
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: [[phi_result:%\w+]] = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}}
+;CHECK: OpStore %_entryPointOutput_vColor [[phi_result]]
 OpReturn
 OpFunctionEnd
 )";
-
-  const std::string output_func = kStreamWrite4Frag;
+  // clang-format on
 
   SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndMatch<InstBindlessCheckPass, uint32_t, uint32_t, bool, bool>(
-      entry + names_annots + consts_types_vars + main_func + output_func, true,
-      7u, 23u, false, false, false, false, false);
+  SinglePassRunAndMatch<InstBindlessCheckPass>(
+      entry + names_annots + consts_types_vars + kImportStub + main_func, true,
+      23u);
 }
 
 TEST_F(InstBindlessTest, InstrumentMultipleInstructions) {
@@ -664,11 +204,11 @@
   // clang-format off
   const std::string defs = R"(
 OpCapability Shader
-; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
+OpCapability Linkage
 %1 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel Logical GLSL450
 OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor
-; CHECK: OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord
+;CHECK: OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord
 OpExecutionMode %MainPs OriginUpperLeft
 OpSource HLSL 500
 OpName %MainPs "MainPs"
@@ -680,16 +220,15 @@
 OpName %i_vTextureCoords "i.vTextureCoords"
 OpName %_entryPointOutput_vColor "@entryPointOutput.vColor"
 OpDecorate %g_tColor DescriptorSet 3
-OpDecorate %g_tColor Binding 0
+OpDecorate %g_tColor Binding 4
 OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0
 OpMemberDecorate %PerViewConstantBuffer_t 1 Offset 4
 OpDecorate %PerViewConstantBuffer_t Block
-OpDecorate %g_sAniso DescriptorSet 0
+OpDecorate %g_sAniso DescriptorSet 3
 OpDecorate %i_vTextureCoords Location 0
 OpDecorate %_entryPointOutput_vColor Location 0
-; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
-)" + kOutputDecorations + R"(
-; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
+)" + kImportDeco + R"(
+;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
 %void = OpTypeVoid
 %10 = OpTypeFunction %void
 %float = OpTypeFloat 32
@@ -717,33 +256,14 @@
 %i_vTextureCoords = OpVariable %_ptr_Input_v2float Input
 %_ptr_Output_v4float = OpTypePointer Output %v4float
 %_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output
-; CHECK: %uint_0 = OpConstant %uint 0
-; CHECK: %bool = OpTypeBool
-; CHECK: %56 = OpTypeFunction %void %uint %uint %uint %uint
-; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
-)" + kOutputGlobals + R"(
-; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-; CHECK: %uint_10 = OpConstant %uint 10
-; CHECK: %uint_4 = OpConstant %uint 4
-; CHECK: %uint_1 = OpConstant %uint 1
-; CHECK: %uint_23 = OpConstant %uint 23
-; CHECK: %uint_2 = OpConstant %uint 2
-; CHECK: %uint_3 = OpConstant %uint 3
-; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
-; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
-; CHECK: %v4uint = OpTypeVector %uint 4
-; CHECK: %uint_5 = OpConstant %uint 5
-; CHECK: %uint_7 = OpConstant %uint 7
-; CHECK: %uint_8 = OpConstant %uint 8
-; CHECK: %uint_9 = OpConstant %uint 9
-; CHECK: %uint_58 = OpConstant %uint 58
-; CHECK: %111 = OpConstantNull %v4float
-; CHECK: %uint_64 = OpConstant %uint 64
+;CHECK: %v4uint = OpTypeVector %uint 4
+;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
+;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
+;CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float
 )";
-  // clang-format on
 
-  const std::string main_func =
-      R"(%MainPs = OpFunction %void None %10
+  const std::string main_func = R"(
+%MainPs = OpFunction %void None %10
 %30 = OpLabel
 %31 = OpLoad %v2float %i_vTextureCoords
 %32 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0
@@ -753,20 +273,24 @@
 %36 = OpLoad %25 %g_sAniso
 %37 = OpSampledImage %27 %35 %36
 %38 = OpImageSampleImplicitLod %v4float %37 %31
-; CHECK-NOT: %38 = OpImageSampleImplicitLod %v4float %37 %31
-; CHECK: %48 = OpULessThan %bool %33 %uint_128
-; CHECK: OpSelectionMerge %49 None
-; CHECK: OpBranchConditional %48 %50 %51
-; CHECK: %50 = OpLabel
-; CHECK: %52 = OpLoad %17 %34
-; CHECK: %53 = OpSampledImage %27 %52 %36
-; CHECK: %54 = OpImageSampleImplicitLod %v4float %53 %31
-; CHECK: OpBranch %49
-; CHECK: %51 = OpLabel
-; CHECK: %110 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_58 %uint_0 %33 %uint_128
-; CHECK: OpBranch %49
-; CHECK: %49 = OpLabel
-; CHECK: %112 = OpPhi %v4float %54 %50 %111 %51
+;CHECK-NOT: %38 = OpImageSampleImplicitLod %v4float %37 %31
+;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord
+;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}}
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_60 {{%\w+}} %uint_3 %uint_4 %33 %uint_0
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpLoad %17 %34
+;CHECK: {{%\w+}} = OpSampledImage %27 {{%\w+}} %36
+;CHECK: {{%\w+}} = OpImageSampleImplicitLod %v4float {{%\w+}} %31
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}}
 %39 = OpAccessChain %_ptr_PushConstant_uint %_ %int_1
 %40 = OpLoad %uint %39
 %41 = OpAccessChain %_ptr_UniformConstant_17 %g_tColor %40
@@ -774,33 +298,35 @@
 %43 = OpSampledImage %27 %42 %36
 %44 = OpImageSampleImplicitLod %v4float %43 %31
 %45 = OpFAdd %v4float %38 %44
-; CHECK-NOT: %44 = OpImageSampleImplicitLod %v4float %43 %31
-; CHECK-NOT: %45 = OpFAdd %v4float %38 %44
-; CHECK: %113 = OpULessThan %bool %40 %uint_128
-; CHECK: OpSelectionMerge %114 None
-; CHECK: OpBranchConditional %113 %115 %116
-; CHECK: %115 = OpLabel
-; CHECK: %117 = OpLoad %17 %41
-; CHECK: %118 = OpSampledImage %27 %117 %36
-; CHECK: %119 = OpImageSampleImplicitLod %v4float %118 %31
-; CHECK: OpBranch %114
-; CHECK: %116 = OpLabel
-; CHECK: %121 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_64 %uint_0 %40 %uint_128
-; CHECK: OpBranch %114
-; CHECK: %114 = OpLabel
-; CHECK: %122 = OpPhi %v4float %119 %115 %111 %116
-; CHECK: %45 = OpFAdd %v4float %112 %122
+;CHECK-NOT: %44 = OpImageSampleImplicitLod %v4float %43 %31
+;CHECK-NOT: %45 = OpFAdd %v4float %38 %44
+;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord
+;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}}
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_66 {{%\w+}} %uint_3 %uint_4 %40 %uint_0
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpLoad %17 %41
+;CHECK: {{%\w+}} = OpSampledImage %27 {{%\w+}} %36
+;CHECK: {{%\w+}} = OpImageSampleImplicitLod %v4float {{%\w+}} %31
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}}
+;CHECK: %45 = OpFAdd %v4float {{%\w+}} {{%\w+}}
 OpStore %_entryPointOutput_vColor %45
 OpReturn
 OpFunctionEnd
 )";
-
-  const std::string output_func = kStreamWrite4Frag;
+  // clang-format on
 
   SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + output_func,
-                                               true, 7u, 23u, false, false,
-                                               false, false, false);
+  SinglePassRunAndMatch<InstBindlessCheckPass>(defs + kImportStub + main_func,
+                                               true, 23u);
 }
 
 TEST_F(InstBindlessTest, InstrumentOpImage) {
@@ -812,11 +338,11 @@
   const std::string defs = R"(
 OpCapability Shader
 OpCapability StorageImageReadWithoutFormat
-; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
+;CHECK: OpCapability Linkage
 %1 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel Logical GLSL450
 OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor
-; CHECK: OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord
+;CHECK: OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord
 OpExecutionMode %MainPs OriginUpperLeft
 OpSource HLSL 500
 OpName %MainPs "MainPs"
@@ -827,14 +353,13 @@
 OpName %i_vTextureCoords "i.vTextureCoords"
 OpName %_entryPointOutput_vColor "@entryPointOutput.vColor"
 OpDecorate %g_tColor DescriptorSet 3
-OpDecorate %g_tColor Binding 0
+OpDecorate %g_tColor Binding 9
 OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0
 OpDecorate %PerViewConstantBuffer_t Block
 OpDecorate %i_vTextureCoords Location 0
 OpDecorate %_entryPointOutput_vColor Location 0
-; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
-)" + kOutputDecorations + R"(
-; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
+)" + kImportDeco + R"(
+;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
 %void = OpTypeVoid
 %3 = OpTypeFunction %void
 %float = OpTypeFloat 32
@@ -858,29 +383,10 @@
 %i_vTextureCoords = OpVariable %_ptr_Input_v2int Input
 %_ptr_Output_v4float = OpTypePointer Output %v4float
 %_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output
-; CHECK: uint_0 = OpConstant %uint 0
-; CHECK: bool = OpTypeBool
-; CHECK: %86 = OpTypeFunction %void %uint %uint %uint %uint
-; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
-)" + kOutputGlobals + R"(
-; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-; CHECK: %uint_10 = OpConstant %uint 10
-; CHECK: %uint_4 = OpConstant %uint 4
-; CHECK: %uint_1 = OpConstant %uint 1
-; CHECK: %uint_23 = OpConstant %uint 23
-; CHECK: %uint_2 = OpConstant %uint 2
-; CHECK: %uint_3 = OpConstant %uint 3
-; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
-; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
-; CHECK: %v4uint = OpTypeVector %uint 4
-; CHECK: %uint_5 = OpConstant %uint 5
-; CHECK: %uint_7 = OpConstant %uint 7
-; CHECK: %uint_8 = OpConstant %uint 8
-; CHECK: %uint_9 = OpConstant %uint 9
-; CHECK: %uint_51 = OpConstant %uint 51
-; CHECK: %141 = OpConstantNull %v4float
+;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
+;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
+;CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float
 )";
-  // clang-format on
 
   const std::string main_func = R"(
 %MainPs = OpFunction %void None %3
@@ -893,32 +399,34 @@
 %75 = OpImage %20 %66
 %71 = OpImageRead %v4float %75 %53
 OpStore %_entryPointOutput_vColor %71
-; CHECK-NOT: %71 = OpImageRead %v4float %75 %53
-; CHECK-NOT: OpStore %_entryPointOutput_vColor %71
-; CHECK: %78 = OpULessThan %bool %64 %uint_128
-; CHECK: OpSelectionMerge %79 None
-; CHECK: OpBranchConditional %78 %80 %81
-; CHECK: %80 = OpLabel
-; CHECK: %82 = OpLoad %39 %65
-; CHECK: %83 = OpImage %20 %82
-; CHECK: %84 = OpImageRead %v4float %83 %53
-; CHECK: OpBranch %79
-; CHECK: %81 = OpLabel
-; CHECK: %140 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_51 %uint_0 %64 %uint_128
-; CHECK: OpBranch %79
-; CHECK: %79 = OpLabel
-; CHECK: %142 = OpPhi %v4float %84 %80 %141 %81
-; CHECK: OpStore %_entryPointOutput_vColor %142
+;CHECK-NOT: %71 = OpImageRead %v4float %75 %53
+;CHECK-NOT: OpStore %_entryPointOutput_vColor %71
+;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord
+;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}}
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_52 {{%\w+}} %uint_3 %uint_9 %64 %uint_0
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpLoad %39 %65
+;CHECK: {{%\w+}} = OpImage %20 {{%\w+}}
+;CHECK: {{%\w+}} = OpImageRead %v4float {{%\w+}} %53
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}}
+;CHECK: OpStore %_entryPointOutput_vColor {{%\w+}}
 OpReturn
 OpFunctionEnd
 )";
-
-  const std::string output_func = kStreamWrite4Frag;
+  // clang-format on
 
   SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + output_func,
-                                               true, 7u, 23u, false, false,
-                                               false, false, false);
+  SinglePassRunAndMatch<InstBindlessCheckPass>(defs + kImportStub + main_func,
+                                               true, 23u);
 }
 
 TEST_F(InstBindlessTest, InstrumentSampledImage) {
@@ -929,11 +437,11 @@
   // clang-format off
   const std::string defs = R"(
 OpCapability Shader
-; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
+;CHECK: OpCapability Linkage
 %1 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel Logical GLSL450
 OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor
-; CHECK: OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord
+;CHECK: OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord
 OpExecutionMode %MainPs OriginUpperLeft
 OpSource HLSL 500
 OpName %MainPs "MainPs"
@@ -943,14 +451,14 @@
 OpName %_ ""
 OpName %i_vTextureCoords "i.vTextureCoords"
 OpName %_entryPointOutput_vColor "@entryPointOutput.vColor"
-OpDecorate %g_tColor DescriptorSet 3
-OpDecorate %g_tColor Binding 0
+OpDecorate %g_tColor DescriptorSet 4
+OpDecorate %g_tColor Binding 11
 OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0
 OpDecorate %PerViewConstantBuffer_t Block
 OpDecorate %i_vTextureCoords Location 0
 OpDecorate %_entryPointOutput_vColor Location 0
-)" + kOutputDecorations + R"(
-; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
+)" + kImportDeco + R"(
+;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
 %void = OpTypeVoid
 %3 = OpTypeFunction %void
 %float = OpTypeFloat 32
@@ -974,29 +482,10 @@
 %i_vTextureCoords = OpVariable %_ptr_Input_v2float Input
 %_ptr_Output_v4float = OpTypePointer Output %v4float
 %_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output
-; CHECK: uint_0 = OpConstant %uint 0
-; CHECK: bool = OpTypeBool
-; CHECK: %81 = OpTypeFunction %void %uint %uint %uint %uint
-; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
-)" + kOutputGlobals + R"(
-; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-; CHECK: %uint_10 = OpConstant %uint 10
-; CHECK: %uint_4 = OpConstant %uint 4
-; CHECK: %uint_1 = OpConstant %uint 1
-; CHECK: %uint_23 = OpConstant %uint 23
-; CHECK: %uint_2 = OpConstant %uint 2
-; CHECK: %uint_3 = OpConstant %uint 3
-; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
-; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
-; CHECK: %v4uint = OpTypeVector %uint 4
-; CHECK: %uint_5 = OpConstant %uint 5
-; CHECK: %uint_7 = OpConstant %uint 7
-; CHECK: %uint_8 = OpConstant %uint 8
-; CHECK: %uint_9 = OpConstant %uint 9
-; CHECK: %uint_49 = OpConstant %uint 49
-; CHECK: %136 = OpConstantNull %v4float
+;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
+;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
+;CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float
 )";
-  // clang-format on
 
   const std::string main_func = R"(
 %MainPs = OpFunction %void None %3
@@ -1008,31 +497,33 @@
 %66 = OpLoad %39 %65
 %71 = OpImageSampleImplicitLod %v4float %66 %53
 OpStore %_entryPointOutput_vColor %71
-; CHECK-NOT: %71 = OpImageSampleImplicitLod %v4float %66 %53
-; CHECK-NOT: OpStore %_entryPointOutput_vColor %71
-; CHECK: %74 = OpULessThan %bool %64 %uint_128
-; CHECK: OpSelectionMerge %75 None
-; CHECK: OpBranchConditional %74 %76 %77
-; CHECK: %76 = OpLabel
-; CHECK: %78 = OpLoad %39 %65
-; CHECK: %79 = OpImageSampleImplicitLod %v4float %78 %53
-; CHECK: OpBranch %75
-; CHECK: %77 = OpLabel
-; CHECK: %135 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_49 %uint_0 %64 %uint_128
-; CHECK: OpBranch %75
-; CHECK: %75 = OpLabel
-; CHECK: %137 = OpPhi %v4float %79 %76 %136 %77
-; CHECK: OpStore %_entryPointOutput_vColor %137
+;CHECK-NOT: %71 = OpImageSampleImplicitLod %v4float %66 %53
+;CHECK-NOT: OpStore %_entryPointOutput_vColor %71
+;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord
+;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}}
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_50 {{%\w+}} %uint_4 %uint_11 %64 %uint_0
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpLoad %39 %65
+;CHECK: {{%\w+}} = OpImageSampleImplicitLod %v4float {{%\w+}} %53
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}}
+;CHECK: OpStore %_entryPointOutput_vColor {{%\w+}}
 OpReturn
 OpFunctionEnd
 )";
-
-  const std::string output_func = kStreamWrite4Frag;
+  // clang-format on
 
   SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + output_func,
-                                               true, 7u, 23u, false, false,
-                                               false, false, false);
+  SinglePassRunAndMatch<InstBindlessCheckPass>(defs + kImportStub + main_func,
+                                               true, 23u);
 }
 
 TEST_F(InstBindlessTest, InstrumentImageWrite) {
@@ -1044,11 +535,11 @@
   const std::string defs = R"(
 OpCapability Shader
 OpCapability StorageImageWriteWithoutFormat
-; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
+;CHECK: OpCapability Linkage
 %1 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel Logical GLSL450
 OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor
-; CHECK: OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord
+;CHECK: OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord
 OpExecutionMode %MainPs OriginUpperLeft
 OpSource HLSL 500
 OpName %MainPs "MainPs"
@@ -1058,15 +549,14 @@
 OpName %_ ""
 OpName %i_vTextureCoords "i.vTextureCoords"
 OpName %_entryPointOutput_vColor "@entryPointOutput.vColor"
-OpDecorate %g_tColor DescriptorSet 3
-OpDecorate %g_tColor Binding 0
+OpDecorate %g_tColor DescriptorSet 30
+OpDecorate %g_tColor Binding 2
 OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0
 OpDecorate %PerViewConstantBuffer_t Block
 OpDecorate %i_vTextureCoords Location 0
 OpDecorate %_entryPointOutput_vColor Location 0
-; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
-)" + kOutputDecorations + R"(
-; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
+)" + kImportDeco + R"(
+;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
 %void = OpTypeVoid
 %3 = OpTypeFunction %void
 %float = OpTypeFloat 32
@@ -1091,28 +581,10 @@
 %i_vTextureCoords = OpVariable %_ptr_Input_v2int Input
 %_ptr_Output_v4float = OpTypePointer Output %v4float
 %_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output
-; CHECK: uint_0 = OpConstant %uint 0
-; CHECK: bool = OpTypeBool
-; CHECK: %41 = OpTypeFunction %void %uint %uint %uint %uint
-; CHECK: _runtimearr_uint = OpTypeRuntimeArray %uint
-)" + kOutputGlobals + R"(
-; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-; CHECK: %uint_10 = OpConstant %uint 10
-; CHECK: %uint_4 = OpConstant %uint 4
-; CHECK: %uint_1 = OpConstant %uint 1
-; CHECK: %uint_23 = OpConstant %uint 23
-; CHECK: %uint_2 = OpConstant %uint 2
-; CHECK: %uint_3 = OpConstant %uint 3
-; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
-; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
-; CHECK: %v4uint = OpTypeVector %uint 4
-; CHECK: %uint_5 = OpConstant %uint 5
-; CHECK: %uint_7 = OpConstant %uint 7
-; CHECK: %uint_8 = OpConstant %uint 8
-; CHECK: %uint_9 = OpConstant %uint 9
-; CHECK: %uint_51 = OpConstant %uint 51
+;CHECK: %v4uint = OpTypeVector %uint 4
+;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
+;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
 )";
-  // clang-format on
 
   const std::string main_func = R"(
 %MainPs = OpFunction %void None %3
@@ -1124,30 +596,33 @@
 %66 = OpLoad %20 %65
 OpImageWrite %66 %53 %80
 OpStore %_entryPointOutput_vColor %80
-; CHECK-NOT: OpImageWrite %66 %53 %80
-; CHECK-NOT: OpStore %_entryPointOutput_vColor %80
-; CHECK: %35 = OpULessThan %bool %30 %uint_128
-; CHECK: OpSelectionMerge %36 None
-; CHECK: OpBranchConditional %35 %37 %38
-; CHECK: %37 = OpLabel
-; CHECK: %39 = OpLoad %16 %31
-; CHECK: OpImageWrite %39 %28 %19
-; CHECK: OpBranch %36
-; CHECK: %38 = OpLabel
-; CHECK: %95 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_51 %uint_0 %30 %uint_128
-; CHECK: OpBranch %36
-; CHECK: %36 = OpLabel
-; CHECK: OpStore %_entryPointOutput_vColor %19
+;CHECK-NOT: OpImageWrite %66 %53 %80
+;CHECK-NOT: OpStore %_entryPointOutput_vColor %80
+;CHECK: %32 = OpLoad %16 %31
+;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord
+;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}}
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_52 {{%\w+}} %uint_30 %uint_2 %30 %uint_0
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpLoad %16 %31
+;CHECK: OpImageWrite {{%\w+}} %28 %19
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpStore %_entryPointOutput_vColor %19
 OpReturn
 OpFunctionEnd
 )";
-
-  const std::string output_func = kStreamWrite4Frag;
+  // clang-format on
 
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + output_func,
-                                               true, 7u, 23u, false, false,
-                                               false, false, false);
+  SinglePassRunAndMatch<InstBindlessCheckPass>(defs + kImportStub + main_func,
+                                               true, 23u);
 }
 
 TEST_F(InstBindlessTest, InstrumentVertexSimple) {
@@ -1159,7 +634,7 @@
   const std::string defs = R"(
 OpCapability Shader
 OpCapability Sampled1D
-; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
+;CHECK: OpCapability Linkage
 %1 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel Logical GLSL450
 OpEntryPoint Vertex %main "main" %_ %coords2D
@@ -1178,20 +653,19 @@
 OpMemberName %foo 0 "g_idx"
 OpName %__0 ""
 OpName %coords2D "coords2D"
-; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
-)" + kOutputDecorations + R"(
-; CHECK: OpDecorate %gl_VertexIndex BuiltIn VertexIndex
-; CHECK: OpDecorate %gl_InstanceIndex BuiltIn InstanceIndex
+)" + kImportDeco + R"(
+;CHECK: OpDecorate %gl_VertexIndex BuiltIn VertexIndex
+;CHECK: OpDecorate %gl_InstanceIndex BuiltIn InstanceIndex
 OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
 OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize
 OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance
 OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance
 OpDecorate %gl_PerVertex Block
-OpDecorate %texSampler1D DescriptorSet 0
-OpDecorate %texSampler1D Binding 3
+OpDecorate %texSampler1D DescriptorSet 2
+OpDecorate %texSampler1D Binding 13
 OpMemberDecorate %foo 0 Offset 0
 OpDecorate %foo Block
-OpDecorate %__0 DescriptorSet 0
+OpDecorate %__0 DescriptorSet 7
 OpDecorate %__0 Binding 5
 OpDecorate %coords2D Location 0
 %void = OpTypeVoid
@@ -1224,28 +698,11 @@
 %v2float = OpTypeVector %float 2
 %_ptr_Input_v2float = OpTypePointer Input %v2float
 %coords2D = OpVariable %_ptr_Input_v2float Input
-; CHECK: %uint_0 = OpConstant %uint 0
-; CHECK: %bool = OpTypeBool
-; CHECK: %54 = OpTypeFunction %void %uint %uint %uint %uint
-; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
-)" + kOutputGlobals + R"(
-; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-; CHECK: %uint_10 = OpConstant %uint 10
-; CHECK: %uint_4 = OpConstant %uint 4
-; CHECK: %uint_23 = OpConstant %uint 23
-; CHECK: %uint_2 = OpConstant %uint 2
-; CHECK: %uint_3 = OpConstant %uint 3
-; CHECK: %_ptr_Input_uint = OpTypePointer Input %uint
-; CHECK: %gl_VertexIndex = OpVariable %_ptr_Input_uint Input
-; CHECK: %gl_InstanceIndex = OpVariable %_ptr_Input_uint Input
-; CHECK: %uint_5 = OpConstant %uint 5
-; CHECK: %uint_7 = OpConstant %uint 7
-; CHECK: %uint_8 = OpConstant %uint 8
-; CHECK: %uint_9 = OpConstant %uint 9
-; CHECK: %uint_74 = OpConstant %uint 74
-; CHECK: %106 = OpConstantNull %v4float
+;CHECK: %_ptr_Input_uint = OpTypePointer Input %uint
+;CHECK: %gl_VertexIndex = OpVariable %_ptr_Input_uint Input
+;CHECK: %gl_InstanceIndex = OpVariable %_ptr_Input_uint Input
+;CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float
 )";
-  // clang-format on
 
   const std::string main_func = R"(
 %main = OpFunction %void None %3
@@ -1263,34 +720,51 @@
 %38 = OpImageSampleExplicitLod %v4float %35 %36 Lod %37
 %40 = OpAccessChain %_ptr_Output_v4float %_ %int_0
 OpStore %40 %38
-; CHECK-NOT: %38 = OpImageSampleExplicitLod %v4float %35 %36 Lod %37
-; CHECK-NOT: %40 = OpAccessChain %_ptr_Output_v4float %_ %int_0
-; CHECK-NOT: OpStore %40 %38
-; CHECK: %46 = OpULessThan %bool %37 %uint_128
-; CHECK: OpSelectionMerge %47 None
-; CHECK: OpBranchConditional %46 %48 %49
-; CHECK: %48 = OpLabel
-; CHECK: %50 = OpLoad %25 %38
-; CHECK: %51 = OpImageSampleExplicitLod %v4float %50 %40 Lod %41
-; CHECK: OpBranch %47
-; CHECK: %49 = OpLabel
-; CHECK: %52 = OpBitcast %uint %37
-; CHECK: %105 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_74 %uint_0 %52 %uint_128
-; CHECK: OpBranch %47
-; CHECK: %47 = OpLabel
-; CHECK: %107 = OpPhi %v4float %51 %48 %106 %49
-; CHECK: %43 = OpAccessChain %_ptr_Output_v4float %_ %int_0
-; CHECK: OpStore %43 %107
+;CHECK-NOT: %38 = OpImageSampleExplicitLod %v4float %35 %36 Lod %37
+;CHECK-NOT: %40 = OpAccessChain %_ptr_Output_v4float %_ %int_0
+;CHECK-NOT: OpStore %40 %38
+;CHECK: {{%\w+}} = OpLoad %uint %gl_VertexIndex
+;CHECK: {{%\w+}} = OpLoad %uint %gl_InstanceIndex
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_0 {{%\w+}} {{%\w+}} %uint_0
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_70 {{%\w+}} %uint_7 %uint_5 %uint_0 {{%\w+}}
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpLoad %int {{%\w+}}
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpPhi %int {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpAccessChain %_ptr_UniformConstant_25 %texSampler1D {{%\w+}}
+;CHECK: {{%\w+}} = OpLoad {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLoad %float %coords1D
+;CHECK: {{%\w+}} = OpLoad %float %lod
+;CHECK: {{%\w+}} = OpLoad %uint %gl_VertexIndex
+;CHECK: {{%\w+}} = OpLoad %uint %gl_InstanceIndex
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_0 {{%\w+}} {{%\w+}} %uint_0
+;CHECK: {{%\w+}} = OpBitcast %uint {{%\w+}}
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_75 {{%\w+}} %uint_2 %uint_13 {{%\w+}} %uint_0
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpLoad %25 %38
+;CHECK: {{%\w+}} = OpImageSampleExplicitLod %v4float {{%\w+}} %40 Lod %41
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpPhi %v4float {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: %43 = OpAccessChain %_ptr_Output_v4float %_ %int_0
+;CHECK: OpStore %43 {{%\w+}}
 OpReturn
 OpFunctionEnd
 )";
-
-  const std::string output_func = kStreamWrite4Vert;
+  // clang-format on
 
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + output_func,
-                                               true, 7u, 23u, false, false,
-                                               false, false, false);
+  SinglePassRunAndMatch<InstBindlessCheckPass>(defs + kImportStub + main_func,
+                                               true, 23u);
 }
 
 TEST_F(InstBindlessTest, InstrumentTeseSimple) {
@@ -1302,9 +776,9 @@
   // #version 450
   // #extension GL_EXT_nonuniform_qualifier : enable
   //
-  // layout(std140, set = 0, binding = 0) uniform ufoo { uint index; } uniform_index_buffer;
+  // layout(std140, set = 9, binding = 1) uniform ufoo { uint index; } uniform_index_buffer;
   //
-  // layout(set = 0, binding = 1) buffer bfoo { vec4 val; } adds[11];
+  // layout(set = 9, binding = 2) buffer bfoo { vec4 val; } adds[11];
   //
   // layout(triangles, equal_spacing, cw) in;
   //
@@ -1315,10 +789,11 @@
 
   const std::string defs = R"(
 OpCapability Tessellation
+;CHECK: OpCapability Linkage
 %1 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel Logical GLSL450
 OpEntryPoint TessellationEvaluation %main "main" %_
-; CHECK: OpEntryPoint TessellationEvaluation %main "main" %_ %gl_PrimitiveID %gl_TessCoord
+;CHECK: OpEntryPoint TessellationEvaluation %main "main" %_ %gl_PrimitiveID %gl_TessCoord
 OpExecutionMode %main Triangles
 OpExecutionMode %main SpacingEqual
 OpExecutionMode %main VertexOrderCw
@@ -1344,16 +819,15 @@
 OpDecorate %gl_PerVertex Block
 OpMemberDecorate %bfoo 0 Offset 0
 OpDecorate %bfoo Block
-OpDecorate %adds DescriptorSet 0
+OpDecorate %adds DescriptorSet 9
 OpDecorate %adds Binding 1
 OpMemberDecorate %ufoo 0 Offset 0
 OpDecorate %ufoo Block
-OpDecorate %uniform_index_buffer DescriptorSet 0
-OpDecorate %uniform_index_buffer Binding 0
-; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
-)" + kOutputDecorations + R"(
-; CHECK: OpDecorate %gl_PrimitiveID BuiltIn PrimitiveId
-; CHECK: OpDecorate %gl_TessCoord BuiltIn TessCoord
+OpDecorate %uniform_index_buffer DescriptorSet 9
+OpDecorate %uniform_index_buffer Binding 2
+)" + kImportDeco + R"(
+;CHECK: OpDecorate %gl_PrimitiveID BuiltIn PrimitiveId
+;CHECK: OpDecorate %gl_TessCoord BuiltIn TessCoord
 %void = OpTypeVoid
 %3 = OpTypeFunction %void
 %float = OpTypeFloat 32
@@ -1377,66 +851,69 @@
 %_ptr_Uniform_uint = OpTypePointer Uniform %uint
 %_ptr_StorageBuffer_v4float = OpTypePointer StorageBuffer %v4float
 %_ptr_Output_v4float = OpTypePointer Output %v4float
-; CHECK: %uint_0 = OpConstant %uint 0
-; CHECK: %bool = OpTypeBool
-; CHECK: %40 = OpTypeFunction %void %uint %uint %uint %uint
-; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
-)" + kOutputGlobals + R"(
-; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-; CHECK: %uint_10 = OpConstant %uint 10
-; CHECK: %uint_4 = OpConstant %uint 4
-; CHECK: %uint_23 = OpConstant %uint 23
-; CHECK: %uint_2 = OpConstant %uint 2
-; CHECK: %uint_3 = OpConstant %uint 3
-; CHECK: %_ptr_Input_uint = OpTypePointer Input %uint
-; CHECK: %gl_PrimitiveID = OpVariable %_ptr_Input_uint Input
-; CHECK: %v3float = OpTypeVector %float 3
-; CHECK: %_ptr_Input_v3float = OpTypePointer Input %v3float
-; CHECK: %gl_TessCoord = OpVariable %_ptr_Input_v3float Input
-; CHECK: %v3uint = OpTypeVector %uint 3
-; CHECK: %uint_5 = OpConstant %uint 5
-; CHECK: %uint_6 = OpConstant %uint 6
-; CHECK: %uint_7 = OpConstant %uint 7
-; CHECK: %uint_8 = OpConstant %uint 8
-; CHECK: %uint_9 = OpConstant %uint 9
-; CHECK: %uint_63 = OpConstant %uint 63
-; CHECK: %101 = OpConstantNull %v4float
+;CHECK: %_ptr_Input_uint = OpTypePointer Input %uint
+;CHECK: %gl_PrimitiveID = OpVariable %_ptr_Input_uint Input
+;CHECK: %v3float = OpTypeVector %float 3
+;CHECK: %_ptr_Input_v3float = OpTypePointer Input %v3float
+;CHECK: %gl_TessCoord = OpVariable %_ptr_Input_v3float Input
+;CHECK: %v3uint = OpTypeVector %uint 3
+;CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float
 )";
-  // clang-format on
 
-  const std::string main_func = R"(
+  const std::string main_func =
+      R"(
 %main = OpFunction %void None %3
 %5 = OpLabel
 %25 = OpAccessChain %_ptr_Uniform_uint %uniform_index_buffer %int_0
 %26 = OpLoad %uint %25
 %28 = OpAccessChain %_ptr_StorageBuffer_v4float %adds %26 %int_0
 %29 = OpLoad %v4float %28
-; CHECK-NOT: %29 = OpLoad %v4float %28
-; CHECK: %34 = OpULessThan %bool %28 %uint_11
-; CHECK: OpSelectionMerge %35 None
-; CHECK: OpBranchConditional %34 %36 %37
-; CHECK: %36 = OpLabel
-; CHECK: %38 = OpLoad %v4float %29
-; CHECK: OpBranch %35
-; CHECK: %37 = OpLabel
-; CHECK: %100 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_63 %uint_0 %28 %uint_11
-; CHECK: OpBranch %35
-; CHECK: %35 = OpLabel
-; CHECK: %102 = OpPhi %v4float %38 %36 %101 %37
+;CHECK-NOT: %29 = OpLoad %v4float %28
+;CHECK: {{%\w+}} = OpLoad %uint %gl_PrimitiveID
+;CHECK: {{%\w+}} = OpLoad %v3float %gl_TessCoord
+;CHECK: {{%\w+}} = OpBitcast %v3uint {{%\w+}}
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_2 {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_62 {{%\w+}} %uint_9 %uint_2 %uint_0 {{%\w+}}
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpLoad %uint %27
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpPhi %uint {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}}
 %31 = OpAccessChain %_ptr_Output_v4float %_ %int_0
 OpStore %31 %29
-; CHECK-NOT: OpStore %31 %29
-; CHECK: OpStore %31 %102
+;CHECK-NOT: OpStore %31 %29
+;CHECK: {{%\w+}} = OpLoad %uint %gl_PrimitiveID
+;CHECK: {{%\w+}} = OpLoad %v3float %gl_TessCoord
+;CHECK: {{%\w+}} = OpBitcast %v3uint {{%\w+}}
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_2 {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_64 {{%\w+}} %uint_9 %uint_1 {{%\w+}} {{%\w+}}
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpLoad %v4float %29
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: [[phi_result:%\w+]] = OpPhi {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: %31 = OpAccessChain %_ptr_Output_v4float %_ %int_0
+;CHECK: OpStore %31 [[phi_result]]
 OpReturn
 OpFunctionEnd
 )";
-
-  const std::string output_func = kStreamWrite4Tese;
+  // clang-format on
 
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + output_func,
-                                               true, 7u, 23u, false, false,
-                                               false, false, false);
+  SinglePassRunAndMatch<InstBindlessCheckPass>(defs + kImportStub + main_func,
+                                               true, 23u);
 }
 
 TEST_F(InstBindlessTest, MultipleDebugFunctions) {
@@ -1446,11 +923,11 @@
   // clang-format off
   const std::string defs = R"(
 OpCapability Shader
-; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
+;CHECK: OpCapability Linkage
 %2 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel Logical GLSL450
 OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor
-; CHECK: OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord
+;CHECK: OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord
 OpExecutionMode %MainPs OriginUpperLeft
 %1 = OpString "foo5.frag"
 OpSource HLSL 500 %1
@@ -1471,17 +948,16 @@
 OpName %i_vTextureCoords "i.vTextureCoords"
 OpName %_entryPointOutput_vColor "@entryPointOutput.vColor"
 OpName %param "param"
-OpDecorate %g_tColor DescriptorSet 0
-OpDecorate %g_tColor Binding 0
+OpDecorate %g_tColor DescriptorSet 1
+OpDecorate %g_tColor Binding 2
 OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0
 OpDecorate %PerViewConstantBuffer_t Block
-OpDecorate %g_sAniso DescriptorSet 0
-OpDecorate %g_sAniso Binding 1
+OpDecorate %g_sAniso DescriptorSet 1
+OpDecorate %g_sAniso Binding 3
 OpDecorate %i_vTextureCoords Location 0
 OpDecorate %_entryPointOutput_vColor Location 0
-; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
-)" + kOutputDecorations + R"(
-; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
+)" + kImportDeco + R"(
+;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
 %void = OpTypeVoid
 %4 = OpTypeFunction %void
 %float = OpTypeFloat 32
@@ -1515,29 +991,10 @@
 %i_vTextureCoords = OpVariable %_ptr_Input_v2float Input
 %_ptr_Output_v4float = OpTypePointer Output %v4float
 %_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output
-; CHECK: %uint_0 = OpConstant %uint 0
-; CHECK: %bool = OpTypeBool
-; CHECK: %70 = OpTypeFunction %void %uint %uint %uint %uint
-; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
-)" + kOutputGlobals + R"(
-; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-; CHECK: %uint_10 = OpConstant %uint 10
-; CHECK: %uint_4 = OpConstant %uint 4
-; CHECK: %uint_1 = OpConstant %uint 1
-; CHECK: %uint_23 = OpConstant %uint 23
-; CHECK: %uint_2 = OpConstant %uint 2
-; CHECK: %uint_3 = OpConstant %uint 3
-; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
-; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
-; CHECK: %v4uint = OpTypeVector %uint 4
-; CHECK: %uint_5 = OpConstant %uint 5
-; CHECK: %uint_7 = OpConstant %uint 7
-; CHECK: %uint_8 = OpConstant %uint 8
-; CHECK: %uint_9 = OpConstant %uint 9
-; CHECK: %uint_109 = OpConstant %uint 109
-; CHECK: %125 = OpConstantNull %v4float
+;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
+;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
+;CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float
 )";
-  // clang-format on
 
   const std::string func1 = R"(
 %MainPs = OpFunction %void None %4
@@ -1572,38 +1029,43 @@
 %43 = OpAccessChain %_ptr_Function_v2float %i %int_0
 %44 = OpLoad %v2float %43
 %45 = OpImageSampleImplicitLod %v4float %41 %44
-; CHECK-NOT: %45 = OpImageSampleImplicitLod %v4float %41 %44
-; CHECK: OpNoLine
-; CHECK: %62 = OpULessThan %bool %50 %uint_128
-; CHECK: OpSelectionMerge %63 None
-; CHECK: OpBranchConditional %62 %64 %65
-; CHECK: %64 = OpLabel
-; CHECK: %66 = OpLoad %27 %51
-; CHECK: %67 = OpSampledImage %37 %66 %53
-; CHECK: OpLine %5 24 0
-; CHECK: %68 = OpImageSampleImplicitLod %v4float %67 %56
-; CHECK: OpNoLine
-; CHECK: OpBranch %63
-; CHECK: %65 = OpLabel
-; CHECK: %124 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_109 %uint_0 %50 %uint_128
-; CHECK: OpBranch %63
-; CHECK: %63 = OpLabel
-; CHECK: %126 = OpPhi %v4float %68 %64 %125 %65
-; CHECK: OpLine %5 24 0
+;CHECK-NOT: %45 = OpImageSampleImplicitLod %v4float %41 %44
+;CHECK: {{%\w+}} = OpLoad %v2float {{%\w+}}
+;CHECK: OpNoLine
+;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord
+;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}}
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_128 {{%\w+}} %uint_1 %uint_2 {{%\w+}} %uint_0
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpLoad %27 {{%\w+}}
+;CHECK: {{%\w+}} = OpSampledImage %37 {{%\w+}} {{%\w+}}
+;CHECK: OpLine %5 24 0
+;CHECK: {{%\w+}} = OpImageSampleImplicitLod %v4float {{%\w+}} {{%\w+}}
+;CHECK: OpNoLine
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: [[phi_result:%\w+]] = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}}
 %47 = OpAccessChain %_ptr_Function_v4float %ps_output %int_0
 OpStore %47 %45
+;CHECK-NOT: OpStore %47 %45
+;CHECK: [[store_loc:%\w+]] = OpAccessChain %_ptr_Function_v4float %ps_output %int_0
+;CHECK: OpStore [[store_loc]] [[phi_result]]
 OpLine %1 25 0
 %48 = OpLoad %PS_OUTPUT %ps_output
 OpReturnValue %48
 OpFunctionEnd
 )";
-
-  const std::string output_func = kStreamWrite4Frag;
+  // clang-format on
 
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   SinglePassRunAndMatch<InstBindlessCheckPass>(
-      defs + func1 + func2 + output_func, true, 7u, 23u, false, false, false,
-      false, false);
+      defs + kImportStub + func1 + func2, true, 23u);
 }
 
 TEST_F(InstBindlessTest, RuntimeArray) {
@@ -1615,12 +1077,12 @@
   const std::string defs = R"(
 OpCapability Shader
 OpCapability RuntimeDescriptorArray
+;CHECK: OpCapability Linkage
 OpExtension "SPV_EXT_descriptor_indexing"
-; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
 %1 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel Logical GLSL450
 OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor
-; CHECK: OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord
+;CHECK: OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord
 OpExecutionMode %MainPs OriginUpperLeft
 OpSource HLSL 500
 OpName %MainPs "MainPs"
@@ -1636,12 +1098,11 @@
 OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0
 OpDecorate %PerViewConstantBuffer_t Block
 OpDecorate %g_sAniso DescriptorSet 1
-OpDecorate %g_sAniso Binding 0
+OpDecorate %g_sAniso Binding 3
 OpDecorate %i_vTextureCoords Location 0
 OpDecorate %_entryPointOutput_vColor Location 0
-; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
-)" + kInputDecorations + kOutputDecorations + R"(
-; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
+)" + kImportDeco + R"(
+;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
 %void = OpTypeVoid
 %3 = OpTypeFunction %void
 %float = OpTypeFloat 32
@@ -1668,31 +1129,10 @@
 %i_vTextureCoords = OpVariable %_ptr_Input_v2float Input
 %_ptr_Output_v4float = OpTypePointer Output %v4float
 %_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output
-; CHECK: %uint_0 = OpConstant %uint 0
-; CHECK: %uint_2 = OpConstant %uint 2
-; CHECK: %41 = OpTypeFunction %uint %uint %uint
-; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
-)" + kInputGlobals + R"(
-; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-; CHECK: %bool = OpTypeBool
-; CHECK: %65 = OpTypeFunction %void %uint %uint %uint %uint
-)" + kOutputGlobals + R"(
-; CHECK: %uint_10 = OpConstant %uint 10
-; CHECK: %uint_4 = OpConstant %uint 4
-; CHECK: %uint_23 = OpConstant %uint 23
-; CHECK: %uint_3 = OpConstant %uint 3
-; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
-; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
-; CHECK: %v4uint = OpTypeVector %uint 4
-; CHECK: %uint_5 = OpConstant %uint 5
-; CHECK: %uint_7 = OpConstant %uint 7
-; CHECK: %uint_8 = OpConstant %uint 8
-; CHECK: %uint_9 = OpConstant %uint 9
-; CHECK: %uint_59 = OpConstant %uint 59
-; CHECK: %116 = OpConstantNull %v4float
-; CHECK: %119 = OpTypeFunction %uint %uint %uint %uint %uint
+;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
+;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
+;CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float
 )";
-  // clang-format on
 
   const std::string main_func = R"(
 %MainPs = OpFunction %void None %3
@@ -1706,46 +1146,34 @@
 %68 = OpSampledImage %39 %66 %67
 %71 = OpImageSampleImplicitLod %v4float %68 %53
 OpStore %_entryPointOutput_vColor %71
-; CHECK-NOT: %71 = OpImageSampleImplicitLod %v4float %68 %53
-; CHECK-NOT: OpStore %_entryPointOutput_vColor %71
-; CHECK: %55 = OpFunctionCall %uint %inst_bindless_direct_read_2 %uint_2 %uint_2
-; CHECK: %57 = OpULessThan %bool %32 %55
-; CHECK: OpSelectionMerge %58 None
-; CHECK: OpBranchConditional %57 %59 %60
-; CHECK: %59 = OpLabel
-; CHECK: %61 = OpLoad %16 %33
-; CHECK: %62 = OpSampledImage %26 %61 %35
-; CHECK: %136 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_1 %uint_2 %32
-; CHECK: %137 = OpULessThan %bool %uint_0 %136
-; CHECK: OpSelectionMerge %138 None
-; CHECK: OpBranchConditional %137 %139 %140
-; CHECK: %139 = OpLabel
-; CHECK: %141 = OpLoad %16 %33
-; CHECK: %142 = OpSampledImage %26 %141 %35
-; CHECK: %143 = OpImageSampleImplicitLod %v4float %142 %30
-; CHECK: OpBranch %138
-; CHECK: %140 = OpLabel
-; CHECK: %144 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_59 %uint_1 %32 %uint_0
-; CHECK: OpBranch %138
-; CHECK: %138 = OpLabel
-; CHECK: %145 = OpPhi %v4float %143 %139 %116 %140
-; CHECK: OpBranch %58
-; CHECK: %60 = OpLabel
-; CHECK: %115 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_59 %uint_0 %32 %55
-; CHECK: OpBranch %58
-; CHECK: %58 = OpLabel
-; CHECK: %117 = OpPhi %v4float %145 %138 %116 %60
-; CHECK: OpStore %_entryPointOutput_vColor %117
+;CHECK-NOT: %71 = OpImageSampleImplicitLod %v4float %68 %53
+;CHECK-NOT: OpStore %_entryPointOutput_vColor %71
+;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord
+;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}}
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0
+;CHECK: [[check_result:%\w+]] = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_60 {{%\w+}} %uint_1 %uint_2 %32 %uint_0
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpLoad %16 %33
+;CHECK: {{%\w+}} = OpSampledImage %26 {{%\w+}} %35
+;CHECK: {{%\w+}} = OpImageSampleImplicitLod %v4float {{%\w+}} %30
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: [[phi_result_1:%\w+]] = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}}
+;CHECK: OpStore %_entryPointOutput_vColor [[phi_result_1]]
 OpReturn
 OpFunctionEnd
 )";
-
-  const std::string new_funcs = kDirectRead2 + kStreamWrite4Frag + kDirectRead4;
+  // clang-format on
 
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs,
-                                               true, 7u, 23u, true, true, false,
-                                               false, false);
+  SinglePassRunAndMatch<InstBindlessCheckPass>(defs + kImportStub + main_func,
+                                               true, 23u);
 }
 
 TEST_F(InstBindlessTest, InstrumentInitCheckOnScalarDescriptor) {
@@ -1759,11 +1187,11 @@
   // clang-format off
   const std::string defs = R"(
 OpCapability Shader
-; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
+;CHECK: OpCapability Linkage
 %1 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel Logical GLSL450
 OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor
-; CHECK: OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord
+;CHECK: OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord
 OpExecutionMode %MainPs OriginUpperLeft
 OpSource HLSL 500
 OpName %MainPs "MainPs"
@@ -1771,15 +1199,14 @@
 OpName %g_sAniso "g_sAniso"
 OpName %i_vTextureCoords "i.vTextureCoords"
 OpName %_entryPointOutput_vColor "@entryPointOutput.vColor"
-OpDecorate %g_tColor DescriptorSet 0
-OpDecorate %g_tColor Binding 0
-OpDecorate %g_sAniso DescriptorSet 0
-OpDecorate %g_sAniso Binding 0
+OpDecorate %g_tColor DescriptorSet 1
+OpDecorate %g_tColor Binding 2
+OpDecorate %g_sAniso DescriptorSet 1
+OpDecorate %g_sAniso Binding 2
 OpDecorate %i_vTextureCoords Location 0
 OpDecorate %_entryPointOutput_vColor Location 0
-; check: OpDecorate %_runtimearr_uint ArrayStride 4
-)" + kInputDecorations + kOutputDecorations + R"(
-; check: OpDecorate %gl_FragCoord BuiltIn FragCoord
+)" + kImportDeco + R"(
+;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
 %void = OpTypeVoid
 %8 = OpTypeFunction %void
 %float = OpTypeFloat 32
@@ -1796,32 +1223,8 @@
 %i_vTextureCoords = OpVariable %_ptr_Input_v2float Input
 %_ptr_Output_v4float = OpTypePointer Output %v4float
 %_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output
-; CHECK: %uint = OpTypeInt 32 0
-; CHECK: %uint_0 = OpConstant %uint 0
-; CHECK: %28 = OpTypeFunction %uint %uint %uint %uint %uint
-; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
-)" + kInputGlobals + R"(
-; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-; CHECK: %bool = OpTypeBool
-; CHECK: %uint_1 = OpConstant %uint 1
-; CHECK: %61 = OpTypeFunction %void %uint %uint %uint %uint
-)" + kOutputGlobals + R"(
-; CHECK: %uint_10 = OpConstant %uint 10
-; CHECK: %uint_4 = OpConstant %uint 4
-; CHECK: %uint_23 = OpConstant %uint 23
-; CHECK: %uint_2 = OpConstant %uint 2
-; CHECK: %uint_3 = OpConstant %uint 3
-; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
-; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
-; CHECK: %v4uint = OpTypeVector %uint 4
-; CHECK: %uint_5 = OpConstant %uint 5
-; CHECK: %uint_7 = OpConstant %uint 7
-; CHECK: %uint_8 = OpConstant %uint 8
-; CHECK: %uint_9 = OpConstant %uint 9
-; CHECK: %uint_39 = OpConstant %uint 39
-; CHECK: %113 = OpConstantNull %v4float
+;CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float
 )";
-  // clang-format on
 
   const std::string main_func = R"(
 %MainPs = OpFunction %void None %8
@@ -1832,51 +1235,43 @@
 %23 = OpSampledImage %16 %21 %22
 %24 = OpImageSampleImplicitLod %v4float %23 %20
 OpStore %_entryPointOutput_vColor %24
-; CHECK-NOT: %24 = OpImageSampleImplicitLod %v4float %23 %20
-; CHECK-NOT: OpStore %_entryPointOutput_vColor %24
-; CHECK: %50 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0
-; CHECK: %52 = OpULessThan %bool %uint_0 %50
-; CHECK: OpSelectionMerge %54 None
-; CHECK: OpBranchConditional %52 %55 %56
-; CHECK: %55 = OpLabel
-; CHECK: %57 = OpLoad %12 %g_tColor
-; CHECK: %58 = OpSampledImage %16 %57 %22
-; CHECK: %59 = OpImageSampleImplicitLod %v4float %58 %20
-; CHECK: OpBranch %54
-; CHECK: %56 = OpLabel
-; CHECK: %112 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_39 %uint_1 %uint_0 %uint_0
-; CHECK: OpBranch %54
-; CHECK: %54 = OpLabel
-; CHECK: %114 = OpPhi %v4float %59 %55 %113 %56
-; CHECK: OpStore %_entryPointOutput_vColor %114
+;CHECK-NOT: %24 = OpImageSampleImplicitLod %v4float %23 %20
+;CHECK-NOT: OpStore %_entryPointOutput_vColor %24
+;CHECK: [[check_result:%\w+]] = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_40 {{%\w+}} %uint_1 %uint_2 %uint_0 %uint_0
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional [[check_result]] {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpLoad %12 %g_tColor
+;CHECK: {{%\w+}} = OpSampledImage %16 {{%\w+}} %22
+;CHECK: {{%\w+}} = OpImageSampleImplicitLod %v4float {{%\w+}} %20
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: [[phi_result:%\w+]] = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}}
+;CHECK: OpStore %_entryPointOutput_vColor [[phi_result]]
 OpReturn
 OpFunctionEnd
 )";
-
-  const std::string new_funcs = kDirectRead4 + kStreamWrite4Frag;
+  // clang-format on
 
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs,
-                                               true, 7u, 23u, true, true, false,
-                                               false, false);
+  SinglePassRunAndMatch<InstBindlessCheckPass>(defs + kImportStub + main_func,
+                                               true, 23u);
 }
 
 TEST_F(InstBindlessTest, SPV14AddToEntryPoint) {
   const std::string text = R"(
-; CHECK: OpEntryPoint Fragment {{%\w+}} "foo" {{%\w+}} {{%\w+}} {{%\w+}} [[v1:%\w+]] [[v2:%\w+]]
-; CHECK: OpDecorate [[v1]] DescriptorSet 7
-; CHECK: OpDecorate [[v2]] DescriptorSet 7
-; CHECK: [[v1]] = OpVariable {{%\w+}} StorageBuffer
-; CHECK: [[v2]] = OpVariable {{%\w+}} StorageBuffer
 OpCapability Shader
 OpExtension "SPV_EXT_descriptor_indexing"
 OpMemoryModel Logical GLSL450
 OpEntryPoint Fragment %foo "foo" %gid %image_var %sampler_var
+;CHECK: OpEntryPoint Fragment {{%\w+}} "foo" {{%\w+}} {{%\w+}} {{%\w+}} %gl_FragCoord
 OpExecutionMode %foo OriginUpperLeft
-OpDecorate %image_var DescriptorSet 0
-OpDecorate %image_var Binding 0
-OpDecorate %sampler_var DescriptorSet 0
-OpDecorate %sampler_var Binding 1
+OpDecorate %image_var DescriptorSet 4
+OpDecorate %image_var Binding 1
+OpDecorate %sampler_var DescriptorSet 4
+OpDecorate %sampler_var Binding 2
 OpDecorate %gid DescriptorSet 0
 OpDecorate %gid Binding 2
 OpDecorate %struct Block
@@ -1914,30 +1309,26 @@
 )";
 
   SetTargetEnv(SPV_ENV_VULKAN_1_1_SPIRV_1_4);
-  SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 7u, 23u, true, true,
-                                               false, false, false);
+  SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 23u);
 }
 
 TEST_F(InstBindlessTest, SPV14AddToEntryPoints) {
   const std::string text = R"(
-; CHECK: OpEntryPoint Fragment {{%\w+}} "foo" {{%\w+}} {{%\w+}} {{%\w+}} [[v1:%\w+]] [[v2:%\w+]]
-; CHECK: OpEntryPoint Fragment {{%\w+}} "bar" {{%\w+}} {{%\w+}} {{%\w+}} [[v1:%\w+]] [[v2:%\w+]]
-; CHECK: OpDecorate [[v1]] DescriptorSet 7
-; CHECK: OpDecorate [[v2]] DescriptorSet 7
-; CHECK: [[v1]] = OpVariable {{%\w+}} StorageBuffer
-; CHECK: [[v2]] = OpVariable {{%\w+}} StorageBuffer
 OpCapability Shader
+;CHECK: OpCapability Linkage
 OpExtension "SPV_EXT_descriptor_indexing"
 OpMemoryModel Logical GLSL450
 OpEntryPoint Fragment %foo "foo" %gid %image_var %sampler_var
+;CHECK: OpEntryPoint Fragment {{%\w+}} "foo" {{%\w+}} {{%\w+}} {{%\w+}} %gl_FragCoord
 OpEntryPoint Fragment %foo "bar" %gid %image_var %sampler_var
+;CHECK: OpEntryPoint Fragment {{%\w+}} "bar" {{%\w+}} {{%\w+}} {{%\w+}} %gl_FragCoord
 OpExecutionMode %foo OriginUpperLeft
-OpDecorate %image_var DescriptorSet 0
-OpDecorate %image_var Binding 0
-OpDecorate %sampler_var DescriptorSet 0
-OpDecorate %sampler_var Binding 1
-OpDecorate %gid DescriptorSet 0
-OpDecorate %gid Binding 2
+OpDecorate %image_var DescriptorSet 3
+OpDecorate %image_var Binding 2
+OpDecorate %sampler_var DescriptorSet 3
+OpDecorate %sampler_var Binding 3
+OpDecorate %gid DescriptorSet 3
+OpDecorate %gid Binding 4
 OpDecorate %struct Block
 OpMemberDecorate %struct 0 Offset 0
 %void = OpTypeVoid
@@ -1973,8 +1364,7 @@
 )";
 
   SetTargetEnv(SPV_ENV_VULKAN_1_1_SPIRV_1_4);
-  SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 7u, 23u, true, true,
-                                               false, false, false);
+  SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 23u);
 }
 
 TEST_F(InstBindlessTest, InstBoundsAndInitLoadUnsizedUBOArray) {
@@ -1984,7 +1374,7 @@
   // layout(location=0) in nonuniformEXT flat int nu_ii;
   // layout(location=0) out float b;
   //
-  // layout(binding=3)  uniform uname { float a; }  uniformBuffer[];
+  // layout(set = 6, binding=3)  uniform uname { float a; }  uniformBuffer[];
   //
   // void main()
   // {
@@ -1997,12 +1387,12 @@
 OpCapability ShaderNonUniform
 OpCapability RuntimeDescriptorArray
 OpCapability UniformBufferArrayNonUniformIndexing
+;CHECK: OpCapability Linkage
 OpExtension "SPV_EXT_descriptor_indexing"
-; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
 %1 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel Logical GLSL450
 OpEntryPoint Fragment %main "main" %b %nu_ii
-; CHECK: OpEntryPoint Fragment %main "main" %b %nu_ii %gl_FragCoord
+;CHECK: OpEntryPoint Fragment %main "main" %b %nu_ii %gl_FragCoord
 OpExecutionMode %main OriginUpperLeft
 OpSource GLSL 450
 OpSourceExtension "GL_EXT_nonuniform_qualifier"
@@ -2015,19 +1405,17 @@
 OpDecorate %b Location 0
 OpMemberDecorate %uname 0 Offset 0
 OpDecorate %uname Block
-OpDecorate %uniformBuffer DescriptorSet 0
+OpDecorate %uniformBuffer DescriptorSet 6
 OpDecorate %uniformBuffer Binding 3
 OpDecorate %nu_ii Flat
 OpDecorate %nu_ii Location 0
 OpDecorate %nu_ii NonUniform
 OpDecorate %16 NonUniform
 OpDecorate %20 NonUniform
-; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
-)" + kInputDecorations + R"(
-; CHECK: OpDecorate %130 NonUniform
-)" + kOutputDecorations + R"(
-; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
-; CHECK: OpDecorate %127 NonUniform
+;CHECK: OpDecorate {{%\w+}} NonUniform
+)" + kImportDeco + R"(
+;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
+;CHECK: OpDecorate {{%\w+}} NonUniform
 %void = OpTypeVoid
 %3 = OpTypeFunction %void
 %float = OpTypeFloat 32
@@ -2042,34 +1430,12 @@
 %nu_ii = OpVariable %_ptr_Input_int Input
 %int_0 = OpConstant %int 0
 %_ptr_Uniform_float = OpTypePointer Uniform %float
-; CHECK: %uint = OpTypeInt 32 0
-; CHECK: %uint_0 = OpConstant %uint 0
-; CHECK: %uint_1 = OpConstant %uint 1
-; CHECK: %uint_3 = OpConstant %uint 3
-; CHECK: %26 = OpTypeFunction %uint %uint %uint
-; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
-)" + kInputGlobals + R"(
-; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-; CHECK: %bool = OpTypeBool
-; CHECK: %49 = OpTypeFunction %void %uint %uint %uint %uint
-)" + kOutputGlobals + R"(
-; CHECK: %uint_10 = OpConstant %uint 10
-; CHECK: %uint_4 = OpConstant %uint 4
-; CHECK: %uint_23 = OpConstant %uint 23
-; CHECK: %uint_2 = OpConstant %uint 2
-; CHECK: %v4float = OpTypeVector %float 4
-; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
-; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
-; CHECK: %v4uint = OpTypeVector %uint 4
-; CHECK: %uint_5 = OpConstant %uint 5
-; CHECK: %uint_7 = OpConstant %uint 7
-; CHECK: %uint_8 = OpConstant %uint 8
-; CHECK: %uint_9 = OpConstant %uint 9
-; CHECK: %uint_45 = OpConstant %uint 45
-; CHECK: %101 = OpConstantNull %float
-; CHECK: %105 = OpTypeFunction %uint %uint %uint %uint %uint
+;CHECK: %v4uint = OpTypeVector %uint 4
+;CHECK: %v4float = OpTypeVector %float 4
+;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
+;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
+;CHECK: [[null_float:%\w+]] = OpConstantNull %float
 )";
-  // clang-format on
 
   const std::string main_func = R"(
 %main = OpFunction %void None %3
@@ -2078,45 +1444,33 @@
 %19 = OpAccessChain %_ptr_Uniform_float %uniformBuffer %16 %int_0
 %20 = OpLoad %float %19
 OpStore %b %20
-; CHECK-NOT: %20 = OpLoad %float %19
-; CHECK-NOT: OpStore %b %20
-; CHECK: %40 = OpFunctionCall %uint %inst_bindless_direct_read_2 %uint_1 %uint_3
-; CHECK: %42 = OpULessThan %bool %7 %40
-; CHECK: OpSelectionMerge %43 None
-; CHECK: OpBranchConditional %42 %44 %45
-; CHECK: %44 = OpLabel
-; CHECK: %103 = OpBitcast %uint %7
-; CHECK: %122 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_3 %103
-; CHECK: %123 = OpULessThan %bool %uint_0 %122
-; CHECK: OpSelectionMerge %124 None
-; CHECK: OpBranchConditional %123 %125 %126
-; CHECK: %125 = OpLabel
-; CHECK: %127 = OpLoad %float %20
-; CHECK: OpBranch %124
-; CHECK: %126 = OpLabel
-; CHECK: %128 = OpBitcast %uint %7
-; CHECK: %129 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_45 %uint_1 %128 %uint_0
-; CHECK: OpBranch %124
-; CHECK: %124 = OpLabel
-; CHECK: %130 = OpPhi %float %127 %125 %101 %126
-; CHECK: OpBranch %43
-; CHECK: %45 = OpLabel
-; CHECK: %47 = OpBitcast %uint %7
-; CHECK: %100 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_45 %uint_0 %47 %40
-; CHECK: OpBranch %43
-; CHECK: %43 = OpLabel
-; CHECK: %102 = OpPhi %float %130 %124 %101 %45
-; CHECK: OpStore %b %102
+;CHECK-NOT: %20 = OpLoad %float %19
+;CHECK-NOT: OpStore %b %20
+;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord
+;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}}
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0
+;CHECK: {{%\w+}} = OpBitcast %uint %7
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_46 {{%\w+}} %uint_6 %uint_3 {{%\w+}} {{%\w+}}
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpLoad %float %20
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: [[phi_result:%\w+]] = OpPhi %float {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: OpStore %b [[phi_result]]
 OpReturn
 OpFunctionEnd
 )";
-
-  const std::string new_funcs = kDirectRead2 + kStreamWrite4Frag + kDirectRead4;
+  // clang-format on
 
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs,
-                                               true, 7u, 23u, true, true, false,
-                                               false, false);
+  SinglePassRunAndMatch<InstBindlessCheckPass>(defs + kImportStub + main_func,
+                                               true, 23u);
 }
 
 TEST_F(InstBindlessTest, InstBoundsAndInitLoadUnsizedSSBOArrayDeprecated) {
@@ -2126,7 +1480,7 @@
   // layout(location=0) in nonuniformEXT flat int nu_ii;
   // layout(location=0) out float b;
   //
-  // layout(binding=3)  buffer bname { float b; }  storageBuffer[];
+  // layout(set = 7, binding=3)  buffer bname { float b; }  storageBuffer[];
   //
   // void main()
   // {
@@ -2139,12 +1493,12 @@
 OpCapability ShaderNonUniform
 OpCapability RuntimeDescriptorArray
 OpCapability StorageBufferArrayNonUniformIndexing
+;CHECK: OpCapability Linkage
 OpExtension "SPV_EXT_descriptor_indexing"
-; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
 %1 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel Logical GLSL450
 OpEntryPoint Fragment %main "main" %b %nu_ii
-; CHECK: OpEntryPoint Fragment %main "main" %b %nu_ii %gl_FragCoord
+;CHECK: OpEntryPoint Fragment %main "main" %b %nu_ii %gl_FragCoord
 OpExecutionMode %main OriginUpperLeft
 OpSource GLSL 450
 OpSourceExtension "GL_EXT_nonuniform_qualifier"
@@ -2157,19 +1511,15 @@
 OpDecorate %b Location 0
 OpMemberDecorate %bname 0 Offset 0
 OpDecorate %bname Block
-OpDecorate %storageBuffer DescriptorSet 0
+OpDecorate %storageBuffer DescriptorSet 7
 OpDecorate %storageBuffer Binding 3
 OpDecorate %nu_ii Flat
 OpDecorate %nu_ii Location 0
 OpDecorate %nu_ii NonUniform
 OpDecorate %16 NonUniform
 OpDecorate %20 NonUniform
-; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
-)" + kInputDecorations + R"(
-; CHECK: OpDecorate %130 NonUniform
-)" + kOutputDecorations + R"(
-; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
-; CHECK: OpDecorate %127 NonUniform
+)" + kImportDeco + R"(
+;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
 %void = OpTypeVoid
 %3 = OpTypeFunction %void
 %float = OpTypeFloat 32
@@ -2184,34 +1534,13 @@
 %nu_ii = OpVariable %_ptr_Input_int Input
 %int_0 = OpConstant %int 0
 %_ptr_StorageBuffer_float = OpTypePointer StorageBuffer %float
-; CHECK: %uint = OpTypeInt 32 0
-; CHECK: %uint_0 = OpConstant %uint 0
-; CHECK: %uint_1 = OpConstant %uint 1
-; CHECK: %uint_3 = OpConstant %uint 3
-; CHECK: %26 = OpTypeFunction %uint %uint %uint
-; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
-)" + kInputGlobals + R"(
-; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-; CHECK: %bool = OpTypeBool
-; CHECK: %49 = OpTypeFunction %void %uint %uint %uint %uint
-)" + kOutputGlobals + R"(
-; CHECK: %uint_10 = OpConstant %uint 10
-; CHECK: %uint_4 = OpConstant %uint 4
-; CHECK: %uint_23 = OpConstant %uint 23
-; CHECK: %uint_2 = OpConstant %uint 2
-; CHECK: %v4float = OpTypeVector %float 4
-; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
-; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
-; CHECK: %v4uint = OpTypeVector %uint 4
-; CHECK: %uint_5 = OpConstant %uint 5
-; CHECK: %uint_7 = OpConstant %uint 7
-; CHECK: %uint_8 = OpConstant %uint 8
-; CHECK: %uint_9 = OpConstant %uint 9
-; CHECK: %uint_45 = OpConstant %uint 45
-; CHECK: %101 = OpConstantNull %float
-; CHECK: %105 = OpTypeFunction %uint %uint %uint %uint %uint
+;CHECK: %uint = OpTypeInt 32 0
+;CHECK: %v4uint = OpTypeVector %uint 4
+;CHECK: %v4float = OpTypeVector %float 4
+;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
+;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
+;CHECK: [[null_float:%\w+]] = OpConstantNull %float
 )";
-  // clang-format on
 
   const std::string main_func = R"(
 %main = OpFunction %void None %3
@@ -2220,45 +1549,33 @@
 %19 = OpAccessChain %_ptr_StorageBuffer_float %storageBuffer %16 %int_0
 %20 = OpLoad %float %19
 OpStore %b %20
-; CHECK-NOT: %20 = OpLoad %float %19
-; CHECK-NOT: OpStore %b %20
-; CHECK: %40 = OpFunctionCall %uint %inst_bindless_direct_read_2 %uint_1 %uint_3
-; CHECK: %42 = OpULessThan %bool %7 %40
-; CHECK: OpSelectionMerge %43 None
-; CHECK: OpBranchConditional %42 %44 %45
-; CHECK: %44 = OpLabel
-; CHECK: %103 = OpBitcast %uint %7
-; CHECK: %122 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_3 %103
-; CHECK: %123 = OpULessThan %bool %uint_0 %122
-; CHECK: OpSelectionMerge %124 None
-; CHECK: OpBranchConditional %123 %125 %126
-; CHECK: %125 = OpLabel
-; CHECK: %127 = OpLoad %float %20
-; CHECK: OpBranch %124
-; CHECK: %126 = OpLabel
-; CHECK: %128 = OpBitcast %uint %7
-; CHECK: %129 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_45 %uint_1 %128 %uint_0
-; CHECK: OpBranch %124
-; CHECK: %124 = OpLabel
-; CHECK: %130 = OpPhi %float %127 %125 %101 %126
-; CHECK: OpBranch %43
-; CHECK: %45 = OpLabel
-; CHECK: %47 = OpBitcast %uint %7
-; CHECK: %100 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_45 %uint_0 %47 %40
-; CHECK: OpBranch %43
-; CHECK: %43 = OpLabel
-; CHECK: %102 = OpPhi %float %130 %124 %101 %45
-; CHECK: OpStore %b %102
+;CHECK-NOT: %20 = OpLoad %float %19
+;CHECK-NOT: OpStore %b %20
+;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord
+;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}}
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0
+;CHECK: {{%\w+}} = OpBitcast %uint %7
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_46 {{%\w+}} %uint_7 %uint_3 {{%\w+}} {{%\w+}}
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpLoad %float %20
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: [[phi_result:%\w+]] = OpPhi %float {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: OpStore %b [[phi_result]]
 OpReturn
 OpFunctionEnd
 )";
-
-  const std::string new_funcs = kDirectRead2 + kStreamWrite4Frag + kDirectRead4;
+  // clang-format on
 
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs,
-                                               true, 7u, 23u, true, true, false,
-                                               false, false);
+  SinglePassRunAndMatch<InstBindlessCheckPass>(defs + kImportStub + main_func,
+                                               true, 23u);
 }
 
 TEST_F(InstBindlessTest, InstBoundsAndInitLoadUnsizedSSBOArray) {
@@ -2270,12 +1587,12 @@
 OpCapability ShaderNonUniform
 OpCapability RuntimeDescriptorArray
 OpCapability StorageBufferArrayNonUniformIndexing
+;CHECK: OpCapability Linkage
 OpExtension "SPV_EXT_descriptor_indexing"
-; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
 %1 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel Logical GLSL450
 OpEntryPoint Fragment %main "main" %b %nu_ii
-; CHECK: OpEntryPoint Fragment %main "main" %b %nu_ii %gl_FragCoord
+;CHECK: OpEntryPoint Fragment %main "main" %b %nu_ii %gl_FragCoord
 OpExecutionMode %main OriginUpperLeft
 OpSource GLSL 450
 OpSourceExtension "GL_EXT_nonuniform_qualifier"
@@ -2295,12 +1612,10 @@
 OpDecorate %nu_ii NonUniform
 OpDecorate %16 NonUniform
 OpDecorate %20 NonUniform
-; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
-)" + kInputDecorations + R"(
-; CHECK: OpDecorate %130 NonUniform
-)" + kOutputDecorations + R"(
-; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
-; CHECK: OpDecorate %127 NonUniform
+;CHECK: OpDecorate {{%\w+}} NonUniform
+)" + kImportDeco + R"(
+;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
+;CHECK: OpDecorate {{%\w+}} NonUniform
 %void = OpTypeVoid
 %3 = OpTypeFunction %void
 %float = OpTypeFloat 32
@@ -2315,34 +1630,12 @@
 %nu_ii = OpVariable %_ptr_Input_int Input
 %int_0 = OpConstant %int 0
 %_ptr_StorageBuffer_float = OpTypePointer StorageBuffer %float
-; CHECK: %uint = OpTypeInt 32 0
-; CHECK: %uint_0 = OpConstant %uint 0
-; CHECK: %uint_1 = OpConstant %uint 1
-; CHECK: %uint_3 = OpConstant %uint 3
-; CHECK: %26 = OpTypeFunction %uint %uint %uint
-; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
-)" + kInputGlobals + R"(
-; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-; CHECK: %bool = OpTypeBool
-; CHECK: %49 = OpTypeFunction %void %uint %uint %uint %uint
-)" + kOutputGlobals + R"(
-; CHECK: %uint_10 = OpConstant %uint 10
-; CHECK: %uint_4 = OpConstant %uint 4
-; CHECK: %uint_23 = OpConstant %uint 23
-; CHECK: %uint_2 = OpConstant %uint 2
-; CHECK: %v4float = OpTypeVector %float 4
-; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
-; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
-; CHECK: %v4uint = OpTypeVector %uint 4
-; CHECK: %uint_5 = OpConstant %uint 5
-; CHECK: %uint_7 = OpConstant %uint 7
-; CHECK: %uint_8 = OpConstant %uint 8
-; CHECK: %uint_9 = OpConstant %uint 9
-; CHECK: %uint_45 = OpConstant %uint 45
-; CHECK: %101 = OpConstantNull %float
-; CHECK: %105 = OpTypeFunction %uint %uint %uint %uint %uint
+;CHECK: %v4uint = OpTypeVector %uint 4
+;CHECK: %v4float = OpTypeVector %float 4
+;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
+;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
+;CHECK: [[null_float:%\w+]] = OpConstantNull %float
 )";
-  // clang-format on
 
   const std::string main_func = R"(
 %main = OpFunction %void None %3
@@ -2351,45 +1644,33 @@
 %19 = OpAccessChain %_ptr_StorageBuffer_float %storageBuffer %16 %int_0
 %20 = OpLoad %float %19
 OpStore %b %20
-; CHECK-NOT: %20 = OpLoad %float %19
-; CHECK-NOT: OpStore %b %20
-; CHECK: %40 = OpFunctionCall %uint %inst_bindless_direct_read_2 %uint_1 %uint_3
-; CHECK: %42 = OpULessThan %bool %7 %40
-; CHECK: OpSelectionMerge %43 None
-; CHECK: OpBranchConditional %42 %44 %45
-; CHECK: %44 = OpLabel
-; CHECK: %103 = OpBitcast %uint %7
-; CHECK: %122 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_3 %103
-; CHECK: %123 = OpULessThan %bool %uint_0 %122
-; CHECK: OpSelectionMerge %124 None
-; CHECK: OpBranchConditional %123 %125 %126
-; CHECK: %125 = OpLabel
-; CHECK: %127 = OpLoad %float %20
-; CHECK: OpBranch %124
-; CHECK: %126 = OpLabel
-; CHECK: %128 = OpBitcast %uint %7
-; CHECK: %129 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_45 %uint_1 %128 %uint_0
-; CHECK: OpBranch %124
-; CHECK: %124 = OpLabel
-; CHECK: %130 = OpPhi %float %127 %125 %101 %126
-; CHECK: OpBranch %43
-; CHECK: %45 = OpLabel
-; CHECK: %47 = OpBitcast %uint %7
-; CHECK: %100 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_45 %uint_0 %47 %40
-; CHECK: OpBranch %43
-; CHECK: %43 = OpLabel
-; CHECK: %102 = OpPhi %float %130 %124 %101 %45
-; CHECK: OpStore %b %102
+;CHECK-NOT: %20 = OpLoad %float %19
+;CHECK-NOT: OpStore %b %20
+;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord
+;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}}
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0
+;CHECK: {{%\w+}} = OpBitcast %uint %7
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_46 {{%\w+}} %uint_0 %uint_3 {{%\w+}} {{%\w+}}
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpLoad %float %20
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpPhi %float {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: OpStore %b {{%\w+}}
 OpReturn
 OpFunctionEnd
 )";
-
-  const std::string new_funcs = kDirectRead2 + kStreamWrite4Frag + kDirectRead4;
+  // clang-format on
 
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs,
-                                               true, 7u, 23u, true, true, false,
-                                               false, false);
+  SinglePassRunAndMatch<InstBindlessCheckPass>(defs + kImportStub + main_func,
+                                               true, 23u);
 }
 
 TEST_F(InstBindlessTest, InstInitLoadUBOScalar) {
@@ -2397,7 +1678,7 @@
   // #extension GL_EXT_nonuniform_qualifier : enable
   //
   // layout(location=0) out float b;
-  // layout(binding=3)  uniform uname { float a; }  uniformBuffer;
+  // layout(set=7, binding=3)  uniform uname { float a; }  uniformBuffer;
   //
   // void main()
   // {
@@ -2407,12 +1688,12 @@
   // clang-format off
   const std::string defs = R"(
 OpCapability Shader
+;CHECK: OpCapability Linkage
 OpExtension "SPV_EXT_descriptor_indexing"
-; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
 %1 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel Logical GLSL450
 OpEntryPoint Fragment %main "main" %b
-; CHECK: OpEntryPoint Fragment %main "main" %b %gl_FragCoord
+;CHECK: OpEntryPoint Fragment %main "main" %b %gl_FragCoord
 OpExecutionMode %main OriginUpperLeft
 OpSource GLSL 450
 OpSourceExtension "GL_EXT_nonuniform_qualifier"
@@ -2424,11 +1705,10 @@
 OpDecorate %b Location 0
 OpMemberDecorate %uname 0 Offset 0
 OpDecorate %uname Block
-OpDecorate %uniformBuffer DescriptorSet 0
+OpDecorate %uniformBuffer DescriptorSet 7
 OpDecorate %uniformBuffer Binding 3
-; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
-)" + kInputDecorations + kOutputDecorations + R"(
-; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
+)" + kImportDeco + R"(
+;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
 %void = OpTypeVoid
 %3 = OpTypeFunction %void
 %float = OpTypeFloat 32
@@ -2440,36 +1720,15 @@
 %int = OpTypeInt 32 1
 %int_0 = OpConstant %int 0
 %_ptr_Uniform_float = OpTypePointer Uniform %float
-; CHECK: %int = OpTypeInt 32 1
-; CHECK: %int_0 = OpConstant %int 0
-; CHECK: %_ptr_Uniform_float = OpTypePointer Uniform %float
-; CHECK: %uint = OpTypeInt 32 0
-; CHECK: %uint_0 = OpConstant %uint 0
-; CHECK: %uint_3 = OpConstant %uint 3
-; CHECK: %21 = OpTypeFunction %uint %uint %uint %uint %uint
-; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
-)" + kInputGlobals + R"(
-; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-; CHECK: %bool = OpTypeBool
-; CHECK: %uint_1 = OpConstant %uint 1
-; CHECK: %52 = OpTypeFunction %void %uint %uint %uint %uint
-)" + kOutputGlobals + R"(
-; CHECK: %uint_10 = OpConstant %uint 10
-; CHECK: %uint_4 = OpConstant %uint 4
-; CHECK: %uint_23 = OpConstant %uint 23
-; CHECK: %uint_2 = OpConstant %uint 2
-; CHECK: %v4float = OpTypeVector %float 4
-; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
-; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
-; CHECK: %v4uint = OpTypeVector %uint 4
-; CHECK: %uint_5 = OpConstant %uint 5
-; CHECK: %uint_7 = OpConstant %uint 7
-; CHECK: %uint_8 = OpConstant %uint 8
-; CHECK: %uint_9 = OpConstant %uint 9
-; CHECK: %uint_32 = OpConstant %uint 32
-; CHECK: %104 = OpConstantNull %float
+;CHECK: %int = OpTypeInt 32 1
+;CHECK: %_ptr_Uniform_float = OpTypePointer Uniform %float
+;CHECK: %uint = OpTypeInt 32 0
+;CHECK: %v4uint = OpTypeVector %uint 4
+;CHECK: %v4float = OpTypeVector %float 4
+;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
+;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
+;CHECK: [[null_float:%\w+]] = OpConstantNull %float
 )";
-  // clang-format on
 
   const std::string main_func = R"(
 %main = OpFunction %void None %3
@@ -2477,31 +1736,32 @@
 %15 = OpAccessChain %_ptr_Uniform_float %uniformBuffer %int_0
 %16 = OpLoad %float %15
 OpStore %b %16
-; CHECK-NOT: %16 = OpLoad %float %15
-; CHECK-NOT: OpStore %b %16
-; CHECK: %43 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_3 %uint_0
-; CHECK: %45 = OpULessThan %bool %uint_0 %43
-; CHECK: OpSelectionMerge %47 None
-; CHECK: OpBranchConditional %45 %48 %49
-; CHECK: %48 = OpLabel
-; CHECK: %50 = OpLoad %float %15
-; CHECK: OpBranch %47
-; CHECK: %49 = OpLabel
-; CHECK: %103 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_32 %uint_1 %uint_0 %uint_0
-; CHECK: OpBranch %47
-; CHECK: %47 = OpLabel
-; CHECK: %105 = OpPhi %float %50 %48 %104 %49
-; CHECK: OpStore %b %105
+;CHECK-NOT: %16 = OpLoad %float %15
+;CHECK-NOT: OpStore %b %16
+;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord
+;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}}
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0
+;CHECK: [[check_result:%\w+]] = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_33 {{%\w+}} %uint_7 %uint_3 %uint_0 {{%\w+}}
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional [[check_result]] {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpLoad %float %15
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: [[phi_result:%\w+]] = OpPhi %float {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: OpStore %b [[phi_result]]
 OpReturn
 OpFunctionEnd
 )";
-
-  const std::string new_funcs = kDirectRead4 + kStreamWrite4Frag;
+  // clang-format on
 
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs,
-                                               true, 7u, 23u, true, true, false,
-                                               false, false);
+  SinglePassRunAndMatch<InstBindlessCheckPass>(defs + kImportStub + main_func,
+                                               true, 23u);
 }
 
 TEST_F(InstBindlessTest, InstBoundsInitStoreUnsizedSSBOArray) {
@@ -2511,7 +1771,7 @@
   // layout(location=0) in nonuniformEXT flat int nu_ii;
   // layout(location=1) in float b;
   //
-  // layout(binding=4)  buffer bname { float b; }  storageBuffer[];
+  // layout(set=5, binding=4)  buffer bname { float b; }  storageBuffer[];
   //
   // void main()
   // {
@@ -2523,12 +1783,12 @@
 OpCapability ShaderNonUniform
 OpCapability RuntimeDescriptorArray
 OpCapability StorageBufferArrayNonUniformIndexing
+;CHECK: OpCapability Linkage
 OpExtension "SPV_EXT_descriptor_indexing"
-; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
 %1 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel Logical GLSL450
 OpEntryPoint Fragment %main "main" %nu_ii %b
-; CHECK: OpEntryPoint Fragment %main "main" %nu_ii %b %gl_FragCoord
+;CHECK: OpEntryPoint Fragment %main "main" %nu_ii %b %gl_FragCoord
 OpExecutionMode %main OriginUpperLeft
 OpSource GLSL 450
 OpSourceExtension "GL_EXT_nonuniform_qualifier"
@@ -2540,16 +1800,15 @@
 OpName %b "b"
 OpMemberDecorate %bname 0 Offset 0
 OpDecorate %bname BufferBlock
-OpDecorate %storageBuffer DescriptorSet 0
+OpDecorate %storageBuffer DescriptorSet 5
 OpDecorate %storageBuffer Binding 4
 OpDecorate %nu_ii Flat
 OpDecorate %nu_ii Location 0
 OpDecorate %nu_ii NonUniform
 OpDecorate %14 NonUniform
 OpDecorate %b Location 1
-; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
-)" + kInputDecorations + kOutputDecorations + R"(
-; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
+)" + kImportDeco + R"(
+;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
 %void = OpTypeVoid
 %3 = OpTypeFunction %void
 %float = OpTypeFloat 32
@@ -2564,33 +1823,9 @@
 %_ptr_Input_float = OpTypePointer Input %float
 %b = OpVariable %_ptr_Input_float Input
 %_ptr_Uniform_float = OpTypePointer Uniform %float
-; CHECK: %uint = OpTypeInt 32 0
-; CHECK: %uint_0 = OpConstant %uint 0
-; CHECK: %uint_1 = OpConstant %uint 1
-; CHECK: %uint_4 = OpConstant %uint 4
-; CHECK: %26 = OpTypeFunction %uint %uint %uint
-; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
-)" + kInputGlobals + R"(
-; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-; CHECK: %bool = OpTypeBool
-; CHECK: %48 = OpTypeFunction %void %uint %uint %uint %uint
-)" + kOutputGlobals + R"(
-; CHECK: %uint_10 = OpConstant %uint 10
-; CHECK: %uint_23 = OpConstant %uint 23
-; CHECK: %uint_2 = OpConstant %uint 2
-; CHECK: %uint_3 = OpConstant %uint 3
-; CHECK: %v4float = OpTypeVector %float 4
-; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
-; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
-; CHECK: %v4uint = OpTypeVector %uint 4
-; CHECK: %uint_5 = OpConstant %uint 5
-; CHECK: %uint_7 = OpConstant %uint 7
-; CHECK: %uint_8 = OpConstant %uint 8
-; CHECK: %uint_9 = OpConstant %uint 9
-; CHECK: %uint_45 = OpConstant %uint 45
-; CHECK: %102 = OpTypeFunction %uint %uint %uint %uint %uint
+;CHECK: %v4uint = OpTypeVector %uint 4
+;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
 )";
-  // clang-format on
 
   const std::string main_func = R"(
 %main = OpFunction %void None %3
@@ -2599,41 +1834,30 @@
 %18 = OpLoad %float %b
 %20 = OpAccessChain %_ptr_Uniform_float %storageBuffer %14 %int_0
 OpStore %20 %18
-; CHECK-NOT: OpStore %20 %18
-; CHECK: %40 = OpFunctionCall %uint %inst_bindless_direct_read_2 %uint_1 %uint_4
-; CHECK: %42 = OpULessThan %bool %7 %40
-; CHECK: OpSelectionMerge %43 None
-; CHECK: OpBranchConditional %42 %44 %45
-; CHECK: %44 = OpLabel
-; CHECK: %100 = OpBitcast %uint %7
-; CHECK: %119 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_4 %100
-; CHECK: %120 = OpULessThan %bool %uint_0 %119
-; CHECK: OpSelectionMerge %121 None
-; CHECK: OpBranchConditional %120 %122 %123
-; CHECK: %122 = OpLabel
-; CHECK: OpStore %20 %19
-; CHECK: OpBranch %121
-; CHECK: %123 = OpLabel
-; CHECK: %124 = OpBitcast %uint %7
-; CHECK: %125 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_45 %uint_1 %124 %uint_0
-; CHECK: OpBranch %121
-; CHECK: %121 = OpLabel
-; CHECK: OpBranch %43
-; CHECK: %45 = OpLabel
-; CHECK: %46 = OpBitcast %uint %7
-; CHECK: %99 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_45 %uint_0 %46 %40
-; CHECK: OpBranch %43
-; CHECK: %43 = OpLabel
+;CHECK-NOT: OpStore %20 %18
+;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord
+;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}}
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0
+;CHECK: {{%\w+}} = OpBitcast %uint %7
+;CHECK: [[check_result:%\w+]] = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_46 {{%\w+}} %uint_5 %uint_4 {{%\w+}} {{%\w+}}
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional [[check_result]] {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpStore %20 %19
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
 OpReturn
 OpFunctionEnd
 )";
-
-  const std::string new_funcs = kDirectRead2 + kStreamWrite4Frag + kDirectRead4;
+  // clang-format on
 
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs,
-                                               true, 7u, 23u, true, true, false,
-                                               false, false);
+  SinglePassRunAndMatch<InstBindlessCheckPass>(defs + kImportStub + main_func,
+                                               true, 23u);
 }
 
 TEST_F(InstBindlessTest, InstBoundsInitLoadSizedUBOArray) {
@@ -2643,7 +1867,7 @@
   // layout(location=0) in nonuniformEXT flat int nu_ii;
   // layout(location=0) out float b;
   //
-  // layout(binding=3)  uniform uname { float a; }  uniformBuffer[128];
+  // layout(set=1, binding=3)  uniform uname { float a; }  uniformBuffer[128];
   //
   // void main()
   // {
@@ -2655,12 +1879,12 @@
 OpCapability Shader
 OpCapability ShaderNonUniform
 OpCapability UniformBufferArrayNonUniformIndexing
+;CHECK: OpCapability Linkage
 OpExtension "SPV_EXT_descriptor_indexing"
-; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
 %1 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel Logical GLSL450
 OpEntryPoint Fragment %main "main" %b %nu_ii
-; CHECK: OpEntryPoint Fragment %main "main" %b %nu_ii %gl_FragCoord
+;CHECK: OpEntryPoint Fragment %main "main" %b %nu_ii %gl_FragCoord
 OpExecutionMode %main OriginUpperLeft
 OpSource GLSL 450
 OpSourceExtension "GL_EXT_nonuniform_qualifier"
@@ -2673,18 +1897,16 @@
 OpDecorate %b Location 0
 OpMemberDecorate %uname 0 Offset 0
 OpDecorate %uname Block
-OpDecorate %uniformBuffer DescriptorSet 0
+OpDecorate %uniformBuffer DescriptorSet 1
 OpDecorate %uniformBuffer Binding 3
 OpDecorate %nu_ii Flat
 OpDecorate %nu_ii Location 0
 OpDecorate %nu_ii NonUniform
 OpDecorate %18 NonUniform
 OpDecorate %22 NonUniform
-; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
-)" + kOutputDecorations + R"(
-; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
-)" + kInputDecorations + R"(
-; CHECK: OpDecorate %117 NonUniform
+)" + kImportDeco + R"(
+;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
+;CHECK: OpDecorate [[load_result:%\w+]] NonUniform
 %void = OpTypeVoid
 %3 = OpTypeFunction %void
 %float = OpTypeFloat 32
@@ -2701,31 +1923,10 @@
 %nu_ii = OpVariable %_ptr_Input_int Input
 %int_0 = OpConstant %int 0
 %_ptr_Uniform_float = OpTypePointer Uniform %float
-; CHECK: %uint_0 = OpConstant %uint 0
-; CHECK: %bool = OpTypeBool
-; CHECK: %32 = OpTypeFunction %void %uint %uint %uint %uint
-; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
-)" + kOutputGlobals + R"(
-; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-; CHECK: %uint_10 = OpConstant %uint 10
-; CHECK: %uint_4 = OpConstant %uint 4
-; CHECK: %uint_1 = OpConstant %uint 1
-; CHECK: %uint_23 = OpConstant %uint 23
-; CHECK: %uint_2 = OpConstant %uint 2
-; CHECK: %uint_3 = OpConstant %uint 3
-; CHECK: %v4float = OpTypeVector %float 4
-; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
-; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
-; CHECK: %v4uint = OpTypeVector %uint 4
-; CHECK: %uint_5 = OpConstant %uint 5
-; CHECK: %uint_7 = OpConstant %uint 7
-; CHECK: %uint_8 = OpConstant %uint 8
-; CHECK: %uint_9 = OpConstant %uint 9
-; CHECK: %uint_46 = OpConstant %uint 46
-; CHECK: %88 = OpConstantNull %float
-; CHECK: %92 = OpTypeFunction %uint %uint %uint %uint %uint
-)" + kInputGlobals;
-  // clang-format on
+;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
+;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
+;CHECK: [[null_float:%\w+]] = OpConstantNull %float
+)";
 
   const std::string main_func = R"(
 %main = OpFunction %void None %3
@@ -2734,44 +1935,33 @@
 %21 = OpAccessChain %_ptr_Uniform_float %uniformBuffer %18 %int_0
 %22 = OpLoad %float %21
 OpStore %b %22
-; CHECK-NOT: %22 = OpLoad %float %21
-; CHECK-NOT: OpStore %b %22
-; CHECK: %25 = OpULessThan %bool %7 %uint_128
-; CHECK: OpSelectionMerge %26 None
-; CHECK: OpBranchConditional %25 %27 %28
-; CHECK: %27 = OpLabel
-; CHECK: %90 = OpBitcast %uint %7
-; CHECK: %112 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_3 %90
-; CHECK: %113 = OpULessThan %bool %uint_0 %112
-; CHECK: OpSelectionMerge %114 None
-; CHECK: OpBranchConditional %113 %115 %116
-; CHECK: %115 = OpLabel
-; CHECK: %117 = OpLoad %float %22
-; CHECK: OpBranch %114
-; CHECK: %116 = OpLabel
-; CHECK: %118 = OpBitcast %uint %7
-; CHECK: %119 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_46 %uint_1 %118 %uint_0
-; CHECK: OpBranch %114
-; CHECK: %114 = OpLabel
-; CHECK: %120 = OpPhi %float %117 %115 %88 %116
-; CHECK: OpBranch %26
-; CHECK: %28 = OpLabel
-; CHECK: %30 = OpBitcast %uint %7
-; CHECK: %87 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_46 %uint_0 %30 %uint_128
-; CHECK: OpBranch %26
-; CHECK: %26 = OpLabel
-; CHECK: %89 = OpPhi %float %120 %114 %88 %28
-; CHECK: OpStore %b %89
+;CHECK-NOT: %22 = OpLoad %float %21
+;CHECK-NOT: OpStore %b %22
+;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord
+;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}}
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0
+;CHECK: {{%\w+}} = OpBitcast %uint %7
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_47 {{%\w+}} %uint_1 %uint_3 {{%\w+}} {{%\w+}}
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpLoad %float %22
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpPhi %float {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: OpStore %b {{%\w+}}
 OpReturn
 OpFunctionEnd
 )";
-
-  const std::string new_funcs = kStreamWrite4Frag + kDirectRead4;
+  // clang-format on
 
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs,
-                                               true, 7u, 23u, true, true, false,
-                                               false, false);
+  SinglePassRunAndMatch<InstBindlessCheckPass>(defs + kImportStub + main_func,
+                                               true, 23u);
 }
 
 TEST_F(InstBindlessTest,
@@ -2781,12 +1971,12 @@
   //
   // layout (local_size_x = 1, local_size_y = 1) in;
   //
-  // layout(set = 0, binding = 0, std140) buffer Input {
+  // layout(set = 2, binding = 0, std140) buffer Input {
   //   uint index;
   //   float red;
   // } sbo;
   //
-  // layout(set = 0, binding = 1, rgba32f) readonly uniform image2D images[];
+  // layout(set = 2, binding = 1, rgba32f) readonly uniform image2D images[];
   //
   // void main()
   // {
@@ -2797,12 +1987,12 @@
   const std::string defs = R"(
 OpCapability Shader
 OpCapability RuntimeDescriptorArray
+;CHECK: OpCapability Linkage
 OpExtension "SPV_EXT_descriptor_indexing"
-; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
 %1 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel Logical GLSL450
 OpEntryPoint GLCompute %main "main"
-; CHECK: OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID
+;CHECK: OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID
 OpExecutionMode %main LocalSize 1 1 1
 OpSource GLSL 450
 OpSourceExtension "GL_EXT_nonuniform_qualifier"
@@ -2815,14 +2005,13 @@
 OpMemberDecorate %Input 0 Offset 0
 OpMemberDecorate %Input 1 Offset 4
 OpDecorate %Input BufferBlock
-OpDecorate %sbo DescriptorSet 0
+OpDecorate %sbo DescriptorSet 2
 OpDecorate %sbo Binding 0
-OpDecorate %images DescriptorSet 0
+OpDecorate %images DescriptorSet 2
 OpDecorate %images Binding 1
 OpDecorate %images NonWritable
-; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
-)" + kInputDecorations + kOutputDecorations + R"(
-; CHECK: OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId
+)" + kImportDeco + R"(
+;CHECK: OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId
 %void = OpTypeVoid
 %3 = OpTypeFunction %void
 %uint = OpTypeInt 32 0
@@ -2844,35 +2033,12 @@
 %v4float = OpTypeVector %float 4
 %uint_0 = OpConstant %uint 0
 %_ptr_Uniform_float = OpTypePointer Uniform %float
-; CHECK: %uint_1 = OpConstant %uint 1
-; CHECK: %34 = OpTypeFunction %uint %uint %uint
-; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
-)" + kInputGlobals + R"(
-; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-; CHECK: %bool = OpTypeBool
-; CHECK: %57 = OpTypeFunction %void %uint %uint %uint %uint
-)" + kOutputGlobals + R"(
-; CHECK: %uint_10 = OpConstant %uint 10
-; CHECK: %uint_4 = OpConstant %uint 4
-; CHECK: %uint_23 = OpConstant %uint 23
-; CHECK: %uint_2 = OpConstant %uint 2
-; CHECK: %uint_5 = OpConstant %uint 5
-; CHECK: %uint_3 = OpConstant %uint 3
-; CHECK: %v3uint = OpTypeVector %uint 3
-; CHECK: %_ptr_Input_v3uint = OpTypePointer Input %v3uint
-; CHECK: %gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input
-; CHECK: %uint_6 = OpConstant %uint 6
-; CHECK: %uint_7 = OpConstant %uint 7
-; CHECK: %uint_8 = OpConstant %uint 8
-; CHECK: %uint_9 = OpConstant %uint 9
-; CHECK: %uint_50 = OpConstant %uint 50
-; CHECK: %112 = OpConstantNull %v4float
-; CHECK: %115 = OpTypeFunction %uint %uint %uint %uint %uint
-; CHECK: %uint_47 = OpConstant %uint 47
-; CHECK: %140 = OpConstantNull %uint
-; CHECK: %uint_53 = OpConstant %uint 53
+;CHECK: %v3uint = OpTypeVector %uint 3
+;CHECK: %_ptr_Input_v3uint = OpTypePointer Input %v3uint
+;CHECK: %gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input
+;CHECK: [[null_uint:%\w+]] = OpConstantNull %uint
+;CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float
 )";
-  // clang-format on
 
   const std::string main_func = R"(
 %main = OpFunction %void None %3
@@ -2885,70 +2051,64 @@
 %29 = OpCompositeExtract %float %27 0
 %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
 OpStore %31 %29
-; CHECK-NOT: OpStore %31 %29
-; CHECK: %132 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0
-; CHECK: %133 = OpULessThan %bool %uint_0 %132
-; CHECK: OpSelectionMerge %134 None
-; CHECK: OpBranchConditional %133 %135 %136
-; CHECK: %135 = OpLabel
-; CHECK: %137 = OpLoad %uint %25
-; CHECK: OpBranch %134
-; CHECK: %136 = OpLabel
-; CHECK: %139 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_47 %uint_1 %uint_0 %uint_0
-; CHECK: OpBranch %134
-; CHECK: %134 = OpLabel
-; CHECK: %141 = OpPhi %uint %137 %135 %140 %136
-; CHECK: %27 = OpAccessChain %_ptr_UniformConstant_13 %images %141
-; CHECK: %28 = OpLoad %13 %27
-; CHECK: %48 = OpFunctionCall %uint %inst_bindless_direct_read_2 %uint_1 %uint_1
-; CHECK: %50 = OpULessThan %bool %141 %48
-; CHECK: OpSelectionMerge %51 None
-; CHECK: OpBranchConditional %50 %52 %53
-; CHECK: %52 = OpLabel
-; CHECK: %54 = OpLoad %13 %27
-; CHECK: %142 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_1 %141
-; CHECK: %143 = OpULessThan %bool %uint_0 %142
-; CHECK: OpSelectionMerge %144 None
-; CHECK: OpBranchConditional %143 %145 %146
-; CHECK: %145 = OpLabel
-; CHECK: %147 = OpLoad %13 %27
-; CHECK: %148 = OpImageRead %v4float %147 %20
-; CHECK: OpBranch %144
-; CHECK: %146 = OpLabel
-; CHECK: %149 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_50 %uint_1 %141 %uint_0
-; CHECK: OpBranch %144
-; CHECK: %144 = OpLabel
-; CHECK: %150 = OpPhi %v4float %148 %145 %112 %146
-; CHECK: OpBranch %51
-; CHECK: %53 = OpLabel
-; CHECK: %111 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_50 %uint_0 %141 %48
-; CHECK: OpBranch %51
-; CHECK: %51 = OpLabel
-; CHECK: %113 = OpPhi %v4float %150 %144 %112 %53
-; CHECK: %30 = OpCompositeExtract %float %113 0
-; CHECK: %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
-; CHECK: %151 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0
-; CHECK: %152 = OpULessThan %bool %uint_0 %151
-; CHECK: OpSelectionMerge %153 None
-; CHECK: OpBranchConditional %152 %154 %155
-; CHECK: %154 = OpLabel
-; CHECK: OpStore %31 %30
-; CHECK: OpBranch %153
-; CHECK: %155 = OpLabel
-; CHECK: %157 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_53 %uint_1 %uint_0 %uint_0
-; CHECK: OpBranch %153
-; CHECK: %153 = OpLabel
+;CHECK-NOT: OpStore %31 %29
+;CHECK: {{%\w+}} = OpLoad %v3uint %gl_GlobalInvocationID
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5 {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_48 {{%\w+}} %uint_2 %uint_0 %uint_0 {{%\w+}}
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpLoad %uint %25
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpPhi %uint {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpAccessChain %_ptr_UniformConstant_13 %images {{%\w+}}
+;CHECK: {{%\w+}} = OpLoad %13 {{%\w+}}
+;CHECK: {{%\w+}} = OpLoad %v3uint %gl_GlobalInvocationID
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5 {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_51 {{%\w+}} %uint_2 %uint_1 {{%\w+}} %uint_0
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpLoad %13 {{%\w+}}
+;CHECK: {{%\w+}} = OpImageRead %v4float {{%\w+}} %20
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpPhi %v4float {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpCompositeExtract %float {{%\w+}} 0
+;CHECK: {{%\w+}} = OpAccessChain %_ptr_Uniform_float %sbo %int_1
+;CHECK: {{%\w+}} = OpLoad %v3uint %gl_GlobalInvocationID
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5 {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_54 {{%\w+}} %uint_2 %uint_0 %uint_0 {{%\w+}}
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpStore %31 {{%\w+}}
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
 OpReturn
 OpFunctionEnd
 )";
-
-  const std::string new_funcs =
-      kDirectRead2 + kStreamWrite4Compute + kDirectRead4;
+  // clang-format on
 
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs,
-                                               true, 7u, 23u, true, true, false,
-                                               false, false);
+  SinglePassRunAndMatch<InstBindlessCheckPass>(defs + kImportStub + main_func,
+                                               true, 23u);
 }
 
 TEST_F(InstBindlessTest,
@@ -2957,12 +2117,12 @@
   // #extension GL_EXT_nonuniform_qualifier : require
   // #extension GL_NV_ray_tracing : require
   //
-  // layout(set = 0, binding = 0, std140) buffer StorageBuffer {
+  // layout(set = 3, binding = 1, std140) buffer StorageBuffer {
   //   uint index;
   //   float red;
   // } sbo;
   //
-  // layout(set = 0, binding = 1, rgba32f) readonly uniform image2D images[];
+  // layout(set = 3, binding = 5, rgba32f) readonly uniform image2D images[];
   //
   // void main()
   // {
@@ -2973,13 +2133,13 @@
   const std::string defs = R"(
 OpCapability RuntimeDescriptorArray
 OpCapability RayTracingNV
+;CHECK: OpCapability Linkage
 OpExtension "SPV_EXT_descriptor_indexing"
 OpExtension "SPV_NV_ray_tracing"
-; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
 %1 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel Logical GLSL450
 OpEntryPoint RayGenerationNV %main "main"
-; CHECK: OpEntryPoint RayGenerationNV %main "main" %89
+;CHECK: OpEntryPoint RayGenerationNV %main "main" [[launch_id:%\w+]]
 OpSource GLSL 460
 OpSourceExtension "GL_EXT_nonuniform_qualifier"
 OpSourceExtension "GL_NV_ray_tracing"
@@ -2992,14 +2152,13 @@
 OpMemberDecorate %StorageBuffer 0 Offset 0
 OpMemberDecorate %StorageBuffer 1 Offset 4
 OpDecorate %StorageBuffer BufferBlock
-OpDecorate %sbo DescriptorSet 0
-OpDecorate %sbo Binding 0
-OpDecorate %images DescriptorSet 0
-OpDecorate %images Binding 1
+OpDecorate %sbo DescriptorSet 3
+OpDecorate %sbo Binding 1
+OpDecorate %images DescriptorSet 3
+OpDecorate %images Binding 5
 OpDecorate %images NonWritable
-; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
-)" + kInputDecorations + kOutputDecorations + R"(
-; CHECK: OpDecorate %89 BuiltIn LaunchIdNV
+)" + kImportDeco + R"(
+;CHECK: OpDecorate [[launch_id]] BuiltIn LaunchIdNV
 %void = OpTypeVoid
 %3 = OpTypeFunction %void
 %uint = OpTypeInt 32 0
@@ -3021,36 +2180,9 @@
 %v4float = OpTypeVector %float 4
 %uint_0 = OpConstant %uint 0
 %_ptr_Uniform_float = OpTypePointer Uniform %float
-; CHECK: %uint_1 = OpConstant %uint 1
-; CHECK: %34 = OpTypeFunction %uint %uint %uint
-; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
-)" + kInputGlobals + R"(
-; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-; CHECK: %bool = OpTypeBool
-; CHECK: %57 = OpTypeFunction %void %uint %uint %uint %uint
-)" + kOutputGlobals + R"(
-; CHECK: %uint_10 = OpConstant %uint 10
-; CHECK: %uint_4 = OpConstant %uint 4
-; CHECK: %uint_23 = OpConstant %uint 23
-; CHECK: %uint_2 = OpConstant %uint 2
-; CHECK: %uint_5313 = OpConstant %uint 5313
-; CHECK: %uint_3 = OpConstant %uint 3
-; CHECK: %v3uint = OpTypeVector %uint 3
-; CHECK: %_ptr_Input_v3uint = OpTypePointer Input %v3uint
-; CHECK: %89 = OpVariable %_ptr_Input_v3uint Input
-; CHECK: %uint_5 = OpConstant %uint 5
-; CHECK: %uint_6 = OpConstant %uint 6
-; CHECK: %uint_7 = OpConstant %uint 7
-; CHECK: %uint_8 = OpConstant %uint 8
-; CHECK: %uint_9 = OpConstant %uint 9
-; CHECK: %uint_51 = OpConstant %uint 51
-; CHECK: %113 = OpConstantNull %v4float
-; CHECK: %116 = OpTypeFunction %uint %uint %uint %uint %uint
-; CHECK: %uint_48 = OpConstant %uint 48
-; CHECK: %141 = OpConstantNull %uint
-; CHECK: %uint_54 = OpConstant %uint 54
+;CHECK: [[null_uint:%\w+]] = OpConstantNull %uint
+;CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float
 )";
-  // clang-format on
 
   const std::string main_func = R"(
 %main = OpFunction %void None %3
@@ -3063,69 +2195,64 @@
 %29 = OpCompositeExtract %float %27 0
 %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
 OpStore %31 %29
-; CHECK-NOT: OpStore %31 %29
-; CHECK: %133 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0
-; CHECK: %134 = OpULessThan %bool %uint_0 %133
-; CHECK: OpSelectionMerge %135 None
-; CHECK: OpBranchConditional %134 %136 %137
-; CHECK: %136 = OpLabel
-; CHECK: %138 = OpLoad %uint %25
-; CHECK: OpBranch %135
-; CHECK: %137 = OpLabel
-; CHECK: %140 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_48 %uint_1 %uint_0 %uint_0
-; CHECK: OpBranch %135
-; CHECK: %135 = OpLabel
-; CHECK: %142 = OpPhi %uint %138 %136 %141 %137
-; CHECK: %27 = OpAccessChain %_ptr_UniformConstant_13 %images %142
-; CHECK: %28 = OpLoad %13 %27
-; CHECK: %48 = OpFunctionCall %uint %inst_bindless_direct_read_2 %uint_1 %uint_1
-; CHECK: %50 = OpULessThan %bool %142 %48
-; CHECK: OpSelectionMerge %51 None
-; CHECK: OpBranchConditional %50 %52 %53
-; CHECK: %52 = OpLabel
-; CHECK: %54 = OpLoad %13 %27
-; CHECK: %143 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_1 %142
-; CHECK: %144 = OpULessThan %bool %uint_0 %143
-; CHECK: OpSelectionMerge %145 None
-; CHECK: OpBranchConditional %144 %146 %147
-; CHECK: %146 = OpLabel
-; CHECK: %148 = OpLoad %13 %27
-; CHECK: %149 = OpImageRead %v4float %148 %20
-; CHECK: OpBranch %145
-; CHECK: %147 = OpLabel
-; CHECK: %150 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_51 %uint_1 %142 %uint_0
-; CHECK: OpBranch %145
-; CHECK: %145 = OpLabel
-; CHECK: %151 = OpPhi %v4float %149 %146 %113 %147
-; CHECK: OpBranch %51
-; CHECK: %53 = OpLabel
-; CHECK: %112 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_51 %uint_0 %142 %48
-; CHECK: OpBranch %51
-; CHECK: %51 = OpLabel
-; CHECK: %114 = OpPhi %v4float %151 %145 %113 %53
-; CHECK: %30 = OpCompositeExtract %float %114 0
-; CHECK: %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
-; CHECK: %152 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0
-; CHECK: %153 = OpULessThan %bool %uint_0 %152
-; CHECK: OpSelectionMerge %154 None
-; CHECK: OpBranchConditional %153 %155 %156
-; CHECK: %155 = OpLabel
-; CHECK: OpStore %31 %30
-; CHECK: OpBranch %154
-; CHECK: %156 = OpLabel
-; CHECK: %158 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_54 %uint_1 %uint_0 %uint_0
-; CHECK: OpBranch %154
-; CHECK: %154 = OpLabel
+;CHECK-NOT: OpStore %31 %29
+;CHECK: {{%\w+}} = OpLoad %v3uint {{%\w+}}
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5313 {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_49 {{%\w+}} %uint_3 %uint_1 %uint_0 {{%\w+}}
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpLoad %uint %25
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpPhi %uint {{%\w+}} {{%\w+}} [[null_uint]] {{%\w+}}
+;CHECK: {{%\w+}} = OpAccessChain %_ptr_UniformConstant_13 %images {{%\w+}}
+;CHECK: {{%\w+}} = OpLoad %13 {{%\w+}}
+;CHECK: {{%\w+}} = OpLoad %v3uint {{%\w+}}
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5313 {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_52 {{%\w+}} %uint_3 %uint_5 {{%\w+}} %uint_0
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpLoad %13 {{%\w+}}
+;CHECK: {{%\w+}} = OpImageRead %v4float {{%\w+}} %20
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}}
+;CHECK: {{%\w+}} = OpCompositeExtract %float {{%\w+}} 0
+;CHECK: {{%\w+}} = OpAccessChain %_ptr_Uniform_float %sbo %int_1
+;CHECK: {{%\w+}} = OpLoad %v3uint {{%\w+}}
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5313 {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_55 {{%\w+}} %uint_3 %uint_1 %uint_0 {{%\w+}}
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpStore {{%\w+}} {{%\w+}}
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
 OpReturn
 OpFunctionEnd
 )";
-
-  const std::string new_funcs = kDirectRead2 + kStreamWrite4Ray + kDirectRead4;
+  // clang-format on
 
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs,
-                                               true, 7u, 23u, true, true, false,
-                                               false, false);
+  SinglePassRunAndMatch<InstBindlessCheckPass>(defs + kImportStub + main_func,
+                                               true, 23u);
 }
 
 TEST_F(InstBindlessTest,
@@ -3134,12 +2261,12 @@
   // #extension GL_EXT_nonuniform_qualifier : require
   // #extension GL_NV_ray_tracing : require
   //
-  // layout(set = 0, binding = 0, std140) buffer StorageBuffer {
+  // layout(set = 5, binding = 1, std140) buffer StorageBuffer {
   //   uint index;
   //   float red;
   // } sbo;
   //
-  // layout(set = 0, binding = 1, rgba32f) readonly uniform image2D images[];
+  // layout(set = 5, binding = 3, rgba32f) readonly uniform image2D images[];
   //
   // void main()
   // {
@@ -3150,13 +2277,13 @@
   const std::string defs = R"(
 OpCapability RuntimeDescriptorArray
 OpCapability RayTracingNV
+;CHECK: OpCapability Linkage
 OpExtension "SPV_EXT_descriptor_indexing"
 OpExtension "SPV_NV_ray_tracing"
-; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
 %1 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel Logical GLSL450
 OpEntryPoint IntersectionNV %main "main"
-; CHECK: OpEntryPoint IntersectionNV %main "main" %89
+;CHECK: OpEntryPoint IntersectionNV %main "main" [[launch_id:%\w+]]
 OpSource GLSL 460
 OpSourceExtension "GL_EXT_nonuniform_qualifier"
 OpSourceExtension "GL_NV_ray_tracing"
@@ -3169,14 +2296,13 @@
 OpMemberDecorate %StorageBuffer 0 Offset 0
 OpMemberDecorate %StorageBuffer 1 Offset 4
 OpDecorate %StorageBuffer BufferBlock
-OpDecorate %sbo DescriptorSet 0
-OpDecorate %sbo Binding 0
-OpDecorate %images DescriptorSet 0
-OpDecorate %images Binding 1
+OpDecorate %sbo DescriptorSet 5
+OpDecorate %sbo Binding 1
+OpDecorate %images DescriptorSet 5
+OpDecorate %images Binding 3
 OpDecorate %images NonWritable
-; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
-)" + kInputDecorations + kOutputDecorations + R"(
-; CHECK: OpDecorate %89 BuiltIn LaunchIdNV
+)" + kImportDeco + R"(
+;CHECK: OpDecorate [[launch_id]] BuiltIn LaunchIdNV
 %void = OpTypeVoid
 %3 = OpTypeFunction %void
 %uint = OpTypeInt 32 0
@@ -3198,35 +2324,10 @@
 %v4float = OpTypeVector %float 4
 %uint_0 = OpConstant %uint 0
 %_ptr_Uniform_float = OpTypePointer Uniform %float
-; CHECK: %uint_1 = OpConstant %uint 1
-; CHECK: %34 = OpTypeFunction %uint %uint %uint
-; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
-)" + kInputGlobals + R"(
-; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-; CHECK: %bool = OpTypeBool
-)" + kOutputGlobals + R"(
-; CHECK: %uint_10 = OpConstant %uint 10
-; CHECK: %uint_4 = OpConstant %uint 4
-; CHECK: %uint_23 = OpConstant %uint 23
-; CHECK: %uint_2 = OpConstant %uint 2
-; CHECK: %uint_5314 = OpConstant %uint 5314
-; CHECK: %uint_3 = OpConstant %uint 3
-; CHECK: %v3uint = OpTypeVector %uint 3
-; CHECK: %_ptr_Input_v3uint = OpTypePointer Input %v3uint
-; CHECK: %89 = OpVariable %_ptr_Input_v3uint Input
-; CHECK: %uint_5 = OpConstant %uint 5
-; CHECK: %uint_6 = OpConstant %uint 6
-; CHECK: %uint_7 = OpConstant %uint 7
-; CHECK: %uint_8 = OpConstant %uint 8
-; CHECK: %uint_9 = OpConstant %uint 9
-; CHECK: %uint_51 = OpConstant %uint 51
-; CHECK: %113 = OpConstantNull %v4float
-; CHECK: %116 = OpTypeFunction %uint %uint %uint %uint %uint
-; CHECK: %uint_48 = OpConstant %uint 48
-; CHECK: %141 = OpConstantNull %uint
-; CHECK: %uint_54 = OpConstant %uint 54
+;CHECK: [[launch_id]] = OpVariable %_ptr_Input_v3uint Input
+;CHECK: [[null_uint:%\w+]] = OpConstantNull %uint
+;CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float
 )";
-  // clang-format on
 
   const std::string main_func = R"(
 %main = OpFunction %void None %3
@@ -3239,69 +2340,63 @@
 %29 = OpCompositeExtract %float %27 0
 %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
 OpStore %31 %29
-; CHECK-NOT: OpStore %31 %29
-; CHECK: %133 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0
-; CHECK: %134 = OpULessThan %bool %uint_0 %133
-; CHECK: OpSelectionMerge %135 None
-; CHECK: OpBranchConditional %134 %136 %137
-; CHECK: %136 = OpLabel
-; CHECK: %138 = OpLoad %uint %25
-; CHECK: OpBranch %135
-; CHECK: %137 = OpLabel
-; CHECK: %140 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_48 %uint_1 %uint_0 %uint_0
-; CHECK: OpBranch %135
-; CHECK: %135 = OpLabel
-; CHECK: %142 = OpPhi %uint %138 %136 %141 %137
-; CHECK: %27 = OpAccessChain %_ptr_UniformConstant_13 %images %142
-; CHECK: %28 = OpLoad %13 %27
-; CHECK: %48 = OpFunctionCall %uint %inst_bindless_direct_read_2 %uint_1 %uint_1
-; CHECK: %50 = OpULessThan %bool %142 %48
-; CHECK: OpSelectionMerge %51 None
-; CHECK: OpBranchConditional %50 %52 %53
-; CHECK: %52 = OpLabel
-; CHECK: %54 = OpLoad %13 %27
-; CHECK: %143 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_1 %142
-; CHECK: %144 = OpULessThan %bool %uint_0 %143
-; CHECK: OpSelectionMerge %145 None
-; CHECK: OpBranchConditional %144 %146 %147
-; CHECK: %146 = OpLabel
-; CHECK: %148 = OpLoad %13 %27
-; CHECK: %149 = OpImageRead %v4float %148 %20
-; CHECK: OpBranch %145
-; CHECK: %147 = OpLabel
-; CHECK: %150 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_51 %uint_1 %142 %uint_0
-; CHECK: OpBranch %145
-; CHECK: %145 = OpLabel
-; CHECK: %151 = OpPhi %v4float %149 %146 %113 %147
-; CHECK: OpBranch %51
-; CHECK: %53 = OpLabel
-; CHECK: %112 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_51 %uint_0 %142 %48
-; CHECK: OpBranch %51
-; CHECK: %51 = OpLabel
-; CHECK: %114 = OpPhi %v4float %151 %145 %113 %53
-; CHECK: %30 = OpCompositeExtract %float %114 0
-; CHECK: %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
-; CHECK: %152 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0
-; CHECK: %153 = OpULessThan %bool %uint_0 %152
-; CHECK: OpSelectionMerge %154 None
-; CHECK: OpBranchConditional %153 %155 %156
-; CHECK: %155 = OpLabel
-; CHECK: OpStore %31 %30
-; CHECK: OpBranch %154
-; CHECK: %156 = OpLabel
-; CHECK: %158 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_54 %uint_1 %uint_0 %uint_0
-; CHECK: OpBranch %154
-; CHECK: %154 = OpLabel
+;CHECK-NOT: OpStore %31 %29
+;CHECK: {{%\w+}} = OpLoad %v3uint {{%\w+}}
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5314 {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_49 {{%\w+}} %uint_5 %uint_1 %uint_0 {{%\w+}}
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpLoad %uint %25
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpPhi %uint {{%\w+}} {{%\w+}} [[null_uint]] {{%\w+}}
+;CHECK: {{%\w+}} = OpAccessChain %_ptr_UniformConstant_13 %images {{%\w+}}
+;CHECK: {{%\w+}} = OpLoad %13 {{%\w+}}
+;CHECK: {{%\w+}} = OpLoad %v3uint {{%\w+}}
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5314 {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_52 {{%\w+}} %uint_5 %uint_3 {{%\w+}} %uint_0
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpLoad %13 {{%\w+}}
+;CHECK: {{%\w+}} = OpImageRead %v4float {{%\w+}} %20
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}}
+;CHECK: {{%\w+}} = OpCompositeExtract %float {{%\w+}} 0
+;CHECK: {{%\w+}} = OpAccessChain %_ptr_Uniform_float %sbo %int_1
+;CHECK: {{%\w+}} = OpLoad %v3uint {{%\w+}}
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5314 {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_55 {{%\w+}} %uint_5 %uint_1 %uint_0 {{%\w+}}
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpStore %31 {{%\w+}}
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
 OpReturn
 OpFunctionEnd
 )";
-
-  const std::string new_funcs = kDirectRead2 + kStreamWrite4Ray + kDirectRead4;
+  // clang-format on
 
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs,
-                                               true, 7u, 23u, true, true, false,
-                                               false, false);
+  SinglePassRunAndMatch<InstBindlessCheckPass>(defs + kImportStub + main_func,
+                                               true, 23u);
 }
 
 TEST_F(InstBindlessTest,
@@ -3310,12 +2405,12 @@
   // #extension GL_EXT_nonuniform_qualifier : require
   // #extension GL_NV_ray_tracing : require
   //
-  // layout(set = 0, binding = 0, std140) buffer StorageBuffer {
+  // layout(set = 2, binding = 1, std140) buffer StorageBuffer {
   //   uint index;
   //   float red;
   // } sbo;
   //
-  // layout(set = 0, binding = 1, rgba32f) readonly uniform image2D images[];
+  // layout(set = 2, binding = 3, rgba32f) readonly uniform image2D images[];
   //
   // void main()
   // {
@@ -3326,13 +2421,13 @@
   const std::string defs = R"(
 OpCapability RuntimeDescriptorArray
 OpCapability RayTracingNV
+;CHECK: OpCapability Linkage
 OpExtension "SPV_EXT_descriptor_indexing"
 OpExtension "SPV_NV_ray_tracing"
-; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
 %1 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel Logical GLSL450
 OpEntryPoint AnyHitNV %main "main"
-; CHECK: OpEntryPoint AnyHitNV %main "main" %89
+;CHECK: OpEntryPoint AnyHitNV %main "main" [[launch_id:%\w+]]
 OpSource GLSL 460
 OpSourceExtension "GL_EXT_nonuniform_qualifier"
 OpSourceExtension "GL_NV_ray_tracing"
@@ -3345,14 +2440,13 @@
 OpMemberDecorate %StorageBuffer 0 Offset 0
 OpMemberDecorate %StorageBuffer 1 Offset 4
 OpDecorate %StorageBuffer BufferBlock
-OpDecorate %sbo DescriptorSet 0
-OpDecorate %sbo Binding 0
-OpDecorate %images DescriptorSet 0
-OpDecorate %images Binding 1
+OpDecorate %sbo DescriptorSet 2
+OpDecorate %sbo Binding 1
+OpDecorate %images DescriptorSet 2
+OpDecorate %images Binding 3
 OpDecorate %images NonWritable
-; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
-)" + kInputDecorations + kOutputDecorations + R"(
-; CHECK: OpDecorate %89 BuiltIn LaunchIdNV
+)" + kImportDeco + R"(
+;CHECK: OpDecorate [[launch_id]] BuiltIn LaunchIdNV
 %void = OpTypeVoid
 %3 = OpTypeFunction %void
 %uint = OpTypeInt 32 0
@@ -3374,36 +2468,10 @@
 %v4float = OpTypeVector %float 4
 %uint_0 = OpConstant %uint 0
 %_ptr_Uniform_float = OpTypePointer Uniform %float
-; CHECK: %uint_1 = OpConstant %uint 1
-; CHECK: %34 = OpTypeFunction %uint %uint %uint
-; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
-)" + kInputGlobals + R"(
-; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-; CHECK: %bool = OpTypeBool
-; CHECK: %57 = OpTypeFunction %void %uint %uint %uint %uint
-)" + kOutputGlobals + R"(
-; CHECK: %uint_10 = OpConstant %uint 10
-; CHECK: %uint_4 = OpConstant %uint 4
-; CHECK: %uint_23 = OpConstant %uint 23
-; CHECK: %uint_2 = OpConstant %uint 2
-; CHECK: %uint_5315 = OpConstant %uint 5315
-; CHECK: %uint_3 = OpConstant %uint 3
-; CHECK: %v3uint = OpTypeVector %uint 3
-; CHECK: %_ptr_Input_v3uint = OpTypePointer Input %v3uint
-; CHECK: %89 = OpVariable %_ptr_Input_v3uint Input
-; CHECK: %uint_5 = OpConstant %uint 5
-; CHECK: %uint_6 = OpConstant %uint 6
-; CHECK: %uint_7 = OpConstant %uint 7
-; CHECK: %uint_8 = OpConstant %uint 8
-; CHECK: %uint_9 = OpConstant %uint 9
-; CHECK: %uint_51 = OpConstant %uint 51
-; CHECK: %113 = OpConstantNull %v4float
-; CHECK: %116 = OpTypeFunction %uint %uint %uint %uint %uint
-; CHECK: %uint_48 = OpConstant %uint 48
-; CHECK: %141 = OpConstantNull %uint
-; CHECK: %uint_54 = OpConstant %uint 54
+;CHECK: [[launch_id]] = OpVariable %_ptr_Input_v3uint Input
+;CHECK: [[null_uint:%\w+]] = OpConstantNull %uint
+;CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float
 )";
-  // clang-format on
 
   const std::string main_func = R"(
 %main = OpFunction %void None %3
@@ -3416,69 +2484,70 @@
 %29 = OpCompositeExtract %float %27 0
 %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
 OpStore %31 %29
-; CHECK-NOT: OpStore %31 %29
-; CHECK: %133 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0
-; CHECK: %134 = OpULessThan %bool %uint_0 %133
-; CHECK: OpSelectionMerge %135 None
-; CHECK: OpBranchConditional %134 %136 %137
-; CHECK: %136 = OpLabel
-; CHECK: %138 = OpLoad %uint %25
-; CHECK: OpBranch %135
-; CHECK: %137 = OpLabel
-; CHECK: %140 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_48 %uint_1 %uint_0 %uint_0
-; CHECK: OpBranch %135
-; CHECK: %135 = OpLabel
-; CHECK: %142 = OpPhi %uint %138 %136 %141 %137
-; CHECK: %27 = OpAccessChain %_ptr_UniformConstant_13 %images %142
-; CHECK: %28 = OpLoad %13 %27
-; CHECK: %48 = OpFunctionCall %uint %inst_bindless_direct_read_2 %uint_1 %uint_1
-; CHECK: %50 = OpULessThan %bool %142 %48
-; CHECK: OpSelectionMerge %51 None
-; CHECK: OpBranchConditional %50 %52 %53
-; CHECK: %52 = OpLabel
-; CHECK: %54 = OpLoad %13 %27
-; CHECK: %143 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_1 %142
-; CHECK: %144 = OpULessThan %bool %uint_0 %143
-; CHECK: OpSelectionMerge %145 None
-; CHECK: OpBranchConditional %144 %146 %147
-; CHECK: %146 = OpLabel
-; CHECK: %148 = OpLoad %13 %27
-; CHECK: %149 = OpImageRead %v4float %148 %20
-; CHECK: OpBranch %145
-; CHECK: %147 = OpLabel
-; CHECK: %150 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_51 %uint_1 %142 %uint_0
-; CHECK: OpBranch %145
-; CHECK: %145 = OpLabel
-; CHECK: %151 = OpPhi %v4float %149 %146 %113 %147
-; CHECK: OpBranch %51
-; CHECK: %53 = OpLabel
-; CHECK: %112 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_51 %uint_0 %142 %48
-; CHECK: OpBranch %51
-; CHECK: %51 = OpLabel
-; CHECK: %114 = OpPhi %v4float %151 %145 %113 %53
-; CHECK: %30 = OpCompositeExtract %float %114 0
-; CHECK: %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
-; CHECK: %152 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0
-; CHECK: %153 = OpULessThan %bool %uint_0 %152
-; CHECK: OpSelectionMerge %154 None
-; CHECK: OpBranchConditional %153 %155 %156
-; CHECK: %155 = OpLabel
-; CHECK: OpStore %31 %30
-; CHECK: OpBranch %154
-; CHECK: %156 = OpLabel
-; CHECK: %158 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_54 %uint_1 %uint_0 %uint_0
-; CHECK: OpBranch %154
-; CHECK: %154 = OpLabel
+;CHECK-NOT: %20 = OpLoad %uint %19
+;CHECK-NOT: %22 = OpAccessChain %_ptr_UniformConstant_13 %images %20
+;CHECK-NOT: %23 = OpLoad %13 %22
+;CHECK-NOT: %27 = OpImageRead %v4float %23 %25
+;CHECK-NOT: %29 = OpCompositeExtract %float %27 0
+;CHECK-NOT: %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
+;CHECK-NOT: OpStore %31 %29
+;CHECK: {{%\w+}} = OpLoad %v3uint {{%\w+}}
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5315 {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_49 {{%\w+}} %uint_2 %uint_1 %uint_0 {{%\w+}}
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpLoad %uint %25
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: [[phi_result:%\w+]] = OpPhi %uint {{%\w+}} {{%\w+}} [[null_uint]] {{%\w+}}
+;CHECK: %27 = OpAccessChain %_ptr_UniformConstant_13 %images [[phi_result]]
+;CHECK: %28 = OpLoad %13 %27
+;CHECK: {{%\w+}} = OpLoad %v3uint {{%\w+}}
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5315 {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_52 {{%\w+}} %uint_2 %uint_3 {{%\w+}} %uint_0
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpLoad %13 %27
+;CHECK: {{%\w+}} = OpImageRead %v4float {{%\w+}} %20
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}}
+;CHECK: %30 = OpCompositeExtract %float {{%\w+}} 0
+;CHECK: %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
+;CHECK: {{%\w+}} = OpLoad %v3uint {{%\w+}}
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5315 {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_55 {{%\w+}} %uint_2 %uint_1 %uint_0 {{%\w+}}
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpStore %31 %30
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
 OpReturn
 OpFunctionEnd
 )";
-
-  const std::string new_funcs = kDirectRead2 + kStreamWrite4Ray + kDirectRead4;
+  // clang-format on
 
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs,
-                                               true, 7u, 23u, true, true, false,
-                                               false, false);
+  SinglePassRunAndMatch<InstBindlessCheckPass>(defs + kImportStub + main_func,
+                                               true, 23u);
 }
 
 TEST_F(InstBindlessTest,
@@ -3487,12 +2556,12 @@
   // #extension GL_EXT_nonuniform_qualifier : require
   // #extension GL_NV_ray_tracing : require
   //
-  // layout(set = 0, binding = 0, std140) buffer StorageBuffer {
+  // layout(set = 1, binding = 2, std140) buffer StorageBuffer {
   //   uint index;
   //   float red;
   // } sbo;
   //
-  // layout(set = 0, binding = 1, rgba32f) readonly uniform image2D images[];
+  // layout(set = 1, binding = 3, rgba32f) readonly uniform image2D images[];
   //
   // void main()
   // {
@@ -3503,13 +2572,13 @@
   const std::string defs = R"(
 OpCapability RuntimeDescriptorArray
 OpCapability RayTracingNV
+;CHECK: OpCapability Linkage
 OpExtension "SPV_EXT_descriptor_indexing"
 OpExtension "SPV_NV_ray_tracing"
-; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
 %1 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel Logical GLSL450
 OpEntryPoint ClosestHitNV %main "main"
-; CHECK: OpEntryPoint ClosestHitNV %main "main" %89
+;CHECK: OpEntryPoint ClosestHitNV %main "main" [[launch_id:%\w+]]
 OpSource GLSL 460
 OpSourceExtension "GL_EXT_nonuniform_qualifier"
 OpSourceExtension "GL_NV_ray_tracing"
@@ -3522,14 +2591,13 @@
 OpMemberDecorate %StorageBuffer 0 Offset 0
 OpMemberDecorate %StorageBuffer 1 Offset 4
 OpDecorate %StorageBuffer BufferBlock
-OpDecorate %sbo DescriptorSet 0
-OpDecorate %sbo Binding 0
-OpDecorate %images DescriptorSet 0
-OpDecorate %images Binding 1
+OpDecorate %sbo DescriptorSet 1
+OpDecorate %sbo Binding 2
+OpDecorate %images DescriptorSet 1
+OpDecorate %images Binding 3
 OpDecorate %images NonWritable
-; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
-)" + kInputDecorations + kOutputDecorations + R"(
-; CHECK: OpDecorate %89 BuiltIn LaunchIdNV
+)" + kImportDeco + R"(
+;CHECK: OpDecorate [[launch_id]] BuiltIn LaunchIdNV
 %void = OpTypeVoid
 %3 = OpTypeFunction %void
 %uint = OpTypeInt 32 0
@@ -3551,36 +2619,10 @@
 %v4float = OpTypeVector %float 4
 %uint_0 = OpConstant %uint 0
 %_ptr_Uniform_float = OpTypePointer Uniform %float
-; CHECK: %uint_1 = OpConstant %uint 1
-; CHECK: %34 = OpTypeFunction %uint %uint %uint
-; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
-)" + kInputGlobals + R"(
-; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-; CHECK: %bool = OpTypeBool
-; CHECK: %57 = OpTypeFunction %void %uint %uint %uint %uint
-)" + kOutputGlobals + R"(
-; CHECK: %uint_10 = OpConstant %uint 10
-; CHECK: %uint_4 = OpConstant %uint 4
-; CHECK: %uint_23 = OpConstant %uint 23
-; CHECK: %uint_2 = OpConstant %uint 2
-; CHECK: %uint_5316 = OpConstant %uint 5316
-; CHECK: %uint_3 = OpConstant %uint 3
-; CHECK: %v3uint = OpTypeVector %uint 3
-; CHECK: %_ptr_Input_v3uint = OpTypePointer Input %v3uint
-; CHECK: %89 = OpVariable %_ptr_Input_v3uint Input
-; CHECK: %uint_5 = OpConstant %uint 5
-; CHECK: %uint_6 = OpConstant %uint 6
-; CHECK: %uint_7 = OpConstant %uint 7
-; CHECK: %uint_8 = OpConstant %uint 8
-; CHECK: %uint_9 = OpConstant %uint 9
-; CHECK: %uint_51 = OpConstant %uint 51
-; CHECK: %113 = OpConstantNull %v4float
-; CHECK: %116 = OpTypeFunction %uint %uint %uint %uint %uint
-; CHECK: %uint_48 = OpConstant %uint 48
-; CHECK: %141 = OpConstantNull %uint
-; CHECK: %uint_54 = OpConstant %uint 54
+;CHECK: [[launch_id]] = OpVariable %_ptr_Input_v3uint Input
+;CHECK: [[null_uint:%\w+]] = OpConstantNull %uint
+;CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float
 )";
-  // clang-format on
 
   const std::string main_func = R"(
 %main = OpFunction %void None %3
@@ -3593,69 +2635,70 @@
 %29 = OpCompositeExtract %float %27 0
 %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
 OpStore %31 %29
-; CHECK-NOT: OpStore %31 %29
-; CHECK: %133 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0
-; CHECK: %134 = OpULessThan %bool %uint_0 %133
-; CHECK: OpSelectionMerge %135 None
-; CHECK: OpBranchConditional %134 %136 %137
-; CHECK: %136 = OpLabel
-; CHECK: %138 = OpLoad %uint %25
-; CHECK: OpBranch %135
-; CHECK: %137 = OpLabel
-; CHECK: %140 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_48 %uint_1 %uint_0 %uint_0
-; CHECK: OpBranch %135
-; CHECK: %135 = OpLabel
-; CHECK: %142 = OpPhi %uint %138 %136 %141 %137
-; CHECK: %27 = OpAccessChain %_ptr_UniformConstant_13 %images %142
-; CHECK: %28 = OpLoad %13 %27
-; CHECK: %48 = OpFunctionCall %uint %inst_bindless_direct_read_2 %uint_1 %uint_1
-; CHECK: %50 = OpULessThan %bool %142 %48
-; CHECK: OpSelectionMerge %51 None
-; CHECK: OpBranchConditional %50 %52 %53
-; CHECK: %52 = OpLabel
-; CHECK: %54 = OpLoad %13 %27
-; CHECK: %143 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_1 %142
-; CHECK: %144 = OpULessThan %bool %uint_0 %143
-; CHECK: OpSelectionMerge %145 None
-; CHECK: OpBranchConditional %144 %146 %147
-; CHECK: %146 = OpLabel
-; CHECK: %148 = OpLoad %13 %27
-; CHECK: %149 = OpImageRead %v4float %148 %20
-; CHECK: OpBranch %145
-; CHECK: %147 = OpLabel
-; CHECK: %150 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_51 %uint_1 %142 %uint_0
-; CHECK: OpBranch %145
-; CHECK: %145 = OpLabel
-; CHECK: %151 = OpPhi %v4float %149 %146 %113 %147
-; CHECK: OpBranch %51
-; CHECK: %53 = OpLabel
-; CHECK: %112 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_51 %uint_0 %142 %48
-; CHECK: OpBranch %51
-; CHECK: %51 = OpLabel
-; CHECK: %114 = OpPhi %v4float %151 %145 %113 %53
-; CHECK: %30 = OpCompositeExtract %float %114 0
-; CHECK: %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
-; CHECK: %152 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0
-; CHECK: %153 = OpULessThan %bool %uint_0 %152
-; CHECK: OpSelectionMerge %154 None
-; CHECK: OpBranchConditional %153 %155 %156
-; CHECK: %155 = OpLabel
-; CHECK: OpStore %31 %30
-; CHECK: OpBranch %154
-; CHECK: %156 = OpLabel
-; CHECK: %158 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_54 %uint_1 %uint_0 %uint_0
-; CHECK: OpBranch %154
-; CHECK: %154 = OpLabel
+;CHECK-NOT: %20 = OpLoad %uint %19
+;CHECK-NOT: %22 = OpAccessChain %_ptr_UniformConstant_13 %images %20
+;CHECK-NOT: %23 = OpLoad %13 %22
+;CHECK-NOT: %27 = OpImageRead %v4float %23 %25
+;CHECK-NOT: %29 = OpCompositeExtract %float %27 0
+;CHECK-NOT: %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
+;CHECK-NOT: OpStore %31 %29
+;CHECK: {{%\w+}} = OpLoad %v3uint {{%\w+}}
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5316 {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_49 {{%\w+}} %uint_1 %uint_2 %uint_0 {{%\w+}}
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpLoad %uint %25
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: [[phi_result:%\w+]] = OpPhi %uint {{%\w+}} {{%\w+}} [[null_uint]] {{%\w+}}
+;CHECK: %27 = OpAccessChain %_ptr_UniformConstant_13 %images [[phi_result]]
+;CHECK: %28 = OpLoad %13 %27
+;CHECK: {{%\w+}} = OpLoad %v3uint {{%\w+}}
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5316 {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_52 {{%\w+}} %uint_1 %uint_3 {{%\w+}} %uint_0
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpLoad %13 %27
+;CHECK: {{%\w+}} = OpImageRead %v4float {{%\w+}} %20
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}}
+;CHECK: %30 = OpCompositeExtract %float {{%\w+}} 0
+;CHECK: %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
+;CHECK: {{%\w+}} = OpLoad %v3uint {{%\w+}}
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5316 {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_55 {{%\w+}} %uint_1 %uint_2 %uint_0 {{%\w+}}
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpStore %31 %30
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
 OpReturn
 OpFunctionEnd
 )";
-
-  const std::string new_funcs = kDirectRead2 + kStreamWrite4Ray + kDirectRead4;
+  // clang-format on
 
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs,
-                                               true, 7u, 23u, true, true, false,
-                                               false, false);
+  SinglePassRunAndMatch<InstBindlessCheckPass>(defs + kImportStub + main_func,
+                                               true, 23u);
 }
 
 TEST_F(InstBindlessTest,
@@ -3664,12 +2707,12 @@
   // #extension GL_EXT_nonuniform_qualifier : require
   // #extension GL_NV_ray_tracing : require
   //
-  // layout(set = 0, binding = 0, std140) buffer StorageBuffer {
+  // layout(set = 1, binding = 2, std140) buffer StorageBuffer {
   //   uint index;
   //   float red;
   // } sbo;
   //
-  // layout(set = 0, binding = 1, rgba32f) readonly uniform image2D images[];
+  // layout(set = 1, binding = 3, rgba32f) readonly uniform image2D images[];
   //
   // void main()
   // {
@@ -3680,13 +2723,13 @@
   const std::string defs = R"(
 OpCapability RuntimeDescriptorArray
 OpCapability RayTracingNV
+;CHECK: OpCapability Linkage
 OpExtension "SPV_EXT_descriptor_indexing"
 OpExtension "SPV_NV_ray_tracing"
-; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
 %1 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel Logical GLSL450
 OpEntryPoint MissNV %main "main"
-; CHECK: OpEntryPoint MissNV %main "main" %89
+;CHECK: OpEntryPoint MissNV %main "main" [[launch_id:%\w+]]
 OpSource GLSL 460
 OpSourceExtension "GL_EXT_nonuniform_qualifier"
 OpSourceExtension "GL_NV_ray_tracing"
@@ -3699,14 +2742,13 @@
 OpMemberDecorate %StorageBuffer 0 Offset 0
 OpMemberDecorate %StorageBuffer 1 Offset 4
 OpDecorate %StorageBuffer BufferBlock
-OpDecorate %sbo DescriptorSet 0
-OpDecorate %sbo Binding 0
-OpDecorate %images DescriptorSet 0
-OpDecorate %images Binding 1
+OpDecorate %sbo DescriptorSet 1
+OpDecorate %sbo Binding 2
+OpDecorate %images DescriptorSet 1
+OpDecorate %images Binding 3
 OpDecorate %images NonWritable
-; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
-)" + kInputDecorations + kOutputDecorations + R"(
-; CHECK: OpDecorate %89 BuiltIn LaunchIdNV
+)" + kImportDeco + R"(
+;CHECK: OpDecorate [[launch_id]] BuiltIn LaunchIdNV
 %void = OpTypeVoid
 %3 = OpTypeFunction %void
 %uint = OpTypeInt 32 0
@@ -3728,36 +2770,10 @@
 %v4float = OpTypeVector %float 4
 %uint_0 = OpConstant %uint 0
 %_ptr_Uniform_float = OpTypePointer Uniform %float
-; CHECK: %uint_1 = OpConstant %uint 1
-; CHECK: %34 = OpTypeFunction %uint %uint %uint
-; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
-)" + kInputGlobals + R"(
-; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-; CHECK: %bool = OpTypeBool
-; CHECK: %57 = OpTypeFunction %void %uint %uint %uint %uint
-)" + kOutputGlobals + R"(
-; CHECK: %uint_10 = OpConstant %uint 10
-; CHECK: %uint_4 = OpConstant %uint 4
-; CHECK: %uint_23 = OpConstant %uint 23
-; CHECK: %uint_2 = OpConstant %uint 2
-; CHECK: %uint_5317 = OpConstant %uint 5317
-; CHECK: %uint_3 = OpConstant %uint 3
-; CHECK: %v3uint = OpTypeVector %uint 3
-; CHECK: %_ptr_Input_v3uint = OpTypePointer Input %v3uint
-; CHECK: %89 = OpVariable %_ptr_Input_v3uint Input
-; CHECK: %uint_5 = OpConstant %uint 5
-; CHECK: %uint_6 = OpConstant %uint 6
-; CHECK: %uint_7 = OpConstant %uint 7
-; CHECK: %uint_8 = OpConstant %uint 8
-; CHECK: %uint_9 = OpConstant %uint 9
-; CHECK: %uint_51 = OpConstant %uint 51
-; CHECK: %113 = OpConstantNull %v4float
-; CHECK: %116 = OpTypeFunction %uint %uint %uint %uint %uint
-; CHECK: %uint_48 = OpConstant %uint 48
-; CHECK: %141 = OpConstantNull %uint
-; CHECK: %uint_54 = OpConstant %uint 54
+;CHECK: [[launch_id]] = OpVariable %_ptr_Input_v3uint Input
+;CHECK: [[null_uint:%\w+]] = OpConstantNull %uint
+;CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float
 )";
-  // clang-format on
 
   const std::string main_func = R"(
 %main = OpFunction %void None %3
@@ -3770,69 +2786,67 @@
 %29 = OpCompositeExtract %float %27 0
 %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
 OpStore %31 %29
-; CHECK-NOT OpStore %31 %29
-; CHECK: %133 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0
-; CHECK: %134 = OpULessThan %bool %uint_0 %133
-; CHECK: OpSelectionMerge %135 None
-; CHECK: OpBranchConditional %134 %136 %137
-; CHECK: %136 = OpLabel
-; CHECK: %138 = OpLoad %uint %25
-; CHECK: OpBranch %135
-; CHECK: %137 = OpLabel
-; CHECK: %140 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_48 %uint_1 %uint_0 %uint_0
-; CHECK: OpBranch %135
-; CHECK: %135 = OpLabel
-; CHECK: %142 = OpPhi %uint %138 %136 %141 %137
-; CHECK: %27 = OpAccessChain %_ptr_UniformConstant_13 %images %142
-; CHECK: %28 = OpLoad %13 %27
-; CHECK: %48 = OpFunctionCall %uint %inst_bindless_direct_read_2 %uint_1 %uint_1
-; CHECK: %50 = OpULessThan %bool %142 %48
-; CHECK: OpSelectionMerge %51 None
-; CHECK: OpBranchConditional %50 %52 %53
-; CHECK: %52 = OpLabel
-; CHECK: %54 = OpLoad %13 %27
-; CHECK: %143 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_1 %142
-; CHECK: %144 = OpULessThan %bool %uint_0 %143
-; CHECK: OpSelectionMerge %145 None
-; CHECK: OpBranchConditional %144 %146 %147
-; CHECK: %146 = OpLabel
-; CHECK: %148 = OpLoad %13 %27
-; CHECK: %149 = OpImageRead %v4float %148 %20
-; CHECK: OpBranch %145
-; CHECK: %147 = OpLabel
-; CHECK: %150 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_51 %uint_1 %142 %uint_0
-; CHECK: OpBranch %145
-; CHECK: %145 = OpLabel
-; CHECK: %151 = OpPhi %v4float %149 %146 %113 %147
-; CHECK: OpBranch %51
-; CHECK: %53 = OpLabel
-; CHECK: %112 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_51 %uint_0 %142 %48
-; CHECK: OpBranch %51
-; CHECK: %51 = OpLabel
-; CHECK: %114 = OpPhi %v4float %151 %145 %113 %53
-; CHECK: %30 = OpCompositeExtract %float %114 0
-; CHECK: %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
-; CHECK: %152 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0
-; CHECK: %153 = OpULessThan %bool %uint_0 %152
-; CHECK: OpSelectionMerge %154 None
-; CHECK: OpBranchConditional %153 %155 %156
-; CHECK: %155 = OpLabel
-; CHECK: OpStore %31 %30
-; CHECK: OpBranch %154
-; CHECK: %156 = OpLabel
-; CHECK: %158 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_54 %uint_1 %uint_0 %uint_0
-; CHECK: OpBranch %154
-; CHECK: %154 = OpLabel
+;CHECK-NOT: %20 = OpLoad %uint %19
+;CHECK-NOT: %22 = OpAccessChain %_ptr_UniformConstant_13 %images %20
+;CHECK-NOT: %27 = OpImageRead %v4float %23 %25
+;CHECK-NOT: %29 = OpCompositeExtract %float %27 0
+;CHECK-NOT OpStore %31 %29
+;CHECK: {{%\w+}} = OpLoad %v3uint {{%\w+}}
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5317 {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_49 {{%\w+}} %uint_1 %uint_2 %uint_0 {{%\w+}}
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpLoad %uint %25
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: [[phi_result:%\w+]] = OpPhi %uint {{%\w+}} {{%\w+}} [[null_uint]] {{%\w+}}
+;CHECK: %27 = OpAccessChain %_ptr_UniformConstant_13 %images [[phi_result]]
+;CHECK: {{%\w+}} = OpLoad %v3uint {{%\w+}}
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5317 {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_52 {{%\w+}} %uint_1 %uint_3 {{%\w+}} %uint_0
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpLoad %13 %27
+;CHECK: {{%\w+}} = OpImageRead %v4float {{%\w+}} %20
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}}
+;CHECK: %30 = OpCompositeExtract %float {{%\w+}} 0
+;CHECK: %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
+;CHECK: {{%\w+}} = OpLoad %v3uint {{%\w+}}
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5317 {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_55 {{%\w+}} %uint_1 %uint_2 %uint_0 {{%\w+}}
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpStore %31 %30
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
 OpReturn
 OpFunctionEnd
 )";
-
-  const std::string new_funcs = kDirectRead2 + kStreamWrite4Ray + kDirectRead4;
+  // clang-format on
 
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs,
-                                               true, 7u, 23u, true, true, false,
-                                               false, false);
+  SinglePassRunAndMatch<InstBindlessCheckPass>(defs + kImportStub + main_func,
+                                               true, 23u);
 }
 
 TEST_F(InstBindlessTest,
@@ -3841,12 +2855,12 @@
   // #extension GL_EXT_nonuniform_qualifier : require
   // #extension GL_NV_ray_tracing : require
   //
-  // layout(set = 0, binding = 0, std140) buffer StorageBuffer {
+  // layout(set = 1, binding = 2, std140) buffer StorageBuffer {
   //   uint index;
   //   float red;
   // } sbo;
   //
-  // layout(set = 0, binding = 1, rgba32f) readonly uniform image2D images[];
+  // layout(set = 1, binding = 3, rgba32f) readonly uniform image2D images[];
   //
   // void main()
   // {
@@ -3857,13 +2871,13 @@
   const std::string defs = R"(
 OpCapability RuntimeDescriptorArray
 OpCapability RayTracingNV
+;CHECK: OpCapability Linkage
 OpExtension "SPV_EXT_descriptor_indexing"
 OpExtension "SPV_NV_ray_tracing"
-; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
 %1 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel Logical GLSL450
 OpEntryPoint CallableNV %main "main"
-; CHECK: OpEntryPoint CallableNV %main "main" %89
+;CHECK: OpEntryPoint CallableNV %main "main" [[launch_id:%\w+]]
 OpSource GLSL 460
 OpSourceExtension "GL_EXT_nonuniform_qualifier"
 OpSourceExtension "GL_NV_ray_tracing"
@@ -3876,14 +2890,13 @@
 OpMemberDecorate %StorageBuffer 0 Offset 0
 OpMemberDecorate %StorageBuffer 1 Offset 4
 OpDecorate %StorageBuffer BufferBlock
-OpDecorate %sbo DescriptorSet 0
-OpDecorate %sbo Binding 0
-OpDecorate %images DescriptorSet 0
-OpDecorate %images Binding 1
+OpDecorate %sbo DescriptorSet 1
+OpDecorate %sbo Binding 2
+OpDecorate %images DescriptorSet 1
+OpDecorate %images Binding 3
 OpDecorate %images NonWritable
-; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
-)" + kInputDecorations + kOutputDecorations + R"(
-; CHECK: OpDecorate %89 BuiltIn LaunchIdNV
+)" + kImportDeco + R"(
+;CHECK: OpDecorate [[launch_id]] BuiltIn LaunchIdNV
 %void = OpTypeVoid
 %3 = OpTypeFunction %void
 %uint = OpTypeInt 32 0
@@ -3905,36 +2918,9 @@
 %v4float = OpTypeVector %float 4
 %uint_0 = OpConstant %uint 0
 %_ptr_Uniform_float = OpTypePointer Uniform %float
-; CHECK: %uint_1 = OpConstant %uint 1
-; CHECK: %34 = OpTypeFunction %uint %uint %uint
-; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
-)" + kInputGlobals + R"(
-; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-; CHECK: %bool = OpTypeBool
-; CHECK: %57 = OpTypeFunction %void %uint %uint %uint %uint
-)" + kOutputGlobals + R"(
-; CHECK: %uint_10 = OpConstant %uint 10
-; CHECK: %uint_4 = OpConstant %uint 4
-; CHECK: %uint_23 = OpConstant %uint 23
-; CHECK: %uint_2 = OpConstant %uint 2
-; CHECK: %uint_5318 = OpConstant %uint 5318
-; CHECK: %uint_3 = OpConstant %uint 3
-; CHECK: %v3uint = OpTypeVector %uint 3
-; CHECK: %_ptr_Input_v3uint = OpTypePointer Input %v3uint
-; CHECK: %89 = OpVariable %_ptr_Input_v3uint Input
-; CHECK: %uint_5 = OpConstant %uint 5
-; CHECK: %uint_6 = OpConstant %uint 6
-; CHECK: %uint_7 = OpConstant %uint 7
-; CHECK: %uint_8 = OpConstant %uint 8
-; CHECK: %uint_9 = OpConstant %uint 9
-; CHECK: %uint_51 = OpConstant %uint 51
-; CHECK: %113 = OpConstantNull %v4float
-; CHECK: %116 = OpTypeFunction %uint %uint %uint %uint %uint
-; CHECK: %uint_48 = OpConstant %uint 48
-; CHECK: %141 = OpConstantNull %uint
-; CHECK: %uint_54 = OpConstant %uint 54
+;CHECK: [[null_uint:%\w+]] = OpConstantNull %uint
+;CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float
 )";
-  // clang-format on
 
   const std::string main_func = R"(
 %main = OpFunction %void None %3
@@ -3945,71 +2931,71 @@
 %23 = OpLoad %13 %22
 %27 = OpImageRead %v4float %23 %25
 %29 = OpCompositeExtract %float %27 0
+;CHECK-NOT: %20 = OpLoad %uint %19
+;CHECK-NOT: %22 = OpAccessChain %_ptr_UniformConstant_13 %images %20
+;CHECK: {{%\w+}} = OpLoad %v3uint {{%\w+}}
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5318 {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_49 {{%\w+}} %uint_1 %uint_2 %uint_0 {{%\w+}}
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpLoad %uint %25
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpPhi %uint {{%\w+}} {{%\w+}} [[null_uint]] {{%\w+}}
+;CHECK: %27 = OpAccessChain %_ptr_UniformConstant_13 %images {{%\w+}}
+;CHECK-NOT: %23 = OpLoad %13 %22
+;CHECK-NOT: %27 = OpImageRead %v4float %23 %25
+;CHECK-NOT: %29 = OpCompositeExtract %float %27 0
+;CHECK: {{%\w+}} = OpLoad %v3uint {{%\w+}}
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5318 {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_52 {{%\w+}} %uint_1 %uint_3 {{%\w+}} %uint_0
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpLoad %13 %27
+;CHECK: {{%\w+}} = OpImageRead %v4float {{%\w+}} %20
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}}
+;CHECK: %30 = OpCompositeExtract %float {{%\w+}} 0
+;CHECK: %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
 %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
 OpStore %31 %29
-; CHECK-NOT: OpStore %31 %29
-; CHECK: %133 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0
-; CHECK: %134 = OpULessThan %bool %uint_0 %133
-; CHECK: OpSelectionMerge %135 None
-; CHECK: OpBranchConditional %134 %136 %137
-; CHECK: %136 = OpLabel
-; CHECK: %138 = OpLoad %uint %25
-; CHECK: OpBranch %135
-; CHECK: %137 = OpLabel
-; CHECK: %140 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_48 %uint_1 %uint_0 %uint_0
-; CHECK: OpBranch %135
-; CHECK: %135 = OpLabel
-; CHECK: %142 = OpPhi %uint %138 %136 %141 %137
-; CHECK: %27 = OpAccessChain %_ptr_UniformConstant_13 %images %142
-; CHECK: %28 = OpLoad %13 %27
-; CHECK: %48 = OpFunctionCall %uint %inst_bindless_direct_read_2 %uint_1 %uint_1
-; CHECK: %50 = OpULessThan %bool %142 %48
-; CHECK: OpSelectionMerge %51 None
-; CHECK: OpBranchConditional %50 %52 %53
-; CHECK: %52 = OpLabel
-; CHECK: %54 = OpLoad %13 %27
-; CHECK: %143 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_1 %142
-; CHECK: %144 = OpULessThan %bool %uint_0 %143
-; CHECK: OpSelectionMerge %145 None
-; CHECK: OpBranchConditional %144 %146 %147
-; CHECK: %146 = OpLabel
-; CHECK: %148 = OpLoad %13 %27
-; CHECK: %149 = OpImageRead %v4float %148 %20
-; CHECK: OpBranch %145
-; CHECK: %147 = OpLabel
-; CHECK: %150 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_51 %uint_1 %142 %uint_0
-; CHECK: OpBranch %145
-; CHECK: %145 = OpLabel
-; CHECK: %151 = OpPhi %v4float %149 %146 %113 %147
-; CHECK: OpBranch %51
-; CHECK: %53 = OpLabel
-; CHECK: %112 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_51 %uint_0 %142 %48
-; CHECK: OpBranch %51
-; CHECK: %51 = OpLabel
-; CHECK: %114 = OpPhi %v4float %151 %145 %113 %53
-; CHECK: %30 = OpCompositeExtract %float %114 0
-; CHECK: %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
-; CHECK: %152 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0
-; CHECK: %153 = OpULessThan %bool %uint_0 %152
-; CHECK: OpSelectionMerge %154 None
-; CHECK: OpBranchConditional %153 %155 %156
-; CHECK: %155 = OpLabel
-; CHECK: OpStore %31 %30
-; CHECK: OpBranch %154
-; CHECK: %156 = OpLabel
-; CHECK: %158 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_54 %uint_1 %uint_0 %uint_0
-; CHECK: OpBranch %154
-; CHECK: %154 = OpLabel
+;CHECK-NOT: %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
+;CHECK-NOT: OpStore %31 %29
+;CHECK: {{%\w+}} = OpLoad %v3uint {{%\w+}}
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5318 {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_55 {{%\w+}} %uint_1 %uint_2 %uint_0 {{%\w+}}
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpStore %31 %30
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
 OpReturn
 OpFunctionEnd
 )";
-
-  const std::string new_funcs = kDirectRead2 + kStreamWrite4Ray + kDirectRead4;
+  // clang-format on
 
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs,
-                                               true, 7u, 23u, true, true, false,
-                                               false, false);
+  SinglePassRunAndMatch<InstBindlessCheckPass>(defs + kImportStub + main_func,
+                                               true, 23u);
 }
 
 TEST_F(InstBindlessTest, InstBoundsInitSameBlockOpReplication) {
@@ -4024,13 +3010,13 @@
   // layout(location = 0) in vec2 inTexcoord;
   // layout(location = 0) out vec4 outColor;
   //
-  // layout(set = 0, binding = 0) uniform Uniforms {
+  // layout(set = 1, binding = 0) uniform Uniforms {
   //   vec2 var0;
   // } uniforms;
   //
-  // layout(set = 0, binding = 1) uniform sampler uniformSampler;
-  // layout(set = 0, binding = 2) uniform texture2D uniformTex;
-  // layout(set = 0, binding = 3) uniform texture2D uniformTexArr[8];
+  // layout(set = 1, binding = 1) uniform sampler uniformSampler;
+  // layout(set = 1, binding = 2) uniform texture2D uniformTex;
+  // layout(set = 1, binding = 3) uniform texture2D uniformTexArr[8];
   //
   // void main() {
   //   int index = 0;
@@ -4044,12 +3030,12 @@
 OpCapability Shader
 OpCapability ShaderNonUniformEXT
 OpCapability SampledImageArrayNonUniformIndexingEXT
+;CHECK: OpCapability Linkage
 OpExtension "SPV_EXT_descriptor_indexing"
-; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
 %1 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel Logical GLSL450
 OpEntryPoint Fragment %main "main" %inTexcoord %outColor
-; CHECK: OpEntryPoint Fragment %main "main" %inTexcoord %outColor %gl_FragCoord
+;CHECK: OpEntryPoint Fragment %main "main" %inTexcoord %outColor %gl_FragCoord
 OpExecutionMode %main OriginUpperLeft
 OpSource GLSL 450
 OpSourceExtension "GL_EXT_nonuniform_qualifier"
@@ -4065,26 +3051,25 @@
 OpMemberName %Uniforms 0 "var0"
 OpName %uniforms "uniforms"
 OpName %outColor "outColor"
-OpDecorate %uniformTexArr DescriptorSet 0
+OpDecorate %uniformTexArr DescriptorSet 1
 OpDecorate %uniformTexArr Binding 3
 OpDecorate %19 NonUniformEXT
 OpDecorate %22 NonUniformEXT
-OpDecorate %uniformSampler DescriptorSet 0
+OpDecorate %uniformSampler DescriptorSet 1
 OpDecorate %uniformSampler Binding 1
 OpDecorate %inTexcoord Location 0
-OpDecorate %uniformTex DescriptorSet 0
+OpDecorate %uniformTex DescriptorSet 1
 OpDecorate %uniformTex Binding 2
 OpMemberDecorate %Uniforms 0 Offset 0
 OpDecorate %Uniforms Block
-OpDecorate %uniforms DescriptorSet 0
+OpDecorate %uniforms DescriptorSet 1
 OpDecorate %uniforms Binding 0
 OpDecorate %outColor Location 0
-; CHECK: OpDecorate %63 NonUniform
-; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
-)" + kOutputDecorations + R"(
-; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
-)" + kInputDecorations + R"(
-; CHECK: OpDecorate %151 NonUniform
+;CHECK: OpDecorate {{%\w+}} NonUniform
+;CHECK: OpDecorate {{%\w+}} NonUniform
+)" + kImportDeco + R"(
+;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
+;CHECK: OpDecorate [[desc_state_result:%\w+]] NonUniform
 %void = OpTypeVoid
 %3 = OpTypeFunction %void
 %int = OpTypeInt 32 1
@@ -4116,32 +3101,9 @@
 %_ptr_Output_v4float = OpTypePointer Output %v4float
 %outColor = OpVariable %_ptr_Output_v4float Output
 %float_0 = OpConstant %float 0
-; CHECK: %bool = OpTypeBool
-; CHECK: %68 = OpTypeFunction %void %uint %uint %uint %uint
-; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
-)" + kOutputGlobals + R"(
-; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-; CHECK: %uint_10 = OpConstant %uint 10
-; CHECK: %uint_4 = OpConstant %uint 4
-; CHECK: %uint_1 = OpConstant %uint 1
-; CHECK: %uint_23 = OpConstant %uint 23
-; CHECK: %uint_2 = OpConstant %uint 2
-; CHECK: %uint_3 = OpConstant %uint 3
-; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
-; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
-; CHECK: %v4uint = OpTypeVector %uint 4
-; CHECK: %uint_5 = OpConstant %uint 5
-; CHECK: %uint_7 = OpConstant %uint 7
-; CHECK: %uint_9 = OpConstant %uint 9
-; CHECK: %uint_79 = OpConstant %uint 79
-; CHECK: %122 = OpConstantNull %v4float
-; CHECK: %126 = OpTypeFunction %uint %uint %uint %uint %uint
-)" + kInputGlobals + R"(
-; CHECK: %uint_87 = OpConstant %uint 87
-; CHECK: %165 = OpConstantNull %v2float
-; CHECK: %uint_89 = OpConstant %uint 89
+;CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float
+;CHECK: [[null_v2float:%\w+]] = OpConstantNull %v2float
 )";
-  // clang-format on
 
   const std::string main_func = R"(
 %main = OpFunction %void None %3
@@ -4158,6 +3120,26 @@
 %32 = OpLoad %v2float %inTexcoord
 %34 = OpImageSampleImplicitLod %v4float %28 %32
 %36 = OpCompositeExtract %float %34 0
+;CHECK-NOT: %34 = OpImageSampleImplicitLod %v4float %28 %32
+;CHECK-NOT: %36 = OpCompositeExtract %float %34 0
+;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord
+;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}}
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0
+;CHECK: {{%\w+}} = OpBitcast %uint %19
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_80 {{%\w+}} %uint_1 %uint_3 {{%\w+}} %uint_0
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpLoad %13 %21
+;CHECK: {{%\w+}} = OpSampledImage %27 {{%\w+}} %26
+;CHECK: {{%\w+}} = OpImageSampleImplicitLod %v4float {{%\w+}} %32
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}}
 OpStore %x %36
 %39 = OpLoad %13 %uniformTex
 %40 = OpLoad %23 %uniformSampler
@@ -4166,38 +3148,48 @@
 %47 = OpAccessChain %_ptr_Uniform_v2float %uniforms %int_0
 %48 = OpLoad %v2float %47
 %49 = OpFMul %v2float %42 %48
+;CHECK-NOT: %48 = OpLoad %v2float %47
+;CHECK-NOT: %49 = OpFMul %v2float %42 %48
+;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord
+;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}}
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_88 {{%\w+}} %uint_1 %uint_0 %uint_0 {{%\w+}}
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpLoad %v2float %47
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: [[phi_result:%\w+]] = OpPhi %v2float {{%\w+}} {{%\w+}} [[null_v2float]] {{%\w+}}
+;CHECK: %49 = OpFMul %v2float %42 [[phi_result]]
 %50 = OpImageSampleImplicitLod %v4float %41 %49
 %51 = OpCompositeExtract %float %50 0
-; CHECK-NOT: %51 = OpCompositeExtract %float %50 0
-; CHECK: %157 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0
-; CHECK: %158 = OpULessThan %bool %uint_0 %157
-; CHECK: OpSelectionMerge %159 None
-; CHECK: OpBranchConditional %158 %160 %161
-; CHECK: %160 = OpLabel
-; CHECK: %162 = OpLoad %v2float %47
-; CHECK: OpBranch %159
-; CHECK: %161 = OpLabel
-; CHECK: %164 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_87 %uint_1 %uint_0 %uint_0
-; CHECK: OpBranch %159
-; CHECK: %159 = OpLabel
-; CHECK: %166 = OpPhi %v2float %162 %160 %165 %161
-; CHECK: %49 = OpFMul %v2float %42 %166
-; CHECK: %167 = OpSampledImage %27 %39 %40
-; CHECK: %168 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_2 %uint_0
-; CHECK: %169 = OpULessThan %bool %uint_0 %168
-; CHECK: OpSelectionMerge %170 None
-; CHECK: OpBranchConditional %169 %171 %172
-; CHECK: %171 = OpLabel
-; CHECK: %173 = OpLoad %13 %uniformTex
-; CHECK: %174 = OpSampledImage %27 %173 %40
-; CHECK: %175 = OpImageSampleImplicitLod %v4float %174 %49
-; CHECK: OpBranch %170
-; CHECK: %172 = OpLabel
-; CHECK: %177 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_89 %uint_1 %uint_0 %uint_0
-; CHECK: OpBranch %170
-; CHECK: %170 = OpLabel
-; CHECK: %178 = OpPhi %v4float %175 %171 %122 %172
-; CHECK: %51 = OpCompositeExtract %float %178 0
+OpStore %y %51
+;CHECK-NOT: %50 = OpImageSampleImplicitLod %v4float %41 %49
+;CHECK-NOT: %51 = OpCompositeExtract %float %50 0
+;CHECK: {{%\w+}} = OpSampledImage %27 %39 %40
+;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord
+;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}}
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_90 {{%\w+}} %uint_1 %uint_2 %uint_0 %uint_0
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpLoad %13 %uniformTex
+;CHECK: {{%\w+}} = OpSampledImage %27 {{%\w+}} %40
+;CHECK: {{%\w+}} = OpImageSampleImplicitLod %v4float {{%\w+}} %49
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}}
+;CHECK: %51 = OpCompositeExtract %float {{%\w+}} 0
 OpStore %y %51
 %54 = OpLoad %float %x
 %55 = OpLoad %float %y
@@ -4206,13 +3198,11 @@
 OpReturn
 OpFunctionEnd
 )";
-
-  const std::string new_funcs = kStreamWrite4Frag + kDirectRead4;
+  // clang-format on
 
   SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs,
-                                               true, 7u, 23u, true, true, false,
-                                               false, false);
+  SinglePassRunAndMatch<InstBindlessCheckPass>(defs + kImportStub + main_func,
+                                               true, 23u);
 }
 
 TEST_F(InstBindlessTest, MultipleUniformNonAggregateRefsNoDescInit) {
@@ -4254,161 +3244,145 @@
 
   // clang-format off
   const std::string text = R"(
-               OpCapability Shader
-;CHECK:        OpExtension "SPV_KHR_storage_buffer_storage_class"
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %MainPs "MainPs" %_ %__0 %g_tColor %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor
-;CHECK:        OpEntryPoint Fragment %MainPs "MainPs" %_ %__0 %g_tColor %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor %inst_bindless_input_buffer %inst_bindless_output_buffer %gl_FragCoord
-               OpExecutionMode %MainPs OriginUpperLeft
-               OpSource HLSL 500
-               OpName %MainPs "MainPs"
-               OpName %PerViewPushConst_t "PerViewPushConst_t"
-               OpMemberName %PerViewPushConst_t 0 "g_B"
-               OpName %_ ""
-               OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t"
-               OpMemberName %PerViewConstantBuffer_t 0 "g_TexOff0"
-               OpMemberName %PerViewConstantBuffer_t 1 "g_TexOff1"
-               OpName %__0 ""
-               OpName %g_tColor "g_tColor"
-               OpName %g_sAniso "g_sAniso"
-               OpName %i_vTextureCoords "i.vTextureCoords"
-               OpName %_entryPointOutput_vColor "@entryPointOutput.vColor"
-               OpMemberDecorate %PerViewPushConst_t 0 Offset 0
-               OpDecorate %PerViewPushConst_t Block
-               OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0
-               OpMemberDecorate %PerViewConstantBuffer_t 1 Offset 8
-               OpDecorate %PerViewConstantBuffer_t Block
-               OpDecorate %__0 DescriptorSet 0
-               OpDecorate %__0 Binding 1
-               OpDecorate %g_tColor DescriptorSet 0
-               OpDecorate %g_tColor Binding 0
-               OpDecorate %g_sAniso DescriptorSet 0
-               OpDecorate %g_sAniso Binding 2
-               OpDecorate %i_vTextureCoords Location 0
-               OpDecorate %_entryPointOutput_vColor Location 0
- ;CHECK:       OpDecorate %_runtimearr_uint ArrayStride 4
-)" + kInputDecorations + kOutputDecorations + R"(
- ;CHECK:       OpDecorate %gl_FragCoord BuiltIn FragCoord
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v2float = OpTypeVector %float 2
-    %v4float = OpTypeVector %float 4
-       %uint = OpTypeInt 32 0
+OpCapability Shader
+;CHECK: OpCapability Linkage
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %MainPs "MainPs" %_ %__0 %g_tColor %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor
+;CHECK: OpEntryPoint Fragment %MainPs "MainPs" %_ %__0 %g_tColor %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord
+OpExecutionMode %MainPs OriginUpperLeft
+OpSource HLSL 500
+OpName %MainPs "MainPs"
+OpName %PerViewPushConst_t "PerViewPushConst_t"
+OpMemberName %PerViewPushConst_t 0 "g_B"
+OpName %_ ""
+OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t"
+OpMemberName %PerViewConstantBuffer_t 0 "g_TexOff0"
+OpMemberName %PerViewConstantBuffer_t 1 "g_TexOff1"
+OpName %__0 ""
+OpName %g_tColor "g_tColor"
+OpName %g_sAniso "g_sAniso"
+OpName %i_vTextureCoords "i.vTextureCoords"
+OpName %_entryPointOutput_vColor "@entryPointOutput.vColor"
+OpMemberDecorate %PerViewPushConst_t 0 Offset 0
+OpDecorate %PerViewPushConst_t Block
+OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0
+OpMemberDecorate %PerViewConstantBuffer_t 1 Offset 8
+OpDecorate %PerViewConstantBuffer_t Block
+OpDecorate %__0 DescriptorSet 0
+OpDecorate %__0 Binding 1
+OpDecorate %g_tColor DescriptorSet 0
+OpDecorate %g_tColor Binding 0
+OpDecorate %g_sAniso DescriptorSet 0
+OpDecorate %g_sAniso Binding 2
+OpDecorate %i_vTextureCoords Location 0
+OpDecorate %_entryPointOutput_vColor Location 0
+)" + kImportDeco + R"(
+;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v2float = OpTypeVector %float 2
+%v4float = OpTypeVector %float 4
+%uint = OpTypeInt 32 0
 %PerViewPushConst_t = OpTypeStruct %uint
 %_ptr_PushConstant_PerViewPushConst_t = OpTypePointer PushConstant %PerViewPushConst_t
-          %_ = OpVariable %_ptr_PushConstant_PerViewPushConst_t PushConstant
-        %int = OpTypeInt 32 1
-      %int_0 = OpConstant %int 0
+%_ = OpVariable %_ptr_PushConstant_PerViewPushConst_t PushConstant
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
 %_ptr_PushConstant_uint = OpTypePointer PushConstant %uint
-       %bool = OpTypeBool
-     %uint_0 = OpConstant %uint 0
+%bool = OpTypeBool
+%uint_0 = OpConstant %uint 0
 %PerViewConstantBuffer_t = OpTypeStruct %v2float %v2float
 %_ptr_Uniform_PerViewConstantBuffer_t = OpTypePointer Uniform %PerViewConstantBuffer_t
-        %__0 = OpVariable %_ptr_Uniform_PerViewConstantBuffer_t Uniform
+%__0 = OpVariable %_ptr_Uniform_PerViewConstantBuffer_t Uniform
 %_ptr_Uniform_v2float = OpTypePointer Uniform %v2float
-      %int_1 = OpConstant %int 1
-         %49 = OpTypeImage %float 2D 0 0 0 1 Unknown
+%int_1 = OpConstant %int 1
+%49 = OpTypeImage %float 2D 0 0 0 1 Unknown
 %_ptr_UniformConstant_49 = OpTypePointer UniformConstant %49
-   %g_tColor = OpVariable %_ptr_UniformConstant_49 UniformConstant
-         %53 = OpTypeSampler
+%g_tColor = OpVariable %_ptr_UniformConstant_49 UniformConstant
+%53 = OpTypeSampler
 %_ptr_UniformConstant_53 = OpTypePointer UniformConstant %53
-   %g_sAniso = OpVariable %_ptr_UniformConstant_53 UniformConstant
-         %57 = OpTypeSampledImage %49
+%g_sAniso = OpVariable %_ptr_UniformConstant_53 UniformConstant
+%57 = OpTypeSampledImage %49
 %_ptr_Input_v2float = OpTypePointer Input %v2float
 %i_vTextureCoords = OpVariable %_ptr_Input_v2float Input
 %_ptr_Output_v4float = OpTypePointer Output %v4float
 %_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output
- ;CHECK:      %uint_7 = OpConstant %uint 7
- ;CHECK:      %uint_1 = OpConstant %uint 1
- ;CHECK:         %122 = OpTypeFunction %uint %uint %uint %uint
- ;CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
- )" + kInputGlobals + R"(
- ;CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
- ;CHECK:     %uint_4 = OpConstant %uint 4
- ;CHECK:         %148 = OpTypeFunction %void %uint %uint %uint %uint %uint
- )" + kOutputGlobals + R"(
- ;CHECK:    %uint_11 = OpConstant %uint 11
- ;CHECK:    %uint_23 = OpConstant %uint 23
- ;CHECK:     %uint_2 = OpConstant %uint 2
- ;CHECK:      %uint_3 = OpConstant %uint 3
- ;CHECK:%_ptr_Input_v4float = OpTypePointer Input %v4float
- ;CHECK:%gl_FragCoord = OpVariable %_ptr_Input_v4float Input
- ;CHECK:     %v4uint = OpTypeVector %uint 4
- ;CHECK:     %uint_5 = OpConstant %uint 5
- ;CHECK:     %uint_8 = OpConstant %uint 8
- ;CHECK:     %uint_9 = OpConstant %uint 9
- ;CHECK:    %uint_10 = OpConstant %uint 10
- ;CHECK:    %uint_71 = OpConstant %uint 71
- ;CHECK:        %202 = OpConstantNull %v2float
- ;CHECK:    %uint_75 = OpConstant %uint 75
-     %MainPs = OpFunction %void None %3
-          %5 = OpLabel
- ;CHECK: %140 = OpFunctionCall %uint %inst_bindless_direct_read_3 %uint_1 %uint_1 %uint_0
- ;CHECK:        OpBranch %117
- ;CHECK: %117 = OpLabel
- ;CHECK:        OpBranch %116
- ;CHECK: %116 = OpLabel
-         %69 = OpLoad %v2float %i_vTextureCoords
-         %82 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0
-         %83 = OpLoad %uint %82
-         %84 = OpINotEqual %bool %83 %uint_0
-               OpSelectionMerge %91 None
-               OpBranchConditional %84 %85 %88
-         %85 = OpLabel
-         %86 = OpAccessChain %_ptr_Uniform_v2float %__0 %int_0
-         %87 = OpLoad %v2float %86
- ;CHECK-NOT:     %87 = OpLoad %v2float %86
- ;CHECK:        %119 = OpIAdd %uint %uint_0 %uint_7
- ;CHECK:        %141 = OpULessThan %bool %119 %140
- ;CHECK:               OpSelectionMerge %143 None
- ;CHECK:               OpBranchConditional %141 %144 %145
- ;CHECK:        %144 = OpLabel
- ;CHECK:        %146 = OpLoad %v2float %86
- ;CHECK:               OpBranch %143
- ;CHECK:        %145 = OpLabel
- ;CHECK:        %201 = OpFunctionCall %void %inst_bindless_stream_write_5 %uint_71 %uint_4 %uint_0 %119 %140
- ;CHECK:               OpBranch %143
- ;CHECK:        %143 = OpLabel
- ;CHECK:        %203 = OpPhi %v2float %146 %144 %202 %145
-               OpBranch %91
-         %88 = OpLabel
-         %89 = OpAccessChain %_ptr_Uniform_v2float %__0 %int_1
-         %90 = OpLoad %v2float %89
- ;CHECK-NOT:     %90 = OpLoad %v2float %89
- ;CHECK:        %204 = OpIAdd %uint %uint_8 %uint_7
- ;CHECK:        %205 = OpULessThan %bool %204 %140
- ;CHECK:               OpSelectionMerge %206 None
- ;CHECK:               OpBranchConditional %205 %207 %208
- ;CHECK:        %207 = OpLabel
- ;CHECK:        %209 = OpLoad %v2float %89
- ;CHECK:               OpBranch %206
- ;CHECK:        %208 = OpLabel
- ;CHECK:        %211 = OpFunctionCall %void %inst_bindless_stream_write_5 %uint_75 %uint_4 %uint_0 %204 %140
- ;CHECK:               OpBranch %206
- ;CHECK:        %206 = OpLabel
- ;CHECK:        %212 = OpPhi %v2float %209 %207 %202 %208
-               OpBranch %91
-         %91 = OpLabel
-        %115 = OpPhi %v2float %87 %85 %90 %88
- ;CHECK-NOT:       %115 = OpPhi %v2float %87 %85 %90 %88
- ;CHECK:           %115 = OpPhi %v2float %203 %143 %212 %206
-         %95 = OpFAdd %v2float %69 %115
-         %96 = OpLoad %49 %g_tColor
-         %97 = OpLoad %53 %g_sAniso
-         %98 = OpSampledImage %57 %96 %97
-        %100 = OpImageSampleImplicitLod %v4float %98 %95
-               OpStore %_entryPointOutput_vColor %100
-               OpReturn
-               OpFunctionEnd
-)" + kDirectRead3 + kStreamWrite5Frag;
+;CHECK: %v4uint = OpTypeVector %uint 4
+;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
+;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
+;CHECK: [[null_v2float:%\w+]] = OpConstantNull %v2float
+ )" + kImportStub + R"(
+%MainPs = OpFunction %void None %3
+%5 = OpLabel
+%69 = OpLoad %v2float %i_vTextureCoords
+%82 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0
+%83 = OpLoad %uint %82
+%84 = OpINotEqual %bool %83 %uint_0
+OpSelectionMerge %91 None
+OpBranchConditional %84 %85 %88
+%85 = OpLabel
+%86 = OpAccessChain %_ptr_Uniform_v2float %__0 %int_0
+%87 = OpLoad %v2float %86
+;CHECK-NOT:     %87 = OpLoad %v2float %86
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpIAdd %uint %uint_0 %uint_7
+;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord
+;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}}
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0
+;CHECK: [[desc_state_result:%\w+]] = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_72 {{%\w+}} %uint_0 %uint_1 %uint_0 {{%\w+}}
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional [[desc_state_result]] {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpLoad %v2float %86
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+ ;CHECK: {{%\w+}} = OpPhi %v2float {{%\w+}} {{%\w+}} [[null_v2float]] {{%\w+}}
+OpBranch %91
+%88 = OpLabel
+%89 = OpAccessChain %_ptr_Uniform_v2float %__0 %int_1
+%90 = OpLoad %v2float %89
+;CHECK-NOT:     %90 = OpLoad %v2float %89
+;CHECK: {{%\w+}} = OpIAdd %uint %uint_8 %uint_7
+;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord
+;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}}
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_76 {{%\w+}} %uint_0 %uint_1 %uint_0 {{%\w+}}
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpLoad %v2float %89
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpPhi %v2float {{%\w+}} {{%\w+}} [[null_v2float]] {{%\w+}}
+OpBranch %91
+%91 = OpLabel
+%115 = OpPhi %v2float %87 %85 %90 %88
+;CHECK-NOT:       %115 = OpPhi %v2float %87 %85 %90 %88
+;CHECK: %115 = OpPhi %v2float {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}}
+%95 = OpFAdd %v2float %69 %115
+%96 = OpLoad %49 %g_tColor
+%97 = OpLoad %53 %g_sAniso
+%98 = OpSampledImage %57 %96 %97
+%100 = OpImageSampleImplicitLod %v4float %98 %95
+OpStore %_entryPointOutput_vColor %100
+OpReturn
+OpFunctionEnd
+)";
   // clang-format on
 
-  SetTargetEnv(SPV_ENV_VULKAN_1_2);
+  SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
   SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 7u, 23u, false,
-                                               false, true, false, true);
+  SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 23u);
 }
 
 TEST_F(InstBindlessTest, UniformArrayRefNoDescInit) {
@@ -4449,153 +3423,129 @@
 
   // clang-format off
   const std::string text = R"(
-               OpCapability Shader
-;CHECK:               OpExtension "SPV_KHR_storage_buffer_storage_class"
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %MainPs "MainPs" %_ %__0 %g_tColor %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor
-               OpExecutionMode %MainPs OriginUpperLeft
-               OpSource HLSL 500
-               OpName %MainPs "MainPs"
-               OpName %PerBatchEnvMapConstantBuffer_t "PerBatchEnvMapConstantBuffer_t"
-               OpMemberName %PerBatchEnvMapConstantBuffer_t 0 "g_matEnvMapWorldToLocal"
-               OpMemberName %PerBatchEnvMapConstantBuffer_t 1 "g_vEnvironmentMapBoxMins"
-               OpMemberName %PerBatchEnvMapConstantBuffer_t 2 "g_TexOff"
-               OpName %_BindlessFastEnvMapCB_PS_t "_BindlessFastEnvMapCB_PS_t"
-               OpMemberName %_BindlessFastEnvMapCB_PS_t 0 "g_envMapConstants"
-               OpName %_ ""
-               OpName %PerViewPushConst_t "PerViewPushConst_t"
-               OpMemberName %PerViewPushConst_t 0 "g_c"
-               OpName %__0 ""
-               OpName %g_tColor "g_tColor"
-               OpName %g_sAniso "g_sAniso"
-               OpName %i_vTextureCoords "i.vTextureCoords"
-               OpName %_entryPointOutput_vColor "@entryPointOutput.vColor"
-               OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 0 RowMajor
-               OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 0 Offset 0
-               OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 0 MatrixStride 16
-               OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 1 Offset 48
-               OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 2 Offset 64
-               OpDecorate %_arr_PerBatchEnvMapConstantBuffer_t_uint_128 ArrayStride 80
-               OpMemberDecorate %_BindlessFastEnvMapCB_PS_t 0 Offset 0
-               OpDecorate %_BindlessFastEnvMapCB_PS_t Block
-               OpDecorate %_ DescriptorSet 0
-               OpDecorate %_ Binding 2
-               OpMemberDecorate %PerViewPushConst_t 0 Offset 0
-               OpDecorate %PerViewPushConst_t Block
-               OpDecorate %g_tColor DescriptorSet 0
-               OpDecorate %g_tColor Binding 0
-               OpDecorate %g_sAniso DescriptorSet 0
-               OpDecorate %g_sAniso Binding 1
-               OpDecorate %i_vTextureCoords Location 0
-               OpDecorate %_entryPointOutput_vColor Location 0
-;CHECK:               OpDecorate %_runtimearr_uint ArrayStride 4
-)" + kInputDecorations + kOutputDecorations + R"(
-;CHECK:               OpDecorate %gl_FragCoord BuiltIn FragCoord
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v2float = OpTypeVector %float 2
-    %v4float = OpTypeVector %float 4
-    %v3float = OpTypeVector %float 3
+OpCapability Shader
+;CHECK: OpCapability Linkage
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %MainPs "MainPs" %_ %__0 %g_tColor %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor
+OpExecutionMode %MainPs OriginUpperLeft
+OpSource HLSL 500
+OpName %MainPs "MainPs"
+OpName %PerBatchEnvMapConstantBuffer_t "PerBatchEnvMapConstantBuffer_t"
+OpMemberName %PerBatchEnvMapConstantBuffer_t 0 "g_matEnvMapWorldToLocal"
+OpMemberName %PerBatchEnvMapConstantBuffer_t 1 "g_vEnvironmentMapBoxMins"
+OpMemberName %PerBatchEnvMapConstantBuffer_t 2 "g_TexOff"
+OpName %_BindlessFastEnvMapCB_PS_t "_BindlessFastEnvMapCB_PS_t"
+OpMemberName %_BindlessFastEnvMapCB_PS_t 0 "g_envMapConstants"
+OpName %_ ""
+OpName %PerViewPushConst_t "PerViewPushConst_t"
+OpMemberName %PerViewPushConst_t 0 "g_c"
+OpName %__0 ""
+OpName %g_tColor "g_tColor"
+OpName %g_sAniso "g_sAniso"
+OpName %i_vTextureCoords "i.vTextureCoords"
+OpName %_entryPointOutput_vColor "@entryPointOutput.vColor"
+OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 0 RowMajor
+OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 0 Offset 0
+OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 0 MatrixStride 16
+OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 1 Offset 48
+OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 2 Offset 64
+OpDecorate %_arr_PerBatchEnvMapConstantBuffer_t_uint_128 ArrayStride 80
+OpMemberDecorate %_BindlessFastEnvMapCB_PS_t 0 Offset 0
+OpDecorate %_BindlessFastEnvMapCB_PS_t Block
+OpDecorate %_ DescriptorSet 0
+OpDecorate %_ Binding 2
+OpMemberDecorate %PerViewPushConst_t 0 Offset 0
+OpDecorate %PerViewPushConst_t Block
+OpDecorate %g_tColor DescriptorSet 0
+OpDecorate %g_tColor Binding 0
+OpDecorate %g_sAniso DescriptorSet 0
+OpDecorate %g_sAniso Binding 1
+OpDecorate %i_vTextureCoords Location 0
+OpDecorate %_entryPointOutput_vColor Location 0
+)" + kImportDeco + R"(
+;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v2float = OpTypeVector %float 2
+%v4float = OpTypeVector %float 4
+%v3float = OpTypeVector %float 3
 %mat4v3float = OpTypeMatrix %v3float 4
 %PerBatchEnvMapConstantBuffer_t = OpTypeStruct %mat4v3float %v4float %v2float
-       %uint = OpTypeInt 32 0
-   %uint_128 = OpConstant %uint 128
+%uint = OpTypeInt 32 0
+%uint_128 = OpConstant %uint 128
 %_arr_PerBatchEnvMapConstantBuffer_t_uint_128 = OpTypeArray %PerBatchEnvMapConstantBuffer_t %uint_128
 %_BindlessFastEnvMapCB_PS_t = OpTypeStruct %_arr_PerBatchEnvMapConstantBuffer_t_uint_128
 %_ptr_Uniform__BindlessFastEnvMapCB_PS_t = OpTypePointer Uniform %_BindlessFastEnvMapCB_PS_t
-          %_ = OpVariable %_ptr_Uniform__BindlessFastEnvMapCB_PS_t Uniform
-        %int = OpTypeInt 32 1
-      %int_0 = OpConstant %int 0
+%_ = OpVariable %_ptr_Uniform__BindlessFastEnvMapCB_PS_t Uniform
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
 %PerViewPushConst_t = OpTypeStruct %uint
 %_ptr_PushConstant_PerViewPushConst_t = OpTypePointer PushConstant %PerViewPushConst_t
-        %__0 = OpVariable %_ptr_PushConstant_PerViewPushConst_t PushConstant
+%__0 = OpVariable %_ptr_PushConstant_PerViewPushConst_t PushConstant
 %_ptr_PushConstant_uint = OpTypePointer PushConstant %uint
-      %int_2 = OpConstant %int 2
+%int_2 = OpConstant %int 2
 %_ptr_Uniform_v2float = OpTypePointer Uniform %v2float
-         %46 = OpTypeImage %float 2D 0 0 0 1 Unknown
+%46 = OpTypeImage %float 2D 0 0 0 1 Unknown
 %_ptr_UniformConstant_46 = OpTypePointer UniformConstant %46
-   %g_tColor = OpVariable %_ptr_UniformConstant_46 UniformConstant
-         %50 = OpTypeSampler
+%g_tColor = OpVariable %_ptr_UniformConstant_46 UniformConstant
+%50 = OpTypeSampler
 %_ptr_UniformConstant_50 = OpTypePointer UniformConstant %50
-   %g_sAniso = OpVariable %_ptr_UniformConstant_50 UniformConstant
-         %54 = OpTypeSampledImage %46
+%g_sAniso = OpVariable %_ptr_UniformConstant_50 UniformConstant
+%54 = OpTypeSampledImage %46
 %_ptr_Input_v2float = OpTypePointer Input %v2float
 %i_vTextureCoords = OpVariable %_ptr_Input_v2float Input
 %_ptr_Output_v4float = OpTypePointer Output %v4float
 %_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output
-;CHECK:     %uint_0 = OpConstant %uint 0
-;CHECK:    %uint_80 = OpConstant %uint 80
-;CHECK:    %uint_64 = OpConstant %uint 64
-;CHECK:     %uint_7 = OpConstant %uint 7
-;CHECK:     %uint_2 = OpConstant %uint 2
-;CHECK:     %uint_1 = OpConstant %uint 1
-;CHECK:        %105 = OpTypeFunction %uint %uint %uint %uint
-;CHECK:%_runtimearr_uint = OpTypeRuntimeArray %uint
-)" + kInputGlobals + R"(
-;CHECK:%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-;CHECK:       %bool = OpTypeBool
-;CHECK:     %uint_4 = OpConstant %uint 4
-;CHECK:        %132 = OpTypeFunction %void %uint %uint %uint %uint %uint
-)" + kOutputGlobals + R"(
-;CHECK:    %uint_11 = OpConstant %uint 11
-;CHECK:    %uint_23 = OpConstant %uint 23
-;CHECK:     %uint_3 = OpConstant %uint 3
-;CHECK:%_ptr_Input_v4float = OpTypePointer Input %v4float
-;CHECK:%gl_FragCoord = OpVariable %_ptr_Input_v4float Input
-;CHECK:     %v4uint = OpTypeVector %uint 4
-;CHECK:     %uint_5 = OpConstant %uint 5
-;CHECK:     %uint_8 = OpConstant %uint 8
-;CHECK:     %uint_9 = OpConstant %uint 9
-;CHECK:    %uint_10 = OpConstant %uint 10
-;CHECK:    %uint_78 = OpConstant %uint 78
-;CHECK:        %185 = OpConstantNull %v2float
-     %MainPs = OpFunction %void None %3
-          %5 = OpLabel
-;CHECK:        %123 = OpFunctionCall %uint %inst_bindless_direct_read_3 %uint_1 %uint_2 %uint_0
-;CHECK:               OpBranch %93
-;CHECK:         %93 = OpLabel
-;CHECK:               OpBranch %92
-;CHECK:         %92 = OpLabel
-         %66 = OpLoad %v2float %i_vTextureCoords
-         %79 = OpAccessChain %_ptr_PushConstant_uint %__0 %int_0
-         %80 = OpLoad %uint %79
-         %81 = OpAccessChain %_ptr_Uniform_v2float %_ %int_0 %80 %int_2
-         %82 = OpLoad %v2float %81
-;CHECK-NOT:     %82 = OpLoad %v2float %81
-;CHECK:         %96 = OpIMul %uint %uint_80 %80
-;CHECK:         %97 = OpIAdd %uint %uint_0 %96
-;CHECK:         %99 = OpIAdd %uint %97 %uint_64
-;CHECK:        %101 = OpIAdd %uint %99 %uint_7
-;CHECK:        %125 = OpULessThan %bool %101 %123
-;CHECK:               OpSelectionMerge %127 None
-;CHECK:               OpBranchConditional %125 %128 %129
-;CHECK:        %128 = OpLabel
-;CHECK:        %130 = OpLoad %v2float %81
-;CHECK:               OpBranch %127
-;CHECK:        %129 = OpLabel
-;CHECK:        %184 = OpFunctionCall %void %inst_bindless_stream_write_5 %uint_78 %uint_4 %uint_0 %101 %123
-;CHECK:               OpBranch %127
-;CHECK:        %127 = OpLabel
-;CHECK:        %186 = OpPhi %v2float %130 %128 %185 %129
-         %86 = OpFAdd %v2float %66 %82
-;CHECK-NOT:         %86 = OpFAdd %v2float %66 %82
-;CHECK:             %86 = OpFAdd %v2float %66 %186
-         %87 = OpLoad %46 %g_tColor
-         %88 = OpLoad %50 %g_sAniso
-         %89 = OpSampledImage %54 %87 %88
-         %91 = OpImageSampleImplicitLod %v4float %89 %86
-               OpStore %_entryPointOutput_vColor %91
-               OpReturn
-               OpFunctionEnd
-)" + kDirectRead3 + kStreamWrite5Frag;
+;CHECK: %v4uint = OpTypeVector %uint 4
+;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
+;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
+;CHECK: [[null_v2float:%\w+]] = OpConstantNull %v2float
+)" + kImportStub + R"(
+%MainPs = OpFunction %void None %3
+%5 = OpLabel
+%66 = OpLoad %v2float %i_vTextureCoords
+%79 = OpAccessChain %_ptr_PushConstant_uint %__0 %int_0
+%80 = OpLoad %uint %79
+%81 = OpAccessChain %_ptr_Uniform_v2float %_ %int_0 %80 %int_2
+%82 = OpLoad %v2float %81
+;CHECK-NOT: %82 = OpLoad %v2float %81
+;CHECK: {{%\w+}} = OpIMul %uint %uint_80 %80
+;CHECK: {{%\w+}} = OpIAdd %uint %uint_0 {{%\w+}}
+;CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_64
+;CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_7
+;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord
+;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}}
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_79 {{%\w+}} %uint_0 %uint_2 %uint_0 {{%\w+}}
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpLoad %v2float %81
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpPhi %v2float {{%\w+}} {{%\w+}} [[null_v2float]] {{%\w+}}
+%86 = OpFAdd %v2float %66 %82
+;CHECK-NOT: %86 = OpFAdd %v2float %66 %82
+;CHECK: %86 = OpFAdd %v2float %66 {{%\w+}}
+%87 = OpLoad %46 %g_tColor
+%88 = OpLoad %50 %g_sAniso
+%89 = OpSampledImage %54 %87 %88
+%91 = OpImageSampleImplicitLod %v4float %89 %86
+OpStore %_entryPointOutput_vColor %91
+OpReturn
+OpFunctionEnd
+)";
   // clang-format on
 
-  SetTargetEnv(SPV_ENV_VULKAN_1_2);
+  SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
   SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 7u, 23u, false,
-                                               false, true, false, true);
+  SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 23u);
 }
 
 TEST_F(InstBindlessTest, UniformArrayRefWithDescInit) {
@@ -4606,173 +3556,151 @@
 
   // clang-format off
   const std::string text = R"(
-               OpCapability Shader
-;CHECK:               OpExtension "SPV_KHR_storage_buffer_storage_class"
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %MainPs "MainPs" %_ %__0 %g_tColor %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor
-;CHECK:        OpEntryPoint Fragment %MainPs "MainPs" %_ %__0 %g_tColor %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor %inst_bindless_input_buffer %inst_bindless_output_buffer %gl_FragCoord
-               OpExecutionMode %MainPs OriginUpperLeft
-               OpSource HLSL 500
-               OpName %MainPs "MainPs"
-               OpName %PerBatchEnvMapConstantBuffer_t "PerBatchEnvMapConstantBuffer_t"
-               OpMemberName %PerBatchEnvMapConstantBuffer_t 0 "g_matEnvMapWorldToLocal"
-               OpMemberName %PerBatchEnvMapConstantBuffer_t 1 "g_vEnvironmentMapBoxMins"
-               OpMemberName %PerBatchEnvMapConstantBuffer_t 2 "g_TexOff"
-               OpName %_BindlessFastEnvMapCB_PS_t "_BindlessFastEnvMapCB_PS_t"
-               OpMemberName %_BindlessFastEnvMapCB_PS_t 0 "g_envMapConstants"
-               OpName %_ ""
-               OpName %PerViewPushConst_t "PerViewPushConst_t"
-               OpMemberName %PerViewPushConst_t 0 "g_c"
-               OpName %__0 ""
-               OpName %g_tColor "g_tColor"
-               OpName %g_sAniso "g_sAniso"
-               OpName %i_vTextureCoords "i.vTextureCoords"
-               OpName %_entryPointOutput_vColor "@entryPointOutput.vColor"
-               OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 0 RowMajor
-               OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 0 Offset 0
-               OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 0 MatrixStride 16
-               OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 1 Offset 48
-               OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 2 Offset 64
-               OpDecorate %_arr_PerBatchEnvMapConstantBuffer_t_uint_128 ArrayStride 80
-               OpMemberDecorate %_BindlessFastEnvMapCB_PS_t 0 Offset 0
-               OpDecorate %_BindlessFastEnvMapCB_PS_t Block
-               OpDecorate %_ DescriptorSet 0
-               OpDecorate %_ Binding 2
-               OpMemberDecorate %PerViewPushConst_t 0 Offset 0
-               OpDecorate %PerViewPushConst_t Block
-               OpDecorate %g_tColor DescriptorSet 0
-               OpDecorate %g_tColor Binding 0
-               OpDecorate %g_sAniso DescriptorSet 0
-               OpDecorate %g_sAniso Binding 1
-               OpDecorate %i_vTextureCoords Location 0
-               OpDecorate %_entryPointOutput_vColor Location 0
-;CHECK:               OpDecorate %_runtimearr_uint ArrayStride 4
-)" + kInputDecorations + kOutputDecorations + R"(
-;CHECK:               OpDecorate %gl_FragCoord BuiltIn FragCoord
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v2float = OpTypeVector %float 2
-    %v4float = OpTypeVector %float 4
-    %v3float = OpTypeVector %float 3
+OpCapability Shader
+;CHECK: OpCapability Linkage
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %MainPs "MainPs" %_ %__0 %g_tColor %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor
+;CHECK: OpEntryPoint Fragment %MainPs "MainPs" %_ %__0 %g_tColor %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord
+OpExecutionMode %MainPs OriginUpperLeft
+OpSource HLSL 500
+OpName %MainPs "MainPs"
+OpName %PerBatchEnvMapConstantBuffer_t "PerBatchEnvMapConstantBuffer_t"
+OpMemberName %PerBatchEnvMapConstantBuffer_t 0 "g_matEnvMapWorldToLocal"
+OpMemberName %PerBatchEnvMapConstantBuffer_t 1 "g_vEnvironmentMapBoxMins"
+OpMemberName %PerBatchEnvMapConstantBuffer_t 2 "g_TexOff"
+OpName %_BindlessFastEnvMapCB_PS_t "_BindlessFastEnvMapCB_PS_t"
+OpMemberName %_BindlessFastEnvMapCB_PS_t 0 "g_envMapConstants"
+OpName %_ ""
+OpName %PerViewPushConst_t "PerViewPushConst_t"
+OpMemberName %PerViewPushConst_t 0 "g_c"
+OpName %__0 ""
+OpName %g_tColor "g_tColor"
+OpName %g_sAniso "g_sAniso"
+OpName %i_vTextureCoords "i.vTextureCoords"
+OpName %_entryPointOutput_vColor "@entryPointOutput.vColor"
+OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 0 RowMajor
+OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 0 Offset 0
+OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 0 MatrixStride 16
+OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 1 Offset 48
+OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 2 Offset 64
+OpDecorate %_arr_PerBatchEnvMapConstantBuffer_t_uint_128 ArrayStride 80
+OpMemberDecorate %_BindlessFastEnvMapCB_PS_t 0 Offset 0
+OpDecorate %_BindlessFastEnvMapCB_PS_t Block
+OpDecorate %_ DescriptorSet 0
+OpDecorate %_ Binding 2
+OpMemberDecorate %PerViewPushConst_t 0 Offset 0
+OpDecorate %PerViewPushConst_t Block
+OpDecorate %g_tColor DescriptorSet 0
+OpDecorate %g_tColor Binding 0
+OpDecorate %g_sAniso DescriptorSet 0
+OpDecorate %g_sAniso Binding 1
+OpDecorate %i_vTextureCoords Location 0
+OpDecorate %_entryPointOutput_vColor Location 0
+)" + kImportDeco + R"(
+;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v2float = OpTypeVector %float 2
+%v4float = OpTypeVector %float 4
+%v3float = OpTypeVector %float 3
 %mat4v3float = OpTypeMatrix %v3float 4
 %PerBatchEnvMapConstantBuffer_t = OpTypeStruct %mat4v3float %v4float %v2float
-       %uint = OpTypeInt 32 0
-   %uint_128 = OpConstant %uint 128
+%uint = OpTypeInt 32 0
+%uint_128 = OpConstant %uint 128
 %_arr_PerBatchEnvMapConstantBuffer_t_uint_128 = OpTypeArray %PerBatchEnvMapConstantBuffer_t %uint_128
 %_BindlessFastEnvMapCB_PS_t = OpTypeStruct %_arr_PerBatchEnvMapConstantBuffer_t_uint_128
 %_ptr_Uniform__BindlessFastEnvMapCB_PS_t = OpTypePointer Uniform %_BindlessFastEnvMapCB_PS_t
-          %_ = OpVariable %_ptr_Uniform__BindlessFastEnvMapCB_PS_t Uniform
-        %int = OpTypeInt 32 1
-      %int_0 = OpConstant %int 0
+%_ = OpVariable %_ptr_Uniform__BindlessFastEnvMapCB_PS_t Uniform
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
 %PerViewPushConst_t = OpTypeStruct %uint
 %_ptr_PushConstant_PerViewPushConst_t = OpTypePointer PushConstant %PerViewPushConst_t
-        %__0 = OpVariable %_ptr_PushConstant_PerViewPushConst_t PushConstant
+%__0 = OpVariable %_ptr_PushConstant_PerViewPushConst_t PushConstant
 %_ptr_PushConstant_uint = OpTypePointer PushConstant %uint
-      %int_2 = OpConstant %int 2
+%int_2 = OpConstant %int 2
 %_ptr_Uniform_v2float = OpTypePointer Uniform %v2float
-         %46 = OpTypeImage %float 2D 0 0 0 1 Unknown
+%46 = OpTypeImage %float 2D 0 0 0 1 Unknown
 %_ptr_UniformConstant_46 = OpTypePointer UniformConstant %46
-   %g_tColor = OpVariable %_ptr_UniformConstant_46 UniformConstant
-         %50 = OpTypeSampler
+%g_tColor = OpVariable %_ptr_UniformConstant_46 UniformConstant
+%50 = OpTypeSampler
 %_ptr_UniformConstant_50 = OpTypePointer UniformConstant %50
-   %g_sAniso = OpVariable %_ptr_UniformConstant_50 UniformConstant
-         %54 = OpTypeSampledImage %46
+%g_sAniso = OpVariable %_ptr_UniformConstant_50 UniformConstant
+%54 = OpTypeSampledImage %46
 %_ptr_Input_v2float = OpTypePointer Input %v2float
 %i_vTextureCoords = OpVariable %_ptr_Input_v2float Input
 %_ptr_Output_v4float = OpTypePointer Output %v4float
 %_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output
-;CHECK:     %uint_0 = OpConstant %uint 0
-;CHECK:    %uint_80 = OpConstant %uint 80
-;CHECK:    %uint_64 = OpConstant %uint 64
-;CHECK:     %uint_7 = OpConstant %uint 7
-;CHECK:     %uint_2 = OpConstant %uint 2
-;CHECK:        %104 = OpTypeFunction %uint %uint %uint %uint %uint
-;CHECK:%_runtimearr_uint = OpTypeRuntimeArray %uint
-)" + kInputGlobals + R"(
-;CHECK:%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-;CHECK:       %bool = OpTypeBool
-;CHECK:     %uint_4 = OpConstant %uint 4
-;CHECK:        %135 = OpTypeFunction %void %uint %uint %uint %uint %uint
-)" + kOutputGlobals + R"(
-;CHECK:    %uint_11 = OpConstant %uint 11
-;CHECK:     %uint_1 = OpConstant %uint 1
-;CHECK:    %uint_23 = OpConstant %uint 23
-;CHECK:     %uint_3 = OpConstant %uint 3
-;CHECK:%_ptr_Input_v4float = OpTypePointer Input %v4float
-;CHECK:%gl_FragCoord = OpVariable %_ptr_Input_v4float Input
-;CHECK:     %v4uint = OpTypeVector %uint 4
-;CHECK:     %uint_5 = OpConstant %uint 5
-;CHECK:     %uint_8 = OpConstant %uint 8
-;CHECK:     %uint_9 = OpConstant %uint 9
-;CHECK:    %uint_10 = OpConstant %uint 10
-;CHECK:    %uint_78 = OpConstant %uint 78
-;CHECK:        %189 = OpConstantNull %v2float
-;CHECK:    %uint_83 = OpConstant %uint 83
-;CHECK:        %201 = OpConstantNull %v4float
-     %MainPs = OpFunction %void None %3
-          %5 = OpLabel
-;CHECK:        %126 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_2 %uint_0
-;CHECK:        %191 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0
-;CHECK:               OpBranch %93
-;CHECK:         %93 = OpLabel
-;CHECK:               OpBranch %92
-;CHECK:         %92 = OpLabel
-         %66 = OpLoad %v2float %i_vTextureCoords
-         %79 = OpAccessChain %_ptr_PushConstant_uint %__0 %int_0
-         %80 = OpLoad %uint %79
-         %81 = OpAccessChain %_ptr_Uniform_v2float %_ %int_0 %80 %int_2
-         %82 = OpLoad %v2float %81
-         %86 = OpFAdd %v2float %66 %82
+;CHECK: %v4uint = OpTypeVector %uint 4
+;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
+;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
+;CHECK: [[null_v2float:%\w+]] = OpConstantNull %v2float
+;CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float
+)" + kImportStub + R"(
+%MainPs = OpFunction %void None %3
+%5 = OpLabel
+%66 = OpLoad %v2float %i_vTextureCoords
+%79 = OpAccessChain %_ptr_PushConstant_uint %__0 %int_0
+%80 = OpLoad %uint %79
+%81 = OpAccessChain %_ptr_Uniform_v2float %_ %int_0 %80 %int_2
+%82 = OpLoad %v2float %81
+%86 = OpFAdd %v2float %66 %82
 ;CHECK-NOT: %82 = OpLoad %v2float %81
 ;CHECK-NOT: %86 = OpFAdd %v2float %66 %82
-;CHECK:         %96 = OpIMul %uint %uint_80 %80
-;CHECK:         %97 = OpIAdd %uint %uint_0 %96
-;CHECK:         %99 = OpIAdd %uint %97 %uint_64
-;CHECK:        %101 = OpIAdd %uint %99 %uint_7
-;CHECK:        %128 = OpULessThan %bool %101 %126
-;CHECK:               OpSelectionMerge %130 None
-;CHECK:               OpBranchConditional %128 %131 %132
-;CHECK:        %131 = OpLabel
-;CHECK:        %133 = OpLoad %v2float %81
-;CHECK:               OpBranch %130
-;CHECK:        %132 = OpLabel
-;CHECK:        %188 = OpFunctionCall %void %inst_bindless_stream_write_5 %uint_78 %uint_4 %uint_0 %101 %126
-;CHECK:               OpBranch %130
-;CHECK:        %130 = OpLabel
-;CHECK:        %190 = OpPhi %v2float %133 %131 %189 %132
-;CHECK:         %86 = OpFAdd %v2float %66 %190
-         %87 = OpLoad %46 %g_tColor
-         %88 = OpLoad %50 %g_sAniso
-         %89 = OpSampledImage %54 %87 %88
-         %91 = OpImageSampleImplicitLod %v4float %89 %86
-               OpStore %_entryPointOutput_vColor %91
+;CHECK: {{%\w+}} = OpIMul %uint %uint_80 %80
+;CHECK: {{%\w+}} = OpIAdd %uint %uint_0 {{%\w+}}
+;CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_64
+;CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_7
+;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord
+;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}}
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_79 {{%\w+}} %uint_0 %uint_2 %uint_0 {{%\w+}}
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpLoad %v2float %81
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpPhi %v2float {{%\w+}} {{%\w+}} [[null_v2float]] {{%\w+}}
+;CHECK: %86 = OpFAdd %v2float %66 {{%\w+}}
+%87 = OpLoad %46 %g_tColor
+%88 = OpLoad %50 %g_sAniso
+%89 = OpSampledImage %54 %87 %88
+%91 = OpImageSampleImplicitLod %v4float %89 %86
+OpStore %_entryPointOutput_vColor %91
 ;CHECK-NOT: %91 = OpImageSampleImplicitLod %v4float %89 %86
 ;CHECK-NOT:       OpStore %_entryPointOutput_vColor %91
-;CHECK:        %192 = OpULessThan %bool %uint_0 %191
-;CHECK:               OpSelectionMerge %193 None
-;CHECK:               OpBranchConditional %192 %194 %195
-;CHECK:        %194 = OpLabel
-;CHECK:        %196 = OpLoad %46 %g_tColor
-;CHECK:        %197 = OpSampledImage %54 %196 %88
-;CHECK:        %198 = OpImageSampleImplicitLod %v4float %197 %86
-;CHECK:               OpBranch %193
-;CHECK:        %195 = OpLabel
-;CHECK:        %200 = OpFunctionCall %void %inst_bindless_stream_write_5 %uint_83 %uint_1 %uint_0 %uint_0 %uint_0
-;CHECK:               OpBranch %193
-;CHECK:        %193 = OpLabel
-;CHECK:        %202 = OpPhi %v4float %198 %194 %201 %195
-;CHECK:               OpStore %_entryPointOutput_vColor %202
-               OpReturn
-               OpFunctionEnd
-)" + kDirectRead4 + kStreamWrite5Frag;
+;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord
+;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}}
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_84 {{%\w+}} %uint_0 %uint_0 %uint_0 %uint_0
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpLoad %46 %g_tColor
+;CHECK: {{%\w+}} = OpSampledImage %54 {{%\w+}} %88
+;CHECK: {{%\w+}} = OpImageSampleImplicitLod %v4float {{%\w+}} %86
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}}
+;CHECK: OpStore %_entryPointOutput_vColor {{%\w+}}
+OpReturn
+OpFunctionEnd
+)";
   // clang-format on
 
-  SetTargetEnv(SPV_ENV_VULKAN_1_2);
+  SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
   SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 7u, 23u, true, true,
-                                               true, false, true);
+  SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 23u);
 }
 
 TEST_F(InstBindlessTest, Descriptor16BitIdxRef) {
@@ -4783,142 +3711,107 @@
 
   // clang-format off
   const std::string text = R"(
-               OpCapability Shader
-               OpCapability Int16
-               OpCapability StoragePushConstant16
-;CHECK:               OpExtension "SPV_KHR_storage_buffer_storage_class"
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %MainPs "MainPs" %g_tColor %_ %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor
-;CHECK:        OpEntryPoint Fragment %MainPs "MainPs" %g_tColor %_ %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor %inst_bindless_output_buffer %gl_FragCoord %inst_bindless_input_buffer
-               OpExecutionMode %MainPs OriginUpperLeft
-               OpSource HLSL 500
-               OpName %MainPs "MainPs"
-               OpName %g_tColor "g_tColor"
-               OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t"
-               OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx"
-               OpName %_ ""
-               OpName %g_sAniso "g_sAniso"
-               OpName %i_vTextureCoords "i.vTextureCoords"
-               OpName %_entryPointOutput_vColor "@entryPointOutput.vColor"
-               OpDecorate %g_tColor DescriptorSet 0
-               OpDecorate %g_tColor Binding 0
-               OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0
-               OpDecorate %PerViewConstantBuffer_t Block
-               OpDecorate %g_sAniso DescriptorSet 0
-               OpDecorate %g_sAniso Binding 0
-               OpDecorate %i_vTextureCoords Location 0
-               OpDecorate %_entryPointOutput_vColor Location 0
-;CHECK:               OpDecorate %_runtimearr_uint ArrayStride 4
-)" + kOutputDecorations + R"(
-;CHECK:               OpDecorate %gl_FragCoord BuiltIn FragCoord
-)" + kInputDecorations + R"(
-       %void = OpTypeVoid
-         %10 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v2float = OpTypeVector %float 2
-    %v4float = OpTypeVector %float 4
-        %int = OpTypeInt 32 1
-      %int_0 = OpConstant %int 0
-         %16 = OpTypeImage %float 2D 0 0 0 1 Unknown
-       %uint = OpTypeInt 32 0
-   %uint_128 = OpConstant %uint 128
+OpCapability Shader
+OpCapability Int16
+OpCapability StoragePushConstant16
+;CHECK: OpCapability Linkage
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %MainPs "MainPs" %g_tColor %_ %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor
+;CHECK: OpEntryPoint Fragment %MainPs "MainPs" %g_tColor %_ %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord
+OpExecutionMode %MainPs OriginUpperLeft
+OpSource HLSL 500
+OpName %MainPs "MainPs"
+OpName %g_tColor "g_tColor"
+OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t"
+OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx"
+OpName %_ ""
+OpName %g_sAniso "g_sAniso"
+OpName %i_vTextureCoords "i.vTextureCoords"
+OpName %_entryPointOutput_vColor "@entryPointOutput.vColor"
+OpDecorate %g_tColor DescriptorSet 1
+OpDecorate %g_tColor Binding 2
+OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0
+OpDecorate %PerViewConstantBuffer_t Block
+OpDecorate %g_sAniso DescriptorSet 1
+OpDecorate %g_sAniso Binding 2
+OpDecorate %i_vTextureCoords Location 0
+OpDecorate %_entryPointOutput_vColor Location 0
+)" + kImportDeco + R"(
+;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
+%void = OpTypeVoid
+%10 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v2float = OpTypeVector %float 2
+%v4float = OpTypeVector %float 4
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%16 = OpTypeImage %float 2D 0 0 0 1 Unknown
+%uint = OpTypeInt 32 0
+%uint_128 = OpConstant %uint 128
 %_arr_16_uint_128 = OpTypeArray %16 %uint_128
 %_ptr_UniformConstant__arr_16_uint_128 = OpTypePointer UniformConstant %_arr_16_uint_128
-   %g_tColor = OpVariable %_ptr_UniformConstant__arr_16_uint_128 UniformConstant
-     %ushort = OpTypeInt 16 0
+%g_tColor = OpVariable %_ptr_UniformConstant__arr_16_uint_128 UniformConstant
+%ushort = OpTypeInt 16 0
 %PerViewConstantBuffer_t = OpTypeStruct %ushort
 %_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t
-          %_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant
+%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant
 %_ptr_PushConstant_ushort = OpTypePointer PushConstant %ushort
 %_ptr_UniformConstant_16 = OpTypePointer UniformConstant %16
-         %25 = OpTypeSampler
+%25 = OpTypeSampler
 %_ptr_UniformConstant_25 = OpTypePointer UniformConstant %25
-   %g_sAniso = OpVariable %_ptr_UniformConstant_25 UniformConstant
-         %27 = OpTypeSampledImage %16
+%g_sAniso = OpVariable %_ptr_UniformConstant_25 UniformConstant
+%27 = OpTypeSampledImage %16
 %_ptr_Input_v2float = OpTypePointer Input %v2float
 %i_vTextureCoords = OpVariable %_ptr_Input_v2float Input
 %_ptr_Output_v4float = OpTypePointer Output %v4float
 %_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output
-;CHECK:     %uint_0 = OpConstant %uint 0
-;CHECK:       %bool = OpTypeBool
-;CHECK:         %51 = OpTypeFunction %void %uint %uint %uint %uint
-;CHECK:%_runtimearr_uint = OpTypeRuntimeArray %uint
-)" + kOutputGlobals + R"(
-;CHECK:%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-;CHECK:    %uint_10 = OpConstant %uint 10
-;CHECK:     %uint_4 = OpConstant %uint 4
-;CHECK:     %uint_1 = OpConstant %uint 1
-;CHECK:    %uint_23 = OpConstant %uint 23
-;CHECK:     %uint_2 = OpConstant %uint 2
-;CHECK:     %uint_3 = OpConstant %uint 3
-;CHECK:%_ptr_Input_v4float = OpTypePointer Input %v4float
-;CHECK:%gl_FragCoord = OpVariable %_ptr_Input_v4float Input
-;CHECK:     %v4uint = OpTypeVector %uint 4
-;CHECK:     %uint_5 = OpConstant %uint 5
-;CHECK:     %uint_7 = OpConstant %uint 7
-;CHECK:     %uint_8 = OpConstant %uint 8
-;CHECK:     %uint_9 = OpConstant %uint 9
-;CHECK:    %uint_60 = OpConstant %uint 60
-;CHECK:        %106 = OpConstantNull %v4float
-;CHECK:        %111 = OpTypeFunction %uint %uint %uint %uint %uint
-)" + kInputGlobals + R"(
-     %MainPs = OpFunction %void None %10
-         %30 = OpLabel
-;CHECK:               OpBranch %108
-;CHECK:        %108 = OpLabel
-;CHECK:               OpBranch %39
-;CHECK:         %39 = OpLabel
-         %31 = OpLoad %v2float %i_vTextureCoords
-         %32 = OpAccessChain %_ptr_PushConstant_ushort %_ %int_0
-         %33 = OpLoad %ushort %32
-         %34 = OpAccessChain %_ptr_UniformConstant_16 %g_tColor %33
-         %35 = OpLoad %16 %34
-         %36 = OpLoad %25 %g_sAniso
-         %37 = OpSampledImage %27 %35 %36
-         %38 = OpImageSampleImplicitLod %v4float %37 %31
-               OpStore %_entryPointOutput_vColor %38
-;CHECK-NOT:         %38 = OpImageSampleImplicitLod %v4float %37 %31
-;CHECK-NOT:               OpStore %_entryPointOutput_vColor %38
-;CHECK:         %41 = OpUConvert %uint %33
-;CHECK:         %43 = OpULessThan %bool %41 %uint_128
-;CHECK:               OpSelectionMerge %44 None
-;CHECK:               OpBranchConditional %43 %45 %46
-;CHECK:         %45 = OpLabel
-;CHECK:         %47 = OpLoad %16 %34
-;CHECK:         %48 = OpSampledImage %27 %47 %36
-;CHECK:        %109 = OpUConvert %uint %33
-;CHECK:        %131 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %109
-;CHECK:        %132 = OpULessThan %bool %uint_0 %131
-;CHECK:               OpSelectionMerge %133 None
-;CHECK:               OpBranchConditional %132 %134 %135
-;CHECK:        %134 = OpLabel
-;CHECK:        %136 = OpLoad %16 %34
-;CHECK:        %137 = OpSampledImage %27 %136 %36
-;CHECK:        %138 = OpImageSampleImplicitLod %v4float %137 %31
-;CHECK:               OpBranch %133
-;CHECK:        %135 = OpLabel
-;CHECK:        %139 = OpUConvert %uint %33
-;CHECK:        %140 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_60 %uint_1 %139 %uint_0
-;CHECK:               OpBranch %133
-;CHECK:        %133 = OpLabel
-;CHECK:        %141 = OpPhi %v4float %138 %134 %106 %135
-;CHECK:               OpBranch %44
-;CHECK:         %46 = OpLabel
-;CHECK:        %105 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_60 %uint_0 %41 %uint_128
-;CHECK:               OpBranch %44
-;CHECK:         %44 = OpLabel
-;CHECK:        %107 = OpPhi %v4float %141 %133 %106 %46
-;CHECK:               OpStore %_entryPointOutput_vColor %107
-               OpReturn
-               OpFunctionEnd
-)" + kStreamWrite4Frag + kDirectRead4;
+;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
+;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
+;CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float
+)" + kImportStub + R"(
+%MainPs = OpFunction %void None %10
+%30 = OpLabel
+;CHECK:  OpBranch %39
+;CHECK:  %39 = OpLabel
+%31 = OpLoad %v2float %i_vTextureCoords
+%32 = OpAccessChain %_ptr_PushConstant_ushort %_ %int_0
+%33 = OpLoad %ushort %32
+%34 = OpAccessChain %_ptr_UniformConstant_16 %g_tColor %33
+%35 = OpLoad %16 %34
+%36 = OpLoad %25 %g_sAniso
+%37 = OpSampledImage %27 %35 %36
+%38 = OpImageSampleImplicitLod %v4float %37 %31
+OpStore %_entryPointOutput_vColor %38
+;CHECK-NOT: %38 = OpImageSampleImplicitLod %v4float %37 %31
+;CHECK-NOT: OpStore %_entryPointOutput_vColor %38
+;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord
+;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}}
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0
+;CHECK: {{%\w+}} = OpUConvert %uint %33
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_61 {{%\w+}} %uint_1 %uint_2 {{%\w+}} %uint_0
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpLoad %16 %34
+;CHECK: {{%\w+}} = OpSampledImage %27 {{%\w+}} %36
+;CHECK: {{%\w+}} = OpImageSampleImplicitLod %v4float {{%\w+}} %31
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: [[phi_result:%\w+]] = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}}
+;CHECK: OpStore %_entryPointOutput_vColor [[phi_result]]
+OpReturn
+OpFunctionEnd
+)";
   // clang-format on
 
-  SetTargetEnv(SPV_ENV_VULKAN_1_2);
+  SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
   SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 7u, 23u, true, true,
-                                               false, false, true);
+  SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 23u);
 }
 
 TEST_F(InstBindlessTest, UniformArray16bitIdxRef) {
@@ -4959,158 +3852,133 @@
 
   // clang-format off
   const std::string text = R"(
-               OpCapability Shader
-               OpCapability Int16
-               OpCapability StoragePushConstant16
-;CHECK:               OpExtension "SPV_KHR_storage_buffer_storage_class"
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %MainPs "MainPs" %_ %__0 %g_tColor %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor
-;CHECK:        OpEntryPoint Fragment %MainPs "MainPs" %_ %__0 %g_tColor %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor %inst_bindless_input_buffer %inst_bindless_output_buffer %gl_FragCoord
-               OpExecutionMode %MainPs OriginUpperLeft
-               OpSource HLSL 500
-               OpName %MainPs "MainPs"
-               OpName %PerBatchEnvMapConstantBuffer_t "PerBatchEnvMapConstantBuffer_t"
-               OpMemberName %PerBatchEnvMapConstantBuffer_t 0 "g_matEnvMapWorldToLocal"
-               OpMemberName %PerBatchEnvMapConstantBuffer_t 1 "g_vEnvironmentMapBoxMins"
-               OpMemberName %PerBatchEnvMapConstantBuffer_t 2 "g_TexOff"
-               OpName %_BindlessFastEnvMapCB_PS_t "_BindlessFastEnvMapCB_PS_t"
-               OpMemberName %_BindlessFastEnvMapCB_PS_t 0 "g_envMapConstants"
-               OpName %_ ""
-               OpName %PerViewPushConst_t "PerViewPushConst_t"
-               OpMemberName %PerViewPushConst_t 0 "g_c"
-               OpName %__0 ""
-               OpName %g_tColor "g_tColor"
-               OpName %g_sAniso "g_sAniso"
-               OpName %i_vTextureCoords "i.vTextureCoords"
-               OpName %_entryPointOutput_vColor "@entryPointOutput.vColor"
-               OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 0 RowMajor
-               OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 0 Offset 0
-               OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 0 MatrixStride 16
-               OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 1 Offset 48
-               OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 2 Offset 64
-               OpDecorate %_arr_PerBatchEnvMapConstantBuffer_t_uint_128 ArrayStride 80
-               OpMemberDecorate %_BindlessFastEnvMapCB_PS_t 0 Offset 0
-               OpDecorate %_BindlessFastEnvMapCB_PS_t Block
-               OpDecorate %_ DescriptorSet 0
-               OpDecorate %_ Binding 0
-               OpMemberDecorate %PerViewPushConst_t 0 Offset 0
-               OpDecorate %PerViewPushConst_t Block
-               OpDecorate %g_tColor DescriptorSet 0
-               OpDecorate %g_tColor Binding 0
-               OpDecorate %g_sAniso DescriptorSet 0
-               OpDecorate %g_sAniso Binding 0
-               OpDecorate %i_vTextureCoords Location 0
-               OpDecorate %_entryPointOutput_vColor Location 0
-;CHECK:               OpDecorate %_runtimearr_uint ArrayStride 4
-)" + kInputDecorations + kOutputDecorations + R"(
-;CHECK:               OpDecorate %gl_FragCoord BuiltIn FragCoord
-       %void = OpTypeVoid
-         %14 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v2float = OpTypeVector %float 2
-    %v4float = OpTypeVector %float 4
-    %v3float = OpTypeVector %float 3
+OpCapability Shader
+OpCapability Int16
+OpCapability StoragePushConstant16
+;CHECK: OpCapability Linkage
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %MainPs "MainPs" %_ %__0 %g_tColor %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor
+;CHECK: OpEntryPoint Fragment %MainPs "MainPs" %_ %__0 %g_tColor %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord
+OpExecutionMode %MainPs OriginUpperLeft
+OpSource HLSL 500
+OpName %MainPs "MainPs"
+OpName %PerBatchEnvMapConstantBuffer_t "PerBatchEnvMapConstantBuffer_t"
+OpMemberName %PerBatchEnvMapConstantBuffer_t 0 "g_matEnvMapWorldToLocal"
+OpMemberName %PerBatchEnvMapConstantBuffer_t 1 "g_vEnvironmentMapBoxMins"
+OpMemberName %PerBatchEnvMapConstantBuffer_t 2 "g_TexOff"
+OpName %_BindlessFastEnvMapCB_PS_t "_BindlessFastEnvMapCB_PS_t"
+OpMemberName %_BindlessFastEnvMapCB_PS_t 0 "g_envMapConstants"
+OpName %_ ""
+OpName %PerViewPushConst_t "PerViewPushConst_t"
+OpMemberName %PerViewPushConst_t 0 "g_c"
+OpName %__0 ""
+OpName %g_tColor "g_tColor"
+OpName %g_sAniso "g_sAniso"
+OpName %i_vTextureCoords "i.vTextureCoords"
+OpName %_entryPointOutput_vColor "@entryPointOutput.vColor"
+OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 0 RowMajor
+OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 0 Offset 0
+OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 0 MatrixStride 16
+OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 1 Offset 48
+OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 2 Offset 64
+OpDecorate %_arr_PerBatchEnvMapConstantBuffer_t_uint_128 ArrayStride 80
+OpMemberDecorate %_BindlessFastEnvMapCB_PS_t 0 Offset 0
+OpDecorate %_BindlessFastEnvMapCB_PS_t Block
+OpDecorate %_ DescriptorSet 0
+OpDecorate %_ Binding 0
+OpMemberDecorate %PerViewPushConst_t 0 Offset 0
+OpDecorate %PerViewPushConst_t Block
+OpDecorate %g_tColor DescriptorSet 0
+OpDecorate %g_tColor Binding 0
+OpDecorate %g_sAniso DescriptorSet 0
+OpDecorate %g_sAniso Binding 0
+OpDecorate %i_vTextureCoords Location 0
+OpDecorate %_entryPointOutput_vColor Location 0
+)" + kImportDeco + R"(
+;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
+%void = OpTypeVoid
+%14 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v2float = OpTypeVector %float 2
+%v4float = OpTypeVector %float 4
+%v3float = OpTypeVector %float 3
 %mat4v3float = OpTypeMatrix %v3float 4
 %PerBatchEnvMapConstantBuffer_t = OpTypeStruct %mat4v3float %v4float %v2float
-       %uint = OpTypeInt 32 0
-   %uint_128 = OpConstant %uint 128
+%uint = OpTypeInt 32 0
+%uint_128 = OpConstant %uint 128
 %_arr_PerBatchEnvMapConstantBuffer_t_uint_128 = OpTypeArray %PerBatchEnvMapConstantBuffer_t %uint_128
 %_BindlessFastEnvMapCB_PS_t = OpTypeStruct %_arr_PerBatchEnvMapConstantBuffer_t_uint_128
 %_ptr_Uniform__BindlessFastEnvMapCB_PS_t = OpTypePointer Uniform %_BindlessFastEnvMapCB_PS_t
-          %_ = OpVariable %_ptr_Uniform__BindlessFastEnvMapCB_PS_t Uniform
-        %int = OpTypeInt 32 1
-      %int_0 = OpConstant %int 0
-     %ushort = OpTypeInt 16 0
+%_ = OpVariable %_ptr_Uniform__BindlessFastEnvMapCB_PS_t Uniform
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%ushort = OpTypeInt 16 0
 %PerViewPushConst_t = OpTypeStruct %ushort
 %_ptr_PushConstant_PerViewPushConst_t = OpTypePointer PushConstant %PerViewPushConst_t
-        %__0 = OpVariable %_ptr_PushConstant_PerViewPushConst_t PushConstant
+%__0 = OpVariable %_ptr_PushConstant_PerViewPushConst_t PushConstant
 %_ptr_PushConstant_ushort = OpTypePointer PushConstant %ushort
-      %int_2 = OpConstant %int 2
+%int_2 = OpConstant %int 2
 %_ptr_Uniform_v2float = OpTypePointer Uniform %v2float
-         %30 = OpTypeImage %float 2D 0 0 0 1 Unknown
+%30 = OpTypeImage %float 2D 0 0 0 1 Unknown
 %_ptr_UniformConstant_30 = OpTypePointer UniformConstant %30
-   %g_tColor = OpVariable %_ptr_UniformConstant_30 UniformConstant
-         %32 = OpTypeSampler
+%g_tColor = OpVariable %_ptr_UniformConstant_30 UniformConstant
+%32 = OpTypeSampler
 %_ptr_UniformConstant_32 = OpTypePointer UniformConstant %32
-   %g_sAniso = OpVariable %_ptr_UniformConstant_32 UniformConstant
-         %34 = OpTypeSampledImage %30
+%g_sAniso = OpVariable %_ptr_UniformConstant_32 UniformConstant
+%34 = OpTypeSampledImage %30
 %_ptr_Input_v2float = OpTypePointer Input %v2float
 %i_vTextureCoords = OpVariable %_ptr_Input_v2float Input
 %_ptr_Output_v4float = OpTypePointer Output %v4float
 %_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output
-;CHECK:     %uint_0 = OpConstant %uint 0
-;CHECK:    %uint_80 = OpConstant %uint 80
-;CHECK:    %uint_64 = OpConstant %uint 64
-;CHECK:     %uint_7 = OpConstant %uint 7
-;CHECK:     %uint_1 = OpConstant %uint 1
-;CHECK:         %61 = OpTypeFunction %uint %uint %uint %uint
-;CHECK:%_runtimearr_uint = OpTypeRuntimeArray %uint
-)" + kInputGlobals + R"(
-;CHECK:%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-;CHECK:       %bool = OpTypeBool
-;CHECK:     %uint_4 = OpConstant %uint 4
-;CHECK:         %88 = OpTypeFunction %void %uint %uint %uint %uint %uint
-)" + kOutputGlobals + R"(
-;CHECK:    %uint_11 = OpConstant %uint 11
-;CHECK:    %uint_23 = OpConstant %uint 23
-;CHECK:     %uint_2 = OpConstant %uint 2
-;CHECK:     %uint_3 = OpConstant %uint 3
-;CHECK:%_ptr_Input_v4float = OpTypePointer Input %v4float
-;CHECK:%gl_FragCoord = OpVariable %_ptr_Input_v4float Input
-;CHECK:     %v4uint = OpTypeVector %uint 4
-;CHECK:     %uint_5 = OpConstant %uint 5
-;CHECK:     %uint_8 = OpConstant %uint 8
-;CHECK:     %uint_9 = OpConstant %uint 9
-;CHECK:    %uint_10 = OpConstant %uint 10
-;CHECK:    %uint_81 = OpConstant %uint 81
-;CHECK:        %142 = OpConstantNull %v2float
-     %MainPs = OpFunction %void None %14
-         %37 = OpLabel
-;CHECK:         %79 = OpFunctionCall %uint %inst_bindless_direct_read_3 %uint_1 %uint_0 %uint_0
-;CHECK:               OpBranch %49
-;CHECK:         %49 = OpLabel
-;CHECK:               OpBranch %48
-;CHECK:         %48 = OpLabel
-         %38 = OpLoad %v2float %i_vTextureCoords
-         %39 = OpAccessChain %_ptr_PushConstant_ushort %__0 %int_0
-         %40 = OpLoad %ushort %39
-         %41 = OpAccessChain %_ptr_Uniform_v2float %_ %int_0 %40 %int_2
-         %42 = OpLoad %v2float %41
-         %43 = OpFAdd %v2float %38 %42
-;CHECK-NOT:     %42 = OpLoad %v2float %41
-;CHECK-NOT:     %43 = OpFAdd %v2float %38 %42
-;CHECK:         %52 = OpUConvert %uint %40
-;CHECK:         %53 = OpIMul %uint %uint_80 %52
-;CHECK:         %54 = OpIAdd %uint %uint_0 %53
-;CHECK:         %56 = OpIAdd %uint %54 %uint_64
-;CHECK:         %58 = OpIAdd %uint %56 %uint_7
-;CHECK:         %81 = OpULessThan %bool %58 %79
-;CHECK:               OpSelectionMerge %83 None
-;CHECK:               OpBranchConditional %81 %84 %85
-;CHECK:         %84 = OpLabel
-;CHECK:         %86 = OpLoad %v2float %41
-;CHECK:               OpBranch %83
-;CHECK:         %85 = OpLabel
-;CHECK:        %141 = OpFunctionCall %void %inst_bindless_stream_write_5 %uint_81 %uint_4 %uint_0 %58 %79
-;CHECK:               OpBranch %83
-;CHECK:         %83 = OpLabel
-;CHECK:        %143 = OpPhi %v2float %86 %84 %142 %85
-;CHECK:         %43 = OpFAdd %v2float %38 %143
-         %44 = OpLoad %30 %g_tColor
-         %45 = OpLoad %32 %g_sAniso
-         %46 = OpSampledImage %34 %44 %45
-         %47 = OpImageSampleImplicitLod %v4float %46 %43
-               OpStore %_entryPointOutput_vColor %47
-               OpReturn
-               OpFunctionEnd
-               )" + kDirectRead3 + kStreamWrite5Frag;
+;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
+;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
+;CHECK: [[null_v2float:%\w+]] = OpConstantNull %v2float
+)" + kImportStub + R"(
+%MainPs = OpFunction %void None %14
+%37 = OpLabel
+%38 = OpLoad %v2float %i_vTextureCoords
+%39 = OpAccessChain %_ptr_PushConstant_ushort %__0 %int_0
+%40 = OpLoad %ushort %39
+%41 = OpAccessChain %_ptr_Uniform_v2float %_ %int_0 %40 %int_2
+%42 = OpLoad %v2float %41
+%43 = OpFAdd %v2float %38 %42
+;CHECK-NOT: %42 = OpLoad %v2float %41
+;CHECK-NOT: %43 = OpFAdd %v2float %38 %42
+;CHECK: {{%\w+}} = OpUConvert %uint %40
+;CHECK: {{%\w+}} = OpIMul %uint %uint_80 {{%\w+}}
+;CHECK: {{%\w+}} = OpIAdd %uint %uint_0 {{%\w+}}
+;CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_64
+;CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_7
+;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord
+;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}}
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_82 {{%\w+}} %uint_0 %uint_0 %uint_0 {{%\w+}}
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpLoad %v2float %41
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpPhi %v2float {{%\w+}} {{%\w+}} [[null_v2float]] {{%\w+}}
+;CHECK: %43 = OpFAdd %v2float %38 {{%\w+}}
+%44 = OpLoad %30 %g_tColor
+%45 = OpLoad %32 %g_sAniso
+%46 = OpSampledImage %34 %44 %45
+%47 = OpImageSampleImplicitLod %v4float %46 %43
+OpStore %_entryPointOutput_vColor %47
+OpReturn
+OpFunctionEnd
+)";
   // clang-format on
 
-  SetTargetEnv(SPV_ENV_VULKAN_1_2);
+  SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
   SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 7u, 23u, false,
-                                               false, true, false, true);
+  SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 23u);
 }
 
 TEST_F(InstBindlessTest, UniformMatrixRefRowMajor) {
@@ -5134,120 +4002,98 @@
 
   // clang-format off
   std::string text = R"(
-               OpCapability Shader
-;CHECK:               OpExtension "SPV_KHR_storage_buffer_storage_class"
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Vertex %main "main" %v_vtxResult %_ %a_position
-;CHECK:        OpEntryPoint Vertex %main "main" %v_vtxResult %_ %a_position %inst_bindless_input_buffer %inst_bindless_output_buffer %gl_VertexIndex %gl_InstanceIndex
-               OpSource GLSL 450
-               OpSourceExtension "GL_EXT_scalar_block_layout"
-               OpName %main "main"
-               OpName %v_vtxResult "v_vtxResult"
-               OpName %Block "Block"
-               OpMemberName %Block 0 "var"
-               OpName %_ ""
-               OpName %a_position "a_position"
-               OpDecorate %v_vtxResult RelaxedPrecision
-               OpDecorate %v_vtxResult Location 0
-               OpMemberDecorate %Block 0 RowMajor
-               OpMemberDecorate %Block 0 RelaxedPrecision
-               OpMemberDecorate %Block 0 Offset 0
-               OpMemberDecorate %Block 0 MatrixStride 16
-               OpDecorate %Block Block
-               OpDecorate %_ DescriptorSet 0
-               OpDecorate %_ Binding 0
-               OpDecorate %21 RelaxedPrecision
-;CHECK-NOT:           OpDecorate %21 RelaxedPrecision
-;CHECK:               OpDecorate %116 RelaxedPrecision
-               OpDecorate %a_position Location 0
-;CHECK:               OpDecorate %_runtimearr_uint ArrayStride 4
-)" + kInputDecorations + R"(
-;CHECK:               OpDecorate %61 RelaxedPrecision
-)" + kOutputDecorations + R"(
-;CHECK:               OpDecorate %gl_VertexIndex BuiltIn VertexIndex
-;CHECK:               OpDecorate %gl_InstanceIndex BuiltIn InstanceIndex
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
+OpCapability Shader
+;CHECK: OpCapability Linkage
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %main "main" %v_vtxResult %_ %a_position
+;CHECK: OpEntryPoint Vertex %main "main" %v_vtxResult %_ %a_position %gl_VertexIndex %gl_InstanceIndex
+OpSource GLSL 450
+OpSourceExtension "GL_EXT_scalar_block_layout"
+OpName %main "main"
+OpName %v_vtxResult "v_vtxResult"
+OpName %Block "Block"
+OpMemberName %Block 0 "var"
+OpName %_ ""
+OpName %a_position "a_position"
+OpDecorate %v_vtxResult RelaxedPrecision
+OpDecorate %v_vtxResult Location 0
+OpMemberDecorate %Block 0 RowMajor
+OpMemberDecorate %Block 0 RelaxedPrecision
+OpMemberDecorate %Block 0 Offset 0
+OpMemberDecorate %Block 0 MatrixStride 16
+OpDecorate %Block Block
+OpDecorate %_ DescriptorSet 0
+OpDecorate %_ Binding 0
+OpDecorate %21 RelaxedPrecision
+;CHECK-NOT: OpDecorate %21 RelaxedPrecision
+;CHECK: OpDecorate %v_vtxResult RelaxedPrecision
+;CHECK: OpDecorate [[phi_result:%\w+]] RelaxedPrecision
+OpDecorate %a_position Location 0
+)" + kImportDeco + R"(
+;CHECK: OpDecorate %gl_VertexIndex BuiltIn VertexIndex
+;CHECK: OpDecorate %gl_InstanceIndex BuiltIn InstanceIndex
+;CHECK: OpDecorate [[load_result:%\w+]] RelaxedPrecision
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%float = OpTypeFloat 32
 %_ptr_Output_float = OpTypePointer Output %float
 %v_vtxResult = OpVariable %_ptr_Output_float Output
-    %v2float = OpTypeVector %float 2
+%v2float = OpTypeVector %float 2
 %mat4v2float = OpTypeMatrix %v2float 4
-      %Block = OpTypeStruct %mat4v2float
+%Block = OpTypeStruct %mat4v2float
 %_ptr_Uniform_Block = OpTypePointer Uniform %Block
-          %_ = OpVariable %_ptr_Uniform_Block Uniform
-        %int = OpTypeInt 32 1
-      %int_0 = OpConstant %int 0
-      %int_2 = OpConstant %int 2
-       %uint = OpTypeInt 32 0
-     %uint_1 = OpConstant %uint 1
+%_ = OpVariable %_ptr_Uniform_Block Uniform
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%int_2 = OpConstant %int 2
+%uint = OpTypeInt 32 0
+%uint_1 = OpConstant %uint 1
 %_ptr_Uniform_float = OpTypePointer Uniform %float
-    %v4float = OpTypeVector %float 4
+%v4float = OpTypeVector %float 4
 %_ptr_Input_v4float = OpTypePointer Input %v4float
- %a_position = OpVariable %_ptr_Input_v4float Input
-;CHECK;     %uint_0 = OpConstant %uint 0
-;CHECK;     %uint_16 = OpConstant %uint 16
-;CHECK;     %uint_4 = OpConstant %uint 4
-;CHECK;     %uint_3 = OpConstant %uint 3
-;CHECK;         %37 = OpTypeFunction %uint %uint %uint %uint
-;CHECK;%_runtimearr_uint = OpTypeRuntimeArray %uint
-)" + kInputGlobals + R"(
-;CHECK;%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-;CHECK;       %bool = OpTypeBool
-;CHECK;         %63 = OpTypeFunction %void %uint %uint %uint %uint %uint
-)" + kOutputGlobals + R"(
-;CHECK;    %uint_11 = OpConstant %uint 11
-;CHECK;    %uint_23 = OpConstant %uint 23
-;CHECK;     %uint_2 = OpConstant %uint 2
-;CHECK;%_ptr_Input_uint = OpTypePointer Input %uint
-;CHECK;%gl_VertexIndex = OpVariable %_ptr_Input_uint Input
-;CHECK;%gl_InstanceIndex = OpVariable %_ptr_Input_uint Input
-;CHECK;     %uint_5 = OpConstant %uint 5
-;CHECK;     %uint_7 = OpConstant %uint 7
-;CHECK;     %uint_8 = OpConstant %uint 8
-;CHECK;     %uint_9 = OpConstant %uint 9
-;CHECK;    %uint_10 = OpConstant %uint 10
-;CHECK;    %uint_45 = OpConstant %uint 45
-;CHECK;        %115 = OpConstantNull %float
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-;CHECK:         %55 = OpFunctionCall %uint %inst_bindless_direct_read_3 %uint_1 %uint_0 %uint_0
-;CHECK:               OpBranch %26
-;CHECK:         %26 = OpLabel
-;CHECK:               OpBranch %25
-;CHECK:         %25 = OpLabel
-         %20 = OpAccessChain %_ptr_Uniform_float %_ %int_0 %int_2 %uint_1
-         %21 = OpLoad %float %20
-;CHECK-NOT:     %21 = OpLoad %float %20
-;CHECK:         %30 = OpIMul %uint %uint_4 %int_2
-;CHECK:         %31 = OpIAdd %uint %uint_0 %30
-;CHECK:         %32 = OpIMul %uint %uint_16 %uint_1
-;CHECK:         %33 = OpIAdd %uint %31 %32
-;CHECK:         %35 = OpIAdd %uint %33 %uint_3
-;CHECK:         %57 = OpULessThan %bool %35 %55
-;CHECK:               OpSelectionMerge %58 None
-;CHECK:               OpBranchConditional %57 %59 %60
-;CHECK:         %59 = OpLabel
-;CHECK:         %61 = OpLoad %float %20
-;CHECK:               OpBranch %58
-;CHECK:         %60 = OpLabel
-;CHECK:        %114 = OpFunctionCall %void %inst_bindless_stream_write_5 %uint_45 %uint_4 %uint_0 %35 %55
-;CHECK:               OpBranch %58
-;CHECK:         %58 = OpLabel
-;CHECK:        %116 = OpPhi %float %61 %59 %115 %60
-               OpStore %v_vtxResult %21
-;CHECK-NOT:           OpStore %v_vtxResult %21
-;CHECK:               OpStore %v_vtxResult %116
-               OpReturn
-               OpFunctionEnd
-               )" + kDirectRead3 + kStreamWrite5Vert;
+%a_position = OpVariable %_ptr_Input_v4float Input
+;CHECK: %_ptr_Input_uint = OpTypePointer Input %uint
+;CHECK: %gl_VertexIndex = OpVariable %_ptr_Input_uint Input
+;CHECK: %gl_InstanceIndex = OpVariable %_ptr_Input_uint Input
+;CHECK: [[null_float:%\w+]] = OpConstantNull %float
+)" + kImportStub + R"(
+%main = OpFunction %void None %3
+%5 = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+%20 = OpAccessChain %_ptr_Uniform_float %_ %int_0 %int_2 %uint_1
+%21 = OpLoad %float %20
+;CHECK-NOT: %21 = OpLoad %float %20
+;CHECK: {{%\w+}} = OpIMul %uint %uint_4 %int_2
+;CHECK: {{%\w+}} = OpIAdd %uint %uint_0 {{%\w+}}
+;CHECK: {{%\w+}} = OpIMul %uint %uint_16 %uint_1
+;CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_3
+;CHECK: {{%\w+}} = OpLoad %uint %gl_VertexIndex
+;CHECK: {{%\w+}} = OpLoad %uint %gl_InstanceIndex
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_0 {{%\w+}} {{%\w+}} %uint_0
+;CHECK: [[desc_state:%\w+]] = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_46 {{%\w+}} %uint_0 %uint_0 %uint_0 {{%\w+}}
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional [[desc_state]] {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: [[load_result]] = OpLoad %float %20
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: [[phi_result]] = OpPhi %float [[load_result]] {{%\w+}} [[null_float]] {{%\w+}}
+OpStore %v_vtxResult %21
+;CHECK-NOT: OpStore %v_vtxResult %21$
+;CHECK: OpStore %v_vtxResult [[phi_result]]
+OpReturn
+OpFunctionEnd
+)";
   // clang-format on
 
-  SetTargetEnv(SPV_ENV_VULKAN_1_2);
+  SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
   SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 7u, 23u, false,
-                                               false, true, false, true);
+  SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 23u);
 }
 
 TEST_F(InstBindlessTest, UniformMatrixRefColumnMajor) {
@@ -5271,120 +4117,99 @@
 
   // clang-format off
   const std::string text = R"(
-               OpCapability Shader
-;CHECK:               OpExtension "SPV_KHR_storage_buffer_storage_class"
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Vertex %main "main" %v_vtxResult %_ %a_position
-;CHECK:        OpEntryPoint Vertex %main "main" %v_vtxResult %_ %a_position %inst_bindless_input_buffer %inst_bindless_output_buffer %gl_VertexIndex %gl_InstanceIndex
-               OpSource GLSL 450
-               OpSourceExtension "GL_EXT_scalar_block_layout"
-               OpName %main "main"
-               OpName %v_vtxResult "v_vtxResult"
-               OpName %Block "Block"
-               OpMemberName %Block 0 "var"
-               OpName %_ ""
-               OpName %a_position "a_position"
-               OpDecorate %v_vtxResult RelaxedPrecision
-               OpDecorate %v_vtxResult Location 0
-               OpMemberDecorate %Block 0 ColMajor
-               OpMemberDecorate %Block 0 RelaxedPrecision
-               OpMemberDecorate %Block 0 Offset 0
-               OpMemberDecorate %Block 0 MatrixStride 8
-               OpDecorate %Block Block
-               OpDecorate %_ DescriptorSet 0
-               OpDecorate %_ Binding 0
-               OpDecorate %21 RelaxedPrecision
-;CHECK-NOT:           OpDecorate %21 RelaxedPrecision
-;CHECK:               OpDecorate %115 RelaxedPrecision
-               OpDecorate %a_position Location 0
-;CHECK:               OpDecorate %_runtimearr_uint ArrayStride 4
-)" + kInputDecorations + R"(
-;CHECK:               OpDecorate %61 RelaxedPrecision
-)" + kOutputDecorations + R"(
-;CHECK:               OpDecorate %gl_VertexIndex BuiltIn VertexIndex
-;CHECK:               OpDecorate %gl_InstanceIndex BuiltIn InstanceIndex
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
+OpCapability Shader
+;CHECK: OpCapability Linkage
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %main "main" %v_vtxResult %_ %a_position
+;CHECK: OpEntryPoint Vertex %main "main" %v_vtxResult %_ %a_position %gl_VertexIndex %gl_InstanceIndex
+OpSource GLSL 450
+OpSourceExtension "GL_EXT_scalar_block_layout"
+OpName %main "main"
+OpName %v_vtxResult "v_vtxResult"
+OpName %Block "Block"
+OpMemberName %Block 0 "var"
+OpName %_ ""
+OpName %a_position "a_position"
+OpDecorate %v_vtxResult RelaxedPrecision
+OpDecorate %v_vtxResult Location 0
+OpMemberDecorate %Block 0 ColMajor
+OpMemberDecorate %Block 0 RelaxedPrecision
+OpMemberDecorate %Block 0 Offset 0
+OpMemberDecorate %Block 0 MatrixStride 8
+OpDecorate %Block Block
+OpDecorate %_ DescriptorSet 0
+OpDecorate %_ Binding 0
+OpDecorate %21 RelaxedPrecision
+;CHECK-NOT: OpDecorate %21 RelaxedPrecision
+;CHECK: OpDecorate %v_vtxResult RelaxedPrecision
+;CHECK: OpDecorate [[phi_result:%\w+]] RelaxedPrecision
+OpDecorate %a_position Location 0
+)" + kImportDeco + R"(
+;CHECK: OpDecorate %gl_VertexIndex BuiltIn VertexIndex
+;CHECK: OpDecorate %gl_InstanceIndex BuiltIn InstanceIndex
+;CHECK: OpDecorate [[load_result:%\w+]] RelaxedPrecision
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%float = OpTypeFloat 32
 %_ptr_Output_float = OpTypePointer Output %float
 %v_vtxResult = OpVariable %_ptr_Output_float Output
-    %v2float = OpTypeVector %float 2
+%v2float = OpTypeVector %float 2
 %mat4v2float = OpTypeMatrix %v2float 4
-      %Block = OpTypeStruct %mat4v2float
+%Block = OpTypeStruct %mat4v2float
 %_ptr_Uniform_Block = OpTypePointer Uniform %Block
-          %_ = OpVariable %_ptr_Uniform_Block Uniform
-        %int = OpTypeInt 32 1
-      %int_0 = OpConstant %int 0
-      %int_2 = OpConstant %int 2
-       %uint = OpTypeInt 32 0
-     %uint_1 = OpConstant %uint 1
+%_ = OpVariable %_ptr_Uniform_Block Uniform
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%int_2 = OpConstant %int 2
+%uint = OpTypeInt 32 0
+%uint_1 = OpConstant %uint 1
 %_ptr_Uniform_float = OpTypePointer Uniform %float
-    %v4float = OpTypeVector %float 4
+%v4float = OpTypeVector %float 4
 %_ptr_Input_v4float = OpTypePointer Input %v4float
- %a_position = OpVariable %_ptr_Input_v4float Input
-;CHECK:     %uint_0 = OpConstant %uint 0
-;CHECK:     %uint_8 = OpConstant %uint 8
-;CHECK:     %uint_4 = OpConstant %uint 4
-;CHECK:     %uint_3 = OpConstant %uint 3
-;CHECK:         %37 = OpTypeFunction %uint %uint %uint %uint
-;CHECK:%_runtimearr_uint = OpTypeRuntimeArray %uint
-)" + kInputGlobals + R"(
-;CHECK:%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-;CHECK:       %bool = OpTypeBool
-;CHECK:         %63 = OpTypeFunction %void %uint %uint %uint %uint %uint
-)" + kOutputGlobals + R"(
-;CHECK:    %uint_11 = OpConstant %uint 11
-;CHECK:    %uint_23 = OpConstant %uint 23
-;CHECK:     %uint_2 = OpConstant %uint 2
-;CHECK:%_ptr_Input_uint = OpTypePointer Input %uint
-;CHECK:%gl_VertexIndex = OpVariable %_ptr_Input_uint Input
-;CHECK:%gl_InstanceIndex = OpVariable %_ptr_Input_uint Input
-;CHECK:     %uint_5 = OpConstant %uint 5
-;CHECK:     %uint_7 = OpConstant %uint 7
-;CHECK:     %uint_9 = OpConstant %uint 9
-;CHECK:    %uint_10 = OpConstant %uint 10
-;CHECK:    %uint_45 = OpConstant %uint 45
-;CHECK:        %114 = OpConstantNull %float
+%a_position = OpVariable %_ptr_Input_v4float Input
+;CHECK: %_ptr_Input_uint = OpTypePointer Input %uint
+;CHECK: %gl_VertexIndex = OpVariable %_ptr_Input_uint Input
+;CHECK: %gl_InstanceIndex = OpVariable %_ptr_Input_uint Input
+;CHECK: [[null_float:%\w+]] = OpConstantNull %float
+)" + kImportStub + R"(
 %main = OpFunction %void None %3
-          %5 = OpLabel
-;CHECK:         %55 = OpFunctionCall %uint %inst_bindless_direct_read_3 %uint_1 %uint_0 %uint_0
-;CHECK:               OpBranch %26
-;CHECK:         %26 = OpLabel
-;CHECK:               OpBranch %25
-;CHECK:         %25 = OpLabel
-         %20 = OpAccessChain %_ptr_Uniform_float %_ %int_0 %int_2 %uint_1
-         %21 = OpLoad %float %20
-;CHECK-NOT:     %21 = OpLoad %float %20
-;CHECK:         %29 = OpIMul %uint %uint_8 %int_2
-;CHECK:         %30 = OpIAdd %uint %uint_0 %29
-;CHECK:         %32 = OpIMul %uint %uint_4 %uint_1
-;CHECK:         %33 = OpIAdd %uint %30 %32
-;CHECK:         %35 = OpIAdd %uint %33 %uint_3
-;CHECK:         %57 = OpULessThan %bool %35 %55
-;CHECK:               OpSelectionMerge %58 None
-;CHECK:               OpBranchConditional %57 %59 %60
-;CHECK:         %59 = OpLabel
-;CHECK:         %61 = OpLoad %float %20
-;CHECK:               OpBranch %58
-;CHECK:         %60 = OpLabel
-;CHECK:        %113 = OpFunctionCall %void %inst_bindless_stream_write_5 %uint_45 %uint_4 %uint_0 %35 %55
-;CHECK:               OpBranch %58
-;CHECK:         %58 = OpLabel
-;CHECK:        %115 = OpPhi %float %61 %59 %114 %60
-               OpStore %v_vtxResult %21
-;CHECK-NOT:           OpStore %v_vtxResult %21
-;CHECK:               OpStore %v_vtxResult %115
-               OpReturn
-               OpFunctionEnd
-               )" + kDirectRead3 + kStreamWrite5Vert;
+%5 = OpLabel
+%20 = OpAccessChain %_ptr_Uniform_float %_ %int_0 %int_2 %uint_1
+%21 = OpLoad %float %20
+;CHECK-NOT: %21 = OpLoad %float %20
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpIMul %uint %uint_8 %int_2
+;CHECK: {{%\w+}} = OpIAdd %uint %uint_0 {{%\w+}}
+;CHECK: {{%\w+}} = OpIMul %uint %uint_4 %uint_1
+;CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_3
+;CHECK: {{%\w+}} = OpLoad %uint %gl_VertexIndex
+;CHECK: {{%\w+}} = OpLoad %uint %gl_InstanceIndex
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_0 {{%\w+}} {{%\w+}} %uint_0
+;CHECK: [[desc_state:%\w+]] = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_46 {{%\w+}} %uint_0 %uint_0 %uint_0 {{%\w+}}
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional [[desc_state]] {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK:[[load_result]] = OpLoad %float %20
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: [[phi_result]] = OpPhi %float [[load_result]] {{%\w+}} [[null_float]] {{%\w+}}
+OpStore %v_vtxResult %21
+;CHECK-NOT: OpStore %v_vtxResult %21$
+;CHECK: OpStore %v_vtxResult [[phi_result]]
+OpReturn
+OpFunctionEnd
+)";
   // clang-format on
 
-  SetTargetEnv(SPV_ENV_VULKAN_1_2);
+  SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
   ValidatorOptions()->uniform_buffer_standard_layout = true;
   SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 7u, 23u, false,
-                                               false, true, false, true);
+  SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 23u);
 }
 
 TEST_F(InstBindlessTest, UniformMatrixVecRefRowMajor) {
@@ -5396,7 +4221,7 @@
   // layout(location = 0) in highp vec4 a_position;
   // layout(location = 0) out highp vec2 v_vtxResult;
   //
-  // layout(set = 0, binding = 0, std430, row_major) uniform Block
+  // layout(set = 3, binding = 7, std430, row_major) uniform Block
   // {
   //    lowp mat2 var[3][4];
   // };
@@ -5408,130 +4233,103 @@
 
   // clang-format off
   const std::string text = R"(
-               OpCapability Shader
-;CHECK:               OpExtension "SPV_KHR_storage_buffer_storage_class"
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Vertex %main "main" %v_vtxResult %_ %a_position
-;CHECK:        OpEntryPoint Vertex %main "main" %v_vtxResult %_ %a_position %inst_bindless_input_buffer %inst_bindless_output_buffer %gl_VertexIndex %gl_InstanceIndex
-               OpSource GLSL 450
-               OpSourceExtension "GL_EXT_scalar_block_layout"
-               OpName %main "main"
-               OpName %v_vtxResult "v_vtxResult"
-               OpName %Block "Block"
-               OpMemberName %Block 0 "var"
-               OpName %_ ""
-               OpName %a_position "a_position"
-               OpDecorate %v_vtxResult Location 0
-               OpDecorate %_arr_mat2v2float_uint_4 ArrayStride 32
-               OpDecorate %_arr__arr_mat2v2float_uint_4_uint_3 ArrayStride 128
-               OpMemberDecorate %Block 0 RowMajor
-               OpMemberDecorate %Block 0 RelaxedPrecision
-               OpMemberDecorate %Block 0 Offset 0
-               OpMemberDecorate %Block 0 MatrixStride 16
-               OpDecorate %Block Block
-               OpDecorate %_ DescriptorSet 0
-               OpDecorate %_ Binding 0
-               OpDecorate %26 RelaxedPrecision
-;CHECK-NOT:               OpDecorate %26 RelaxedPrecision
-;CHECK:               OpDecorate %125 RelaxedPrecision
-               OpDecorate %a_position Location 0
-;CHECK:               OpDecorate %_runtimearr_uint ArrayStride 4
-)" + kInputDecorations + R"(
-;CHECK:               OpDecorate %70 RelaxedPrecision
-)" + kOutputDecorations + R"(
-;CHECK:               OpDecorate %gl_VertexIndex BuiltIn VertexIndex
-;CHECK:               OpDecorate %gl_InstanceIndex BuiltIn InstanceIndex
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v2float = OpTypeVector %float 2
+OpCapability Shader
+;CHECK: OpCapability Linkage
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %main "main" %v_vtxResult %_ %a_position
+;CHECK: OpEntryPoint Vertex %main "main" %v_vtxResult %_ %a_position %gl_VertexIndex %gl_InstanceIndex
+OpSource GLSL 450
+OpSourceExtension "GL_EXT_scalar_block_layout"
+OpName %main "main"
+OpName %v_vtxResult "v_vtxResult"
+OpName %Block "Block"
+OpMemberName %Block 0 "var"
+OpName %_ ""
+OpName %a_position "a_position"
+OpDecorate %v_vtxResult Location 0
+OpDecorate %_arr_mat2v2float_uint_4 ArrayStride 32
+OpDecorate %_arr__arr_mat2v2float_uint_4_uint_3 ArrayStride 128
+OpMemberDecorate %Block 0 RowMajor
+OpMemberDecorate %Block 0 RelaxedPrecision
+OpMemberDecorate %Block 0 Offset 0
+OpMemberDecorate %Block 0 MatrixStride 16
+OpDecorate %Block Block
+OpDecorate %_ DescriptorSet 3
+OpDecorate %_ Binding 7
+OpDecorate %26 RelaxedPrecision
+;CHECK-NOT: OpDecorate %26 RelaxedPrecision
+;CHECK: OpDecorate [[phi_result:%\w+]] RelaxedPrecision
+OpDecorate %a_position Location 0
+)" + kImportDeco + R"(
+;CHECK: OpDecorate %gl_VertexIndex BuiltIn VertexIndex
+;CHECK: OpDecorate %gl_InstanceIndex BuiltIn InstanceIndex
+;CHECK: OpDecorate [[load_result:%\w+]] RelaxedPrecision
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v2float = OpTypeVector %float 2
 %_ptr_Output_v2float = OpTypePointer Output %v2float
 %v_vtxResult = OpVariable %_ptr_Output_v2float Output
 %mat2v2float = OpTypeMatrix %v2float 2
-       %uint = OpTypeInt 32 0
-     %uint_4 = OpConstant %uint 4
+%uint = OpTypeInt 32 0
+%uint_4 = OpConstant %uint 4
 %_arr_mat2v2float_uint_4 = OpTypeArray %mat2v2float %uint_4
-     %uint_3 = OpConstant %uint 3
+%uint_3 = OpConstant %uint 3
 %_arr__arr_mat2v2float_uint_4_uint_3 = OpTypeArray %_arr_mat2v2float_uint_4 %uint_3
-      %Block = OpTypeStruct %_arr__arr_mat2v2float_uint_4_uint_3
+%Block = OpTypeStruct %_arr__arr_mat2v2float_uint_4_uint_3
 %_ptr_Uniform_Block = OpTypePointer Uniform %Block
-          %_ = OpVariable %_ptr_Uniform_Block Uniform
-        %int = OpTypeInt 32 1
-      %int_0 = OpConstant %int 0
-      %int_2 = OpConstant %int 2
-      %int_3 = OpConstant %int 3
-      %int_1 = OpConstant %int 1
+%_ = OpVariable %_ptr_Uniform_Block Uniform
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%int_2 = OpConstant %int 2
+%int_3 = OpConstant %int 3
+%int_1 = OpConstant %int 1
 %_ptr_Uniform_v2float = OpTypePointer Uniform %v2float
-    %v4float = OpTypeVector %float 4
+%v4float = OpTypeVector %float 4
 %_ptr_Input_v4float = OpTypePointer Input %v4float
- %a_position = OpVariable %_ptr_Input_v4float Input
-;CHECK:     %uint_0 = OpConstant %uint 0
-;CHECK:   %uint_128 = OpConstant %uint 128
-;CHECK:    %uint_32 = OpConstant %uint 32
-;CHECK:    %uint_16 = OpConstant %uint 16
-;CHECK:    %uint_19 = OpConstant %uint 19
-;CHECK:     %uint_1 = OpConstant %uint 1
-;CHECK:         %46 = OpTypeFunction %uint %uint %uint %uint
-;CHECK:%_runtimearr_uint = OpTypeRuntimeArray %uint
-)" + kInputGlobals + R"(
-;CHECK:%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-;CHECK:       %bool = OpTypeBool
-;CHECK:         %72 = OpTypeFunction %void %uint %uint %uint %uint %uint
-)" + kOutputGlobals + R"(
-;CHECK:    %uint_11 = OpConstant %uint 11
-;CHECK:    %uint_23 = OpConstant %uint 23
-;CHECK:     %uint_2 = OpConstant %uint 2
-;CHECK:%_ptr_Input_uint = OpTypePointer Input %uint
-;CHECK:%gl_VertexIndex = OpVariable %_ptr_Input_uint Input
-;CHECK:%gl_InstanceIndex = OpVariable %_ptr_Input_uint Input
-;CHECK:     %uint_5 = OpConstant %uint 5
-;CHECK:     %uint_7 = OpConstant %uint 7
-;CHECK:     %uint_8 = OpConstant %uint 8
-;CHECK:     %uint_9 = OpConstant %uint 9
-;CHECK:    %uint_10 = OpConstant %uint 10
-;CHECK:    %uint_51 = OpConstant %uint 51
-;CHECK:        %124 = OpConstantNull %v2float
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-;CHECK:         %64 = OpFunctionCall %uint %inst_bindless_direct_read_3 %uint_1 %uint_0 %uint_0
-;CHECK:               OpBranch %31
-;CHECK:         %31 = OpLabel
-;CHECK:               OpBranch %30
-;CHECK:         %30 = OpLabel
-         %25 = OpAccessChain %_ptr_Uniform_v2float %_ %int_0 %int_2 %int_3 %int_1
-         %26 = OpLoad %v2float %25
-               OpStore %v_vtxResult %26
-;CHECK-NOT:         %26 = OpLoad %v2float %25
-;CHECK-NOT:               OpStore %v_vtxResult %26
-;CHECK:         %34 = OpIMul %uint %uint_128 %int_2
-;CHECK:         %35 = OpIAdd %uint %uint_0 %34
-;CHECK:         %37 = OpIMul %uint %uint_32 %int_3
-;CHECK:         %38 = OpIAdd %uint %35 %37
-;CHECK:         %40 = OpIMul %uint %uint_4 %int_1
-;CHECK:         %41 = OpIAdd %uint %38 %40
-;CHECK:         %43 = OpIAdd %uint %41 %uint_19
-;CHECK:         %66 = OpULessThan %bool %43 %64
-;CHECK:               OpSelectionMerge %67 None
-;CHECK:               OpBranchConditional %66 %68 %69
-;CHECK:         %68 = OpLabel
-;CHECK:         %70 = OpLoad %v2float %25
-;CHECK:               OpBranch %67
-;CHECK:         %69 = OpLabel
-;CHECK:        %123 = OpFunctionCall %void %inst_bindless_stream_write_5 %uint_51 %uint_4 %uint_0 %43 %64
-;CHECK:               OpBranch %67
-;CHECK:         %67 = OpLabel
-;CHECK:        %125 = OpPhi %v2float %70 %68 %124 %69
-;CHECK:               OpStore %v_vtxResult %125
-               OpReturn
-               OpFunctionEnd
-               )" + kDirectRead3 + kStreamWrite5Vert;
+%a_position = OpVariable %_ptr_Input_v4float Input
+;CHECK: %_ptr_Input_uint = OpTypePointer Input %uint
+;CHECK: %gl_VertexIndex = OpVariable %_ptr_Input_uint Input
+;CHECK: %gl_InstanceIndex = OpVariable %_ptr_Input_uint Input
+;CHECK: [[null_v2float:%\w+]] = OpConstantNull %v2float
+)" + kImportStub + R"(
+%main = OpFunction %void None %3
+%5 = OpLabel
+%25 = OpAccessChain %_ptr_Uniform_v2float %_ %int_0 %int_2 %int_3 %int_1
+;CHECK: {{%\w+}} = OpIMul %uint %uint_128 %int_2
+;CHECK: {{%\w+}} = OpIAdd %uint %uint_0 {{%\w+}}
+;CHECK: {{%\w+}} = OpIMul %uint %uint_32 %int_3
+;CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpIMul %uint %uint_4 %int_1
+;CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_19
+;CHECK: {{%\w+}} = OpLoad %uint %gl_VertexIndex
+;CHECK: {{%\w+}} = OpLoad %uint %gl_InstanceIndex
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_0 {{%\w+}} {{%\w+}} %uint_0
+;CHECK: [[desc_state:%\w+]] = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_52 {{%\w+}} %uint_3 %uint_7 %uint_0 {{%\w+}}
+%26 = OpLoad %v2float %25
+OpStore %v_vtxResult %26
+;CHECK-NOT: %26 = OpLoad %v2float %25
+;CHECK-NOT: OpStore %v_vtxResult %26
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional [[desc_state]] {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: [[load_result]] = OpLoad %v2float %25
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: [[phi_result]] = OpPhi %v2float [[load_result]] {{%\w+}} [[null_v2float]] {{%\w+}}
+;CHECK: OpStore %v_vtxResult [[phi_result]]
+OpReturn
+OpFunctionEnd
+)";
   // clang-format on
 
-  SetTargetEnv(SPV_ENV_VULKAN_1_2);
+  SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
   SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 7u, 23u, false,
-                                               false, true, false, true);
+  SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 23u);
 }
 
 TEST_F(InstBindlessTest, ImageBufferOOBRead) {
@@ -5548,101 +4346,76 @@
 
   // clang-format off
   const std::string text = R"(
-                          OpCapability Shader
-                          OpCapability ImageBuffer
-;CHECK:                   OpCapability ImageQuery
-;CHECK:                   OpExtension "SPV_KHR_storage_buffer_storage_class"
-                     %1 = OpExtInstImport "GLSL.std.450"
-                          OpMemoryModel Logical GLSL450
-                          OpEntryPoint Fragment %main "main" %x %s %ii
-                          OpExecutionMode %main OriginUpperLeft
-                          OpSource GLSL 450
-                          OpName %main "main"
-                          OpName %x "x"
-                          OpName %s "s"
-                          OpName %ii "ii"
-                          OpDecorate %x Location 11
-                          OpDecorate %s DescriptorSet 3
-                          OpDecorate %s Binding 7
-                          OpDecorate %s NonWritable
-                          OpDecorate %ii Flat
-                          OpDecorate %ii Location 13
-;CHECK:                   OpDecorate %_runtimearr_uint ArrayStride 4
-)" + kOutputDecorations + R"(
-;CHECK:                   OpDecorate %gl_FragCoord BuiltIn FragCoord
-                  %void = OpTypeVoid
-                     %3 = OpTypeFunction %void
-                 %float = OpTypeFloat 32
-               %v4float = OpTypeVector %float 4
-           %_ptr_Output_v4float = OpTypePointer Output %v4float
-                     %x = OpVariable %_ptr_Output_v4float Output
-                    %10 = OpTypeImage %float Buffer 0 0 0 2 R32f
-           %_ptr_UniformConstant_10 = OpTypePointer UniformConstant %10
-                     %s = OpVariable %_ptr_UniformConstant_10 UniformConstant
-                   %int = OpTypeInt 32 1
-           %_ptr_Input_int = OpTypePointer Input %int
-                    %ii = OpVariable %_ptr_Input_int Input
-;CHECK:           %uint = OpTypeInt 32 0
-;CHECK:         %uint_0 = OpConstant %uint 0
-;CHECK:           %bool = OpTypeBool
-;CHECK:         %uint_7 = OpConstant %uint 7
-;CHECK:             %35 = OpTypeFunction %void %uint %uint %uint %uint %uint
-;CHECK:    %_runtimearr_uint = OpTypeRuntimeArray %uint
-)" + kOutputGlobals + R"(
-;CHECK:    %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-;CHECK:        %uint_11 = OpConstant %uint 11
-;CHECK:         %uint_4 = OpConstant %uint 4
-;CHECK:         %uint_1 = OpConstant %uint 1
-;CHECK:        %uint_23 = OpConstant %uint 23
-;CHECK:         %uint_2 = OpConstant %uint 2
-;CHECK:         %uint_3 = OpConstant %uint 3
-;CHECK:    %_ptr_Input_v4float = OpTypePointer Input %v4float
-;CHECK:    %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
-;CHECK:         %v4uint = OpTypeVector %uint 4
-;CHECK:         %uint_5 = OpConstant %uint 5
-;CHECK:         %uint_8 = OpConstant %uint 8
-;CHECK:         %uint_9 = OpConstant %uint 9
-;CHECK:        %uint_10 = OpConstant %uint 10
-;CHECK:        %uint_33 = OpConstant %uint 33
-;CHECK:             %93 = OpConstantNull %v4float
-                  %main = OpFunction %void None %3
-                     %5 = OpLabel
-;CHECK:                   OpBranch %21
-;CHECK:             %21 = OpLabel
-;CHECK:                   OpBranch %20
-;CHECK:             %20 = OpLabel
-;CHECK:                   OpBranch %19
-;CHECK:             %19 = OpLabel
-                    %13 = OpLoad %10 %s
-                    %17 = OpLoad %int %ii
-                    %18 = OpImageRead %v4float %13 %17
-                          OpStore %x %18
-;CHECK-NOT:         %18 = OpImageRead %v4float %13 %17
-;CHECK-NOT:               OpStore %x %18
-;CHECK:             %23 = OpBitcast %uint %17
-;CHECK:             %25 = OpImageQuerySize %uint %13
-;CHECK:             %27 = OpULessThan %bool %23 %25
-;CHECK:                   OpSelectionMerge %29 None
-;CHECK:                   OpBranchConditional %27 %30 %31
-;CHECK:             %30 = OpLabel
-;CHECK:             %32 = OpLoad %10 %s
-;CHECK:             %33 = OpImageRead %v4float %32 %17
-;CHECK:                   OpBranch %29
-;CHECK:             %31 = OpLabel
-;CHECK:             %92 = OpFunctionCall %void %inst_bindless_stream_write_5 %uint_33 %uint_7 %uint_0 %23 %25
-;CHECK:                   OpBranch %29
-;CHECK:             %29 = OpLabel
-;CHECK:             %94 = OpPhi %v4float %33 %30 %93 %31
-;CHECK:                   OpStore %x %94
-                          OpReturn
-                          OpFunctionEnd
-                          )" + kStreamWrite5Frag;
+OpCapability Shader
+OpCapability ImageBuffer
+;CHECK: OpCapability Linkage
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %x %s %ii
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 450
+OpName %main "main"
+OpName %x "x"
+OpName %s "s"
+OpName %ii "ii"
+OpDecorate %x Location 11
+OpDecorate %s DescriptorSet 3
+OpDecorate %s Binding 7
+OpDecorate %s NonWritable
+OpDecorate %ii Flat
+OpDecorate %ii Location 13
+)" + kImportDeco + R"(
+;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%x = OpVariable %_ptr_Output_v4float Output
+%10 = OpTypeImage %float Buffer 0 0 0 2 R32f
+%_ptr_UniformConstant_10 = OpTypePointer UniformConstant %10
+%s = OpVariable %_ptr_UniformConstant_10 UniformConstant
+%int = OpTypeInt 32 1
+%_ptr_Input_int = OpTypePointer Input %int
+%ii = OpVariable %_ptr_Input_int Input
+;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
+;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
+;CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float
+%main = OpFunction %void None %3
+%5 = OpLabel
+;CHECK: OpBranch %19
+;CHECK: %19 = OpLabel
+%13 = OpLoad %10 %s
+%17 = OpLoad %int %ii
+%18 = OpImageRead %v4float %13 %17
+OpStore %x %18
+;CHECK-NOT: %18 = OpImageRead %v4float %13 %17
+;CHECK-NOT: OpStore %x %18
+;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord
+;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}}
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_34 {{%\w+}} %uint_3 %uint_7 %uint_0 %22
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpLoad %10 %s
+;CHECK: {{%\w+}} = OpImageRead %v4float {{%\w+}} %17
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: [[phi_result:%\w+]] = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}}
+;CHECK: OpStore %x [[phi_result]]
+OpReturn
+OpFunctionEnd
+)";
   // clang-format on
 
-  SetTargetEnv(SPV_ENV_VULKAN_1_2);
+  SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
   SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 7u, 23u, false,
-                                               false, true, true, true);
+  SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 23u);
 }
 
 TEST_F(InstBindlessTest, ImageBufferOOBWrite) {
@@ -5659,98 +4432,75 @@
 
   // clang-format off
   const std::string text = R"(
-                          OpCapability Shader
-                          OpCapability ImageBuffer
-;CHECK:                   OpCapability ImageQuery
-;CHECK:                   OpExtension "SPV_KHR_storage_buffer_storage_class"
-                     %1 = OpExtInstImport "GLSL.std.450"
-                          OpMemoryModel Logical GLSL450
-                          OpEntryPoint Fragment %main "main" %s %ii %x
-;CHECK:                   OpEntryPoint Fragment %main "main" %s %ii %x %inst_bindless_output_buffer %gl_FragCoord
-                          OpExecutionMode %main OriginUpperLeft
-                          OpSource GLSL 450
-                          OpName %main "main"
-                          OpName %s "s"
-                          OpName %ii "ii"
-                          OpName %x "x"
-                          OpDecorate %s DescriptorSet 3
-                          OpDecorate %s Binding 7
-                          OpDecorate %s NonReadable
-                          OpDecorate %ii Flat
-                          OpDecorate %ii Location 13
-                          OpDecorate %x Location 11
-;CHECK:                   OpDecorate %_runtimearr_uint ArrayStride 4
-)" + kOutputDecorations + R"(
-;CHECK:                   OpDecorate %gl_FragCoord BuiltIn FragCoord
-                  %void = OpTypeVoid
-                     %3 = OpTypeFunction %void
-                 %float = OpTypeFloat 32
-                     %7 = OpTypeImage %float Buffer 0 0 0 2 R32f
-           %_ptr_UniformConstant_7 = OpTypePointer UniformConstant %7
-                     %s = OpVariable %_ptr_UniformConstant_7 UniformConstant
-                   %int = OpTypeInt 32 1
-           %_ptr_Input_int = OpTypePointer Input %int
-                    %ii = OpVariable %_ptr_Input_int Input
-               %v4float = OpTypeVector %float 4
-           %_ptr_Output_v4float = OpTypePointer Output %v4float
-                     %x = OpVariable %_ptr_Output_v4float Output
-;CHECK:           %uint = OpTypeInt 32 0
-;CHECK:         %uint_0 = OpConstant %uint 0
-;CHECK:           %bool = OpTypeBool
-;CHECK:         %uint_7 = OpConstant %uint 7
-;CHECK:             %34 = OpTypeFunction %void %uint %uint %uint %uint %uint
-;CHECK:    %_runtimearr_uint = OpTypeRuntimeArray %uint
-)" + kOutputGlobals + R"(
-;CHECK:    %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-;CHECK:        %uint_11 = OpConstant %uint 11
-;CHECK:         %uint_4 = OpConstant %uint 4
-;CHECK:         %uint_1 = OpConstant %uint 1
-;CHECK:        %uint_23 = OpConstant %uint 23
-;CHECK:         %uint_2 = OpConstant %uint 2
-;CHECK:         %uint_3 = OpConstant %uint 3
-;CHECK:    %_ptr_Input_v4float = OpTypePointer Input %v4float
-;CHECK:    %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
-;CHECK:         %v4uint = OpTypeVector %uint 4
-;CHECK:         %uint_5 = OpConstant %uint 5
-;CHECK:         %uint_8 = OpConstant %uint 8
-;CHECK:         %uint_9 = OpConstant %uint 9
-;CHECK:        %uint_10 = OpConstant %uint 10
-;CHECK:        %uint_34 = OpConstant %uint 34
-                  %main = OpFunction %void None %3
-                     %5 = OpLabel
-;CHECK:                   OpBranch %21
-;CHECK:             %21 = OpLabel
-;CHECK:                   OpBranch %20
-;CHECK:             %20 = OpLabel
-;CHECK:                   OpBranch %19
-;CHECK:             %19 = OpLabel
-                    %10 = OpLoad %7 %s
-                    %14 = OpLoad %int %ii
-                    %18 = OpLoad %v4float %x
-                          OpImageWrite %10 %14 %18
-;CHECK-NOT:               OpImageWrite %10 %14 %18
-;CHECK:             %23 = OpBitcast %uint %14
-;CHECK:             %25 = OpImageQuerySize %uint %10
-;CHECK:             %27 = OpULessThan %bool %23 %25
-;CHECK:                   OpSelectionMerge %29 None
-;CHECK:                   OpBranchConditional %27 %30 %31
-;CHECK:             %30 = OpLabel
-;CHECK:             %32 = OpLoad %7 %s
-;CHECK:                   OpImageWrite %32 %14 %18
-;CHECK:                   OpBranch %29
-;CHECK:             %31 = OpLabel
-;CHECK:             %91 = OpFunctionCall %void %inst_bindless_stream_write_5 %uint_34 %uint_7 %uint_0 %23 %25
-;CHECK:                   OpBranch %29
-;CHECK:             %29 = OpLabel
-                          OpReturn
-                          OpFunctionEnd
-                          )" + kStreamWrite5Frag;
+OpCapability Shader
+OpCapability ImageBuffer
+;CHECK: OpCapability Linkage
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %s %ii %x
+;CHECK: OpEntryPoint Fragment %main "main" %s %ii %x %gl_FragCoord
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 450
+OpName %main "main"
+OpName %s "s"
+OpName %ii "ii"
+OpName %x "x"
+OpDecorate %s DescriptorSet 3
+OpDecorate %s Binding 7
+OpDecorate %s NonReadable
+OpDecorate %ii Flat
+OpDecorate %ii Location 13
+OpDecorate %x Location 11
+)" + kImportDeco + R"(
+;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%7 = OpTypeImage %float Buffer 0 0 0 2 R32f
+%_ptr_UniformConstant_7 = OpTypePointer UniformConstant %7
+%s = OpVariable %_ptr_UniformConstant_7 UniformConstant
+%int = OpTypeInt 32 1
+%_ptr_Input_int = OpTypePointer Input %int
+%ii = OpVariable %_ptr_Input_int Input
+%v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%x = OpVariable %_ptr_Output_v4float Output
+;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
+;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
+)" + kImportStub + R"(
+%main = OpFunction %void None %3
+%5 = OpLabel
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: %19 = OpLabel
+%10 = OpLoad %7 %s
+%14 = OpLoad %int %ii
+%18 = OpLoad %v4float %x
+OpImageWrite %10 %14 %18
+;CHECK-NOT: OpImageWrite %10 %14 %18
+;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord
+;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}}
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_35 {{%\w+}} %uint_3 %uint_7 %uint_0 {{%\w+}}
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpLoad %7 %s
+;CHECK: OpImageWrite {{%\w+}} %14 %18
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+OpReturn
+OpFunctionEnd
+)";
   // clang-format on
 
-  SetTargetEnv(SPV_ENV_VULKAN_1_2);
+  SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
   SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 7u, 23u, false,
-                                               false, true, true, true);
+  SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 23u);
 }
 
 TEST_F(InstBindlessTest, TextureBufferOOBFetch) {
@@ -5767,102 +4517,75 @@
 
   // clang-format off
   const std::string text = R"(
-                          OpCapability Shader
-                          OpCapability SampledBuffer
-;CHECK:                   OpCapability ImageQuery
-;CHECK:                   OpExtension "SPV_KHR_storage_buffer_storage_class"
-                     %1 = OpExtInstImport "GLSL.std.450"
-                          OpMemoryModel Logical GLSL450
-                          OpEntryPoint Fragment %main "main" %x %s %ii
-;CHECK:                   OpEntryPoint Fragment %main "main" %x %s %ii %inst_bindless_output_buffer %gl_FragCoord
-                          OpExecutionMode %main OriginUpperLeft
-                          OpSource GLSL 450
-                          OpName %main "main"
-                          OpName %x "x"
-                          OpName %s "s"
-                          OpName %ii "ii"
-                          OpDecorate %x Location 11
-                          OpDecorate %s DescriptorSet 3
-                          OpDecorate %s Binding 7
-                          OpDecorate %ii Flat
-                          OpDecorate %ii Location 13
-;CHECK:                   OpDecorate %_runtimearr_uint ArrayStride 4
-)" + kOutputDecorations + R"(
-;CHECK:                   OpDecorate %gl_FragCoord BuiltIn FragCoord
-                  %void = OpTypeVoid
-                     %3 = OpTypeFunction %void
-                 %float = OpTypeFloat 32
-               %v4float = OpTypeVector %float 4
-           %_ptr_Output_v4float = OpTypePointer Output %v4float
-                     %x = OpVariable %_ptr_Output_v4float Output
-                    %10 = OpTypeImage %float Buffer 0 0 0 1 Unknown
-           %_ptr_UniformConstant_10 = OpTypePointer UniformConstant %10
-                     %s = OpVariable %_ptr_UniformConstant_10 UniformConstant
-                   %int = OpTypeInt 32 1
-           %_ptr_Input_int = OpTypePointer Input %int
-                    %ii = OpVariable %_ptr_Input_int Input
-;CHECK:           %uint = OpTypeInt 32 0
-;CHECK:         %uint_0 = OpConstant %uint 0
-;CHECK:           %bool = OpTypeBool
-;CHECK:         %uint_6 = OpConstant %uint 6
-;CHECK:             %35 = OpTypeFunction %void %uint %uint %uint %uint %uint
-;CHECK:    %_runtimearr_uint = OpTypeRuntimeArray %uint
-)" + kOutputGlobals + R"(
-;CHECK:    %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-;CHECK:        %uint_11 = OpConstant %uint 11
-;CHECK:         %uint_4 = OpConstant %uint 4
-;CHECK:         %uint_1 = OpConstant %uint 1
-;CHECK:        %uint_23 = OpConstant %uint 23
-;CHECK:         %uint_2 = OpConstant %uint 2
-;CHECK:         %uint_3 = OpConstant %uint 3
-;CHECK:    %_ptr_Input_v4float = OpTypePointer Input %v4float
-;CHECK:    %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
-;CHECK:         %v4uint = OpTypeVector %uint 4
-;CHECK:         %uint_5 = OpConstant %uint 5
-;CHECK:         %uint_7 = OpConstant %uint 7
-;CHECK:         %uint_8 = OpConstant %uint 8
-;CHECK:         %uint_9 = OpConstant %uint 9
-;CHECK:        %uint_10 = OpConstant %uint 10
-;CHECK:        %uint_32 = OpConstant %uint 32
-;CHECK:             %94 = OpConstantNull %v4float
-                  %main = OpFunction %void None %3
-                     %5 = OpLabel
-;CHECK:                   OpBranch %21
-;CHECK:             %21 = OpLabel
-;CHECK:                   OpBranch %20
-;CHECK:             %20 = OpLabel
-;CHECK:                   OpBranch %19
-;CHECK:             %19 = OpLabel
-                    %13 = OpLoad %10 %s
-                    %17 = OpLoad %int %ii
-                    %18 = OpImageFetch %v4float %13 %17
-                          OpStore %x %18
-;CHECK-NOT:         %18 = OpImageFetch %v4float %13 %17
-;CHECK-NOT:               OpStore %x %18
-;CHECK:             %23 = OpBitcast %uint %17
-;CHECK:             %25 = OpImageQuerySize %uint %13
-;CHECK:             %27 = OpULessThan %bool %23 %25
-;CHECK:                   OpSelectionMerge %29 None
-;CHECK:                   OpBranchConditional %27 %30 %31
-;CHECK:             %30 = OpLabel
-;CHECK:             %32 = OpLoad %10 %s
-;CHECK:             %33 = OpImageFetch %v4float %32 %17
-;CHECK:                   OpBranch %29
-;CHECK:             %31 = OpLabel
-;CHECK:             %93 = OpFunctionCall %void %inst_bindless_stream_write_5 %uint_32 %uint_6 %uint_0 %23 %25
-;CHECK:                   OpBranch %29
-;CHECK:             %29 = OpLabel
-;CHECK:             %95 = OpPhi %v4float %33 %30 %94 %31
-;CHECK:                   OpStore %x %95
-                          OpReturn
-                          OpFunctionEnd
-                          )" + kStreamWrite5Frag;
+OpCapability Shader
+OpCapability SampledBuffer
+;CHECK: OpCapability Linkage
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %x %s %ii
+;CHECK: OpEntryPoint Fragment %main "main" %x %s %ii %gl_FragCoord
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 450
+OpName %main "main"
+OpName %x "x"
+OpName %s "s"
+OpName %ii "ii"
+OpDecorate %x Location 11
+OpDecorate %s DescriptorSet 3
+OpDecorate %s Binding 7
+OpDecorate %ii Flat
+OpDecorate %ii Location 13
+;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%x = OpVariable %_ptr_Output_v4float Output
+%10 = OpTypeImage %float Buffer 0 0 0 1 Unknown
+%_ptr_UniformConstant_10 = OpTypePointer UniformConstant %10
+%s = OpVariable %_ptr_UniformConstant_10 UniformConstant
+%int = OpTypeInt 32 1
+%_ptr_Input_int = OpTypePointer Input %int
+%ii = OpVariable %_ptr_Input_int Input
+;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
+;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
+;CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float
+%main = OpFunction %void None %3
+%5 = OpLabel
+;CHECK: OpBranch %19
+;CHECK: %19 = OpLabel
+%13 = OpLoad %10 %s
+%17 = OpLoad %int %ii
+%18 = OpImageFetch %v4float %13 %17
+OpStore %x %18
+;CHECK-NOT: %18 = OpImageFetch %v4float %13 %17
+;CHECK-NOT: OpStore %x %18
+;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord
+;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}}
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_33 {{%\w+}} %uint_3 %uint_7 %uint_0 {{%\w+}}
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpLoad %10 %s
+;CHECK: {{%\w+}} = OpImageFetch %v4float {{%\w+}} %17
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: [[phi_result:%\w+]] = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}}
+;CHECK: OpStore %x [[phi_result]]
+OpReturn
+OpFunctionEnd
+)";
   // clang-format on
 
-  SetTargetEnv(SPV_ENV_VULKAN_1_2);
+  SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
   SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 7u, 23u, false,
-                                               false, true, true, true);
+  SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 23u);
 }
 
 TEST_F(InstBindlessTest, SamplerBufferOOBFetch) {
@@ -5879,105 +4602,80 @@
 
   // clang-format off
   const std::string text = R"(
-                          OpCapability Shader
-                          OpCapability SampledBuffer
-;CHECK:                   OpCapability ImageQuery
-;CHECK:                   OpExtension "SPV_KHR_storage_buffer_storage_class"
-                     %1 = OpExtInstImport "GLSL.std.450"
-                          OpMemoryModel Logical GLSL450
-                          OpEntryPoint Fragment %main "main" %x %s %ii
-;CHECK:                   OpEntryPoint Fragment %main "main" %x %s %ii %inst_bindless_output_buffer %gl_FragCoord
-                          OpExecutionMode %main OriginUpperLeft
-                          OpSource GLSL 450
-                          OpName %main "main"
-                          OpName %x "x"
-                          OpName %s "s"
-                          OpName %ii "ii"
-                          OpDecorate %x Location 11
-                          OpDecorate %s DescriptorSet 3
-                          OpDecorate %s Binding 7
-                          OpDecorate %ii Flat
-                          OpDecorate %ii Location 13
-;CHECK:                   OpDecorate %_runtimearr_uint ArrayStride 4
-)" + kOutputDecorations + R"(
-;CHECK:                   OpDecorate %gl_FragCoord BuiltIn FragCoord
-                  %void = OpTypeVoid
-                     %3 = OpTypeFunction %void
-                 %float = OpTypeFloat 32
-               %v4float = OpTypeVector %float 4
-           %_ptr_Output_v4float = OpTypePointer Output %v4float
-                     %x = OpVariable %_ptr_Output_v4float Output
-                    %10 = OpTypeImage %float Buffer 0 0 0 1 Unknown
-                    %11 = OpTypeSampledImage %10
-           %_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11
-                     %s = OpVariable %_ptr_UniformConstant_11 UniformConstant
-                   %int = OpTypeInt 32 1
-           %_ptr_Input_int = OpTypePointer Input %int
-                    %ii = OpVariable %_ptr_Input_int Input
-;CHECK:           %uint = OpTypeInt 32 0
-;CHECK:         %uint_0 = OpConstant %uint 0
-;CHECK:           %bool = OpTypeBool
-;CHECK:         %uint_6 = OpConstant %uint 6
-;CHECK:             %38 = OpTypeFunction %void %uint %uint %uint %uint %uint
-;CHECK:    %_runtimearr_uint = OpTypeRuntimeArray %uint
-)" + kOutputGlobals + R"(
-;CHECK:    %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-;CHECK:        %uint_11 = OpConstant %uint 11
-;CHECK:         %uint_4 = OpConstant %uint 4
-;CHECK:         %uint_1 = OpConstant %uint 1
-;CHECK:        %uint_23 = OpConstant %uint 23
-;CHECK:         %uint_2 = OpConstant %uint 2
-;CHECK:         %uint_3 = OpConstant %uint 3
-;CHECK:    %_ptr_Input_v4float = OpTypePointer Input %v4float
-;CHECK:    %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
-;CHECK:         %v4uint = OpTypeVector %uint 4
-;CHECK:         %uint_5 = OpConstant %uint 5
-;CHECK:         %uint_7 = OpConstant %uint 7
-;CHECK:         %uint_8 = OpConstant %uint 8
-;CHECK:         %uint_9 = OpConstant %uint 9
-;CHECK:        %uint_10 = OpConstant %uint 10
-;CHECK:        %uint_34 = OpConstant %uint 34
-;CHECK:             %97 = OpConstantNull %v4float
-                  %main = OpFunction %void None %3
-                     %5 = OpLabel
-;CHECK:                   OpBranch %23
-;CHECK:             %23 = OpLabel
-;CHECK:                   OpBranch %22
-;CHECK:             %22 = OpLabel
-;CHECK:                   OpBranch %21
-;CHECK:             %21 = OpLabel
-                    %14 = OpLoad %11 %s
-                    %18 = OpLoad %int %ii
-                    %19 = OpImage %10 %14
-                    %20 = OpImageFetch %v4float %19 %18
-                          OpStore %x %20
-;CHECK-NOT:         %20 = OpImageFetch %v4float %19 %18
-;CHECK-NOT:               OpStore %x %20
-;CHECK:             %25 = OpBitcast %uint %18
-;CHECK:             %27 = OpImageQuerySize %uint %19
-;CHECK:             %29 = OpULessThan %bool %25 %27
-;CHECK:                   OpSelectionMerge %31 None
-;CHECK:                   OpBranchConditional %29 %32 %33
-;CHECK:             %32 = OpLabel
-;CHECK:             %34 = OpLoad %11 %s
-;CHECK:             %35 = OpImage %10 %34
-;CHECK:             %36 = OpImageFetch %v4float %35 %18
-;CHECK:                   OpBranch %31
-;CHECK:             %33 = OpLabel
-;CHECK:             %96 = OpFunctionCall %void %inst_bindless_stream_write_5 %uint_34 %uint_6 %uint_0 %25 %27
-;CHECK:                   OpBranch %31
-;CHECK:             %31 = OpLabel
-;CHECK:             %98 = OpPhi %v4float %36 %32 %97 %33
-;CHECK:                   OpStore %x %98
-                          OpReturn
-                          OpFunctionEnd
-                          )" + kStreamWrite5Frag;
+OpCapability Shader
+OpCapability SampledBuffer
+;CHECK: OpCapability Linkage
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %x %s %ii
+;CHECK: OpEntryPoint Fragment %main "main" %x %s %ii %gl_FragCoord
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 450
+OpName %main "main"
+OpName %x "x"
+OpName %s "s"
+OpName %ii "ii"
+OpDecorate %x Location 11
+OpDecorate %s DescriptorSet 3
+OpDecorate %s Binding 7
+OpDecorate %ii Flat
+OpDecorate %ii Location 13
+)" + kImportDeco + R"(
+;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%x = OpVariable %_ptr_Output_v4float Output
+%10 = OpTypeImage %float Buffer 0 0 0 1 Unknown
+%11 = OpTypeSampledImage %10
+%_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11
+%s = OpVariable %_ptr_UniformConstant_11 UniformConstant
+%int = OpTypeInt 32 1
+%_ptr_Input_int = OpTypePointer Input %int
+%ii = OpVariable %_ptr_Input_int Input
+;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
+;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
+;CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float
+)" + kImportStub + R"(
+%main = OpFunction %void None %3
+%5 = OpLabel
+;CHECK: OpBranch %21
+;CHECK: %21 = OpLabel
+%14 = OpLoad %11 %s
+%18 = OpLoad %int %ii
+%19 = OpImage %10 %14
+%20 = OpImageFetch %v4float %19 %18
+OpStore %x %20
+;CHECK-NOT: %20 = OpImageFetch %v4float %19 %18
+;CHECK-NOT: OpStore %x %20
+;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord
+;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}}
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_35 {{%\w+}} %uint_3 %uint_7 %uint_0 {{%\w+}}
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpLoad %11 %s
+;CHECK: {{%\w+}} = OpImage %10 {{%\w+}}
+;CHECK: {{%\w+}} = OpImageFetch %v4float {{%\w+}} {{%\w+}}
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: [[phi_result:%\w+]] = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}}
+;CHECK: OpStore %x [[phi_result]]
+OpReturn
+OpFunctionEnd
+)";
   // clang-format on
 
-  SetTargetEnv(SPV_ENV_VULKAN_1_2);
+  SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
   SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 7u, 23u, false,
-                                               false, true, true, true);
+  SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 23u);
 }
 
 TEST_F(InstBindlessTest, SamplerBufferConstructorOOBFetch) {
@@ -5995,114 +4693,356 @@
 
   // clang-format off
   const std::string text = R"(
-                          OpCapability Shader
-                          OpCapability SampledBuffer
-;CHECK:                   OpCapability ImageQuery
-;CHECK:                   OpExtension "SPV_KHR_storage_buffer_storage_class"
-                     %1 = OpExtInstImport "GLSL.std.450"
-                          OpMemoryModel Logical GLSL450
-                          OpEntryPoint Fragment %main "main" %x %tBuf %s %ii
-;CHECK:                   OpEntryPoint Fragment %main "main" %x %tBuf %s %ii %inst_bindless_output_buffer %gl_FragCoord
-                          OpExecutionMode %main OriginUpperLeft
-                          OpSource GLSL 450
-                          OpName %main "main"
-                          OpName %x "x"
-                          OpName %tBuf "tBuf"
-                          OpName %s "s"
-                          OpName %ii "ii"
-                          OpDecorate %x Location 11
-                          OpDecorate %tBuf DescriptorSet 3
-                          OpDecorate %tBuf Binding 7
-                          OpDecorate %s DescriptorSet 3
-                          OpDecorate %s Binding 8
-                          OpDecorate %ii Flat
-                          OpDecorate %ii Location 13
-;CHECK:                   OpDecorate %_runtimearr_uint ArrayStride 4
-)" + kOutputDecorations + R"(
-;CHECK:                   OpDecorate %gl_FragCoord BuiltIn FragCoord
-                  %void = OpTypeVoid
-                     %3 = OpTypeFunction %void
-                 %float = OpTypeFloat 32
-               %v4float = OpTypeVector %float 4
-           %_ptr_Output_v4float = OpTypePointer Output %v4float
-                     %x = OpVariable %_ptr_Output_v4float Output
-                    %10 = OpTypeImage %float Buffer 0 0 0 1 Unknown
-           %_ptr_UniformConstant_10 = OpTypePointer UniformConstant %10
-                  %tBuf = OpVariable %_ptr_UniformConstant_10 UniformConstant
-                    %14 = OpTypeSampler
-           %_ptr_UniformConstant_14 = OpTypePointer UniformConstant %14
-                     %s = OpVariable %_ptr_UniformConstant_14 UniformConstant
-                    %18 = OpTypeSampledImage %10
-                   %int = OpTypeInt 32 1
-           %_ptr_Input_int = OpTypePointer Input %int
-                    %ii = OpVariable %_ptr_Input_int Input
-;CHECK:           %uint = OpTypeInt 32 0
-;CHECK:         %uint_0 = OpConstant %uint 0
-;CHECK:           %bool = OpTypeBool
-;CHECK:         %uint_6 = OpConstant %uint 6
-;CHECK:             %44 = OpTypeFunction %void %uint %uint %uint %uint %uint
-;CHECK:    %_runtimearr_uint = OpTypeRuntimeArray %uint
-)" + kOutputGlobals + R"(
-;CHECK:    %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-;CHECK:        %uint_11 = OpConstant %uint 11
-;CHECK:         %uint_4 = OpConstant %uint 4
-;CHECK:         %uint_1 = OpConstant %uint 1
-;CHECK:        %uint_23 = OpConstant %uint 23
-;CHECK:         %uint_2 = OpConstant %uint 2
-;CHECK:         %uint_3 = OpConstant %uint 3
-;CHECK:    %_ptr_Input_v4float = OpTypePointer Input %v4float
-;CHECK:    %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
-;CHECK:         %v4uint = OpTypeVector %uint 4
-;CHECK:         %uint_5 = OpConstant %uint 5
-;CHECK:         %uint_7 = OpConstant %uint 7
-;CHECK:         %uint_8 = OpConstant %uint 8
-;CHECK:         %uint_9 = OpConstant %uint 9
-;CHECK:        %uint_10 = OpConstant %uint 10
-;CHECK:        %uint_42 = OpConstant %uint 42
-;CHECK:            %103 = OpConstantNull %v4float
-                  %main = OpFunction %void None %3
-                     %5 = OpLabel
-;CHECK:                   OpBranch %28
-;CHECK:             %28 = OpLabel
-;CHECK:                   OpBranch %27
-;CHECK:             %27 = OpLabel
-;CHECK:                   OpBranch %26
-;CHECK:             %26 = OpLabel
-                    %13 = OpLoad %10 %tBuf
-                    %17 = OpLoad %14 %s
-                    %19 = OpSampledImage %18 %13 %17
-                    %23 = OpLoad %int %ii
-                    %24 = OpImage %10 %19
-                    %25 = OpImageFetch %v4float %24 %23
-                          OpStore %x %25
-;CHECK-NOT:         %25 = OpImageFetch %v4float %24 %23
-;CHECK-NOT:               OpStore %x %25
-;CHECK:             %30 = OpBitcast %uint %23
-;CHECK:             %32 = OpImageQuerySize %uint %24
-;CHECK:             %34 = OpULessThan %bool %30 %32
-;CHECK:                   OpSelectionMerge %36 None
-;CHECK:                   OpBranchConditional %34 %37 %38
-;CHECK:             %37 = OpLabel
-;CHECK:             %39 = OpLoad %10 %tBuf
-;CHECK:             %40 = OpSampledImage %18 %39 %17
-;CHECK:             %41 = OpImage %10 %40
-;CHECK:             %42 = OpImageFetch %v4float %41 %23
-;CHECK:                   OpBranch %36
-;CHECK:             %38 = OpLabel
-;CHECK:            %102 = OpFunctionCall %void %inst_bindless_stream_write_5 %uint_42 %uint_6 %uint_0 %30 %32
-;CHECK:                   OpBranch %36
-;CHECK:             %36 = OpLabel
-;CHECK:            %104 = OpPhi %v4float %42 %37 %103 %38
-;CHECK:                   OpStore %x %104
-                          OpReturn
-                          OpFunctionEnd
-                          )" + kStreamWrite5Frag;
+OpCapability Shader
+OpCapability SampledBuffer
+;CHECK: OpCapability Linkage
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %x %tBuf %s %ii
+;CHECK: OpEntryPoint Fragment %main "main" %x %tBuf %s %ii %gl_FragCoord
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 450
+OpName %main "main"
+OpName %x "x"
+OpName %tBuf "tBuf"
+OpName %s "s"
+OpName %ii "ii"
+OpDecorate %x Location 11
+OpDecorate %tBuf DescriptorSet 3
+OpDecorate %tBuf Binding 7
+OpDecorate %s DescriptorSet 3
+OpDecorate %s Binding 8
+OpDecorate %ii Flat
+OpDecorate %ii Location 13
+)" + kImportDeco + R"(
+;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%x = OpVariable %_ptr_Output_v4float Output
+%10 = OpTypeImage %float Buffer 0 0 0 1 Unknown
+%_ptr_UniformConstant_10 = OpTypePointer UniformConstant %10
+%tBuf = OpVariable %_ptr_UniformConstant_10 UniformConstant
+%14 = OpTypeSampler
+%_ptr_UniformConstant_14 = OpTypePointer UniformConstant %14
+%s = OpVariable %_ptr_UniformConstant_14 UniformConstant
+%18 = OpTypeSampledImage %10
+%int = OpTypeInt 32 1
+%_ptr_Input_int = OpTypePointer Input %int
+%ii = OpVariable %_ptr_Input_int Input
+;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
+;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
+;CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float
+%main = OpFunction %void None %3
+%5 = OpLabel
+%13 = OpLoad %10 %tBuf
+%17 = OpLoad %14 %s
+%19 = OpSampledImage %18 %13 %17
+%23 = OpLoad %int %ii
+%24 = OpImage %10 %19
+%25 = OpImageFetch %v4float %24 %23
+OpStore %x %25
+;CHECK-NOT: %25 = OpImageFetch %v4float %24 %23
+;CHECK-NOT: OpStore %x %25
+;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord
+;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}}
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_43 {{%\w+}} %uint_3 %uint_7 %uint_0 {{%\w+}}
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpLoad %10 %tBuf
+;CHECK: {{%\w+}} = OpSampledImage %18 {{%\w+}} %17
+;CHECK: {{%\w+}} = OpImage %10 {{%\w+}}
+;CHECK: {{%\w+}} = OpImageFetch %v4float {{%\w+}} %23
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: [[phi_result:%\w+]] = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}}
+;CHECK: OpStore %x [[phi_result]]
+OpReturn
+OpFunctionEnd
+)";
   // clang-format on
 
-  SetTargetEnv(SPV_ENV_VULKAN_1_2);
+  SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
   SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 7u, 23u, false,
-                                               false, true, true, true);
+  SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 23u);
+}
+
+TEST_F(InstBindlessTest, DeviceBufferAddressOOB) {
+  // #version 450
+  // #extension GL_EXT_buffer_reference : enable
+  //  layout(buffer_reference, buffer_reference_align = 16) buffer bufStruct;
+  // layout(set = 0, binding = 0) uniform ufoo {
+  //     bufStruct data;
+  //     int nWrites;
+  // } u_info;
+  // layout(buffer_reference, std140) buffer bufStruct {
+  //     int a[4];
+  // };
+  // void main() {
+  //     for (int i=0; i < u_info.nWrites; ++i) {
+  //         u_info.data.a[i] = 0xdeadca71;
+  //     }
+  // }
+
+  // clang-format off
+  const std::string text = R"(
+OpCapability Shader
+OpCapability PhysicalStorageBufferAddresses
+;CHECK: OpCapability Linkage
+;CHECK: OpCapability Int64
+OpExtension "SPV_KHR_physical_storage_buffer"
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
+OpEntryPoint Vertex %main "main" %u_info
+;CHECK: OpEntryPoint Vertex %main "main" %u_info %gl_VertexIndex %gl_InstanceIndex
+OpSource GLSL 450
+OpSourceExtension "GL_EXT_buffer_reference"
+OpName %main "main"
+OpName %i "i"
+OpName %ufoo "ufoo"
+OpMemberName %ufoo 0 "data"
+OpMemberName %ufoo 1 "nWrites"
+OpName %bufStruct "bufStruct"
+OpMemberName %bufStruct 0 "a"
+OpName %u_info "u_info"
+OpMemberDecorate %ufoo 0 Offset 0
+OpMemberDecorate %ufoo 1 Offset 8
+OpDecorate %ufoo Block
+OpDecorate %_arr_int_uint_4 ArrayStride 16
+OpMemberDecorate %bufStruct 0 Offset 0
+OpDecorate %bufStruct Block
+OpDecorate %u_info DescriptorSet 0
+OpDecorate %u_info Binding 0
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%int = OpTypeInt 32 1
+%_ptr_Function_int = OpTypePointer Function %int
+%int_0 = OpConstant %int 0
+OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_bufStruct PhysicalStorageBuffer
+%ufoo = OpTypeStruct %_ptr_PhysicalStorageBuffer_bufStruct %int
+%uint = OpTypeInt 32 0
+%uint_4 = OpConstant %uint 4
+%_arr_int_uint_4 = OpTypeArray %int %uint_4
+%bufStruct = OpTypeStruct %_arr_int_uint_4
+%_ptr_PhysicalStorageBuffer_bufStruct = OpTypePointer PhysicalStorageBuffer %bufStruct
+%_ptr_Uniform_ufoo = OpTypePointer Uniform %ufoo
+%u_info = OpVariable %_ptr_Uniform_ufoo Uniform
+%int_1 = OpConstant %int 1
+%_ptr_Uniform_int = OpTypePointer Uniform %int
+%bool = OpTypeBool
+%_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct = OpTypePointer Uniform %_ptr_PhysicalStorageBuffer_bufStruct
+%int_n559035791 = OpConstant %int -559035791
+%_ptr_PhysicalStorageBuffer_int = OpTypePointer PhysicalStorageBuffer %int
+)" + kImportStub + R"(
+%main = OpFunction %void None %3
+%5 = OpLabel
+%i = OpVariable %_ptr_Function_int Function
+OpStore %i %int_0
+OpBranch %10
+%10 = OpLabel
+OpLoopMerge %12 %13 None
+OpBranch %14
+%14 = OpLabel
+%15 = OpLoad %int %i
+%26 = OpAccessChain %_ptr_Uniform_int %u_info %int_1
+;CHECK: {{%\w+}} = OpIAdd %uint %uint_8 %uint_3
+;CHECK: {{%\w+}} = OpLoad %uint %gl_VertexIndex
+;CHECK: {{%\w+}} = OpLoad %uint %gl_InstanceIndex
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_0 {{%\w+}} {{%\w+}} %uint_0
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_56 {{%\w+}} %uint_0 %uint_0 %uint_0 {{%\w+}}
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: [[load_result:%\w+]] = OpLoad %int %26
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: [[phi_result:%\w+]] = OpPhi %int [[load_result]] {{%\w+}} {{%\w+}} {{%\w+}}
+%27 = OpLoad %int %26
+%29 = OpSLessThan %bool %15 %27
+;CHECK-NOT: %27 = OpLoad %int %26
+;CHECK-NOT: %29 = OpSLessThan %bool %15 %27
+;CHECK: %29 = OpSLessThan %bool %15 [[phi_result]]
+OpBranchConditional %29 %11 %12
+%11 = OpLabel
+%31 = OpAccessChain %_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct %u_info %int_0
+%32 = OpLoad %_ptr_PhysicalStorageBuffer_bufStruct %31
+;CHECK-NOT: %32 = OpLoad %_ptr_PhysicalStorageBuffer_bufStruct %31
+;CHECK: {{%\w+}} = OpIAdd %uint %uint_0 %uint_7
+;CHECK: {{%\w+}} = OpLoad %uint %gl_VertexIndex
+;CHECK: {{%\w+}} = OpLoad %uint %gl_InstanceIndex
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_0 {{%\w+}} {{%\w+}} %uint_0
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_61 {{%\w+}} %uint_0 %uint_0 %uint_0 {{%\w+}}
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: [[load_result_2:%\w+]] = OpLoad %_ptr_PhysicalStorageBuffer_bufStruct %31
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpConvertUToPtr %_ptr_PhysicalStorageBuffer_bufStruct {{%\w+}}
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: [[phi_result_2:%\w+]] = OpPhi %_ptr_PhysicalStorageBuffer_bufStruct [[load_result_2]] {{%\w+}} {{%\w+}} {{%\w+}}
+%33 = OpLoad %int %i
+%36 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %32 %int_0 %33
+;CHECK-NOT: %36 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %32 %int_0 %33
+;CHECK: %36 = OpAccessChain %_ptr_PhysicalStorageBuffer_int [[phi_result_2]] %int_0 %33
+OpStore %36 %int_n559035791 Aligned 16
+OpBranch %13
+%13 = OpLabel
+%37 = OpLoad %int %i
+%38 = OpIAdd %int %37 %int_1
+OpStore %i %38
+OpBranch %10
+%12 = OpLabel
+OpReturn
+OpFunctionEnd)";
+  // clang-format on
+
+  SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 23u);
+}
+
+TEST_F(InstBindlessTest, VertexIndexOOB) {
+  // #version 450
+  // layout(std140, binding = 0) uniform foo { uint tex_index[1]; }
+  // uniform_index_buffer; layout(location = 0) out flat uint index; vec2
+  // vertices[3]; void main() {
+  //     vertices[0] = vec2(-1.0, -1.0);
+  //     vertices[1] = vec2( 1.0, -1.0);
+  //     vertices[2] = vec2( 0.0,  1.0);
+  //     gl_Position = vec4(vertices[gl_VertexIndex % 3], 0.0, 1.0);
+  //     index = uniform_index_buffer.tex_index[0];
+  // }
+  // clang-format off
+  const std::string text = R"(
+OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %main "main" %vertices %_ %gl_VertexIndex %index %uniform_index_buffer
+OpSource GLSL 450
+OpName %main "main"
+OpName %vertices "vertices"
+OpName %gl_PerVertex "gl_PerVertex"
+OpMemberName %gl_PerVertex 0 "gl_Position"
+OpMemberName %gl_PerVertex 1 "gl_PointSize"
+OpMemberName %gl_PerVertex 2 "gl_ClipDistance"
+OpMemberName %gl_PerVertex 3 "gl_CullDistance"
+OpName %_ ""
+OpName %gl_VertexIndex "gl_VertexIndex"
+OpName %index "index"
+OpName %foo "foo"
+OpMemberName %foo 0 "tex_index"
+OpName %uniform_index_buffer "uniform_index_buffer"
+OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
+OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize
+OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance
+OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance
+OpDecorate %gl_PerVertex Block
+OpDecorate %gl_VertexIndex BuiltIn VertexIndex
+OpDecorate %index Flat
+OpDecorate %index Location 0
+OpDecorate %_arr_uint_uint_1 ArrayStride 16
+OpMemberDecorate %foo 0 Offset 0
+OpDecorate %foo Block
+OpDecorate %uniform_index_buffer DescriptorSet 0
+OpDecorate %uniform_index_buffer Binding 0
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v2float = OpTypeVector %float 2
+%uint = OpTypeInt 32 0
+%uint_3 = OpConstant %uint 3
+%_arr_v2float_uint_3 = OpTypeArray %v2float %uint_3
+%_ptr_Private__arr_v2float_uint_3 = OpTypePointer Private %_arr_v2float_uint_3
+%vertices = OpVariable %_ptr_Private__arr_v2float_uint_3 Private
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%float_n1 = OpConstant %float -1
+%16 = OpConstantComposite %v2float %float_n1 %float_n1
+%_ptr_Private_v2float = OpTypePointer Private %v2float
+%int_1 = OpConstant %int 1
+%float_1 = OpConstant %float 1
+%21 = OpConstantComposite %v2float %float_1 %float_n1
+%int_2 = OpConstant %int 2
+%float_0 = OpConstant %float 0
+%25 = OpConstantComposite %v2float %float_0 %float_1
+%v4float = OpTypeVector %float 4
+%uint_1 = OpConstant %uint 1
+%_arr_float_uint_1 = OpTypeArray %float %uint_1
+%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1
+%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex
+%_ = OpVariable %_ptr_Output_gl_PerVertex Output
+%_ptr_Input_int = OpTypePointer Input %int
+%gl_VertexIndex = OpVariable %_ptr_Input_int Input
+%int_3 = OpConstant %int 3
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%_ptr_Output_uint = OpTypePointer Output %uint
+%index = OpVariable %_ptr_Output_uint Output
+%_arr_uint_uint_1 = OpTypeArray %uint %uint_1
+%foo = OpTypeStruct %_arr_uint_uint_1
+%_ptr_Uniform_foo = OpTypePointer Uniform %foo
+%uniform_index_buffer = OpVariable %_ptr_Uniform_foo Uniform
+%_ptr_Uniform_uint = OpTypePointer Uniform %uint
+)" + kImportStub + R"(
+%main = OpFunction %void None %3
+%5 = OpLabel
+%18 = OpAccessChain %_ptr_Private_v2float %vertices %int_0
+OpStore %18 %16
+%22 = OpAccessChain %_ptr_Private_v2float %vertices %int_1
+OpStore %22 %21
+%26 = OpAccessChain %_ptr_Private_v2float %vertices %int_2
+OpStore %26 %25
+%35 = OpLoad %int %gl_VertexIndex
+%37 = OpSMod %int %35 %int_3
+%38 = OpAccessChain %_ptr_Private_v2float %vertices %37
+%39 = OpLoad %v2float %38
+%40 = OpCompositeExtract %float %39 0
+%41 = OpCompositeExtract %float %39 1
+%42 = OpCompositeConstruct %v4float %40 %41 %float_0 %float_1
+%44 = OpAccessChain %_ptr_Output_v4float %_ %int_0
+OpStore %44 %42
+%52 = OpAccessChain %_ptr_Uniform_uint %uniform_index_buffer %int_0 %int_0
+%53 = OpLoad %uint %52
+;CHECK-NOT: %53 = OpLoad %uint %52
+;CHECK: {{%\w+}} = OpIMul %uint %uint_16 %int_0
+;CHECK: {{%\w+}} = OpIAdd %uint %uint_0 {{%\w+}}
+;CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_3
+;CHECK: {{%\w+}} = OpLoad %int %gl_VertexIndex
+;CHECK: {{%\w+}} = OpBitcast %uint {{%\w+}}
+;CHECK: {{%\w+}} = OpLoad %uint %gl_InstanceIndex
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_0 {{%\w+}} {{%\w+}} %uint_0
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_87 {{%\w+}} %uint_0 %uint_0 %uint_0 {{%\w+}}
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpLoad %uint %52
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: [[phi_result:%\w+]] = OpPhi %uint {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: OpStore %index [[phi_result]]
+OpStore %index %53
+;CHECK-NOT: OpStore %index %53
+OpReturn
+;CHECK: OpReturn
+OpFunctionEnd)";
+  // clang-format on
+
+  SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 23u);
 }
 
 // TODO(greg-lunarg): Add tests to verify handling of these cases:
diff --git a/test/opt/inst_buff_addr_check_test.cpp b/test/opt/inst_buff_addr_check_test.cpp
index e095eb7..72d3438 100644
--- a/test/opt/inst_buff_addr_check_test.cpp
+++ b/test/opt/inst_buff_addr_check_test.cpp
@@ -19,7 +19,6 @@
 #include <string>
 #include <vector>
 
-#include "test/opt/assembly_builder.h"
 #include "test/opt/pass_fixture.h"
 #include "test/opt/pass_utils.h"
 
@@ -27,145 +26,14 @@
 namespace opt {
 namespace {
 
-static const std::string kOutputDecorations = R"(
-; CHECK: OpDecorate [[output_buffer_type:%inst_buff_addr_OutputBuffer]] Block
-; CHECK: OpMemberDecorate [[output_buffer_type]] 0 Offset 0
-; CHECK: OpMemberDecorate [[output_buffer_type]] 1 Offset 4
-; CHECK: OpDecorate [[output_buffer_var:%\w+]] DescriptorSet 7
-; CHECK: OpDecorate [[output_buffer_var]] Binding 0
+static const std::string kFuncName = "inst_buff_addr_search_and_test";
+static const std::string kImportDeco = R"(
+;CHECK: OpDecorate %)" + kFuncName + R"( LinkageAttributes ")" +
+                                       kFuncName + R"(" Import
 )";
-
-static const std::string kOutputGlobals = R"(
-; CHECK: [[output_buffer_type]] = OpTypeStruct %uint %_runtimearr_uint
-; CHECK: [[output_ptr_type:%\w+]] = OpTypePointer StorageBuffer [[output_buffer_type]]
-; CHECK: [[output_buffer_var]] = OpVariable [[output_ptr_type]] StorageBuffer
-)";
-
-static const std::string kStreamWrite4Begin = R"(
-; CHECK: {{%\w+}} = OpFunction %void None {{%\w+}}
-; CHECK: [[param_1:%\w+]] = OpFunctionParameter %uint
-; CHECK: [[param_2:%\w+]] = OpFunctionParameter %uint
-; CHECK: [[param_3:%\w+]] = OpFunctionParameter %uint
-; CHECK: [[param_4:%\w+]] = OpFunctionParameter %uint
-; CHECK: {{%\w+}} = OpLabel
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_0
-; CHECK: {{%\w+}} = OpAtomicIAdd %uint {{%\w+}} %uint_4 %uint_0 %uint_10
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_10
-; CHECK: {{%\w+}} = OpArrayLength %uint [[output_buffer_var]] 1
-; CHECK: {{%\w+}} = OpULessThanEqual %bool {{%\w+}} {{%\w+}}
-; CHECK: OpSelectionMerge {{%\w+}} None
-; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
-; CHECK: {{%\w+}} = OpLabel
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_0
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
-; CHECK: OpStore {{%\w+}} %uint_10
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_1
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
-; CHECK: OpStore {{%\w+}} %uint_23
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_2
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
-; CHECK: OpStore {{%\w+}} [[param_1]]
-)";
-
-static const std::string kStreamWrite4End = R"(
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_7
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
-; CHECK: OpStore {{%\w+}} [[param_2]]
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_8
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
-; CHECK: OpStore {{%\w+}} [[param_3]]
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_9
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
-; CHECK: OpStore {{%\w+}} [[param_4]]
-; CHECK: OpBranch {{%\w+}}
-; CHECK: {{%\w+}} = OpLabel
-; CHECK: OpReturn
-; CHECK: OpFunctionEnd
-)";
-
-// clang-format off
-static const std::string kStreamWrite4Frag = kStreamWrite4Begin + R"(
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_3
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
-; CHECK: OpStore {{%\w+}} %uint_4
-; CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord
-; CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}}
-; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_4
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
-; CHECK: OpStore {{%\w+}} {{%\w+}}
-; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_5
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
-; CHECK: OpStore {{%\w+}} {{%\w+}}
-)" + kStreamWrite4End;
-
-static const std::string kStreamWrite4Compute = kStreamWrite4Begin + R"(
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_3
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
-; CHECK: OpStore {{%\w+}} %uint_5
-; CHECK: {{%\w+}} = OpLoad %v3uint %gl_GlobalInvocationID
-; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
-; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
-; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_4
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
-; CHECK: OpStore {{%\w+}} {{%\w+}}
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_5
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
-; CHECK: OpStore {{%\w+}} {{%\w+}}
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_6
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
-; CHECK: OpStore {{%\w+}} {{%\w+}}
-)" + kStreamWrite4End;
-// clang-format on
-
-static const std::string kInputDecorations = R"(
-; CHECK: OpDecorate [[input_buffer_type:%inst_buff_addr_InputBuffer]] Block
-; CHECK: OpMemberDecorate [[input_buffer_type]] 0 Offset 0
-; CHECK: OpDecorate [[input_buffer_var:%\w+]] DescriptorSet 7
-; CHECK: OpDecorate [[input_buffer_var]] Binding 2
-)";
-
-static const std::string kInputGlobals = R"(
-; CHECK: [[input_buffer_type]] = OpTypeStruct %_runtimearr_ulong
-; CHECK: [[input_ptr_type:%\w+]] = OpTypePointer StorageBuffer [[input_buffer_type]]
-; CHECK: [[input_buffer_var]] = OpVariable [[input_ptr_type]] StorageBuffer
-)";
-
-static const std::string kSearchAndTest = R"(
-; CHECK: {{%\w+}} = OpFunction %bool None {{%\w+}}
-; CHECK: [[param_1:%\w+]] = OpFunctionParameter %ulong
-; CHECK: [[param_2:%\w+]] = OpFunctionParameter %uint
-; CHECK: {{%\w+}} = OpLabel
-; CHECK: OpBranch {{%\w+}}
-; CHECK: {{%\w+}} = OpLabel
-; CHECK: {{%\w+}} = OpPhi %uint %uint_1 {{%\w+}} {{%\w+}} {{%\w+}}
-; CHECK: OpLoopMerge {{%\w+}} {{%\w+}} None
-; CHECK: OpBranch {{%\w+}}
-; CHECK: {{%\w+}} = OpLabel
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_1
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_ulong [[input_buffer_var]] %uint_0 {{%\w+}}
-; CHECK: {{%\w+}} = OpLoad %ulong {{%\w+}}
-; CHECK: {{%\w+}} = OpUGreaterThan %bool {{%\w+}} [[param_1]]
-; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
-; CHECK: {{%\w+}} = OpLabel
-; CHECK: {{%\w+}} = OpISub %uint {{%\w+}} %uint_1
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_ulong [[input_buffer_var]] %uint_0 {{%\w+}}
-; CHECK: {{%\w+}} = OpLoad %ulong {{%\w+}}
-; CHECK: {{%\w+}} = OpISub %ulong [[param_1]] {{%\w+}}
-; CHECK: {{%\w+}} = OpUConvert %ulong [[param_2]]
-; CHECK: {{%\w+}} = OpIAdd %ulong {{%\w+}} {{%\w+}}
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_ulong [[input_buffer_var]] %uint_0 %uint_0
-; CHECK: {{%\w+}} = OpLoad %ulong {{%\w+}}
-; CHECK: {{%\w+}} = OpUConvert %uint {{%\w+}}
-; CHECK: {{%\w+}} = OpISub %uint {{%\w+}} %uint_1
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} {{%\w+}}
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_ulong [[input_buffer_var]] %uint_0 {{%\w+}}
-; CHECK: {{%\w+}} = OpLoad %ulong {{%\w+}}
-; CHECK: {{%\w+}} = OpULessThanEqual %bool {{%\w+}} {{%\w+}}
-; CHECK: OpReturnValue {{%\w+}}
-; CHECK: OpFunctionEnd
+static const std::string kImportStub = R"(
+;CHECK: %)" + kFuncName + R"( = OpFunction %bool None {{%\w+}}
+;CHECK: OpFunctionEnd
 )";
 // clang-format on
 
@@ -194,13 +62,13 @@
   const std::string defs = R"(
 OpCapability Shader
 OpCapability PhysicalStorageBufferAddresses
-; CHECK: OpCapability Int64
+;CHECK: OpCapability Int64
 OpExtension "SPV_EXT_physical_storage_buffer"
-; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
+;CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
 %1 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel PhysicalStorageBuffer64 GLSL450
 OpEntryPoint GLCompute %main "main"
-; CHECK: OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID
+;CHECK: OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID
 OpExecutionMode %main LocalSize 1 1 1
 OpSource GLSL 450
 OpSourceExtension "GL_EXT_buffer_reference"
@@ -225,11 +93,8 @@
 OpDecorate %bufStruct Block
 OpDecorate %u_info DescriptorSet 0
 OpDecorate %u_info Binding 0
-; CHECK: OpDecorate %_runtimearr_ulong ArrayStride 8
-)" + kInputDecorations + R"(
-; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
-)" + kOutputDecorations + R"(
-; CHECK: OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId
+)" + kImportDeco + R"(
+;CHECK: OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId
 )";
 
   const std::string globals = R"(
@@ -250,32 +115,11 @@
 %int_1 = OpConstant %int 1
 %int_3239 = OpConstant %int 3239
 %_ptr_PhysicalStorageBuffer_int = OpTypePointer PhysicalStorageBuffer %int
-; CHECK: %ulong = OpTypeInt 64 0
-; CHECK: %uint_4 = OpConstant %uint 4
-; CHECK: %bool = OpTypeBool
-; CHECK: %28 = OpTypeFunction %bool %ulong %uint
-; CHECK: %uint_1 = OpConstant %uint 1
-; CHECK: %_runtimearr_ulong = OpTypeRuntimeArray %ulong
-)" + kInputGlobals + R"(
-; CHECK: %_ptr_StorageBuffer_ulong = OpTypePointer StorageBuffer %ulong
-; CHECK: %uint_0 = OpConstant %uint 0
-; CHECK: %uint_32 = OpConstant %uint 32
-; CHECK: %70 = OpTypeFunction %void %uint %uint %uint %uint
-; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
-)" + kOutputGlobals + R"(
-; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-; CHECK: %uint_10 = OpConstant %uint 10
-; CHECK: %uint_23 = OpConstant %uint 23
-; CHECK: %uint_5 = OpConstant %uint 5
-; CHECK: %uint_3 = OpConstant %uint 3
-; CHECK: %v3uint = OpTypeVector %uint 3
-; CHECK: %_ptr_Input_v3uint = OpTypePointer Input %v3uint
-; CHECK: %gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input
-; CHECK: %uint_6 = OpConstant %uint 6
-; CHECK: %uint_7 = OpConstant %uint 7
-; CHECK: %uint_8 = OpConstant %uint 8
-; CHECK: %uint_9 = OpConstant %uint 9
-; CHECK: %uint_48 = OpConstant %uint 48
+;CHECK: %ulong = OpTypeInt 64 0
+;CHECK: %bool = OpTypeBool
+;CHECK: %v3uint = OpTypeVector %uint 3
+;CHECK: %_ptr_Input_v3uint = OpTypePointer Input %v3uint
+;CHECK: %gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input
 )";
 // clang-format off
 
@@ -285,36 +129,35 @@
 %17 = OpAccessChain %_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct %u_info %int_0
 %18 = OpLoad %_ptr_PhysicalStorageBuffer_bufStruct %17
 %22 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %18 %int_1
-; CHECK-NOT: %17 = OpAccessChain %_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct %u_info %int_0
-; CHECK-NOT: %18 = OpLoad %_ptr_PhysicalStorageBuffer_bufStruct %17
-; CHECK-NOT: %22 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %18 %int_1
-; CHECK: %20 = OpAccessChain %_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct %u_info %int_0
-; CHECK: %21 = OpLoad %_ptr_PhysicalStorageBuffer_bufStruct %20
-; CHECK: %22 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %21 %int_1
-; CHECK: %24 = OpConvertPtrToU %ulong %22
-; CHECK: %61 = OpFunctionCall %bool %inst_buff_addr_search_and_test %24 %uint_4
-; CHECK: OpSelectionMerge %62 None
-; CHECK: OpBranchConditional %61 %63 %64
-; CHECK: %63 = OpLabel
+;CHECK-NOT: %17 = OpAccessChain %_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct %u_info %int_0
+;CHECK-NOT: %18 = OpLoad %_ptr_PhysicalStorageBuffer_bufStruct %17
+;CHECK-NOT: %22 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %18 %int_1
+;CHECK: %20 = OpAccessChain %_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct %u_info %int_0
+;CHECK: %21 = OpLoad %_ptr_PhysicalStorageBuffer_bufStruct %20
+;CHECK: %22 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %21 %int_1
+;CHECK: {{%\w+}} = OpConvertPtrToU %ulong %22
+;CHECK: {{%\w+}} = OpLoad %v3uint %gl_GlobalInvocationID
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5 {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_49 {{%\w+}} {{%\w+}} %uint_4
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
 OpStore %22 %int_3239 Aligned 16
-; CHECK: OpStore %22 %int_3239 Aligned 16
-; CHECK: OpBranch %62
-; CHECK: %64 = OpLabel
-; CHECK: %65 = OpUConvert %uint %24
-; CHECK: %67 = OpShiftRightLogical %ulong %24 %uint_32
-; CHECK: %68 = OpUConvert %uint %67
-; CHECK: %124 = OpFunctionCall %void %inst_buff_addr_stream_write_4 %uint_48 %uint_2 %65 %68
-; CHECK: OpBranch %62
-; CHECK: %62 = OpLabel
+;CHECK: OpStore %22 %int_3239 Aligned 16
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
 OpReturn
 OpFunctionEnd
 )";
 
-  const std::string output_funcs = kSearchAndTest + kStreamWrite4Compute;
-
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   SinglePassRunAndMatch<InstBuffAddrCheckPass>(
-      defs + decorates + globals + main_func + output_funcs, true, 7u, 23u);
+      defs + decorates + globals + kImportStub + main_func, true, 23u);
 }
 
 TEST_F(InstBuffAddrTest, InstPhysicalStorageBufferLoadAndStore) {
@@ -344,7 +187,7 @@
   const std::string defs = R"(
 OpCapability Shader
 OpCapability PhysicalStorageBufferAddresses
-; CHECK: OpCapability Int64
+;CHECK: OpCapability Int64
 OpExtension "SPV_EXT_physical_storage_buffer"
 OpExtension "SPV_KHR_storage_buffer_storage_class"
 %1 = OpExtInstImport "GLSL.std.450"
@@ -354,7 +197,7 @@
 OpSource GLSL 450
 OpSourceExtension "GL_EXT_buffer_reference"
 OpName %main "main"
-; CHECK: OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID
+;CHECK: OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID
 OpName %blockType "blockType"
 OpMemberName %blockType 0 "x"
 OpMemberName %blockType 1 "next"
@@ -372,13 +215,9 @@
 OpDecorate %rootBlock Block
 OpDecorate %r DescriptorSet 0
 OpDecorate %r Binding 0
-; CHECK: OpDecorate %_runtimearr_ulong ArrayStride 8
-)" + kInputDecorations + R"(
-; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
-)" + kOutputDecorations + R"(
-; CHECK: OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId
+)" + kImportDeco + R"(
+;CHECK: OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId
 )";
-  // clang-format on
 
   const std::string globals = R"(
 %void = OpTypeVoid
@@ -396,7 +235,7 @@
 %_ptr_PhysicalStorageBuffer__ptr_PhysicalStorageBuffer_blockType = OpTypePointer PhysicalStorageBuffer %_ptr_PhysicalStorageBuffer_blockType
 %int_531 = OpConstant %int 531
 %_ptr_PhysicalStorageBuffer_int = OpTypePointer PhysicalStorageBuffer %int
-)" + kInputGlobals + kOutputGlobals;
+)";
 
   const std::string main_func = R"(
 %main = OpFunction %void None %3
@@ -407,48 +246,49 @@
 %22 = OpLoad %_ptr_PhysicalStorageBuffer_blockType %21 Aligned 8
 %26 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %22 %int_0
 OpStore %26 %int_531 Aligned 16
-; CHECK-NOT: %22 = OpLoad %_ptr_PhysicalStorageBuffer_blockType %21 Aligned 8
-; CHECK-NOT: %26 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %22 %int_0
-; CHECK: %30 = OpConvertPtrToU %ulong %21
-; CHECK: %67 = OpFunctionCall %bool %inst_buff_addr_search_and_test %30 %uint_8
-; CHECK: OpSelectionMerge %68 None
-; CHECK: OpBranchConditional %67 %69 %70
-; CHECK: %69 = OpLabel
-; CHECK: %71 = OpLoad %_ptr_PhysicalStorageBuffer_blockType %21 Aligned 8
-; CHECK: OpBranch %68
-; CHECK: %70 = OpLabel
-; CHECK: %72 = OpUConvert %uint %30
-; CHECK: %74 = OpShiftRightLogical %ulong %30 %uint_32
-; CHECK: %75 = OpUConvert %uint %74
-; CHECK: %131 = OpFunctionCall %void %inst_buff_addr_stream_write_4 %uint_44 %uint_2 %72 %75
-; CHECK: %133 = OpConvertUToPtr %_ptr_PhysicalStorageBuffer_blockType %132
-; CHECK: OpBranch %68
-; CHECK: %68 = OpLabel
-; CHECK: %134 = OpPhi %_ptr_PhysicalStorageBuffer_blockType %71 %69 %133 %70
-; CHECK: %26 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %134 %int_0
-; CHECK: %135 = OpConvertPtrToU %ulong %26
-; CHECK: %136 = OpFunctionCall %bool %inst_buff_addr_search_and_test %135 %uint_4
-; CHECK: OpSelectionMerge %137 None
-; CHECK: OpBranchConditional %136 %138 %139
-; CHECK: %138 = OpLabel
-; CHECK: OpStore %26 %int_531 Aligned 16
-; CHECK: OpBranch %137
-; CHECK: %139 = OpLabel
-; CHECK: %140 = OpUConvert %uint %135
-; CHECK: %141 = OpShiftRightLogical %ulong %135 %uint_32
-; CHECK: %142 = OpUConvert %uint %141
-; CHECK: %144 = OpFunctionCall %void %inst_buff_addr_stream_write_4 %uint_46 %uint_2 %140 %142
-; CHECK: OpBranch %137
-; CHECK: %137 = OpLabel
+;CHECK-NOT: %22 = OpLoad %_ptr_PhysicalStorageBuffer_blockType %21 Aligned 8
+;CHECK-NOT: %26 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %22 %int_0
+;CHECK: {{%\w+}} = OpConvertPtrToU %ulong %21
+;CHECK: {{%\w+}} = OpLoad %v3uint %gl_GlobalInvocationID
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5 {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_45 {{%\w+}} {{%\w+}} %uint_8
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpLoad %_ptr_PhysicalStorageBuffer_blockType %21 Aligned 8
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpConvertUToPtr %_ptr_PhysicalStorageBuffer_blockType %52
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpPhi %_ptr_PhysicalStorageBuffer_blockType {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: %26 = OpAccessChain %_ptr_PhysicalStorageBuffer_int {{%\w+}} %int_0
+;CHECK: {{%\w+}} = OpConvertPtrToU %ulong %26
+;CHECK: {{%\w+}} = OpLoad %v3uint %gl_GlobalInvocationID
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5 {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_47 {{%\w+}} {{%\w+}} %uint_4
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpStore %26 %int_531 Aligned 16
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
 OpReturn
 OpFunctionEnd
 )";
-
-  const std::string output_funcs = kSearchAndTest + kStreamWrite4Compute;
+  // clang-format on
 
   SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   SinglePassRunAndMatch<InstBuffAddrCheckPass>(
-      defs + decorates + globals + main_func + output_funcs, true, 7u, 23u);
+      defs + decorates + globals + kImportStub + main_func, true, 23u);
 }
 
 TEST_F(InstBuffAddrTest, StructLoad) {
@@ -475,11 +315,11 @@
 OpCapability Shader
 OpCapability Int64
 OpCapability PhysicalStorageBufferAddresses
-; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
+;CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
 %1 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel PhysicalStorageBuffer64 GLSL450
 OpEntryPoint Fragment %main "main"
-; CHECK: OpEntryPoint Fragment %main "main" %inst_buff_addr_input_buffer %inst_buff_addr_output_buffer %gl_FragCoord
+;CHECK: OpEntryPoint Fragment %main "main" %gl_FragCoord
 OpExecutionMode %main OriginUpperLeft
 OpSource GLSL 450
 OpSourceExtension "GL_ARB_gpu_shader_int64"
@@ -498,11 +338,8 @@
 OpMemberDecorate %Test_0 0 Offset 0
 OpMemberDecorate %TestBuffer 0 Offset 0
 OpDecorate %TestBuffer Block
-; CHECK: OpDecorate %_runtimearr_ulong ArrayStride 8
-)" + kInputDecorations + R"(
-; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
-)" + kOutputDecorations + R"(
-; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
+)" + kImportDeco + R"(
+;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
 )";
 
   const std::string globals = R"(
@@ -519,50 +356,415 @@
 %int_0 = OpConstant %int 0
 %_ptr_PhysicalStorageBuffer_Test_0 = OpTypePointer PhysicalStorageBuffer %Test_0
 %ulong_18446744073172680704 = OpConstant %ulong 18446744073172680704
-; CHECK: %47 = OpTypeFunction %bool %ulong %uint
-)" + kInputGlobals + R"(
-; CHECK: %90 = OpTypeFunction %void %uint %uint %uint %uint
-)" + kOutputGlobals + R"(
-; CHECK: %143 = OpConstantNull %Test_0
+;CHECK: {{%\w+}} = OpConstantNull %Test_0
 )";
-  // clang-format on
 
-  const std::string main_func =
-      R"(
+  const std::string main_func = R"(
 %main = OpFunction %void None %3
 %5 = OpLabel
 %37 = OpConvertUToPtr %_ptr_PhysicalStorageBuffer_TestBuffer %ulong_18446744073172680704
 %38 = OpAccessChain %_ptr_PhysicalStorageBuffer_Test_0 %37 %int_0
 %39 = OpLoad %Test_0 %38 Aligned 16
-; CHECK-NOT: %39 = OpLoad %Test_0 %38 Aligned 16
-; CHECK: %43 = OpConvertPtrToU %ulong %38
-; CHECK: %80 = OpFunctionCall %bool %inst_buff_addr_search_and_test %43 %uint_4
-; CHECK: OpSelectionMerge %81 None
-; CHECK: OpBranchConditional %80 %82 %83
-; CHECK: %82 = OpLabel
-; CHECK: %84 = OpLoad %Test_0 %38 Aligned 16
-; CHECK: OpBranch %81
-; CHECK: %83 = OpLabel
-; CHECK: %85 = OpUConvert %uint %43
-; CHECK: %87 = OpShiftRightLogical %ulong %43 %uint_32
-; CHECK: %88 = OpUConvert %uint %87
-; CHECK: %142 = OpFunctionCall %void %inst_buff_addr_stream_write_4 %uint_37 %uint_2 %85 %88
-; CHECK: OpBranch %81
-; CHECK: %81 = OpLabel
-; CHECK: %144 = OpPhi %Test_0 %84 %82 %143 %83
+;CHECK-NOT: %39 = OpLoad %Test_0 %38 Aligned 16
+;CHECK: {{%\w+}} = OpConvertPtrToU %ulong %38
+;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord
+;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}}
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_38 {{%\w+}} {{%\w+}} %uint_4
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpLoad %Test_0 %38 Aligned 16
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: [[phi_result:%\w+]] = OpPhi %Test_0 {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}}
 %40 = OpCopyLogical %Test %39
-; CHECK-NOT: %40 = OpCopyLogical %Test %39
-; CHECK: %40 = OpCopyLogical %Test %144
+;CHECK-NOT: %40 = OpCopyLogical %Test %39
+;CHECK: %40 = OpCopyLogical %Test [[phi_result]]
 OpReturn
 OpFunctionEnd
 )";
+  // clang-format on
 
-  const std::string output_funcs = kSearchAndTest + kStreamWrite4Frag;
-
-  SetTargetEnv(SPV_ENV_VULKAN_1_2);
+  SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
   SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   SinglePassRunAndMatch<InstBuffAddrCheckPass>(
-      defs + decorates + globals + main_func + output_funcs, true);
+      defs + decorates + globals + kImportStub + main_func, true);
+}
+
+TEST_F(InstBuffAddrTest, PaddedStructLoad) {
+  // #version 450
+  // #extension GL_EXT_buffer_reference : enable
+  // #extension GL_ARB_gpu_shader_int64 : enable
+  // struct Test {
+  //   uvec3 pad_1;  // Offset 0 Size 12
+  //   double pad_2; // Offset 16 Size 8 (alignment requirement)
+  //   float a;      // Offset 24 Size 4
+  // }; // Total Size 28
+  //
+  // layout(buffer_reference, std430, buffer_reference_align = 16) buffer
+  // TestBuffer { Test test; };
+  //
+  // Test GetTest(uint64_t ptr) {
+  //   return TestBuffer(ptr).test;
+  // }
+  //
+  // void main() {
+  //   GetTest(0xe0000000);
+  // }
+
+  const std::string defs =
+      R"(
+OpCapability Shader
+OpCapability Float64
+OpCapability Int64
+OpCapability PhysicalStorageBufferAddresses
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
+OpEntryPoint Vertex %main "main"
+OpSource GLSL 450
+OpSourceExtension "GL_ARB_gpu_shader_int64"
+OpSourceExtension "GL_EXT_buffer_reference"
+OpName %main "main"
+OpName %Test "Test"
+OpMemberName %Test 0 "pad_1"
+OpMemberName %Test 1 "pad_2"
+OpMemberName %Test 2 "a"
+OpName %GetTest_u641_ "GetTest(u641;"
+OpName %ptr "ptr"
+OpName %Test_0 "Test"
+OpMemberName %Test_0 0 "pad_1"
+OpMemberName %Test_0 1 "pad_2"
+OpMemberName %Test_0 2 "a"
+OpName %TestBuffer "TestBuffer"
+OpMemberName %TestBuffer 0 "test"
+OpName %param "param"
+)";
+
+  // clang-format off
+  const std::string decorates = R"(
+OpDecorate %TestBuffer Block
+OpMemberDecorate %Test_0 0 Offset 0
+OpMemberDecorate %Test_0 1 Offset 16
+OpMemberDecorate %Test_0 2 Offset 24
+OpMemberDecorate %TestBuffer 0 Offset 0
+)" + kImportDeco + R"(
+;CHECK: OpDecorate %gl_VertexIndex BuiltIn VertexIndex
+;CHECK: OpDecorate %gl_InstanceIndex BuiltIn InstanceIndex
+)";
+
+  const std::string globals = R"(
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%ulong = OpTypeInt 64 0
+%_ptr_Function_ulong = OpTypePointer Function %ulong
+%uint = OpTypeInt 32 0
+%v3uint = OpTypeVector %uint 3
+%double = OpTypeFloat 64
+%float = OpTypeFloat 32
+%Test = OpTypeStruct %v3uint %double %float
+%13 = OpTypeFunction %Test %_ptr_Function_ulong
+OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_TestBuffer PhysicalStorageBuffer
+%Test_0 = OpTypeStruct %v3uint %double %float
+%TestBuffer = OpTypeStruct %Test_0
+%_ptr_PhysicalStorageBuffer_TestBuffer = OpTypePointer PhysicalStorageBuffer %TestBuffer
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%_ptr_PhysicalStorageBuffer_Test_0 = OpTypePointer PhysicalStorageBuffer %Test_0
+%_ptr_Function_Test = OpTypePointer Function %Test
+%ulong_18446744073172680704 = OpConstant %ulong 18446744073172680704
+;CHECK: {{%\w+}} = OpConstantNull %Test_0
+)";
+
+  const std::string main_func = R"(
+%main = OpFunction %void None %3
+%5 = OpLabel
+%param = OpVariable %_ptr_Function_ulong Function
+OpStore %param %ulong_18446744073172680704
+%35 = OpFunctionCall %Test %GetTest_u641_ %param
+OpReturn
+OpFunctionEnd
+%GetTest_u641_ = OpFunction %Test None %13
+%ptr = OpFunctionParameter %_ptr_Function_ulong
+%16 = OpLabel
+%28 = OpVariable %_ptr_Function_Test Function
+%17 = OpLoad %ulong %ptr
+%21 = OpConvertUToPtr %_ptr_PhysicalStorageBuffer_TestBuffer %17
+%25 = OpAccessChain %_ptr_PhysicalStorageBuffer_Test_0 %21 %int_0
+%26 = OpLoad %Test_0 %25 Aligned 16
+%29 = OpCopyLogical %Test %26
+;CHECK-NOT: %30 = OpLoad %Test %28
+;CHECK-NOT: %26 = OpLoad %Test_0 %25 Aligned 16
+;CHECK-NOT: %29 = OpCopyLogical %Test %26
+;CHECK: {{%\w+}} = OpConvertPtrToU %ulong %25
+;CHECK: {{%\w+}} = OpLoad %uint %gl_VertexIndex
+;CHECK: {{%\w+}} = OpLoad %uint %gl_InstanceIndex
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_0 {{%\w+}} {{%\w+}} %uint_0
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_63 {{%\w+}} {{%\w+}} %uint_28
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpLoad %Test_0 %25 Aligned 16
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: [[phi_result:%\w+]] = OpPhi %Test_0 {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: %29 = OpCopyLogical %Test [[phi_result]]
+OpStore %28 %29
+%30 = OpLoad %Test %28
+OpReturnValue %30
+OpFunctionEnd
+)";
+  // clang-format on
+
+  SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndMatch<InstBuffAddrCheckPass>(
+      defs + decorates + globals + kImportStub + main_func, true);
+}
+
+TEST_F(InstBuffAddrTest, DeviceBufferAddressOOB) {
+  // #version 450
+  // #extension GL_EXT_buffer_reference : enable
+  //  layout(buffer_reference, buffer_reference_align = 16) buffer bufStruct;
+  // layout(set = 0, binding = 0) uniform ufoo {
+  //     bufStruct data;
+  //     int nWrites;
+  // } u_info;
+  // layout(buffer_reference, std140) buffer bufStruct {
+  //     int a[4];
+  // };
+  // void main() {
+  //     for (int i=0; i < u_info.nWrites; ++i) {
+  //         u_info.data.a[i] = 0xdeadca71;
+  //     }
+  // }
+
+  // clang-format off
+  const std::string text = R"(
+OpCapability Shader
+OpCapability PhysicalStorageBufferAddresses
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
+OpEntryPoint Vertex %main "main" %u_info
+;CHECK: OpEntryPoint Vertex %main "main" %u_info %gl_VertexIndex %gl_InstanceIndex
+OpSource GLSL 450
+OpSourceExtension "GL_EXT_buffer_reference"
+OpName %main "main"
+OpName %i "i"
+OpName %ufoo "ufoo"
+OpMemberName %ufoo 0 "data"
+OpMemberName %ufoo 1 "nWrites"
+OpName %bufStruct "bufStruct"
+OpMemberName %bufStruct 0 "a"
+OpName %u_info "u_info"
+OpMemberDecorate %ufoo 0 Offset 0
+OpMemberDecorate %ufoo 1 Offset 8
+OpDecorate %ufoo Block
+OpDecorate %_arr_int_uint_4 ArrayStride 16
+OpMemberDecorate %bufStruct 0 Offset 0
+OpDecorate %bufStruct Block
+OpDecorate %u_info DescriptorSet 0
+OpDecorate %u_info Binding 0
+)" + kImportDeco + R"(
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%int = OpTypeInt 32 1
+%_ptr_Function_int = OpTypePointer Function %int
+%int_0 = OpConstant %int 0
+OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_bufStruct PhysicalStorageBuffer
+%ufoo = OpTypeStruct %_ptr_PhysicalStorageBuffer_bufStruct %int
+%uint = OpTypeInt 32 0
+%uint_4 = OpConstant %uint 4
+%_arr_int_uint_4 = OpTypeArray %int %uint_4
+%bufStruct = OpTypeStruct %_arr_int_uint_4
+%_ptr_PhysicalStorageBuffer_bufStruct = OpTypePointer PhysicalStorageBuffer %bufStruct
+%_ptr_Uniform_ufoo = OpTypePointer Uniform %ufoo
+%u_info = OpVariable %_ptr_Uniform_ufoo Uniform
+%int_1 = OpConstant %int 1
+%_ptr_Uniform_int = OpTypePointer Uniform %int
+%bool = OpTypeBool
+%_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct = OpTypePointer Uniform %_ptr_PhysicalStorageBuffer_bufStruct
+%int_n559035791 = OpConstant %int -559035791
+%_ptr_PhysicalStorageBuffer_int = OpTypePointer PhysicalStorageBuffer %int
+)" + kImportStub + R"(
+%main = OpFunction %void None %3
+%5 = OpLabel
+%i = OpVariable %_ptr_Function_int Function
+OpStore %i %int_0
+OpBranch %10
+%10 = OpLabel
+OpLoopMerge %12 %13 None
+OpBranch %14
+%14 = OpLabel
+%15 = OpLoad %int %i
+%26 = OpAccessChain %_ptr_Uniform_int %u_info %int_1
+%27 = OpLoad %int %26
+%29 = OpSLessThan %bool %15 %27
+OpBranchConditional %29 %11 %12
+%11 = OpLabel
+%31 = OpAccessChain %_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct %u_info %int_0
+%32 = OpLoad %_ptr_PhysicalStorageBuffer_bufStruct %31
+%33 = OpLoad %int %i
+%36 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %32 %int_0 %33
+OpStore %36 %int_n559035791 Aligned 16
+;CHECK: {{%\w+}} = OpConvertPtrToU %ulong %36
+;CHECK: {{%\w+}} = OpLoad %uint %gl_VertexIndex
+;CHECK: {{%\w+}} = OpLoad %uint %gl_InstanceIndex
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_0 {{%\w+}} {{%\w+}} %uint_0
+;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_63 {{%\w+}} {{%\w+}} %uint_4
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpStore %36 %int_n559035791 Aligned 16
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+OpBranch %13
+%13 = OpLabel
+%37 = OpLoad %int %i
+%38 = OpIAdd %int %37 %int_1
+OpStore %i %38
+OpBranch %10
+%12 = OpLabel
+OpReturn
+OpFunctionEnd)";
+  // clang-format on
+
+  SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndMatch<InstBuffAddrCheckPass>(text, true, 23);
+}
+
+TEST_F(InstBuffAddrTest, UVec3ScalarAddressOOB) {
+  // clang-format off
+  // #version 450
+  //    #extension GL_EXT_buffer_reference : enable
+  //    #extension GL_EXT_scalar_block_layout : enable
+  //    layout(buffer_reference, std430, scalar) readonly buffer IndexBuffer
+  //    {
+  //        uvec3 indices[];
+  //    };
+  //    layout(set = 0, binding = 0) uniform ufoo {
+  //        IndexBuffer data;
+  //        int nReads;
+  //    } u_info;
+  //    void main() {
+  //        uvec3 readvec;
+  //        for (int i=0; i < u_info.nReads; ++i) {
+  //            readvec = u_info.data.indices[i];
+  //        }
+  //    }
+  const std::string text = R"(
+OpCapability Shader
+OpCapability PhysicalStorageBufferAddresses
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
+OpEntryPoint Vertex %main "main" %u_info
+OpSource GLSL 450
+OpSourceExtension "GL_EXT_buffer_reference"
+OpSourceExtension "GL_EXT_scalar_block_layout"
+OpName %main "main"
+OpName %i "i"
+OpName %ufoo "ufoo"
+OpMemberName %ufoo 0 "data"
+OpMemberName %ufoo 1 "nReads"
+OpName %IndexBuffer "IndexBuffer"
+OpMemberName %IndexBuffer 0 "indices"
+OpName %u_info "u_info"
+OpName %readvec "readvec"
+OpMemberDecorate %ufoo 0 Offset 0
+OpMemberDecorate %ufoo 1 Offset 8
+OpDecorate %ufoo Block
+OpDecorate %_runtimearr_v3uint ArrayStride 12
+OpMemberDecorate %IndexBuffer 0 NonWritable
+OpMemberDecorate %IndexBuffer 0 Offset 0
+OpDecorate %IndexBuffer Block
+OpDecorate %u_info DescriptorSet 0
+OpDecorate %u_info Binding 0
+)" + kImportDeco + R"(
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%int = OpTypeInt 32 1
+%_ptr_Function_int = OpTypePointer Function %int
+%int_0 = OpConstant %int 0
+OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_IndexBuffer PhysicalStorageBuffer
+%ufoo = OpTypeStruct %_ptr_PhysicalStorageBuffer_IndexBuffer %int
+%uint = OpTypeInt 32 0
+%v3uint = OpTypeVector %uint 3
+%_runtimearr_v3uint = OpTypeRuntimeArray %v3uint
+%IndexBuffer = OpTypeStruct %_runtimearr_v3uint
+%_ptr_PhysicalStorageBuffer_IndexBuffer = OpTypePointer PhysicalStorageBuffer %IndexBuffer
+%_ptr_Uniform_ufoo = OpTypePointer Uniform %ufoo
+%u_info = OpVariable %_ptr_Uniform_ufoo Uniform
+%int_1 = OpConstant %int 1
+%_ptr_Uniform_int = OpTypePointer Uniform %int
+%bool = OpTypeBool
+)" + kImportStub + R"(
+%_ptr_Function_v3uint = OpTypePointer Function %v3uint
+%_ptr_Uniform__ptr_PhysicalStorageBuffer_IndexBuffer = OpTypePointer Uniform %_ptr_PhysicalStorageBuffer_IndexBuffer
+%_ptr_PhysicalStorageBuffer_v3uint = OpTypePointer PhysicalStorageBuffer %v3uint
+%main = OpFunction %void None %3
+%5 = OpLabel
+%i = OpVariable %_ptr_Function_int Function
+%readvec = OpVariable %_ptr_Function_v3uint Function
+OpStore %i %int_0
+OpBranch %10
+%10 = OpLabel
+OpLoopMerge %12 %13 None
+OpBranch %14
+%14 = OpLabel
+%15 = OpLoad %int %i
+%26 = OpAccessChain %_ptr_Uniform_int %u_info %int_1
+%27 = OpLoad %int %26
+%29 = OpSLessThan %bool %15 %27
+OpBranchConditional %29 %11 %12
+%11 = OpLabel
+%33 = OpAccessChain %_ptr_Uniform__ptr_PhysicalStorageBuffer_IndexBuffer %u_info %int_0
+%34 = OpLoad %_ptr_PhysicalStorageBuffer_IndexBuffer %33
+%35 = OpLoad %int %i
+%37 = OpAccessChain %_ptr_PhysicalStorageBuffer_v3uint %34 %int_0 %35
+%38 = OpLoad %v3uint %37 Aligned 4
+OpStore %readvec %38
+;CHECK-NOT: %38 = OpLoad %v3uint %37 Aligned 4
+;CHECK-NOT: OpStore %readvec %38
+;CHECK: {{%\w+}} = OpConvertPtrToU %ulong %37
+;CHECK: {{%\w+}} = OpLoad %uint %gl_VertexIndex
+;CHECK: {{%\w+}} = OpLoad %uint %gl_InstanceIndex
+;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_0 {{%\w+}} {{%\w+}} %uint_0
+;CHECK: [[test_result:%\w+]] = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_67 {{%\w+}} {{%\w+}} %uint_12
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional [[test_result]] {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpLoad %v3uint %37 Aligned 4
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: OpBranch {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: [[phi_result:%\w+]] = OpPhi %v3uint {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: OpStore %readvec [[phi_result]]
+OpBranch %13
+%13 = OpLabel
+%39 = OpLoad %int %i
+%40 = OpIAdd %int %39 %int_1
+OpStore %i %40
+OpBranch %10
+%12 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+  // clang-format on
+
+  SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  ValidatorOptions()->scalar_block_layout = true;
+  SinglePassRunAndMatch<InstBuffAddrCheckPass>(text, true, 23);
 }
 
 }  // namespace
diff --git a/test/opt/inst_debug_printf_test.cpp b/test/opt/inst_debug_printf_test.cpp
index 57e5044..e9774de 100644
--- a/test/opt/inst_debug_printf_test.cpp
+++ b/test/opt/inst_debug_printf_test.cpp
@@ -18,7 +18,6 @@
 #include <string>
 #include <vector>
 
-#include "test/opt/assembly_builder.h"
 #include "test/opt/pass_fixture.h"
 #include "test/opt/pass_utils.h"
 
@@ -30,12 +29,13 @@
 ; CHECK: OpDecorate [[output_buffer_type:%inst_printf_OutputBuffer]] Block
 ; CHECK: OpMemberDecorate [[output_buffer_type]] 0 Offset 0
 ; CHECK: OpMemberDecorate [[output_buffer_type]] 1 Offset 4
+; CHECK: OpMemberDecorate [[output_buffer_type]] 2 Offset 8
 ; CHECK: OpDecorate [[output_buffer_var:%\w+]] DescriptorSet 7
 ; CHECK: OpDecorate [[output_buffer_var]] Binding 3
 )";
 
 static const std::string kOutputGlobals = R"(
-; CHECK: [[output_buffer_type]] = OpTypeStruct %uint %_runtimearr_uint
+; CHECK: [[output_buffer_type]] = OpTypeStruct %uint %uint %_runtimearr_uint
 ; CHECK: [[output_ptr_type:%\w+]] = OpTypePointer StorageBuffer [[output_buffer_type]]
 ; CHECK: [[output_buffer_var]] = OpVariable [[output_ptr_type]] StorageBuffer
 )";
@@ -87,10 +87,9 @@
 OpDecorate %7 Binding 0
 OpDecorate %3 Location 0
 OpDecorate %4 Location 0
-; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
-)" + kOutputDecorations + R"(
 ; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
-)";
+; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
+)" + kOutputDecorations;
 
   const std::string globals =
       R"(%void = OpTypeVoid
@@ -110,14 +109,14 @@
 %_ptr_Output_v4float = OpTypePointer Output %v4float
 %4 = OpVariable %_ptr_Output_v4float Output
 ; CHECK: %uint = OpTypeInt 32 0
-; CHECK: %38 = OpTypeFunction %void %uint %uint %uint %uint %uint %uint
+; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
+; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
+; CHECK: %v4uint = OpTypeVector %uint 4
+; CHECK: [[func_type:%\w+]] = OpTypeFunction %void %uint %uint %v4uint %uint %uint %uint %uint %uint
 ; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
 )" + kOutputGlobals + R"(
 ; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
 ; CHECK: %bool = OpTypeBool
-; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
-; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
-; CHECK: %v4uint = OpTypeVector %uint 4
 )";
   // clang-format on
 
@@ -131,78 +130,88 @@
 %25 = OpImageSampleImplicitLod %v4float %24 %21
 %26 = OpExtInst %void %1 1 %5 %25
 ; CHECK-NOT: %26 = OpExtInst %void %1 1 %5 %25
-; CHECK: %29 = OpCompositeExtract %float %25 0
-; CHECK: %30 = OpBitcast %uint %29
-; CHECK: %31 = OpCompositeExtract %float %25 1
-; CHECK: %32 = OpBitcast %uint %31
-; CHECK: %33 = OpCompositeExtract %float %25 2
-; CHECK: %34 = OpBitcast %uint %33
-; CHECK: %35 = OpCompositeExtract %float %25 3
-; CHECK: %36 = OpBitcast %uint %35
-; CHECK: %101 = OpFunctionCall %void %inst_printf_stream_write_6 %uint_36 %uint_5 %30 %32 %34 %36
-; CHECK: OpBranch %102
-; CHECK: %102 = OpLabel
+; CHECK: {{%\w+}} = OpCompositeExtract %float %25 0
+; CHECK: {{%\w+}} = OpBitcast %uint {{%\w+}}
+; CHECK: {{%\w+}} = OpCompositeExtract %float %25 1
+; CHECK: {{%\w+}} = OpBitcast %uint {{%\w+}}
+; CHECK: {{%\w+}} = OpCompositeExtract %float %25 2
+; CHECK: {{%\w+}} = OpBitcast %uint {{%\w+}}
+; CHECK: {{%\w+}} = OpCompositeExtract %float %25 3
+; CHECK: {{%\w+}} = OpBitcast %uint {{%\w+}}
+; CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord
+; CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}}
+; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+; CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0
+; CHECK: {{%\w+}} = OpFunctionCall %void %inst_printf_stream_write_5 %uint_23 %uint_36 {{%\w+}} %uint_5 {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}}
+; CHECK: OpBranch {{%\w+}}
+; CHECK: {{%\w+}} = OpLabel
 OpStore %4 %25
 OpReturn
 OpFunctionEnd
 )";
 
   const std::string output_func = R"(
-; CHECK: %inst_printf_stream_write_6 = OpFunction %void None %38
-; CHECK: %39 = OpFunctionParameter %uint
-; CHECK: %40 = OpFunctionParameter %uint
-; CHECK: %41 = OpFunctionParameter %uint
-; CHECK: %42 = OpFunctionParameter %uint
-; CHECK: %43 = OpFunctionParameter %uint
-; CHECK: %44 = OpFunctionParameter %uint
-; CHECK: %45 = OpLabel
-; CHECK: %52 = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_0
-; CHECK: %55 = OpAtomicIAdd %uint %52 %uint_4 %uint_0 %uint_12
-; CHECK: %56 = OpIAdd %uint %55 %uint_12
-; CHECK: %57 = OpArrayLength %uint %inst_printf_output_buffer 1
-; CHECK: %59 = OpULessThanEqual %bool %56 %57
-; CHECK: OpSelectionMerge %60 None
-; CHECK: OpBranchConditional %59 %61 %60
-; CHECK: %61 = OpLabel
-; CHECK: %62 = OpIAdd %uint %55 %uint_0
-; CHECK: %64 = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_1 %62
-; CHECK: OpStore %64 %uint_12
-; CHECK: %66 = OpIAdd %uint %55 %uint_1
-; CHECK: %67 = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_1 %66
-; CHECK: OpStore %67 %uint_23
-; CHECK: %69 = OpIAdd %uint %55 %uint_2
-; CHECK: %70 = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_1 %69
-; CHECK: OpStore %70 %39
-; CHECK: %72 = OpIAdd %uint %55 %uint_3
-; CHECK: %73 = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_1 %72
-; CHECK: OpStore %73 %uint_4
-; CHECK: %76 = OpLoad %v4float %gl_FragCoord
-; CHECK: %78 = OpBitcast %v4uint %76
-; CHECK: %79 = OpCompositeExtract %uint %78 0
-; CHECK: %80 = OpIAdd %uint %55 %uint_4
-; CHECK: %81 = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_1 %80
-; CHECK: OpStore %81 %79
-; CHECK: %82 = OpCompositeExtract %uint %78 1
-; CHECK: %83 = OpIAdd %uint %55 %uint_5
-; CHECK: %84 = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_1 %83
-; CHECK: OpStore %84 %82
-; CHECK: %86 = OpIAdd %uint %55 %uint_7
-; CHECK: %87 = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_1 %86
-; CHECK: OpStore %87 %40
-; CHECK: %89 = OpIAdd %uint %55 %uint_8
-; CHECK: %90 = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_1 %89
-; CHECK: OpStore %90 %41
-; CHECK: %92 = OpIAdd %uint %55 %uint_9
-; CHECK: %93 = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_1 %92
-; CHECK: OpStore %93 %42
-; CHECK: %95 = OpIAdd %uint %55 %uint_10
-; CHECK: %96 = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_1 %95
-; CHECK: OpStore %96 %43
-; CHECK: %98 = OpIAdd %uint %55 %uint_11
-; CHECK: %99 = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_1 %98
-; CHECK: OpStore %99 %44
-; CHECK: OpBranch %60
-; CHECK: %60 = OpLabel
+; CHECK: %inst_printf_stream_write_5 = OpFunction %void None {{%\w+}}
+; CHECK: [[sw_shader_id:%\w+]] = OpFunctionParameter %uint
+; CHECK: [[sw_inst_idx:%\w+]] = OpFunctionParameter %uint
+; CHECK: [[sw_stage_info:%\w+]] = OpFunctionParameter %v4uint
+; CHECK: [[sw_param_1:%\w+]] = OpFunctionParameter %uint
+; CHECK: [[sw_param_2:%\w+]] = OpFunctionParameter %uint
+; CHECK: [[sw_param_3:%\w+]] = OpFunctionParameter %uint
+; CHECK: [[sw_param_4:%\w+]] = OpFunctionParameter %uint
+; CHECK: [[sw_param_5:%\w+]] = OpFunctionParameter %uint
+; CHECK: {{%\w+}} = OpLabel
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1
+; CHECK: {{%\w+}} = OpAtomicIAdd %uint {{%\w+}} %uint_4 %uint_0 %uint_12
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_12
+; CHECK: {{%\w+}} = OpArrayLength %uint [[output_buffer_var]] 2
+; CHECK: {{%\w+}} = OpULessThanEqual %bool {{%\w+}} {{%\w+}}
+; CHECK: OpSelectionMerge {{%\w+}} None
+; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+; CHECK: {{%\w+}} = OpLabel
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_0
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
+; CHECK: OpStore {{%\w+}} %uint_12
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_1
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
+; CHECK: OpStore {{%\w+}} [[sw_shader_id]]
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_2
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
+; CHECK: OpStore {{%\w+}} [[sw_inst_idx]]
+; CHECK: {{%\w+}} = OpCompositeExtract %uint [[sw_stage_info]] 0
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_3
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
+; CHECK: OpStore {{%\w+}} {{%\w+}}
+; CHECK: {{%\w+}} = OpCompositeExtract %uint [[sw_stage_info]] 1
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_4
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
+; CHECK: OpStore {{%\w+}} {{%\w+}}
+; CHECK: {{%\w+}} = OpCompositeExtract %uint [[sw_stage_info]] 2
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_5
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
+; CHECK: OpStore {{%\w+}} {{%\w+}}
+; CHECK: {{%\w+}} = OpCompositeExtract %uint [[sw_stage_info]] 3
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_6
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
+; CHECK: OpStore {{%\w+}} {{%\w+}}
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_7
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
+; CHECK: OpStore {{%\w+}} [[sw_param_1]]
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_8
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
+; CHECK: OpStore {{%\w+}} [[sw_param_2]]
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_9
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
+; CHECK: OpStore {{%\w+}} [[sw_param_3]]
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_10
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
+; CHECK: OpStore {{%\w+}} [[sw_param_4]]
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_11
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
+; CHECK: OpStore {{%\w+}} [[sw_param_5]]
+; CHECK: OpBranch {{%\w+}}
+; CHECK: {{%\w+}} = OpLabel
 ; CHECK: OpReturn
 ; CHECK: OpFunctionEnd
 )";
diff --git a/test/opt/instruction_test.cpp b/test/opt/instruction_test.cpp
index dd749ab..67961eb 100644
--- a/test/opt/instruction_test.cpp
+++ b/test/opt/instruction_test.cpp
@@ -12,13 +12,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "source/opt/instruction.h"
+
 #include <memory>
-#include <string>
 #include <utility>
 #include <vector>
 
 #include "gmock/gmock.h"
-#include "source/opt/instruction.h"
 #include "source/opt/ir_context.h"
 #include "spirv-tools/libspirv.h"
 #include "test/opt/pass_fixture.h"
@@ -39,7 +39,7 @@
 
 TEST(InstructionTest, CreateTrivial) {
   Instruction empty;
-  EXPECT_EQ(SpvOpNop, empty.opcode());
+  EXPECT_EQ(spv::Op::OpNop, empty.opcode());
   EXPECT_EQ(0u, empty.type_id());
   EXPECT_EQ(0u, empty.result_id());
   EXPECT_EQ(0u, empty.NumOperands());
@@ -51,8 +51,8 @@
 
 TEST(InstructionTest, CreateWithOpcodeAndNoOperands) {
   IRContext context(SPV_ENV_UNIVERSAL_1_2, nullptr);
-  Instruction inst(&context, SpvOpReturn);
-  EXPECT_EQ(SpvOpReturn, inst.opcode());
+  Instruction inst(&context, spv::Op::OpReturn);
+  EXPECT_EQ(spv::Op::OpReturn, inst.opcode());
   EXPECT_EQ(0u, inst.type_id());
   EXPECT_EQ(0u, inst.result_id());
   EXPECT_EQ(0u, inst.NumOperands());
@@ -81,8 +81,8 @@
 }
 
 // The words for an OpTypeInt for 32-bit signed integer resulting in Id 44.
-uint32_t kSampleInstructionWords[] = {(4 << 16) | uint32_t(SpvOpTypeInt), 44,
-                                      32, 1};
+uint32_t kSampleInstructionWords[] = {(4 << 16) | uint32_t(spv::Op::OpTypeInt),
+                                      44, 32, 1};
 // The operands that would be parsed from kSampleInstructionWords
 spv_parsed_operand_t kSampleParsedOperands[] = {
     {1, 1, SPV_OPERAND_TYPE_RESULT_ID, SPV_NUMBER_NONE, 0},
@@ -91,18 +91,19 @@
 };
 
 // A valid parse of kSampleParsedOperands.
-spv_parsed_instruction_t kSampleParsedInstruction = {kSampleInstructionWords,
-                                                     uint16_t(4),
-                                                     uint16_t(SpvOpTypeInt),
-                                                     SPV_EXT_INST_TYPE_NONE,
-                                                     0,   // type id
-                                                     44,  // result id
-                                                     kSampleParsedOperands,
-                                                     3};
+spv_parsed_instruction_t kSampleParsedInstruction = {
+    kSampleInstructionWords,
+    uint16_t(4),
+    uint16_t(spv::Op::OpTypeInt),
+    SPV_EXT_INST_TYPE_NONE,
+    0,   // type id
+    44,  // result id
+    kSampleParsedOperands,
+    3};
 
 // The words for an OpAccessChain instruction.
 uint32_t kSampleAccessChainInstructionWords[] = {
-    (7 << 16) | uint32_t(SpvOpAccessChain), 100, 101, 102, 103, 104, 105};
+    (7 << 16) | uint32_t(spv::Op::OpAccessChain), 100, 101, 102, 103, 104, 105};
 
 // The operands that would be parsed from kSampleAccessChainInstructionWords.
 spv_parsed_operand_t kSampleAccessChainOperands[] = {
@@ -118,7 +119,7 @@
 spv_parsed_instruction_t kSampleAccessChainInstruction = {
     kSampleAccessChainInstructionWords,
     uint16_t(7),
-    uint16_t(SpvOpAccessChain),
+    uint16_t(spv::Op::OpAccessChain),
     SPV_EXT_INST_TYPE_NONE,
     100,  // type id
     101,  // result id
@@ -127,7 +128,7 @@
 
 // The words for an OpControlBarrier instruction.
 uint32_t kSampleControlBarrierInstructionWords[] = {
-    (4 << 16) | uint32_t(SpvOpControlBarrier), 100, 101, 102};
+    (4 << 16) | uint32_t(spv::Op::OpControlBarrier), 100, 101, 102};
 
 // The operands that would be parsed from kSampleControlBarrierInstructionWords.
 spv_parsed_operand_t kSampleControlBarrierOperands[] = {
@@ -141,7 +142,7 @@
 spv_parsed_instruction_t kSampleControlBarrierInstruction = {
     kSampleControlBarrierInstructionWords,
     uint16_t(4),
-    uint16_t(SpvOpControlBarrier),
+    uint16_t(spv::Op::OpControlBarrier),
     SPV_EXT_INST_TYPE_NONE,
     0,  // type id
     0,  // result id
@@ -151,7 +152,7 @@
 TEST(InstructionTest, CreateWithOpcodeAndOperands) {
   IRContext context(SPV_ENV_UNIVERSAL_1_2, nullptr);
   Instruction inst(&context, kSampleParsedInstruction);
-  EXPECT_EQ(SpvOpTypeInt, inst.opcode());
+  EXPECT_EQ(spv::Op::OpTypeInt, inst.opcode());
   EXPECT_EQ(0u, inst.type_id());
   EXPECT_EQ(44u, inst.result_id());
   EXPECT_EQ(3u, inst.NumOperands());
diff --git a/test/opt/interface_var_sroa_test.cpp b/test/opt/interface_var_sroa_test.cpp
index 7762458..6f51b08 100644
--- a/test/opt/interface_var_sroa_test.cpp
+++ b/test/opt/interface_var_sroa_test.cpp
@@ -14,8 +14,6 @@
 
 #include <iostream>
 
-#include "gmock/gmock.h"
-#include "test/opt/assembly_builder.h"
 #include "test/opt/pass_fixture.h"
 #include "test/opt/pass_utils.h"
 
diff --git a/test/opt/invocation_interlock_placement_test.cpp b/test/opt/invocation_interlock_placement_test.cpp
new file mode 100644
index 0000000..2c4ff65
--- /dev/null
+++ b/test/opt/invocation_interlock_placement_test.cpp
@@ -0,0 +1,613 @@
+// Copyright (c) 2023 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 "spirv-tools/optimizer.hpp"
+#include "test/opt/pass_fixture.h"
+#include "test/opt/pass_utils.h"
+
+namespace spvtools {
+namespace opt {
+namespace {
+
+using InterlockInvocationPlacementTest = PassTest<::testing::Test>;
+
+TEST_F(InterlockInvocationPlacementTest, CheckUnchangedIfNotFragment) {
+  const std::string kTest = R"(
+               OpCapability Shader
+               OpCapability FragmentShaderSampleInterlockEXT
+               OpExtension "SPV_EXT_fragment_shader_interlock"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main"
+               OpExecutionMode %main SampleInterlockOrderedEXT
+               OpName %main "main"
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+       %main = OpFunction %void None %1
+          %2 = OpLabel
+               OpBeginInvocationInterlockEXT
+               OpBeginInvocationInterlockEXT
+	             OpEndInvocationInterlockEXT
+	             OpBeginInvocationInterlockEXT
+               OpEndInvocationInterlockEXT
+               OpReturn
+               OpFunctionEnd
+  )";
+  SetTargetEnv(SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(
+      Pass::Status::SuccessWithoutChange,
+      std::get<1>(SinglePassRunAndDisassemble<InvocationInterlockPlacementPass>(
+          kTest, /* skip_nop= */ false, /* do_validation= */ false)));
+}
+
+TEST_F(InterlockInvocationPlacementTest, CheckUnchangedWithoutCapability) {
+  const std::string kTest = R"(
+               OpCapability Shader
+               OpExtension "SPV_EXT_fragment_shader_interlock"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpExecutionMode %main SampleInterlockOrderedEXT
+               OpName %main "main"
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+       %main = OpFunction %void None %1
+          %2 = OpLabel
+               OpBeginInvocationInterlockEXT
+               OpBeginInvocationInterlockEXT
+	             OpEndInvocationInterlockEXT
+	             OpBeginInvocationInterlockEXT
+               OpEndInvocationInterlockEXT
+               OpReturn
+               OpFunctionEnd
+  )";
+  SetTargetEnv(SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(
+      Pass::Status::SuccessWithoutChange,
+      std::get<1>(SinglePassRunAndDisassemble<InvocationInterlockPlacementPass>(
+          kTest, /* skip_nop= */ false, /* do_validation= */ false)));
+}
+
+TEST_F(InterlockInvocationPlacementTest, CheckSingleBasicBlock) {
+  // We're using OpNoLine as a generic standin for any other instruction, to
+  // test that begin and end aren't moved.
+  const std::string kTest = R"(
+               OpCapability Shader
+               OpCapability FragmentShaderSampleInterlockEXT
+               OpExtension "SPV_EXT_fragment_shader_interlock"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpExecutionMode %main SampleInterlockOrderedEXT
+               OpName %main "main"
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+       %main = OpFunction %void None %1
+; CHECK: OpLabel
+          %2 = OpLabel
+; CHECK-NEXT: OpNoLine
+               OpNoLine
+; CHECK-NEXT: OpBeginInvocationInterlockEXT
+               OpBeginInvocationInterlockEXT
+               OpBeginInvocationInterlockEXT
+	             OpEndInvocationInterlockEXT
+	             OpBeginInvocationInterlockEXT
+; CHECK-NEXT: OpNoLine
+               OpNoLine
+; CHECK-NEXT: OpEndInvocationInterlockEXT
+               OpEndInvocationInterlockEXT
+; CHECK-NEXT: OpNoLine
+               OpNoLine
+; CHECK-NEXT: OpReturn
+               OpReturn
+               OpFunctionEnd
+  )";
+  SetTargetEnv(SPV_ENV_VULKAN_1_3);
+  const auto result = SinglePassRunAndMatch<InvocationInterlockPlacementPass>(
+      kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
+}
+
+TEST_F(InterlockInvocationPlacementTest, CheckFunctionCallExtractionBegin) {
+  const std::string kTest = R"(
+               OpCapability Shader
+               OpCapability FragmentShaderSampleInterlockEXT
+               OpExtension "SPV_EXT_fragment_shader_interlock"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpExecutionMode %main SampleInterlockOrderedEXT
+               OpName %main "main"
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+        %foo = OpFunction %void None %1
+; CHECK: OpLabel
+; CHECK-NOT: OpBeginInvocationInterlockEXT
+          %2 = OpLabel
+               OpBeginInvocationInterlockEXT
+               OpBeginInvocationInterlockEXT
+               OpReturn
+; CHECK: OpFunctionEnd
+               OpFunctionEnd
+       %main = OpFunction %void None %1
+; CHECK: OpLabel
+          %3 = OpLabel
+; CHECK-NEXT: OpBeginInvocationInterlockEXT
+; CHECK-NEXT: OpFunctionCall
+          %4 = OpFunctionCall %void %foo
+; CHECK-NEXT: OpReturn
+               OpReturn
+               OpFunctionEnd
+  )";
+  SetTargetEnv(SPV_ENV_VULKAN_1_3);
+  const auto result = SinglePassRunAndMatch<InvocationInterlockPlacementPass>(
+      kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
+}
+
+TEST_F(InterlockInvocationPlacementTest, CheckFunctionCallExtractionEnd) {
+  const std::string kTest = R"(
+               OpCapability Shader
+               OpCapability FragmentShaderSampleInterlockEXT
+               OpExtension "SPV_EXT_fragment_shader_interlock"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpExecutionMode %main SampleInterlockOrderedEXT
+               OpName %main "main"
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+        %foo = OpFunction %void None %1
+; CHECK: OpLabel
+; CHECK-NOT: OpEndInvocationInterlockEXT
+          %2 = OpLabel
+               OpEndInvocationInterlockEXT
+               OpEndInvocationInterlockEXT
+               OpReturn
+; CHECK: OpFunctionEnd
+               OpFunctionEnd
+       %main = OpFunction %void None %1
+; CHECK: OpLabel
+          %3 = OpLabel
+; CHECK-NEXT: OpFunctionCall
+          %4 = OpFunctionCall %void %foo
+; CHECK-NEXT: OpEndInvocationInterlockEXT
+; CHECK-NEXT: OpReturn
+               OpReturn
+               OpFunctionEnd
+  )";
+  SetTargetEnv(SPV_ENV_VULKAN_1_3);
+  const auto result = SinglePassRunAndMatch<InvocationInterlockPlacementPass>(
+      kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
+}
+
+TEST_F(InterlockInvocationPlacementTest,
+       CheckFunctionCallExtractionRepeatedCall) {
+  const std::string kTest = R"(
+               OpCapability Shader
+               OpCapability FragmentShaderSampleInterlockEXT
+               OpExtension "SPV_EXT_fragment_shader_interlock"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpExecutionMode %main SampleInterlockOrderedEXT
+               OpName %main "main"
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+        %foo = OpFunction %void None %1
+; CHECK: OpLabel
+; CHECK-NOT: OpBeginInvocationInterlockEXT
+; CHECK-NOT: OpEndInvocationInterlockEXT
+          %2 = OpLabel
+               OpBeginInvocationInterlockEXT
+               OpEndInvocationInterlockEXT
+               OpReturn
+; CHECK: OpFunctionEnd
+               OpFunctionEnd
+       %main = OpFunction %void None %1
+; CHECK: OpLabel
+          %3 = OpLabel
+; CHECK-NEXT: OpBeginInvocationInterlockEXT
+; CHECK-NEXT: OpFunctionCall
+          %4 = OpFunctionCall %void %foo
+; CHECK-NEXT: OpFunctionCall
+          %5 = OpFunctionCall %void %foo
+; CHECK-NEXT: OpEndInvocationInterlockEXT
+; CHECK-NEXT: OpReturn
+               OpReturn
+               OpFunctionEnd
+  )";
+  SetTargetEnv(SPV_ENV_VULKAN_1_3);
+  const auto result = SinglePassRunAndMatch<InvocationInterlockPlacementPass>(
+      kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
+}
+
+TEST_F(InterlockInvocationPlacementTest,
+       CheckFunctionCallExtractionNestedCall) {
+  const std::string kTest = R"(
+               OpCapability Shader
+               OpCapability FragmentShaderSampleInterlockEXT
+               OpExtension "SPV_EXT_fragment_shader_interlock"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpExecutionMode %main SampleInterlockOrderedEXT
+               OpName %main "main"
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+        %foo = OpFunction %void None %1
+; CHECK: OpLabel
+; CHECK-NOT: OpBeginInvocationInterlockEXT
+; CHECK-NOT: OpEndInvocationInterlockEXT
+          %2 = OpLabel
+               OpBeginInvocationInterlockEXT
+               OpEndInvocationInterlockEXT
+               OpReturn
+; CHECK: OpFunctionEnd
+               OpFunctionEnd
+        %bar = OpFunction %void None %1
+; CHECK: OpLabel
+; CHECK-NOT: OpBeginInvocationInterlockEXT
+; CHECK-NOT: OpEndInvocationInterlockEXT
+          %3 = OpLabel
+          %4 = OpFunctionCall %void %foo
+               OpReturn
+; CHECK: OpFunctionEnd
+               OpFunctionEnd
+       %main = OpFunction %void None %1
+; CHECK: OpLabel
+          %5 = OpLabel
+; CHECK-NEXT: OpBeginInvocationInterlockEXT
+; CHECK-NEXT: OpFunctionCall
+          %6 = OpFunctionCall %void %bar
+; CHECK-NEXT: OpEndInvocationInterlockEXT
+; CHECK-NEXT: OpReturn
+               OpReturn
+               OpFunctionEnd
+  )";
+  SetTargetEnv(SPV_ENV_VULKAN_1_3);
+  const auto result = SinglePassRunAndMatch<InvocationInterlockPlacementPass>(
+      kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
+}
+
+TEST_F(InterlockInvocationPlacementTest, CheckLoopExtraction) {
+  // Tests that any begin or end instructions in a loop are moved outside of the
+  // loop.
+  const std::string kTest = R"(
+               OpCapability Shader
+               OpCapability FragmentShaderSampleInterlockEXT
+               OpExtension "SPV_EXT_fragment_shader_interlock"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpExecutionMode %main SampleInterlockOrderedEXT
+       %void = OpTypeVoid
+       %bool = OpTypeBool
+       %true = OpConstantTrue %bool
+           %1 = OpTypeFunction %void
+       %main = OpFunction %void None %1
+
+          %2 = OpLabel
+; CHECK: OpBeginInvocationInterlockEXT
+; CHECK-NOT: OpBeginInvocationInterlockEXT
+; CHECK-NOT: OpEndInvocationInterlockEXT
+               OpBranch %3
+
+          %3 = OpLabel
+               OpLoopMerge %3 %4 None
+; CHECK: OpBranchConditional
+; CHECK-NOT: OpBeginInvocationInterlockEXT
+; CHECK-NOT: OpEndInvocationInterlockEXT
+               OpBranchConditional %true %4 %5
+
+          %4 = OpLabel
+               OpBeginInvocationInterlockEXT
+               OpEndInvocationInterlockEXT
+; CHECK: OpBranch
+               OpBranch %3
+
+; CHECK-NEXT: OpLabel
+          %5 = OpLabel
+; CHECK-NEXT: OpEndInvocationInterlockEXT
+; CHECK-NOT: OpEndInvocationInterlockEXT
+               OpEndInvocationInterlockEXT
+               OpReturn
+               OpFunctionEnd
+  )";
+  SetTargetEnv(SPV_ENV_VULKAN_1_3);
+  const auto result = SinglePassRunAndMatch<InvocationInterlockPlacementPass>(
+      kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
+}
+
+TEST_F(InterlockInvocationPlacementTest, CheckAddBeginToElse) {
+  // Test that if there is a begin in a single branch of a conditional, begin
+  // will be added to the other branch.
+  const std::string kTest = R"(
+               OpCapability Shader
+               OpCapability FragmentShaderSampleInterlockEXT
+	             OpExtension "SPV_EXT_fragment_shader_interlock"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpExecutionMode %main SampleInterlockOrderedEXT
+               OpName %main "main"
+       %void = OpTypeVoid
+       %bool = OpTypeBool
+       %true = OpConstantTrue %bool
+           %1 = OpTypeFunction %void
+       %main = OpFunction %void None %1
+
+          %2 = OpLabel
+; CHECK-NOT: OpBeginInvocationInterlockEXT
+               OpSelectionMerge %5 None
+; CHECK: OpBranchConditional
+               OpBranchConditional %true %3 %4
+
+; CHECK-NEXT: OpLabel
+          %3 = OpLabel
+; CHECK-NEXT: OpBeginInvocationInterlockEXT
+               OpBeginInvocationInterlockEXT
+               OpEndInvocationInterlockEXT
+; CHECK-NEXT: OpBranch
+               OpBranch %5
+
+          %4 = OpLabel
+; CHECK: OpBeginInvocationInterlockEXT
+; CHECK-NEXT: OpBranch
+               OpBranch %5
+
+; CHECK-NEXT: OpLabel
+          %5 = OpLabel
+               OpBeginInvocationInterlockEXT
+; CHECK-NEXT: OpEndInvocationInterlockEXT
+               OpEndInvocationInterlockEXT
+               OpReturn
+               OpFunctionEnd
+  )";
+  SetTargetEnv(SPV_ENV_VULKAN_1_3);
+  const auto result = SinglePassRunAndMatch<InvocationInterlockPlacementPass>(
+      kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
+}
+
+TEST_F(InterlockInvocationPlacementTest, CheckAddEndToElse) {
+  const std::string kTest = R"(
+               OpCapability Shader
+               OpCapability FragmentShaderSampleInterlockEXT
+	             OpExtension "SPV_EXT_fragment_shader_interlock"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpExecutionMode %main SampleInterlockOrderedEXT
+               OpName %main "main"
+       %void = OpTypeVoid
+       %bool = OpTypeBool
+       %true = OpConstantTrue %bool
+           %1 = OpTypeFunction %void
+       %main = OpFunction %void None %1
+
+          %2 = OpLabel
+; CHECK: OpBeginInvocationInterlockEXT
+               OpBeginInvocationInterlockEXT
+; CHECK-NOT: OpEndInvocationInterlockEXT
+               OpEndInvocationInterlockEXT
+               OpSelectionMerge %5 None
+; CHECK: OpBranchConditional
+               OpBranchConditional %true %3 %4
+
+; CHECK-NEXT: OpLabel
+          %3 = OpLabel
+               OpBeginInvocationInterlockEXT
+; CHECK-NEXT: OpEndInvocationInterlockEXT
+               OpEndInvocationInterlockEXT
+; CHECK-NEXT: OpBranch
+               OpBranch %5
+
+          %4 = OpLabel
+; CHECK: OpEndInvocationInterlockEXT
+; CHECK-NEXT: OpBranch
+               OpBranch %5
+
+; CHECK-NEXT: OpLabel
+          %5 = OpLabel
+; CHECK-NOT: OpEndInvocationInterlockEXT
+               OpReturn
+               OpFunctionEnd
+  )";
+  SetTargetEnv(SPV_ENV_VULKAN_1_3);
+  const auto result = SinglePassRunAndMatch<InvocationInterlockPlacementPass>(
+      kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
+}
+
+TEST_F(InterlockInvocationPlacementTest, CheckSplitIfWithoutElseBegin) {
+  // Test that if there is a begin in the then branch of a conditional, and no
+  // else branch, an else branch with a begin will created.
+  const std::string kTest = R"(
+               OpCapability Shader
+               OpCapability FragmentShaderSampleInterlockEXT
+	             OpExtension "SPV_EXT_fragment_shader_interlock"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpExecutionMode %main SampleInterlockOrderedEXT
+               OpName %main "main"
+       %void = OpTypeVoid
+       %bool = OpTypeBool
+       %true = OpConstantTrue %bool
+           %1 = OpTypeFunction %void
+       %main = OpFunction %void None %1
+
+          %2 = OpLabel
+; CHECK-NOT: OpBeginInvocationInterlockEXT
+               OpSelectionMerge %5 None
+; CHECK: OpBranchConditional
+               OpBranchConditional %true %3 %5
+
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpBeginInvocationInterlockEXT
+; CHECK-NEXT: OpBranch
+
+; CHECK-NEXT: OpLabel
+          %3 = OpLabel
+; CHECK-NEXT: OpBeginInvocationInterlockEXT
+; CHECK-NOT: OpEndInvocationInterlockEXT
+               OpBeginInvocationInterlockEXT
+               OpEndInvocationInterlockEXT
+               OpBranch %5
+
+; CHECK: OpLabel
+          %5 = OpLabel
+; CHECK-NOT: OpBeginInvocationInterlockEXT
+               OpBeginInvocationInterlockEXT
+; CHECK-NEXT: OpEndInvocationInterlockEXT
+               OpEndInvocationInterlockEXT
+               OpReturn
+               OpFunctionEnd
+  )";
+  SetTargetEnv(SPV_ENV_VULKAN_1_3);
+  const auto result = SinglePassRunAndMatch<InvocationInterlockPlacementPass>(
+      kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
+}
+
+TEST_F(InterlockInvocationPlacementTest, CheckSplitIfWithoutElseEnd) {
+  const std::string kTest = R"(
+               OpCapability Shader
+               OpCapability FragmentShaderSampleInterlockEXT
+	             OpExtension "SPV_EXT_fragment_shader_interlock"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpExecutionMode %main SampleInterlockOrderedEXT
+               OpName %main "main"
+       %void = OpTypeVoid
+       %bool = OpTypeBool
+       %true = OpConstantTrue %bool
+           %1 = OpTypeFunction %void
+       %main = OpFunction %void None %1
+
+          %2 = OpLabel
+
+; CHECK: OpBeginInvocationInterlockEXT
+               OpBeginInvocationInterlockEXT
+; CHECK-NOT: OpEndInvocationInterlockEXT
+               OpEndInvocationInterlockEXT
+; CHECK-NEXT: OpSelectionMerge [[merge:%\d+]]
+               OpSelectionMerge %5 None
+; CHECK-NEXT: OpBranchConditional %true [[then:%\d+]] [[else:%\d+]]
+               OpBranchConditional %true %3 %5
+
+; CHECK-NEXT: [[else]] = OpLabel
+; CHECK-NEXT: OpEndInvocationInterlockEXT
+; CHECK-NEXT: OpBranch [[merge]]
+
+; CHECK-NEXT: [[then]] = OpLabel
+          %3 = OpLabel
+; CHECK-NEXT: OpEndInvocationInterlockEXT
+               OpBeginInvocationInterlockEXT
+               OpEndInvocationInterlockEXT
+; CHECK-NEXT: OpBranch [[merge]]
+               OpBranch %5
+
+; CHECK-NEXT: [[merge]] = OpLabel
+          %5 = OpLabel
+; CHECK-NEXT: OpReturn
+               OpReturn
+               OpFunctionEnd
+  )";
+  SetTargetEnv(SPV_ENV_VULKAN_1_3);
+  const auto result = SinglePassRunAndMatch<InvocationInterlockPlacementPass>(
+      kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
+}
+
+TEST_F(InterlockInvocationPlacementTest, CheckSplitSwitch) {
+  // Test that if there is a begin or end in a single branch of a switch, begin
+  // or end will be added to all the other branches.
+  const std::string kTest = R"(
+               OpCapability Shader
+               OpCapability FragmentShaderSampleInterlockEXT
+	             OpExtension "SPV_EXT_fragment_shader_interlock"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpExecutionMode %main SampleInterlockOrderedEXT
+               OpName %main "main"
+       %void = OpTypeVoid
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+           %1 = OpTypeFunction %void
+       %main = OpFunction %void None %1
+
+; CHECK: OpLabel
+          %2 = OpLabel
+; CHECK-NEXT: OpSelectionMerge [[merge:%\d+]]
+               OpSelectionMerge %8 None
+; CHECK-NEXT: OpSwitch %uint_1 [[default:%\d+]] 0 [[case_0:%\d+]] 1 [[case_1:%\d+]] 2 [[case_2:%\d+]]
+               OpSwitch %uint_1 %8 0 %4 1 %5 2 %8
+
+; CHECK-NEXT: [[case_2]] = OpLabel
+; CHECK-NEXT: OpBeginInvocationInterlockEXT
+; CHECK-NEXT: OpBranch [[merge]]
+
+; CHECK-NEXT: [[default]] = OpLabel
+; CHECK-NEXT: OpBeginInvocationInterlockEXT
+; CHECK-NEXT: OpBranch [[merge]]
+
+; CHECK-NEXT: [[case_0]] = OpLabel
+          %4 = OpLabel
+; CHECK-NEXT: OpBeginInvocationInterlockEXT
+; CHECK-NOT: OpEndInvocationInterlockEXT
+               OpBeginInvocationInterlockEXT
+               OpEndInvocationInterlockEXT
+; CHECK-NEXT: OpNoLine
+               OpNoLine
+; CHECK-NEXT: OpBranch [[merge]]
+               OpBranch %8
+
+; CHECK-NEXT: [[case_1]] = OpLabel
+          %5 = OpLabel
+; CHECK-NEXT: OpBeginInvocationInterlockEXT
+; CHECK-NOT: OpEndInvocationInterlockEXT
+               OpBeginInvocationInterlockEXT
+               OpEndInvocationInterlockEXT
+; CHECK-NEXT: OpNoLine
+               OpNoLine
+; CHECK-NEXT: OpNoLine
+               OpNoLine
+; CHECK-NEXT: OpBranch [[merge]]
+               OpBranch %8
+
+; CHECK-NEXT: [[merge]] = OpLabel
+          %8 = OpLabel
+; CHECK-NOT: OpBeginInvocationInterlockEXT
+               OpBeginInvocationInterlockEXT
+; CHECK-NEXT: OpEndInvocationInterlockEXT
+               OpEndInvocationInterlockEXT
+               OpReturn
+               OpFunctionEnd
+  )";
+  SetTargetEnv(SPV_ENV_VULKAN_1_3);
+  const auto result = SinglePassRunAndMatch<InvocationInterlockPlacementPass>(
+      kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
+}
+
+}  // namespace
+}  // namespace opt
+}  // namespace spvtools
diff --git a/test/opt/ir_builder.cpp b/test/opt/ir_builder.cpp
index cb234e0..f0cfc18 100644
--- a/test/opt/ir_builder.cpp
+++ b/test/opt/ir_builder.cpp
@@ -12,18 +12,16 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <algorithm>
+#include "source/opt/ir_builder.h"
+
 #include <memory>
-#include <string>
 #include <vector>
 
 #include "effcee/effcee.h"
-#include "gmock/gmock.h"
 #include "gtest/gtest.h"
 #include "source/opt/basic_block.h"
 #include "source/opt/build_module.h"
 #include "source/opt/instruction.h"
-#include "source/opt/ir_builder.h"
 #include "source/opt/type_manager.h"
 #include "spirv-tools/libspirv.hpp"
 
@@ -201,7 +199,7 @@
     // TODO(1841): Handle id overflow.
     fn.begin().InsertBefore(std::unique_ptr<BasicBlock>(
         new BasicBlock(std::unique_ptr<Instruction>(new Instruction(
-            context.get(), SpvOpLabel, 0, context->TakeNextId(), {})))));
+            context.get(), spv::Op::OpLabel, 0, context->TakeNextId(), {})))));
     BasicBlock& bb_true = *fn.begin();
     {
       InstructionBuilder builder(context.get(), &*bb_true.begin());
@@ -211,7 +209,7 @@
     // TODO(1841): Handle id overflow.
     fn.begin().InsertBefore(std::unique_ptr<BasicBlock>(
         new BasicBlock(std::unique_ptr<Instruction>(new Instruction(
-            context.get(), SpvOpLabel, 0, context->TakeNextId(), {})))));
+            context.get(), spv::Op::OpLabel, 0, context->TakeNextId(), {})))));
     BasicBlock& bb_cond = *fn.begin();
 
     InstructionBuilder builder(context.get(), &bb_cond);
diff --git a/test/opt/ir_context_test.cpp b/test/opt/ir_context_test.cpp
index dcae7cf..621fe8c 100644
--- a/test/opt/ir_context_test.cpp
+++ b/test/opt/ir_context_test.cpp
@@ -16,7 +16,6 @@
 
 #include <algorithm>
 #include <memory>
-#include <string>
 #include <utility>
 
 #include "OpenCLDebugInfo100.h"
@@ -229,12 +228,12 @@
 
   // Make sure all of the name are removed.
   for (auto& inst : context->debugs2()) {
-    EXPECT_EQ(inst.opcode(), SpvOpNop);
+    EXPECT_EQ(inst.opcode(), spv::Op::OpNop);
   }
 
   // Make sure all of the decorations are removed.
   for (auto& inst : context->annotations()) {
-    EXPECT_EQ(inst.opcode(), SpvOpNop);
+    EXPECT_EQ(inst.opcode(), spv::Op::OpNop);
   }
 }
 
@@ -276,17 +275,17 @@
 
   // Check the OpDecorate instruction
   auto inst = context->annotation_begin();
-  EXPECT_EQ(inst->opcode(), SpvOpDecorate);
+  EXPECT_EQ(inst->opcode(), spv::Op::OpDecorate);
   EXPECT_EQ(inst->GetSingleWordInOperand(0), 3);
 
   // Check the OpDecorationGroup Instruction
   ++inst;
-  EXPECT_EQ(inst->opcode(), SpvOpDecorationGroup);
+  EXPECT_EQ(inst->opcode(), spv::Op::OpDecorationGroup);
   EXPECT_EQ(inst->result_id(), 3);
 
   // Check that %5 is no longer part of the group.
   ++inst;
-  EXPECT_EQ(inst->opcode(), SpvOpGroupDecorate);
+  EXPECT_EQ(inst->opcode(), spv::Op::OpGroupDecorate);
   EXPECT_EQ(inst->NumInOperands(), 2);
   EXPECT_EQ(inst->GetSingleWordInOperand(0), 3);
   EXPECT_EQ(inst->GetSingleWordInOperand(1), 4);
@@ -340,12 +339,12 @@
 
   // Check the OpDecorationGroup Instruction
   auto inst = context->annotation_begin();
-  EXPECT_EQ(inst->opcode(), SpvOpDecorationGroup);
+  EXPECT_EQ(inst->opcode(), spv::Op::OpDecorationGroup);
   EXPECT_EQ(inst->result_id(), 3);
 
   // Check that %5 is no longer part of the group.
   ++inst;
-  EXPECT_EQ(inst->opcode(), SpvOpGroupDecorate);
+  EXPECT_EQ(inst->opcode(), spv::Op::OpGroupDecorate);
   EXPECT_EQ(inst->NumInOperands(), 2);
   EXPECT_EQ(inst->GetSingleWordInOperand(0), 3);
   EXPECT_EQ(inst->GetSingleWordInOperand(1), 4);
@@ -1149,6 +1148,339 @@
             20);
 }
 
+struct TargetEnvCompareTestData {
+  spv_target_env later_env, earlier_env;
+};
+
+using TargetEnvCompareTest = ::testing::TestWithParam<TargetEnvCompareTestData>;
+
+TEST_P(TargetEnvCompareTest, Case) {
+  // If new environments are added, then we must update the list of tests.
+  ASSERT_EQ(SPV_ENV_VULKAN_1_3 + 1, SPV_ENV_MAX);
+
+  const auto& tc = GetParam();
+
+  std::unique_ptr<Module> module(new Module());
+  IRContext localContext(tc.later_env, std::move(module),
+                         spvtools::MessageConsumer());
+  EXPECT_TRUE(localContext.IsTargetEnvAtLeast(tc.earlier_env));
+
+  if (tc.earlier_env != tc.later_env) {
+    std::unique_ptr<Module> module(new Module());
+    IRContext localContext(tc.earlier_env, std::move(module),
+                           spvtools::MessageConsumer());
+    EXPECT_FALSE(localContext.IsTargetEnvAtLeast(tc.later_env));
+  }
+}
+
+TEST_F(IRContextTest, ReturnsTrueWhenExtensionIsRemoved) {
+  const std::string text = R"(
+               OpCapability Shader
+               OpExtension "SPV_KHR_shader_clock"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "main"
+       %void = OpTypeVoid
+          %6 = OpTypeFunction %void
+          %1 = OpFunction %void None %6
+          %9 = OpLabel
+               OpReturn
+               OpFunctionEnd)";
+
+  std::unique_ptr<IRContext> ctx =
+      BuildModule(SPV_ENV_UNIVERSAL_1_6, nullptr, text,
+                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  EXPECT_TRUE(ctx->get_feature_mgr()->HasExtension(kSPV_KHR_shader_clock));
+  EXPECT_EQ(std::distance(ctx->module()->extension_begin(),
+                          ctx->module()->extension_end()),
+            1);
+
+  EXPECT_TRUE(ctx->RemoveExtension(kSPV_KHR_shader_clock));
+
+  EXPECT_FALSE(ctx->get_feature_mgr()->HasExtension(kSPV_KHR_shader_clock));
+  EXPECT_EQ(std::distance(ctx->module()->extension_begin(),
+                          ctx->module()->extension_end()),
+            0);
+}
+
+TEST_F(IRContextTest, ReturnsFalseWhenExtensionIsNotRemoved) {
+  const std::string text = R"(
+               OpCapability Shader
+               OpExtension "SPV_KHR_device_group"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "main"
+       %void = OpTypeVoid
+          %6 = OpTypeFunction %void
+          %1 = OpFunction %void None %6
+          %9 = OpLabel
+               OpReturn
+               OpFunctionEnd)";
+
+  std::unique_ptr<IRContext> ctx =
+      BuildModule(SPV_ENV_UNIVERSAL_1_6, nullptr, text,
+                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  EXPECT_TRUE(ctx->get_feature_mgr()->HasExtension(kSPV_KHR_device_group));
+  EXPECT_EQ(std::distance(ctx->module()->extension_begin(),
+                          ctx->module()->extension_end()),
+            1);
+
+  EXPECT_FALSE(ctx->RemoveExtension(kSPV_KHR_shader_clock));
+
+  EXPECT_TRUE(ctx->get_feature_mgr()->HasExtension(kSPV_KHR_device_group));
+  EXPECT_EQ(std::distance(ctx->module()->extension_begin(),
+                          ctx->module()->extension_end()),
+            1);
+}
+
+TEST_F(IRContextTest, RemovesExtensionIfLast) {
+  const std::string text = R"(
+               OpCapability Shader
+               OpExtension "SPV_KHR_device_group"
+               OpExtension "SPV_KHR_shader_clock"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "main"
+       %void = OpTypeVoid
+          %6 = OpTypeFunction %void
+          %1 = OpFunction %void None %6
+          %9 = OpLabel
+               OpReturn
+               OpFunctionEnd)";
+
+  std::unique_ptr<IRContext> ctx =
+      BuildModule(SPV_ENV_UNIVERSAL_1_6, nullptr, text,
+                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  EXPECT_TRUE(ctx->get_feature_mgr()->HasExtension(kSPV_KHR_device_group));
+  EXPECT_TRUE(ctx->get_feature_mgr()->HasExtension(kSPV_KHR_shader_clock));
+  EXPECT_EQ(std::distance(ctx->module()->extension_begin(),
+                          ctx->module()->extension_end()),
+            2);
+
+  EXPECT_TRUE(ctx->RemoveExtension(kSPV_KHR_shader_clock));
+
+  EXPECT_TRUE(ctx->get_feature_mgr()->HasExtension(kSPV_KHR_device_group));
+  EXPECT_FALSE(ctx->get_feature_mgr()->HasExtension(kSPV_KHR_shader_clock));
+  EXPECT_EQ(std::distance(ctx->module()->extension_begin(),
+                          ctx->module()->extension_end()),
+            1);
+}
+
+TEST_F(IRContextTest, RemovesExtensionIfFirst) {
+  const std::string text = R"(
+               OpCapability Shader
+               OpExtension "SPV_KHR_shader_clock"
+               OpExtension "SPV_KHR_device_group"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "main"
+       %void = OpTypeVoid
+          %6 = OpTypeFunction %void
+          %1 = OpFunction %void None %6
+          %9 = OpLabel
+               OpReturn
+               OpFunctionEnd)";
+  std::unique_ptr<IRContext> ctx =
+      BuildModule(SPV_ENV_UNIVERSAL_1_6, nullptr, text,
+                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  EXPECT_TRUE(ctx->get_feature_mgr()->HasExtension(kSPV_KHR_device_group));
+  EXPECT_TRUE(ctx->get_feature_mgr()->HasExtension(kSPV_KHR_shader_clock));
+  EXPECT_EQ(std::distance(ctx->module()->extension_begin(),
+                          ctx->module()->extension_end()),
+            2);
+
+  EXPECT_TRUE(ctx->RemoveExtension(kSPV_KHR_shader_clock));
+
+  EXPECT_TRUE(ctx->get_feature_mgr()->HasExtension(kSPV_KHR_device_group));
+  EXPECT_FALSE(ctx->get_feature_mgr()->HasExtension(kSPV_KHR_shader_clock));
+  EXPECT_EQ(std::distance(ctx->module()->extension_begin(),
+                          ctx->module()->extension_end()),
+            1);
+}
+
+TEST_F(IRContextTest, RemovesMultipleExtensions) {
+  const std::string text = R"(
+               OpCapability Shader
+               OpExtension "SPV_KHR_shader_clock"
+               OpExtension "SPV_KHR_shader_clock"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "main"
+       %void = OpTypeVoid
+          %6 = OpTypeFunction %void
+          %1 = OpFunction %void None %6
+          %9 = OpLabel
+               OpReturn
+               OpFunctionEnd)";
+
+  std::unique_ptr<IRContext> ctx =
+      BuildModule(SPV_ENV_UNIVERSAL_1_6, nullptr, text,
+                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  EXPECT_TRUE(ctx->get_feature_mgr()->HasExtension(kSPV_KHR_shader_clock));
+  EXPECT_EQ(std::distance(ctx->module()->extension_begin(),
+                          ctx->module()->extension_end()),
+            2);
+
+  EXPECT_TRUE(ctx->RemoveExtension(kSPV_KHR_shader_clock));
+
+  EXPECT_FALSE(ctx->get_feature_mgr()->HasExtension(kSPV_KHR_shader_clock));
+  EXPECT_EQ(std::distance(ctx->module()->extension_begin(),
+                          ctx->module()->extension_end()),
+            0);
+}
+
+TEST_F(IRContextTest, ReturnsTrueWhenCapabilityIsRemoved) {
+  const std::string text = R"(
+               OpCapability Shader
+               OpCapability ShaderClockKHR
+               OpExtension "SPV_KHR_shader_clock"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "main"
+       %void = OpTypeVoid
+          %6 = OpTypeFunction %void
+          %1 = OpFunction %void None %6
+          %9 = OpLabel
+               OpReturn
+               OpFunctionEnd)";
+
+  std::unique_ptr<IRContext> ctx =
+      BuildModule(SPV_ENV_UNIVERSAL_1_6, nullptr, text,
+                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  EXPECT_TRUE(
+      ctx->get_feature_mgr()->HasCapability(spv::Capability::ShaderClockKHR));
+  EXPECT_EQ(std::distance(ctx->module()->capability_begin(),
+                          ctx->module()->capability_end()),
+            2);
+
+  EXPECT_TRUE(ctx->RemoveCapability(spv::Capability::ShaderClockKHR));
+
+  EXPECT_FALSE(
+      ctx->get_feature_mgr()->HasCapability(spv::Capability::ShaderClockKHR));
+  EXPECT_EQ(std::distance(ctx->module()->capability_begin(),
+                          ctx->module()->capability_end()),
+            1);
+}
+
+TEST_F(IRContextTest, ReturnsFalseWhenCapabilityIsNotRemoved) {
+  const std::string text = R"(
+               OpCapability Shader
+               OpCapability DeviceGroup
+               OpExtension "SPV_KHR_device_group"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "main"
+       %void = OpTypeVoid
+          %6 = OpTypeFunction %void
+          %1 = OpFunction %void None %6
+          %9 = OpLabel
+               OpReturn
+               OpFunctionEnd)";
+
+  std::unique_ptr<IRContext> ctx =
+      BuildModule(SPV_ENV_UNIVERSAL_1_6, nullptr, text,
+                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  EXPECT_TRUE(
+      ctx->get_feature_mgr()->HasCapability(spv::Capability::DeviceGroup));
+  EXPECT_EQ(std::distance(ctx->module()->capability_begin(),
+                          ctx->module()->capability_end()),
+            2);
+
+  EXPECT_FALSE(ctx->RemoveCapability(spv::Capability::ShaderClockKHR));
+
+  EXPECT_TRUE(
+      ctx->get_feature_mgr()->HasCapability(spv::Capability::DeviceGroup));
+  EXPECT_EQ(std::distance(ctx->module()->capability_begin(),
+                          ctx->module()->capability_end()),
+            2);
+}
+
+TEST_F(IRContextTest, RemovesMultipleCapabilities) {
+  const std::string text = R"(
+               OpCapability Shader
+               OpCapability DeviceGroup
+               OpCapability DeviceGroup
+               OpExtension "SPV_KHR_device_group"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "main"
+       %void = OpTypeVoid
+          %6 = OpTypeFunction %void
+          %1 = OpFunction %void None %6
+          %9 = OpLabel
+               OpReturn
+               OpFunctionEnd)";
+
+  std::unique_ptr<IRContext> ctx =
+      BuildModule(SPV_ENV_UNIVERSAL_1_6, nullptr, text,
+                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  EXPECT_TRUE(
+      ctx->get_feature_mgr()->HasCapability(spv::Capability::DeviceGroup));
+  EXPECT_EQ(std::distance(ctx->module()->capability_begin(),
+                          ctx->module()->capability_end()),
+            3);
+
+  EXPECT_TRUE(ctx->RemoveCapability(spv::Capability::DeviceGroup));
+
+  EXPECT_FALSE(
+      ctx->get_feature_mgr()->HasCapability(spv::Capability::DeviceGroup));
+  EXPECT_EQ(std::distance(ctx->module()->capability_begin(),
+                          ctx->module()->capability_end()),
+            1);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    TestCase, TargetEnvCompareTest,
+    ::testing::Values(
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_0},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_1, SPV_ENV_UNIVERSAL_1_0},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_0},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_3, SPV_ENV_UNIVERSAL_1_0},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_4, SPV_ENV_UNIVERSAL_1_0},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_5, SPV_ENV_UNIVERSAL_1_0},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_6, SPV_ENV_UNIVERSAL_1_0},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_1, SPV_ENV_UNIVERSAL_1_1},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_1},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_3, SPV_ENV_UNIVERSAL_1_1},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_4, SPV_ENV_UNIVERSAL_1_1},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_5, SPV_ENV_UNIVERSAL_1_1},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_6, SPV_ENV_UNIVERSAL_1_1},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_2},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_3, SPV_ENV_UNIVERSAL_1_2},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_4, SPV_ENV_UNIVERSAL_1_2},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_5, SPV_ENV_UNIVERSAL_1_2},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_6, SPV_ENV_UNIVERSAL_1_2},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_3, SPV_ENV_UNIVERSAL_1_3},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_4, SPV_ENV_UNIVERSAL_1_3},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_5, SPV_ENV_UNIVERSAL_1_3},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_6, SPV_ENV_UNIVERSAL_1_3},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_4, SPV_ENV_UNIVERSAL_1_4},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_5, SPV_ENV_UNIVERSAL_1_4},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_6, SPV_ENV_UNIVERSAL_1_4},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_5, SPV_ENV_UNIVERSAL_1_5},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_6, SPV_ENV_UNIVERSAL_1_5},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_6, SPV_ENV_UNIVERSAL_1_6},
+        TargetEnvCompareTestData{SPV_ENV_VULKAN_1_0, SPV_ENV_UNIVERSAL_1_0},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_1, SPV_ENV_VULKAN_1_0},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_2, SPV_ENV_VULKAN_1_0},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_3, SPV_ENV_VULKAN_1_0},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_4, SPV_ENV_VULKAN_1_0},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_5, SPV_ENV_VULKAN_1_0},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_6, SPV_ENV_VULKAN_1_0},
+        TargetEnvCompareTestData{SPV_ENV_VULKAN_1_1, SPV_ENV_UNIVERSAL_1_0},
+        TargetEnvCompareTestData{SPV_ENV_VULKAN_1_1, SPV_ENV_UNIVERSAL_1_1},
+        TargetEnvCompareTestData{SPV_ENV_VULKAN_1_1, SPV_ENV_UNIVERSAL_1_2},
+        TargetEnvCompareTestData{SPV_ENV_VULKAN_1_1, SPV_ENV_UNIVERSAL_1_3},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_4, SPV_ENV_VULKAN_1_1},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_5, SPV_ENV_VULKAN_1_1},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_6, SPV_ENV_VULKAN_1_1},
+        TargetEnvCompareTestData{SPV_ENV_VULKAN_1_2, SPV_ENV_UNIVERSAL_1_0},
+        TargetEnvCompareTestData{SPV_ENV_VULKAN_1_2, SPV_ENV_UNIVERSAL_1_1},
+        TargetEnvCompareTestData{SPV_ENV_VULKAN_1_2, SPV_ENV_UNIVERSAL_1_2},
+        TargetEnvCompareTestData{SPV_ENV_VULKAN_1_2, SPV_ENV_UNIVERSAL_1_3},
+        TargetEnvCompareTestData{SPV_ENV_VULKAN_1_2, SPV_ENV_UNIVERSAL_1_4},
+        TargetEnvCompareTestData{SPV_ENV_VULKAN_1_2, SPV_ENV_UNIVERSAL_1_5},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_6, SPV_ENV_VULKAN_1_2},
+        TargetEnvCompareTestData{SPV_ENV_VULKAN_1_3, SPV_ENV_UNIVERSAL_1_0},
+        TargetEnvCompareTestData{SPV_ENV_VULKAN_1_3, SPV_ENV_UNIVERSAL_1_1},
+        TargetEnvCompareTestData{SPV_ENV_VULKAN_1_3, SPV_ENV_UNIVERSAL_1_2},
+        TargetEnvCompareTestData{SPV_ENV_VULKAN_1_3, SPV_ENV_UNIVERSAL_1_3},
+        TargetEnvCompareTestData{SPV_ENV_VULKAN_1_3, SPV_ENV_UNIVERSAL_1_4},
+        TargetEnvCompareTestData{SPV_ENV_VULKAN_1_3, SPV_ENV_UNIVERSAL_1_5},
+        TargetEnvCompareTestData{SPV_ENV_VULKAN_1_3, SPV_ENV_UNIVERSAL_1_6}));
+
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools
diff --git a/test/opt/ir_loader_test.cpp b/test/opt/ir_loader_test.cpp
index ccdd032..769a25d 100644
--- a/test/opt/ir_loader_test.cpp
+++ b/test/opt/ir_loader_test.cpp
@@ -14,7 +14,6 @@
 
 #include <algorithm>
 #include <memory>
-#include <string>
 #include <unordered_set>
 #include <utility>
 #include <vector>
@@ -244,10 +243,10 @@
     auto& lines = def_use_mgr->GetDef(check.id)->dbg_line_insts();
     for (uint32_t i = 0; i < check.line_numbers.size(); ++i) {
       if (check.line_numbers[i] == kNoLine) {
-        EXPECT_EQ(lines[i].opcode(), SpvOpNoLine);
+        EXPECT_EQ(lines[i].opcode(), spv::Op::OpNoLine);
         continue;
       }
-      EXPECT_EQ(lines[i].opcode(), SpvOpLine);
+      EXPECT_EQ(lines[i].opcode(), spv::Op::OpLine);
       EXPECT_EQ(lines[i].GetSingleWordOperand(kOpLineOperandLineIndex),
                 check.line_numbers[i]);
     }
@@ -286,9 +285,10 @@
   spvtools::opt::analysis::DefUseManager* def_use_mgr =
       context->get_def_use_mgr();
 
-  std::vector<SpvOp> opcodes;
+  std::vector<spv::Op> opcodes;
   for (auto* inst = def_use_mgr->GetDef(1);
-       inst && (inst->opcode() != SpvOpFunctionEnd); inst = inst->NextNode()) {
+       inst && (inst->opcode() != spv::Op::OpFunctionEnd);
+       inst = inst->NextNode()) {
     inst->ForEachInst(
         [&opcodes](spvtools::opt::Instruction* sub_inst) {
           opcodes.push_back(sub_inst->opcode());
@@ -296,9 +296,9 @@
         true);
   }
 
-  EXPECT_THAT(opcodes,
-              ContainerEq(std::vector<SpvOp>{SpvOpFAdd, SpvOpLine, SpvOpFMul,
-                                             SpvOpFSub, SpvOpReturn}));
+  EXPECT_THAT(opcodes, ContainerEq(std::vector<spv::Op>{
+                           spv::Op::OpFAdd, spv::Op::OpLine, spv::Op::OpFMul,
+                           spv::Op::OpFSub, spv::Op::OpReturn}));
 }
 
 TEST(IrBuilder, BuildModule_WithExtraLines_IsDefault) {
@@ -333,9 +333,10 @@
   spvtools::opt::analysis::DefUseManager* def_use_mgr =
       context->get_def_use_mgr();
 
-  std::vector<SpvOp> opcodes;
+  std::vector<spv::Op> opcodes;
   for (auto* inst = def_use_mgr->GetDef(1);
-       inst && (inst->opcode() != SpvOpFunctionEnd); inst = inst->NextNode()) {
+       inst && (inst->opcode() != spv::Op::OpFunctionEnd);
+       inst = inst->NextNode()) {
     inst->ForEachInst(
         [&opcodes](spvtools::opt::Instruction* sub_inst) {
           opcodes.push_back(sub_inst->opcode());
@@ -343,9 +344,10 @@
         true);
   }
 
-  EXPECT_THAT(opcodes, ContainerEq(std::vector<SpvOp>{
-                           SpvOpFAdd, SpvOpLine, SpvOpFMul, SpvOpLine,
-                           SpvOpFSub, SpvOpLine, SpvOpReturn}));
+  EXPECT_THAT(opcodes, ContainerEq(std::vector<spv::Op>{
+                           spv::Op::OpFAdd, spv::Op::OpLine, spv::Op::OpFMul,
+                           spv::Op::OpLine, spv::Op::OpFSub, spv::Op::OpLine,
+                           spv::Op::OpReturn}));
 }
 
 TEST(IrBuilder, ConsumeDebugInfoInst) {
@@ -1297,8 +1299,9 @@
 
   const auto opundef_count = std::count_if(
       context->module()->types_values_begin(),
-      context->module()->types_values_end(),
-      [](const Instruction& inst) { return inst.opcode() == SpvOpUndef; });
+      context->module()->types_values_end(), [](const Instruction& inst) {
+        return inst.opcode() == spv::Op::OpUndef;
+      });
   EXPECT_EQ(3, opundef_count);
 
   std::vector<uint32_t> binary;
diff --git a/test/opt/local_access_chain_convert_test.cpp b/test/opt/local_access_chain_convert_test.cpp
index 07fb537..b35f3a3 100644
--- a/test/opt/local_access_chain_convert_test.cpp
+++ b/test/opt/local_access_chain_convert_test.cpp
@@ -1348,6 +1348,56 @@
                                                      true);
 }
 
+TEST_F(LocalAccessChainConvertTest, VkMemoryModelTest) {
+  const std::string text =
+      R"(
+; CHECK: OpCapability Shader
+; CHECK: OpCapability VulkanMemoryModel
+; CHECK: OpExtension "SPV_KHR_vulkan_memory_model"
+               OpCapability Shader
+               OpCapability VulkanMemoryModel
+               OpExtension "SPV_KHR_vulkan_memory_model"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical Vulkan
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpSource GLSL 450
+               OpSourceExtension "GL_GOOGLE_cpp_style_line_directive"
+               OpSourceExtension "GL_GOOGLE_include_directive"
+               OpName %main "main"
+               OpName %a "a"
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+       %uint = OpTypeInt 32 0
+     %uint_0 = OpConstant %uint 0
+%_ptr_Function_float = OpTypePointer Function %float
+    %float_1 = OpConstant %float 1
+; CHECK: OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[a:%\w+]] = OpVariable
+; Make sure the access chains were removed.
+; CHECK: [[ld:%\w+]] = OpLoad {{%\w+}} [[a]]
+; CHECK: [[ex:%\w+]] = OpCompositeExtract {{%\w+}} [[ld]] 0
+; CHECK: [[ld2:%\w+]] = OpLoad {{%\w+}} [[a]]
+; CHECK: [[v:%\w+]] = OpCompositeInsert {{%\w+}} [[ex]] [[ld2]] 0
+; CHECK: OpStore [[a]] [[v]]
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+          %a = OpVariable %_ptr_Function_v4float Function
+         %13 = OpAccessChain %_ptr_Function_float %a %uint_0
+         %14 = OpLoad %float %13
+         %17 = OpAccessChain %_ptr_Function_float %a %uint_0
+               OpStore %17 %14
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<LocalAccessChainConvertPass>(text, false);
+}
+
 // TODO(greg-lunarg): Add tests to verify handling of these cases:
 //
 //    Assorted vector and matrix types
diff --git a/test/opt/local_redundancy_elimination_test.cpp b/test/opt/local_redundancy_elimination_test.cpp
index 291e1bc..01f7666 100644
--- a/test/opt/local_redundancy_elimination_test.cpp
+++ b/test/opt/local_redundancy_elimination_test.cpp
@@ -15,9 +15,7 @@
 #include <string>
 
 #include "gmock/gmock.h"
-#include "source/opt/build_module.h"
 #include "source/opt/value_number_table.h"
-#include "test/opt/assembly_builder.h"
 #include "test/opt/pass_fixture.h"
 #include "test/opt/pass_utils.h"
 
diff --git a/test/opt/local_single_block_elim.cpp b/test/opt/local_single_block_elim.cpp
index 28b8a07..7d19c22 100644
--- a/test/opt/local_single_block_elim.cpp
+++ b/test/opt/local_single_block_elim.cpp
@@ -1502,6 +1502,49 @@
   SinglePassRunAndMatch<LocalSingleBlockLoadStoreElimPass>(text, false);
 }
 
+TEST_F(LocalSingleBlockLoadStoreElimTest, VkMemoryModelTest) {
+  const std::string text =
+      R"(
+; CHECK: OpCapability Shader
+; CHECK: OpCapability VulkanMemoryModel
+; CHECK: OpExtension "SPV_KHR_vulkan_memory_model"
+               OpCapability Shader
+               OpCapability VulkanMemoryModel
+               OpExtension "SPV_KHR_vulkan_memory_model"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical Vulkan
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpSource GLSL 450
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+        %int = OpTypeInt 32 1
+%_ptr_Function_int = OpTypePointer Function %int
+      %int_0 = OpConstant %int 0
+      %int_1 = OpConstant %int 1
+       %bool = OpTypeBool
+      %false = OpConstantFalse %bool
+; CHECK: OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[a:%\w+]] = OpVariable
+; CHECK-NEXT: [[b:%\w+]] = OpVariable
+; CHECK: OpStore [[a]] [[v:%\w+]]
+; CHECK-NOT: OpLoad %int [[a]]
+; CHECK: OpStore [[b]] [[v]]
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+          %a = OpVariable %_ptr_Function_int Function
+          %b = OpVariable %_ptr_Function_int Function
+               OpStore %a %int_0
+         %16 = OpLoad %int %a
+               OpStore %b %16
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<LocalSingleBlockLoadStoreElimPass>(text, false);
+}
+
 // TODO(greg-lunarg): Add tests to verify handling of these cases:
 //
 //    Other target variable types
diff --git a/test/opt/local_single_store_elim_test.cpp b/test/opt/local_single_store_elim_test.cpp
index 8f43a11..ffe352e 100644
--- a/test/opt/local_single_store_elim_test.cpp
+++ b/test/opt/local_single_store_elim_test.cpp
@@ -1750,6 +1750,58 @@
   SinglePassRunAndMatch<LocalSingleStoreElimPass>(text, false);
 }
 
+TEST_F(LocalSingleStoreElimTest, VkMemoryModelTest) {
+  const std::string text =
+      R"(
+; CHECK: OpCapability Shader
+; CHECK: OpCapability VulkanMemoryModel
+; CHECK: OpExtension "SPV_KHR_vulkan_memory_model"
+               OpCapability Shader
+               OpCapability VulkanMemoryModel
+               OpExtension "SPV_KHR_vulkan_memory_model"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical Vulkan
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpSource GLSL 450
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+        %int = OpTypeInt 32 1
+%_ptr_Function_int = OpTypePointer Function %int
+      %int_0 = OpConstant %int 0
+      %int_1 = OpConstant %int 1
+       %bool = OpTypeBool
+      %false = OpConstantFalse %bool
+; CHECK: OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[a:%\w+]] = OpVariable
+; CHECK-NEXT: [[b:%\w+]] = OpVariable
+; CHECK: OpStore [[a]] [[v:%\w+]]
+; CHECK: OpStore [[b]]
+; Make sure the load was removed.
+; CHECK: OpLabel
+; CHECK-NOT: OpLoad %int [[a]]
+; CHECK: OpStore [[b]] [[v]]
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+          %a = OpVariable %_ptr_Function_int Function
+          %b = OpVariable %_ptr_Function_int Function
+               OpStore %a %int_0
+               OpStore %b %int_1
+               OpSelectionMerge %15 None
+               OpBranchConditional %false %14 %15
+         %14 = OpLabel
+         %16 = OpLoad %int %a
+               OpStore %b %16
+               OpBranch %15
+         %15 = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<LocalSingleStoreElimPass>(text, false);
+}
+
 // TODO(greg-lunarg): Add tests to verify handling of these cases:
 //
 //    Other types
diff --git a/test/opt/loop_optimizations/CMakeLists.txt b/test/opt/loop_optimizations/CMakeLists.txt
index e362078..6e20f72 100644
--- a/test/opt/loop_optimizations/CMakeLists.txt
+++ b/test/opt/loop_optimizations/CMakeLists.txt
@@ -21,6 +21,7 @@
        fusion_illegal.cpp
        fusion_legal.cpp
        fusion_pass.cpp
+       hoist_access_chains.cpp
        hoist_all_loop_types.cpp
        hoist_double_nested_loops.cpp
        hoist_from_independent_loops.cpp
diff --git a/test/opt/loop_optimizations/dependence_analysis.cpp b/test/opt/loop_optimizations/dependence_analysis.cpp
index 8aeb20a..40520f5 100644
--- a/test/opt/loop_optimizations/dependence_analysis.cpp
+++ b/test/opt/loop_optimizations/dependence_analysis.cpp
@@ -14,17 +14,11 @@
 
 #include <memory>
 #include <set>
-#include <string>
-#include <unordered_set>
 #include <utility>
 #include <vector>
 
-#include "gmock/gmock.h"
-#include "source/opt/iterator.h"
 #include "source/opt/loop_dependence.h"
 #include "source/opt/loop_descriptor.h"
-#include "source/opt/pass.h"
-#include "source/opt/tree_iterator.h"
 #include "test/opt//assembly_builder.h"
 #include "test/opt//function_utils.h"
 #include "test/opt//pass_fixture.h"
@@ -135,7 +129,7 @@
   const Instruction* store[4];
   int stores_found = 0;
   for (const Instruction& inst : *spvtest::GetBasicBlock(f, 13)) {
-    if (inst.opcode() == SpvOp::SpvOpStore) {
+    if (inst.opcode() == spv::Op::OpStore) {
       store[stores_found] = &inst;
       ++stores_found;
     }
@@ -292,7 +286,7 @@
   const Instruction* store[4];
   int stores_found = 0;
   for (const Instruction& inst : *spvtest::GetBasicBlock(f, 22)) {
-    if (inst.opcode() == SpvOp::SpvOpStore) {
+    if (inst.opcode() == spv::Op::OpStore) {
       store[stores_found] = &inst;
       ++stores_found;
     }
@@ -528,7 +522,7 @@
     const Instruction* store[4];
     int stores_found = 0;
     for (const Instruction& inst : *spvtest::GetBasicBlock(f, 17)) {
-      if (inst.opcode() == SpvOp::SpvOpStore) {
+      if (inst.opcode() == spv::Op::OpStore) {
         store[stores_found] = &inst;
         ++stores_found;
       }
@@ -601,7 +595,7 @@
     const Instruction* store[4];
     int stores_found = 0;
     for (const Instruction& inst : *spvtest::GetBasicBlock(f, 68)) {
-      if (inst.opcode() == SpvOp::SpvOpStore) {
+      if (inst.opcode() == spv::Op::OpStore) {
         store[stores_found] = &inst;
         ++stores_found;
       }
@@ -922,7 +916,7 @@
     const Instruction* store[4];
     int stores_found = 0;
     for (const Instruction& inst : *spvtest::GetBasicBlock(f, 29)) {
-      if (inst.opcode() == SpvOp::SpvOpStore) {
+      if (inst.opcode() == spv::Op::OpStore) {
         store[stores_found] = &inst;
         ++stores_found;
       }
@@ -978,7 +972,7 @@
     const Instruction* store[4];
     int stores_found = 0;
     for (const Instruction& inst : *spvtest::GetBasicBlock(f, 114)) {
-      if (inst.opcode() == SpvOp::SpvOpStore) {
+      if (inst.opcode() == spv::Op::OpStore) {
         store[stores_found] = &inst;
         ++stores_found;
       }
@@ -1420,7 +1414,7 @@
 
     const Instruction* store = nullptr;
     for (const Instruction& inst : *spvtest::GetBasicBlock(f, 29)) {
-      if (inst.opcode() == SpvOp::SpvOpStore) {
+      if (inst.opcode() == spv::Op::OpStore) {
         store = &inst;
       }
     }
@@ -1441,7 +1435,7 @@
 
     const Instruction* store = nullptr;
     for (const Instruction& inst : *spvtest::GetBasicBlock(f, 54)) {
-      if (inst.opcode() == SpvOp::SpvOpStore) {
+      if (inst.opcode() == spv::Op::OpStore) {
         store = &inst;
       }
     }
@@ -1463,7 +1457,7 @@
 
     const Instruction* store = nullptr;
     for (const Instruction& inst : *spvtest::GetBasicBlock(f, 75)) {
-      if (inst.opcode() == SpvOp::SpvOpStore) {
+      if (inst.opcode() == spv::Op::OpStore) {
         store = &inst;
       }
     }
@@ -1484,7 +1478,7 @@
 
     const Instruction* store = nullptr;
     for (const Instruction& inst : *spvtest::GetBasicBlock(f, 99)) {
-      if (inst.opcode() == SpvOp::SpvOpStore) {
+      if (inst.opcode() == spv::Op::OpStore) {
         store = &inst;
       }
     }
@@ -1506,7 +1500,7 @@
 
     const Instruction* store = nullptr;
     for (const Instruction& inst : *spvtest::GetBasicBlock(f, 121)) {
-      if (inst.opcode() == SpvOp::SpvOpStore) {
+      if (inst.opcode() == spv::Op::OpStore) {
         store = &inst;
       }
     }
@@ -1527,7 +1521,7 @@
 
     const Instruction* store = nullptr;
     for (const Instruction& inst : *spvtest::GetBasicBlock(f, 142)) {
-      if (inst.opcode() == SpvOp::SpvOpStore) {
+      if (inst.opcode() == spv::Op::OpStore) {
         store = &inst;
       }
     }
@@ -1549,7 +1543,7 @@
 
     const Instruction* store = nullptr;
     for (const Instruction& inst : *spvtest::GetBasicBlock(f, 162)) {
-      if (inst.opcode() == SpvOp::SpvOpStore) {
+      if (inst.opcode() == spv::Op::OpStore) {
         store = &inst;
       }
     }
@@ -1570,7 +1564,7 @@
 
     const Instruction* store = nullptr;
     for (const Instruction& inst : *spvtest::GetBasicBlock(f, 183)) {
-      if (inst.opcode() == SpvOp::SpvOpStore) {
+      if (inst.opcode() == spv::Op::OpStore) {
         store = &inst;
       }
     }
@@ -1846,7 +1840,7 @@
     const Instruction* store[4];
     int stores_found = 0;
     for (const Instruction& inst : *spvtest::GetBasicBlock(f, 19)) {
-      if (inst.opcode() == SpvOp::SpvOpStore) {
+      if (inst.opcode() == spv::Op::OpStore) {
         store[stores_found] = &inst;
         ++stores_found;
       }
@@ -1914,7 +1908,7 @@
     const Instruction* store[4];
     int stores_found = 0;
     for (const Instruction& inst : *spvtest::GetBasicBlock(f, 54)) {
-      if (inst.opcode() == SpvOp::SpvOpStore) {
+      if (inst.opcode() == spv::Op::OpStore) {
         store[stores_found] = &inst;
         ++stores_found;
       }
@@ -1981,7 +1975,7 @@
     const Instruction* store[4];
     int stores_found = 0;
     for (const Instruction& inst : *spvtest::GetBasicBlock(f, 84)) {
-      if (inst.opcode() == SpvOp::SpvOpStore) {
+      if (inst.opcode() == spv::Op::OpStore) {
         store[stores_found] = &inst;
         ++stores_found;
       }
@@ -2049,7 +2043,7 @@
     const Instruction* store[4];
     int stores_found = 0;
     for (const Instruction& inst : *spvtest::GetBasicBlock(f, 111)) {
-      if (inst.opcode() == SpvOp::SpvOpStore) {
+      if (inst.opcode() == spv::Op::OpStore) {
         store[stores_found] = &inst;
         ++stores_found;
       }
@@ -2210,7 +2204,7 @@
   const Instruction* store[6];
   int stores_found = 0;
   for (const Instruction& inst : *spvtest::GetBasicBlock(f, 11)) {
-    if (inst.opcode() == SpvOp::SpvOpStore) {
+    if (inst.opcode() == spv::Op::OpStore) {
       store[stores_found] = &inst;
       ++stores_found;
     }
@@ -2443,7 +2437,7 @@
     const Instruction* store[1];
     int stores_found = 0;
     for (const Instruction& inst : *spvtest::GetBasicBlock(f, 25)) {
-      if (inst.opcode() == SpvOp::SpvOpStore) {
+      if (inst.opcode() == spv::Op::OpStore) {
         store[stores_found] = &inst;
         ++stores_found;
       }
@@ -2479,7 +2473,7 @@
     const Instruction* store[1];
     int stores_found = 0;
     for (const Instruction& inst : *spvtest::GetBasicBlock(f, 56)) {
-      if (inst.opcode() == SpvOp::SpvOpStore) {
+      if (inst.opcode() == spv::Op::OpStore) {
         store[stores_found] = &inst;
         ++stores_found;
       }
@@ -2790,12 +2784,12 @@
   ASSERT_TRUE(spvtest::GetBasicBlock(f, block_id));
 
   for (const Instruction& inst : *spvtest::GetBasicBlock(f, block_id)) {
-    if (inst.opcode() == SpvOp::SpvOpStore) {
+    if (inst.opcode() == spv::Op::OpStore) {
       store[stores_found] = &inst;
       ++stores_found;
     }
 
-    if (inst.opcode() == SpvOp::SpvOpLoad) {
+    if (inst.opcode() == spv::Op::OpLoad) {
       load[loads_found] = &inst;
       ++loads_found;
     }
@@ -3080,12 +3074,12 @@
   ASSERT_TRUE(spvtest::GetBasicBlock(f, block_id));
 
   for (const Instruction& inst : *spvtest::GetBasicBlock(f, block_id)) {
-    if (inst.opcode() == SpvOp::SpvOpStore) {
+    if (inst.opcode() == spv::Op::OpStore) {
       store[stores_found] = &inst;
       ++stores_found;
     }
 
-    if (inst.opcode() == SpvOp::SpvOpLoad) {
+    if (inst.opcode() == spv::Op::OpLoad) {
       load[loads_found] = &inst;
       ++loads_found;
     }
@@ -3461,11 +3455,11 @@
     ASSERT_TRUE(spvtest::GetBasicBlock(f, block_id));
 
     for (const Instruction& inst : *spvtest::GetBasicBlock(f, block_id)) {
-      if (inst.opcode() == SpvOp::SpvOpStore) {
+      if (inst.opcode() == spv::Op::OpStore) {
         store = &inst;
       }
 
-      if (inst.opcode() == SpvOp::SpvOpLoad) {
+      if (inst.opcode() == spv::Op::OpLoad) {
         load = &inst;
       }
     }
@@ -3502,11 +3496,11 @@
     ASSERT_TRUE(spvtest::GetBasicBlock(f, block_id));
 
     for (const Instruction& inst : *spvtest::GetBasicBlock(f, block_id)) {
-      if (inst.opcode() == SpvOp::SpvOpStore) {
+      if (inst.opcode() == spv::Op::OpStore) {
         store = &inst;
       }
 
-      if (inst.opcode() == SpvOp::SpvOpLoad) {
+      if (inst.opcode() == spv::Op::OpLoad) {
         load = &inst;
       }
     }
@@ -3534,11 +3528,11 @@
     ASSERT_TRUE(spvtest::GetBasicBlock(f, block_id));
 
     for (const Instruction& inst : *spvtest::GetBasicBlock(f, block_id)) {
-      if (inst.opcode() == SpvOp::SpvOpStore) {
+      if (inst.opcode() == spv::Op::OpStore) {
         store = &inst;
       }
 
-      if (inst.opcode() == SpvOp::SpvOpLoad) {
+      if (inst.opcode() == spv::Op::OpLoad) {
         load = &inst;
       }
     }
@@ -3569,11 +3563,11 @@
     ASSERT_TRUE(spvtest::GetBasicBlock(f, block_id));
 
     for (const Instruction& inst : *spvtest::GetBasicBlock(f, block_id)) {
-      if (inst.opcode() == SpvOp::SpvOpStore) {
+      if (inst.opcode() == spv::Op::OpStore) {
         store = &inst;
       }
 
-      if (inst.opcode() == SpvOp::SpvOpLoad) {
+      if (inst.opcode() == spv::Op::OpLoad) {
         load = &inst;
       }
     }
@@ -3611,11 +3605,11 @@
     ASSERT_TRUE(spvtest::GetBasicBlock(f, block_id));
 
     for (const Instruction& inst : *spvtest::GetBasicBlock(f, block_id)) {
-      if (inst.opcode() == SpvOp::SpvOpStore) {
+      if (inst.opcode() == spv::Op::OpStore) {
         store = &inst;
       }
 
-      if (inst.opcode() == SpvOp::SpvOpLoad) {
+      if (inst.opcode() == spv::Op::OpLoad) {
         load = &inst;
       }
     }
diff --git a/test/opt/loop_optimizations/dependence_analysis_helpers.cpp b/test/opt/loop_optimizations/dependence_analysis_helpers.cpp
index 715cf54..6206199 100644
--- a/test/opt/loop_optimizations/dependence_analysis_helpers.cpp
+++ b/test/opt/loop_optimizations/dependence_analysis_helpers.cpp
@@ -13,17 +13,11 @@
 // limitations under the License.
 
 #include <memory>
-#include <string>
-#include <unordered_set>
 #include <vector>
 
-#include "gmock/gmock.h"
-#include "source/opt/iterator.h"
 #include "source/opt/loop_dependence.h"
 #include "source/opt/loop_descriptor.h"
-#include "source/opt/pass.h"
 #include "source/opt/scalar_analysis.h"
-#include "source/opt/tree_iterator.h"
 #include "test/opt/assembly_builder.h"
 #include "test/opt/function_utils.h"
 #include "test/opt/pass_fixture.h"
@@ -172,7 +166,7 @@
     const Instruction* store[1] = {nullptr};
     int stores_found = 0;
     for (const Instruction& inst : *spvtest::GetBasicBlock(f, 16)) {
-      if (inst.opcode() == SpvOp::SpvOpStore) {
+      if (inst.opcode() == spv::Op::OpStore) {
         store[stores_found] = &inst;
         ++stores_found;
       }
@@ -199,7 +193,7 @@
     const Instruction* store[1] = {nullptr};
     int stores_found = 0;
     for (const Instruction& inst : *spvtest::GetBasicBlock(f, 47)) {
-      if (inst.opcode() == SpvOp::SpvOpStore) {
+      if (inst.opcode() == spv::Op::OpStore) {
         store[stores_found] = &inst;
         ++stores_found;
       }
@@ -1495,7 +1489,7 @@
     const Instruction* stores[2];
     int stores_found = 0;
     for (const Instruction& inst : *spvtest::GetBasicBlock(f, 30)) {
-      if (inst.opcode() == SpvOp::SpvOpStore) {
+      if (inst.opcode() == spv::Op::OpStore) {
         stores[stores_found] = &inst;
         ++stores_found;
       }
@@ -1576,7 +1570,7 @@
     const Instruction* stores[2];
     int stores_found = 0;
     for (const Instruction& inst : *spvtest::GetBasicBlock(f, 65)) {
-      if (inst.opcode() == SpvOp::SpvOpStore) {
+      if (inst.opcode() == spv::Op::OpStore) {
         stores[stores_found] = &inst;
         ++stores_found;
       }
@@ -1655,7 +1649,7 @@
     const Instruction* stores[2];
     int stores_found = 0;
     for (const Instruction& inst : *spvtest::GetBasicBlock(f, 96)) {
-      if (inst.opcode() == SpvOp::SpvOpStore) {
+      if (inst.opcode() == spv::Op::OpStore) {
         stores[stores_found] = &inst;
         ++stores_found;
       }
@@ -1734,7 +1728,7 @@
     const Instruction* stores[2];
     int stores_found = 0;
     for (const Instruction& inst : *spvtest::GetBasicBlock(f, 126)) {
-      if (inst.opcode() == SpvOp::SpvOpStore) {
+      if (inst.opcode() == spv::Op::OpStore) {
         stores[stores_found] = &inst;
         ++stores_found;
       }
@@ -2067,7 +2061,7 @@
     const Instruction* stores[2];
     int stores_found = 0;
     for (const Instruction& inst : *spvtest::GetBasicBlock(f, 30)) {
-      if (inst.opcode() == SpvOp::SpvOpStore) {
+      if (inst.opcode() == spv::Op::OpStore) {
         stores[stores_found] = &inst;
         ++stores_found;
       }
@@ -2148,7 +2142,7 @@
     const Instruction* stores[2];
     int stores_found = 0;
     for (const Instruction& inst : *spvtest::GetBasicBlock(f, 66)) {
-      if (inst.opcode() == SpvOp::SpvOpStore) {
+      if (inst.opcode() == spv::Op::OpStore) {
         stores[stores_found] = &inst;
         ++stores_found;
       }
@@ -2229,7 +2223,7 @@
     const Instruction* stores[2];
     int stores_found = 0;
     for (const Instruction& inst : *spvtest::GetBasicBlock(f, 96)) {
-      if (inst.opcode() == SpvOp::SpvOpStore) {
+      if (inst.opcode() == spv::Op::OpStore) {
         stores[stores_found] = &inst;
         ++stores_found;
       }
@@ -2310,7 +2304,7 @@
     const Instruction* stores[2];
     int stores_found = 0;
     for (const Instruction& inst : *spvtest::GetBasicBlock(f, 127)) {
-      if (inst.opcode() == SpvOp::SpvOpStore) {
+      if (inst.opcode() == spv::Op::OpStore) {
         stores[stores_found] = &inst;
         ++stores_found;
       }
@@ -2705,7 +2699,7 @@
     const Instruction* stores[2];
     int stores_found = 0;
     for (const Instruction& inst : *spvtest::GetBasicBlock(f, 35)) {
-      if (inst.opcode() == SpvOp::SpvOpStore) {
+      if (inst.opcode() == spv::Op::OpStore) {
         stores[stores_found] = &inst;
         ++stores_found;
       }
@@ -2784,7 +2778,7 @@
     const Instruction* stores[2];
     int stores_found = 0;
     for (const Instruction& inst : *spvtest::GetBasicBlock(f, 90)) {
-      if (inst.opcode() == SpvOp::SpvOpStore) {
+      if (inst.opcode() == spv::Op::OpStore) {
         stores[stores_found] = &inst;
         ++stores_found;
       }
@@ -2863,7 +2857,7 @@
     const Instruction* stores[2];
     int stores_found = 0;
     for (const Instruction& inst : *spvtest::GetBasicBlock(f, 139)) {
-      if (inst.opcode() == SpvOp::SpvOpStore) {
+      if (inst.opcode() == spv::Op::OpStore) {
         stores[stores_found] = &inst;
         ++stores_found;
       }
@@ -2942,7 +2936,7 @@
     const Instruction* stores[2];
     int stores_found = 0;
     for (const Instruction& inst : *spvtest::GetBasicBlock(f, 188)) {
-      if (inst.opcode() == SpvOp::SpvOpStore) {
+      if (inst.opcode() == spv::Op::OpStore) {
         stores[stores_found] = &inst;
         ++stores_found;
       }
diff --git a/test/opt/loop_optimizations/fusion_compatibility.cpp b/test/opt/loop_optimizations/fusion_compatibility.cpp
index cda8576..9acfe8f 100644
--- a/test/opt/loop_optimizations/fusion_compatibility.cpp
+++ b/test/opt/loop_optimizations/fusion_compatibility.cpp
@@ -12,10 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <algorithm>
-#include <iterator>
 #include <memory>
-#include <string>
 #include <vector>
 
 #include "gmock/gmock.h"
diff --git a/test/opt/loop_optimizations/fusion_illegal.cpp b/test/opt/loop_optimizations/fusion_illegal.cpp
index 26d5445..bff416b 100644
--- a/test/opt/loop_optimizations/fusion_illegal.cpp
+++ b/test/opt/loop_optimizations/fusion_illegal.cpp
@@ -12,10 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <algorithm>
-#include <iterator>
 #include <memory>
-#include <string>
 #include <vector>
 
 #include "gmock/gmock.h"
diff --git a/test/opt/loop_optimizations/fusion_legal.cpp b/test/opt/loop_optimizations/fusion_legal.cpp
index 56b0b76..ef7daee 100644
--- a/test/opt/loop_optimizations/fusion_legal.cpp
+++ b/test/opt/loop_optimizations/fusion_legal.cpp
@@ -12,10 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <algorithm>
-#include <iterator>
 #include <memory>
-#include <string>
 #include <vector>
 
 #include "effcee/effcee.h"
diff --git a/test/opt/loop_optimizations/hoist_access_chains.cpp b/test/opt/loop_optimizations/hoist_access_chains.cpp
new file mode 100644
index 0000000..0c688b3
--- /dev/null
+++ b/test/opt/loop_optimizations/hoist_access_chains.cpp
@@ -0,0 +1,157 @@
+// Copyright (c) 2023 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 "source/opt/licm_pass.h"
+#include "test/opt/pass_fixture.h"
+
+namespace spvtools {
+namespace opt {
+namespace {
+
+using PassClassTest = PassTest<::testing::Test>;
+
+/*
+  Tests for the LICM pass to check it handles access chains correctly
+
+  Generated from the following GLSL fragment shader
+--eliminate-local-multi-store has also been run on the spv binary
+#version 460
+void main() {
+  for (uint i = 0; i < 123u; ++i) {
+    vec2 do_not_hoist_store = vec2(0.0f);
+    float do_not_hoist_access_chain_load = do_not_hoist_store.x;
+  }
+}
+*/
+
+TEST_F(PassClassTest, HoistAccessChains) {
+  const std::string before_hoist = R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 460
+OpName %main "main"
+OpName %i "i"
+OpName %do_not_hoist_store "do_not_hoist_store"
+OpName %do_not_hoist_access_chain_load "do_not_hoist_access_chain_load"
+%void = OpTypeVoid
+%7 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%_ptr_Function_uint = OpTypePointer Function %uint
+%uint_0 = OpConstant %uint 0
+%uint_123 = OpConstant %uint 123
+%bool = OpTypeBool
+%float = OpTypeFloat 32
+%v2float = OpTypeVector %float 2
+%_ptr_Function_v2float = OpTypePointer Function %v2float
+%float_0 = OpConstant %float 0
+%17 = OpConstantComposite %v2float %float_0 %float_0
+%_ptr_Function_float = OpTypePointer Function %float
+%int = OpTypeInt 32 1
+%int_1 = OpConstant %int 1
+%main = OpFunction %void None %7
+%21 = OpLabel
+%i = OpVariable %_ptr_Function_uint Function
+%do_not_hoist_store = OpVariable %_ptr_Function_v2float Function
+%do_not_hoist_access_chain_load = OpVariable %_ptr_Function_float Function
+OpStore %i %uint_0
+OpBranch %22
+%22 = OpLabel
+OpLoopMerge %23 %24 None
+OpBranch %25
+%25 = OpLabel
+%26 = OpLoad %uint %i
+%27 = OpULessThan %bool %26 %uint_123
+OpBranchConditional %27 %28 %23
+%28 = OpLabel
+OpStore %do_not_hoist_store %17
+%29 = OpAccessChain %_ptr_Function_float %do_not_hoist_store %uint_0
+%30 = OpLoad %float %29
+OpStore %do_not_hoist_access_chain_load %30
+OpBranch %24
+%24 = OpLabel
+%31 = OpLoad %uint %i
+%32 = OpIAdd %uint %31 %int_1
+OpStore %i %32
+OpBranch %22
+%23 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  const std::string after_hoist = R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 460
+OpName %main "main"
+OpName %i "i"
+OpName %do_not_hoist_store "do_not_hoist_store"
+OpName %do_not_hoist_access_chain_load "do_not_hoist_access_chain_load"
+%void = OpTypeVoid
+%7 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%_ptr_Function_uint = OpTypePointer Function %uint
+%uint_0 = OpConstant %uint 0
+%uint_123 = OpConstant %uint 123
+%bool = OpTypeBool
+%float = OpTypeFloat 32
+%v2float = OpTypeVector %float 2
+%_ptr_Function_v2float = OpTypePointer Function %v2float
+%float_0 = OpConstant %float 0
+%17 = OpConstantComposite %v2float %float_0 %float_0
+%_ptr_Function_float = OpTypePointer Function %float
+%int = OpTypeInt 32 1
+%int_1 = OpConstant %int 1
+%main = OpFunction %void None %7
+%21 = OpLabel
+%i = OpVariable %_ptr_Function_uint Function
+%do_not_hoist_store = OpVariable %_ptr_Function_v2float Function
+%do_not_hoist_access_chain_load = OpVariable %_ptr_Function_float Function
+OpStore %i %uint_0
+%29 = OpAccessChain %_ptr_Function_float %do_not_hoist_store %uint_0
+OpBranch %22
+%22 = OpLabel
+OpLoopMerge %23 %24 None
+OpBranch %25
+%25 = OpLabel
+%26 = OpLoad %uint %i
+%27 = OpULessThan %bool %26 %uint_123
+OpBranchConditional %27 %28 %23
+%28 = OpLabel
+OpStore %do_not_hoist_store %17
+%30 = OpLoad %float %29
+OpStore %do_not_hoist_access_chain_load %30
+OpBranch %24
+%24 = OpLabel
+%31 = OpLoad %uint %i
+%32 = OpIAdd %uint %31 %int_1
+OpStore %i %32
+OpBranch %22
+%23 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  SinglePassRunAndCheck<LICMPass>(before_hoist, after_hoist, true);
+}
+
+}  // namespace
+}  // namespace opt
+}  // namespace spvtools
diff --git a/test/opt/loop_optimizations/lcssa.cpp b/test/opt/loop_optimizations/lcssa.cpp
index ace6ce1..32c2f72 100644
--- a/test/opt/loop_optimizations/lcssa.cpp
+++ b/test/opt/loop_optimizations/lcssa.cpp
@@ -13,7 +13,6 @@
 // limitations under the License.
 
 #include <memory>
-#include <string>
 #include <vector>
 
 #include "effcee/effcee.h"
@@ -21,7 +20,6 @@
 #include "source/opt/build_module.h"
 #include "source/opt/loop_descriptor.h"
 #include "source/opt/loop_utils.h"
-#include "source/opt/pass.h"
 #include "test/opt//assembly_builder.h"
 #include "test/opt/function_utils.h"
 
diff --git a/test/opt/loop_optimizations/loop_descriptions.cpp b/test/opt/loop_optimizations/loop_descriptions.cpp
index b3f4f44..3dd0b93 100644
--- a/test/opt/loop_optimizations/loop_descriptions.cpp
+++ b/test/opt/loop_optimizations/loop_descriptions.cpp
@@ -13,7 +13,6 @@
 // limitations under the License.
 
 #include <memory>
-#include <string>
 #include <vector>
 
 #include "gmock/gmock.h"
diff --git a/test/opt/loop_optimizations/loop_fission.cpp b/test/opt/loop_optimizations/loop_fission.cpp
index bc3ec39..41e40c3 100644
--- a/test/opt/loop_optimizations/loop_fission.cpp
+++ b/test/opt/loop_optimizations/loop_fission.cpp
@@ -12,15 +12,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "source/opt/loop_fission.h"
+
 #include <memory>
-#include <string>
 #include <vector>
 
 #include "gmock/gmock.h"
-#include "source/opt/loop_fission.h"
-#include "source/opt/loop_unroller.h"
 #include "source/opt/loop_utils.h"
-#include "source/opt/pass.h"
 #include "test/opt/assembly_builder.h"
 #include "test/opt/function_utils.h"
 #include "test/opt/pass_fixture.h"
diff --git a/test/opt/loop_optimizations/peeling.cpp b/test/opt/loop_optimizations/peeling.cpp
index 10d8add..34c3307 100644
--- a/test/opt/loop_optimizations/peeling.cpp
+++ b/test/opt/loop_optimizations/peeling.cpp
@@ -13,7 +13,6 @@
 // limitations under the License.
 
 #include <memory>
-#include <string>
 #include <vector>
 
 #include "effcee/effcee.h"
@@ -765,7 +764,7 @@
     EXPECT_EQ(ld.NumLoops(), 1u);
 
     Instruction* loop_count = context->get_def_use_mgr()->GetDef(16);
-    EXPECT_EQ(loop_count->opcode(), SpvOpLoad);
+    EXPECT_EQ(loop_count->opcode(), spv::Op::OpLoad);
 
     LoopPeeling peel(&*ld.begin(), loop_count);
     EXPECT_TRUE(peel.CanPeelLoop());
@@ -817,7 +816,7 @@
     EXPECT_EQ(ld.NumLoops(), 1u);
 
     Instruction* loop_count = context->get_def_use_mgr()->GetDef(16);
-    EXPECT_EQ(loop_count->opcode(), SpvOpLoad);
+    EXPECT_EQ(loop_count->opcode(), spv::Op::OpLoad);
 
     LoopPeeling peel(&*ld.begin(), loop_count);
     EXPECT_TRUE(peel.CanPeelLoop());
@@ -1090,7 +1089,7 @@
     EXPECT_EQ(ld.NumLoops(), 1u);
 
     Instruction* loop_count = context->get_def_use_mgr()->GetDef(15);
-    EXPECT_EQ(loop_count->opcode(), SpvOpLoad);
+    EXPECT_EQ(loop_count->opcode(), spv::Op::OpLoad);
 
     LoopPeeling peel(&*ld.begin(), loop_count);
     EXPECT_TRUE(peel.CanPeelLoop());
@@ -1142,7 +1141,7 @@
     EXPECT_EQ(ld.NumLoops(), 1u);
 
     Instruction* loop_count = context->get_def_use_mgr()->GetDef(15);
-    EXPECT_EQ(loop_count->opcode(), SpvOpLoad);
+    EXPECT_EQ(loop_count->opcode(), spv::Op::OpLoad);
 
     LoopPeeling peel(&*ld.begin(), loop_count);
     EXPECT_TRUE(peel.CanPeelLoop());
diff --git a/test/opt/loop_optimizations/peeling_pass.cpp b/test/opt/loop_optimizations/peeling_pass.cpp
index 284ad83..ad7fcdc 100644
--- a/test/opt/loop_optimizations/peeling_pass.cpp
+++ b/test/opt/loop_optimizations/peeling_pass.cpp
@@ -17,7 +17,6 @@
 #include <vector>
 
 #include "gmock/gmock.h"
-#include "source/opt/ir_builder.h"
 #include "source/opt/loop_descriptor.h"
 #include "source/opt/loop_peeling.h"
 #include "test/opt/pass_fixture.h"
@@ -30,27 +29,27 @@
  public:
   // Generic routine to run the loop peeling pass and check
   LoopPeelingPass::LoopPeelingStats AssembleAndRunPeelingTest(
-      const std::string& text_head, const std::string& text_tail, SpvOp opcode,
-      const std::string& res_id, const std::string& op1,
+      const std::string& text_head, const std::string& text_tail,
+      spv::Op opcode, const std::string& res_id, const std::string& op1,
       const std::string& op2) {
     std::string opcode_str;
     switch (opcode) {
-      case SpvOpSLessThan:
+      case spv::Op::OpSLessThan:
         opcode_str = "OpSLessThan";
         break;
-      case SpvOpSGreaterThan:
+      case spv::Op::OpSGreaterThan:
         opcode_str = "OpSGreaterThan";
         break;
-      case SpvOpSLessThanEqual:
+      case spv::Op::OpSLessThanEqual:
         opcode_str = "OpSLessThanEqual";
         break;
-      case SpvOpSGreaterThanEqual:
+      case spv::Op::OpSGreaterThanEqual:
         opcode_str = "OpSGreaterThanEqual";
         break;
-      case SpvOpIEqual:
+      case spv::Op::OpIEqual:
         opcode_str = "OpIEqual";
         break;
-      case SpvOpINotEqual:
+      case spv::Op::OpINotEqual:
         opcode_str = "OpINotEqual";
         break;
       default:
@@ -69,9 +68,9 @@
 
   // Generic routine to run the loop peeling pass and check
   LoopPeelingPass::LoopPeelingStats RunPeelingTest(
-      const std::string& text_head, const std::string& text_tail, SpvOp opcode,
-      const std::string& res_id, const std::string& op1, const std::string& op2,
-      size_t nb_of_loops) {
+      const std::string& text_head, const std::string& text_tail,
+      spv::Op opcode, const std::string& res_id, const std::string& op1,
+      const std::string& op2, size_t nb_of_loops) {
     LoopPeelingPass::LoopPeelingStats stats = AssembleAndRunPeelingTest(
         text_head, text_tail, opcode, res_id, op1, op2);
 
@@ -86,7 +85,7 @@
       std::vector<std::pair<LoopPeelingPass::PeelDirection, uint32_t>>;
 
   void BuildAndCheckTrace(const std::string& text_head,
-                          const std::string& text_tail, SpvOp opcode,
+                          const std::string& text_tail, spv::Op opcode,
                           const std::string& res_id, const std::string& op1,
                           const std::string& op2,
                           const PeelTraceType& expected_peel_trace,
@@ -203,7 +202,7 @@
                OpFunctionEnd
   )";
 
-  auto run_test = [&text_head, &text_tail, this](SpvOp opcode,
+  auto run_test = [&text_head, &text_tail, this](spv::Op opcode,
                                                  const std::string& op1,
                                                  const std::string& op2) {
     auto stats =
@@ -225,7 +224,7 @@
     SCOPED_TRACE("Peel before iv < 4");
 
     std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
-        run_test(SpvOpSLessThan, "%32", "%int_4");
+        run_test(spv::Op::OpSLessThan, "%32", "%int_4");
     EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
     EXPECT_EQ(peel_info.second, 2u);
   }
@@ -233,7 +232,7 @@
     SCOPED_TRACE("Peel before 4 > iv");
 
     std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
-        run_test(SpvOpSGreaterThan, "%int_4", "%32");
+        run_test(spv::Op::OpSGreaterThan, "%int_4", "%32");
     EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
     EXPECT_EQ(peel_info.second, 2u);
   }
@@ -241,7 +240,7 @@
     SCOPED_TRACE("Peel before iv < 5");
 
     std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
-        run_test(SpvOpSLessThan, "%32", "%int_5");
+        run_test(spv::Op::OpSLessThan, "%32", "%int_5");
     EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
     EXPECT_EQ(peel_info.second, 2u);
   }
@@ -249,7 +248,7 @@
     SCOPED_TRACE("Peel before 5 > iv");
 
     std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
-        run_test(SpvOpSGreaterThan, "%int_5", "%32");
+        run_test(spv::Op::OpSGreaterThan, "%int_5", "%32");
     EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
     EXPECT_EQ(peel_info.second, 2u);
   }
@@ -259,7 +258,7 @@
     SCOPED_TRACE("Peel after iv < 16");
 
     std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
-        run_test(SpvOpSLessThan, "%32", "%int_16");
+        run_test(spv::Op::OpSLessThan, "%32", "%int_16");
     EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
     EXPECT_EQ(peel_info.second, 2u);
   }
@@ -267,7 +266,7 @@
     SCOPED_TRACE("Peel after 16 > iv");
 
     std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
-        run_test(SpvOpSGreaterThan, "%int_16", "%32");
+        run_test(spv::Op::OpSGreaterThan, "%int_16", "%32");
     EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
     EXPECT_EQ(peel_info.second, 2u);
   }
@@ -275,7 +274,7 @@
     SCOPED_TRACE("Peel after iv < 17");
 
     std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
-        run_test(SpvOpSLessThan, "%32", "%int_17");
+        run_test(spv::Op::OpSLessThan, "%32", "%int_17");
     EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
     EXPECT_EQ(peel_info.second, 2u);
   }
@@ -283,7 +282,7 @@
     SCOPED_TRACE("Peel after 17 > iv");
 
     std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
-        run_test(SpvOpSGreaterThan, "%int_17", "%32");
+        run_test(spv::Op::OpSGreaterThan, "%int_17", "%32");
     EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
     EXPECT_EQ(peel_info.second, 2u);
   }
@@ -294,7 +293,7 @@
     SCOPED_TRACE("Peel before iv > 5");
 
     std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
-        run_test(SpvOpSGreaterThan, "%32", "%int_5");
+        run_test(spv::Op::OpSGreaterThan, "%32", "%int_5");
     EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
     EXPECT_EQ(peel_info.second, 2u);
   }
@@ -302,7 +301,7 @@
     SCOPED_TRACE("Peel before 5 < iv");
 
     std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
-        run_test(SpvOpSLessThan, "%int_5", "%32");
+        run_test(spv::Op::OpSLessThan, "%int_5", "%32");
     EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
     EXPECT_EQ(peel_info.second, 2u);
   }
@@ -310,7 +309,7 @@
     SCOPED_TRACE("Peel before iv > 4");
 
     std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
-        run_test(SpvOpSGreaterThan, "%32", "%int_4");
+        run_test(spv::Op::OpSGreaterThan, "%32", "%int_4");
     EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
     EXPECT_EQ(peel_info.second, 2u);
   }
@@ -318,7 +317,7 @@
     SCOPED_TRACE("Peel before 4 < iv");
 
     std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
-        run_test(SpvOpSLessThan, "%int_4", "%32");
+        run_test(spv::Op::OpSLessThan, "%int_4", "%32");
     EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
     EXPECT_EQ(peel_info.second, 2u);
   }
@@ -328,7 +327,7 @@
     SCOPED_TRACE("Peel after iv > 16");
 
     std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
-        run_test(SpvOpSGreaterThan, "%32", "%int_16");
+        run_test(spv::Op::OpSGreaterThan, "%32", "%int_16");
     EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
     EXPECT_EQ(peel_info.second, 2u);
   }
@@ -336,7 +335,7 @@
     SCOPED_TRACE("Peel after 16 < iv");
 
     std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
-        run_test(SpvOpSLessThan, "%int_16", "%32");
+        run_test(spv::Op::OpSLessThan, "%int_16", "%32");
     EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
     EXPECT_EQ(peel_info.second, 2u);
   }
@@ -344,7 +343,7 @@
     SCOPED_TRACE("Peel after iv > 17");
 
     std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
-        run_test(SpvOpSGreaterThan, "%32", "%int_17");
+        run_test(spv::Op::OpSGreaterThan, "%32", "%int_17");
     EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
     EXPECT_EQ(peel_info.second, 2u);
   }
@@ -352,7 +351,7 @@
     SCOPED_TRACE("Peel after 17 < iv");
 
     std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
-        run_test(SpvOpSLessThan, "%int_17", "%32");
+        run_test(spv::Op::OpSLessThan, "%int_17", "%32");
     EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
     EXPECT_EQ(peel_info.second, 2u);
   }
@@ -363,7 +362,7 @@
     SCOPED_TRACE("Peel before iv <= 4");
 
     std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
-        run_test(SpvOpSLessThanEqual, "%32", "%int_4");
+        run_test(spv::Op::OpSLessThanEqual, "%32", "%int_4");
     EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
     EXPECT_EQ(peel_info.second, 2u);
   }
@@ -371,7 +370,7 @@
     SCOPED_TRACE("Peel before 4 => iv");
 
     std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
-        run_test(SpvOpSGreaterThanEqual, "%int_4", "%32");
+        run_test(spv::Op::OpSGreaterThanEqual, "%int_4", "%32");
     EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
     EXPECT_EQ(peel_info.second, 2u);
   }
@@ -379,7 +378,7 @@
     SCOPED_TRACE("Peel before iv <= 3");
 
     std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
-        run_test(SpvOpSLessThanEqual, "%32", "%int_3");
+        run_test(spv::Op::OpSLessThanEqual, "%32", "%int_3");
     EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
     EXPECT_EQ(peel_info.second, 2u);
   }
@@ -387,7 +386,7 @@
     SCOPED_TRACE("Peel before 3 => iv");
 
     std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
-        run_test(SpvOpSGreaterThanEqual, "%int_3", "%32");
+        run_test(spv::Op::OpSGreaterThanEqual, "%int_3", "%32");
     EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
     EXPECT_EQ(peel_info.second, 2u);
   }
@@ -397,7 +396,7 @@
     SCOPED_TRACE("Peel after iv <= 16");
 
     std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
-        run_test(SpvOpSLessThanEqual, "%32", "%int_16");
+        run_test(spv::Op::OpSLessThanEqual, "%32", "%int_16");
     EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
     EXPECT_EQ(peel_info.second, 2u);
   }
@@ -405,7 +404,7 @@
     SCOPED_TRACE("Peel after 16 => iv");
 
     std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
-        run_test(SpvOpSGreaterThanEqual, "%int_16", "%32");
+        run_test(spv::Op::OpSGreaterThanEqual, "%int_16", "%32");
     EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
     EXPECT_EQ(peel_info.second, 2u);
   }
@@ -413,7 +412,7 @@
     SCOPED_TRACE("Peel after iv <= 15");
 
     std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
-        run_test(SpvOpSLessThanEqual, "%32", "%int_15");
+        run_test(spv::Op::OpSLessThanEqual, "%32", "%int_15");
     EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
     EXPECT_EQ(peel_info.second, 2u);
   }
@@ -421,7 +420,7 @@
     SCOPED_TRACE("Peel after 15 => iv");
 
     std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
-        run_test(SpvOpSGreaterThanEqual, "%int_15", "%32");
+        run_test(spv::Op::OpSGreaterThanEqual, "%int_15", "%32");
     EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
     EXPECT_EQ(peel_info.second, 2u);
   }
@@ -432,7 +431,7 @@
     SCOPED_TRACE("Peel before iv >= 5");
 
     std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
-        run_test(SpvOpSGreaterThanEqual, "%32", "%int_5");
+        run_test(spv::Op::OpSGreaterThanEqual, "%32", "%int_5");
     EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
     EXPECT_EQ(peel_info.second, 2u);
   }
@@ -440,7 +439,7 @@
     SCOPED_TRACE("Peel before 35 >= iv");
 
     std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
-        run_test(SpvOpSLessThanEqual, "%int_5", "%32");
+        run_test(spv::Op::OpSLessThanEqual, "%int_5", "%32");
     EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
     EXPECT_EQ(peel_info.second, 2u);
   }
@@ -448,7 +447,7 @@
     SCOPED_TRACE("Peel before iv >= 4");
 
     std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
-        run_test(SpvOpSGreaterThanEqual, "%32", "%int_4");
+        run_test(spv::Op::OpSGreaterThanEqual, "%32", "%int_4");
     EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
     EXPECT_EQ(peel_info.second, 2u);
   }
@@ -456,7 +455,7 @@
     SCOPED_TRACE("Peel before 4 <= iv");
 
     std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
-        run_test(SpvOpSLessThanEqual, "%int_4", "%32");
+        run_test(spv::Op::OpSLessThanEqual, "%int_4", "%32");
     EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
     EXPECT_EQ(peel_info.second, 2u);
   }
@@ -466,7 +465,7 @@
     SCOPED_TRACE("Peel after iv >= 17");
 
     std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
-        run_test(SpvOpSGreaterThanEqual, "%32", "%int_17");
+        run_test(spv::Op::OpSGreaterThanEqual, "%32", "%int_17");
     EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
     EXPECT_EQ(peel_info.second, 2u);
   }
@@ -474,7 +473,7 @@
     SCOPED_TRACE("Peel after 17 <= iv");
 
     std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
-        run_test(SpvOpSLessThanEqual, "%int_17", "%32");
+        run_test(spv::Op::OpSLessThanEqual, "%int_17", "%32");
     EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
     EXPECT_EQ(peel_info.second, 2u);
   }
@@ -482,7 +481,7 @@
     SCOPED_TRACE("Peel after iv >= 16");
 
     std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
-        run_test(SpvOpSGreaterThanEqual, "%32", "%int_16");
+        run_test(spv::Op::OpSGreaterThanEqual, "%32", "%int_16");
     EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
     EXPECT_EQ(peel_info.second, 2u);
   }
@@ -490,7 +489,7 @@
     SCOPED_TRACE("Peel after 16 <= iv");
 
     std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
-        run_test(SpvOpSLessThanEqual, "%int_16", "%32");
+        run_test(spv::Op::OpSLessThanEqual, "%int_16", "%32");
     EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
     EXPECT_EQ(peel_info.second, 2u);
   }
@@ -501,7 +500,7 @@
     SCOPED_TRACE("Peel before iv == 1");
 
     std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
-        run_test(SpvOpIEqual, "%32", "%int_1");
+        run_test(spv::Op::OpIEqual, "%32", "%int_1");
     EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
     EXPECT_EQ(peel_info.second, 1u);
   }
@@ -509,7 +508,7 @@
     SCOPED_TRACE("Peel before 1 == iv");
 
     std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
-        run_test(SpvOpIEqual, "%int_1", "%32");
+        run_test(spv::Op::OpIEqual, "%int_1", "%32");
     EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
     EXPECT_EQ(peel_info.second, 1u);
   }
@@ -519,7 +518,7 @@
     SCOPED_TRACE("Peel after iv == 19");
 
     std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
-        run_test(SpvOpIEqual, "%32", "%int_19");
+        run_test(spv::Op::OpIEqual, "%32", "%int_19");
     EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
     EXPECT_EQ(peel_info.second, 1u);
   }
@@ -527,7 +526,7 @@
     SCOPED_TRACE("Peel after 19 == iv");
 
     std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
-        run_test(SpvOpIEqual, "%int_19", "%32");
+        run_test(spv::Op::OpIEqual, "%int_19", "%32");
     EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
     EXPECT_EQ(peel_info.second, 1u);
   }
@@ -538,7 +537,7 @@
     SCOPED_TRACE("Peel before iv != 1");
 
     std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
-        run_test(SpvOpINotEqual, "%32", "%int_1");
+        run_test(spv::Op::OpINotEqual, "%32", "%int_1");
     EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
     EXPECT_EQ(peel_info.second, 1u);
   }
@@ -546,7 +545,7 @@
     SCOPED_TRACE("Peel before 1 != iv");
 
     std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
-        run_test(SpvOpINotEqual, "%int_1", "%32");
+        run_test(spv::Op::OpINotEqual, "%int_1", "%32");
     EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
     EXPECT_EQ(peel_info.second, 1u);
   }
@@ -556,7 +555,7 @@
     SCOPED_TRACE("Peel after iv != 19");
 
     std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
-        run_test(SpvOpINotEqual, "%32", "%int_19");
+        run_test(spv::Op::OpINotEqual, "%32", "%int_19");
     EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
     EXPECT_EQ(peel_info.second, 1u);
   }
@@ -564,7 +563,7 @@
     SCOPED_TRACE("Peel after 19 != iv");
 
     std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
-        run_test(SpvOpINotEqual, "%int_19", "%32");
+        run_test(spv::Op::OpINotEqual, "%int_19", "%32");
     EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
     EXPECT_EQ(peel_info.second, 1u);
   }
@@ -573,7 +572,7 @@
   {
     SCOPED_TRACE("No Peel: 20 => iv");
 
-    auto stats = RunPeelingTest(text_head, text_tail, SpvOpSLessThanEqual,
+    auto stats = RunPeelingTest(text_head, text_tail, spv::Op::OpSLessThanEqual,
                                 "%22", "%int_20", "%32", 1);
 
     EXPECT_EQ(stats.peeled_loops_.size(), 0u);
@@ -673,7 +672,7 @@
   )";
 
   auto run_test = [&text_head, &text_tail, this](
-                      SpvOp opcode, const std::string& op1,
+                      spv::Op opcode, const std::string& op1,
                       const std::string& op2,
                       const PeelTraceType& expected_peel_trace) {
     BuildAndCheckTrace(text_head, text_tail, opcode, "%22", op1, op2,
@@ -685,13 +684,13 @@
   {
     SCOPED_TRACE("Peel before iv < 3");
 
-    run_test(SpvOpSLessThan, "%38", "%int_3",
+    run_test(spv::Op::OpSLessThan, "%38", "%int_3",
              {{LoopPeelingPass::PeelDirection::kBefore, 3u}});
   }
   {
     SCOPED_TRACE("Peel before 3 > iv");
 
-    run_test(SpvOpSGreaterThan, "%int_3", "%38",
+    run_test(spv::Op::OpSGreaterThan, "%int_3", "%38",
              {{LoopPeelingPass::PeelDirection::kBefore, 3u}});
   }
 
@@ -699,13 +698,13 @@
   {
     SCOPED_TRACE("Peel after iv < 8");
 
-    run_test(SpvOpSLessThan, "%38", "%int_8",
+    run_test(spv::Op::OpSLessThan, "%38", "%int_8",
              {{LoopPeelingPass::PeelDirection::kAfter, 2u}});
   }
   {
     SCOPED_TRACE("Peel after 8 > iv");
 
-    run_test(SpvOpSGreaterThan, "%int_8", "%38",
+    run_test(spv::Op::OpSGreaterThan, "%int_8", "%38",
              {{LoopPeelingPass::PeelDirection::kAfter, 2u}});
   }
 
@@ -714,13 +713,13 @@
   {
     SCOPED_TRACE("Peel before iv > 2");
 
-    run_test(SpvOpSGreaterThan, "%38", "%int_2",
+    run_test(spv::Op::OpSGreaterThan, "%38", "%int_2",
              {{LoopPeelingPass::PeelDirection::kBefore, 2u}});
   }
   {
     SCOPED_TRACE("Peel before 2 < iv");
 
-    run_test(SpvOpSLessThan, "%int_2", "%38",
+    run_test(spv::Op::OpSLessThan, "%int_2", "%38",
              {{LoopPeelingPass::PeelDirection::kBefore, 2u}});
   }
 
@@ -728,13 +727,13 @@
   {
     SCOPED_TRACE("Peel after iv > 7");
 
-    run_test(SpvOpSGreaterThan, "%38", "%int_7",
+    run_test(spv::Op::OpSGreaterThan, "%38", "%int_7",
              {{LoopPeelingPass::PeelDirection::kAfter, 3u}});
   }
   {
     SCOPED_TRACE("Peel after 7 < iv");
 
-    run_test(SpvOpSLessThan, "%int_7", "%38",
+    run_test(spv::Op::OpSLessThan, "%int_7", "%38",
              {{LoopPeelingPass::PeelDirection::kAfter, 3u}});
   }
 
@@ -743,13 +742,13 @@
   {
     SCOPED_TRACE("Peel before iv <= 1");
 
-    run_test(SpvOpSLessThanEqual, "%38", "%int_1",
+    run_test(spv::Op::OpSLessThanEqual, "%38", "%int_1",
              {{LoopPeelingPass::PeelDirection::kBefore, 2u}});
   }
   {
     SCOPED_TRACE("Peel before 1 => iv");
 
-    run_test(SpvOpSGreaterThanEqual, "%int_1", "%38",
+    run_test(spv::Op::OpSGreaterThanEqual, "%int_1", "%38",
              {{LoopPeelingPass::PeelDirection::kBefore, 2u}});
   }
 
@@ -757,13 +756,13 @@
   {
     SCOPED_TRACE("Peel after iv <= 7");
 
-    run_test(SpvOpSLessThanEqual, "%38", "%int_7",
+    run_test(spv::Op::OpSLessThanEqual, "%38", "%int_7",
              {{LoopPeelingPass::PeelDirection::kAfter, 2u}});
   }
   {
     SCOPED_TRACE("Peel after 7 => iv");
 
-    run_test(SpvOpSGreaterThanEqual, "%int_7", "%38",
+    run_test(spv::Op::OpSGreaterThanEqual, "%int_7", "%38",
              {{LoopPeelingPass::PeelDirection::kAfter, 2u}});
   }
 
@@ -772,13 +771,13 @@
   {
     SCOPED_TRACE("Peel before iv >= 2");
 
-    run_test(SpvOpSGreaterThanEqual, "%38", "%int_2",
+    run_test(spv::Op::OpSGreaterThanEqual, "%38", "%int_2",
              {{LoopPeelingPass::PeelDirection::kBefore, 2u}});
   }
   {
     SCOPED_TRACE("Peel before 2 <= iv");
 
-    run_test(SpvOpSLessThanEqual, "%int_2", "%38",
+    run_test(spv::Op::OpSLessThanEqual, "%int_2", "%38",
              {{LoopPeelingPass::PeelDirection::kBefore, 2u}});
   }
 
@@ -786,13 +785,13 @@
   {
     SCOPED_TRACE("Peel after iv >= 8");
 
-    run_test(SpvOpSGreaterThanEqual, "%38", "%int_8",
+    run_test(spv::Op::OpSGreaterThanEqual, "%38", "%int_8",
              {{LoopPeelingPass::PeelDirection::kAfter, 2u}});
   }
   {
     SCOPED_TRACE("Peel after 8 <= iv");
 
-    run_test(SpvOpSLessThanEqual, "%int_8", "%38",
+    run_test(spv::Op::OpSLessThanEqual, "%int_8", "%38",
              {{LoopPeelingPass::PeelDirection::kAfter, 2u}});
   }
   // Test EQ
@@ -800,13 +799,13 @@
   {
     SCOPED_TRACE("Peel before iv == 0");
 
-    run_test(SpvOpIEqual, "%38", "%int_0",
+    run_test(spv::Op::OpIEqual, "%38", "%int_0",
              {{LoopPeelingPass::PeelDirection::kBefore, 1u}});
   }
   {
     SCOPED_TRACE("Peel before 0 == iv");
 
-    run_test(SpvOpIEqual, "%int_0", "%38",
+    run_test(spv::Op::OpIEqual, "%int_0", "%38",
              {{LoopPeelingPass::PeelDirection::kBefore, 1u}});
   }
 
@@ -814,13 +813,13 @@
   {
     SCOPED_TRACE("Peel after iv == 9");
 
-    run_test(SpvOpIEqual, "%38", "%int_9",
+    run_test(spv::Op::OpIEqual, "%38", "%int_9",
              {{LoopPeelingPass::PeelDirection::kBefore, 1u}});
   }
   {
     SCOPED_TRACE("Peel after 9 == iv");
 
-    run_test(SpvOpIEqual, "%int_9", "%38",
+    run_test(spv::Op::OpIEqual, "%int_9", "%38",
              {{LoopPeelingPass::PeelDirection::kBefore, 1u}});
   }
 
@@ -829,13 +828,13 @@
   {
     SCOPED_TRACE("Peel before iv != 0");
 
-    run_test(SpvOpINotEqual, "%38", "%int_0",
+    run_test(spv::Op::OpINotEqual, "%38", "%int_0",
              {{LoopPeelingPass::PeelDirection::kBefore, 1u}});
   }
   {
     SCOPED_TRACE("Peel before 0 != iv");
 
-    run_test(SpvOpINotEqual, "%int_0", "%38",
+    run_test(spv::Op::OpINotEqual, "%int_0", "%38",
              {{LoopPeelingPass::PeelDirection::kBefore, 1u}});
   }
 
@@ -843,13 +842,13 @@
   {
     SCOPED_TRACE("Peel after iv != 9");
 
-    run_test(SpvOpINotEqual, "%38", "%int_9",
+    run_test(spv::Op::OpINotEqual, "%38", "%int_9",
              {{LoopPeelingPass::PeelDirection::kBefore, 1u}});
   }
   {
     SCOPED_TRACE("Peel after 9 != iv");
 
-    run_test(SpvOpINotEqual, "%int_9", "%38",
+    run_test(spv::Op::OpINotEqual, "%int_9", "%38",
              {{LoopPeelingPass::PeelDirection::kBefore, 1u}});
   }
 }
@@ -952,7 +951,7 @@
 
   auto run_test =
       [&text_head, &text_tail, this](
-          SpvOp opcode, const std::string& op1, const std::string& op2,
+          spv::Op opcode, const std::string& op1, const std::string& op2,
           const PeelTraceType& expected_peel_trace, size_t nb_of_loops) {
         BuildAndCheckTrace(text_head, text_tail, opcode, "%30", op1, op2,
                            expected_peel_trace, nb_of_loops);
@@ -963,7 +962,7 @@
     SCOPED_TRACE("Peel before iv_i < 3");
 
     // Expect peel before by a factor of 3 and 4 loops at the end.
-    run_test(SpvOpSLessThan, "%42", "%int_3",
+    run_test(spv::Op::OpSLessThan, "%42", "%int_3",
              {{LoopPeelingPass::PeelDirection::kBefore, 3u}}, 4);
   }
   // Peeling outer loop after by a factor of 3.
@@ -971,7 +970,7 @@
     SCOPED_TRACE("Peel after iv_i < 7");
 
     // Expect peel after by a factor of 3 and 4 loops at the end.
-    run_test(SpvOpSLessThan, "%42", "%int_7",
+    run_test(spv::Op::OpSLessThan, "%42", "%int_7",
              {{LoopPeelingPass::PeelDirection::kAfter, 3u}}, 4);
   }
 
@@ -980,7 +979,7 @@
     SCOPED_TRACE("Peel before iv_j < 3");
 
     // Expect peel before by a factor of 3 and 3 loops at the end.
-    run_test(SpvOpSLessThan, "%46", "%int_3",
+    run_test(spv::Op::OpSLessThan, "%46", "%int_3",
              {{LoopPeelingPass::PeelDirection::kBefore, 3u}}, 3);
   }
   // Peeling inner loop after by a factor of 3.
@@ -988,7 +987,7 @@
     SCOPED_TRACE("Peel after iv_j < 7");
 
     // Expect peel after by a factor of 3 and 3 loops at the end.
-    run_test(SpvOpSLessThan, "%46", "%int_7",
+    run_test(spv::Op::OpSLessThan, "%46", "%int_7",
              {{LoopPeelingPass::PeelDirection::kAfter, 3u}}, 3);
   }
 
@@ -997,7 +996,7 @@
     SCOPED_TRACE("No peel");
 
     // Expect no peeling and 2 loops at the end.
-    run_test(SpvOpSLessThan, "%46", "%42", {}, 2);
+    run_test(spv::Op::OpSLessThan, "%46", "%42", {}, 2);
   }
 
   // Could do a peeling of 3, but the goes over the threshold.
@@ -1007,7 +1006,7 @@
     size_t current_threshold = LoopPeelingPass::GetLoopPeelingThreshold();
     LoopPeelingPass::SetLoopPeelingThreshold(1u);
     // Expect no peeling and 2 loops at the end.
-    run_test(SpvOpSLessThan, "%46", "%int_7", {}, 2);
+    run_test(spv::Op::OpSLessThan, "%46", "%int_7", {}, 2);
     LoopPeelingPass::SetLoopPeelingThreshold(current_threshold);
   }
 }
diff --git a/test/opt/loop_optimizations/unroll_assumptions.cpp b/test/opt/loop_optimizations/unroll_assumptions.cpp
index 159e4a1..81657a5 100644
--- a/test/opt/loop_optimizations/unroll_assumptions.cpp
+++ b/test/opt/loop_optimizations/unroll_assumptions.cpp
@@ -13,7 +13,6 @@
 // limitations under the License.
 
 #include <memory>
-#include <string>
 #include <vector>
 
 #include "gmock/gmock.h"
diff --git a/test/opt/loop_optimizations/unroll_simple.cpp b/test/opt/loop_optimizations/unroll_simple.cpp
index 299fb2d..6468adf 100644
--- a/test/opt/loop_optimizations/unroll_simple.cpp
+++ b/test/opt/loop_optimizations/unroll_simple.cpp
@@ -13,7 +13,6 @@
 // limitations under the License.
 
 #include <memory>
-#include <string>
 #include <vector>
 
 #include "gmock/gmock.h"
diff --git a/test/opt/module_test.cpp b/test/opt/module_test.cpp
index 17a1365..a93a50b 100644
--- a/test/opt/module_test.cpp
+++ b/test/opt/module_test.cpp
@@ -12,16 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "source/opt/module.h"
+
 #include <memory>
-#include <sstream>
-#include <string>
 #include <vector>
 
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 #include "source/opt/build_module.h"
-#include "source/opt/module.h"
-#include "source/opt/pass.h"
 #include "spirv-tools/libspirv.hpp"
 #include "test/opt/module_utils.h"
 
@@ -324,7 +322,7 @@
   std::unordered_set<uint32_t> non_semantic_ids;
   context->module()->ForEachInst(
       [&non_semantic_ids](const Instruction* inst) {
-        if (inst->opcode() == SpvOpExtInst) {
+        if (inst->opcode() == spv::Op::OpExtInst) {
           non_semantic_ids.insert(inst->result_id());
         }
       },
diff --git a/test/opt/module_utils.h b/test/opt/module_utils.h
index 007f132..6859188 100644
--- a/test/opt/module_utils.h
+++ b/test/opt/module_utils.h
@@ -17,6 +17,7 @@
 
 #include <vector>
 #include "source/opt/module.h"
+#include "gtest/gtest.h"
 
 namespace spvtest {
 
diff --git a/test/opt/pass_manager_test.cpp b/test/opt/pass_manager_test.cpp
index 4f36d5b..ec11069 100644
--- a/test/opt/pass_manager_test.cpp
+++ b/test/opt/pass_manager_test.cpp
@@ -144,8 +144,8 @@
 
   const char* name() const override { return "AppendTypeVoidInstPass"; }
   Status Process() override {
-    auto inst = MakeUnique<Instruction>(context(), SpvOpTypeVoid, 0, result_id_,
-                                        std::vector<Operand>{});
+    auto inst = MakeUnique<Instruction>(context(), spv::Op::OpTypeVoid, 0,
+                                        result_id_, std::vector<Operand>{});
     context()->AddType(std::move(inst));
     return Status::SuccessWithChange;
   }
diff --git a/test/opt/pass_merge_return_test.cpp b/test/opt/pass_merge_return_test.cpp
index 04bd5d9..494f2e9 100644
--- a/test/opt/pass_merge_return_test.cpp
+++ b/test/opt/pass_merge_return_test.cpp
@@ -14,9 +14,7 @@
 
 #include <string>
 
-#include "gmock/gmock.h"
 #include "spirv-tools/libspirv.hpp"
-#include "spirv-tools/optimizer.hpp"
 #include "test/opt/pass_fixture.h"
 #include "test/opt/pass_utils.h"
 
diff --git a/test/opt/pass_remove_duplicates_test.cpp b/test/opt/pass_remove_duplicates_test.cpp
index 887fdfd..131a6b4 100644
--- a/test/opt/pass_remove_duplicates_test.cpp
+++ b/test/opt/pass_remove_duplicates_test.cpp
@@ -12,12 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <iostream>
 #include <memory>
 #include <string>
 #include <vector>
 
-#include "gmock/gmock.h"
 #include "source/opt/build_module.h"
 #include "source/opt/ir_context.h"
 #include "source/opt/pass_manager.h"
@@ -91,7 +89,7 @@
   std::string GetErrorMessage() const { return error_message_; }
 
   std::string ToText(const std::vector<Instruction*>& inst) {
-    std::vector<uint32_t> binary = {SpvMagicNumber, 0x10200, 0u, 2u, 0u};
+    std::vector<uint32_t> binary = {spv::MagicNumber, 0x10200, 0u, 2u, 0u};
     for (const Instruction* i : inst)
       i->ToBinaryWithoutAttachedDebugInsts(&binary);
     std::string text;
diff --git a/test/opt/private_to_local_test.cpp b/test/opt/private_to_local_test.cpp
index 8b5ec59..f7c37c9 100644
--- a/test/opt/private_to_local_test.cpp
+++ b/test/opt/private_to_local_test.cpp
@@ -15,7 +15,6 @@
 #include <string>
 
 #include "gmock/gmock.h"
-#include "source/opt/build_module.h"
 #include "source/opt/value_number_table.h"
 #include "test/opt/assembly_builder.h"
 #include "test/opt/pass_fixture.h"
diff --git a/test/opt/propagator_test.cpp b/test/opt/propagator_test.cpp
index fb8e487..307a2a1 100644
--- a/test/opt/propagator_test.cpp
+++ b/test/opt/propagator_test.cpp
@@ -12,9 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "source/opt/propagator.h"
+
 #include <map>
 #include <memory>
-#include <string>
 #include <vector>
 
 #include "gmock/gmock.h"
@@ -22,8 +23,6 @@
 #include "source/opt/build_module.h"
 #include "source/opt/cfg.h"
 #include "source/opt/ir_context.h"
-#include "source/opt/pass.h"
-#include "source/opt/propagator.h"
 
 namespace spvtools {
 namespace opt {
@@ -107,11 +106,11 @@
 
   const auto visit_fn = [this](Instruction* instr, BasicBlock** dest_bb) {
     *dest_bb = nullptr;
-    if (instr->opcode() == SpvOpStore) {
+    if (instr->opcode() == spv::Op::OpStore) {
       uint32_t lhs_id = instr->GetSingleWordOperand(0);
       uint32_t rhs_id = instr->GetSingleWordOperand(1);
       Instruction* rhs_def = ctx_->get_def_use_mgr()->GetDef(rhs_id);
-      if (rhs_def->opcode() == SpvOpConstant) {
+      if (rhs_def->opcode() == spv::Op::OpConstant) {
         uint32_t val = rhs_def->GetSingleWordOperand(2);
         values_[lhs_id] = val;
         return SSAPropagator::kInteresting;
@@ -175,15 +174,15 @@
   const auto visit_fn = [this, &phi_instr](Instruction* instr,
                                            BasicBlock** dest_bb) {
     *dest_bb = nullptr;
-    if (instr->opcode() == SpvOpLoad) {
+    if (instr->opcode() == spv::Op::OpLoad) {
       uint32_t rhs_id = instr->GetSingleWordOperand(2);
       Instruction* rhs_def = ctx_->get_def_use_mgr()->GetDef(rhs_id);
-      if (rhs_def->opcode() == SpvOpConstant) {
+      if (rhs_def->opcode() == spv::Op::OpConstant) {
         uint32_t val = rhs_def->GetSingleWordOperand(2);
         values_[instr->result_id()] = val;
         return SSAPropagator::kInteresting;
       }
-    } else if (instr->opcode() == SpvOpPhi) {
+    } else if (instr->opcode() == spv::Op::OpPhi) {
       phi_instr = instr;
       SSAPropagator::PropStatus retval;
       for (uint32_t i = 2; i < instr->NumOperands(); i += 2) {
diff --git a/test/opt/redundancy_elimination_test.cpp b/test/opt/redundancy_elimination_test.cpp
index 28eda73..eb78497 100644
--- a/test/opt/redundancy_elimination_test.cpp
+++ b/test/opt/redundancy_elimination_test.cpp
@@ -15,8 +15,6 @@
 #include <string>
 
 #include "gmock/gmock.h"
-#include "source/opt/build_module.h"
-#include "source/opt/value_number_table.h"
 #include "test/opt/assembly_builder.h"
 #include "test/opt/pass_fixture.h"
 #include "test/opt/pass_utils.h"
diff --git a/test/opt/register_liveness.cpp b/test/opt/register_liveness.cpp
index 7cb210f..3870e2f 100644
--- a/test/opt/register_liveness.cpp
+++ b/test/opt/register_liveness.cpp
@@ -13,7 +13,6 @@
 // limitations under the License.
 
 #include <memory>
-#include <string>
 #include <unordered_set>
 #include <vector>
 
diff --git a/test/opt/relax_float_ops_test.cpp b/test/opt/relax_float_ops_test.cpp
index b9cb0de..e486df3 100644
--- a/test/opt/relax_float_ops_test.cpp
+++ b/test/opt/relax_float_ops_test.cpp
@@ -18,7 +18,6 @@
 #include <string>
 #include <vector>
 
-#include "test/opt/assembly_builder.h"
 #include "test/opt/pass_fixture.h"
 #include "test/opt/pass_utils.h"
 
diff --git a/test/opt/remove_unused_interface_variables_test.cpp b/test/opt/remove_unused_interface_variables_test.cpp
index ddf027f..8bb40f7 100644
--- a/test/opt/remove_unused_interface_variables_test.cpp
+++ b/test/opt/remove_unused_interface_variables_test.cpp
@@ -12,7 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "gmock/gmock.h"
 #include "test/opt/assembly_builder.h"
 #include "test/opt/pass_fixture.h"
 #include "test/opt/pass_utils.h"
diff --git a/test/opt/replace_desc_array_access_using_var_index_test.cpp b/test/opt/replace_desc_array_access_using_var_index_test.cpp
index 9ab9eb1..6018be2 100644
--- a/test/opt/replace_desc_array_access_using_var_index_test.cpp
+++ b/test/opt/replace_desc_array_access_using_var_index_test.cpp
@@ -14,8 +14,6 @@
 
 #include <string>
 
-#include "gmock/gmock.h"
-#include "test/opt/assembly_builder.h"
 #include "test/opt/pass_fixture.h"
 #include "test/opt/pass_utils.h"
 
diff --git a/test/opt/replace_invalid_opc_test.cpp b/test/opt/replace_invalid_opc_test.cpp
index 1be904b..aee0d6e 100644
--- a/test/opt/replace_invalid_opc_test.cpp
+++ b/test/opt/replace_invalid_opc_test.cpp
@@ -12,11 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <cstdarg>
 #include <string>
 #include <vector>
 
-#include "gmock/gmock.h"
 #include "pass_utils.h"
 #include "test/opt/assembly_builder.h"
 #include "test/opt/pass_fixture.h"
@@ -404,6 +402,7 @@
             OpReturn
             OpFunctionEnd)";
 
+  SetTargetEnv(SPV_ENV_UNIVERSAL_1_2);
   auto result = SinglePassRunAndDisassemble<ReplaceInvalidOpcodePass>(
       text, /* skip_nop = */ true, /* do_validation = */ false);
   EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result));
@@ -432,9 +431,40 @@
             OpReturn
             OpFunctionEnd)";
 
+  SetTargetEnv(SPV_ENV_UNIVERSAL_1_2);
   SinglePassRunAndMatch<ReplaceInvalidOpcodePass>(text, false);
 }
 
+// Since version 1.3 OpControlBarriers are allowed is more shaders.
+// https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#OpControlBarrier
+TEST_F(ReplaceInvalidOpcodeTest, BarrierDontReplaceV13) {
+  const std::string text = R"(
+            OpCapability Shader
+       %1 = OpExtInstImport "GLSL.std.450"
+            OpMemoryModel Logical GLSL450
+            OpEntryPoint Vertex %main "main"
+            OpExecutionMode %main LocalSize 1 1 1
+            OpSource GLSL 450
+            OpSourceExtension "GL_GOOGLE_cpp_style_line_directive"
+            OpSourceExtension "GL_GOOGLE_include_directive"
+            OpName %main "main"
+    %void = OpTypeVoid
+       %3 = OpTypeFunction %void
+    %uint = OpTypeInt 32 0
+  %uint_2 = OpConstant %uint 2
+%uint_264 = OpConstant %uint 264
+    %main = OpFunction %void None %3
+       %5 = OpLabel
+            OpControlBarrier %uint_2 %uint_2 %uint_264
+            OpReturn
+            OpFunctionEnd)";
+
+  SetTargetEnv(SPV_ENV_UNIVERSAL_1_3);
+  auto result = SinglePassRunAndDisassemble<ReplaceInvalidOpcodePass>(
+      text, /* skip_nop = */ true, /* do_validation = */ false);
+  EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result));
+}
+
 TEST_F(ReplaceInvalidOpcodeTest, MessageTest) {
   const std::string text = R"(
                OpCapability Shader
diff --git a/test/opt/scalar_analysis.cpp b/test/opt/scalar_analysis.cpp
index df2aa8f..4779658 100644
--- a/test/opt/scalar_analysis.cpp
+++ b/test/opt/scalar_analysis.cpp
@@ -12,17 +12,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "source/opt/scalar_analysis.h"
+
 #include <memory>
-#include <string>
-#include <unordered_set>
 #include <vector>
 
 #include "gmock/gmock.h"
-#include "source/opt/iterator.h"
-#include "source/opt/loop_descriptor.h"
 #include "source/opt/pass.h"
-#include "source/opt/scalar_analysis.h"
-#include "source/opt/tree_iterator.h"
 #include "test/opt/assembly_builder.h"
 #include "test/opt/function_utils.h"
 #include "test/opt/pass_fixture.h"
@@ -109,10 +105,10 @@
   const Instruction* store = nullptr;
   const Instruction* load = nullptr;
   for (const Instruction& inst : *spvtest::GetBasicBlock(f, 11)) {
-    if (inst.opcode() == SpvOp::SpvOpStore) {
+    if (inst.opcode() == spv::Op::OpStore) {
       store = &inst;
     }
-    if (inst.opcode() == SpvOp::SpvOpLoad) {
+    if (inst.opcode() == spv::Op::OpLoad) {
       load = &inst;
     }
   }
@@ -236,7 +232,7 @@
 
   const Instruction* load = nullptr;
   for (const Instruction& inst : *spvtest::GetBasicBlock(f, 28)) {
-    if (inst.opcode() == SpvOp::SpvOpLoad) {
+    if (inst.opcode() == spv::Op::OpLoad) {
       load = &inst;
     }
   }
@@ -352,7 +348,7 @@
 
   const Instruction* load = nullptr;
   for (const Instruction& inst : *spvtest::GetBasicBlock(f, 21)) {
-    if (inst.opcode() == SpvOp::SpvOpLoad && inst.result_id() == 33) {
+    if (inst.opcode() == spv::Op::OpLoad && inst.result_id() == 33) {
       load = &inst;
     }
   }
@@ -506,11 +502,11 @@
   int store_count = 0;
 
   for (const Instruction& inst : *spvtest::GetBasicBlock(f, 22)) {
-    if (inst.opcode() == SpvOp::SpvOpLoad) {
+    if (inst.opcode() == spv::Op::OpLoad) {
       loads[load_count] = &inst;
       ++load_count;
     }
-    if (inst.opcode() == SpvOp::SpvOpStore) {
+    if (inst.opcode() == spv::Op::OpStore) {
       stores[store_count] = &inst;
       ++store_count;
     }
@@ -745,11 +741,11 @@
   int store_count = 0;
 
   for (const Instruction& inst : *spvtest::GetBasicBlock(f, 31)) {
-    if (inst.opcode() == SpvOp::SpvOpLoad) {
+    if (inst.opcode() == spv::Op::OpLoad) {
       loads[load_count] = &inst;
       ++load_count;
     }
-    if (inst.opcode() == SpvOp::SpvOpStore) {
+    if (inst.opcode() == spv::Op::OpStore) {
       stores[store_count] = &inst;
       ++store_count;
     }
@@ -880,7 +876,7 @@
   int load_count = 0;
 
   for (const Instruction& inst : *spvtest::GetBasicBlock(f, 29)) {
-    if (inst.opcode() == SpvOp::SpvOpLoad) {
+    if (inst.opcode() == spv::Op::OpLoad) {
       loads[load_count] = &inst;
       ++load_count;
     }
@@ -1025,10 +1021,10 @@
   std::vector<const Instruction*> stores{};
 
   for (const Instruction& inst : *spvtest::GetBasicBlock(f, 30)) {
-    if (inst.opcode() == SpvOp::SpvOpLoad) {
+    if (inst.opcode() == spv::Op::OpLoad) {
       loads.push_back(&inst);
     }
-    if (inst.opcode() == SpvOp::SpvOpStore) {
+    if (inst.opcode() == spv::Op::OpStore) {
       stores.push_back(&inst);
     }
   }
@@ -1194,7 +1190,7 @@
   std::vector<const Instruction*> phis{};
 
   for (const Instruction& inst : *spvtest::GetBasicBlock(f, 21)) {
-    if (inst.opcode() == SpvOp::SpvOpPhi) {
+    if (inst.opcode() == spv::Op::OpPhi) {
       phis.push_back(&inst);
     }
   }
diff --git a/test/opt/scalar_replacement_test.cpp b/test/opt/scalar_replacement_test.cpp
index 0c97c80..0ba285b 100644
--- a/test/opt/scalar_replacement_test.cpp
+++ b/test/opt/scalar_replacement_test.cpp
@@ -12,11 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "source/opt/scalar_replacement_pass.h"
-
 #include <string>
 
-#include "gmock/gmock.h"
+#include "source/opt/scalar_replacement_pass.h"
 #include "test/opt/assembly_builder.h"
 #include "test/opt/pass_fixture.h"
 #include "test/opt/pass_utils.h"
@@ -2310,6 +2308,54 @@
   SinglePassRunAndMatch<ScalarReplacementPass>(text, true);
 }
 
+TEST_F(ScalarReplacementTest, RestrictPointer) {
+  // This test makes sure that a variable with the restrict pointer decoration
+  // is replaced, and that the pointer is applied to the new variable.
+  const std::string text = R"(
+; CHECK: OpDecorate [[new_var:%\w+]] RestrictPointer
+; CHECK: [[struct_type:%\w+]] = OpTypeStruct %int
+; CHECK: [[ptr_type:%\w+]] = OpTypePointer PhysicalStorageBuffer [[struct_type]]
+; CHECK: [[dup_struct_type:%\w+]] = OpTypeStruct %int
+; CHECK: {{%\w+}} = OpTypePointer PhysicalStorageBuffer [[dup_struct_type]]
+; CHECK: [[var_type:%\w+]] = OpTypePointer Function [[ptr_type]]
+; CHECK: [[new_var]] = OpVariable [[var_type]] Function
+               OpCapability Shader
+               OpCapability PhysicalStorageBufferAddresses
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel PhysicalStorageBuffer64 GLSL450
+               OpEntryPoint Fragment %2 "main"
+               OpExecutionMode %2 OriginUpperLeft
+               OpMemberDecorate %3 0 Offset 0
+               OpDecorate %3 Block
+               OpMemberDecorate %4 0 Offset 0
+               OpDecorate %4 Block
+               OpDecorate %5 RestrictPointer
+          %6 = OpTypeVoid
+          %7 = OpTypeFunction %6
+          %8 = OpTypeInt 32 1
+          %9 = OpConstant %8 0
+          %3 = OpTypeStruct %8
+         %10 = OpTypePointer PhysicalStorageBuffer %3
+         %11 = OpTypeStruct %10
+          %4 = OpTypeStruct %8
+         %12 = OpTypePointer PhysicalStorageBuffer %4
+         %13 = OpTypePointer Function %11
+         %14 = OpTypePointer Function %10
+         %15 = OpTypePointer Function %12
+         %16 = OpUndef %11
+          %2 = OpFunction %6 None %7
+         %17 = OpLabel
+          %5 = OpVariable %13 Function
+               OpStore %5 %16
+         %18 = OpAccessChain %14 %5 %9
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  SetTargetEnv(SPV_ENV_UNIVERSAL_1_6);
+  SinglePassRunAndMatch<ScalarReplacementPass>(text, true);
+}
+
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools
diff --git a/test/opt/simplification_test.cpp b/test/opt/simplification_test.cpp
index 7727f56..7fce289 100644
--- a/test/opt/simplification_test.cpp
+++ b/test/opt/simplification_test.cpp
@@ -16,7 +16,6 @@
 
 #include "gmock/gmock.h"
 #include "source/opt/simplification_pass.h"
-#include "test/opt/assembly_builder.h"
 #include "test/opt/pass_fixture.h"
 
 namespace spvtools {
diff --git a/test/opt/spread_volatile_semantics_test.cpp b/test/opt/spread_volatile_semantics_test.cpp
index dbb889c..4328c39 100644
--- a/test/opt/spread_volatile_semantics_test.cpp
+++ b/test/opt/spread_volatile_semantics_test.cpp
@@ -14,7 +14,6 @@
 
 #include <string>
 
-#include "gmock/gmock.h"
 #include "test/opt/pass_fixture.h"
 #include "test/opt/pass_utils.h"
 
diff --git a/test/opt/strength_reduction_test.cpp b/test/opt/strength_reduction_test.cpp
index 31d0503..a37c6c2 100644
--- a/test/opt/strength_reduction_test.cpp
+++ b/test/opt/strength_reduction_test.cpp
@@ -12,16 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <algorithm>
-#include <cstdarg>
-#include <iostream>
-#include <sstream>
 #include <string>
 #include <unordered_set>
 #include <vector>
 
 #include "gmock/gmock.h"
-#include "test/opt/assembly_builder.h"
 #include "test/opt/pass_fixture.h"
 #include "test/opt/pass_utils.h"
 
diff --git a/test/opt/struct_cfg_analysis_test.cpp b/test/opt/struct_cfg_analysis_test.cpp
index e7031cb..9c72cee 100644
--- a/test/opt/struct_cfg_analysis_test.cpp
+++ b/test/opt/struct_cfg_analysis_test.cpp
@@ -17,7 +17,6 @@
 #include <string>
 
 #include "gmock/gmock.h"
-#include "test/opt/assembly_builder.h"
 #include "test/opt/pass_fixture.h"
 #include "test/opt/pass_utils.h"
 
diff --git a/test/opt/switch_descriptorset_test.cpp b/test/opt/switch_descriptorset_test.cpp
new file mode 100644
index 0000000..f26178f
--- /dev/null
+++ b/test/opt/switch_descriptorset_test.cpp
@@ -0,0 +1,193 @@
+// Copyright (c) 2023 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.
+
+// Bindless Check Instrumentation Tests.
+// Tests ending with V2 use version 2 record format.
+
+#include <string>
+#include <vector>
+
+#include "test/opt/pass_fixture.h"
+#include "test/opt/pass_utils.h"
+
+namespace spvtools {
+namespace opt {
+namespace {
+
+using SwitchDescriptorSetTest = PassTest<::testing::Test>;
+
+TEST_F(SwitchDescriptorSetTest, Basic) {
+  // #version 450
+  // #extension GL_EXT_buffer_reference : enable
+  //
+  // layout(buffer_reference, buffer_reference_align = 16) buffer bufStruct;
+  //
+  // layout(set = 7, binding = 7) uniform ufoo {
+  //     bufStruct data;
+  //     uint offset;
+  // } u_info;
+  //
+  // layout(buffer_reference, std140) buffer bufStruct {
+  //     layout(offset = 0) int a[2];
+  //     layout(offset = 32) int b;
+  // };
+  //
+  // void main() {
+  //     u_info.data.b = 0xca7;
+  // }
+
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability PhysicalStorageBufferAddresses
+OpExtension "SPV_EXT_physical_storage_buffer"
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpSource GLSL 450
+OpSourceExtension "GL_EXT_buffer_reference"
+OpName %main "main"
+OpName %ufoo "ufoo"
+OpMemberName %ufoo 0 "data"
+OpMemberName %ufoo 1 "offset"
+OpName %bufStruct "bufStruct"
+OpMemberName %bufStruct 0 "a"
+OpMemberName %bufStruct 1 "b"
+OpName %u_info "u_info"
+OpMemberDecorate %ufoo 0 Offset 0
+OpMemberDecorate %ufoo 1 Offset 8
+OpDecorate %ufoo Block
+OpDecorate %_arr_int_uint_2 ArrayStride 16
+OpMemberDecorate %bufStruct 0 Offset 0
+OpMemberDecorate %bufStruct 1 Offset 32
+OpDecorate %bufStruct Block
+OpDecorate %u_info DescriptorSet 7
+;CHECK: OpDecorate %u_info DescriptorSet 31
+OpDecorate %u_info Binding 7
+;CHECK: OpDecorate %u_info Binding 7
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_bufStruct PhysicalStorageBuffer
+%uint = OpTypeInt 32 0
+%ufoo = OpTypeStruct %_ptr_PhysicalStorageBuffer_bufStruct %uint
+%int = OpTypeInt 32 1
+%uint_2 = OpConstant %uint 2
+%_arr_int_uint_2 = OpTypeArray %int %uint_2
+%bufStruct = OpTypeStruct %_arr_int_uint_2 %int
+%_ptr_PhysicalStorageBuffer_bufStruct = OpTypePointer PhysicalStorageBuffer %bufStruct
+%_ptr_Uniform_ufoo = OpTypePointer Uniform %ufoo
+%u_info = OpVariable %_ptr_Uniform_ufoo Uniform
+%int_0 = OpConstant %int 0
+%_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct = OpTypePointer Uniform %_ptr_PhysicalStorageBuffer_bufStruct
+%int_1 = OpConstant %int 1
+%int_3239 = OpConstant %int 3239
+%_ptr_PhysicalStorageBuffer_int = OpTypePointer PhysicalStorageBuffer %int
+%main = OpFunction %void None %3
+%5 = OpLabel
+%17 = OpAccessChain %_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct %u_info %int_0
+%18 = OpLoad %_ptr_PhysicalStorageBuffer_bufStruct %17
+%22 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %18 %int_1
+OpReturn
+OpFunctionEnd
+)";
+  // clang-format off
+
+  SinglePassRunAndMatch<SwitchDescriptorSetPass>(spirv, true, 7, 31);
+}
+
+
+// Make sure DescriptorSet decorations that don't match the requested number
+// are left unchanged.
+TEST_F(SwitchDescriptorSetTest, Unchanged) {
+  // #version 450
+  // #extension GL_EXT_buffer_reference : enable
+  //
+  // layout(buffer_reference, buffer_reference_align = 16) buffer bufStruct;
+  //
+  // layout(set = 11, binding = 7) uniform ufoo {
+  //     bufStruct data;
+  //     uint offset;
+  // } u_info;
+  //
+  // layout(buffer_reference, std140) buffer bufStruct {
+  //     layout(offset = 0) int a[2];
+  //     layout(offset = 32) int b;
+  // };
+  //
+  // void main() {
+  //     u_info.data.b = 0xca7;
+  // }
+
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability PhysicalStorageBufferAddresses
+OpExtension "SPV_EXT_physical_storage_buffer"
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpSource GLSL 450
+OpSourceExtension "GL_EXT_buffer_reference"
+OpName %main "main"
+OpName %ufoo "ufoo"
+OpMemberName %ufoo 0 "data"
+OpMemberName %ufoo 1 "offset"
+OpName %bufStruct "bufStruct"
+OpMemberName %bufStruct 0 "a"
+OpMemberName %bufStruct 1 "b"
+OpName %u_info "u_info"
+OpMemberDecorate %ufoo 0 Offset 0
+OpMemberDecorate %ufoo 1 Offset 8
+OpDecorate %ufoo Block
+OpDecorate %_arr_int_uint_2 ArrayStride 16
+OpMemberDecorate %bufStruct 0 Offset 0
+OpMemberDecorate %bufStruct 1 Offset 32
+OpDecorate %bufStruct Block
+OpDecorate %u_info DescriptorSet 11
+;CHECK: OpDecorate %u_info DescriptorSet 11
+OpDecorate %u_info Binding 7
+;CHECK: OpDecorate %u_info Binding 7
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_bufStruct PhysicalStorageBuffer
+%uint = OpTypeInt 32 0
+%ufoo = OpTypeStruct %_ptr_PhysicalStorageBuffer_bufStruct %uint
+%int = OpTypeInt 32 1
+%uint_2 = OpConstant %uint 2
+%_arr_int_uint_2 = OpTypeArray %int %uint_2
+%bufStruct = OpTypeStruct %_arr_int_uint_2 %int
+%_ptr_PhysicalStorageBuffer_bufStruct = OpTypePointer PhysicalStorageBuffer %bufStruct
+%_ptr_Uniform_ufoo = OpTypePointer Uniform %ufoo
+%u_info = OpVariable %_ptr_Uniform_ufoo Uniform
+%int_0 = OpConstant %int 0
+%_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct = OpTypePointer Uniform %_ptr_PhysicalStorageBuffer_bufStruct
+%int_1 = OpConstant %int 1
+%int_3239 = OpConstant %int 3239
+%_ptr_PhysicalStorageBuffer_int = OpTypePointer PhysicalStorageBuffer %int
+%main = OpFunction %void None %3
+%5 = OpLabel
+%17 = OpAccessChain %_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct %u_info %int_0
+%18 = OpLoad %_ptr_PhysicalStorageBuffer_bufStruct %17
+%22 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %18 %int_1
+OpReturn
+OpFunctionEnd
+)";
+  // clang-format off
+
+  SinglePassRunAndMatch<SwitchDescriptorSetPass>(spirv, true, 7, 31);
+}
+
+}  // namespace
+}  // namespace opt
+}  // namespace spvtools
diff --git a/test/opt/trim_capabilities_pass_test.cpp b/test/opt/trim_capabilities_pass_test.cpp
new file mode 100644
index 0000000..8aaf860
--- /dev/null
+++ b/test/opt/trim_capabilities_pass_test.cpp
@@ -0,0 +1,2134 @@
+// Copyright (c) 2023 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 "spirv-tools/optimizer.hpp"
+#include "test/opt/pass_fixture.h"
+#include "test/opt/pass_utils.h"
+
+namespace spvtools {
+namespace opt {
+namespace {
+
+using TrimCapabilitiesPassTest = PassTest<::testing::Test>;
+
+TEST_F(TrimCapabilitiesPassTest, CheckKnownAliasTransformations) {
+  // Those are expected changes caused by the test process:
+  //  - SPV is assembled. -> capability goes from text to number.
+  //  - SPV is optimized.
+  //  - SPV is disassembled -> capability goes from number to text.
+  //  - CHECK rule compares both text versions.
+  // Because some capabilities share the same number (aliases), the text
+  // compared with the CHECK rules depends on which alias is the first on the
+  // SPIRV-Headers enum. This could change, and we want to easily distinguish
+  // real failure from alias order change. This test is only here to list known
+  // alias transformations. If this test breaks, it's not a bug in the
+  // optimization pass, but just the SPIRV-Headers enum order that has changed.
+  // If that happens, tests needs to be updated to the correct alias is used in
+  // the CHECK rule.
+  const std::string kTest = R"(
+               OpCapability Linkage
+               OpCapability StorageUniform16
+               OpCapability StorageUniformBufferBlock16
+               OpCapability ShaderViewportIndexLayerNV
+               OpCapability FragmentBarycentricNV
+               OpCapability ShadingRateNV
+               OpCapability ShaderNonUniformEXT
+               OpCapability RuntimeDescriptorArrayEXT
+               OpCapability InputAttachmentArrayDynamicIndexingEXT
+               OpCapability UniformTexelBufferArrayDynamicIndexingEXT
+               OpCapability StorageTexelBufferArrayDynamicIndexingEXT
+               OpCapability UniformBufferArrayNonUniformIndexingEXT
+               OpCapability SampledImageArrayNonUniformIndexingEXT
+               OpCapability StorageBufferArrayNonUniformIndexingEXT
+               OpCapability StorageImageArrayNonUniformIndexingEXT
+               OpCapability InputAttachmentArrayNonUniformIndexingEXT
+               OpCapability UniformTexelBufferArrayNonUniformIndexingEXT
+               OpCapability StorageTexelBufferArrayNonUniformIndexingEXT
+               OpCapability VulkanMemoryModelKHR
+               OpCapability VulkanMemoryModelDeviceScopeKHR
+               OpCapability PhysicalStorageBufferAddressesEXT
+               OpCapability DemoteToHelperInvocationEXT
+               OpCapability DotProductInputAllKHR
+               OpCapability DotProductInput4x8BitKHR
+               OpCapability DotProductInput4x8BitPackedKHR
+               OpCapability DotProductKHR
+; CHECK: OpCapability Linkage
+; CHECK-NOT: OpCapability StorageUniform16
+; CHECK-NOT: OpCapability StorageUniformBufferBlock16
+; CHECK-NOT: OpCapability ShaderViewportIndexLayerNV
+; CHECK-NOT: OpCapability FragmentBarycentricNV
+; CHECK-NOT: OpCapability ShadingRateNV
+; CHECK-NOT: OpCapability ShaderNonUniformEXT
+; CHECK-NOT: OpCapability RuntimeDescriptorArrayEXT
+; CHECK-NOT: OpCapability InputAttachmentArrayDynamicIndexingEXT
+; CHECK-NOT: OpCapability UniformTexelBufferArrayDynamicIndexingEXT
+; CHECK-NOT: OpCapability StorageTexelBufferArrayDynamicIndexingEXT
+; CHECK-NOT: OpCapability UniformBufferArrayNonUniformIndexingEXT
+; CHECK-NOT: OpCapability SampledImageArrayNonUniformIndexingEXT
+; CHECK-NOT: OpCapability StorageBufferArrayNonUniformIndexingEXT
+; CHECK-NOT: OpCapability StorageImageArrayNonUniformIndexingEXT
+; CHECK-NOT: OpCapability InputAttachmentArrayNonUniformIndexingEXT
+; CHECK-NOT: OpCapability UniformTexelBufferArrayNonUniformIndexingEXT
+; CHECK-NOT: OpCapability StorageTexelBufferArrayNonUniformIndexingEXT
+; CHECK-NOT: OpCapability VulkanMemoryModelKHR
+; CHECK-NOT: OpCapability VulkanMemoryModelDeviceScopeKHR
+; CHECK-NOT: OpCapability PhysicalStorageBufferAddressesEXT
+; CHECK-NOT: OpCapability DemoteToHelperInvocationEXT
+; CHECK-NOT: OpCapability DotProductInputAllKHR
+; CHECK-NOT: OpCapability DotProductInput4x8BitKHR
+; CHECK-NOT: OpCapability DotProductInput4x8BitPackedKHR
+; CHECK-NOT: OpCapability DotProductKHR
+; CHECK: OpCapability UniformAndStorageBuffer16BitAccess
+; CHECK: OpCapability StorageBuffer16BitAccess
+; CHECK: OpCapability ShaderViewportIndexLayerEXT
+; CHECK: OpCapability FragmentBarycentricKHR
+; CHECK: OpCapability FragmentDensityEXT
+; CHECK: OpCapability ShaderNonUniform
+; CHECK: OpCapability RuntimeDescriptorArray
+; CHECK: OpCapability InputAttachmentArrayDynamicIndexing
+; CHECK: OpCapability UniformTexelBufferArrayDynamicIndexing
+; CHECK: OpCapability StorageTexelBufferArrayDynamicIndexing
+; CHECK: OpCapability UniformBufferArrayNonUniformIndexing
+; CHECK: OpCapability SampledImageArrayNonUniformIndexing
+; CHECK: OpCapability StorageBufferArrayNonUniformIndexing
+; CHECK: OpCapability StorageImageArrayNonUniformIndexing
+; CHECK: OpCapability InputAttachmentArrayNonUniformIndexing
+; CHECK: OpCapability UniformTexelBufferArrayNonUniformIndexing
+; CHECK: OpCapability StorageTexelBufferArrayNonUniformIndexing
+; CHECK: OpCapability VulkanMemoryModel
+; CHECK: OpCapability VulkanMemoryModelDeviceScope
+; CHECK: OpCapability PhysicalStorageBufferAddresses
+; CHECK: OpCapability DemoteToHelperInvocation
+; CHECK: OpCapability DotProductInputAll
+; CHECK: OpCapability DotProductInput4x8Bit
+; CHECK: OpCapability DotProductInput4x8BitPacked
+; CHECK: OpCapability DotProduct
+               OpMemoryModel Logical Vulkan
+               OpEntryPoint GLCompute %1 "main"
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+          %1 = OpFunction %void None %3
+          %6 = OpLabel
+               OpReturn
+               OpFunctionEnd;
+  )";
+  SetTargetEnv(SPV_ENV_VULKAN_1_3);
+  const auto result =
+      SinglePassRunAndMatch<EmptyPass>(kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange);
+}
+
+TEST_F(TrimCapabilitiesPassTest, LinkagePreventsChanges) {
+  const std::string kTest = R"(
+               OpCapability Linkage
+               OpCapability ClipDistance
+               OpCapability CullDistance
+               OpCapability DemoteToHelperInvocation
+               OpCapability DeviceGroup
+               OpCapability DrawParameters
+               OpCapability Float16
+               OpCapability Float64
+               OpCapability FragmentBarycentricKHR
+               OpCapability FragmentFullyCoveredEXT
+               OpCapability FragmentShadingRateKHR
+               OpCapability GroupNonUniform
+               OpCapability GroupNonUniformArithmetic
+               OpCapability GroupNonUniformBallot
+               OpCapability GroupNonUniformQuad
+               OpCapability GroupNonUniformShuffle
+               OpCapability Image1D
+               OpCapability ImageBuffer
+               OpCapability ImageGatherExtended
+               OpCapability ImageMSArray
+               OpCapability ImageQuery
+               OpCapability InputAttachment
+               OpCapability InputAttachmentArrayNonUniformIndexing
+               OpCapability Int16
+               OpCapability Int64
+               OpCapability Int64Atomics
+               OpCapability Int64ImageEXT
+               OpCapability MeshShadingNV
+               OpCapability MinLod
+               OpCapability MultiView
+               OpCapability MultiViewport
+               OpCapability PhysicalStorageBufferAddresses
+               OpCapability RayQueryKHR
+               OpCapability RayTracingKHR
+               OpCapability RayTracingNV
+               OpCapability RayTraversalPrimitiveCullingKHR
+               OpCapability RuntimeDescriptorArray
+               OpCapability SampleMaskPostDepthCoverage
+               OpCapability SampleRateShading
+               OpCapability Sampled1D
+               OpCapability SampledBuffer
+               OpCapability SampledImageArrayNonUniformIndexing
+               OpCapability Shader
+               OpCapability ShaderClockKHR
+               OpCapability ShaderLayer
+               OpCapability ShaderNonUniform
+               OpCapability ShaderViewportIndex
+               OpCapability ShaderViewportIndexLayerEXT
+               OpCapability SparseResidency
+               OpCapability StencilExportEXT
+               OpCapability StorageImageArrayNonUniformIndexingEXT
+               OpCapability StorageImageExtendedFormats
+               OpCapability StorageImageReadWithoutFormat
+               OpCapability StorageImageWriteWithoutFormat
+               OpCapability StorageInputOutput16
+               OpCapability StoragePushConstant16
+               OpCapability StorageTexelBufferArrayNonUniformIndexing
+               OpCapability StorageUniform16
+               OpCapability StorageUniformBufferBlock16
+               OpCapability Tessellation
+               OpCapability UniformTexelBufferArrayNonUniformIndexing
+               OpCapability VulkanMemoryModel
+               OpExtension "SPV_EXT_fragment_fully_covered"
+               OpExtension "SPV_EXT_shader_image_int64"
+               OpExtension "SPV_EXT_shader_stencil_export"
+               OpExtension "SPV_EXT_shader_viewport_index_layer"
+               OpExtension "SPV_KHR_fragment_shader_barycentric"
+               OpExtension "SPV_KHR_fragment_shading_rate"
+               OpExtension "SPV_KHR_post_depth_coverage"
+               OpExtension "SPV_KHR_ray_query"
+               OpExtension "SPV_KHR_ray_tracing"
+               OpExtension "SPV_KHR_shader_clock"
+               OpExtension "SPV_NV_mesh_shader"
+               OpExtension "SPV_NV_ray_tracing"
+               OpExtension "SPV_NV_viewport_array2"
+; CHECK: OpCapability Linkage
+; CHECK: OpCapability ClipDistance
+; CHECK: OpCapability CullDistance
+; CHECK: OpCapability DemoteToHelperInvocation
+; CHECK: OpCapability DeviceGroup
+; CHECK: OpCapability DrawParameters
+; CHECK: OpCapability Float16
+; CHECK: OpCapability Float64
+; CHECK: OpCapability FragmentBarycentricKHR
+; CHECK: OpCapability FragmentFullyCoveredEXT
+; CHECK: OpCapability FragmentShadingRateKHR
+; CHECK: OpCapability GroupNonUniform
+; CHECK: OpCapability GroupNonUniformArithmetic
+; CHECK: OpCapability GroupNonUniformBallot
+; CHECK: OpCapability GroupNonUniformQuad
+; CHECK: OpCapability GroupNonUniformShuffle
+; CHECK: OpCapability Image1D
+; CHECK: OpCapability ImageBuffer
+; CHECK: OpCapability ImageGatherExtended
+; CHECK: OpCapability ImageMSArray
+; CHECK: OpCapability ImageQuery
+; CHECK: OpCapability InputAttachment
+; CHECK: OpCapability InputAttachmentArrayNonUniformIndexing
+; CHECK: OpCapability Int16
+; CHECK: OpCapability Int64
+; CHECK: OpCapability Int64Atomics
+; CHECK: OpCapability Int64ImageEXT
+; CHECK: OpCapability MeshShadingNV
+; CHECK: OpCapability MinLod
+; CHECK: OpCapability MultiView
+; CHECK: OpCapability MultiViewport
+; CHECK: OpCapability PhysicalStorageBufferAddresses
+; CHECK: OpCapability RayQueryKHR
+; CHECK: OpCapability RayTracingKHR
+; CHECK: OpCapability RayTracingNV
+; CHECK: OpCapability RayTraversalPrimitiveCullingKHR
+; CHECK: OpCapability RuntimeDescriptorArray
+; CHECK: OpCapability SampleMaskPostDepthCoverage
+; CHECK: OpCapability SampleRateShading
+; CHECK: OpCapability Sampled1D
+; CHECK: OpCapability SampledBuffer
+; CHECK: OpCapability SampledImageArrayNonUniformIndexing
+; CHECK: OpCapability Shader
+; CHECK: OpCapability ShaderClockKHR
+; CHECK: OpCapability ShaderLayer
+; CHECK: OpCapability ShaderNonUniform
+; CHECK: OpCapability ShaderViewportIndex
+; CHECK: OpCapability ShaderViewportIndexLayerEXT
+; CHECK: OpCapability SparseResidency
+; CHECK: OpCapability StencilExportEXT
+; CHECK: OpCapability StorageImageArrayNonUniformIndexing
+; CHECK: OpCapability StorageImageExtendedFormats
+; CHECK: OpCapability StorageImageReadWithoutFormat
+; CHECK: OpCapability StorageImageWriteWithoutFormat
+; CHECK: OpCapability StorageInputOutput16
+; CHECK: OpCapability StoragePushConstant16
+; CHECK: OpCapability StorageTexelBufferArrayNonUniformIndexing
+; CHECK: OpCapability Tessellation
+; CHECK: OpCapability UniformTexelBufferArrayNonUniformIndex
+; CHECK: OpCapability VulkanMemoryModel
+; CHECK: OpExtension "SPV_EXT_fragment_fully_covered"
+; CHECK: OpExtension "SPV_EXT_shader_image_int64"
+; CHECK: OpExtension "SPV_EXT_shader_stencil_export"
+; CHECK: OpExtension "SPV_EXT_shader_viewport_index_layer"
+; CHECK: OpExtension "SPV_KHR_fragment_shader_barycentric"
+; CHECK: OpExtension "SPV_KHR_fragment_shading_rate"
+; CHECK: OpExtension "SPV_KHR_post_depth_coverage"
+; CHECK: OpExtension "SPV_KHR_ray_query"
+; CHECK: OpExtension "SPV_KHR_ray_tracing"
+; CHECK: OpExtension "SPV_KHR_shader_clock"
+; CHECK: OpExtension "SPV_NV_mesh_shader"
+; CHECK: OpExtension "SPV_NV_ray_tracing"
+; CHECK: OpExtension "SPV_NV_viewport_array2"
+               OpMemoryModel Logical Vulkan
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+          %1 = OpFunction %void None %3
+          %6 = OpLabel
+               OpReturn
+               OpFunctionEnd;
+  )";
+  SetTargetEnv(SPV_ENV_VULKAN_1_3);
+  const auto result =
+      SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange);
+}
+
+TEST_F(TrimCapabilitiesPassTest, KeepShader) {
+  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
+               OpReturn
+               OpFunctionEnd;
+  )";
+  const auto result =
+      SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange);
+}
+
+TEST_F(TrimCapabilitiesPassTest, KeepShaderClockWhenInUse) {
+  const std::string kTest = R"(
+               OpCapability Shader
+               OpCapability Int64
+               OpCapability ShaderClockKHR
+               OpExtension "SPV_KHR_shader_clock"
+; CHECK: OpCapability ShaderClockKHR
+; CHECK: OpExtension "SPV_KHR_shader_clock"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "main"
+       %void = OpTypeVoid
+       %uint = OpTypeInt 32 0
+      %ulong = OpTypeInt 64 0
+      %scope = OpConstant %uint 1
+          %3 = OpTypeFunction %void
+          %1 = OpFunction %void None %3
+          %6 = OpLabel
+          %7 = OpReadClockKHR %ulong %scope
+               OpReturn
+               OpFunctionEnd;
+  )";
+  const auto result =
+      SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange);
+}
+
+TEST_F(TrimCapabilitiesPassTest, TrimShaderClockWhenUnused) {
+  const std::string kTest = R"(
+               OpCapability Shader
+               OpCapability Int64
+               OpCapability ShaderClockKHR
+               OpExtension "SPV_KHR_shader_clock"
+; CHECK-NOT: OpCapability ShaderClockKHR
+; CHECK-NOT: OpExtension "SPV_KHR_shader_clock"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "main"
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+          %1 = OpFunction %void None %3
+          %6 = OpLabel
+               OpReturn
+               OpFunctionEnd;
+  )";
+  const auto result =
+      SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
+}
+
+TEST_F(TrimCapabilitiesPassTest, AMDShaderBallotExtensionRemains) {
+  const std::string kTest = R"(
+               OpCapability Shader
+               OpCapability Groups
+               OpExtension "SPV_AMD_shader_ballot"
+; CHECK: OpCapability Groups
+; CHECK: OpExtension "SPV_AMD_shader_ballot"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "main"
+       %void = OpTypeVoid
+       %uint = OpTypeInt 32 0
+          %1 = OpTypeFunction %void
+     %uint_0 = OpConstant %uint 0
+          %2 = OpFunction %void None %1
+          %3 = OpLabel
+          %4 = OpGroupIAddNonUniformAMD %uint %uint_0 ExclusiveScan %uint_0
+               OpReturn
+               OpFunctionEnd;
+  )";
+  const auto result =
+      SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange);
+}
+
+TEST_F(TrimCapabilitiesPassTest, AMDShaderBallotExtensionRemoved) {
+  const std::string kTest = R"(
+               OpCapability Shader
+               OpCapability Groups
+               OpExtension "SPV_AMD_shader_ballot"
+; CHECK-NOT: OpCapability Groups
+; CHECK-NOT: OpExtension "SPV_AMD_shader_ballot"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "main"
+       %void = OpTypeVoid
+          %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, MinLod_RemovedIfNotUsed) {
+  const std::string kTest = R"(
+                      OpCapability Shader
+                      OpCapability Sampled1D
+                      OpCapability MinLod
+; CHECK-NOT:          OpCapability MinLod
+                      OpMemoryModel Logical GLSL450
+                      OpEntryPoint GLCompute %1 "main"
+              %void = OpTypeVoid
+             %float = OpTypeFloat 32
+           %v3float = OpTypeVector %float 3
+           %v4float = OpTypeVector %float 4
+        %type_image = OpTypeImage %float Cube 2 0 0 1 Rgba32f
+    %ptr_type_image = OpTypePointer UniformConstant %type_image
+      %type_sampler = OpTypeSampler
+  %ptr_type_sampler = OpTypePointer UniformConstant %type_sampler
+           %float_0 = OpConstant %float 0
+         %float_000 = OpConstantComposite %v3float %float_0 %float_0 %float_0
+             %image = OpVariable %ptr_type_image UniformConstant
+           %sampler = OpVariable %ptr_type_sampler UniformConstant
+                 %1 = OpTypeFunction %void
+                 %2 = OpFunction %void None %1
+                 %3 = OpLabel
+                %21 = OpLoad %type_image %image
+                %22 = OpLoad %type_sampler %sampler
+                %24 = OpSampledImage %type_sampled_image %21 %22
+                %25 = OpImageSampleImplicitLod %v4float %24 %float_000
+                      OpReturn
+                      OpFunctionEnd;
+  )";
+  const auto result =
+      SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
+}
+
+TEST_F(TrimCapabilitiesPassTest, MinLod_RemainsWithOpImageSampleImplicitLod) {
+  const std::string kTest = R"(
+                      OpCapability Shader
+                      OpCapability Sampled1D
+                      OpCapability MinLod
+; CHECK:              OpCapability MinLod
+                      OpMemoryModel Logical GLSL450
+                      OpEntryPoint GLCompute %1 "main"
+              %void = OpTypeVoid
+             %float = OpTypeFloat 32
+           %v3float = OpTypeVector %float 3
+           %v4float = OpTypeVector %float 4
+        %type_image = OpTypeImage %float Cube 2 0 0 1 Rgba32f
+    %ptr_type_image = OpTypePointer UniformConstant %type_image
+      %type_sampler = OpTypeSampler
+  %ptr_type_sampler = OpTypePointer UniformConstant %type_sampler
+           %float_0 = OpConstant %float 0
+         %float_000 = OpConstantComposite %v3float %float_0 %float_0 %float_0
+             %image = OpVariable %ptr_type_image UniformConstant
+           %sampler = OpVariable %ptr_type_sampler UniformConstant
+                 %1 = OpTypeFunction %void
+                 %2 = OpFunction %void None %1
+                 %3 = OpLabel
+                %21 = OpLoad %type_image %image
+                %22 = OpLoad %type_sampler %sampler
+                %24 = OpSampledImage %type_sampled_image %21 %22
+                %25 = OpImageSampleImplicitLod %v4float %24 %float_000 MinLod %float_0
+                      OpReturn
+                      OpFunctionEnd;
+  )";
+  const auto result =
+      SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange);
+}
+
+TEST_F(TrimCapabilitiesPassTest,
+       MinLod_RemainsWithOpImageSparseSampleImplicitLod) {
+  const std::string kTest = R"(
+                      OpCapability Shader
+                      OpCapability SparseResidency
+                      OpCapability ImageGatherExtended
+                      OpCapability MinLod
+; CHECK:              OpCapability MinLod
+                      OpMemoryModel Logical GLSL450
+                      OpEntryPoint Fragment %2 "main"
+                      OpExecutionMode %2 OriginUpperLeft
+              %void = OpTypeVoid
+              %uint = OpTypeInt 32 0
+             %float = OpTypeFloat 32
+           %v2float = OpTypeVector %float 2
+           %v3float = OpTypeVector %float 3
+           %v4float = OpTypeVector %float 4
+        %type_image = OpTypeImage %float 2D 2 0 0 1 Unknown
+    %ptr_type_image = OpTypePointer UniformConstant %type_image
+      %type_sampler = OpTypeSampler
+  %ptr_type_sampler = OpTypePointer UniformConstant %type_sampler
+%type_sampled_image = OpTypeSampledImage %type_image
+     %sparse_struct = OpTypeStruct %uint %v4float
+           %float_0 = OpConstant %float 0
+          %float_00 = OpConstantComposite %v2float %float_0 %float_0
+         %float_000 = OpConstantComposite %v3float %float_0 %float_0 %float_0
+             %image = OpVariable %ptr_type_image UniformConstant
+           %sampler = OpVariable %ptr_type_sampler UniformConstant
+                 %1 = OpTypeFunction %void
+                 %2 = OpFunction %void None %1
+                 %3 = OpLabel
+                %21 = OpLoad %type_image %image
+                %22 = OpLoad %type_sampler %sampler
+                %24 = OpSampledImage %type_sampled_image %21 %22
+                %25 = OpImageSparseSampleImplicitLod %sparse_struct %24 %float_00 MinLod %float_0
+                      OpReturn
+                      OpFunctionEnd;
+  )";
+  const auto result =
+      SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange);
+}
+
+TEST_F(TrimCapabilitiesPassTest, MinLod_DetectsMinLodWithBitmaskImageOperand) {
+  const std::string kTest = R"(
+                            OpCapability MinLod
+; CHECK:                    OpCapability MinLod
+                            OpCapability Shader
+                            OpCapability SparseResidency
+                            OpCapability ImageGatherExtended
+                            OpMemoryModel Logical GLSL450
+                            OpEntryPoint Fragment %1 "main"
+                            OpExecutionMode %1 OriginUpperLeft
+            %type_sampler = OpTypeSampler
+                     %int = OpTypeInt 32 1
+                   %float = OpTypeFloat 32
+                   %v2int = OpTypeVector %int 2
+                 %v2float = OpTypeVector %float 2
+                 %v4float = OpTypeVector %float 4
+             %ptr_sampler = OpTypePointer UniformConstant %type_sampler
+              %type_image = OpTypeImage %float 2D 2 0 0 1 Unknown
+               %ptr_image = OpTypePointer UniformConstant %type_image
+                    %void = OpTypeVoid
+                    %uint = OpTypeInt 32 0
+      %type_sampled_image = OpTypeSampledImage %type_image
+             %type_struct = OpTypeStruct %uint %v4float
+
+                   %int_1 = OpConstant %int 1
+                 %float_0 = OpConstant %float 0
+                 %float_1 = OpConstant %float 1
+                       %8 = OpConstantComposite %v2float %float_0 %float_0
+                      %12 = OpConstantComposite %v2int %int_1 %int_1
+
+                       %2 = OpVariable %ptr_sampler UniformConstant
+                       %3 = OpVariable %ptr_image UniformConstant
+                      %27 = OpTypeFunction %void
+                       %1 = OpFunction %void None %27
+                      %28 = OpLabel
+                      %29 = OpLoad %type_image %3
+                      %30 = OpLoad %type_sampler %2
+                      %31 = OpSampledImage %type_sampled_image %29 %30
+                      %32 = OpImageSparseSampleImplicitLod %type_struct %31 %8 ConstOffset|MinLod %12 %float_0
+                            OpReturn
+                            OpFunctionEnd
+  )";
+  const auto result =
+      SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange);
+}
+
+TEST_F(TrimCapabilitiesPassTest,
+       StorageInputOutput16_RemainsWithInputPointer_Vulkan1_0) {
+  const std::string kTest = R"(
+                      OpCapability Shader
+                      OpCapability Float16
+                      OpCapability StorageInputOutput16
+                      OpExtension "SPV_KHR_16bit_storage"
+; CHECK:              OpCapability StorageInputOutput16
+; CHECK:              OpExtension "SPV_KHR_16bit_storage"
+                      OpMemoryModel Logical GLSL450
+                      OpEntryPoint GLCompute %2 "main"
+              %void = OpTypeVoid
+              %half = OpTypeFloat 16
+               %ptr = OpTypePointer Input %half
+                 %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,
+       StorageInputOutput16_RemainsWithInputPointer_Vulkan1_1) {
+  const std::string kTest = R"(
+                      OpCapability Shader
+                      OpCapability Float16
+                      OpCapability StorageInputOutput16
+                      OpExtension "SPV_KHR_16bit_storage"
+; CHECK:              OpCapability StorageInputOutput16
+; CHECK-NOT:          OpExtension "SPV_KHR_16bit_storage"
+                      OpMemoryModel Logical GLSL450
+                      OpEntryPoint GLCompute %2 "main"
+              %void = OpTypeVoid
+              %half = OpTypeFloat 16
+               %ptr = OpTypePointer Input %half
+                 %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,
+       StorageInputOutput16_RemainsWithInputPointerArray_Vulkan1_0) {
+  const std::string kTest = R"(
+                      OpCapability Shader
+                      OpCapability Float16
+                      OpCapability StorageInputOutput16
+                      OpExtension "SPV_KHR_16bit_storage"
+; CHECK:              OpCapability StorageInputOutput16
+; CHECK:              OpExtension "SPV_KHR_16bit_storage"
+                      OpMemoryModel Logical GLSL450
+                      OpEntryPoint GLCompute %2 "main"
+              %void = OpTypeVoid
+              %half = OpTypeFloat 16
+              %uint = OpTypeInt 32 0
+            %uint_1 = OpConstant %uint 1
+             %array = OpTypeArray %half %uint_1
+               %ptr = OpTypePointer Input %array
+                 %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,
+       StorageInputOutput16_RemainsWithInputPointerArray_Vulkan1_1) {
+  const std::string kTest = R"(
+                      OpCapability Shader
+                      OpCapability Float16
+                      OpCapability StorageInputOutput16
+                      OpExtension "SPV_KHR_16bit_storage"
+; CHECK:              OpCapability StorageInputOutput16
+; CHECK-NOT:          OpExtension "SPV_KHR_16bit_storage"
+                      OpMemoryModel Logical GLSL450
+                      OpEntryPoint GLCompute %2 "main"
+              %void = OpTypeVoid
+              %half = OpTypeFloat 16
+              %uint = OpTypeInt 32 0
+            %uint_1 = OpConstant %uint 1
+             %array = OpTypeArray %half %uint_1
+               %ptr = OpTypePointer Input %array
+                 %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,
+       StorageInputOutput16_RemainsWithInputPointerStruct_Vulkan1_0) {
+  const std::string kTest = R"(
+                      OpCapability Shader
+                      OpCapability Float16
+                      OpCapability StorageInputOutput16
+                      OpExtension "SPV_KHR_16bit_storage"
+; CHECK:              OpCapability StorageInputOutput16
+; CHECK:              OpExtension "SPV_KHR_16bit_storage"
+                      OpMemoryModel Logical GLSL450
+                      OpEntryPoint GLCompute %2 "main"
+              %void = OpTypeVoid
+              %half = OpTypeFloat 16
+            %struct = OpTypeStruct %half
+               %ptr = OpTypePointer Input %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,
+       StorageInputOutput16_RemainsWithInputPointerStruct_Vulkan1_1) {
+  const std::string kTest = R"(
+                      OpCapability Shader
+                      OpCapability Float16
+                      OpCapability StorageInputOutput16
+                      OpExtension "SPV_KHR_16bit_storage"
+; CHECK:              OpCapability StorageInputOutput16
+; CHECK-NOT:          OpExtension "SPV_KHR_16bit_storage"
+                      OpMemoryModel Logical GLSL450
+                      OpEntryPoint GLCompute %2 "main"
+              %void = OpTypeVoid
+              %half = OpTypeFloat 16
+            %struct = OpTypeStruct %half
+               %ptr = OpTypePointer Input %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::SuccessWithChange);
+}
+
+TEST_F(TrimCapabilitiesPassTest,
+       StorageInputOutput16_RemainsWithInputPointerStructOfStruct_Vulkan1_0) {
+  const std::string kTest = R"(
+                      OpCapability Shader
+                      OpCapability Float16
+                      OpCapability StorageInputOutput16
+                      OpExtension "SPV_KHR_16bit_storage"
+; CHECK:              OpCapability StorageInputOutput16
+; CHECK:              OpExtension "SPV_KHR_16bit_storage"
+                      OpMemoryModel Logical GLSL450
+                      OpEntryPoint GLCompute %2 "main"
+              %void = OpTypeVoid
+              %half = OpTypeFloat 16
+             %float = OpTypeFloat 32
+            %struct = OpTypeStruct %float %half
+            %parent = OpTypeStruct %float %struct
+               %ptr = OpTypePointer Input %parent
+                 %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,
+       StorageInputOutput16_RemainsWithInputPointerStructOfStruct_Vulkan1_1) {
+  const std::string kTest = R"(
+                      OpCapability Shader
+                      OpCapability Float16
+                      OpCapability StorageInputOutput16
+                      OpExtension "SPV_KHR_16bit_storage"
+; CHECK:              OpCapability StorageInputOutput16
+; CHECK-NOT:          OpExtension "SPV_KHR_16bit_storage"
+                      OpMemoryModel Logical GLSL450
+                      OpEntryPoint GLCompute %2 "main"
+              %void = OpTypeVoid
+              %half = OpTypeFloat 16
+             %float = OpTypeFloat 32
+            %struct = OpTypeStruct %float %half
+            %parent = OpTypeStruct %float %struct
+               %ptr = OpTypePointer Input %parent
+                 %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,
+       StorageInputOutput16_RemainsWithInputPointerArrayOfStruct_Vulkan1_0) {
+  const std::string kTest = R"(
+                      OpCapability Shader
+                      OpCapability Float16
+                      OpCapability StorageInputOutput16
+                      OpExtension "SPV_KHR_16bit_storage"
+; CHECK:              OpCapability StorageInputOutput16
+; CHECK:              OpExtension "SPV_KHR_16bit_storage"
+                      OpMemoryModel Logical GLSL450
+                      OpEntryPoint GLCompute %2 "main"
+              %void = OpTypeVoid
+              %half = OpTypeFloat 16
+            %struct = OpTypeStruct %half
+              %uint = OpTypeInt 32 0
+            %uint_1 = OpConstant %uint 1
+             %array = OpTypeArray %struct %uint_1
+               %ptr = OpTypePointer Input %array
+                 %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,
+       StorageInputOutput16_RemainsWithInputPointerArrayOfStruct_Vulkan1_1) {
+  const std::string kTest = R"(
+                      OpCapability Shader
+                      OpCapability Float16
+                      OpCapability StorageInputOutput16
+                      OpExtension "SPV_KHR_16bit_storage"
+; CHECK:              OpCapability StorageInputOutput16
+; CHECK-NOT:          OpExtension "SPV_KHR_16bit_storage"
+                      OpMemoryModel Logical GLSL450
+                      OpEntryPoint GLCompute %2 "main"
+              %void = OpTypeVoid
+              %half = OpTypeFloat 16
+            %struct = OpTypeStruct %half
+              %uint = OpTypeInt 32 0
+            %uint_1 = OpConstant %uint 1
+             %array = OpTypeArray %struct %uint_1
+               %ptr = OpTypePointer Input %array
+                 %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,
+       StorageInputOutput16_RemainsWithInputPointerVector_Vulkan1_0) {
+  const std::string kTest = R"(
+                      OpCapability Shader
+                      OpCapability Float16
+                      OpCapability StorageInputOutput16
+                      OpExtension "SPV_KHR_16bit_storage"
+; CHECK:              OpCapability StorageInputOutput16
+; CHECK:              OpExtension "SPV_KHR_16bit_storage"
+                      OpMemoryModel Logical GLSL450
+                      OpEntryPoint GLCompute %2 "main"
+              %void = OpTypeVoid
+              %half = OpTypeFloat 16
+            %vector = OpTypeVector %half 4
+               %ptr = OpTypePointer Input %vector
+                 %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,
+       StorageInputOutput16_RemainsWithInputPointerVector_Vulkan1_1) {
+  const std::string kTest = R"(
+                      OpCapability Shader
+                      OpCapability Float16
+                      OpCapability StorageInputOutput16
+                      OpExtension "SPV_KHR_16bit_storage"
+; CHECK:              OpCapability StorageInputOutput16
+; CHECK-NOT:          OpExtension "SPV_KHR_16bit_storage"
+                      OpMemoryModel Logical GLSL450
+                      OpEntryPoint GLCompute %2 "main"
+              %void = OpTypeVoid
+              %half = OpTypeFloat 16
+            %vector = OpTypeVector %half 4
+               %ptr = OpTypePointer Input %vector
+                 %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,
+       StorageInputOutput16_RemainsWithInputPointerMatrix_Vulkan1_0) {
+  const std::string kTest = R"(
+                      OpCapability Shader
+                      OpCapability Float16
+                      OpCapability StorageInputOutput16
+                      OpExtension "SPV_KHR_16bit_storage"
+; CHECK:              OpCapability StorageInputOutput16
+; CHECK:              OpExtension "SPV_KHR_16bit_storage"
+                      OpMemoryModel Logical GLSL450
+                      OpEntryPoint GLCompute %2 "main"
+              %void = OpTypeVoid
+              %half = OpTypeFloat 16
+            %vector = OpTypeVector %half 4
+            %matrix = OpTypeMatrix %vector 4
+               %ptr = OpTypePointer Input %matrix
+                 %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,
+       StorageInputOutput16_RemainsWithInputPointerMatrix_Vulkan1_1) {
+  const std::string kTest = R"(
+                      OpCapability Shader
+                      OpCapability Float16
+                      OpCapability StorageInputOutput16
+                      OpExtension "SPV_KHR_16bit_storage"
+; CHECK:              OpCapability StorageInputOutput16
+; CHECK-NOT:          OpExtension "SPV_KHR_16bit_storage"
+                      OpMemoryModel Logical GLSL450
+                      OpEntryPoint GLCompute %2 "main"
+              %void = OpTypeVoid
+              %half = OpTypeFloat 16
+            %vector = OpTypeVector %half 4
+            %matrix = OpTypeMatrix %vector 4
+               %ptr = OpTypePointer Input %matrix
+                 %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,
+       StorageInputOutput16_IsRemovedWithoutInputPointer) {
+  const std::string kTest = R"(
+                      OpCapability Shader
+                      OpCapability Float16
+                      OpCapability StorageInputOutput16
+                      OpExtension "SPV_KHR_16bit_storage"
+; CHECK-NOT:          OpCapability StorageInputOutput16
+; CHECK-NOT:          OpExtension "SPV_KHR_16bit_storage"
+                      OpMemoryModel Logical GLSL450
+                      OpEntryPoint GLCompute %2 "main"
+              %void = OpTypeVoid
+                 %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::SuccessWithChange);
+}
+
+TEST_F(TrimCapabilitiesPassTest,
+       StorageInputOutput16_RemainsWithOutputPointer_Vulkan1_0) {
+  const std::string kTest = R"(
+                      OpCapability Shader
+                      OpCapability Float16
+                      OpCapability StorageInputOutput16
+                      OpExtension "SPV_KHR_16bit_storage"
+; CHECK:              OpCapability StorageInputOutput16
+; CHECK:              OpExtension "SPV_KHR_16bit_storage"
+                      OpMemoryModel Logical GLSL450
+                      OpEntryPoint GLCompute %2 "main"
+              %void = OpTypeVoid
+              %half = OpTypeFloat 16
+               %ptr = OpTypePointer Output %half
+                 %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,
+       StorageInputOutput16_RemainsWithOutputPointer_Vulkan1_1) {
+  const std::string kTest = R"(
+                      OpCapability Shader
+                      OpCapability Float16
+                      OpCapability StorageInputOutput16
+                      OpExtension "SPV_KHR_16bit_storage"
+; CHECK:              OpCapability StorageInputOutput16
+; CHECK-NOT:          OpExtension "SPV_KHR_16bit_storage"
+                      OpMemoryModel Logical GLSL450
+                      OpEntryPoint GLCompute %2 "main"
+              %void = OpTypeVoid
+              %half = OpTypeFloat 16
+               %ptr = OpTypePointer Output %half
+                 %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,
+       StorageInputOutput16_RemovedWithoutOutputPointer) {
+  const std::string kTest = R"(
+                      OpCapability Shader
+                      OpCapability Float16
+                      OpCapability StorageInputOutput16
+                      OpExtension "SPV_KHR_16bit_storage"
+; CHECK-NOT:          OpCapability StorageInputOutput16
+; CHECK-NOT:          OpExtension "SPV_KHR_16bit_storage"
+                      OpMemoryModel Logical GLSL450
+                      OpEntryPoint GLCompute %2 "main"
+              %void = OpTypeVoid
+                 %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,
+       StoragePushConstant16_RemainsSimplePointer_Vulkan1_0) {
+  const std::string kTest = R"(
+                      OpCapability Shader
+                      OpCapability Float16
+                      OpCapability StoragePushConstant16
+                      OpExtension "SPV_KHR_16bit_storage"
+; CHECK:              OpCapability StoragePushConstant16
+; CHECK:              OpExtension "SPV_KHR_16bit_storage"
+                      OpMemoryModel Logical GLSL450
+                      OpEntryPoint GLCompute %2 "main"
+              %void = OpTypeVoid
+              %half = OpTypeFloat 16
+               %ptr = OpTypePointer PushConstant %half
+                 %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,
+       StoragePushConstant16_RemainsSimplePointer_Vulkan1_1) {
+  const std::string kTest = R"(
+                      OpCapability Shader
+                      OpCapability Float16
+                      OpCapability StoragePushConstant16
+                      OpExtension "SPV_KHR_16bit_storage"
+; CHECK:              OpCapability StoragePushConstant16
+; CHECK-NOT:          OpExtension "SPV_KHR_16bit_storage"
+                      OpMemoryModel Logical GLSL450
+                      OpEntryPoint GLCompute %2 "main"
+              %void = OpTypeVoid
+              %half = OpTypeFloat 16
+               %ptr = OpTypePointer PushConstant %half
+                 %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, StoragePushConstant16_RemovedSimplePointer) {
+  const std::string kTest = R"(
+                      OpCapability Shader
+                      OpCapability Float16
+                      OpCapability StoragePushConstant16
+                      OpExtension "SPV_KHR_16bit_storage"
+; CHECK-NOT:          OpCapability StoragePushConstant16
+; CHECK-NOT:          OpExtension "SPV_KHR_16bit_storage"
+                      OpMemoryModel Logical GLSL450
+                      OpEntryPoint GLCompute %2 "main"
+              %void = OpTypeVoid
+              %half = OpTypeFloat 16
+               %ptr = OpTypePointer Function %half
+                 %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,
+       StorageUniformBufferBlock16_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
+                      OpExtension "SPV_KHR_16bit_storage"
+
+; CHECK:              OpCapability StorageBuffer16BitAccess
+;                                   `-> StorageUniformBufferBlock16
+; CHECK:              OpExtension "SPV_KHR_16bit_storage"
+
+                      OpMemoryModel Logical GLSL450
+                      OpEntryPoint GLCompute %2 "main"
+                      OpDecorate %struct BufferBlock
+              %void = OpTypeVoid
+              %half = OpTypeFloat 16
+            %struct = OpTypeStruct %half
+               %ptr = OpTypePointer Uniform %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,
+       StorageUniformBufferBlock16_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
+                      OpExtension "SPV_KHR_16bit_storage"
+
+; CHECK:              OpCapability StorageBuffer16BitAccess
+;                                   `-> StorageUniformBufferBlock16
+; CHECK-NOT:          OpExtension "SPV_KHR_16bit_storage"
+
+                      OpMemoryModel Logical GLSL450
+                      OpEntryPoint GLCompute %2 "main"
+                      OpDecorate %struct BufferBlock
+              %void = OpTypeVoid
+              %half = OpTypeFloat 16
+            %struct = OpTypeStruct %half
+               %ptr = OpTypePointer Uniform %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::SuccessWithChange);
+}
+
+TEST_F(TrimCapabilitiesPassTest,
+       StorageUniformBufferBlock16_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
+;                                   `-> StorageUniformBufferBlock16
+; 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,
+       StorageUniform16_RemovedWithBufferBlockPointer_Vulkan1_0) {
+  // See https://github.com/KhronosGroup/SPIRV-Tools/issues/5354
+  static_assert(spv::Capability::StorageUniformBufferBlock16 ==
+                spv::Capability::StorageBuffer16BitAccess);
+  static_assert(spv::Capability::StorageUniform16 ==
+                spv::Capability::UniformAndStorageBuffer16BitAccess);
+
+  const std::string kTest = R"(
+                      OpCapability Shader
+                      OpCapability Float16
+                      OpCapability StorageBuffer16BitAccess
+                      OpCapability UniformAndStorageBuffer16BitAccess
+                      OpExtension "SPV_KHR_16bit_storage"
+
+; CHECK:              OpCapability StorageBuffer16BitAccess
+;                                   `-> StorageUniformBufferBlock16
+; CHECK-NOT:          OpCapability UniformAndStorageBuffer16BitAccess
+;                                   `-> StorageUniform16
+; CHECK:              OpExtension "SPV_KHR_16bit_storage"
+
+                      OpMemoryModel Logical GLSL450
+                      OpEntryPoint GLCompute %2 "main"
+                      OpDecorate %struct BufferBlock
+              %void = OpTypeVoid
+              %half = OpTypeFloat 16
+            %struct = OpTypeStruct %half
+               %ptr = OpTypePointer Uniform %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::SuccessWithChange);
+}
+
+TEST_F(TrimCapabilitiesPassTest,
+       StorageUniform16_RemovedWithBufferBlockPointer_Vulkan1_1) {
+  // See https://github.com/KhronosGroup/SPIRV-Tools/issues/5354
+  static_assert(spv::Capability::StorageUniformBufferBlock16 ==
+                spv::Capability::StorageBuffer16BitAccess);
+  static_assert(spv::Capability::StorageUniform16 ==
+                spv::Capability::UniformAndStorageBuffer16BitAccess);
+
+  const std::string kTest = R"(
+                      OpCapability Shader
+                      OpCapability Float16
+                      OpCapability StorageBuffer16BitAccess
+                      OpCapability UniformAndStorageBuffer16BitAccess
+                      OpExtension "SPV_KHR_16bit_storage"
+
+; CHECK:              OpCapability StorageBuffer16BitAccess
+;                                   `-> StorageUniformBufferBlock16
+; CHECK-NOT:          OpCapability UniformAndStorageBuffer16BitAccess
+;                                   `-> StorageUniform16
+; CHECK-NOT:          OpExtension "SPV_KHR_16bit_storage"
+
+                      OpMemoryModel Logical GLSL450
+                      OpEntryPoint GLCompute %2 "main"
+                      OpDecorate %struct BufferBlock
+              %void = OpTypeVoid
+              %half = OpTypeFloat 16
+            %struct = OpTypeStruct %half
+               %ptr = OpTypePointer Uniform %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::SuccessWithChange);
+}
+
+TEST_F(TrimCapabilitiesPassTest,
+       StorageUniform16_RemovedWithNonBlockUniformPointer_Vulkan1_0) {
+  // See https://github.com/KhronosGroup/SPIRV-Tools/issues/5354
+  static_assert(spv::Capability::StorageUniformBufferBlock16 ==
+                spv::Capability::StorageBuffer16BitAccess);
+  static_assert(spv::Capability::StorageUniform16 ==
+                spv::Capability::UniformAndStorageBuffer16BitAccess);
+
+  const std::string kTest = R"(
+                      OpCapability Shader
+                      OpCapability Float16
+                      OpCapability StorageBuffer16BitAccess
+                      OpCapability UniformAndStorageBuffer16BitAccess
+                      OpExtension "SPV_KHR_16bit_storage"
+
+; CHECK-NOT:          OpCapability StorageBuffer16BitAccess
+;                                   `-> StorageUniformBufferBlock16
+; CHECK:              OpCapability UniformAndStorageBuffer16BitAccess
+;                                   `-> StorageUniform16
+; CHECK:              OpExtension "SPV_KHR_16bit_storage"
+
+                      OpMemoryModel Logical GLSL450
+                      OpEntryPoint GLCompute %2 "main"
+              %void = OpTypeVoid
+              %half = OpTypeFloat 16
+            %struct = OpTypeStruct %half
+               %ptr = OpTypePointer Uniform %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::SuccessWithChange);
+}
+
+TEST_F(TrimCapabilitiesPassTest,
+       StorageUniform16_RemovedWithNonBlockUniformPointer_Vulkan1_1) {
+  // See https://github.com/KhronosGroup/SPIRV-Tools/issues/5354
+  static_assert(spv::Capability::StorageUniformBufferBlock16 ==
+                spv::Capability::StorageBuffer16BitAccess);
+  static_assert(spv::Capability::StorageUniform16 ==
+                spv::Capability::UniformAndStorageBuffer16BitAccess);
+
+  const std::string kTest = R"(
+                      OpCapability Shader
+                      OpCapability Float16
+                      OpCapability StorageBuffer16BitAccess
+                      OpCapability UniformAndStorageBuffer16BitAccess
+                      OpExtension "SPV_KHR_16bit_storage"
+
+; CHECK-NOT:          OpCapability StorageBuffer16BitAccess
+;                                   `-> StorageUniformBufferBlock16
+; CHECK:              OpCapability UniformAndStorageBuffer16BitAccess
+;                                   `-> StorageUniform16
+; CHECK-NOT:          OpExtension "SPV_KHR_16bit_storage"
+
+                      OpMemoryModel Logical GLSL450
+                      OpEntryPoint GLCompute %2 "main"
+              %void = OpTypeVoid
+              %half = OpTypeFloat 16
+            %struct = OpTypeStruct %half
+               %ptr = OpTypePointer Uniform %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::SuccessWithChange);
+}
+
+TEST_F(TrimCapabilitiesPassTest, FragmentShaderInterlock_RemovedIfNotUsed) {
+  const std::string kTest = R"(
+               OpCapability Shader
+               OpCapability FragmentShaderPixelInterlockEXT
+               OpCapability FragmentShaderSampleInterlockEXT
+               OpCapability FragmentShaderShadingRateInterlockEXT
+               OpExtension "SPV_EXT_fragment_shader_interlock"
+; CHECK-NOT:   OpCapability FragmentShaderPixelInterlockEXT
+; CHECK-NOT:   OpCapability FragmentShaderSampleInterlockEXT
+; CHECK-NOT:   OpCapability FragmentShaderShadingRateInterlockEXT
+; CHECK-NOT:   OpExtension "SPV_EXT_fragment_shader_interlock"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "main"
+       %void = OpTypeVoid
+          %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,
+       FragmentShaderPixelInterlock_RemainsWhenOrderedIsUsed) {
+  const std::string kTest = R"(
+               OpCapability Shader
+               OpCapability FragmentShaderPixelInterlockEXT
+               OpCapability FragmentShaderSampleInterlockEXT
+               OpCapability FragmentShaderShadingRateInterlockEXT
+               OpExtension "SPV_EXT_fragment_shader_interlock"
+; CHECK:       OpCapability FragmentShaderPixelInterlockEXT
+; CHECK-NOT:   OpCapability FragmentShaderSampleInterlockEXT
+; CHECK-NOT:   OpCapability FragmentShaderShadingRateInterlockEXT
+; CHECK:       OpExtension "SPV_EXT_fragment_shader_interlock"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "main"
+               OpExecutionMode %main PixelInterlockOrderedEXT
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+          %2 = OpFunction %void None %1
+          %3 = OpLabel
+               OpBeginInvocationInterlockEXT
+               OpEndInvocationInterlockEXT
+               OpReturn
+               OpFunctionEnd;
+  )";
+  const auto result =
+      SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
+}
+
+TEST_F(TrimCapabilitiesPassTest,
+       FragmentShaderPixelInterlock_RemainsWhenUnorderedIsUsed) {
+  const std::string kTest = R"(
+               OpCapability Shader
+               OpCapability FragmentShaderPixelInterlockEXT
+               OpCapability FragmentShaderSampleInterlockEXT
+               OpCapability FragmentShaderShadingRateInterlockEXT
+               OpExtension "SPV_EXT_fragment_shader_interlock"
+; CHECK:       OpCapability FragmentShaderPixelInterlockEXT
+; CHECK-NOT:   OpCapability FragmentShaderSampleInterlockEXT
+; CHECK-NOT:   OpCapability FragmentShaderShadingRateInterlockEXT
+; CHECK:       OpExtension "SPV_EXT_fragment_shader_interlock"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "main"
+               OpExecutionMode %main PixelInterlockUnorderedEXT
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+          %2 = OpFunction %void None %1
+          %3 = OpLabel
+               OpBeginInvocationInterlockEXT
+               OpEndInvocationInterlockEXT
+               OpReturn
+               OpFunctionEnd;
+  )";
+  const auto result =
+      SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
+}
+
+TEST_F(TrimCapabilitiesPassTest,
+       FragmentShaderSampleInterlock_RemainsWhenOrderedIsUsed) {
+  const std::string kTest = R"(
+               OpCapability Shader
+               OpCapability FragmentShaderPixelInterlockEXT
+               OpCapability FragmentShaderSampleInterlockEXT
+               OpCapability FragmentShaderShadingRateInterlockEXT
+               OpExtension "SPV_EXT_fragment_shader_interlock"
+; CHECK-NOT:   OpCapability FragmentShaderPixelInterlockEXT
+; CHECK:       OpCapability FragmentShaderSampleInterlockEXT
+; CHECK-NOT:   OpCapability FragmentShaderShadingRateInterlockEXT
+; CHECK:       OpExtension "SPV_EXT_fragment_shader_interlock"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "main"
+               OpExecutionMode %main SampleInterlockOrderedEXT
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+          %2 = OpFunction %void None %1
+          %3 = OpLabel
+               OpBeginInvocationInterlockEXT
+               OpEndInvocationInterlockEXT
+               OpReturn
+               OpFunctionEnd;
+  )";
+  const auto result =
+      SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
+}
+
+TEST_F(TrimCapabilitiesPassTest,
+       FragmentShaderSampleInterlock_RemainsWhenUnorderedIsUsed) {
+  const std::string kTest = R"(
+               OpCapability Shader
+               OpCapability FragmentShaderPixelInterlockEXT
+               OpCapability FragmentShaderSampleInterlockEXT
+               OpCapability FragmentShaderShadingRateInterlockEXT
+               OpExtension "SPV_EXT_fragment_shader_interlock"
+; CHECK-NOT:   OpCapability FragmentShaderPixelInterlockEXT
+; CHECK:       OpCapability FragmentShaderSampleInterlockEXT
+; CHECK-NOT:   OpCapability FragmentShaderShadingRateInterlockEXT
+; CHECK:       OpExtension "SPV_EXT_fragment_shader_interlock"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "main"
+               OpExecutionMode %main SampleInterlockUnorderedEXT
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+          %2 = OpFunction %void None %1
+          %3 = OpLabel
+               OpBeginInvocationInterlockEXT
+               OpEndInvocationInterlockEXT
+               OpReturn
+               OpFunctionEnd;
+  )";
+  const auto result =
+      SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
+}
+
+TEST_F(TrimCapabilitiesPassTest,
+       FragmentShaderShadingRateInterlock_RemainsWhenOrderedIsUsed) {
+  const std::string kTest = R"(
+               OpCapability Shader
+               OpCapability FragmentShaderPixelInterlockEXT
+               OpCapability FragmentShaderSampleInterlockEXT
+               OpCapability FragmentShaderShadingRateInterlockEXT
+               OpExtension "SPV_EXT_fragment_shader_interlock"
+; CHECK-NOT:   OpCapability FragmentShaderPixelInterlockEXT
+; CHECK-NOT:   OpCapability FragmentShaderSampleInterlockEXT
+; CHECK:       OpCapability FragmentShaderShadingRateInterlockEXT
+; CHECK:       OpExtension "SPV_EXT_fragment_shader_interlock"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "main"
+               OpExecutionMode %main ShadingRateInterlockOrderedEXT
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+          %2 = OpFunction %void None %1
+          %3 = OpLabel
+               OpBeginInvocationInterlockEXT
+               OpEndInvocationInterlockEXT
+               OpReturn
+               OpFunctionEnd;
+  )";
+  const auto result =
+      SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
+}
+
+TEST_F(TrimCapabilitiesPassTest,
+       FragmentShaderShadingRateInterlock_RemainsWhenUnorderedIsUsed) {
+  const std::string kTest = R"(
+               OpCapability Shader
+               OpCapability FragmentShaderPixelInterlockEXT
+               OpCapability FragmentShaderSampleInterlockEXT
+               OpCapability FragmentShaderShadingRateInterlockEXT
+               OpExtension "SPV_EXT_fragment_shader_interlock"
+; CHECK-NOT:   OpCapability FragmentShaderPixelInterlockEXT
+; CHECK-NOT:   OpCapability FragmentShaderSampleInterlockEXT
+; CHECK:       OpCapability FragmentShaderShadingRateInterlockEXT
+; CHECK:       OpExtension "SPV_EXT_fragment_shader_interlock"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "main"
+               OpExecutionMode %main ShadingRateInterlockUnorderedEXT
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+          %2 = OpFunction %void None %1
+          %3 = OpLabel
+               OpBeginInvocationInterlockEXT
+               OpEndInvocationInterlockEXT
+               OpReturn
+               OpFunctionEnd;
+  )";
+  const auto result =
+      SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
+}
+
+TEST_F(TrimCapabilitiesPassTest, Int64_RemovedWhenUnused) {
+  const std::string kTest = R"(
+               OpCapability Int64
+; CHECK-NOT:   OpCapability Int64
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "main"
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+          %1 = OpFunction %void None %3
+          %6 = OpLabel
+               OpReturn
+               OpFunctionEnd;
+  )";
+  const auto result =
+      SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
+}
+
+TEST_F(TrimCapabilitiesPassTest, Int64_RemainsWhenUsed) {
+  const std::string kTest = R"(
+               OpCapability Int64
+; CHECK:       OpCapability Int64
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "main"
+       %void = OpTypeVoid
+        %int = OpTypeInt 64 0
+          %3 = OpTypeFunction %void
+          %1 = OpFunction %void None %3
+          %6 = OpLabel
+               OpReturn
+               OpFunctionEnd;
+  )";
+  const auto result =
+      SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange);
+}
+
+TEST_F(TrimCapabilitiesPassTest, RayQueryKHR_RemovedWhenUnused) {
+  const std::string kTest = R"(
+               OpCapability Shader
+               OpCapability RayQueryKHR
+               OpExtension "SPV_KHR_ray_query"
+; CHECK-NOT:   OpCapability RayQueryKHR
+; CHECK-NOT:   OpExtension "SPV_KHR_ray_query"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main" %out_var_TEXCOORD1
+               OpSource HLSL 660
+               OpName %out_var_TEXCOORD1 "out.var.TEXCOORD1"
+               OpName %main "main"
+               OpDecorate %out_var_TEXCOORD1 Flat
+               OpDecorate %out_var_TEXCOORD1 Location 0
+       %uint = OpTypeInt 32 0
+  %uint_1234 = OpConstant %uint 1234
+%_ptr_Output_uint = OpTypePointer Output %uint
+       %void = OpTypeVoid
+          %7 = OpTypeFunction %void
+%out_var_TEXCOORD1 = OpVariable %_ptr_Output_uint Output
+       %main = OpFunction %void None %7
+          %8 = OpLabel
+               OpStore %out_var_TEXCOORD1 %uint_1234
+               OpReturn
+               OpFunctionEnd
+  )";
+  const auto result =
+      SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
+}
+
+TEST_F(TrimCapabilitiesPassTest,
+       RayQueryKHR_RemainsWhenAccelerationStructureIsPresent) {
+  const std::string kTest = R"(
+               OpCapability Shader
+               OpCapability RayQueryKHR
+               OpExtension "SPV_KHR_ray_query"
+; CHECK:       OpCapability RayQueryKHR
+; CHECK:       OpExtension "SPV_KHR_ray_query"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 2 4
+               OpDecorate %var_bvh DescriptorSet 0
+               OpDecorate %var_bvh Binding 0
+        %bvh = OpTypeAccelerationStructureKHR
+    %ptr_bvh = OpTypePointer UniformConstant %bvh
+       %void = OpTypeVoid
+         %20 = OpTypeFunction %void
+    %var_bvh = OpVariable %ptr_bvh UniformConstant
+       %main = OpFunction %void None %20
+         %30 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+  const auto result =
+      SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange);
+}
+
+TEST_F(TrimCapabilitiesPassTest, RayQueryKHR_RemainsWhenRayQueryTypeIsPresent) {
+  const std::string kTest = R"(
+               OpCapability Shader
+               OpCapability RayQueryKHR
+               OpExtension "SPV_KHR_ray_query"
+; CHECK:       OpCapability RayQueryKHR
+; CHECK:       OpExtension "SPV_KHR_ray_query"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 2 4
+      %query = OpTypeRayQueryKHR
+       %void = OpTypeVoid
+         %20 = OpTypeFunction %void
+  %ptr_query = OpTypePointer Function %query
+       %main = OpFunction %void None %20
+         %30 = OpLabel
+  %var_query = OpVariable %ptr_query Function
+               OpReturn
+               OpFunctionEnd
+  )";
+  const auto result =
+      SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange);
+}
+
+TEST_F(TrimCapabilitiesPassTest, RayQueryKHR_RemainsWhenUsed) {
+  const std::string kTest = R"(
+               OpCapability Shader
+               OpCapability RayQueryKHR
+               OpExtension "SPV_KHR_ray_query"
+; CHECK:       OpCapability RayQueryKHR
+; CHECK:       OpExtension "SPV_KHR_ray_query"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 2 4
+               OpDecorate %bvh DescriptorSet 0
+               OpDecorate %bvh Binding 0
+               OpDecorate %output DescriptorSet 0
+               OpDecorate %output Binding 1
+               OpDecorate %_runtimearr_float ArrayStride 4
+               OpMemberDecorate %type_RWStructuredBuffer_float 0 Offset 0
+               OpDecorate %type_RWStructuredBuffer_float BufferBlock
+      %float = OpTypeFloat 32
+    %float_0 = OpConstant %float 0
+        %int = OpTypeInt 32 1
+    %v3float = OpTypeVector %float 3
+         %12 = OpConstantComposite %v3float %float_0 %float_0 %float_0
+      %int_0 = OpConstant %int 0
+       %uint = OpTypeInt 32 0
+     %uint_0 = OpConstant %uint 0
+     %uint_1 = OpConstant %uint 1
+%accelerationStructureKHR = OpTypeAccelerationStructureKHR
+%_ptr_UniformConstant_accelerationStructureKHR = OpTypePointer UniformConstant %accelerationStructureKHR
+%_runtimearr_float = OpTypeRuntimeArray %float
+%type_RWStructuredBuffer_float = OpTypeStruct %_runtimearr_float
+%_ptr_Uniform_type_RWStructuredBuffer_float = OpTypePointer Uniform %type_RWStructuredBuffer_float
+       %void = OpTypeVoid
+         %20 = OpTypeFunction %void
+%rayQueryKHR = OpTypeRayQueryKHR
+%_ptr_Function_rayQueryKHR = OpTypePointer Function %rayQueryKHR
+       %bool = OpTypeBool
+%_ptr_Uniform_float = OpTypePointer Uniform %float
+        %bvh = OpVariable %_ptr_UniformConstant_accelerationStructureKHR UniformConstant
+     %output = OpVariable %_ptr_Uniform_type_RWStructuredBuffer_float Uniform
+       %main = OpFunction %void None %20
+         %24 = OpLabel
+         %25 = OpVariable %_ptr_Function_rayQueryKHR Function
+         %26 = OpLoad %accelerationStructureKHR %bvh
+               OpRayQueryInitializeKHR %25 %26 %uint_0 %uint_0 %12 %float_0 %12 %float_0
+         %27 = OpRayQueryProceedKHR %bool %25
+         %28 = OpRayQueryGetIntersectionTypeKHR %uint %25 %uint_1
+         %29 = OpIEqual %bool %28 %uint_1
+               OpSelectionMerge %30 None
+               OpBranchConditional %29 %31 %30
+         %31 = OpLabel
+         %32 = OpAccessChain %_ptr_Uniform_float %output %int_0 %uint_0
+               OpStore %32 %float_0
+               OpBranch %30
+         %30 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+  const auto result =
+      SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange);
+}
+
+TEST_F(TrimCapabilitiesPassTest,
+       RayTracingKHR_RemainsWithIntersectionExecutionMode) {
+  const std::string kTest = R"(
+               OpCapability RayTracingKHR
+               OpExtension "SPV_KHR_ray_tracing"
+; CHECK:       OpCapability RayTracingKHR
+; CHECK:       OpExtension "SPV_KHR_ray_tracing"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint IntersectionKHR %main "main"
+               OpSource HLSL 660
+               OpName %main "main"
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+       %main = OpFunction %void None %3
+          %4 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+  const auto result =
+      SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange);
+}
+
+TEST_F(TrimCapabilitiesPassTest,
+       RayTracingKHR_RemainsWithClosestHitExecutionMode) {
+  const std::string kTest = R"(
+               OpCapability RayTracingKHR
+               OpExtension "SPV_KHR_ray_tracing"
+; CHECK:       OpCapability RayTracingKHR
+; CHECK:       OpExtension "SPV_KHR_ray_tracing"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint ClosestHitKHR %main "main" %a
+               OpSource HLSL 630
+               OpName %Payload "Payload"
+               OpMemberName %Payload 0 "color"
+               OpName %a "a"
+               OpName %main "main"
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+    %Payload = OpTypeStruct %v4float
+%ptr_payload = OpTypePointer IncomingRayPayloadKHR %Payload
+       %void = OpTypeVoid
+          %8 = OpTypeFunction %void
+          %a = OpVariable %ptr_payload IncomingRayPayloadKHR
+       %main = OpFunction %void None %8
+          %9 = OpLabel
+         %10 = OpLoad %Payload %a
+               OpStore %a %10
+               OpReturn
+               OpFunctionEnd
+
+  )";
+  const auto result =
+      SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange);
+}
+
+TEST_F(TrimCapabilitiesPassTest, RayTracingKHR_RemainsWithAnyHitExecutionMode) {
+  const std::string kTest = R"(
+               OpCapability RayTracingKHR
+               OpExtension "SPV_KHR_ray_tracing"
+; CHECK:       OpCapability RayTracingKHR
+; CHECK:       OpExtension "SPV_KHR_ray_tracing"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint AnyHitKHR %main "main" %a
+               OpSource HLSL 630
+               OpName %Payload "Payload"
+               OpMemberName %Payload 0 "color"
+               OpName %a "a"
+               OpName %main "main"
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+    %Payload = OpTypeStruct %v4float
+%ptr_payload = OpTypePointer IncomingRayPayloadKHR %Payload
+       %void = OpTypeVoid
+          %8 = OpTypeFunction %void
+          %a = OpVariable %ptr_payload IncomingRayPayloadKHR
+       %main = OpFunction %void None %8
+          %9 = OpLabel
+         %10 = OpLoad %Payload %a
+               OpStore %a %10
+               OpReturn
+               OpFunctionEnd
+  )";
+  const auto result =
+      SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange);
+}
+
+TEST_F(TrimCapabilitiesPassTest, RayTracingKHR_RemainsWithMissExecutionMode) {
+  const std::string kTest = R"(
+               OpCapability RayTracingKHR
+               OpExtension "SPV_KHR_ray_tracing"
+; CHECK:       OpCapability RayTracingKHR
+; CHECK:       OpExtension "SPV_KHR_ray_tracing"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint MissKHR %main "main" %a
+               OpSource HLSL 630
+               OpName %Payload "Payload"
+               OpMemberName %Payload 0 "color"
+               OpName %a "a"
+               OpName %main "main"
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+    %Payload = OpTypeStruct %v4float
+%ptr_payload = OpTypePointer IncomingRayPayloadKHR %Payload
+       %void = OpTypeVoid
+          %8 = OpTypeFunction %void
+          %a = OpVariable %ptr_payload IncomingRayPayloadKHR
+       %main = OpFunction %void None %8
+          %9 = OpLabel
+         %10 = OpLoad %Payload %a
+               OpStore %a %10
+               OpReturn
+               OpFunctionEnd
+  )";
+  const auto result =
+      SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange);
+}
+
+TEST_F(TrimCapabilitiesPassTest,
+       RayTracingKHR_RemainsWithRayGenerationExecutionMode) {
+  const std::string kTest = R"(
+               OpCapability RayTracingKHR
+               OpExtension "SPV_KHR_ray_tracing"
+; CHECK:       OpCapability RayTracingKHR
+; CHECK:       OpExtension "SPV_KHR_ray_tracing"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint RayGenerationKHR %main "main"
+               OpSource HLSL 630
+               OpName %main "main"
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+       %main = OpFunction %void None %3
+          %4 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+  const auto result =
+      SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange);
+}
+
+TEST_F(TrimCapabilitiesPassTest,
+       RayTracingKHR_RemainsWithCallableExecutionMode) {
+  const std::string kTest = R"(
+; CHECK:       OpCapability RayTracingKHR
+; CHECK:       OpExtension "SPV_KHR_ray_tracing"
+               OpCapability RayTracingKHR
+               OpExtension "SPV_KHR_ray_tracing"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint CallableKHR %main "main" %a
+               OpSource HLSL 660
+               OpName %Payload "Payload"
+               OpMemberName %Payload 0 "data"
+               OpName %a "a"
+               OpName %main "main"
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+    %Payload = OpTypeStruct %v4float
+%ptr_payload = OpTypePointer IncomingCallableDataKHR %Payload
+       %void = OpTypeVoid
+          %8 = OpTypeFunction %void
+          %a = OpVariable %ptr_payload IncomingCallableDataKHR
+       %main = OpFunction %void None %8
+          %9 = OpLabel
+         %10 = OpLoad %Payload %a
+               OpStore %a %10
+               OpReturn
+               OpFunctionEnd
+  )";
+  const auto result =
+      SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange);
+}
+
+TEST_F(TrimCapabilitiesPassTest,
+       ImageMSArray_RemainsIfSampledIs2AndArrayedIs1) {
+  const std::string kTest = R"(
+               OpCapability ImageMSArray
+ ; CHECK:      OpCapability ImageMSArray
+               OpCapability Shader
+               OpCapability StorageImageMultisample
+               OpCapability StorageImageReadWithoutFormat
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpDecorate %var_image DescriptorSet 0
+               OpDecorate %var_image Binding 1
+       %void = OpTypeVoid
+       %func = OpTypeFunction %void
+        %f32 = OpTypeFloat 32
+        %u32 = OpTypeInt 32 0
+     %uint_2 = OpConstant %u32 2
+     %uint_1 = OpConstant %u32 1
+     %v2uint = OpTypeVector %u32 2
+    %v4float = OpTypeVector %f32 4
+    %image = OpTypeImage %f32 2D 2 1 1 2 Unknown
+%ptr_image = OpTypePointer UniformConstant %image
+       %10 = OpConstantComposite %v2uint %uint_1 %uint_2
+%var_image = OpVariable %ptr_image UniformConstant
+     %main = OpFunction %void None %func
+ %main_lab = OpLabel
+       %18 = OpLoad %image %var_image
+       %19 = OpImageRead %v4float %18 %10 Sample %uint_2
+             OpReturn
+             OpFunctionEnd
+  )";
+  const auto result =
+      SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange);
+}
+
+TEST_F(TrimCapabilitiesPassTest, ImageMSArray_RemovedIfNotUsed) {
+  const std::string kTest = R"(
+               OpCapability Shader
+               OpCapability ImageMSArray
+; CHECK-NOT:   OpCapability ImageMSArray
+               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, ImageMSArray_RemovedIfArrayedIsNot1) {
+  const std::string kTest = R"(
+               OpCapability ImageMSArray
+ ; CHECK-NOT:  OpCapability ImageMSArray
+               OpCapability Shader
+               OpCapability StorageImageMultisample
+               OpCapability StorageImageReadWithoutFormat
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpDecorate %var_image DescriptorSet 0
+               OpDecorate %var_image Binding 1
+       %void = OpTypeVoid
+       %func = OpTypeFunction %void
+        %f32 = OpTypeFloat 32
+        %u32 = OpTypeInt 32 0
+     %uint_2 = OpConstant %u32 2
+     %uint_1 = OpConstant %u32 1
+     %v2uint = OpTypeVector %u32 2
+    %v4float = OpTypeVector %f32 4
+    %image = OpTypeImage %f32 2D 2 0 1 2 Unknown
+%ptr_image = OpTypePointer UniformConstant %image
+       %10 = OpConstantComposite %v2uint %uint_1 %uint_2
+%var_image = OpVariable %ptr_image UniformConstant
+     %main = OpFunction %void None %func
+ %main_lab = OpLabel
+       %18 = OpLoad %image %var_image
+       %19 = OpImageRead %v4float %18 %10 Sample %uint_2
+             OpReturn
+             OpFunctionEnd
+  )";
+  const auto result =
+      SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
+}
+
+TEST_F(TrimCapabilitiesPassTest, ImageMSArray_RemovedIfSampledNot2) {
+  const std::string kTest = R"(
+               OpCapability ImageMSArray
+ ; CHECK-NOT:  OpCapability ImageMSArray
+               OpCapability Shader
+               OpCapability StorageImageReadWithoutFormat
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpDecorate %var_image DescriptorSet 0
+               OpDecorate %var_image Binding 1
+       %void = OpTypeVoid
+       %func = OpTypeFunction %void
+        %f32 = OpTypeFloat 32
+        %u32 = OpTypeInt 32 0
+     %uint_3 = OpConstant %u32 3
+     %uint_2 = OpConstant %u32 2
+     %uint_1 = OpConstant %u32 1
+     %v3uint = OpTypeVector %u32 3
+    %v4float = OpTypeVector %f32 4
+    %image = OpTypeImage %f32 2D 2 1 0 2 Unknown
+%ptr_image = OpTypePointer UniformConstant %image
+       %10 = OpConstantComposite %v3uint %uint_1 %uint_2 %uint_3
+%var_image = OpVariable %ptr_image UniformConstant
+     %main = OpFunction %void None %func
+ %main_lab = OpLabel
+       %18 = OpLoad %image %var_image
+       %19 = OpImageRead %v4float %18 %10
+             OpReturn
+             OpFunctionEnd
+  )";
+  const auto result =
+      SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
+}
+
+TEST_F(TrimCapabilitiesPassTest, Float64_RemovedWhenUnused) {
+  const std::string kTest = R"(
+               OpCapability Float64
+; CHECK-NOT:   OpCapability Float64
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "main"
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+          %1 = OpFunction %void None %3
+          %6 = OpLabel
+               OpReturn
+               OpFunctionEnd;
+  )";
+  const auto result =
+      SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
+}
+
+TEST_F(TrimCapabilitiesPassTest, Float64_RemainsWhenUsed) {
+  const std::string kTest = R"(
+               OpCapability Float64
+; CHECK:       OpCapability Float64
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "main"
+       %void = OpTypeVoid
+      %float = OpTypeFloat 64
+          %3 = OpTypeFunction %void
+          %1 = OpFunction %void None %3
+          %6 = OpLabel
+               OpReturn
+               OpFunctionEnd;
+  )";
+  const auto result =
+      SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange);
+}
+
+}  // namespace
+}  // namespace opt
+}  // namespace spvtools
diff --git a/test/opt/type_manager_test.cpp b/test/opt/type_manager_test.cpp
index df216bc..cb30171 100644
--- a/test/opt/type_manager_test.cpp
+++ b/test/opt/type_manager_test.cpp
@@ -12,8 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "source/opt/type_manager.h"
+
 #include <memory>
-#include <string>
 #include <utility>
 #include <vector>
 
@@ -22,7 +23,6 @@
 #include "gtest/gtest.h"
 #include "source/opt/build_module.h"
 #include "source/opt/instruction.h"
-#include "source/opt/type_manager.h"
 #include "spirv-tools/libspirv.hpp"
 
 namespace spvtools {
@@ -98,15 +98,19 @@
   types.emplace_back(new Matrix(v3f32, 4));
 
   // Images
-  types.emplace_back(new Image(s32, SpvDim2D, 0, 0, 0, 0, SpvImageFormatRg8,
-                               SpvAccessQualifierReadOnly));
+  types.emplace_back(new Image(s32, spv::Dim::Dim2D, 0, 0, 0, 0,
+                               spv::ImageFormat::Rg8,
+                               spv::AccessQualifier::ReadOnly));
   auto* image1 = types.back().get();
-  types.emplace_back(new Image(s32, SpvDim2D, 0, 1, 0, 0, SpvImageFormatRg8,
-                               SpvAccessQualifierReadOnly));
-  types.emplace_back(new Image(s32, SpvDim3D, 0, 1, 0, 0, SpvImageFormatRg8,
-                               SpvAccessQualifierReadOnly));
-  types.emplace_back(new Image(voidt, SpvDim3D, 0, 1, 0, 1, SpvImageFormatRg8,
-                               SpvAccessQualifierReadWrite));
+  types.emplace_back(new Image(s32, spv::Dim::Dim2D, 0, 1, 0, 0,
+                               spv::ImageFormat::Rg8,
+                               spv::AccessQualifier::ReadOnly));
+  types.emplace_back(new Image(s32, spv::Dim::Dim3D, 0, 1, 0, 0,
+                               spv::ImageFormat::Rg8,
+                               spv::AccessQualifier::ReadOnly));
+  types.emplace_back(new Image(voidt, spv::Dim::Dim3D, 0, 1, 0, 1,
+                               spv::ImageFormat::Rg8,
+                               spv::AccessQualifier::ReadWrite));
   auto* image2 = types.back().get();
 
   // Sampler
@@ -140,9 +144,9 @@
   types.emplace_back(new Opaque("world"));
 
   // Pointer
-  types.emplace_back(new Pointer(f32, SpvStorageClassInput));
-  types.emplace_back(new Pointer(sts32f32, SpvStorageClassFunction));
-  types.emplace_back(new Pointer(a42f32, SpvStorageClassFunction));
+  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));
 
   // Function
   types.emplace_back(new Function(voidt, {}));
@@ -158,16 +162,18 @@
 
   // Pipe, Forward Pointer, PipeStorage, NamedBarrier, AccelerationStructureNV,
   // CooperativeMatrixNV
-  types.emplace_back(new Pipe(SpvAccessQualifierReadWrite));
-  types.emplace_back(new Pipe(SpvAccessQualifierReadOnly));
-  types.emplace_back(new ForwardPointer(1, SpvStorageClassInput));
-  types.emplace_back(new ForwardPointer(2, SpvStorageClassInput));
-  types.emplace_back(new ForwardPointer(2, SpvStorageClassUniform));
+  types.emplace_back(new Pipe(spv::AccessQualifier::ReadWrite));
+  types.emplace_back(new Pipe(spv::AccessQualifier::ReadOnly));
+  types.emplace_back(new ForwardPointer(1, spv::StorageClass::Input));
+  types.emplace_back(new ForwardPointer(2, spv::StorageClass::Input));
+  types.emplace_back(new ForwardPointer(2, spv::StorageClass::Uniform));
   types.emplace_back(new PipeStorage());
   types.emplace_back(new NamedBarrier());
   types.emplace_back(new AccelerationStructureNV());
   types.emplace_back(new CooperativeMatrixNV(f32, 24, 24, 24));
+  types.emplace_back(new CooperativeMatrixKHR(f32, 8, 8, 8, 1002));
   types.emplace_back(new RayQueryKHR());
+  types.emplace_back(new HitObjectNV());
 
   return types;
 }
@@ -232,6 +238,8 @@
     %arr_long_constant = OpTypeArray %s32 %long_constant
     %arr_spec_const_op = OpTypeArray %s32 %spec_const_op
     %cm   = OpTypeCooperativeMatrixNV %f64 %id4 %id4 %id4
+    %id2    = OpConstant %u32 2
+    %cmkhr  = OpTypeCooperativeMatrixKHR %f64 %id4 %id4 %id4 %id2
   )";
 
   std::vector<std::pair<uint32_t, std::string>> type_id_strs = {
@@ -270,6 +278,7 @@
       {37, "[sint32, id(33), words(0,705032704,1)]"},
       {38, "[sint32, id(34), words(2,34)]"},
       {39, "<float64, 6, 6, 6>"},
+      {41, "<float64, 6, 6, 6, 40>"},
   };
 
   std::unique_ptr<IRContext> context =
@@ -814,22 +823,22 @@
   EXPECT_NE(context, nullptr);
 
   Integer u32(32, false);
-  Pointer u32Ptr(&u32, SpvStorageClassFunction);
+  Pointer u32Ptr(&u32, spv::StorageClass::Function);
   Struct st({&u32});
-  Pointer stPtr(&st, SpvStorageClassInput);
+  Pointer stPtr(&st, spv::StorageClass::Input);
 
   auto pair = context->get_type_mgr()->GetTypeAndPointerType(
-      3u, SpvStorageClassFunction);
+      3u, spv::StorageClass::Function);
   ASSERT_EQ(nullptr, pair.first);
   ASSERT_EQ(nullptr, pair.second);
 
   pair = context->get_type_mgr()->GetTypeAndPointerType(
-      1u, SpvStorageClassFunction);
+      1u, spv::StorageClass::Function);
   ASSERT_TRUE(pair.first->IsSame(&u32));
   ASSERT_TRUE(pair.second->IsSame(&u32Ptr));
 
-  pair =
-      context->get_type_mgr()->GetTypeAndPointerType(2u, SpvStorageClassInput);
+  pair = context->get_type_mgr()->GetTypeAndPointerType(
+      2u, spv::StorageClass::Input);
   ASSERT_TRUE(pair.first->IsSame(&st));
   ASSERT_TRUE(pair.second->IsSame(&stPtr));
 }
@@ -935,12 +944,15 @@
   std::vector<std::unique_ptr<Type>> types = GenerateAllTypes();
   uint32_t id = 1u;
   for (auto& t : types) {
+    std::cout << ". id " << id << std::endl;
     context->get_type_mgr()->RegisterType(id, *t);
     EXPECT_EQ(*t, *context->get_type_mgr()->GetType(id));
   }
+  std::cout << "clear" << id << std::endl;
   types.clear();
 
   for (; id > 0; --id) {
+    std::cout << ". remove id " << id << std::endl;
     context->get_type_mgr()->RemoveId(id);
     EXPECT_EQ(nullptr, context->get_type_mgr()->GetType(id));
   }
@@ -1025,6 +1037,8 @@
 ; CHECK: [[uint:%\w+]] = OpTypeInt 32 0
 ; CHECK: [[input_ptr:%\w+]] = OpTypePointer Input [[uint]]
 ; CHECK: [[uniform_ptr:%\w+]] = OpTypePointer Uniform [[uint]]
+; CHECK: [[uint2:%\w+]] = OpConstant [[uint]] 2
+; CHECK: [[uint8:%\w+]] = OpConstant [[uint]] 8
 ; CHECK: [[uint24:%\w+]] = OpConstant [[uint]] 24
 ; CHECK: [[uint42:%\w+]] = OpConstant [[uint]] 42
 ; CHECK: [[uint100:%\w+]] = OpConstant [[uint]] 100
@@ -1080,7 +1094,9 @@
 ; CHECK: OpTypeNamedBarrier
 ; CHECK: OpTypeAccelerationStructureKHR
 ; CHECK: OpTypeCooperativeMatrixNV [[f32]] [[uint24]] [[uint24]] [[uint24]]
+; CHECK: OpTypeCooperativeMatrixKHR [[f32]] [[uint8]] [[uint8]] [[uint8]] [[uint2]]
 ; CHECK: OpTypeRayQueryKHR
+; CHECK: OpTypeHitObjectNV
 OpCapability Shader
 OpCapability Int64
 OpCapability Linkage
@@ -1088,6 +1104,8 @@
 %uint = OpTypeInt 32 0
 %1 = OpTypePointer Input %uint
 %2 = OpTypePointer Uniform %uint
+%1002 = OpConstant %uint 2
+%8 = OpConstant %uint 8
 %24 = OpConstant %uint 24
 %42 = OpConstant %uint 42
 %100 = OpConstant %uint 100
@@ -1155,7 +1173,7 @@
                   SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   EXPECT_NE(context, nullptr);
 
-  context->get_type_mgr()->FindPointerToType(1, SpvStorageClassFunction);
+  context->get_type_mgr()->FindPointerToType(1, spv::StorageClass::Function);
   Match(text, context.get());
 }
 
@@ -1180,7 +1198,7 @@
                   SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   EXPECT_NE(context, nullptr);
 
-  context->get_type_mgr()->FindPointerToType(2, SpvStorageClassFunction);
+  context->get_type_mgr()->FindPointerToType(2, spv::StorageClass::Function);
   Match(text, context.get());
 }
 
diff --git a/test/opt/types_test.cpp b/test/opt/types_test.cpp
index 552ad97..4ceeb14 100644
--- a/test/opt/types_test.cpp
+++ b/test/opt/types_test.cpp
@@ -34,9 +34,9 @@
     u32_t_ = MakeUnique<Integer>(32, false);
     f64_t_ = MakeUnique<Float>(64);
     v3u32_t_ = MakeUnique<Vector>(u32_t_.get(), 3);
-    image_t_ =
-        MakeUnique<Image>(f64_t_.get(), SpvDim2D, 1, 1, 0, 0, SpvImageFormatR16,
-                          SpvAccessQualifierReadWrite);
+    image_t_ = MakeUnique<Image>(f64_t_.get(), spv::Dim::Dim2D, 1, 1, 0, 0,
+                                 spv::ImageFormat::R16,
+                                 spv::AccessQualifier::ReadWrite);
   }
 
   // Element types to be used for constructing other types for testing.
@@ -72,9 +72,9 @@
 TestMultipleInstancesOfTheSameType(Float, 64)
 TestMultipleInstancesOfTheSameType(Vector, u32_t_.get(), 3)
 TestMultipleInstancesOfTheSameType(Matrix, v3u32_t_.get(), 4)
-TestMultipleInstancesOfTheSameType(Image, f64_t_.get(), SpvDimCube, 0, 0, 1, 1,
-                                   SpvImageFormatRgb10A2,
-                                   SpvAccessQualifierWriteOnly)
+TestMultipleInstancesOfTheSameType(Image, f64_t_.get(), spv::Dim::Cube, 0, 0, 1, 1,
+                                   spv::ImageFormat::Rgb10A2,
+                                   spv::AccessQualifier::WriteOnly)
 TestMultipleInstancesOfTheSameType(Sampler)
 TestMultipleInstancesOfTheSameType(SampledImage, image_t_.get())
 // There are three classes of arrays, based on the kinds of length information
@@ -98,15 +98,15 @@
 TestMultipleInstancesOfTheSameType(Struct, std::vector<const Type*>{
                                                u32_t_.get(), f64_t_.get()})
 TestMultipleInstancesOfTheSameType(Opaque, "testing rocks")
-TestMultipleInstancesOfTheSameType(Pointer, u32_t_.get(), SpvStorageClassInput)
+TestMultipleInstancesOfTheSameType(Pointer, u32_t_.get(), spv::StorageClass::Input)
 TestMultipleInstancesOfTheSameType(Function, u32_t_.get(),
                                    {f64_t_.get(), f64_t_.get()})
 TestMultipleInstancesOfTheSameType(Event)
 TestMultipleInstancesOfTheSameType(DeviceEvent)
 TestMultipleInstancesOfTheSameType(ReserveId)
 TestMultipleInstancesOfTheSameType(Queue)
-TestMultipleInstancesOfTheSameType(Pipe, SpvAccessQualifierReadWrite)
-TestMultipleInstancesOfTheSameType(ForwardPointer, 10, SpvStorageClassUniform)
+TestMultipleInstancesOfTheSameType(Pipe, spv::AccessQualifier::ReadWrite)
+TestMultipleInstancesOfTheSameType(ForwardPointer, 10, spv::StorageClass::Uniform)
 TestMultipleInstancesOfTheSameType(PipeStorage)
 TestMultipleInstancesOfTheSameType(NamedBarrier)
 TestMultipleInstancesOfTheSameType(AccelerationStructureNV)
@@ -119,8 +119,8 @@
   std::vector<std::unique_ptr<Type>> types;
 
   // Forward Pointer
-  types.emplace_back(new ForwardPointer(10000, SpvStorageClassInput));
-  types.emplace_back(new ForwardPointer(20000, SpvStorageClassInput));
+  types.emplace_back(new ForwardPointer(10000, spv::StorageClass::Input));
+  types.emplace_back(new ForwardPointer(20000, spv::StorageClass::Input));
 
   // Void, Bool
   types.emplace_back(new Void());
@@ -155,15 +155,19 @@
   types.emplace_back(new Matrix(v3f32, 4));
 
   // Images
-  types.emplace_back(new Image(s32, SpvDim2D, 0, 0, 0, 0, SpvImageFormatRg8,
-                               SpvAccessQualifierReadOnly));
+  types.emplace_back(new Image(s32, spv::Dim::Dim2D, 0, 0, 0, 0,
+                               spv::ImageFormat::Rg8,
+                               spv::AccessQualifier::ReadOnly));
   auto* image1 = types.back().get();
-  types.emplace_back(new Image(s32, SpvDim2D, 0, 1, 0, 0, SpvImageFormatRg8,
-                               SpvAccessQualifierReadOnly));
-  types.emplace_back(new Image(s32, SpvDim3D, 0, 1, 0, 0, SpvImageFormatRg8,
-                               SpvAccessQualifierReadOnly));
-  types.emplace_back(new Image(voidt, SpvDim3D, 0, 1, 0, 1, SpvImageFormatRg8,
-                               SpvAccessQualifierReadWrite));
+  types.emplace_back(new Image(s32, spv::Dim::Dim2D, 0, 1, 0, 0,
+                               spv::ImageFormat::Rg8,
+                               spv::AccessQualifier::ReadOnly));
+  types.emplace_back(new Image(s32, spv::Dim::Dim3D, 0, 1, 0, 0,
+                               spv::ImageFormat::Rg8,
+                               spv::AccessQualifier::ReadOnly));
+  types.emplace_back(new Image(voidt, spv::Dim::Dim3D, 0, 1, 0, 1,
+                               spv::ImageFormat::Rg8,
+                               spv::AccessQualifier::ReadWrite));
   auto* image2 = types.back().get();
 
   // Sampler
@@ -218,10 +222,10 @@
   types.emplace_back(new Opaque("world"));
 
   // Pointer
-  types.emplace_back(new Pointer(f32, SpvStorageClassInput));
-  types.emplace_back(new Pointer(sts32f32, SpvStorageClassFunction));
-  types.emplace_back(new Pointer(a42f32, SpvStorageClassFunction));
-  types.emplace_back(new Pointer(voidt, SpvStorageClassFunction));
+  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(voidt, spv::StorageClass::Function));
 
   // Function
   types.emplace_back(new Function(voidt, {}));
@@ -236,11 +240,11 @@
   types.emplace_back(new Queue());
 
   // Pipe, Forward Pointer, PipeStorage, NamedBarrier
-  types.emplace_back(new Pipe(SpvAccessQualifierReadWrite));
-  types.emplace_back(new Pipe(SpvAccessQualifierReadOnly));
-  types.emplace_back(new ForwardPointer(1, SpvStorageClassInput));
-  types.emplace_back(new ForwardPointer(2, SpvStorageClassInput));
-  types.emplace_back(new ForwardPointer(2, SpvStorageClassUniform));
+  types.emplace_back(new Pipe(spv::AccessQualifier::ReadWrite));
+  types.emplace_back(new Pipe(spv::AccessQualifier::ReadOnly));
+  types.emplace_back(new ForwardPointer(1, spv::StorageClass::Input));
+  types.emplace_back(new ForwardPointer(2, spv::StorageClass::Input));
+  types.emplace_back(new ForwardPointer(2, spv::StorageClass::Uniform));
   types.emplace_back(new PipeStorage());
   types.emplace_back(new NamedBarrier());
 
@@ -387,18 +391,13 @@
       case Type::kArray:
       case Type::kRuntimeArray:
       case Type::kStruct:
+      case Type::kPointer:
         expectation = false;
         break;
       default:
         break;
     }
-    EXPECT_EQ(t->IsUniqueType(false), expectation)
-        << "expected '" << t->str() << "' to be a "
-        << (expectation ? "" : "non-") << "unique type";
-
-    // Allowing variables pointers.
-    if (t->AsPointer()) expectation = false;
-    EXPECT_EQ(t->IsUniqueType(true), expectation)
+    EXPECT_EQ(t->IsUniqueType(), expectation)
         << "expected '" << t->str() << "' to be a "
         << (expectation ? "" : "non-") << "unique type";
   }
diff --git a/test/opt/upgrade_memory_model_test.cpp b/test/opt/upgrade_memory_model_test.cpp
index 2cd3c7d..d213b8b 100644
--- a/test/opt/upgrade_memory_model_test.cpp
+++ b/test/opt/upgrade_memory_model_test.cpp
@@ -13,7 +13,6 @@
 // limitations under the License.
 
 #include "assembly_builder.h"
-#include "gmock/gmock.h"
 #include "pass_fixture.h"
 #include "pass_utils.h"
 
diff --git a/test/opt/value_table_test.cpp b/test/opt/value_table_test.cpp
index c760f98..3d7aaad 100644
--- a/test/opt/value_table_test.cpp
+++ b/test/opt/value_table_test.cpp
@@ -17,7 +17,6 @@
 #include "gmock/gmock.h"
 #include "source/opt/build_module.h"
 #include "source/opt/value_number_table.h"
-#include "test/opt/assembly_builder.h"
 #include "test/opt/pass_fixture.h"
 
 namespace spvtools {
diff --git a/test/opt/workaround1209_test.cpp b/test/opt/workaround1209_test.cpp
index 50d3c09..5b0146b 100644
--- a/test/opt/workaround1209_test.cpp
+++ b/test/opt/workaround1209_test.cpp
@@ -12,15 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <algorithm>
-#include <cstdarg>
-#include <iostream>
-#include <sstream>
 #include <string>
 #include <unordered_set>
 
-#include "gmock/gmock.h"
-#include "test/opt/assembly_builder.h"
 #include "test/opt/pass_fixture.h"
 #include "test/opt/pass_utils.h"
 
diff --git a/test/opt/wrap_opkill_test.cpp b/test/opt/wrap_opkill_test.cpp
index e40d701..efc834c 100644
--- a/test/opt/wrap_opkill_test.cpp
+++ b/test/opt/wrap_opkill_test.cpp
@@ -12,7 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "gmock/gmock.h"
 #include "test/opt/assembly_builder.h"
 #include "test/opt/pass_fixture.h"
 #include "test/opt/pass_utils.h"
diff --git a/test/reduce/reducer_test.cpp b/test/reduce/reducer_test.cpp
index 276aedc..019e14e 100644
--- a/test/reduce/reducer_test.cpp
+++ b/test/reduce/reducer_test.cpp
@@ -222,7 +222,7 @@
 }
 
 bool InterestingWhileOpcodeExists(const std::vector<uint32_t>& binary,
-                                  uint32_t opcode, uint32_t count, bool dump) {
+                                  spv::Op opcode, uint32_t count, bool dump) {
   if (dump) {
     std::stringstream ss;
     ss << "temp_" << count << ".spv";
@@ -238,7 +238,7 @@
         &*function.begin(),
         [opcode, &interesting](opt::BasicBlock* block) -> void {
           for (auto& inst : *block) {
-            if (inst.opcode() == opcode) {
+            if (inst.opcode() == spv::Op(opcode)) {
               interesting = true;
               break;
             }
@@ -253,12 +253,12 @@
 
 bool InterestingWhileIMulReachable(const std::vector<uint32_t>& binary,
                                    uint32_t count) {
-  return InterestingWhileOpcodeExists(binary, SpvOpIMul, count, false);
+  return InterestingWhileOpcodeExists(binary, spv::Op::OpIMul, count, false);
 }
 
 bool InterestingWhileSDivReachable(const std::vector<uint32_t>& binary,
                                    uint32_t count) {
-  return InterestingWhileOpcodeExists(binary, SpvOpSDiv, count, false);
+  return InterestingWhileOpcodeExists(binary, spv::Op::OpSDiv, count, false);
 }
 
 // The shader below was derived from the following GLSL, and optimized.
diff --git a/test/reduce/validation_during_reduction_test.cpp b/test/reduce/validation_during_reduction_test.cpp
index d864344..8cf14a3 100644
--- a/test/reduce/validation_during_reduction_test.cpp
+++ b/test/reduce/validation_during_reduction_test.cpp
@@ -67,7 +67,7 @@
 
   bool PreconditionHolds() override {
     Instruction* first_instruction = &*function_->begin()[0].begin();
-    return first_instruction->opcode() == SpvOpVariable;
+    return first_instruction->opcode() == spv::Op::OpVariable;
   }
 
  protected:
@@ -75,7 +75,7 @@
     // Duplicate the first OpVariable instruction.
 
     Instruction* first_instruction = &*function_->begin()[0].begin();
-    assert(first_instruction->opcode() == SpvOpVariable &&
+    assert(first_instruction->opcode() == spv::Op::OpVariable &&
            "Expected first instruction to be OpVariable");
     IRContext* context = first_instruction->context();
     Instruction* cloned_instruction = first_instruction->Clone(context);
@@ -105,7 +105,7 @@
     std::vector<std::unique_ptr<ReductionOpportunity>> result;
     for (auto& function : *context->module()) {
       Instruction* first_instruction = &*function.begin()[0].begin();
-      if (first_instruction->opcode() == SpvOpVariable) {
+      if (first_instruction->opcode() == spv::Op::OpVariable) {
         result.push_back(
             MakeUnique<OpVariableDuplicatorReductionOpportunity>(&function));
       }
@@ -523,7 +523,7 @@
                OpFunctionEnd
   )";
 
-  spv_target_env env = SPV_ENV_UNIVERSAL_1_3;
+  spv_target_env env = SPV_ENV_VULKAN_1_0;
   std::vector<uint32_t> binary_in;
   SpirvTools t(env);
 
diff --git a/test/text_to_binary.annotation_test.cpp b/test/text_to_binary.annotation_test.cpp
index 76776de..826812b 100644
--- a/test/text_to_binary.annotation_test.cpp
+++ b/test/text_to_binary.annotation_test.cpp
@@ -41,7 +41,7 @@
 
 using OpDecorateSimpleTest =
     spvtest::TextToBinaryTestBase<::testing::TestWithParam<
-        std::tuple<spv_target_env, EnumCase<SpvDecoration>>>>;
+        std::tuple<spv_target_env, EnumCase<spv::Decoration>>>>;
 
 TEST_P(OpDecorateSimpleTest, AnySimpleDecoration) {
   // This string should assemble, but should not validate.
@@ -51,7 +51,7 @@
     input << " " << operand;
   input << std::endl;
   EXPECT_THAT(CompiledInstructions(input.str(), std::get<0>(GetParam())),
-              Eq(MakeInstruction(SpvOpDecorate,
+              Eq(MakeInstruction(spv::Op::OpDecorate,
                                  {1, uint32_t(std::get<1>(GetParam()).value())},
                                  std::get<1>(GetParam()).operands())));
   // Also check disassembly.
@@ -64,7 +64,7 @@
 // Like above, but parameters to the decoration are IDs.
 using OpDecorateSimpleIdTest =
     spvtest::TextToBinaryTestBase<::testing::TestWithParam<
-        std::tuple<spv_target_env, EnumCase<SpvDecoration>>>>;
+        std::tuple<spv_target_env, EnumCase<spv::Decoration>>>>;
 
 TEST_P(OpDecorateSimpleIdTest, AnySimpleDecoration) {
   // This string should assemble, but should not validate.
@@ -74,7 +74,7 @@
     input << " %" << operand;
   input << std::endl;
   EXPECT_THAT(CompiledInstructions(input.str(), std::get<0>(GetParam())),
-              Eq(MakeInstruction(SpvOpDecorateId,
+              Eq(MakeInstruction(spv::Op::OpDecorateId,
                                  {1, uint32_t(std::get<1>(GetParam()).value())},
                                  std::get<1>(GetParam()).operands())));
   // Also check disassembly.
@@ -84,11 +84,11 @@
       Eq(input.str()));
 }
 
-#define CASE(NAME) SpvDecoration##NAME, #NAME
+#define CASE(NAME) spv::Decoration::NAME, #NAME
 INSTANTIATE_TEST_SUITE_P(
     TextToBinaryDecorateSimple, OpDecorateSimpleTest,
     Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
-            ValuesIn(std::vector<EnumCase<SpvDecoration>>{
+            ValuesIn(std::vector<EnumCase<spv::Decoration>>{
                 // The operand literal values are arbitrarily chosen,
                 // but there are the right number of them.
                 {CASE(RelaxedPrecision), {}},
@@ -134,23 +134,24 @@
 
 INSTANTIATE_TEST_SUITE_P(TextToBinaryDecorateSimpleV11, OpDecorateSimpleTest,
                          Combine(Values(SPV_ENV_UNIVERSAL_1_1),
-                                 Values(EnumCase<SpvDecoration>{
+                                 Values(EnumCase<spv::Decoration>{
                                      CASE(MaxByteOffset), {128}})));
 
-INSTANTIATE_TEST_SUITE_P(TextToBinaryDecorateSimpleV14, OpDecorateSimpleTest,
-                         Combine(Values(SPV_ENV_UNIVERSAL_1_4),
-                                 ValuesIn(std::vector<EnumCase<SpvDecoration>>{
-                                     {CASE(Uniform), {}},
-                                 })));
+INSTANTIATE_TEST_SUITE_P(
+    TextToBinaryDecorateSimpleV14, OpDecorateSimpleTest,
+    Combine(Values(SPV_ENV_UNIVERSAL_1_4),
+            ValuesIn(std::vector<EnumCase<spv::Decoration>>{
+                {CASE(Uniform), {}},
+            })));
 
-INSTANTIATE_TEST_SUITE_P(TextToBinaryDecorateSimpleIdV14,
-                         OpDecorateSimpleIdTest,
-                         Combine(Values(SPV_ENV_UNIVERSAL_1_4),
-                                 ValuesIn(std::vector<EnumCase<SpvDecoration>>{
-                                     // In 1.4, UniformId decoration takes a
-                                     // scope Id.
-                                     {CASE(UniformId), {1}},
-                                 })));
+INSTANTIATE_TEST_SUITE_P(
+    TextToBinaryDecorateSimpleIdV14, OpDecorateSimpleIdTest,
+    Combine(Values(SPV_ENV_UNIVERSAL_1_4),
+            ValuesIn(std::vector<EnumCase<spv::Decoration>>{
+                // In 1.4, UniformId decoration takes a
+                // scope Id.
+                {CASE(UniformId), {1}},
+            })));
 #undef CASE
 
 TEST_F(OpDecorateSimpleTest, WrongDecoration) {
@@ -195,14 +196,14 @@
   const std::string input =
       "OpDecorate %1 " + GetParam().enum_name + " " + GetParam().name;
   EXPECT_THAT(CompiledInstructions(input),
-              Eq(MakeInstruction(SpvOpDecorate, {1, GetParam().enum_value,
-                                                 GetParam().value})));
+              Eq(MakeInstruction(spv::Op::OpDecorate, {1, GetParam().enum_value,
+                                                       GetParam().value})));
 }
 
 // Test OpDecorate BuiltIn.
 // clang-format off
 #define CASE(NAME) \
-  { SpvBuiltIn##NAME, #NAME, SpvDecorationBuiltIn, "BuiltIn" }
+  { uint32_t(spv::BuiltIn::NAME), #NAME, uint32_t(spv::Decoration::BuiltIn), "BuiltIn" }
 INSTANTIATE_TEST_SUITE_P(TextToBinaryDecorateBuiltIn, OpDecorateEnumTest,
                         ::testing::ValuesIn(std::vector<DecorateEnumCase>{
                             CASE(Position),
@@ -260,7 +261,7 @@
 // Test OpDecorate FuncParamAttr
 // clang-format off
 #define CASE(NAME) \
-  { SpvFunctionParameterAttribute##NAME, #NAME, SpvDecorationFuncParamAttr, "FuncParamAttr" }
+  { uint32_t(spv::FunctionParameterAttribute::NAME), #NAME, uint32_t(spv::Decoration::FuncParamAttr), "FuncParamAttr" }
 INSTANTIATE_TEST_SUITE_P(TextToBinaryDecorateFuncParamAttr, OpDecorateEnumTest,
                         ::testing::ValuesIn(std::vector<DecorateEnumCase>{
                             CASE(Zext),
@@ -283,7 +284,7 @@
 // Test OpDecorate FPRoundingMode
 // clang-format off
 #define CASE(NAME) \
-  { SpvFPRoundingMode##NAME, #NAME, SpvDecorationFPRoundingMode, "FPRoundingMode" }
+  { uint32_t(spv::FPRoundingMode::NAME), #NAME, uint32_t(spv::Decoration::FPRoundingMode), "FPRoundingMode" }
 INSTANTIATE_TEST_SUITE_P(TextToBinaryDecorateFPRoundingMode, OpDecorateEnumTest,
                         ::testing::ValuesIn(std::vector<DecorateEnumCase>{
                             CASE(RTE),
@@ -306,15 +307,15 @@
 
 // clang-format off
 #define CASE(ENUM,NAME) \
-  { SpvFPFastMathMode##ENUM, #NAME, SpvDecorationFPFastMathMode, "FPFastMathMode" }
+  { uint32_t(spv::FPFastMathModeMask::ENUM), #NAME, uint32_t(spv::Decoration::FPFastMathMode), "FPFastMathMode" }
 INSTANTIATE_TEST_SUITE_P(TextToBinaryDecorateFPFastMathMode, OpDecorateEnumTest,
                         ::testing::ValuesIn(std::vector<DecorateEnumCase>{
                             CASE(MaskNone, None),
-                            CASE(NotNaNMask, NotNaN),
-                            CASE(NotInfMask, NotInf),
-                            CASE(NSZMask, NSZ),
-                            CASE(AllowRecipMask, AllowRecip),
-                            CASE(FastMask, Fast),
+                            CASE(NotNaN, NotNaN),
+                            CASE(NotInf, NotInf),
+                            CASE(NSZ, NSZ),
+                            CASE(AllowRecip, AllowRecip),
+                            CASE(Fast, Fast),
                       }));
 #undef CASE
 // clang-format on
@@ -323,13 +324,13 @@
   // Sample a single combination.  This ensures we've integrated
   // the instruction parsing logic with spvTextParseMask.
   const std::string input = "OpDecorate %1 FPFastMathMode NotNaN|NotInf|NSZ";
-  const uint32_t expected_enum = SpvDecorationFPFastMathMode;
-  const uint32_t expected_mask = SpvFPFastMathModeNotNaNMask |
-                                 SpvFPFastMathModeNotInfMask |
-                                 SpvFPFastMathModeNSZMask;
-  EXPECT_THAT(
-      CompiledInstructions(input),
-      Eq(MakeInstruction(SpvOpDecorate, {1, expected_enum, expected_mask})));
+  const uint32_t expected_enum = uint32_t(spv::Decoration::FPFastMathMode);
+  const uint32_t expected_mask = uint32_t(spv::FPFastMathModeMask::NotNaN) |
+                                 uint32_t(spv::FPFastMathModeMask::NotInf) |
+                                 uint32_t(spv::FPFastMathModeMask::NSZ);
+  EXPECT_THAT(CompiledInstructions(input),
+              Eq(MakeInstruction(spv::Op::OpDecorate,
+                                 {1, expected_enum, expected_mask})));
 }
 
 TEST_F(OpDecorateEnumTest, WrongFPFastMathMode) {
@@ -355,7 +356,8 @@
   const std::string input = "OpDecorate %1 LinkageAttributes \"" +
                             GetParam().external_name + "\" " +
                             GetParam().linkage_type_name;
-  std::vector<uint32_t> expected_operands{1, SpvDecorationLinkageAttributes};
+  std::vector<uint32_t> expected_operands{
+      1, uint32_t(spv::Decoration::LinkageAttributes)};
   std::vector<uint32_t> encoded_external_name =
       MakeVector(GetParam().external_name);
   expected_operands.insert(expected_operands.end(),
@@ -363,11 +365,11 @@
                            encoded_external_name.end());
   expected_operands.push_back(GetParam().linkage_type_value);
   EXPECT_THAT(CompiledInstructions(input),
-              Eq(MakeInstruction(SpvOpDecorate, expected_operands)));
+              Eq(MakeInstruction(spv::Op::OpDecorate, expected_operands)));
 }
 
 // clang-format off
-#define CASE(ENUM) SpvLinkageType##ENUM, #ENUM
+#define CASE(ENUM) uint32_t(spv::LinkageType::ENUM), #ENUM
 INSTANTIATE_TEST_SUITE_P(TextToBinaryDecorateLinkage, OpDecorateLinkageTest,
                         ::testing::ValuesIn(std::vector<DecorateLinkageCase>{
                             { CASE(Import), "a" },
@@ -387,13 +389,13 @@
 
 TEST_F(TextToBinaryTest, GroupMemberDecorateGoodOneTarget) {
   EXPECT_THAT(CompiledInstructions("OpGroupMemberDecorate %group %id0 42"),
-              Eq(MakeInstruction(SpvOpGroupMemberDecorate, {1, 2, 42})));
+              Eq(MakeInstruction(spv::Op::OpGroupMemberDecorate, {1, 2, 42})));
 }
 
 TEST_F(TextToBinaryTest, GroupMemberDecorateGoodTwoTargets) {
   EXPECT_THAT(
       CompiledInstructions("OpGroupMemberDecorate %group %id0 96 %id1 42"),
-      Eq(MakeInstruction(SpvOpGroupMemberDecorate, {1, 2, 96, 3, 42})));
+      Eq(MakeInstruction(spv::Op::OpGroupMemberDecorate, {1, 2, 96, 3, 42})));
 }
 
 TEST_F(TextToBinaryTest, GroupMemberDecorateMissingGroupId) {
@@ -443,7 +445,7 @@
 
 using OpMemberDecorateSimpleTest =
     spvtest::TextToBinaryTestBase<::testing::TestWithParam<
-        std::tuple<spv_target_env, EnumCase<SpvDecoration>>>>;
+        std::tuple<spv_target_env, EnumCase<spv::Decoration>>>>;
 
 TEST_P(OpMemberDecorateSimpleTest, AnySimpleDecoration) {
   // This string should assemble, but should not validate.
@@ -454,7 +456,7 @@
   input << std::endl;
   EXPECT_THAT(
       CompiledInstructions(input.str(), std::get<0>(GetParam())),
-      Eq(MakeInstruction(SpvOpMemberDecorate,
+      Eq(MakeInstruction(spv::Op::OpMemberDecorate,
                          {1, 42, uint32_t(std::get<1>(GetParam()).value())},
                          std::get<1>(GetParam()).operands())));
   // Also check disassembly.
@@ -464,11 +466,11 @@
       Eq(input.str()));
 }
 
-#define CASE(NAME) SpvDecoration##NAME, #NAME
+#define CASE(NAME) spv::Decoration::NAME, #NAME
 INSTANTIATE_TEST_SUITE_P(
     TextToBinaryDecorateSimple, OpMemberDecorateSimpleTest,
     Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
-            ValuesIn(std::vector<EnumCase<SpvDecoration>>{
+            ValuesIn(std::vector<EnumCase<spv::Decoration>>{
                 // The operand literal values are arbitrarily chosen,
                 // but there are the right number of them.
                 {CASE(RelaxedPrecision), {}},
@@ -515,7 +517,7 @@
 INSTANTIATE_TEST_SUITE_P(
     TextToBinaryDecorateSimpleV11, OpMemberDecorateSimpleTest,
     Combine(Values(SPV_ENV_UNIVERSAL_1_1),
-            Values(EnumCase<SpvDecoration>{CASE(MaxByteOffset), {128}})));
+            Values(EnumCase<spv::Decoration>{CASE(MaxByteOffset), {128}})));
 #undef CASE
 
 TEST_F(OpMemberDecorateSimpleTest, WrongDecoration) {
diff --git a/test/text_to_binary.barrier_test.cpp b/test/text_to_binary.barrier_test.cpp
index f1cb4fb..380cacb 100644
--- a/test/text_to_binary.barrier_test.cpp
+++ b/test/text_to_binary.barrier_test.cpp
@@ -37,7 +37,7 @@
 TEST_F(OpMemoryBarrier, Good) {
   const std::string input = "OpMemoryBarrier %1 %2\n";
   EXPECT_THAT(CompiledInstructions(input),
-              Eq(MakeInstruction(SpvOpMemoryBarrier, {1, 2})));
+              Eq(MakeInstruction(spv::Op::OpMemoryBarrier, {1, 2})));
   EXPECT_THAT(EncodeAndDecodeSuccessfully(input), Eq(input));
 }
 
@@ -89,7 +89,7 @@
   EXPECT_THAT(
       CompiledInstructions("OpMemoryNamedBarrier %bar %scope %semantics",
                            SPV_ENV_UNIVERSAL_1_0),
-      ElementsAre(spvOpcodeMake(4, SpvOpMemoryNamedBarrier), _, _, _));
+      ElementsAre(spvOpcodeMake(4, spv::Op::OpMemoryNamedBarrier), _, _, _));
 }
 
 TEST_F(NamedMemoryBarrierTest, ArgumentCount) {
@@ -107,7 +107,7 @@
   EXPECT_THAT(
       CompiledInstructions("OpMemoryNamedBarrier %bar %scope %semantics",
                            SPV_ENV_UNIVERSAL_1_1),
-      ElementsAre(spvOpcodeMake(4, SpvOpMemoryNamedBarrier), _, _, _));
+      ElementsAre(spvOpcodeMake(4, spv::Op::OpMemoryNamedBarrier), _, _, _));
   EXPECT_THAT(
       CompileFailure("OpMemoryNamedBarrier %bar %scope %semantics %extra",
                      SPV_ENV_UNIVERSAL_1_1),
@@ -128,7 +128,7 @@
 TEST_F(TypeNamedBarrierTest, OpcodeAssemblesInV10) {
   EXPECT_THAT(
       CompiledInstructions("%t = OpTypeNamedBarrier", SPV_ENV_UNIVERSAL_1_0),
-      ElementsAre(spvOpcodeMake(2, SpvOpTypeNamedBarrier), _));
+      ElementsAre(spvOpcodeMake(2, spv::Op::OpTypeNamedBarrier), _));
 }
 
 TEST_F(TypeNamedBarrierTest, ArgumentCount) {
@@ -137,7 +137,7 @@
                  "found 'OpTypeNamedBarrier'."));
   EXPECT_THAT(
       CompiledInstructions("%t = OpTypeNamedBarrier", SPV_ENV_UNIVERSAL_1_1),
-      ElementsAre(spvOpcodeMake(2, SpvOpTypeNamedBarrier), _));
+      ElementsAre(spvOpcodeMake(2, spv::Op::OpTypeNamedBarrier), _));
   EXPECT_THAT(
       CompileFailure("%t = OpTypeNamedBarrier 1 2 3", SPV_ENV_UNIVERSAL_1_1),
       Eq("Expected <opcode> or <result-id> at the beginning of an instruction, "
@@ -150,7 +150,8 @@
   EXPECT_THAT(
       CompiledInstructions("%bar = OpNamedBarrierInitialize %type %count",
                            SPV_ENV_UNIVERSAL_1_0),
-      ElementsAre(spvOpcodeMake(4, SpvOpNamedBarrierInitialize), _, _, _));
+      ElementsAre(spvOpcodeMake(4, spv::Op::OpNamedBarrierInitialize), _, _,
+                  _));
 }
 
 TEST_F(NamedBarrierInitializeTest, ArgumentCount) {
@@ -165,7 +166,8 @@
   EXPECT_THAT(
       CompiledInstructions("%bar = OpNamedBarrierInitialize %type %count",
                            SPV_ENV_UNIVERSAL_1_1),
-      ElementsAre(spvOpcodeMake(4, SpvOpNamedBarrierInitialize), _, _, _));
+      ElementsAre(spvOpcodeMake(4, spv::Op::OpNamedBarrierInitialize), _, _,
+                  _));
   EXPECT_THAT(
       CompileFailure("%bar = OpNamedBarrierInitialize %type %count \"extra\"",
                      SPV_ENV_UNIVERSAL_1_1),
diff --git a/test/text_to_binary.constant_test.cpp b/test/text_to_binary.constant_test.cpp
index 7ab4ca5..bc5cd06 100644
--- a/test/text_to_binary.constant_test.cpp
+++ b/test/text_to_binary.constant_test.cpp
@@ -35,21 +35,21 @@
 // Test Sampler Addressing Mode enum values
 
 using SamplerAddressingModeTest = spvtest::TextToBinaryTestBase<
-    ::testing::TestWithParam<EnumCase<SpvSamplerAddressingMode>>>;
+    ::testing::TestWithParam<EnumCase<spv::SamplerAddressingMode>>>;
 
 TEST_P(SamplerAddressingModeTest, AnySamplerAddressingMode) {
   const std::string input =
       "%result = OpConstantSampler %type " + GetParam().name() + " 0 Nearest";
   EXPECT_THAT(CompiledInstructions(input),
-              Eq(MakeInstruction(SpvOpConstantSampler,
-                                 {1, 2, GetParam().value(), 0, 0})));
+              Eq(MakeInstruction(spv::Op::OpConstantSampler,
+                                 {1, 2, uint32_t(GetParam().value()), 0, 0})));
 }
 
 // clang-format off
-#define CASE(NAME) { SpvSamplerAddressingMode##NAME, #NAME }
+#define CASE(NAME) { spv::SamplerAddressingMode::NAME, #NAME }
 INSTANTIATE_TEST_SUITE_P(
     TextToBinarySamplerAddressingMode, SamplerAddressingModeTest,
-    ::testing::ValuesIn(std::vector<EnumCase<SpvSamplerAddressingMode>>{
+    ::testing::ValuesIn(std::vector<EnumCase<spv::SamplerAddressingMode>>{
         CASE(None),
         CASE(ClampToEdge),
         CASE(Clamp),
@@ -67,21 +67,21 @@
 // Test Sampler Filter Mode enum values
 
 using SamplerFilterModeTest = spvtest::TextToBinaryTestBase<
-    ::testing::TestWithParam<EnumCase<SpvSamplerFilterMode>>>;
+    ::testing::TestWithParam<EnumCase<spv::SamplerFilterMode>>>;
 
 TEST_P(SamplerFilterModeTest, AnySamplerFilterMode) {
   const std::string input =
       "%result = OpConstantSampler %type Clamp 0 " + GetParam().name();
   EXPECT_THAT(CompiledInstructions(input),
-              Eq(MakeInstruction(SpvOpConstantSampler,
-                                 {1, 2, 2, 0, GetParam().value()})));
+              Eq(MakeInstruction(spv::Op::OpConstantSampler,
+                                 {1, 2, 2, 0, uint32_t(GetParam().value())})));
 }
 
 // clang-format off
-#define CASE(NAME) { SpvSamplerFilterMode##NAME, #NAME}
+#define CASE(NAME) { spv::SamplerFilterMode::NAME, #NAME}
 INSTANTIATE_TEST_SUITE_P(
     TextToBinarySamplerFilterMode, SamplerFilterModeTest,
-    ::testing::ValuesIn(std::vector<EnumCase<SpvSamplerFilterMode>>{
+    ::testing::ValuesIn(std::vector<EnumCase<spv::SamplerFilterMode>>{
         CASE(Nearest),
         CASE(Linear),
     }));
@@ -119,119 +119,119 @@
     ::testing::ValuesIn(std::vector<ConstantTestCase>{
       // Check 16 bits
       {"OpTypeInt 16 0", "0x1234",
-        Concatenate({MakeInstruction(SpvOpTypeInt, {1, 16, 0}),
-         MakeInstruction(SpvOpConstant, {1, 2, 0x1234})})},
+        Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 16, 0}),
+         MakeInstruction(spv::Op::OpConstant, {1, 2, 0x1234})})},
       {"OpTypeInt 16 0", "0x8000",
-        Concatenate({MakeInstruction(SpvOpTypeInt, {1, 16, 0}),
-         MakeInstruction(SpvOpConstant, {1, 2, 0x8000})})},
+        Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 16, 0}),
+         MakeInstruction(spv::Op::OpConstant, {1, 2, 0x8000})})},
       {"OpTypeInt 16 0", "0",
-        Concatenate({MakeInstruction(SpvOpTypeInt, {1, 16, 0}),
-         MakeInstruction(SpvOpConstant, {1, 2, 0})})},
+        Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 16, 0}),
+         MakeInstruction(spv::Op::OpConstant, {1, 2, 0})})},
       {"OpTypeInt 16 0", "65535",
-        Concatenate({MakeInstruction(SpvOpTypeInt, {1, 16, 0}),
-         MakeInstruction(SpvOpConstant, {1, 2, 65535})})},
+        Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 16, 0}),
+         MakeInstruction(spv::Op::OpConstant, {1, 2, 65535})})},
       {"OpTypeInt 16 0", "0xffff",
-        Concatenate({MakeInstruction(SpvOpTypeInt, {1, 16, 0}),
-         MakeInstruction(SpvOpConstant, {1, 2, 65535})})},
+        Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 16, 0}),
+         MakeInstruction(spv::Op::OpConstant, {1, 2, 65535})})},
       {"OpTypeInt 16 1", "0x8000", // Test sign extension.
-        Concatenate({MakeInstruction(SpvOpTypeInt, {1, 16, 1}),
-         MakeInstruction(SpvOpConstant, {1, 2, 0xffff8000})})},
+        Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 16, 1}),
+         MakeInstruction(spv::Op::OpConstant, {1, 2, 0xffff8000})})},
       {"OpTypeInt 16 1", "-32",
-        Concatenate({MakeInstruction(SpvOpTypeInt, {1, 16, 1}),
-         MakeInstruction(SpvOpConstant, {1, 2, uint32_t(-32)})})},
+        Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 16, 1}),
+         MakeInstruction(spv::Op::OpConstant, {1, 2, uint32_t(-32)})})},
       {"OpTypeInt 16 1", "0",
-        Concatenate({MakeInstruction(SpvOpTypeInt, {1, 16, 1}),
-         MakeInstruction(SpvOpConstant, {1, 2, 0})})},
+        Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 16, 1}),
+         MakeInstruction(spv::Op::OpConstant, {1, 2, 0})})},
       {"OpTypeInt 16 1", "-0",
-        Concatenate({MakeInstruction(SpvOpTypeInt, {1, 16, 1}),
-         MakeInstruction(SpvOpConstant, {1, 2, 0})})},
+        Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 16, 1}),
+         MakeInstruction(spv::Op::OpConstant, {1, 2, 0})})},
       {"OpTypeInt 16 1", "-0x0",
-        Concatenate({MakeInstruction(SpvOpTypeInt, {1, 16, 1}),
-         MakeInstruction(SpvOpConstant, {1, 2, 0})})},
+        Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 16, 1}),
+         MakeInstruction(spv::Op::OpConstant, {1, 2, 0})})},
       {"OpTypeInt 16 1", "-32768",
-        Concatenate({MakeInstruction(SpvOpTypeInt, {1, 16, 1}),
-         MakeInstruction(SpvOpConstant, {1, 2, uint32_t(-32768)})})},
+        Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 16, 1}),
+         MakeInstruction(spv::Op::OpConstant, {1, 2, uint32_t(-32768)})})},
       // Check 32 bits
       {"OpTypeInt 32 0", "42",
-        Concatenate({MakeInstruction(SpvOpTypeInt, {1, 32, 0}),
-         MakeInstruction(SpvOpConstant, {1, 2, 42})})},
+        Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 32, 0}),
+         MakeInstruction(spv::Op::OpConstant, {1, 2, 42})})},
       {"OpTypeInt 32 1", "-32",
-        Concatenate({MakeInstruction(SpvOpTypeInt, {1, 32, 1}),
-         MakeInstruction(SpvOpConstant, {1, 2, uint32_t(-32)})})},
+        Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 32, 1}),
+         MakeInstruction(spv::Op::OpConstant, {1, 2, uint32_t(-32)})})},
       {"OpTypeInt 32 1", "0",
-        Concatenate({MakeInstruction(SpvOpTypeInt, {1, 32, 1}),
-         MakeInstruction(SpvOpConstant, {1, 2, 0})})},
+        Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 32, 1}),
+         MakeInstruction(spv::Op::OpConstant, {1, 2, 0})})},
       {"OpTypeInt 32 1", "-0",
-        Concatenate({MakeInstruction(SpvOpTypeInt, {1, 32, 1}),
-         MakeInstruction(SpvOpConstant, {1, 2, 0})})},
+        Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 32, 1}),
+         MakeInstruction(spv::Op::OpConstant, {1, 2, 0})})},
       {"OpTypeInt 32 1", "-0x0",
-        Concatenate({MakeInstruction(SpvOpTypeInt, {1, 32, 1}),
-         MakeInstruction(SpvOpConstant, {1, 2, 0})})},
+        Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 32, 1}),
+         MakeInstruction(spv::Op::OpConstant, {1, 2, 0})})},
       {"OpTypeInt 32 1", "-0x001",
-        Concatenate({MakeInstruction(SpvOpTypeInt, {1, 32, 1}),
-         MakeInstruction(SpvOpConstant, {1, 2, uint32_t(-1)})})},
+        Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 32, 1}),
+         MakeInstruction(spv::Op::OpConstant, {1, 2, uint32_t(-1)})})},
       {"OpTypeInt 32 1", "2147483647",
-        Concatenate({MakeInstruction(SpvOpTypeInt, {1, 32, 1}),
-         MakeInstruction(SpvOpConstant, {1, 2, 0x7fffffffu})})},
+        Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 32, 1}),
+         MakeInstruction(spv::Op::OpConstant, {1, 2, 0x7fffffffu})})},
       {"OpTypeInt 32 1", "-2147483648",
-        Concatenate({MakeInstruction(SpvOpTypeInt, {1, 32, 1}),
-         MakeInstruction(SpvOpConstant, {1, 2, 0x80000000u})})},
+        Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 32, 1}),
+         MakeInstruction(spv::Op::OpConstant, {1, 2, 0x80000000u})})},
       {"OpTypeFloat 32", "1.0",
-        Concatenate({MakeInstruction(SpvOpTypeFloat, {1, 32}),
-         MakeInstruction(SpvOpConstant, {1, 2, 0x3f800000})})},
+        Concatenate({MakeInstruction(spv::Op::OpTypeFloat, {1, 32}),
+         MakeInstruction(spv::Op::OpConstant, {1, 2, 0x3f800000})})},
       {"OpTypeFloat 32", "10.0",
-        Concatenate({MakeInstruction(SpvOpTypeFloat, {1, 32}),
-         MakeInstruction(SpvOpConstant, {1, 2, 0x41200000})})},
+        Concatenate({MakeInstruction(spv::Op::OpTypeFloat, {1, 32}),
+         MakeInstruction(spv::Op::OpConstant, {1, 2, 0x41200000})})},
       {"OpTypeFloat 32", "-0x1p+128", // -infinity
-        Concatenate({MakeInstruction(SpvOpTypeFloat, {1, 32}),
-         MakeInstruction(SpvOpConstant, {1, 2, 0xFF800000})})},
+        Concatenate({MakeInstruction(spv::Op::OpTypeFloat, {1, 32}),
+         MakeInstruction(spv::Op::OpConstant, {1, 2, 0xFF800000})})},
       {"OpTypeFloat 32", "0x1p+128", // +infinity
-        Concatenate({MakeInstruction(SpvOpTypeFloat, {1, 32}),
-         MakeInstruction(SpvOpConstant, {1, 2, 0x7F800000})})},
+        Concatenate({MakeInstruction(spv::Op::OpTypeFloat, {1, 32}),
+         MakeInstruction(spv::Op::OpConstant, {1, 2, 0x7F800000})})},
       {"OpTypeFloat 32", "-0x1.8p+128", // A -NaN
-        Concatenate({MakeInstruction(SpvOpTypeFloat, {1, 32}),
-         MakeInstruction(SpvOpConstant, {1, 2, 0xFFC00000})})},
+        Concatenate({MakeInstruction(spv::Op::OpTypeFloat, {1, 32}),
+         MakeInstruction(spv::Op::OpConstant, {1, 2, 0xFFC00000})})},
       {"OpTypeFloat 32", "-0x1.0002p+128", // A +NaN
-        Concatenate({MakeInstruction(SpvOpTypeFloat, {1, 32}),
-         MakeInstruction(SpvOpConstant, {1, 2, 0xFF800100})})},
+        Concatenate({MakeInstruction(spv::Op::OpTypeFloat, {1, 32}),
+         MakeInstruction(spv::Op::OpConstant, {1, 2, 0xFF800100})})},
       // Check 48 bits
       {"OpTypeInt 48 0", "0x1234",
-        Concatenate({MakeInstruction(SpvOpTypeInt, {1, 48, 0}),
-         MakeInstruction(SpvOpConstant, {1, 2, 0x1234, 0})})},
+        Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 48, 0}),
+         MakeInstruction(spv::Op::OpConstant, {1, 2, 0x1234, 0})})},
       {"OpTypeInt 48 0", "0x800000000001",
-        Concatenate({MakeInstruction(SpvOpTypeInt, {1, 48, 0}),
-         MakeInstruction(SpvOpConstant, {1, 2, 1, 0x00008000})})},
+        Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 48, 0}),
+         MakeInstruction(spv::Op::OpConstant, {1, 2, 1, 0x00008000})})},
       {"OpTypeInt 48 1", "0x800000000000", // Test sign extension.
-        Concatenate({MakeInstruction(SpvOpTypeInt, {1, 48, 1}),
-         MakeInstruction(SpvOpConstant, {1, 2, 0, 0xffff8000})})},
+        Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 48, 1}),
+         MakeInstruction(spv::Op::OpConstant, {1, 2, 0, 0xffff8000})})},
       {"OpTypeInt 48 1", "-32",
-        Concatenate({MakeInstruction(SpvOpTypeInt, {1, 48, 1}),
-         MakeInstruction(SpvOpConstant, {1, 2, uint32_t(-32), uint32_t(-1)})})},
+        Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 48, 1}),
+         MakeInstruction(spv::Op::OpConstant, {1, 2, uint32_t(-32), uint32_t(-1)})})},
       // Check 64 bits
       {"OpTypeInt 64 0", "0x1234",
-        Concatenate({MakeInstruction(SpvOpTypeInt, {1, 64, 0}),
-         MakeInstruction(SpvOpConstant, {1, 2, 0x1234, 0})})},
+        Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 64, 0}),
+         MakeInstruction(spv::Op::OpConstant, {1, 2, 0x1234, 0})})},
       {"OpTypeInt 64 0", "18446744073709551615",
-        Concatenate({MakeInstruction(SpvOpTypeInt, {1, 64, 0}),
-         MakeInstruction(SpvOpConstant, {1, 2, 0xffffffffu, 0xffffffffu})})},
+        Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 64, 0}),
+         MakeInstruction(spv::Op::OpConstant, {1, 2, 0xffffffffu, 0xffffffffu})})},
       {"OpTypeInt 64 0", "0xffffffffffffffff",
-        Concatenate({MakeInstruction(SpvOpTypeInt, {1, 64, 0}),
-         MakeInstruction(SpvOpConstant, {1, 2, 0xffffffffu, 0xffffffffu})})},
+        Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 64, 0}),
+         MakeInstruction(spv::Op::OpConstant, {1, 2, 0xffffffffu, 0xffffffffu})})},
       {"OpTypeInt 64 1", "0x1234",
-        Concatenate({MakeInstruction(SpvOpTypeInt, {1, 64, 1}),
-         MakeInstruction(SpvOpConstant, {1, 2, 0x1234, 0})})},
+        Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 64, 1}),
+         MakeInstruction(spv::Op::OpConstant, {1, 2, 0x1234, 0})})},
       {"OpTypeInt 64 1", "-42",
-        Concatenate({MakeInstruction(SpvOpTypeInt, {1, 64, 1}),
-         MakeInstruction(SpvOpConstant, {1, 2, uint32_t(-42), uint32_t(-1)})})},
+        Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 64, 1}),
+         MakeInstruction(spv::Op::OpConstant, {1, 2, uint32_t(-42), uint32_t(-1)})})},
       {"OpTypeInt 64 1", "-0x01",
-        Concatenate({MakeInstruction(SpvOpTypeInt, {1, 64, 1}),
-         MakeInstruction(SpvOpConstant, {1, 2, 0xffffffffu, 0xffffffffu})})},
+        Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 64, 1}),
+         MakeInstruction(spv::Op::OpConstant, {1, 2, 0xffffffffu, 0xffffffffu})})},
       {"OpTypeInt 64 1", "9223372036854775807",
-        Concatenate({MakeInstruction(SpvOpTypeInt, {1, 64, 1}),
-         MakeInstruction(SpvOpConstant, {1, 2, 0xffffffffu, 0x7fffffffu})})},
+        Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 64, 1}),
+         MakeInstruction(spv::Op::OpConstant, {1, 2, 0xffffffffu, 0x7fffffffu})})},
       {"OpTypeInt 64 1", "0x7fffffff",
-        Concatenate({MakeInstruction(SpvOpTypeInt, {1, 64, 1}),
-         MakeInstruction(SpvOpConstant, {1, 2, 0x7fffffffu, 0})})},
+        Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 64, 1}),
+         MakeInstruction(spv::Op::OpConstant, {1, 2, 0x7fffffffu, 0})})},
     }));
 // clang-format on
 
@@ -388,53 +388,53 @@
     ::testing::ValuesIn(std::vector<ConstantTestCase>{
       // Check 16 bits
       {"OpTypeInt 16 0", "0x1234",
-        Concatenate({MakeInstruction(SpvOpTypeInt, {1, 16, 0}),
-         MakeInstruction(SpvOpSpecConstant, {1, 2, 0x1234})})},
+        Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 16, 0}),
+         MakeInstruction(spv::Op::OpSpecConstant, {1, 2, 0x1234})})},
       {"OpTypeInt 16 0", "0x8000",
-        Concatenate({MakeInstruction(SpvOpTypeInt, {1, 16, 0}),
-         MakeInstruction(SpvOpSpecConstant, {1, 2, 0x8000})})},
+        Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 16, 0}),
+         MakeInstruction(spv::Op::OpSpecConstant, {1, 2, 0x8000})})},
       {"OpTypeInt 16 1", "0x8000", // Test sign extension.
-        Concatenate({MakeInstruction(SpvOpTypeInt, {1, 16, 1}),
-         MakeInstruction(SpvOpSpecConstant, {1, 2, 0xffff8000})})},
+        Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 16, 1}),
+         MakeInstruction(spv::Op::OpSpecConstant, {1, 2, 0xffff8000})})},
       {"OpTypeInt 16 1", "-32",
-        Concatenate({MakeInstruction(SpvOpTypeInt, {1, 16, 1}),
-         MakeInstruction(SpvOpSpecConstant, {1, 2, uint32_t(-32)})})},
+        Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 16, 1}),
+         MakeInstruction(spv::Op::OpSpecConstant, {1, 2, uint32_t(-32)})})},
       // Check 32 bits
       {"OpTypeInt 32 0", "42",
-        Concatenate({MakeInstruction(SpvOpTypeInt, {1, 32, 0}),
-         MakeInstruction(SpvOpSpecConstant, {1, 2, 42})})},
+        Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 32, 0}),
+         MakeInstruction(spv::Op::OpSpecConstant, {1, 2, 42})})},
       {"OpTypeInt 32 1", "-32",
-        Concatenate({MakeInstruction(SpvOpTypeInt, {1, 32, 1}),
-         MakeInstruction(SpvOpSpecConstant, {1, 2, uint32_t(-32)})})},
+        Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 32, 1}),
+         MakeInstruction(spv::Op::OpSpecConstant, {1, 2, uint32_t(-32)})})},
       {"OpTypeFloat 32", "1.0",
-        Concatenate({MakeInstruction(SpvOpTypeFloat, {1, 32}),
-         MakeInstruction(SpvOpSpecConstant, {1, 2, 0x3f800000})})},
+        Concatenate({MakeInstruction(spv::Op::OpTypeFloat, {1, 32}),
+         MakeInstruction(spv::Op::OpSpecConstant, {1, 2, 0x3f800000})})},
       {"OpTypeFloat 32", "10.0",
-        Concatenate({MakeInstruction(SpvOpTypeFloat, {1, 32}),
-         MakeInstruction(SpvOpSpecConstant, {1, 2, 0x41200000})})},
+        Concatenate({MakeInstruction(spv::Op::OpTypeFloat, {1, 32}),
+         MakeInstruction(spv::Op::OpSpecConstant, {1, 2, 0x41200000})})},
       // Check 48 bits
       {"OpTypeInt 48 0", "0x1234",
-        Concatenate({MakeInstruction(SpvOpTypeInt, {1, 48, 0}),
-         MakeInstruction(SpvOpSpecConstant, {1, 2, 0x1234, 0})})},
+        Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 48, 0}),
+         MakeInstruction(spv::Op::OpSpecConstant, {1, 2, 0x1234, 0})})},
       {"OpTypeInt 48 0", "0x800000000001",
-        Concatenate({MakeInstruction(SpvOpTypeInt, {1, 48, 0}),
-         MakeInstruction(SpvOpSpecConstant, {1, 2, 1, 0x00008000})})},
+        Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 48, 0}),
+         MakeInstruction(spv::Op::OpSpecConstant, {1, 2, 1, 0x00008000})})},
       {"OpTypeInt 48 1", "0x800000000000", // Test sign extension.
-        Concatenate({MakeInstruction(SpvOpTypeInt, {1, 48, 1}),
-         MakeInstruction(SpvOpSpecConstant, {1, 2, 0, 0xffff8000})})},
+        Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 48, 1}),
+         MakeInstruction(spv::Op::OpSpecConstant, {1, 2, 0, 0xffff8000})})},
       {"OpTypeInt 48 1", "-32",
-        Concatenate({MakeInstruction(SpvOpTypeInt, {1, 48, 1}),
-         MakeInstruction(SpvOpSpecConstant, {1, 2, uint32_t(-32), uint32_t(-1)})})},
+        Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 48, 1}),
+         MakeInstruction(spv::Op::OpSpecConstant, {1, 2, uint32_t(-32), uint32_t(-1)})})},
       // Check 64 bits
       {"OpTypeInt 64 0", "0x1234",
-        Concatenate({MakeInstruction(SpvOpTypeInt, {1, 64, 0}),
-         MakeInstruction(SpvOpSpecConstant, {1, 2, 0x1234, 0})})},
+        Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 64, 0}),
+         MakeInstruction(spv::Op::OpSpecConstant, {1, 2, 0x1234, 0})})},
       {"OpTypeInt 64 1", "0x1234",
-        Concatenate({MakeInstruction(SpvOpTypeInt, {1, 64, 1}),
-         MakeInstruction(SpvOpSpecConstant, {1, 2, 0x1234, 0})})},
+        Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 64, 1}),
+         MakeInstruction(spv::Op::OpSpecConstant, {1, 2, 0x1234, 0})})},
       {"OpTypeInt 64 1", "-42",
-        Concatenate({MakeInstruction(SpvOpTypeInt, {1, 64, 1}),
-         MakeInstruction(SpvOpSpecConstant, {1, 2, uint32_t(-42), uint32_t(-1)})})},
+        Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 64, 1}),
+         MakeInstruction(spv::Op::OpSpecConstant, {1, 2, uint32_t(-42), uint32_t(-1)})})},
     }));
 // clang-format on
 
@@ -648,7 +648,7 @@
 // Test OpSpecConstantOp
 
 using OpSpecConstantOpTestWithIds =
-    spvtest::TextToBinaryTestBase<::testing::TestWithParam<EnumCase<SpvOp>>>;
+    spvtest::TextToBinaryTestBase<::testing::TestWithParam<EnumCase<spv::Op>>>;
 
 // The operands to the OpSpecConstantOp opcode are all Ids.
 TEST_P(OpSpecConstantOpTestWithIds, Assembly) {
@@ -658,7 +658,7 @@
   input << "\n";
 
   EXPECT_THAT(CompiledInstructions(input.str()),
-              Eq(MakeInstruction(SpvOpSpecConstantOp,
+              Eq(MakeInstruction(spv::Op::OpSpecConstantOp,
                                  {1, 2, uint32_t(GetParam().value())},
                                  GetParam().operands())));
 
@@ -667,15 +667,15 @@
 }
 
 // clang-format off
-#define CASE1(NAME) { SpvOp##NAME, #NAME, {3} }
-#define CASE2(NAME) { SpvOp##NAME, #NAME, {3, 4} }
-#define CASE3(NAME) { SpvOp##NAME, #NAME, {3, 4, 5} }
-#define CASE4(NAME) { SpvOp##NAME, #NAME, {3, 4, 5, 6} }
-#define CASE5(NAME) { SpvOp##NAME, #NAME, {3, 4, 5, 6, 7} }
-#define CASE6(NAME) { SpvOp##NAME, #NAME, {3, 4, 5, 6, 7, 8} }
+#define CASE1(NAME) { spv::Op::Op##NAME, #NAME, {3} }
+#define CASE2(NAME) { spv::Op::Op##NAME, #NAME, {3, 4} }
+#define CASE3(NAME) { spv::Op::Op##NAME, #NAME, {3, 4, 5} }
+#define CASE4(NAME) { spv::Op::Op##NAME, #NAME, {3, 4, 5, 6} }
+#define CASE5(NAME) { spv::Op::Op##NAME, #NAME, {3, 4, 5, 6, 7} }
+#define CASE6(NAME) { spv::Op::Op##NAME, #NAME, {3, 4, 5, 6, 7, 8} }
 INSTANTIATE_TEST_SUITE_P(
     TextToBinaryOpSpecConstantOp, OpSpecConstantOpTestWithIds,
-    ::testing::ValuesIn(std::vector<EnumCase<SpvOp>>{
+    ::testing::ValuesIn(std::vector<EnumCase<spv::Op>>{
         // Conversion
         CASE1(SConvert),
         CASE1(FConvert),
@@ -759,7 +759,7 @@
 // clang-format on
 
 using OpSpecConstantOpTestWithTwoIdsThenLiteralNumbers =
-    spvtest::TextToBinaryTestBase<::testing::TestWithParam<EnumCase<SpvOp>>>;
+    spvtest::TextToBinaryTestBase<::testing::TestWithParam<EnumCase<spv::Op>>>;
 
 // The operands to the OpSpecConstantOp opcode are two Ids followed by a
 // sequence of literal numbers.
@@ -770,7 +770,7 @@
   input << "\n";
 
   EXPECT_THAT(CompiledInstructions(input.str()),
-              Eq(MakeInstruction(SpvOpSpecConstantOp,
+              Eq(MakeInstruction(spv::Op::OpSpecConstantOp,
                                  {1, 2, uint32_t(GetParam().value()), 3, 4},
                                  GetParam().operands())));
 
@@ -778,11 +778,11 @@
   EXPECT_THAT(EncodeAndDecodeSuccessfully(input.str()), input.str());
 }
 
-#define CASE(NAME) SpvOp##NAME, #NAME
+#define CASE(NAME) spv::Op::Op##NAME, #NAME
 INSTANTIATE_TEST_SUITE_P(
     TextToBinaryOpSpecConstantOp,
     OpSpecConstantOpTestWithTwoIdsThenLiteralNumbers,
-    ::testing::ValuesIn(std::vector<EnumCase<SpvOp>>{
+    ::testing::ValuesIn(std::vector<EnumCase<spv::Op>>{
         // For VectorShuffle, there are two vector operands, and at least
         // two selector Ids.  OpenCL can have up to 16-element vectors.
         {CASE(VectorShuffle), {0, 0}},
@@ -797,7 +797,7 @@
     }));
 
 using OpSpecConstantOpTestWithOneIdThenLiteralNumbers =
-    spvtest::TextToBinaryTestBase<::testing::TestWithParam<EnumCase<SpvOp>>>;
+    spvtest::TextToBinaryTestBase<::testing::TestWithParam<EnumCase<spv::Op>>>;
 
 // The operands to the OpSpecConstantOp opcode are one Id followed by a
 // sequence of literal numbers.
@@ -808,7 +808,7 @@
   input << "\n";
 
   EXPECT_THAT(CompiledInstructions(input.str()),
-              Eq(MakeInstruction(SpvOpSpecConstantOp,
+              Eq(MakeInstruction(spv::Op::OpSpecConstantOp,
                                  {1, 2, uint32_t(GetParam().value()), 3},
                                  GetParam().operands())));
 
@@ -816,11 +816,11 @@
   EXPECT_THAT(EncodeAndDecodeSuccessfully(input.str()), input.str());
 }
 
-#define CASE(NAME) SpvOp##NAME, #NAME
+#define CASE(NAME) spv::Op::Op##NAME, #NAME
 INSTANTIATE_TEST_SUITE_P(
     TextToBinaryOpSpecConstantOp,
     OpSpecConstantOpTestWithOneIdThenLiteralNumbers,
-    ::testing::ValuesIn(std::vector<EnumCase<SpvOp>>{
+    ::testing::ValuesIn(std::vector<EnumCase<spv::Op>>{
         // For CompositeExtract, the universal limit permits up to 255 literal
         // indices.  Let's only test a few.
         {CASE(CompositeExtract), {0}},
diff --git a/test/text_to_binary.control_flow_test.cpp b/test/text_to_binary.control_flow_test.cpp
index 472cb6d..bc64aa3 100644
--- a/test/text_to_binary.control_flow_test.cpp
+++ b/test/text_to_binary.control_flow_test.cpp
@@ -40,22 +40,22 @@
 // Test OpSelectionMerge
 
 using OpSelectionMergeTest = spvtest::TextToBinaryTestBase<
-    TestWithParam<EnumCase<SpvSelectionControlMask>>>;
+    TestWithParam<EnumCase<spv::SelectionControlMask>>>;
 
 TEST_P(OpSelectionMergeTest, AnySingleSelectionControlMask) {
   const std::string input = "OpSelectionMerge %1 " + GetParam().name();
-  EXPECT_THAT(
-      CompiledInstructions(input),
-      Eq(MakeInstruction(SpvOpSelectionMerge, {1, GetParam().value()})));
+  EXPECT_THAT(CompiledInstructions(input),
+              Eq(MakeInstruction(spv::Op::OpSelectionMerge,
+                                 {1, uint32_t(GetParam().value())})));
 }
 
 // clang-format off
-#define CASE(VALUE,NAME) { SpvSelectionControl##VALUE, NAME}
+#define CASE(VALUE,NAME) { spv::SelectionControlMask::VALUE, NAME}
 INSTANTIATE_TEST_SUITE_P(TextToBinarySelectionMerge, OpSelectionMergeTest,
-                        ValuesIn(std::vector<EnumCase<SpvSelectionControlMask>>{
+                        ValuesIn(std::vector<EnumCase<spv::SelectionControlMask>>{
                             CASE(MaskNone, "None"),
-                            CASE(FlattenMask, "Flatten"),
-                            CASE(DontFlattenMask, "DontFlatten"),
+                            CASE(Flatten, "Flatten"),
+                            CASE(DontFlatten, "DontFlatten"),
                         }));
 #undef CASE
 // clang-format on
@@ -63,9 +63,11 @@
 TEST_F(OpSelectionMergeTest, CombinedSelectionControlMask) {
   const std::string input = "OpSelectionMerge %1 Flatten|DontFlatten";
   const uint32_t expected_mask =
-      SpvSelectionControlFlattenMask | SpvSelectionControlDontFlattenMask;
-  EXPECT_THAT(CompiledInstructions(input),
-              Eq(MakeInstruction(SpvOpSelectionMerge, {1, expected_mask})));
+      uint32_t(spv::SelectionControlMask::Flatten |
+               spv::SelectionControlMask::DontFlatten);
+  EXPECT_THAT(
+      CompiledInstructions(input),
+      Eq(MakeInstruction(spv::Op::OpSelectionMerge, {1, expected_mask})));
 }
 
 TEST_F(OpSelectionMergeTest, WrongSelectionControl) {
@@ -85,15 +87,15 @@
   input << "OpLoopMerge %merge %continue " << ctrl.name();
   for (auto num : ctrl.operands()) input << " " << num;
   EXPECT_THAT(CompiledInstructions(input.str(), std::get<0>(GetParam())),
-              Eq(MakeInstruction(SpvOpLoopMerge, {1, 2, ctrl.value()},
+              Eq(MakeInstruction(spv::Op::OpLoopMerge, {1, 2, ctrl.value()},
                                  ctrl.operands())));
 }
 
 #define CASE(VALUE, NAME) \
-  { SpvLoopControl##VALUE, NAME }
-#define CASE1(VALUE, NAME, PARM)          \
-  {                                       \
-    SpvLoopControl##VALUE, NAME, { PARM } \
+  { int32_t(spv::LoopControlMask::VALUE), NAME }
+#define CASE1(VALUE, NAME, PARM)                         \
+  {                                                      \
+    int32_t(spv::LoopControlMask::VALUE), NAME, { PARM } \
   }
 INSTANTIATE_TEST_SUITE_P(
     TextToBinaryLoopMerge, OpLoopMergeTest,
@@ -101,8 +103,8 @@
             ValuesIn(std::vector<EnumCase<int>>{
                 // clang-format off
                 CASE(MaskNone, "None"),
-                CASE(UnrollMask, "Unroll"),
-                CASE(DontUnrollMask, "DontUnroll"),
+                CASE(Unroll, "Unroll"),
+                CASE(DontUnroll, "DontUnroll"),
                 // clang-format on
             })));
 
@@ -111,9 +113,9 @@
     Combine(Values(SPV_ENV_UNIVERSAL_1_1),
             ValuesIn(std::vector<EnumCase<int>>{
                 // clang-format off
-                CASE(DependencyInfiniteMask, "DependencyInfinite"),
-                CASE1(DependencyLengthMask, "DependencyLength", 234),
-                {SpvLoopControlUnrollMask|SpvLoopControlDependencyLengthMask,
+                CASE(DependencyInfinite, "DependencyInfinite"),
+                CASE1(DependencyLength, "DependencyLength", 234),
+                {int32_t(spv::LoopControlMask::Unroll|spv::LoopControlMask::DependencyLength),
                       "DependencyLength|Unroll", {33}},
                 // clang-format on
             })));
@@ -123,9 +125,9 @@
 TEST_F(OpLoopMergeTest, CombinedLoopControlMask) {
   const std::string input = "OpLoopMerge %merge %continue Unroll|DontUnroll";
   const uint32_t expected_mask =
-      SpvLoopControlUnrollMask | SpvLoopControlDontUnrollMask;
+      uint32_t(spv::LoopControlMask::Unroll | spv::LoopControlMask::DontUnroll);
   EXPECT_THAT(CompiledInstructions(input),
-              Eq(MakeInstruction(SpvOpLoopMerge, {1, 2, expected_mask})));
+              Eq(MakeInstruction(spv::Op::OpLoopMerge, {1, 2, expected_mask})));
 }
 
 TEST_F(OpLoopMergeTest, WrongLoopControl) {
@@ -137,16 +139,17 @@
 
 TEST_F(TextToBinaryTest, SwitchGoodZeroTargets) {
   EXPECT_THAT(CompiledInstructions("OpSwitch %selector %default"),
-              Eq(MakeInstruction(SpvOpSwitch, {1, 2})));
+              Eq(MakeInstruction(spv::Op::OpSwitch, {1, 2})));
 }
 
 TEST_F(TextToBinaryTest, SwitchGoodOneTarget) {
-  EXPECT_THAT(CompiledInstructions("%1 = OpTypeInt 32 0\n"
-                                   "%2 = OpConstant %1 52\n"
-                                   "OpSwitch %2 %default 12 %target0"),
-              Eq(Concatenate({MakeInstruction(SpvOpTypeInt, {1, 32, 0}),
-                              MakeInstruction(SpvOpConstant, {1, 2, 52}),
-                              MakeInstruction(SpvOpSwitch, {2, 3, 12, 4})})));
+  EXPECT_THAT(
+      CompiledInstructions("%1 = OpTypeInt 32 0\n"
+                           "%2 = OpConstant %1 52\n"
+                           "OpSwitch %2 %default 12 %target0"),
+      Eq(Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 32, 0}),
+                      MakeInstruction(spv::Op::OpConstant, {1, 2, 52}),
+                      MakeInstruction(spv::Op::OpSwitch, {2, 3, 12, 4})})));
 }
 
 TEST_F(TextToBinaryTest, SwitchGoodTwoTargets) {
@@ -155,9 +158,9 @@
                            "%2 = OpConstant %1 52\n"
                            "OpSwitch %2 %default 12 %target0 42 %target1"),
       Eq(Concatenate({
-          MakeInstruction(SpvOpTypeInt, {1, 32, 0}),
-          MakeInstruction(SpvOpConstant, {1, 2, 52}),
-          MakeInstruction(SpvOpSwitch, {2, 3, 12, 4, 42, 5}),
+          MakeInstruction(spv::Op::OpTypeInt, {1, 32, 0}),
+          MakeInstruction(spv::Op::OpConstant, {1, 2, 52}),
+          MakeInstruction(spv::Op::OpSwitch, {2, 3, 12, 4, 42, 5}),
       })));
 }
 
@@ -246,11 +249,11 @@
       constant_str,
       case_value_str,
       {Concatenate(
-          {MakeInstruction(SpvOpTypeInt,
+          {MakeInstruction(spv::Op::OpTypeInt,
                            {1, integer_width, integer_signedness}),
-           MakeInstruction(SpvOpConstant,
+           MakeInstruction(spv::Op::OpConstant,
                            Concatenate({{1, 2}, encoded_constant})),
-           MakeInstruction(SpvOpSwitch,
+           MakeInstruction(spv::Op::OpSwitch,
                            Concatenate({{2, 3}, encoded_case_value, {4}}))})}};
 }
 
diff --git a/test/text_to_binary.debug_test.cpp b/test/text_to_binary.debug_test.cpp
index 39ba5c5..013107b 100644
--- a/test/text_to_binary.debug_test.cpp
+++ b/test/text_to_binary.debug_test.cpp
@@ -39,7 +39,7 @@
     return static_cast<uint32_t>(language_value);
   }
   const char* language_name;
-  SpvSourceLanguage language_value;
+  spv::SourceLanguage language_value;
   uint32_t version;
 };
 
@@ -47,7 +47,7 @@
 // The list of OpSource cases to use.
 const LanguageCase kLanguageCases[] = {
 #define CASE(NAME, VERSION) \
-  { #NAME, SpvSourceLanguage##NAME, VERSION }
+  { #NAME, spv::SourceLanguage::NAME, VERSION }
   CASE(Unknown, 0),
   CASE(Unknown, 999),
   CASE(ESSL, 310),
@@ -69,9 +69,10 @@
   const std::string input = std::string("OpSource ") +
                             GetParam().language_name + " " +
                             std::to_string(GetParam().version);
-  EXPECT_THAT(CompiledInstructions(input),
-              Eq(MakeInstruction(SpvOpSource, {GetParam().get_language_value(),
-                                               GetParam().version})));
+  EXPECT_THAT(
+      CompiledInstructions(input),
+      Eq(MakeInstruction(spv::Op::OpSource, {GetParam().get_language_value(),
+                                             GetParam().version})));
 }
 
 INSTANTIATE_TEST_SUITE_P(TextToBinaryTestDebug, OpSourceTest,
@@ -87,7 +88,8 @@
   const std::string input = "OpSource GLSL 450 %file_id";
   EXPECT_THAT(
       CompiledInstructions(input),
-      Eq(MakeInstruction(SpvOpSource, {SpvSourceLanguageGLSL, 450, 1})));
+      Eq(MakeInstruction(spv::Op::OpSource,
+                         {uint32_t(spv::SourceLanguage::GLSL), 450, 1})));
 }
 
 TEST_F(TextToBinaryTest, OpSourceAcceptsOptionalSourceText) {
@@ -95,7 +97,8 @@
   const std::string input =
       "OpSource GLSL 450 %file_id \"" + fake_source + "\"";
   EXPECT_THAT(CompiledInstructions(input),
-              Eq(MakeInstruction(SpvOpSource, {SpvSourceLanguageGLSL, 450, 1},
+              Eq(MakeInstruction(spv::Op::OpSource,
+                                 {uint32_t(spv::SourceLanguage::GLSL), 450, 1},
                                  MakeVector(fake_source))));
 }
 
@@ -110,7 +113,7 @@
       std::string("OpSourceContinued \"") + GetParam() + "\"";
   EXPECT_THAT(
       CompiledInstructions(input),
-      Eq(MakeInstruction(SpvOpSourceContinued, MakeVector(GetParam()))));
+      Eq(MakeInstruction(spv::Op::OpSourceContinued, MakeVector(GetParam()))));
 }
 
 // TODO(dneto): utf-8, quoting, escaping
@@ -129,7 +132,7 @@
       std::string("OpSourceExtension \"") + GetParam() + "\"";
   EXPECT_THAT(
       CompiledInstructions(input),
-      Eq(MakeInstruction(SpvOpSourceExtension, MakeVector(GetParam()))));
+      Eq(MakeInstruction(spv::Op::OpSourceExtension, MakeVector(GetParam()))));
 }
 
 // TODO(dneto): utf-8, quoting, escaping
@@ -139,12 +142,12 @@
 
 TEST_F(TextToBinaryTest, OpLine) {
   EXPECT_THAT(CompiledInstructions("OpLine %srcfile 42 99"),
-              Eq(MakeInstruction(SpvOpLine, {1, 42, 99})));
+              Eq(MakeInstruction(spv::Op::OpLine, {1, 42, 99})));
 }
 
 TEST_F(TextToBinaryTest, OpNoLine) {
   EXPECT_THAT(CompiledInstructions("OpNoLine"),
-              Eq(MakeInstruction(SpvOpNoLine, {})));
+              Eq(MakeInstruction(spv::Op::OpNoLine, {})));
 }
 
 using OpStringTest =
@@ -154,8 +157,9 @@
   // TODO(dneto): utf-8, quoting, escaping
   const std::string input =
       std::string("%result = OpString \"") + GetParam() + "\"";
-  EXPECT_THAT(CompiledInstructions(input),
-              Eq(MakeInstruction(SpvOpString, {1}, MakeVector(GetParam()))));
+  EXPECT_THAT(
+      CompiledInstructions(input),
+      Eq(MakeInstruction(spv::Op::OpString, {1}, MakeVector(GetParam()))));
 }
 
 // TODO(dneto): utf-8, quoting, escaping
@@ -169,8 +173,9 @@
 TEST_P(OpNameTest, AnyString) {
   const std::string input =
       std::string("OpName %target \"") + GetParam() + "\"";
-  EXPECT_THAT(CompiledInstructions(input),
-              Eq(MakeInstruction(SpvOpName, {1}, MakeVector(GetParam()))));
+  EXPECT_THAT(
+      CompiledInstructions(input),
+      Eq(MakeInstruction(spv::Op::OpName, {1}, MakeVector(GetParam()))));
 }
 
 // UTF-8, quoting, escaping, etc. are covered in the StringLiterals tests in
@@ -185,9 +190,9 @@
   // TODO(dneto): utf-8, quoting, escaping
   const std::string input =
       std::string("OpMemberName %type 42 \"") + GetParam() + "\"";
-  EXPECT_THAT(
-      CompiledInstructions(input),
-      Eq(MakeInstruction(SpvOpMemberName, {1, 42}, MakeVector(GetParam()))));
+  EXPECT_THAT(CompiledInstructions(input),
+              Eq(MakeInstruction(spv::Op::OpMemberName, {1, 42},
+                                 MakeVector(GetParam()))));
 }
 
 // TODO(dneto): utf-8, quoting, escaping
@@ -205,7 +210,7 @@
       std::string("OpModuleProcessed \"") + GetParam() + "\"";
   EXPECT_THAT(
       CompiledInstructions(input, SPV_ENV_UNIVERSAL_1_1),
-      Eq(MakeInstruction(SpvOpModuleProcessed, MakeVector(GetParam()))));
+      Eq(MakeInstruction(spv::Op::OpModuleProcessed, MakeVector(GetParam()))));
 }
 
 INSTANTIATE_TEST_SUITE_P(TextToBinaryTestDebug, OpModuleProcessedTest,
diff --git a/test/text_to_binary.device_side_enqueue_test.cpp b/test/text_to_binary.device_side_enqueue_test.cpp
index 2f4dd70..7f424f3 100644
--- a/test/text_to_binary.device_side_enqueue_test.cpp
+++ b/test/text_to_binary.device_side_enqueue_test.cpp
@@ -44,7 +44,7 @@
       " %wait_events %ret_event %invoke %param %param_size %param_align " +
       GetParam().local_size_source;
   EXPECT_THAT(CompiledInstructions(input),
-              Eq(MakeInstruction(SpvOpEnqueueKernel,
+              Eq(MakeInstruction(spv::Op::OpEnqueueKernel,
                                  {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12},
                                  GetParam().local_size_operands)));
 }
diff --git a/test/text_to_binary.extension_test.cpp b/test/text_to_binary.extension_test.cpp
index cf4919a..55f8466 100644
--- a/test/text_to_binary.extension_test.cpp
+++ b/test/text_to_binary.extension_test.cpp
@@ -87,12 +87,14 @@
   EXPECT_THAT(
       CompiledInstructions(input),
       Eq(Concatenate({
-          MakeInstruction(SpvOpExtInstImport, {1}, MakeVector("OpenCL.std")),
-          MakeInstruction(SpvOpExtInstImport, {2}, MakeVector("GLSL.std.450")),
+          MakeInstruction(spv::Op::OpExtInstImport, {1},
+                          MakeVector("OpenCL.std")),
+          MakeInstruction(spv::Op::OpExtInstImport, {2},
+                          MakeVector("GLSL.std.450")),
           MakeInstruction(
-              SpvOpExtInst,
+              spv::Op::OpExtInst,
               {3, 4, 1, uint32_t(OpenCLLIB::Entrypoints::Native_sqrt), 5}),
-          MakeInstruction(SpvOpExtInst,
+          MakeInstruction(spv::Op::OpExtInst,
                           {6, 7, 2, uint32_t(GLSLstd450MatrixInverse), 8}),
       })));
 
@@ -140,62 +142,74 @@
     SPV_KHR_shader_ballot, ExtensionRoundTripTest,
     // We'll get coverage over operand tables by trying the universal
     // environments, and at least one specific environment.
-    Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1,
-                   SPV_ENV_VULKAN_1_0),
-            ValuesIn(std::vector<AssemblyCase>{
-                {"OpCapability SubgroupBallotKHR\n",
-                 MakeInstruction(SpvOpCapability,
-                                 {SpvCapabilitySubgroupBallotKHR})},
-                {"%2 = OpSubgroupBallotKHR %1 %3\n",
-                 MakeInstruction(SpvOpSubgroupBallotKHR, {1, 2, 3})},
-                {"%2 = OpSubgroupFirstInvocationKHR %1 %3\n",
-                 MakeInstruction(SpvOpSubgroupFirstInvocationKHR, {1, 2, 3})},
-                {"OpDecorate %1 BuiltIn SubgroupEqMask\n",
-                 MakeInstruction(SpvOpDecorate, {1, SpvDecorationBuiltIn,
-                                                 SpvBuiltInSubgroupEqMaskKHR})},
-                {"OpDecorate %1 BuiltIn SubgroupGeMask\n",
-                 MakeInstruction(SpvOpDecorate, {1, SpvDecorationBuiltIn,
-                                                 SpvBuiltInSubgroupGeMaskKHR})},
-                {"OpDecorate %1 BuiltIn SubgroupGtMask\n",
-                 MakeInstruction(SpvOpDecorate, {1, SpvDecorationBuiltIn,
-                                                 SpvBuiltInSubgroupGtMaskKHR})},
-                {"OpDecorate %1 BuiltIn SubgroupLeMask\n",
-                 MakeInstruction(SpvOpDecorate, {1, SpvDecorationBuiltIn,
-                                                 SpvBuiltInSubgroupLeMaskKHR})},
-                {"OpDecorate %1 BuiltIn SubgroupLtMask\n",
-                 MakeInstruction(SpvOpDecorate, {1, SpvDecorationBuiltIn,
-                                                 SpvBuiltInSubgroupLtMaskKHR})},
-            })));
+    Combine(
+        Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1,
+               SPV_ENV_VULKAN_1_0),
+        ValuesIn(std::vector<AssemblyCase>{
+            {"OpCapability SubgroupBallotKHR\n",
+             MakeInstruction(spv::Op::OpCapability,
+                             {uint32_t(spv::Capability::SubgroupBallotKHR)})},
+            {"%2 = OpSubgroupBallotKHR %1 %3\n",
+             MakeInstruction(spv::Op::OpSubgroupBallotKHR, {1, 2, 3})},
+            {"%2 = OpSubgroupFirstInvocationKHR %1 %3\n",
+             MakeInstruction(spv::Op::OpSubgroupFirstInvocationKHR, {1, 2, 3})},
+            {"OpDecorate %1 BuiltIn SubgroupEqMask\n",
+             MakeInstruction(spv::Op::OpDecorate,
+                             {1, uint32_t(spv::Decoration::BuiltIn),
+                              uint32_t(spv::BuiltIn::SubgroupEqMaskKHR)})},
+            {"OpDecorate %1 BuiltIn SubgroupGeMask\n",
+             MakeInstruction(spv::Op::OpDecorate,
+                             {1, uint32_t(spv::Decoration::BuiltIn),
+                              uint32_t(spv::BuiltIn::SubgroupGeMaskKHR)})},
+            {"OpDecorate %1 BuiltIn SubgroupGtMask\n",
+             MakeInstruction(spv::Op::OpDecorate,
+                             {1, uint32_t(spv::Decoration::BuiltIn),
+                              uint32_t(spv::BuiltIn::SubgroupGtMaskKHR)})},
+            {"OpDecorate %1 BuiltIn SubgroupLeMask\n",
+             MakeInstruction(spv::Op::OpDecorate,
+                             {1, uint32_t(spv::Decoration::BuiltIn),
+                              uint32_t(spv::BuiltIn::SubgroupLeMaskKHR)})},
+            {"OpDecorate %1 BuiltIn SubgroupLtMask\n",
+             MakeInstruction(spv::Op::OpDecorate,
+                             {1, uint32_t(spv::Decoration::BuiltIn),
+                              uint32_t(spv::BuiltIn::SubgroupLtMaskKHR)})},
+        })));
 
 INSTANTIATE_TEST_SUITE_P(
     SPV_KHR_shader_ballot_vulkan_1_1, ExtensionRoundTripTest,
     // In SPIR-V 1.3 and Vulkan 1.1 we can drop the KHR suffix on the
     // builtin enums.
-    Combine(Values(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_VULKAN_1_1),
-            ValuesIn(std::vector<AssemblyCase>{
-                {"OpCapability SubgroupBallotKHR\n",
-                 MakeInstruction(SpvOpCapability,
-                                 {SpvCapabilitySubgroupBallotKHR})},
-                {"%2 = OpSubgroupBallotKHR %1 %3\n",
-                 MakeInstruction(SpvOpSubgroupBallotKHR, {1, 2, 3})},
-                {"%2 = OpSubgroupFirstInvocationKHR %1 %3\n",
-                 MakeInstruction(SpvOpSubgroupFirstInvocationKHR, {1, 2, 3})},
-                {"OpDecorate %1 BuiltIn SubgroupEqMask\n",
-                 MakeInstruction(SpvOpDecorate, {1, SpvDecorationBuiltIn,
-                                                 SpvBuiltInSubgroupEqMask})},
-                {"OpDecorate %1 BuiltIn SubgroupGeMask\n",
-                 MakeInstruction(SpvOpDecorate, {1, SpvDecorationBuiltIn,
-                                                 SpvBuiltInSubgroupGeMask})},
-                {"OpDecorate %1 BuiltIn SubgroupGtMask\n",
-                 MakeInstruction(SpvOpDecorate, {1, SpvDecorationBuiltIn,
-                                                 SpvBuiltInSubgroupGtMask})},
-                {"OpDecorate %1 BuiltIn SubgroupLeMask\n",
-                 MakeInstruction(SpvOpDecorate, {1, SpvDecorationBuiltIn,
-                                                 SpvBuiltInSubgroupLeMask})},
-                {"OpDecorate %1 BuiltIn SubgroupLtMask\n",
-                 MakeInstruction(SpvOpDecorate, {1, SpvDecorationBuiltIn,
-                                                 SpvBuiltInSubgroupLtMask})},
-            })));
+    Combine(
+        Values(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_VULKAN_1_1),
+        ValuesIn(std::vector<AssemblyCase>{
+            {"OpCapability SubgroupBallotKHR\n",
+             MakeInstruction(spv::Op::OpCapability,
+                             {(uint32_t)spv::Capability::SubgroupBallotKHR})},
+            {"%2 = OpSubgroupBallotKHR %1 %3\n",
+             MakeInstruction(spv::Op::OpSubgroupBallotKHR, {1, 2, 3})},
+            {"%2 = OpSubgroupFirstInvocationKHR %1 %3\n",
+             MakeInstruction(spv::Op::OpSubgroupFirstInvocationKHR, {1, 2, 3})},
+            {"OpDecorate %1 BuiltIn SubgroupEqMask\n",
+             MakeInstruction(spv::Op::OpDecorate,
+                             {1, uint32_t(spv::Decoration::BuiltIn),
+                              uint32_t(spv::BuiltIn::SubgroupEqMask)})},
+            {"OpDecorate %1 BuiltIn SubgroupGeMask\n",
+             MakeInstruction(spv::Op::OpDecorate,
+                             {1, uint32_t(spv::Decoration::BuiltIn),
+                              uint32_t(spv::BuiltIn::SubgroupGeMask)})},
+            {"OpDecorate %1 BuiltIn SubgroupGtMask\n",
+             MakeInstruction(spv::Op::OpDecorate,
+                             {1, uint32_t(spv::Decoration::BuiltIn),
+                              uint32_t(spv::BuiltIn::SubgroupGtMask)})},
+            {"OpDecorate %1 BuiltIn SubgroupLeMask\n",
+             MakeInstruction(spv::Op::OpDecorate,
+                             {1, uint32_t(spv::Decoration::BuiltIn),
+                              uint32_t(spv::BuiltIn::SubgroupLeMask)})},
+            {"OpDecorate %1 BuiltIn SubgroupLtMask\n",
+             MakeInstruction(spv::Op::OpDecorate,
+                             {1, uint32_t(spv::Decoration::BuiltIn),
+                              uint32_t(spv::BuiltIn::SubgroupLtMask)})},
+        })));
 
 // The old builtin names (with KHR suffix) still work in the assembler, and
 // map to the enums without the KHR.
@@ -206,20 +220,25 @@
     Combine(Values(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_VULKAN_1_1),
             ValuesIn(std::vector<AssemblyCase>{
                 {"OpDecorate %1 BuiltIn SubgroupEqMaskKHR\n",
-                 MakeInstruction(SpvOpDecorate, {1, SpvDecorationBuiltIn,
-                                                 SpvBuiltInSubgroupEqMask})},
+                 MakeInstruction(spv::Op::OpDecorate,
+                                 {1, (uint32_t)spv::Decoration::BuiltIn,
+                                  (uint32_t)spv::BuiltIn::SubgroupEqMask})},
                 {"OpDecorate %1 BuiltIn SubgroupGeMaskKHR\n",
-                 MakeInstruction(SpvOpDecorate, {1, SpvDecorationBuiltIn,
-                                                 SpvBuiltInSubgroupGeMask})},
+                 MakeInstruction(spv::Op::OpDecorate,
+                                 {1, (uint32_t)spv::Decoration::BuiltIn,
+                                  (uint32_t)spv::BuiltIn::SubgroupGeMask})},
                 {"OpDecorate %1 BuiltIn SubgroupGtMaskKHR\n",
-                 MakeInstruction(SpvOpDecorate, {1, SpvDecorationBuiltIn,
-                                                 SpvBuiltInSubgroupGtMask})},
+                 MakeInstruction(spv::Op::OpDecorate,
+                                 {1, (uint32_t)spv::Decoration::BuiltIn,
+                                  (uint32_t)spv::BuiltIn::SubgroupGtMask})},
                 {"OpDecorate %1 BuiltIn SubgroupLeMaskKHR\n",
-                 MakeInstruction(SpvOpDecorate, {1, SpvDecorationBuiltIn,
-                                                 SpvBuiltInSubgroupLeMask})},
+                 MakeInstruction(spv::Op::OpDecorate,
+                                 {1, (uint32_t)spv::Decoration::BuiltIn,
+                                  (uint32_t)spv::BuiltIn::SubgroupLeMask})},
                 {"OpDecorate %1 BuiltIn SubgroupLtMaskKHR\n",
-                 MakeInstruction(SpvOpDecorate, {1, SpvDecorationBuiltIn,
-                                                 SpvBuiltInSubgroupLtMask})},
+                 MakeInstruction(spv::Op::OpDecorate,
+                                 {1, (uint32_t)spv::Decoration::BuiltIn,
+                                  (uint32_t)spv::BuiltIn::SubgroupLtMask})},
             })));
 
 // SPV_KHR_shader_draw_parameters
@@ -228,21 +247,24 @@
     SPV_KHR_shader_draw_parameters, ExtensionRoundTripTest,
     // We'll get coverage over operand tables by trying the universal
     // environments, and at least one specific environment.
-    Combine(
-        ValuesIn(CommonVulkanEnvs()),
-        ValuesIn(std::vector<AssemblyCase>{
-            {"OpCapability DrawParameters\n",
-             MakeInstruction(SpvOpCapability, {SpvCapabilityDrawParameters})},
-            {"OpDecorate %1 BuiltIn BaseVertex\n",
-             MakeInstruction(SpvOpDecorate,
-                             {1, SpvDecorationBuiltIn, SpvBuiltInBaseVertex})},
-            {"OpDecorate %1 BuiltIn BaseInstance\n",
-             MakeInstruction(SpvOpDecorate, {1, SpvDecorationBuiltIn,
-                                             SpvBuiltInBaseInstance})},
-            {"OpDecorate %1 BuiltIn DrawIndex\n",
-             MakeInstruction(SpvOpDecorate,
-                             {1, SpvDecorationBuiltIn, SpvBuiltInDrawIndex})},
-        })));
+    Combine(ValuesIn(CommonVulkanEnvs()),
+            ValuesIn(std::vector<AssemblyCase>{
+                {"OpCapability DrawParameters\n",
+                 MakeInstruction(spv::Op::OpCapability,
+                                 {(uint32_t)spv::Capability::DrawParameters})},
+                {"OpDecorate %1 BuiltIn BaseVertex\n",
+                 MakeInstruction(spv::Op::OpDecorate,
+                                 {1, (uint32_t)spv::Decoration::BuiltIn,
+                                  (uint32_t)spv::BuiltIn::BaseVertex})},
+                {"OpDecorate %1 BuiltIn BaseInstance\n",
+                 MakeInstruction(spv::Op::OpDecorate,
+                                 {1, (uint32_t)spv::Decoration::BuiltIn,
+                                  (uint32_t)spv::BuiltIn::BaseInstance})},
+                {"OpDecorate %1 BuiltIn DrawIndex\n",
+                 MakeInstruction(spv::Op::OpDecorate,
+                                 {1, (uint32_t)spv::Decoration::BuiltIn,
+                                  (uint32_t)spv::BuiltIn::DrawIndex})},
+            })));
 
 // SPV_KHR_subgroup_vote
 
@@ -253,14 +275,14 @@
     Combine(ValuesIn(CommonVulkanEnvs()),
             ValuesIn(std::vector<AssemblyCase>{
                 {"OpCapability SubgroupVoteKHR\n",
-                 MakeInstruction(SpvOpCapability,
-                                 {SpvCapabilitySubgroupVoteKHR})},
+                 MakeInstruction(spv::Op::OpCapability,
+                                 {(uint32_t)spv::Capability::SubgroupVoteKHR})},
                 {"%2 = OpSubgroupAnyKHR %1 %3\n",
-                 MakeInstruction(SpvOpSubgroupAnyKHR, {1, 2, 3})},
+                 MakeInstruction(spv::Op::OpSubgroupAnyKHR, {1, 2, 3})},
                 {"%2 = OpSubgroupAllKHR %1 %3\n",
-                 MakeInstruction(SpvOpSubgroupAllKHR, {1, 2, 3})},
+                 MakeInstruction(spv::Op::OpSubgroupAllKHR, {1, 2, 3})},
                 {"%2 = OpSubgroupAllEqualKHR %1 %3\n",
-                 MakeInstruction(SpvOpSubgroupAllEqualKHR, {1, 2, 3})},
+                 MakeInstruction(spv::Op::OpSubgroupAllEqualKHR, {1, 2, 3})},
             })));
 
 // SPV_KHR_16bit_storage
@@ -269,42 +291,50 @@
     SPV_KHR_16bit_storage, ExtensionRoundTripTest,
     // We'll get coverage over operand tables by trying the universal
     // environments, and at least one specific environment.
-    Combine(ValuesIn(CommonVulkanEnvs()),
-            ValuesIn(std::vector<AssemblyCase>{
-                {"OpCapability StorageBuffer16BitAccess\n",
-                 MakeInstruction(SpvOpCapability,
-                                 {SpvCapabilityStorageUniformBufferBlock16})},
-                {"OpCapability StorageBuffer16BitAccess\n",
-                 MakeInstruction(SpvOpCapability,
-                                 {SpvCapabilityStorageBuffer16BitAccess})},
-                {"OpCapability UniformAndStorageBuffer16BitAccess\n",
-                 MakeInstruction(
-                     SpvOpCapability,
-                     {SpvCapabilityUniformAndStorageBuffer16BitAccess})},
-                {"OpCapability UniformAndStorageBuffer16BitAccess\n",
-                 MakeInstruction(SpvOpCapability,
-                                 {SpvCapabilityStorageUniform16})},
-                {"OpCapability StoragePushConstant16\n",
-                 MakeInstruction(SpvOpCapability,
-                                 {SpvCapabilityStoragePushConstant16})},
-                {"OpCapability StorageInputOutput16\n",
-                 MakeInstruction(SpvOpCapability,
-                                 {SpvCapabilityStorageInputOutput16})},
-            })));
+    Combine(
+        ValuesIn(CommonVulkanEnvs()),
+        ValuesIn(std::vector<AssemblyCase>{
+            {"OpCapability StorageBuffer16BitAccess\n",
+             MakeInstruction(
+                 spv::Op::OpCapability,
+                 {(uint32_t)spv::Capability::StorageUniformBufferBlock16})},
+            {"OpCapability StorageBuffer16BitAccess\n",
+             MakeInstruction(
+                 spv::Op::OpCapability,
+                 {(uint32_t)spv::Capability::StorageBuffer16BitAccess})},
+            {"OpCapability UniformAndStorageBuffer16BitAccess\n",
+             MakeInstruction(
+                 spv::Op::OpCapability,
+                 {(uint32_t)
+                      spv::Capability::UniformAndStorageBuffer16BitAccess})},
+            {"OpCapability UniformAndStorageBuffer16BitAccess\n",
+             MakeInstruction(spv::Op::OpCapability,
+                             {(uint32_t)spv::Capability::StorageUniform16})},
+            {"OpCapability StoragePushConstant16\n",
+             MakeInstruction(
+                 spv::Op::OpCapability,
+                 {(uint32_t)spv::Capability::StoragePushConstant16})},
+            {"OpCapability StorageInputOutput16\n",
+             MakeInstruction(
+                 spv::Op::OpCapability,
+                 {(uint32_t)spv::Capability::StorageInputOutput16})},
+        })));
 
 INSTANTIATE_TEST_SUITE_P(
     SPV_KHR_16bit_storage_alias_check, ExtensionAssemblyTest,
-    Combine(ValuesIn(CommonVulkanEnvs()),
-            ValuesIn(std::vector<AssemblyCase>{
-                // The old name maps to the new enum.
-                {"OpCapability StorageUniformBufferBlock16\n",
-                 MakeInstruction(SpvOpCapability,
-                                 {SpvCapabilityStorageBuffer16BitAccess})},
-                // The new name maps to the old enum.
-                {"OpCapability UniformAndStorageBuffer16BitAccess\n",
-                 MakeInstruction(SpvOpCapability,
-                                 {SpvCapabilityStorageUniform16})},
-            })));
+    Combine(
+        ValuesIn(CommonVulkanEnvs()),
+        ValuesIn(std::vector<AssemblyCase>{
+            // The old name maps to the new enum.
+            {"OpCapability StorageUniformBufferBlock16\n",
+             MakeInstruction(
+                 spv::Op::OpCapability,
+                 {(uint32_t)spv::Capability::StorageBuffer16BitAccess})},
+            // The new name maps to the old enum.
+            {"OpCapability UniformAndStorageBuffer16BitAccess\n",
+             MakeInstruction(spv::Op::OpCapability,
+                             {(uint32_t)spv::Capability::StorageUniform16})},
+        })));
 
 // SPV_KHR_device_group
 
@@ -315,10 +345,12 @@
     Combine(ValuesIn(CommonVulkanEnvs()),
             ValuesIn(std::vector<AssemblyCase>{
                 {"OpCapability DeviceGroup\n",
-                 MakeInstruction(SpvOpCapability, {SpvCapabilityDeviceGroup})},
+                 MakeInstruction(spv::Op::OpCapability,
+                                 {(uint32_t)spv::Capability::DeviceGroup})},
                 {"OpDecorate %1 BuiltIn DeviceIndex\n",
-                 MakeInstruction(SpvOpDecorate, {1, SpvDecorationBuiltIn,
-                                                 SpvBuiltInDeviceIndex})},
+                 MakeInstruction(spv::Op::OpDecorate,
+                                 {1, (uint32_t)spv::Decoration::BuiltIn,
+                                  (uint32_t)spv::BuiltIn::DeviceIndex})},
             })));
 
 // SPV_KHR_8bit_storage
@@ -327,19 +359,22 @@
     SPV_KHR_8bit_storage, ExtensionRoundTripTest,
     // We'll get coverage over operand tables by trying the universal
     // environments, and at least one specific environment.
-    Combine(
-        ValuesIn(CommonVulkanEnvs()),
-        ValuesIn(std::vector<AssemblyCase>{
-            {"OpCapability StorageBuffer8BitAccess\n",
-             MakeInstruction(SpvOpCapability,
-                             {SpvCapabilityStorageBuffer8BitAccess})},
-            {"OpCapability UniformAndStorageBuffer8BitAccess\n",
-             MakeInstruction(SpvOpCapability,
-                             {SpvCapabilityUniformAndStorageBuffer8BitAccess})},
-            {"OpCapability StoragePushConstant8\n",
-             MakeInstruction(SpvOpCapability,
-                             {SpvCapabilityStoragePushConstant8})},
-        })));
+    Combine(ValuesIn(CommonVulkanEnvs()),
+            ValuesIn(std::vector<AssemblyCase>{
+                {"OpCapability StorageBuffer8BitAccess\n",
+                 MakeInstruction(
+                     spv::Op::OpCapability,
+                     {(uint32_t)spv::Capability::StorageBuffer8BitAccess})},
+                {"OpCapability UniformAndStorageBuffer8BitAccess\n",
+                 MakeInstruction(
+                     spv::Op::OpCapability,
+                     {(uint32_t)
+                          spv::Capability::UniformAndStorageBuffer8BitAccess})},
+                {"OpCapability StoragePushConstant8\n",
+                 MakeInstruction(
+                     spv::Op::OpCapability,
+                     {(uint32_t)spv::Capability::StoragePushConstant8})},
+            })));
 
 // SPV_KHR_multiview
 
@@ -351,10 +386,12 @@
                    SPV_ENV_VULKAN_1_0),
             ValuesIn(std::vector<AssemblyCase>{
                 {"OpCapability MultiView\n",
-                 MakeInstruction(SpvOpCapability, {SpvCapabilityMultiView})},
+                 MakeInstruction(spv::Op::OpCapability,
+                                 {(uint32_t)spv::Capability::MultiView})},
                 {"OpDecorate %1 BuiltIn ViewIndex\n",
-                 MakeInstruction(SpvOpDecorate, {1, SpvDecorationBuiltIn,
-                                                 SpvBuiltInViewIndex})},
+                 MakeInstruction(spv::Op::OpDecorate,
+                                 {1, (uint32_t)spv::Decoration::BuiltIn,
+                                  (uint32_t)spv::BuiltIn::ViewIndex})},
             })));
 
 // SPV_AMD_shader_explicit_vertex_parameter
@@ -372,9 +409,9 @@
             {PREAMBLE "%3 = OpExtInst %2 %1 InterpolateAtVertexAMD %4 %5\n",
              Concatenate(
                  {MakeInstruction(
-                      SpvOpExtInstImport, {1},
+                      spv::Op::OpExtInstImport, {1},
                       MakeVector("SPV_AMD_shader_explicit_vertex_parameter")),
-                  MakeInstruction(SpvOpExtInst, {2, 3, 1, 1, 4, 5})})},
+                  MakeInstruction(spv::Op::OpExtInst, {2, 3, 1, 1, 4, 5})})},
         })));
 #undef PREAMBLE
 
@@ -391,49 +428,49 @@
         ValuesIn(std::vector<AssemblyCase>{
             {PREAMBLE "%3 = OpExtInst %2 %1 FMin3AMD %4 %5 %6\n",
              Concatenate(
-                 {MakeInstruction(SpvOpExtInstImport, {1},
+                 {MakeInstruction(spv::Op::OpExtInstImport, {1},
                                   MakeVector("SPV_AMD_shader_trinary_minmax")),
-                  MakeInstruction(SpvOpExtInst, {2, 3, 1, 1, 4, 5, 6})})},
+                  MakeInstruction(spv::Op::OpExtInst, {2, 3, 1, 1, 4, 5, 6})})},
             {PREAMBLE "%3 = OpExtInst %2 %1 UMin3AMD %4 %5 %6\n",
              Concatenate(
-                 {MakeInstruction(SpvOpExtInstImport, {1},
+                 {MakeInstruction(spv::Op::OpExtInstImport, {1},
                                   MakeVector("SPV_AMD_shader_trinary_minmax")),
-                  MakeInstruction(SpvOpExtInst, {2, 3, 1, 2, 4, 5, 6})})},
+                  MakeInstruction(spv::Op::OpExtInst, {2, 3, 1, 2, 4, 5, 6})})},
             {PREAMBLE "%3 = OpExtInst %2 %1 SMin3AMD %4 %5 %6\n",
              Concatenate(
-                 {MakeInstruction(SpvOpExtInstImport, {1},
+                 {MakeInstruction(spv::Op::OpExtInstImport, {1},
                                   MakeVector("SPV_AMD_shader_trinary_minmax")),
-                  MakeInstruction(SpvOpExtInst, {2, 3, 1, 3, 4, 5, 6})})},
+                  MakeInstruction(spv::Op::OpExtInst, {2, 3, 1, 3, 4, 5, 6})})},
             {PREAMBLE "%3 = OpExtInst %2 %1 FMax3AMD %4 %5 %6\n",
              Concatenate(
-                 {MakeInstruction(SpvOpExtInstImport, {1},
+                 {MakeInstruction(spv::Op::OpExtInstImport, {1},
                                   MakeVector("SPV_AMD_shader_trinary_minmax")),
-                  MakeInstruction(SpvOpExtInst, {2, 3, 1, 4, 4, 5, 6})})},
+                  MakeInstruction(spv::Op::OpExtInst, {2, 3, 1, 4, 4, 5, 6})})},
             {PREAMBLE "%3 = OpExtInst %2 %1 UMax3AMD %4 %5 %6\n",
              Concatenate(
-                 {MakeInstruction(SpvOpExtInstImport, {1},
+                 {MakeInstruction(spv::Op::OpExtInstImport, {1},
                                   MakeVector("SPV_AMD_shader_trinary_minmax")),
-                  MakeInstruction(SpvOpExtInst, {2, 3, 1, 5, 4, 5, 6})})},
+                  MakeInstruction(spv::Op::OpExtInst, {2, 3, 1, 5, 4, 5, 6})})},
             {PREAMBLE "%3 = OpExtInst %2 %1 SMax3AMD %4 %5 %6\n",
              Concatenate(
-                 {MakeInstruction(SpvOpExtInstImport, {1},
+                 {MakeInstruction(spv::Op::OpExtInstImport, {1},
                                   MakeVector("SPV_AMD_shader_trinary_minmax")),
-                  MakeInstruction(SpvOpExtInst, {2, 3, 1, 6, 4, 5, 6})})},
+                  MakeInstruction(spv::Op::OpExtInst, {2, 3, 1, 6, 4, 5, 6})})},
             {PREAMBLE "%3 = OpExtInst %2 %1 FMid3AMD %4 %5 %6\n",
              Concatenate(
-                 {MakeInstruction(SpvOpExtInstImport, {1},
+                 {MakeInstruction(spv::Op::OpExtInstImport, {1},
                                   MakeVector("SPV_AMD_shader_trinary_minmax")),
-                  MakeInstruction(SpvOpExtInst, {2, 3, 1, 7, 4, 5, 6})})},
+                  MakeInstruction(spv::Op::OpExtInst, {2, 3, 1, 7, 4, 5, 6})})},
             {PREAMBLE "%3 = OpExtInst %2 %1 UMid3AMD %4 %5 %6\n",
              Concatenate(
-                 {MakeInstruction(SpvOpExtInstImport, {1},
+                 {MakeInstruction(spv::Op::OpExtInstImport, {1},
                                   MakeVector("SPV_AMD_shader_trinary_minmax")),
-                  MakeInstruction(SpvOpExtInst, {2, 3, 1, 8, 4, 5, 6})})},
+                  MakeInstruction(spv::Op::OpExtInst, {2, 3, 1, 8, 4, 5, 6})})},
             {PREAMBLE "%3 = OpExtInst %2 %1 SMid3AMD %4 %5 %6\n",
              Concatenate(
-                 {MakeInstruction(SpvOpExtInstImport, {1},
+                 {MakeInstruction(spv::Op::OpExtInstImport, {1},
                                   MakeVector("SPV_AMD_shader_trinary_minmax")),
-                  MakeInstruction(SpvOpExtInst, {2, 3, 1, 9, 4, 5, 6})})},
+                  MakeInstruction(spv::Op::OpExtInst, {2, 3, 1, 9, 4, 5, 6})})},
         })));
 #undef PREAMBLE
 
@@ -444,22 +481,25 @@
     SPV_AMD_gcn_shader, ExtensionRoundTripTest,
     // We'll get coverage over operand tables by trying the universal
     // environments, and at least one specific environment.
-    Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1,
-                   SPV_ENV_VULKAN_1_0),
-            ValuesIn(std::vector<AssemblyCase>{
-                {PREAMBLE "%3 = OpExtInst %2 %1 CubeFaceIndexAMD %4\n",
-                 Concatenate({MakeInstruction(SpvOpExtInstImport, {1},
-                                              MakeVector("SPV_AMD_gcn_shader")),
-                              MakeInstruction(SpvOpExtInst, {2, 3, 1, 1, 4})})},
-                {PREAMBLE "%3 = OpExtInst %2 %1 CubeFaceCoordAMD %4\n",
-                 Concatenate({MakeInstruction(SpvOpExtInstImport, {1},
-                                              MakeVector("SPV_AMD_gcn_shader")),
-                              MakeInstruction(SpvOpExtInst, {2, 3, 1, 2, 4})})},
-                {PREAMBLE "%3 = OpExtInst %2 %1 TimeAMD\n",
-                 Concatenate({MakeInstruction(SpvOpExtInstImport, {1},
-                                              MakeVector("SPV_AMD_gcn_shader")),
-                              MakeInstruction(SpvOpExtInst, {2, 3, 1, 3})})},
-            })));
+    Combine(
+        Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1,
+               SPV_ENV_VULKAN_1_0),
+        ValuesIn(std::vector<AssemblyCase>{
+            {PREAMBLE "%3 = OpExtInst %2 %1 CubeFaceIndexAMD %4\n",
+             Concatenate({MakeInstruction(spv::Op::OpExtInstImport, {1},
+                                          MakeVector("SPV_AMD_gcn_shader")),
+                          MakeInstruction(spv::Op::OpExtInst,
+                                          {2, 3, 1, 1, 4})})},
+            {PREAMBLE "%3 = OpExtInst %2 %1 CubeFaceCoordAMD %4\n",
+             Concatenate({MakeInstruction(spv::Op::OpExtInstImport, {1},
+                                          MakeVector("SPV_AMD_gcn_shader")),
+                          MakeInstruction(spv::Op::OpExtInst,
+                                          {2, 3, 1, 2, 4})})},
+            {PREAMBLE "%3 = OpExtInst %2 %1 TimeAMD\n",
+             Concatenate({MakeInstruction(spv::Op::OpExtInstImport, {1},
+                                          MakeVector("SPV_AMD_gcn_shader")),
+                          MakeInstruction(spv::Op::OpExtInst, {2, 3, 1, 3})})},
+        })));
 #undef PREAMBLE
 
 // SPV_AMD_shader_ballot
@@ -474,23 +514,26 @@
                SPV_ENV_VULKAN_1_0),
         ValuesIn(std::vector<AssemblyCase>{
             {PREAMBLE "%3 = OpExtInst %2 %1 SwizzleInvocationsAMD %4 %5\n",
-             Concatenate({MakeInstruction(SpvOpExtInstImport, {1},
+             Concatenate({MakeInstruction(spv::Op::OpExtInstImport, {1},
                                           MakeVector("SPV_AMD_shader_ballot")),
-                          MakeInstruction(SpvOpExtInst, {2, 3, 1, 1, 4, 5})})},
+                          MakeInstruction(spv::Op::OpExtInst,
+                                          {2, 3, 1, 1, 4, 5})})},
             {PREAMBLE
              "%3 = OpExtInst %2 %1 SwizzleInvocationsMaskedAMD %4 %5\n",
-             Concatenate({MakeInstruction(SpvOpExtInstImport, {1},
+             Concatenate({MakeInstruction(spv::Op::OpExtInstImport, {1},
                                           MakeVector("SPV_AMD_shader_ballot")),
-                          MakeInstruction(SpvOpExtInst, {2, 3, 1, 2, 4, 5})})},
+                          MakeInstruction(spv::Op::OpExtInst,
+                                          {2, 3, 1, 2, 4, 5})})},
             {PREAMBLE "%3 = OpExtInst %2 %1 WriteInvocationAMD %4 %5 %6\n",
-             Concatenate({MakeInstruction(SpvOpExtInstImport, {1},
+             Concatenate({MakeInstruction(spv::Op::OpExtInstImport, {1},
                                           MakeVector("SPV_AMD_shader_ballot")),
-                          MakeInstruction(SpvOpExtInst,
+                          MakeInstruction(spv::Op::OpExtInst,
                                           {2, 3, 1, 3, 4, 5, 6})})},
             {PREAMBLE "%3 = OpExtInst %2 %1 MbcntAMD %4\n",
-             Concatenate({MakeInstruction(SpvOpExtInstImport, {1},
+             Concatenate({MakeInstruction(spv::Op::OpExtInstImport, {1},
                                           MakeVector("SPV_AMD_shader_ballot")),
-                          MakeInstruction(SpvOpExtInst, {2, 3, 1, 4, 4})})},
+                          MakeInstruction(spv::Op::OpExtInst,
+                                          {2, 3, 1, 4, 4})})},
         })));
 #undef PREAMBLE
 
@@ -500,16 +543,18 @@
     SPV_KHR_variable_pointers, ExtensionRoundTripTest,
     // We'll get coverage over operand tables by trying the universal
     // environments, and at least one specific environment.
-    Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1,
-                   SPV_ENV_VULKAN_1_0),
-            ValuesIn(std::vector<AssemblyCase>{
-                {"OpCapability VariablePointers\n",
-                 MakeInstruction(SpvOpCapability,
-                                 {SpvCapabilityVariablePointers})},
-                {"OpCapability VariablePointersStorageBuffer\n",
-                 MakeInstruction(SpvOpCapability,
-                                 {SpvCapabilityVariablePointersStorageBuffer})},
-            })));
+    Combine(
+        Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1,
+               SPV_ENV_VULKAN_1_0),
+        ValuesIn(std::vector<AssemblyCase>{
+            {"OpCapability VariablePointers\n",
+             MakeInstruction(spv::Op::OpCapability,
+                             {(uint32_t)spv::Capability::VariablePointers})},
+            {"OpCapability VariablePointersStorageBuffer\n",
+             MakeInstruction(
+                 spv::Op::OpCapability,
+                 {(uint32_t)spv::Capability::VariablePointersStorageBuffer})},
+        })));
 
 // SPV_KHR_vulkan_memory_model
 
@@ -528,126 +573,145 @@
                SPV_ENV_UNIVERSAL_1_3, SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1),
         ValuesIn(std::vector<AssemblyCase>{
             {"OpCapability VulkanMemoryModel\n",
-             MakeInstruction(SpvOpCapability,
-                             {SpvCapabilityVulkanMemoryModelKHR})},
+             MakeInstruction(
+                 spv::Op::OpCapability,
+                 {(uint32_t)spv::Capability::VulkanMemoryModelKHR})},
             {"OpCapability VulkanMemoryModelDeviceScope\n",
-             MakeInstruction(SpvOpCapability,
-                             {SpvCapabilityVulkanMemoryModelDeviceScopeKHR})},
+             MakeInstruction(
+                 spv::Op::OpCapability,
+                 {(uint32_t)spv::Capability::VulkanMemoryModelDeviceScopeKHR})},
             {"OpMemoryModel Logical Vulkan\n",
-             MakeInstruction(SpvOpMemoryModel, {SpvAddressingModelLogical,
-                                                SpvMemoryModelVulkanKHR})},
+             MakeInstruction(spv::Op::OpMemoryModel,
+                             {(uint32_t)spv::AddressingModel::Logical,
+                              (uint32_t)spv::MemoryModel::VulkanKHR})},
             {"OpStore %1 %2 MakePointerAvailable %3\n",
-             MakeInstruction(SpvOpStore,
-                             {1, 2, SpvMemoryAccessMakePointerAvailableKHRMask,
-                              3})},
+             MakeInstruction(
+                 spv::Op::OpStore,
+                 {1, 2,
+                  (uint32_t)spv::MemoryAccessMask::MakePointerAvailableKHR,
+                  3})},
             {"OpStore %1 %2 Volatile|MakePointerAvailable %3\n",
-             MakeInstruction(SpvOpStore,
-                             {1, 2,
-                              int(SpvMemoryAccessMakePointerAvailableKHRMask) |
-                                  int(SpvMemoryAccessVolatileMask),
-                              3})},
+             MakeInstruction(
+                 spv::Op::OpStore,
+                 {1, 2,
+                  int(spv::MemoryAccessMask::MakePointerAvailableKHR) |
+                      int(spv::MemoryAccessMask::Volatile),
+                  3})},
             {"OpStore %1 %2 Aligned|MakePointerAvailable 4 %3\n",
-             MakeInstruction(SpvOpStore,
-                             {1, 2,
-                              int(SpvMemoryAccessMakePointerAvailableKHRMask) |
-                                  int(SpvMemoryAccessAlignedMask),
-                              4, 3})},
+             MakeInstruction(
+                 spv::Op::OpStore,
+                 {1, 2,
+                  int(spv::MemoryAccessMask::MakePointerAvailableKHR) |
+                      int(spv::MemoryAccessMask::Aligned),
+                  4, 3})},
             {"OpStore %1 %2 MakePointerAvailable|NonPrivatePointer %3\n",
-             MakeInstruction(SpvOpStore,
-                             {1, 2,
-                              int(SpvMemoryAccessMakePointerAvailableKHRMask) |
-                                  int(SpvMemoryAccessNonPrivatePointerKHRMask),
-                              3})},
+             MakeInstruction(
+                 spv::Op::OpStore,
+                 {1, 2,
+                  int(spv::MemoryAccessMask::MakePointerAvailableKHR) |
+                      int(spv::MemoryAccessMask::NonPrivatePointerKHR),
+                  3})},
             {"%2 = OpLoad %1 %3 MakePointerVisible %4\n",
-             MakeInstruction(SpvOpLoad,
-                             {1, 2, 3, SpvMemoryAccessMakePointerVisibleKHRMask,
-                              4})},
+             MakeInstruction(
+                 spv::Op::OpLoad,
+                 {1, 2, 3,
+                  (uint32_t)spv::MemoryAccessMask::MakePointerVisibleKHR, 4})},
             {"%2 = OpLoad %1 %3 Volatile|MakePointerVisible %4\n",
-             MakeInstruction(SpvOpLoad,
-                             {1, 2, 3,
-                              int(SpvMemoryAccessMakePointerVisibleKHRMask) |
-                                  int(SpvMemoryAccessVolatileMask),
-                              4})},
+             MakeInstruction(
+                 spv::Op::OpLoad,
+                 {1, 2, 3,
+                  int(spv::MemoryAccessMask::MakePointerVisibleKHR) |
+                      int(spv::MemoryAccessMask::Volatile),
+                  4})},
             {"%2 = OpLoad %1 %3 Aligned|MakePointerVisible 8 %4\n",
-             MakeInstruction(SpvOpLoad,
-                             {1, 2, 3,
-                              int(SpvMemoryAccessMakePointerVisibleKHRMask) |
-                                  int(SpvMemoryAccessAlignedMask),
-                              8, 4})},
+             MakeInstruction(
+                 spv::Op::OpLoad,
+                 {1, 2, 3,
+                  int(spv::MemoryAccessMask::MakePointerVisibleKHR) |
+                      int(spv::MemoryAccessMask::Aligned),
+                  8, 4})},
             {"%2 = OpLoad %1 %3 MakePointerVisible|NonPrivatePointer "
              "%4\n",
-             MakeInstruction(SpvOpLoad,
-                             {1, 2, 3,
-                              int(SpvMemoryAccessMakePointerVisibleKHRMask) |
-                                  int(SpvMemoryAccessNonPrivatePointerKHRMask),
-                              4})},
+             MakeInstruction(
+                 spv::Op::OpLoad,
+                 {1, 2, 3,
+                  int(spv::MemoryAccessMask::MakePointerVisibleKHR) |
+                      int(spv::MemoryAccessMask::NonPrivatePointerKHR),
+                  4})},
             {"OpCopyMemory %1 %2 "
              "MakePointerAvailable|"
              "MakePointerVisible|"
              "NonPrivatePointer "
              "%3 %4\n",
-             MakeInstruction(SpvOpCopyMemory,
-                             {1, 2,
-                              (int(SpvMemoryAccessMakePointerVisibleKHRMask) |
-                               int(SpvMemoryAccessMakePointerAvailableKHRMask) |
-                               int(SpvMemoryAccessNonPrivatePointerKHRMask)),
-                              3, 4})},
+             MakeInstruction(
+                 spv::Op::OpCopyMemory,
+                 {1, 2,
+                  (int(spv::MemoryAccessMask::MakePointerVisibleKHR) |
+                   int(spv::MemoryAccessMask::MakePointerAvailableKHR) |
+                   int(spv::MemoryAccessMask::NonPrivatePointerKHR)),
+                  3, 4})},
             {"OpCopyMemorySized %1 %2 %3 "
              "MakePointerAvailable|"
              "MakePointerVisible|"
              "NonPrivatePointer "
              "%4 %5\n",
-             MakeInstruction(SpvOpCopyMemorySized,
-                             {1, 2, 3,
-                              (int(SpvMemoryAccessMakePointerVisibleKHRMask) |
-                               int(SpvMemoryAccessMakePointerAvailableKHRMask) |
-                               int(SpvMemoryAccessNonPrivatePointerKHRMask)),
-                              4, 5})},
+             MakeInstruction(
+                 spv::Op::OpCopyMemorySized,
+                 {1, 2, 3,
+                  (int(spv::MemoryAccessMask::MakePointerVisibleKHR) |
+                   int(spv::MemoryAccessMask::MakePointerAvailableKHR) |
+                   int(spv::MemoryAccessMask::NonPrivatePointerKHR)),
+                  4, 5})},
             // Image operands
             {"OpImageWrite %1 %2 %3 MakeTexelAvailable "
              "%4\n",
              MakeInstruction(
-                 SpvOpImageWrite,
-                 {1, 2, 3, int(SpvImageOperandsMakeTexelAvailableKHRMask), 4})},
+                 spv::Op::OpImageWrite,
+                 {1, 2, 3, int(spv::ImageOperandsMask::MakeTexelAvailableKHR),
+                  4})},
             {"OpImageWrite %1 %2 %3 MakeTexelAvailable|NonPrivateTexel "
              "%4\n",
-             MakeInstruction(SpvOpImageWrite,
-                             {1, 2, 3,
-                              int(SpvImageOperandsMakeTexelAvailableKHRMask) |
-                                  int(SpvImageOperandsNonPrivateTexelKHRMask),
-                              4})},
+             MakeInstruction(
+                 spv::Op::OpImageWrite,
+                 {1, 2, 3,
+                  int(spv::ImageOperandsMask::MakeTexelAvailableKHR) |
+                      int(spv::ImageOperandsMask::NonPrivateTexelKHR),
+                  4})},
             {"OpImageWrite %1 %2 %3 "
              "MakeTexelAvailable|NonPrivateTexel|VolatileTexel "
              "%4\n",
-             MakeInstruction(SpvOpImageWrite,
-                             {1, 2, 3,
-                              int(SpvImageOperandsMakeTexelAvailableKHRMask) |
-                                  int(SpvImageOperandsNonPrivateTexelKHRMask) |
-                                  int(SpvImageOperandsVolatileTexelKHRMask),
-                              4})},
+             MakeInstruction(
+                 spv::Op::OpImageWrite,
+                 {1, 2, 3,
+                  int(spv::ImageOperandsMask::MakeTexelAvailableKHR) |
+                      int(spv::ImageOperandsMask::NonPrivateTexelKHR) |
+                      int(spv::ImageOperandsMask::VolatileTexelKHR),
+                  4})},
             {"%2 = OpImageRead %1 %3 %4 MakeTexelVisible "
              "%5\n",
-             MakeInstruction(SpvOpImageRead,
+             MakeInstruction(spv::Op::OpImageRead,
                              {1, 2, 3, 4,
-                              int(SpvImageOperandsMakeTexelVisibleKHRMask),
+                              int(spv::ImageOperandsMask::MakeTexelVisibleKHR),
                               5})},
             {"%2 = OpImageRead %1 %3 %4 "
              "MakeTexelVisible|NonPrivateTexel "
              "%5\n",
-             MakeInstruction(SpvOpImageRead,
-                             {1, 2, 3, 4,
-                              int(SpvImageOperandsMakeTexelVisibleKHRMask) |
-                                  int(SpvImageOperandsNonPrivateTexelKHRMask),
-                              5})},
+             MakeInstruction(
+                 spv::Op::OpImageRead,
+                 {1, 2, 3, 4,
+                  int(spv::ImageOperandsMask::MakeTexelVisibleKHR) |
+                      int(spv::ImageOperandsMask::NonPrivateTexelKHR),
+                  5})},
             {"%2 = OpImageRead %1 %3 %4 "
              "MakeTexelVisible|NonPrivateTexel|VolatileTexel "
              "%5\n",
-             MakeInstruction(SpvOpImageRead,
-                             {1, 2, 3, 4,
-                              int(SpvImageOperandsMakeTexelVisibleKHRMask) |
-                                  int(SpvImageOperandsNonPrivateTexelKHRMask) |
-                                  int(SpvImageOperandsVolatileTexelKHRMask),
-                              5})},
+             MakeInstruction(
+                 spv::Op::OpImageRead,
+                 {1, 2, 3, 4,
+                  int(spv::ImageOperandsMask::MakeTexelVisibleKHR) |
+                      int(spv::ImageOperandsMask::NonPrivateTexelKHR) |
+                      int(spv::ImageOperandsMask::VolatileTexelKHR),
+                  5})},
 
             // Memory semantics ID values are numbers put into a SPIR-V
             // constant integer referenced by Id. There is no token for
@@ -670,20 +734,20 @@
                SPV_ENV_UNIVERSAL_1_2, SPV_ENV_VULKAN_1_0),
         ValuesIn(std::vector<AssemblyCase>{
             {"OpDecorateString %1 UserSemantic \"ABC\"\n",
-             MakeInstruction(SpvOpDecorateStringGOOGLE,
-                             {1, SpvDecorationHlslSemanticGOOGLE},
+             MakeInstruction(spv::Op::OpDecorateStringGOOGLE,
+                             {1, (uint32_t)spv::Decoration::HlslSemanticGOOGLE},
                              MakeVector("ABC"))},
             {"OpDecorateString %1 UserSemantic \"ABC\"\n",
-             MakeInstruction(SpvOpDecorateString,
-                             {1, SpvDecorationUserSemantic},
+             MakeInstruction(spv::Op::OpDecorateString,
+                             {1, (uint32_t)spv::Decoration::UserSemantic},
                              MakeVector("ABC"))},
             {"OpMemberDecorateString %1 3 UserSemantic \"DEF\"\n",
-             MakeInstruction(SpvOpMemberDecorateStringGOOGLE,
-                             {1, 3, SpvDecorationUserSemantic},
+             MakeInstruction(spv::Op::OpMemberDecorateStringGOOGLE,
+                             {1, 3, (uint32_t)spv::Decoration::UserSemantic},
                              MakeVector("DEF"))},
             {"OpMemberDecorateString %1 3 UserSemantic \"DEF\"\n",
-             MakeInstruction(SpvOpMemberDecorateString,
-                             {1, 3, SpvDecorationUserSemantic},
+             MakeInstruction(spv::Op::OpMemberDecorateString,
+                             {1, 3, (uint32_t)spv::Decoration::UserSemantic},
                              MakeVector("DEF"))},
         })));
 
@@ -696,12 +760,13 @@
                SPV_ENV_UNIVERSAL_1_2, SPV_ENV_VULKAN_1_0),
         ValuesIn(std::vector<AssemblyCase>{
             {"OpDecorateStringGOOGLE %1 HlslSemanticGOOGLE \"ABC\"\n",
-             MakeInstruction(SpvOpDecorateStringGOOGLE,
-                             {1, SpvDecorationHlslSemanticGOOGLE},
+             MakeInstruction(spv::Op::OpDecorateStringGOOGLE,
+                             {1, (uint32_t)spv::Decoration::HlslSemanticGOOGLE},
                              MakeVector("ABC"))},
             {"OpMemberDecorateStringGOOGLE %1 3 HlslSemanticGOOGLE \"DEF\"\n",
-             MakeInstruction(SpvOpMemberDecorateStringGOOGLE,
-                             {1, 3, SpvDecorationHlslSemanticGOOGLE},
+             MakeInstruction(spv::Op::OpMemberDecorateStringGOOGLE,
+                             {1, 3,
+                              (uint32_t)spv::Decoration::HlslSemanticGOOGLE},
                              MakeVector("DEF"))},
         })));
 
@@ -721,11 +786,12 @@
         // they are coupled together.
         ValuesIn(std::vector<AssemblyCase>{
             {"OpDecorateId %1 CounterBuffer %2\n",
-             MakeInstruction(SpvOpDecorateId,
-                             {1, SpvDecorationHlslCounterBufferGOOGLE, 2})},
+             MakeInstruction(
+                 spv::Op::OpDecorateId,
+                 {1, (uint32_t)spv::Decoration::HlslCounterBufferGOOGLE, 2})},
             {"OpDecorateId %1 CounterBuffer %2\n",
-             MakeInstruction(SpvOpDecorateId,
-                             {1, SpvDecorationCounterBuffer, 2})},
+             MakeInstruction(spv::Op::OpDecorateId,
+                             {1, (uint32_t)spv::Decoration::CounterBuffer, 2})},
         })));
 
 INSTANTIATE_TEST_SUITE_P(
@@ -739,8 +805,9 @@
         // they are coupled together.
         ValuesIn(std::vector<AssemblyCase>{
             {"OpDecorateId %1 HlslCounterBufferGOOGLE %2\n",
-             MakeInstruction(SpvOpDecorateId,
-                             {1, SpvDecorationHlslCounterBufferGOOGLE, 2})},
+             MakeInstruction(
+                 spv::Op::OpDecorateId,
+                 {1, (uint32_t)spv::Decoration::HlslCounterBufferGOOGLE, 2})},
         })));
 
 // SPV_NV_viewport_array2
@@ -752,23 +819,26 @@
                    SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1),
             ValuesIn(std::vector<AssemblyCase>{
                 {"OpExtension \"SPV_NV_viewport_array2\"\n",
-                 MakeInstruction(SpvOpExtension,
+                 MakeInstruction(spv::Op::OpExtension,
                                  MakeVector("SPV_NV_viewport_array2"))},
                 // The EXT and NV extensions have the same token number for this
                 // capability.
                 {"OpCapability ShaderViewportIndexLayerEXT\n",
-                 MakeInstruction(SpvOpCapability,
-                                 {SpvCapabilityShaderViewportIndexLayerNV})},
+                 MakeInstruction(
+                     spv::Op::OpCapability,
+                     {(uint32_t)spv::Capability::ShaderViewportIndexLayerNV})},
                 // Check the new capability's token number
                 {"OpCapability ShaderViewportIndexLayerEXT\n",
-                 MakeInstruction(SpvOpCapability, {5254})},
+                 MakeInstruction(spv::Op::OpCapability, {5254})},
                 // Decorations
                 {"OpDecorate %1 ViewportRelativeNV\n",
-                 MakeInstruction(SpvOpDecorate,
-                                 {1, SpvDecorationViewportRelativeNV})},
+                 MakeInstruction(
+                     spv::Op::OpDecorate,
+                     {1, (uint32_t)spv::Decoration::ViewportRelativeNV})},
                 {"OpDecorate %1 BuiltIn ViewportMaskNV\n",
-                 MakeInstruction(SpvOpDecorate, {1, SpvDecorationBuiltIn,
-                                                 SpvBuiltInViewportMaskNV})},
+                 MakeInstruction(spv::Op::OpDecorate,
+                                 {1, (uint32_t)spv::Decoration::BuiltIn,
+                                  (uint32_t)spv::BuiltIn::ViewportMaskNV})},
             })));
 
 // SPV_NV_shader_subgroup_partitioned
@@ -779,38 +849,44 @@
         Values(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_VULKAN_1_1),
         ValuesIn(std::vector<AssemblyCase>{
             {"OpExtension \"SPV_NV_shader_subgroup_partitioned\"\n",
-             MakeInstruction(SpvOpExtension,
+             MakeInstruction(spv::Op::OpExtension,
                              MakeVector("SPV_NV_shader_subgroup_partitioned"))},
             {"OpCapability GroupNonUniformPartitionedNV\n",
-             MakeInstruction(SpvOpCapability,
-                             {SpvCapabilityGroupNonUniformPartitionedNV})},
+             MakeInstruction(
+                 spv::Op::OpCapability,
+                 {(uint32_t)spv::Capability::GroupNonUniformPartitionedNV})},
             // Check the new capability's token number
             {"OpCapability GroupNonUniformPartitionedNV\n",
-             MakeInstruction(SpvOpCapability, {5297})},
+             MakeInstruction(spv::Op::OpCapability, {5297})},
             {"%2 = OpGroupNonUniformPartitionNV %1 %3\n",
-             MakeInstruction(SpvOpGroupNonUniformPartitionNV, {1, 2, 3})},
+             MakeInstruction(spv::Op::OpGroupNonUniformPartitionNV, {1, 2, 3})},
             // Check the new instruction's token number
             {"%2 = OpGroupNonUniformPartitionNV %1 %3\n",
-             MakeInstruction(static_cast<SpvOp>(5296), {1, 2, 3})},
+             MakeInstruction(static_cast<spv::Op>(5296), {1, 2, 3})},
             // Check the new group operations
             {"%2 = OpGroupIAdd %1 %3 PartitionedReduceNV %4\n",
-             MakeInstruction(SpvOpGroupIAdd,
-                             {1, 2, 3, SpvGroupOperationPartitionedReduceNV,
-                              4})},
+             MakeInstruction(
+                 spv::Op::OpGroupIAdd,
+                 {1, 2, 3, (uint32_t)spv::GroupOperation::PartitionedReduceNV,
+                  4})},
             {"%2 = OpGroupIAdd %1 %3 PartitionedReduceNV %4\n",
-             MakeInstruction(SpvOpGroupIAdd, {1, 2, 3, 6, 4})},
+             MakeInstruction(spv::Op::OpGroupIAdd, {1, 2, 3, 6, 4})},
             {"%2 = OpGroupIAdd %1 %3 PartitionedInclusiveScanNV %4\n",
-             MakeInstruction(SpvOpGroupIAdd,
-                             {1, 2, 3,
-                              SpvGroupOperationPartitionedInclusiveScanNV, 4})},
+             MakeInstruction(
+                 spv::Op::OpGroupIAdd,
+                 {1, 2, 3,
+                  (uint32_t)spv::GroupOperation::PartitionedInclusiveScanNV,
+                  4})},
             {"%2 = OpGroupIAdd %1 %3 PartitionedInclusiveScanNV %4\n",
-             MakeInstruction(SpvOpGroupIAdd, {1, 2, 3, 7, 4})},
+             MakeInstruction(spv::Op::OpGroupIAdd, {1, 2, 3, 7, 4})},
             {"%2 = OpGroupIAdd %1 %3 PartitionedExclusiveScanNV %4\n",
-             MakeInstruction(SpvOpGroupIAdd,
-                             {1, 2, 3,
-                              SpvGroupOperationPartitionedExclusiveScanNV, 4})},
+             MakeInstruction(
+                 spv::Op::OpGroupIAdd,
+                 {1, 2, 3,
+                  (uint32_t)spv::GroupOperation::PartitionedExclusiveScanNV,
+                  4})},
             {"%2 = OpGroupIAdd %1 %3 PartitionedExclusiveScanNV %4\n",
-             MakeInstruction(SpvOpGroupIAdd, {1, 2, 3, 8, 4})},
+             MakeInstruction(spv::Op::OpGroupIAdd, {1, 2, 3, 8, 4})},
         })));
 
 // SPV_EXT_descriptor_indexing
@@ -823,86 +899,90 @@
                SPV_ENV_VULKAN_1_1),
         ValuesIn(std::vector<AssemblyCase>{
             {"OpExtension \"SPV_EXT_descriptor_indexing\"\n",
-             MakeInstruction(SpvOpExtension,
+             MakeInstruction(spv::Op::OpExtension,
                              MakeVector("SPV_EXT_descriptor_indexing"))},
             // Check capabilities, by name
             {"OpCapability ShaderNonUniform\n",
-             MakeInstruction(SpvOpCapability,
-                             {SpvCapabilityShaderNonUniformEXT})},
+             MakeInstruction(spv::Op::OpCapability,
+                             {(uint32_t)spv::Capability::ShaderNonUniformEXT})},
             {"OpCapability RuntimeDescriptorArray\n",
-             MakeInstruction(SpvOpCapability,
-                             {SpvCapabilityRuntimeDescriptorArrayEXT})},
+             MakeInstruction(
+                 spv::Op::OpCapability,
+                 {(uint32_t)spv::Capability::RuntimeDescriptorArrayEXT})},
             {"OpCapability InputAttachmentArrayDynamicIndexing\n",
-             MakeInstruction(
-                 SpvOpCapability,
-                 {SpvCapabilityInputAttachmentArrayDynamicIndexingEXT})},
+             MakeInstruction(spv::Op::OpCapability,
+                             {(uint32_t)spv::Capability::
+                                  InputAttachmentArrayDynamicIndexingEXT})},
             {"OpCapability UniformTexelBufferArrayDynamicIndexing\n",
-             MakeInstruction(
-                 SpvOpCapability,
-                 {SpvCapabilityUniformTexelBufferArrayDynamicIndexingEXT})},
+             MakeInstruction(spv::Op::OpCapability,
+                             {(uint32_t)spv::Capability::
+                                  UniformTexelBufferArrayDynamicIndexingEXT})},
             {"OpCapability StorageTexelBufferArrayDynamicIndexing\n",
-             MakeInstruction(
-                 SpvOpCapability,
-                 {SpvCapabilityStorageTexelBufferArrayDynamicIndexingEXT})},
+             MakeInstruction(spv::Op::OpCapability,
+                             {(uint32_t)spv::Capability::
+                                  StorageTexelBufferArrayDynamicIndexingEXT})},
             {"OpCapability UniformBufferArrayNonUniformIndexing\n",
-             MakeInstruction(
-                 SpvOpCapability,
-                 {SpvCapabilityUniformBufferArrayNonUniformIndexingEXT})},
+             MakeInstruction(spv::Op::OpCapability,
+                             {(uint32_t)spv::Capability::
+                                  UniformBufferArrayNonUniformIndexingEXT})},
             {"OpCapability SampledImageArrayNonUniformIndexing\n",
-             MakeInstruction(
-                 SpvOpCapability,
-                 {SpvCapabilitySampledImageArrayNonUniformIndexingEXT})},
+             MakeInstruction(spv::Op::OpCapability,
+                             {(uint32_t)spv::Capability::
+                                  SampledImageArrayNonUniformIndexingEXT})},
             {"OpCapability StorageBufferArrayNonUniformIndexing\n",
-             MakeInstruction(
-                 SpvOpCapability,
-                 {SpvCapabilityStorageBufferArrayNonUniformIndexingEXT})},
+             MakeInstruction(spv::Op::OpCapability,
+                             {(uint32_t)spv::Capability::
+                                  StorageBufferArrayNonUniformIndexingEXT})},
             {"OpCapability StorageImageArrayNonUniformIndexing\n",
-             MakeInstruction(
-                 SpvOpCapability,
-                 {SpvCapabilityStorageImageArrayNonUniformIndexingEXT})},
+             MakeInstruction(spv::Op::OpCapability,
+                             {(uint32_t)spv::Capability::
+                                  StorageImageArrayNonUniformIndexingEXT})},
             {"OpCapability InputAttachmentArrayNonUniformIndexing\n",
-             MakeInstruction(
-                 SpvOpCapability,
-                 {SpvCapabilityInputAttachmentArrayNonUniformIndexingEXT})},
+             MakeInstruction(spv::Op::OpCapability,
+                             {(uint32_t)spv::Capability::
+                                  InputAttachmentArrayNonUniformIndexingEXT})},
             {"OpCapability UniformTexelBufferArrayNonUniformIndexing\n",
              MakeInstruction(
-                 SpvOpCapability,
-                 {SpvCapabilityUniformTexelBufferArrayNonUniformIndexingEXT})},
+                 spv::Op::OpCapability,
+                 {(uint32_t)spv::Capability::
+                      UniformTexelBufferArrayNonUniformIndexingEXT})},
             {"OpCapability StorageTexelBufferArrayNonUniformIndexing\n",
              MakeInstruction(
-                 SpvOpCapability,
-                 {SpvCapabilityStorageTexelBufferArrayNonUniformIndexingEXT})},
+                 spv::Op::OpCapability,
+                 {(uint32_t)spv::Capability::
+                      StorageTexelBufferArrayNonUniformIndexingEXT})},
             // Check capabilities, by number
             {"OpCapability ShaderNonUniform\n",
-             MakeInstruction(SpvOpCapability, {5301})},
+             MakeInstruction(spv::Op::OpCapability, {5301})},
             {"OpCapability RuntimeDescriptorArray\n",
-             MakeInstruction(SpvOpCapability, {5302})},
+             MakeInstruction(spv::Op::OpCapability, {5302})},
             {"OpCapability InputAttachmentArrayDynamicIndexing\n",
-             MakeInstruction(SpvOpCapability, {5303})},
+             MakeInstruction(spv::Op::OpCapability, {5303})},
             {"OpCapability UniformTexelBufferArrayDynamicIndexing\n",
-             MakeInstruction(SpvOpCapability, {5304})},
+             MakeInstruction(spv::Op::OpCapability, {5304})},
             {"OpCapability StorageTexelBufferArrayDynamicIndexing\n",
-             MakeInstruction(SpvOpCapability, {5305})},
+             MakeInstruction(spv::Op::OpCapability, {5305})},
             {"OpCapability UniformBufferArrayNonUniformIndexing\n",
-             MakeInstruction(SpvOpCapability, {5306})},
+             MakeInstruction(spv::Op::OpCapability, {5306})},
             {"OpCapability SampledImageArrayNonUniformIndexing\n",
-             MakeInstruction(SpvOpCapability, {5307})},
+             MakeInstruction(spv::Op::OpCapability, {5307})},
             {"OpCapability StorageBufferArrayNonUniformIndexing\n",
-             MakeInstruction(SpvOpCapability, {5308})},
+             MakeInstruction(spv::Op::OpCapability, {5308})},
             {"OpCapability StorageImageArrayNonUniformIndexing\n",
-             MakeInstruction(SpvOpCapability, {5309})},
+             MakeInstruction(spv::Op::OpCapability, {5309})},
             {"OpCapability InputAttachmentArrayNonUniformIndexing\n",
-             MakeInstruction(SpvOpCapability, {5310})},
+             MakeInstruction(spv::Op::OpCapability, {5310})},
             {"OpCapability UniformTexelBufferArrayNonUniformIndexing\n",
-             MakeInstruction(SpvOpCapability, {5311})},
+             MakeInstruction(spv::Op::OpCapability, {5311})},
             {"OpCapability StorageTexelBufferArrayNonUniformIndexing\n",
-             MakeInstruction(SpvOpCapability, {5312})},
+             MakeInstruction(spv::Op::OpCapability, {5312})},
 
             // Check the decoration token
             {"OpDecorate %1 NonUniform\n",
-             MakeInstruction(SpvOpDecorate, {1, SpvDecorationNonUniformEXT})},
+             MakeInstruction(spv::Op::OpDecorate,
+                             {1, (uint32_t)spv::Decoration::NonUniformEXT})},
             {"OpDecorate %1 NonUniform\n",
-             MakeInstruction(SpvOpDecorate, {1, 5300})},
+             MakeInstruction(spv::Op::OpDecorate, {1, 5300})},
         })));
 
 // SPV_KHR_linkonce_odr
@@ -914,13 +994,14 @@
                SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_2),
         ValuesIn(std::vector<AssemblyCase>{
             {"OpExtension \"SPV_KHR_linkonce_odr\"\n",
-             MakeInstruction(SpvOpExtension,
+             MakeInstruction(spv::Op::OpExtension,
                              MakeVector("SPV_KHR_linkonce_odr"))},
             {"OpDecorate %1 LinkageAttributes \"foobar\" LinkOnceODR\n",
-             MakeInstruction(SpvOpDecorate,
-                             Concatenate({{1, SpvDecorationLinkageAttributes},
-                                          MakeVector("foobar"),
-                                          {SpvLinkageTypeLinkOnceODR}}))},
+             MakeInstruction(
+                 spv::Op::OpDecorate,
+                 Concatenate({{1, (uint32_t)spv::Decoration::LinkageAttributes},
+                              MakeVector("foobar"),
+                              {(uint32_t)spv::LinkageType::LinkOnceODR}}))},
         })));
 
 // SPV_KHR_expect_assume
@@ -931,10 +1012,10 @@
                    SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_2),
             ValuesIn(std::vector<AssemblyCase>{
                 {"OpExtension \"SPV_KHR_expect_assume\"\n",
-                 MakeInstruction(SpvOpExtension,
+                 MakeInstruction(spv::Op::OpExtension,
                                  MakeVector("SPV_KHR_expect_assume"))},
                 {"OpAssumeTrueKHR %1\n",
-                 MakeInstruction(SpvOpAssumeTrueKHR, {1})}})));
+                 MakeInstruction(spv::Op::OpAssumeTrueKHR, {1})}})));
 // SPV_KHR_subgroup_uniform_control_flow
 
 INSTANTIATE_TEST_SUITE_P(
@@ -944,12 +1025,12 @@
             ValuesIn(std::vector<AssemblyCase>{
                 {"OpExtension \"SPV_KHR_subgroup_uniform_control_flow\"\n",
                  MakeInstruction(
-                     SpvOpExtension,
+                     spv::Op::OpExtension,
                      MakeVector("SPV_KHR_subgroup_uniform_control_flow"))},
                 {"OpExecutionMode %1 SubgroupUniformControlFlowKHR\n",
-                 MakeInstruction(
-                     SpvOpExecutionMode,
-                     {1, SpvExecutionModeSubgroupUniformControlFlowKHR})},
+                 MakeInstruction(spv::Op::OpExecutionMode,
+                                 {1, (uint32_t)spv::ExecutionMode::
+                                         SubgroupUniformControlFlowKHR})},
             })));
 
 // SPV_KHR_integer_dot_product
@@ -962,61 +1043,71 @@
                SPV_ENV_VULKAN_1_2, SPV_ENV_VULKAN_1_3),
         ValuesIn(std::vector<AssemblyCase>{
             {"OpExtension \"SPV_KHR_integer_dot_product\"\n",
-             MakeInstruction(SpvOpExtension,
+             MakeInstruction(spv::Op::OpExtension,
                              MakeVector("SPV_KHR_integer_dot_product"))},
             {"OpCapability DotProductInputAll\n",
-             MakeInstruction(SpvOpCapability,
-                             {SpvCapabilityDotProductInputAllKHR})},
+             MakeInstruction(
+                 spv::Op::OpCapability,
+                 {(uint32_t)spv::Capability::DotProductInputAllKHR})},
             {"OpCapability DotProductInput4x8Bit\n",
-             MakeInstruction(SpvOpCapability,
-                             {SpvCapabilityDotProductInput4x8BitKHR})},
+             MakeInstruction(
+                 spv::Op::OpCapability,
+                 {(uint32_t)spv::Capability::DotProductInput4x8BitKHR})},
             {"OpCapability DotProductInput4x8BitPacked\n",
-             MakeInstruction(SpvOpCapability,
-                             {SpvCapabilityDotProductInput4x8BitPackedKHR})},
+             MakeInstruction(
+                 spv::Op::OpCapability,
+                 {(uint32_t)spv::Capability::DotProductInput4x8BitPackedKHR})},
             {"OpCapability DotProduct\n",
-             MakeInstruction(SpvOpCapability, {SpvCapabilityDotProductKHR})},
+             MakeInstruction(spv::Op::OpCapability,
+                             {(uint32_t)spv::Capability::DotProductKHR})},
             {"%2 = OpSDot %1 %3 %4\n",
-             MakeInstruction(SpvOpSDotKHR, {1, 2, 3, 4})},
+             MakeInstruction(spv::Op::OpSDotKHR, {1, 2, 3, 4})},
             {"%2 = OpSDot %1 %3 %4 PackedVectorFormat4x8Bit\n",
              MakeInstruction(
-                 SpvOpSDotKHR,
+                 spv::Op::OpSDotKHR,
                  {1, 2, 3, 4,
-                  SpvPackedVectorFormatPackedVectorFormat4x8BitKHR})},
+                  (uint32_t)
+                      spv::PackedVectorFormat::PackedVectorFormat4x8BitKHR})},
             {"%2 = OpUDot %1 %3 %4\n",
-             MakeInstruction(SpvOpUDotKHR, {1, 2, 3, 4})},
+             MakeInstruction(spv::Op::OpUDotKHR, {1, 2, 3, 4})},
             {"%2 = OpUDot %1 %3 %4 PackedVectorFormat4x8Bit\n",
              MakeInstruction(
-                 SpvOpUDotKHR,
+                 spv::Op::OpUDotKHR,
                  {1, 2, 3, 4,
-                  SpvPackedVectorFormatPackedVectorFormat4x8BitKHR})},
+                  (uint32_t)
+                      spv::PackedVectorFormat::PackedVectorFormat4x8BitKHR})},
             {"%2 = OpSUDot %1 %3 %4\n",
-             MakeInstruction(SpvOpSUDotKHR, {1, 2, 3, 4})},
+             MakeInstruction(spv::Op::OpSUDotKHR, {1, 2, 3, 4})},
             {"%2 = OpSUDot %1 %3 %4 PackedVectorFormat4x8Bit\n",
              MakeInstruction(
-                 SpvOpSUDotKHR,
+                 spv::Op::OpSUDotKHR,
                  {1, 2, 3, 4,
-                  SpvPackedVectorFormatPackedVectorFormat4x8BitKHR})},
+                  (uint32_t)
+                      spv::PackedVectorFormat::PackedVectorFormat4x8BitKHR})},
             {"%2 = OpSDotAccSat %1 %3 %4 %5\n",
-             MakeInstruction(SpvOpSDotAccSatKHR, {1, 2, 3, 4, 5})},
+             MakeInstruction(spv::Op::OpSDotAccSatKHR, {1, 2, 3, 4, 5})},
             {"%2 = OpSDotAccSat %1 %3 %4 %5 PackedVectorFormat4x8Bit\n",
              MakeInstruction(
-                 SpvOpSDotAccSatKHR,
+                 spv::Op::OpSDotAccSatKHR,
                  {1, 2, 3, 4, 5,
-                  SpvPackedVectorFormatPackedVectorFormat4x8BitKHR})},
+                  (uint32_t)
+                      spv::PackedVectorFormat::PackedVectorFormat4x8BitKHR})},
             {"%2 = OpUDotAccSat %1 %3 %4 %5\n",
-             MakeInstruction(SpvOpUDotAccSatKHR, {1, 2, 3, 4, 5})},
+             MakeInstruction(spv::Op::OpUDotAccSatKHR, {1, 2, 3, 4, 5})},
             {"%2 = OpUDotAccSat %1 %3 %4 %5 PackedVectorFormat4x8Bit\n",
              MakeInstruction(
-                 SpvOpUDotAccSatKHR,
+                 spv::Op::OpUDotAccSatKHR,
                  {1, 2, 3, 4, 5,
-                  SpvPackedVectorFormatPackedVectorFormat4x8BitKHR})},
+                  (uint32_t)
+                      spv::PackedVectorFormat::PackedVectorFormat4x8BitKHR})},
             {"%2 = OpSUDotAccSat %1 %3 %4 %5\n",
-             MakeInstruction(SpvOpSUDotAccSatKHR, {1, 2, 3, 4, 5})},
+             MakeInstruction(spv::Op::OpSUDotAccSatKHR, {1, 2, 3, 4, 5})},
             {"%2 = OpSUDotAccSat %1 %3 %4 %5 PackedVectorFormat4x8Bit\n",
              MakeInstruction(
-                 SpvOpSUDotAccSatKHR,
+                 spv::Op::OpSUDotAccSatKHR,
                  {1, 2, 3, 4, 5,
-                  SpvPackedVectorFormatPackedVectorFormat4x8BitKHR})},
+                  (uint32_t)
+                      spv::PackedVectorFormat::PackedVectorFormat4x8BitKHR})},
         })));
 
 // SPV_KHR_bit_instructions
@@ -1027,11 +1118,11 @@
                    SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_2),
             ValuesIn(std::vector<AssemblyCase>{
                 {"OpExtension \"SPV_KHR_bit_instructions\"\n",
-                 MakeInstruction(SpvOpExtension,
+                 MakeInstruction(spv::Op::OpExtension,
                                  MakeVector("SPV_KHR_bit_instructions"))},
                 {"OpCapability BitInstructions\n",
-                 MakeInstruction(SpvOpCapability,
-                                 {SpvCapabilityBitInstructions})},
+                 MakeInstruction(spv::Op::OpCapability,
+                                 {(uint32_t)spv::Capability::BitInstructions})},
             })));
 
 // SPV_KHR_uniform_group_instructions
@@ -1044,35 +1135,44 @@
                SPV_ENV_VULKAN_1_2, SPV_ENV_VULKAN_1_3),
         ValuesIn(std::vector<AssemblyCase>{
             {"OpExtension \"SPV_KHR_uniform_group_instructions\"\n",
-             MakeInstruction(SpvOpExtension,
+             MakeInstruction(spv::Op::OpExtension,
                              MakeVector("SPV_KHR_uniform_group_instructions"))},
             {"OpCapability GroupUniformArithmeticKHR\n",
-             MakeInstruction(SpvOpCapability,
-                             {SpvCapabilityGroupUniformArithmeticKHR})},
+             MakeInstruction(
+                 spv::Op::OpCapability,
+                 {(uint32_t)spv::Capability::GroupUniformArithmeticKHR})},
             {"%2 = OpGroupIMulKHR %1 %3 Reduce %4\n",
-             MakeInstruction(SpvOpGroupIMulKHR,
-                             {1, 2, 3, SpvGroupOperationReduce, 4})},
+             MakeInstruction(spv::Op::OpGroupIMulKHR,
+                             {1, 2, 3, (uint32_t)spv::GroupOperation::Reduce,
+                              4})},
             {"%2 = OpGroupFMulKHR %1 %3 Reduce %4\n",
-             MakeInstruction(SpvOpGroupFMulKHR,
-                             {1, 2, 3, SpvGroupOperationReduce, 4})},
+             MakeInstruction(spv::Op::OpGroupFMulKHR,
+                             {1, 2, 3, (uint32_t)spv::GroupOperation::Reduce,
+                              4})},
             {"%2 = OpGroupBitwiseAndKHR %1 %3 Reduce %4\n",
-             MakeInstruction(SpvOpGroupBitwiseAndKHR,
-                             {1, 2, 3, SpvGroupOperationReduce, 4})},
+             MakeInstruction(spv::Op::OpGroupBitwiseAndKHR,
+                             {1, 2, 3, (uint32_t)spv::GroupOperation::Reduce,
+                              4})},
             {"%2 = OpGroupBitwiseOrKHR %1 %3 Reduce %4\n",
-             MakeInstruction(SpvOpGroupBitwiseOrKHR,
-                             {1, 2, 3, SpvGroupOperationReduce, 4})},
+             MakeInstruction(spv::Op::OpGroupBitwiseOrKHR,
+                             {1, 2, 3, (uint32_t)spv::GroupOperation::Reduce,
+                              4})},
             {"%2 = OpGroupBitwiseXorKHR %1 %3 Reduce %4\n",
-             MakeInstruction(SpvOpGroupBitwiseXorKHR,
-                             {1, 2, 3, SpvGroupOperationReduce, 4})},
+             MakeInstruction(spv::Op::OpGroupBitwiseXorKHR,
+                             {1, 2, 3, (uint32_t)spv::GroupOperation::Reduce,
+                              4})},
             {"%2 = OpGroupLogicalAndKHR %1 %3 Reduce %4\n",
-             MakeInstruction(SpvOpGroupLogicalAndKHR,
-                             {1, 2, 3, SpvGroupOperationReduce, 4})},
+             MakeInstruction(spv::Op::OpGroupLogicalAndKHR,
+                             {1, 2, 3, (uint32_t)spv::GroupOperation::Reduce,
+                              4})},
             {"%2 = OpGroupLogicalOrKHR %1 %3 Reduce %4\n",
-             MakeInstruction(SpvOpGroupLogicalOrKHR,
-                             {1, 2, 3, SpvGroupOperationReduce, 4})},
+             MakeInstruction(spv::Op::OpGroupLogicalOrKHR,
+                             {1, 2, 3, (uint32_t)spv::GroupOperation::Reduce,
+                              4})},
             {"%2 = OpGroupLogicalXorKHR %1 %3 Reduce %4\n",
-             MakeInstruction(SpvOpGroupLogicalXorKHR,
-                             {1, 2, 3, SpvGroupOperationReduce, 4})},
+             MakeInstruction(spv::Op::OpGroupLogicalXorKHR,
+                             {1, 2, 3, (uint32_t)spv::GroupOperation::Reduce,
+                              4})},
         })));
 
 // SPV_KHR_subgroup_rotate
@@ -1084,18 +1184,68 @@
                    SPV_ENV_VULKAN_1_3, SPV_ENV_OPENCL_2_1),
             ValuesIn(std::vector<AssemblyCase>{
                 {"OpExtension \"SPV_KHR_subgroup_rotate\"\n",
-                 MakeInstruction(SpvOpExtension,
+                 MakeInstruction(spv::Op::OpExtension,
                                  MakeVector("SPV_KHR_subgroup_rotate"))},
                 {"OpCapability GroupNonUniformRotateKHR\n",
-                 MakeInstruction(SpvOpCapability,
-                                 {SpvCapabilityGroupNonUniformRotateKHR})},
+                 MakeInstruction(
+                     spv::Op::OpCapability,
+                     {(uint32_t)spv::Capability::GroupNonUniformRotateKHR})},
                 {"%2 = OpGroupNonUniformRotateKHR %1 %3 %4 %5\n",
-                 MakeInstruction(SpvOpGroupNonUniformRotateKHR,
+                 MakeInstruction(spv::Op::OpGroupNonUniformRotateKHR,
                                  {1, 2, 3, 4, 5})},
                 {"%2 = OpGroupNonUniformRotateKHR %1 %3 %4 %5 %6\n",
-                 MakeInstruction(SpvOpGroupNonUniformRotateKHR,
+                 MakeInstruction(spv::Op::OpGroupNonUniformRotateKHR,
                                  {1, 2, 3, 4, 5, 6})},
             })));
 
+// SPV_EXT_shader_tile_image
+
+INSTANTIATE_TEST_SUITE_P(
+    SPV_EXT_shader_tile_image, ExtensionRoundTripTest,
+    Combine(
+        Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_5, 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_EXT_shader_tile_image\"\n",
+             MakeInstruction(spv::Op::OpExtension,
+                             MakeVector("SPV_EXT_shader_tile_image"))},
+            {"OpCapability TileImageColorReadAccessEXT\n",
+             MakeInstruction(
+                 spv::Op::OpCapability,
+                 {(uint32_t)spv::Capability::TileImageColorReadAccessEXT})},
+            {"OpCapability TileImageDepthReadAccessEXT\n",
+             MakeInstruction(
+                 spv::Op::OpCapability,
+                 {(uint32_t)spv::Capability::TileImageDepthReadAccessEXT})},
+            {"OpCapability TileImageStencilReadAccessEXT\n",
+             MakeInstruction(
+                 spv::Op::OpCapability,
+                 {(uint32_t)spv::Capability::TileImageStencilReadAccessEXT})},
+            {"OpExecutionMode %1 NonCoherentColorAttachmentReadEXT\n",
+             MakeInstruction(spv::Op::OpExecutionMode,
+                             {1, (uint32_t)spv::ExecutionMode::
+                                     NonCoherentColorAttachmentReadEXT})},
+            {"OpExecutionMode %1 NonCoherentDepthAttachmentReadEXT\n",
+             MakeInstruction(spv::Op::OpExecutionMode,
+                             {1, (uint32_t)spv::ExecutionMode::
+                                     NonCoherentDepthAttachmentReadEXT})},
+            {"OpExecutionMode %1 NonCoherentStencilAttachmentReadEXT\n",
+             MakeInstruction(spv::Op::OpExecutionMode,
+                             {1, (uint32_t)spv::ExecutionMode::
+                                     NonCoherentStencilAttachmentReadEXT})},
+            {"%2 = OpColorAttachmentReadEXT %1 %3\n",
+             MakeInstruction(spv::Op::OpColorAttachmentReadEXT, {1, 2, 3})},
+            {"%2 = OpColorAttachmentReadEXT %1 %3 %4\n",
+             MakeInstruction(spv::Op::OpColorAttachmentReadEXT, {1, 2, 3, 4})},
+            {"%2 = OpDepthAttachmentReadEXT %1\n",
+             MakeInstruction(spv::Op::OpDepthAttachmentReadEXT, {1, 2})},
+            {"%2 = OpDepthAttachmentReadEXT %1 %3\n",
+             MakeInstruction(spv::Op::OpDepthAttachmentReadEXT, {1, 2, 3})},
+            {"%2 = OpStencilAttachmentReadEXT %1\n",
+             MakeInstruction(spv::Op::OpStencilAttachmentReadEXT, {1, 2})},
+            {"%2 = OpStencilAttachmentReadEXT %1 %3\n",
+             MakeInstruction(spv::Op::OpStencilAttachmentReadEXT, {1, 2, 3})},
+        })));
+
 }  // namespace
 }  // namespace spvtools
diff --git a/test/text_to_binary.function_test.cpp b/test/text_to_binary.function_test.cpp
index 55a8e6c..9361d6c 100644
--- a/test/text_to_binary.function_test.cpp
+++ b/test/text_to_binary.function_test.cpp
@@ -33,25 +33,25 @@
 // Test OpFunction
 
 using OpFunctionControlTest = spvtest::TextToBinaryTestBase<
-    ::testing::TestWithParam<EnumCase<SpvFunctionControlMask>>>;
+    ::testing::TestWithParam<EnumCase<spv::FunctionControlMask>>>;
 
 TEST_P(OpFunctionControlTest, AnySingleFunctionControlMask) {
   const std::string input = "%result_id = OpFunction %result_type " +
                             GetParam().name() + " %function_type ";
-  EXPECT_THAT(
-      CompiledInstructions(input),
-      Eq(MakeInstruction(SpvOpFunction, {1, 2, GetParam().value(), 3})));
+  EXPECT_THAT(CompiledInstructions(input),
+              Eq(MakeInstruction(spv::Op::OpFunction,
+                                 {1, 2, (uint32_t)GetParam().value(), 3})));
 }
 
 // clang-format off
-#define CASE(VALUE,NAME) { SpvFunctionControl##VALUE, NAME }
+#define CASE(VALUE,NAME) { spv::FunctionControlMask::VALUE, NAME }
 INSTANTIATE_TEST_SUITE_P(TextToBinaryFunctionTest, OpFunctionControlTest,
-                        ::testing::ValuesIn(std::vector<EnumCase<SpvFunctionControlMask>>{
+                        ::testing::ValuesIn(std::vector<EnumCase<spv::FunctionControlMask>>{
                             CASE(MaskNone, "None"),
-                            CASE(InlineMask, "Inline"),
-                            CASE(DontInlineMask, "DontInline"),
-                            CASE(PureMask, "Pure"),
-                            CASE(ConstMask, "Const"),
+                            CASE(Inline, "Inline"),
+                            CASE(DontInline, "DontInline"),
+                            CASE(Pure, "Pure"),
+                            CASE(Const, "Const"),
                         }));
 #undef CASE
 // clang-format on
@@ -61,11 +61,12 @@
   // the instruction parsing logic with spvTextParseMask.
   const std::string input =
       "%result_id = OpFunction %result_type Inline|Pure|Const %function_type";
-  const uint32_t expected_mask = SpvFunctionControlInlineMask |
-                                 SpvFunctionControlPureMask |
-                                 SpvFunctionControlConstMask;
-  EXPECT_THAT(CompiledInstructions(input),
-              Eq(MakeInstruction(SpvOpFunction, {1, 2, expected_mask, 3})));
+  const uint32_t expected_mask = uint32_t(spv::FunctionControlMask::Inline |
+                                          spv::FunctionControlMask::Pure |
+                                          spv::FunctionControlMask::Const);
+  EXPECT_THAT(
+      CompiledInstructions(input),
+      Eq(MakeInstruction(spv::Op::OpFunction, {1, 2, expected_mask, 3})));
 }
 
 TEST_F(OpFunctionControlTest, WrongFunctionControl) {
diff --git a/test/text_to_binary.group_test.cpp b/test/text_to_binary.group_test.cpp
index becc3aa..606e00e 100644
--- a/test/text_to_binary.group_test.cpp
+++ b/test/text_to_binary.group_test.cpp
@@ -32,20 +32,20 @@
 // Test GroupOperation enum
 
 using GroupOperationTest = spvtest::TextToBinaryTestBase<
-    ::testing::TestWithParam<EnumCase<SpvGroupOperation>>>;
+    ::testing::TestWithParam<EnumCase<spv::GroupOperation>>>;
 
 TEST_P(GroupOperationTest, AnyGroupOperation) {
   const std::string input =
       "%result = OpGroupIAdd %type %scope " + GetParam().name() + " %x";
-  EXPECT_THAT(
-      CompiledInstructions(input),
-      Eq(MakeInstruction(SpvOpGroupIAdd, {1, 2, 3, GetParam().value(), 4})));
+  EXPECT_THAT(CompiledInstructions(input),
+              Eq(MakeInstruction(spv::Op::OpGroupIAdd,
+                                 {1, 2, 3, (uint32_t)GetParam().value(), 4})));
 }
 
 // clang-format off
-#define CASE(NAME) { SpvGroupOperation##NAME, #NAME}
+#define CASE(NAME) { spv::GroupOperation::NAME, #NAME}
 INSTANTIATE_TEST_SUITE_P(TextToBinaryGroupOperation, GroupOperationTest,
-                        ::testing::ValuesIn(std::vector<EnumCase<SpvGroupOperation>>{
+                        ::testing::ValuesIn(std::vector<EnumCase<spv::GroupOperation>>{
                             CASE(Reduce),
                             CASE(InclusiveScan),
                             CASE(ExclusiveScan),
diff --git a/test/text_to_binary.image_test.cpp b/test/text_to_binary.image_test.cpp
index 8d8ff43..3e7a560 100644
--- a/test/text_to_binary.image_test.cpp
+++ b/test/text_to_binary.image_test.cpp
@@ -45,11 +45,11 @@
   const std::string input =
       "%2 = OpImageFetch %1 %3 %4" + GetParam().image_operands + "\n";
   EXPECT_THAT(CompiledInstructions(input),
-              Eq(MakeInstruction(SpvOpImageFetch, {1, 2, 3, 4},
+              Eq(MakeInstruction(spv::Op::OpImageFetch, {1, 2, 3, 4},
                                  GetParam().expected_mask_and_operands)));
 }
 
-#define MASK(NAME) SpvImageOperands##NAME##Mask
+#define MASK(NAME) uint32_t(spv::ImageOperandsMask::NAME)
 INSTANTIATE_TEST_SUITE_P(
     TextToBinaryImageOperandsAny, ImageOperandsTest,
     ::testing::ValuesIn(std::vector<ImageOperandsCase>{
@@ -68,7 +68,7 @@
         {" MinLod %5", {MASK(MinLod), 5}},
     }));
 #undef MASK
-#define MASK(NAME) static_cast<uint32_t>(SpvImageOperands##NAME##Mask)
+#define MASK(NAME) static_cast<uint32_t>(spv::ImageOperandsMask::NAME)
 INSTANTIATE_TEST_SUITE_P(
     TextToBinaryImageOperandsCombination, ImageOperandsTest,
     ::testing::ValuesIn(std::vector<ImageOperandsCase>{
@@ -110,7 +110,7 @@
 TEST_F(OpImageTest, Valid) {
   const std::string input = "%2 = OpImage %1 %3\n";
   EXPECT_THAT(CompiledInstructions(input),
-              Eq(MakeInstruction(SpvOpImage, {1, 2, 3})));
+              Eq(MakeInstruction(spv::Op::OpImage, {1, 2, 3})));
 
   // Test the disassembler.
   EXPECT_THAT(EncodeAndDecodeSuccessfully(input), input);
@@ -153,7 +153,7 @@
 TEST_F(OpImageSparseReadTest, OnlyRequiredOperands) {
   const std::string input = "%2 = OpImageSparseRead %1 %3 %4\n";
   EXPECT_THAT(CompiledInstructions(input),
-              Eq(MakeInstruction(SpvOpImageSparseRead, {1, 2, 3, 4})));
+              Eq(MakeInstruction(spv::Op::OpImageSparseRead, {1, 2, 3, 4})));
   // Test the disassembler.
   EXPECT_THAT(EncodeAndDecodeSuccessfully(input), input);
 }
@@ -167,13 +167,13 @@
   const std::string input =
       "%2 = OpImageSparseRead %1 %3 %4" + GetParam().image_operands + "\n";
   EXPECT_THAT(CompiledInstructions(input),
-              Eq(MakeInstruction(SpvOpImageSparseRead, {1, 2, 3, 4},
+              Eq(MakeInstruction(spv::Op::OpImageSparseRead, {1, 2, 3, 4},
                                  GetParam().expected_mask_and_operands)));
   // Test the disassembler.
   EXPECT_THAT(EncodeAndDecodeSuccessfully(input), input);
 }
 
-#define MASK(NAME) SpvImageOperands##NAME##Mask
+#define MASK(NAME) uint32_t(spv::ImageOperandsMask::NAME)
 INSTANTIATE_TEST_SUITE_P(ImageSparseReadImageOperandsAny,
                          ImageSparseReadImageOperandsTest,
                          ::testing::ValuesIn(std::vector<ImageOperandsCase>{
@@ -190,7 +190,7 @@
                              {" MinLod %5", {MASK(MinLod), 5}},
                          }));
 #undef MASK
-#define MASK(NAME) static_cast<uint32_t>(SpvImageOperands##NAME##Mask)
+#define MASK(NAME) static_cast<uint32_t>(spv::ImageOperandsMask::NAME)
 INSTANTIATE_TEST_SUITE_P(
     ImageSparseReadImageOperandsCombination, ImageSparseReadImageOperandsTest,
     ::testing::ValuesIn(std::vector<ImageOperandsCase>{
diff --git a/test/text_to_binary.memory_test.cpp b/test/text_to_binary.memory_test.cpp
index f94c134..629ab66 100644
--- a/test/text_to_binary.memory_test.cpp
+++ b/test/text_to_binary.memory_test.cpp
@@ -35,51 +35,53 @@
 // Test assembly of Memory Access masks
 
 using MemoryAccessTest = spvtest::TextToBinaryTestBase<
-    ::testing::TestWithParam<EnumCase<SpvMemoryAccessMask>>>;
+    ::testing::TestWithParam<EnumCase<spv::MemoryAccessMask>>>;
 
 TEST_P(MemoryAccessTest, AnySingleMemoryAccessMask) {
   std::stringstream input;
   input << "OpStore %ptr %value " << GetParam().name();
   for (auto operand : GetParam().operands()) input << " " << operand;
-  EXPECT_THAT(CompiledInstructions(input.str()),
-              Eq(MakeInstruction(SpvOpStore, {1, 2, GetParam().value()},
-                                 GetParam().operands())));
+  EXPECT_THAT(
+      CompiledInstructions(input.str()),
+      Eq(MakeInstruction(spv::Op::OpStore, {1, 2, (uint32_t)GetParam().value()},
+                         GetParam().operands())));
 }
 
 INSTANTIATE_TEST_SUITE_P(
     TextToBinaryMemoryAccessTest, MemoryAccessTest,
-    ::testing::ValuesIn(std::vector<EnumCase<SpvMemoryAccessMask>>{
-        {SpvMemoryAccessMaskNone, "None", {}},
-        {SpvMemoryAccessVolatileMask, "Volatile", {}},
-        {SpvMemoryAccessAlignedMask, "Aligned", {16}},
-        {SpvMemoryAccessNontemporalMask, "Nontemporal", {}},
+    ::testing::ValuesIn(std::vector<EnumCase<spv::MemoryAccessMask>>{
+        {spv::MemoryAccessMask::MaskNone, "None", {}},
+        {spv::MemoryAccessMask::Volatile, "Volatile", {}},
+        {spv::MemoryAccessMask::Aligned, "Aligned", {16}},
+        {spv::MemoryAccessMask::Nontemporal, "Nontemporal", {}},
     }));
 
 TEST_F(TextToBinaryTest, CombinedMemoryAccessMask) {
   const std::string input = "OpStore %ptr %value Volatile|Aligned 16";
-  const uint32_t expected_mask =
-      SpvMemoryAccessVolatileMask | SpvMemoryAccessAlignedMask;
+  const uint32_t expected_mask = uint32_t(spv::MemoryAccessMask::Volatile |
+                                          spv::MemoryAccessMask::Aligned);
   EXPECT_THAT(expected_mask, Eq(3u));
   EXPECT_THAT(CompiledInstructions(input),
-              Eq(MakeInstruction(SpvOpStore, {1, 2, expected_mask, 16})));
+              Eq(MakeInstruction(spv::Op::OpStore, {1, 2, expected_mask, 16})));
 }
 
 // Test Storage Class enum values
 
 using StorageClassTest = spvtest::TextToBinaryTestBase<
-    ::testing::TestWithParam<EnumCase<SpvStorageClass>>>;
+    ::testing::TestWithParam<EnumCase<spv::StorageClass>>>;
 
 TEST_P(StorageClassTest, AnyStorageClass) {
   const std::string input = "%1 = OpVariable %2 " + GetParam().name();
   EXPECT_THAT(CompiledInstructions(input),
-              Eq(MakeInstruction(SpvOpVariable, {1, 2, GetParam().value()})));
+              Eq(MakeInstruction(spv::Op::OpVariable,
+                                 {1, 2, (uint32_t)GetParam().value()})));
 }
 
 // clang-format off
-#define CASE(NAME) { SpvStorageClass##NAME, #NAME, {} }
+#define CASE(NAME) { spv::StorageClass::NAME, #NAME, {} }
 INSTANTIATE_TEST_SUITE_P(
     TextToBinaryStorageClassTest, StorageClassTest,
-    ::testing::ValuesIn(std::vector<EnumCase<SpvStorageClass>>{
+    ::testing::ValuesIn(std::vector<EnumCase<spv::StorageClass>>{
         CASE(UniformConstant),
         CASE(Input),
         CASE(Uniform),
@@ -103,7 +105,7 @@
 TEST_F(MemoryRoundTripTest, OpPtrEqualGood) {
   std::string spirv = "%2 = OpPtrEqual %1 %3 %4\n";
   EXPECT_THAT(CompiledInstructions(spirv, SPV_ENV_UNIVERSAL_1_4),
-              Eq(MakeInstruction(SpvOpPtrEqual, {1, 2, 3, 4})));
+              Eq(MakeInstruction(spv::Op::OpPtrEqual, {1, 2, 3, 4})));
   std::string disassembly = EncodeAndDecodeSuccessfully(
       spirv, SPV_BINARY_TO_TEXT_OPTION_NONE, SPV_ENV_UNIVERSAL_1_4);
   EXPECT_THAT(disassembly, Eq(spirv));
@@ -120,7 +122,7 @@
 TEST_F(MemoryRoundTripTest, OpPtrNotEqualGood) {
   std::string spirv = "%2 = OpPtrNotEqual %1 %3 %4\n";
   EXPECT_THAT(CompiledInstructions(spirv, SPV_ENV_UNIVERSAL_1_4),
-              Eq(MakeInstruction(SpvOpPtrNotEqual, {1, 2, 3, 4})));
+              Eq(MakeInstruction(spv::Op::OpPtrNotEqual, {1, 2, 3, 4})));
   std::string disassembly = EncodeAndDecodeSuccessfully(
       spirv, SPV_BINARY_TO_TEXT_OPTION_NONE, SPV_ENV_UNIVERSAL_1_4);
   EXPECT_THAT(disassembly, Eq(spirv));
@@ -137,7 +139,7 @@
 TEST_F(MemoryRoundTripTest, OpPtrDiffGood) {
   std::string spirv = "%2 = OpPtrDiff %1 %3 %4\n";
   EXPECT_THAT(CompiledInstructions(spirv, SPV_ENV_UNIVERSAL_1_4),
-              Eq(MakeInstruction(SpvOpPtrDiff, {1, 2, 3, 4})));
+              Eq(MakeInstruction(spv::Op::OpPtrDiff, {1, 2, 3, 4})));
   std::string disassembly = EncodeAndDecodeSuccessfully(
       spirv, SPV_BINARY_TO_TEXT_OPTION_NONE, SPV_ENV_UNIVERSAL_1_4);
   EXPECT_THAT(disassembly, Eq(spirv));
@@ -157,7 +159,7 @@
 TEST_F(MemoryRoundTripTest, OpCopyMemoryNoMemAccessGood) {
   std::string spirv = "OpCopyMemory %1 %2\n";
   EXPECT_THAT(CompiledInstructions(spirv),
-              Eq(MakeInstruction(SpvOpCopyMemory, {1, 2})));
+              Eq(MakeInstruction(spv::Op::OpCopyMemory, {1, 2})));
   std::string disassembly =
       EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
   EXPECT_THAT(disassembly, Eq(spirv));
@@ -179,7 +181,7 @@
 TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessNoneGood) {
   std::string spirv = "OpCopyMemory %1 %2 None\n";
   EXPECT_THAT(CompiledInstructions(spirv),
-              Eq(MakeInstruction(SpvOpCopyMemory, {1, 2, 0})));
+              Eq(MakeInstruction(spv::Op::OpCopyMemory, {1, 2, 0})));
   std::string disassembly =
       EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
   EXPECT_THAT(disassembly, Eq(spirv));
@@ -188,7 +190,7 @@
 TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessVolatileGood) {
   std::string spirv = "OpCopyMemory %1 %2 Volatile\n";
   EXPECT_THAT(CompiledInstructions(spirv),
-              Eq(MakeInstruction(SpvOpCopyMemory, {1, 2, 1})));
+              Eq(MakeInstruction(spv::Op::OpCopyMemory, {1, 2, 1})));
   std::string disassembly =
       EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
   EXPECT_THAT(disassembly, Eq(spirv));
@@ -197,7 +199,7 @@
 TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessAligned8Good) {
   std::string spirv = "OpCopyMemory %1 %2 Aligned 8\n";
   EXPECT_THAT(CompiledInstructions(spirv),
-              Eq(MakeInstruction(SpvOpCopyMemory, {1, 2, 2, 8})));
+              Eq(MakeInstruction(spv::Op::OpCopyMemory, {1, 2, 2, 8})));
   std::string disassembly =
       EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
   EXPECT_THAT(disassembly, Eq(spirv));
@@ -206,7 +208,7 @@
 TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessNontemporalGood) {
   std::string spirv = "OpCopyMemory %1 %2 Nontemporal\n";
   EXPECT_THAT(CompiledInstructions(spirv),
-              Eq(MakeInstruction(SpvOpCopyMemory, {1, 2, 4})));
+              Eq(MakeInstruction(spv::Op::OpCopyMemory, {1, 2, 4})));
   std::string disassembly =
       EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
   EXPECT_THAT(disassembly, Eq(spirv));
@@ -215,7 +217,7 @@
 TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessAvGood) {
   std::string spirv = "OpCopyMemory %1 %2 MakePointerAvailable %3\n";
   EXPECT_THAT(CompiledInstructions(spirv),
-              Eq(MakeInstruction(SpvOpCopyMemory, {1, 2, 8, 3})));
+              Eq(MakeInstruction(spv::Op::OpCopyMemory, {1, 2, 8, 3})));
   std::string disassembly =
       EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
   EXPECT_THAT(disassembly, Eq(spirv));
@@ -224,7 +226,7 @@
 TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessVisGood) {
   std::string spirv = "OpCopyMemory %1 %2 MakePointerVisible %3\n";
   EXPECT_THAT(CompiledInstructions(spirv),
-              Eq(MakeInstruction(SpvOpCopyMemory, {1, 2, 16, 3})));
+              Eq(MakeInstruction(spv::Op::OpCopyMemory, {1, 2, 16, 3})));
   std::string disassembly =
       EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
   EXPECT_THAT(disassembly, Eq(spirv));
@@ -233,7 +235,7 @@
 TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessNonPrivateGood) {
   std::string spirv = "OpCopyMemory %1 %2 NonPrivatePointer\n";
   EXPECT_THAT(CompiledInstructions(spirv),
-              Eq(MakeInstruction(SpvOpCopyMemory, {1, 2, 32})));
+              Eq(MakeInstruction(spv::Op::OpCopyMemory, {1, 2, 32})));
   std::string disassembly =
       EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
   EXPECT_THAT(disassembly, Eq(spirv));
@@ -245,7 +247,7 @@
       "Volatile|Aligned|Nontemporal|MakePointerAvailable|"
       "MakePointerVisible|NonPrivatePointer 16 %3 %4\n";
   EXPECT_THAT(CompiledInstructions(spirv),
-              Eq(MakeInstruction(SpvOpCopyMemory, {1, 2, 63, 16, 3, 4})));
+              Eq(MakeInstruction(spv::Op::OpCopyMemory, {1, 2, 63, 16, 3, 4})));
   std::string disassembly =
       EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
   EXPECT_THAT(disassembly, Eq(spirv));
@@ -255,7 +257,7 @@
   std::string spirv = "OpCopyMemory %1 %2 Volatile Volatile\n";
   // Note: This will assemble but should not validate for SPIR-V 1.3
   EXPECT_THAT(CompiledInstructions(spirv, SPV_ENV_UNIVERSAL_1_3),
-              Eq(MakeInstruction(SpvOpCopyMemory, {1, 2, 1, 1})));
+              Eq(MakeInstruction(spv::Op::OpCopyMemory, {1, 2, 1, 1})));
   std::string disassembly =
       EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
   EXPECT_THAT(disassembly, Eq(spirv));
@@ -264,7 +266,7 @@
 TEST_F(MemoryRoundTripTest, OpCopyMemoryTwoAccessV14Good) {
   std::string spirv = "OpCopyMemory %1 %2 Volatile Volatile\n";
   EXPECT_THAT(CompiledInstructions(spirv, SPV_ENV_UNIVERSAL_1_4),
-              Eq(MakeInstruction(SpvOpCopyMemory, {1, 2, 1, 1})));
+              Eq(MakeInstruction(spv::Op::OpCopyMemory, {1, 2, 1, 1})));
   std::string disassembly =
       EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
   EXPECT_THAT(disassembly, Eq(spirv));
@@ -275,8 +277,9 @@
       "OpCopyMemory %1 %2 Volatile|Nontemporal|"
       "MakePointerVisible %3 "
       "Aligned|MakePointerAvailable|NonPrivatePointer 16 %4\n";
-  EXPECT_THAT(CompiledInstructions(spirv),
-              Eq(MakeInstruction(SpvOpCopyMemory, {1, 2, 21, 3, 42, 16, 4})));
+  EXPECT_THAT(
+      CompiledInstructions(spirv),
+      Eq(MakeInstruction(spv::Op::OpCopyMemory, {1, 2, 21, 3, 42, 16, 4})));
   std::string disassembly =
       EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
   EXPECT_THAT(disassembly, Eq(spirv));
@@ -287,7 +290,7 @@
 TEST_F(MemoryRoundTripTest, OpCopyMemorySizedNoMemAccessGood) {
   std::string spirv = "OpCopyMemorySized %1 %2 %3\n";
   EXPECT_THAT(CompiledInstructions(spirv),
-              Eq(MakeInstruction(SpvOpCopyMemorySized, {1, 2, 3})));
+              Eq(MakeInstruction(spv::Op::OpCopyMemorySized, {1, 2, 3})));
   std::string disassembly =
       EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
   EXPECT_THAT(disassembly, Eq(spirv));
@@ -309,7 +312,7 @@
 TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessNoneGood) {
   std::string spirv = "OpCopyMemorySized %1 %2 %3 None\n";
   EXPECT_THAT(CompiledInstructions(spirv),
-              Eq(MakeInstruction(SpvOpCopyMemorySized, {1, 2, 3, 0})));
+              Eq(MakeInstruction(spv::Op::OpCopyMemorySized, {1, 2, 3, 0})));
   std::string disassembly =
       EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
   EXPECT_THAT(disassembly, Eq(spirv));
@@ -318,7 +321,7 @@
 TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessVolatileGood) {
   std::string spirv = "OpCopyMemorySized %1 %2 %3 Volatile\n";
   EXPECT_THAT(CompiledInstructions(spirv),
-              Eq(MakeInstruction(SpvOpCopyMemorySized, {1, 2, 3, 1})));
+              Eq(MakeInstruction(spv::Op::OpCopyMemorySized, {1, 2, 3, 1})));
   std::string disassembly =
       EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
   EXPECT_THAT(disassembly, Eq(spirv));
@@ -327,7 +330,7 @@
 TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessAligned8Good) {
   std::string spirv = "OpCopyMemorySized %1 %2 %3 Aligned 8\n";
   EXPECT_THAT(CompiledInstructions(spirv),
-              Eq(MakeInstruction(SpvOpCopyMemorySized, {1, 2, 3, 2, 8})));
+              Eq(MakeInstruction(spv::Op::OpCopyMemorySized, {1, 2, 3, 2, 8})));
   std::string disassembly =
       EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
   EXPECT_THAT(disassembly, Eq(spirv));
@@ -336,7 +339,7 @@
 TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessNontemporalGood) {
   std::string spirv = "OpCopyMemorySized %1 %2 %3 Nontemporal\n";
   EXPECT_THAT(CompiledInstructions(spirv),
-              Eq(MakeInstruction(SpvOpCopyMemorySized, {1, 2, 3, 4})));
+              Eq(MakeInstruction(spv::Op::OpCopyMemorySized, {1, 2, 3, 4})));
   std::string disassembly =
       EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
   EXPECT_THAT(disassembly, Eq(spirv));
@@ -345,7 +348,7 @@
 TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessAvGood) {
   std::string spirv = "OpCopyMemorySized %1 %2 %3 MakePointerAvailable %4\n";
   EXPECT_THAT(CompiledInstructions(spirv),
-              Eq(MakeInstruction(SpvOpCopyMemorySized, {1, 2, 3, 8, 4})));
+              Eq(MakeInstruction(spv::Op::OpCopyMemorySized, {1, 2, 3, 8, 4})));
   std::string disassembly =
       EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
   EXPECT_THAT(disassembly, Eq(spirv));
@@ -353,8 +356,9 @@
 
 TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessVisGood) {
   std::string spirv = "OpCopyMemorySized %1 %2 %3 MakePointerVisible %4\n";
-  EXPECT_THAT(CompiledInstructions(spirv),
-              Eq(MakeInstruction(SpvOpCopyMemorySized, {1, 2, 3, 16, 4})));
+  EXPECT_THAT(
+      CompiledInstructions(spirv),
+      Eq(MakeInstruction(spv::Op::OpCopyMemorySized, {1, 2, 3, 16, 4})));
   std::string disassembly =
       EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
   EXPECT_THAT(disassembly, Eq(spirv));
@@ -363,7 +367,7 @@
 TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessNonPrivateGood) {
   std::string spirv = "OpCopyMemorySized %1 %2 %3 NonPrivatePointer\n";
   EXPECT_THAT(CompiledInstructions(spirv),
-              Eq(MakeInstruction(SpvOpCopyMemorySized, {1, 2, 3, 32})));
+              Eq(MakeInstruction(spv::Op::OpCopyMemorySized, {1, 2, 3, 32})));
   std::string disassembly =
       EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
   EXPECT_THAT(disassembly, Eq(spirv));
@@ -376,7 +380,7 @@
       "MakePointerVisible|NonPrivatePointer 16 %4 %5\n";
   EXPECT_THAT(
       CompiledInstructions(spirv),
-      Eq(MakeInstruction(SpvOpCopyMemorySized, {1, 2, 3, 63, 16, 4, 5})));
+      Eq(MakeInstruction(spv::Op::OpCopyMemorySized, {1, 2, 3, 63, 16, 4, 5})));
   std::string disassembly =
       EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
   EXPECT_THAT(disassembly, Eq(spirv));
@@ -386,7 +390,7 @@
   std::string spirv = "OpCopyMemorySized %1 %2 %3 Volatile Volatile\n";
   // Note: This will assemble but should not validate for SPIR-V 1.3
   EXPECT_THAT(CompiledInstructions(spirv, SPV_ENV_UNIVERSAL_1_3),
-              Eq(MakeInstruction(SpvOpCopyMemorySized, {1, 2, 3, 1, 1})));
+              Eq(MakeInstruction(spv::Op::OpCopyMemorySized, {1, 2, 3, 1, 1})));
   std::string disassembly =
       EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
   EXPECT_THAT(disassembly, Eq(spirv));
@@ -395,7 +399,7 @@
 TEST_F(MemoryRoundTripTest, OpCopyMemorySizedTwoAccessV14Good) {
   std::string spirv = "OpCopyMemorySized %1 %2 %3 Volatile Volatile\n";
   EXPECT_THAT(CompiledInstructions(spirv, SPV_ENV_UNIVERSAL_1_4),
-              Eq(MakeInstruction(SpvOpCopyMemorySized, {1, 2, 3, 1, 1})));
+              Eq(MakeInstruction(spv::Op::OpCopyMemorySized, {1, 2, 3, 1, 1})));
   std::string disassembly =
       EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
   EXPECT_THAT(disassembly, Eq(spirv));
@@ -406,9 +410,9 @@
       "OpCopyMemorySized %1 %2 %3 Volatile|Nontemporal|"
       "MakePointerVisible %4 "
       "Aligned|MakePointerAvailable|NonPrivatePointer 16 %5\n";
-  EXPECT_THAT(
-      CompiledInstructions(spirv),
-      Eq(MakeInstruction(SpvOpCopyMemorySized, {1, 2, 3, 21, 4, 42, 16, 5})));
+  EXPECT_THAT(CompiledInstructions(spirv),
+              Eq(MakeInstruction(spv::Op::OpCopyMemorySized,
+                                 {1, 2, 3, 21, 4, 42, 16, 5})));
   std::string disassembly =
       EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
   EXPECT_THAT(disassembly, Eq(spirv));
diff --git a/test/text_to_binary.misc_test.cpp b/test/text_to_binary.misc_test.cpp
index 03b1e09..9ee425a 100644
--- a/test/text_to_binary.misc_test.cpp
+++ b/test/text_to_binary.misc_test.cpp
@@ -29,7 +29,8 @@
 using TextToBinaryMisc = spvtest::TextToBinaryTest;
 
 TEST_F(TextToBinaryMisc, OpNop) {
-  EXPECT_THAT(CompiledInstructions("OpNop"), Eq(MakeInstruction(SpvOpNop, {})));
+  EXPECT_THAT(CompiledInstructions("OpNop"),
+              Eq(MakeInstruction(spv::Op::OpNop, {})));
 }
 
 TEST_F(TextToBinaryMisc, OpUndef) {
@@ -37,7 +38,8 @@
                                                    %u = OpUndef %f32)");
   const uint32_t typeID = 1;
   EXPECT_THAT(code[1], Eq(typeID));
-  EXPECT_THAT(Subvector(code, 3), Eq(MakeInstruction(SpvOpUndef, {typeID, 2})));
+  EXPECT_THAT(Subvector(code, 3),
+              Eq(MakeInstruction(spv::Op::OpUndef, {typeID, 2})));
 }
 
 TEST_F(TextToBinaryMisc, OpWrong) {
diff --git a/test/text_to_binary.mode_setting_test.cpp b/test/text_to_binary.mode_setting_test.cpp
index 7f15c8b..c62ba3e 100644
--- a/test/text_to_binary.mode_setting_test.cpp
+++ b/test/text_to_binary.mode_setting_test.cpp
@@ -46,9 +46,9 @@
   uint32_t get_memory_value() const {
     return static_cast<uint32_t>(memory_value);
   }
-  SpvAddressingModel addressing_value;
+  spv::AddressingModel addressing_value;
   std::string addressing_name;
-  SpvMemoryModel memory_value;
+  spv::MemoryModel memory_value;
   std::string memory_name;
 };
 
@@ -58,16 +58,16 @@
 TEST_P(OpMemoryModelTest, AnyMemoryModelCase) {
   const std::string input = "OpMemoryModel " + GetParam().addressing_name +
                             " " + GetParam().memory_name;
-  EXPECT_THAT(
-      CompiledInstructions(input),
-      Eq(MakeInstruction(SpvOpMemoryModel, {GetParam().get_addressing_value(),
-                                            GetParam().get_memory_value()})));
+  EXPECT_THAT(CompiledInstructions(input),
+              Eq(MakeInstruction(spv::Op::OpMemoryModel,
+                                 {GetParam().get_addressing_value(),
+                                  GetParam().get_memory_value()})));
 }
 
-#define CASE(ADDRESSING, MEMORY)                                         \
-  {                                                                      \
-    SpvAddressingModel##ADDRESSING, #ADDRESSING, SpvMemoryModel##MEMORY, \
-        #MEMORY                                                          \
+#define CASE(ADDRESSING, MEMORY)                                             \
+  {                                                                          \
+    spv::AddressingModel::ADDRESSING, #ADDRESSING, spv::MemoryModel::MEMORY, \
+        #MEMORY                                                              \
   }
 // clang-format off
 INSTANTIATE_TEST_SUITE_P(TextToBinaryMemoryModel, OpMemoryModelTest,
@@ -97,7 +97,7 @@
   uint32_t get_execution_value() const {
     return static_cast<uint32_t>(execution_value);
   }
-  SpvExecutionModel execution_value;
+  spv::ExecutionModel execution_value;
   std::string execution_name;
   std::string entry_point_name;
 };
@@ -109,14 +109,14 @@
   // TODO(dneto): utf-8, escaping, quoting cases for entry point name.
   const std::string input = "OpEntryPoint " + GetParam().execution_name +
                             " %1 \"" + GetParam().entry_point_name + "\"";
-  EXPECT_THAT(
-      CompiledInstructions(input),
-      Eq(MakeInstruction(SpvOpEntryPoint, {GetParam().get_execution_value(), 1},
-                         MakeVector(GetParam().entry_point_name))));
+  EXPECT_THAT(CompiledInstructions(input),
+              Eq(MakeInstruction(spv::Op::OpEntryPoint,
+                                 {GetParam().get_execution_value(), 1},
+                                 MakeVector(GetParam().entry_point_name))));
 }
 
 // clang-format off
-#define CASE(NAME) SpvExecutionModel##NAME, #NAME
+#define CASE(NAME) spv::ExecutionModel::NAME, #NAME
 INSTANTIATE_TEST_SUITE_P(TextToBinaryEntryPoint, OpEntryPointTest,
                         ValuesIn(std::vector<EntryPointCase>{
                           { CASE(Vertex), "" },
@@ -137,7 +137,7 @@
 
 // Test OpExecutionMode
 using OpExecutionModeTest = spvtest::TextToBinaryTestBase<
-    TestWithParam<std::tuple<spv_target_env, EnumCase<SpvExecutionMode>>>>;
+    TestWithParam<std::tuple<spv_target_env, EnumCase<spv::ExecutionMode>>>>;
 
 TEST_P(OpExecutionModeTest, AnyExecutionMode) {
   // This string should assemble, but should not validate.
@@ -146,16 +146,16 @@
   for (auto operand : std::get<1>(GetParam()).operands())
     input << " " << operand;
   EXPECT_THAT(CompiledInstructions(input.str(), std::get<0>(GetParam())),
-              Eq(MakeInstruction(SpvOpExecutionMode,
+              Eq(MakeInstruction(spv::Op::OpExecutionMode,
                                  {1, std::get<1>(GetParam()).value()},
                                  std::get<1>(GetParam()).operands())));
 }
 
-#define CASE(NAME) SpvExecutionMode##NAME, #NAME
+#define CASE(NAME) spv::ExecutionMode::NAME, #NAME
 INSTANTIATE_TEST_SUITE_P(
     TextToBinaryExecutionMode, OpExecutionModeTest,
     Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
-            ValuesIn(std::vector<EnumCase<SpvExecutionMode>>{
+            ValuesIn(std::vector<EnumCase<spv::ExecutionMode>>{
                 // The operand literal values are arbitrarily chosen,
                 // but there are the right number of them.
                 {CASE(Invocations), {101}},
@@ -195,7 +195,7 @@
 INSTANTIATE_TEST_SUITE_P(
     TextToBinaryExecutionModeV11, OpExecutionModeTest,
     Combine(Values(SPV_ENV_UNIVERSAL_1_1),
-            ValuesIn(std::vector<EnumCase<SpvExecutionMode>>{
+            ValuesIn(std::vector<EnumCase<spv::ExecutionMode>>{
                 {CASE(Initializer)},
                 {CASE(Finalizer)},
                 {CASE(SubgroupSize), {12}},
@@ -216,18 +216,18 @@
 // Test OpCapability
 
 using OpCapabilityTest =
-    spvtest::TextToBinaryTestBase<TestWithParam<EnumCase<SpvCapability>>>;
+    spvtest::TextToBinaryTestBase<TestWithParam<EnumCase<spv::Capability>>>;
 
 TEST_P(OpCapabilityTest, AnyCapability) {
   const std::string input = "OpCapability " + GetParam().name();
   EXPECT_THAT(CompiledInstructions(input),
-              Eq(MakeInstruction(SpvOpCapability, {GetParam().value()})));
+              Eq(MakeInstruction(spv::Op::OpCapability, {GetParam().value()})));
 }
 
 // clang-format off
-#define CASE(NAME) { SpvCapability##NAME, #NAME }
+#define CASE(NAME) { spv::Capability::NAME, #NAME }
 INSTANTIATE_TEST_SUITE_P(TextToBinaryCapability, OpCapabilityTest,
-                        ValuesIn(std::vector<EnumCase<SpvCapability>>{
+                        ValuesIn(std::vector<EnumCase<spv::Capability>>{
                             CASE(Matrix),
                             CASE(Shader),
                             CASE(Geometry),
diff --git a/test/text_to_binary.pipe_storage_test.cpp b/test/text_to_binary.pipe_storage_test.cpp
index 955f5ef..ef899a2 100644
--- a/test/text_to_binary.pipe_storage_test.cpp
+++ b/test/text_to_binary.pipe_storage_test.cpp
@@ -28,7 +28,7 @@
 TEST_F(OpTypePipeStorageTest, OpcodeAssemblesInV10) {
   EXPECT_THAT(
       CompiledInstructions("%res = OpTypePipeStorage", SPV_ENV_UNIVERSAL_1_0),
-      Eq(MakeInstruction(SpvOpTypePipeStorage, {1})));
+      Eq(MakeInstruction(spv::Op::OpTypePipeStorage, {1})));
 }
 
 TEST_F(OpTypePipeStorageTest, ArgumentCount) {
@@ -38,18 +38,19 @@
          "'OpTypePipeStorage'."));
   EXPECT_THAT(
       CompiledInstructions("%res = OpTypePipeStorage", SPV_ENV_UNIVERSAL_1_1),
-      Eq(MakeInstruction(SpvOpTypePipeStorage, {1})));
+      Eq(MakeInstruction(spv::Op::OpTypePipeStorage, {1})));
   EXPECT_THAT(CompileFailure("%res = OpTypePipeStorage %1 %2 %3 %4 %5",
                              SPV_ENV_UNIVERSAL_1_1),
-              Eq("'=' expected after result id."));
+              Eq("'=' expected after result id but found '%2'."));
 }
 
 using OpConstantPipeStorageTest = spvtest::TextToBinaryTest;
 
 TEST_F(OpConstantPipeStorageTest, OpcodeAssemblesInV10) {
-  EXPECT_THAT(CompiledInstructions("%1 = OpConstantPipeStorage %2 3 4 5",
-                                   SPV_ENV_UNIVERSAL_1_0),
-              Eq(MakeInstruction(SpvOpConstantPipeStorage, {1, 2, 3, 4, 5})));
+  EXPECT_THAT(
+      CompiledInstructions("%1 = OpConstantPipeStorage %2 3 4 5",
+                           SPV_ENV_UNIVERSAL_1_0),
+      Eq(MakeInstruction(spv::Op::OpConstantPipeStorage, {1, 2, 3, 4, 5})));
 }
 
 TEST_F(OpConstantPipeStorageTest, ArgumentCount) {
@@ -65,12 +66,13 @@
                              SPV_ENV_UNIVERSAL_1_1),
               Eq("Expected operand for OpConstantPipeStorage instruction, but "
                  "found the end of the stream."));
-  EXPECT_THAT(CompiledInstructions("%1 = OpConstantPipeStorage %2 3 4 5",
-                                   SPV_ENV_UNIVERSAL_1_1),
-              Eq(MakeInstruction(SpvOpConstantPipeStorage, {1, 2, 3, 4, 5})));
+  EXPECT_THAT(
+      CompiledInstructions("%1 = OpConstantPipeStorage %2 3 4 5",
+                           SPV_ENV_UNIVERSAL_1_1),
+      Eq(MakeInstruction(spv::Op::OpConstantPipeStorage, {1, 2, 3, 4, 5})));
   EXPECT_THAT(CompileFailure("%1 = OpConstantPipeStorage %2 3 4 5 %6 %7",
                              SPV_ENV_UNIVERSAL_1_1),
-              Eq("'=' expected after result id."));
+              Eq("'=' expected after result id but found '%7'."));
 }
 
 TEST_F(OpConstantPipeStorageTest, ArgumentTypes) {
@@ -91,9 +93,10 @@
 using OpCreatePipeFromPipeStorageTest = spvtest::TextToBinaryTest;
 
 TEST_F(OpCreatePipeFromPipeStorageTest, OpcodeAssemblesInV10) {
-  EXPECT_THAT(CompiledInstructions("%1 = OpCreatePipeFromPipeStorage %2 %3",
-                                   SPV_ENV_UNIVERSAL_1_0),
-              Eq(MakeInstruction(SpvOpCreatePipeFromPipeStorage, {1, 2, 3})));
+  EXPECT_THAT(
+      CompiledInstructions("%1 = OpCreatePipeFromPipeStorage %2 %3",
+                           SPV_ENV_UNIVERSAL_1_0),
+      Eq(MakeInstruction(spv::Op::OpCreatePipeFromPipeStorage, {1, 2, 3})));
 }
 
 TEST_F(OpCreatePipeFromPipeStorageTest, ArgumentCount) {
@@ -109,12 +112,13 @@
                              SPV_ENV_UNIVERSAL_1_1),
               Eq("Expected operand for OpCreatePipeFromPipeStorage "
                  "instruction, but found the next instruction instead."));
-  EXPECT_THAT(CompiledInstructions("%1 = OpCreatePipeFromPipeStorage %2 %3",
-                                   SPV_ENV_UNIVERSAL_1_1),
-              Eq(MakeInstruction(SpvOpCreatePipeFromPipeStorage, {1, 2, 3})));
+  EXPECT_THAT(
+      CompiledInstructions("%1 = OpCreatePipeFromPipeStorage %2 %3",
+                           SPV_ENV_UNIVERSAL_1_1),
+      Eq(MakeInstruction(spv::Op::OpCreatePipeFromPipeStorage, {1, 2, 3})));
   EXPECT_THAT(CompileFailure("%1 = OpCreatePipeFromPipeStorage %2 %3 %4 %5",
                              SPV_ENV_UNIVERSAL_1_1),
-              Eq("'=' expected after result id."));
+              Eq("'=' expected after result id but found '%5'."));
 }
 
 TEST_F(OpCreatePipeFromPipeStorageTest, ArgumentTypes) {
diff --git a/test/text_to_binary.reserved_sampling_test.cpp b/test/text_to_binary.reserved_sampling_test.cpp
index 42e4e2a..abc1667 100644
--- a/test/text_to_binary.reserved_sampling_test.cpp
+++ b/test/text_to_binary.reserved_sampling_test.cpp
@@ -30,33 +30,36 @@
 
 TEST_F(ReservedSamplingInstTest, OpImageSparseSampleProjImplicitLod) {
   std::string input = "%2 = OpImageSparseSampleProjImplicitLod %1 %3 %4\n";
-  EXPECT_THAT(
-      CompiledInstructions(input, SPV_ENV_UNIVERSAL_1_0),
-      Eq(MakeInstruction(SpvOpImageSparseSampleProjImplicitLod, {1, 2, 3, 4})));
+  EXPECT_THAT(CompiledInstructions(input, SPV_ENV_UNIVERSAL_1_0),
+              Eq(MakeInstruction(spv::Op::OpImageSparseSampleProjImplicitLod,
+                                 {1, 2, 3, 4})));
 }
 
 TEST_F(ReservedSamplingInstTest, OpImageSparseSampleProjExplicitLod) {
   std::string input =
       "%2 = OpImageSparseSampleProjExplicitLod %1 %3 %4 Lod %5\n";
   EXPECT_THAT(CompiledInstructions(input, SPV_ENV_UNIVERSAL_1_0),
-              Eq(MakeInstruction(SpvOpImageSparseSampleProjExplicitLod,
-                                 {1, 2, 3, 4, SpvImageOperandsLodMask, 5})));
+              Eq(MakeInstruction(
+                  spv::Op::OpImageSparseSampleProjExplicitLod,
+                  {1, 2, 3, 4, (uint32_t)spv::ImageOperandsMask::Lod, 5})));
 }
 
 TEST_F(ReservedSamplingInstTest, OpImageSparseSampleProjDrefImplicitLod) {
   std::string input =
       "%2 = OpImageSparseSampleProjDrefImplicitLod %1 %3 %4 %5\n";
-  EXPECT_THAT(CompiledInstructions(input, SPV_ENV_UNIVERSAL_1_0),
-              Eq(MakeInstruction(SpvOpImageSparseSampleProjDrefImplicitLod,
-                                 {1, 2, 3, 4, 5})));
+  EXPECT_THAT(
+      CompiledInstructions(input, SPV_ENV_UNIVERSAL_1_0),
+      Eq(MakeInstruction(spv::Op::OpImageSparseSampleProjDrefImplicitLod,
+                         {1, 2, 3, 4, 5})));
 }
 
 TEST_F(ReservedSamplingInstTest, OpImageSparseSampleProjDrefExplicitLod) {
   std::string input =
       "%2 = OpImageSparseSampleProjDrefExplicitLod %1 %3 %4 %5 Lod %6\n";
   EXPECT_THAT(CompiledInstructions(input, SPV_ENV_UNIVERSAL_1_0),
-              Eq(MakeInstruction(SpvOpImageSparseSampleProjDrefExplicitLod,
-                                 {1, 2, 3, 4, 5, SpvImageOperandsLodMask, 6})));
+              Eq(MakeInstruction(
+                  spv::Op::OpImageSparseSampleProjDrefExplicitLod,
+                  {1, 2, 3, 4, 5, (uint32_t)spv::ImageOperandsMask::Lod, 6})));
 }
 
 }  // namespace
diff --git a/test/text_to_binary.subgroup_dispatch_test.cpp b/test/text_to_binary.subgroup_dispatch_test.cpp
index 8c40445..45d7780 100644
--- a/test/text_to_binary.subgroup_dispatch_test.cpp
+++ b/test/text_to_binary.subgroup_dispatch_test.cpp
@@ -35,7 +35,7 @@
       CompiledInstructions("%res = OpGetKernelLocalSizeForSubgroupCount %type "
                            "%sgcount %invoke %param %param_size %param_align",
                            SPV_ENV_UNIVERSAL_1_0),
-      Eq(MakeInstruction(SpvOpGetKernelLocalSizeForSubgroupCount,
+      Eq(MakeInstruction(spv::Op::OpGetKernelLocalSizeForSubgroupCount,
                          {1, 2, 3, 4, 5, 6, 7})));
 }
 
@@ -57,7 +57,7 @@
       CompiledInstructions("%res = OpGetKernelLocalSizeForSubgroupCount %type "
                            "%sgcount %invoke %param %param_size %param_align",
                            SPV_ENV_UNIVERSAL_1_1),
-      Eq(MakeInstruction(SpvOpGetKernelLocalSizeForSubgroupCount,
+      Eq(MakeInstruction(spv::Op::OpGetKernelLocalSizeForSubgroupCount,
                          {1, 2, 3, 4, 5, 6, 7})));
   EXPECT_THAT(
       CompileFailure("%res = OpGetKernelLocalSizeForSubgroupCount %type "
@@ -81,11 +81,11 @@
 using OpGetKernelMaxNumSubgroupsTest = spvtest::TextToBinaryTest;
 
 TEST_F(OpGetKernelMaxNumSubgroupsTest, OpcodeAssemblesInV10) {
-  EXPECT_THAT(
-      CompiledInstructions("%res = OpGetKernelMaxNumSubgroups %type "
-                           "%invoke %param %param_size %param_align",
-                           SPV_ENV_UNIVERSAL_1_0),
-      Eq(MakeInstruction(SpvOpGetKernelMaxNumSubgroups, {1, 2, 3, 4, 5, 6})));
+  EXPECT_THAT(CompiledInstructions("%res = OpGetKernelMaxNumSubgroups %type "
+                                   "%invoke %param %param_size %param_align",
+                                   SPV_ENV_UNIVERSAL_1_0),
+              Eq(MakeInstruction(spv::Op::OpGetKernelMaxNumSubgroups,
+                                 {1, 2, 3, 4, 5, 6})));
 }
 
 TEST_F(OpGetKernelMaxNumSubgroupsTest, ArgumentCount) {
@@ -101,11 +101,11 @@
                              SPV_ENV_UNIVERSAL_1_1),
               Eq("Expected operand for OpGetKernelMaxNumSubgroups instruction, "
                  "but found the end of the stream."));
-  EXPECT_THAT(
-      CompiledInstructions("%res = OpGetKernelMaxNumSubgroups %type "
-                           "%invoke %param %param_size %param_align",
-                           SPV_ENV_UNIVERSAL_1_1),
-      Eq(MakeInstruction(SpvOpGetKernelMaxNumSubgroups, {1, 2, 3, 4, 5, 6})));
+  EXPECT_THAT(CompiledInstructions("%res = OpGetKernelMaxNumSubgroups %type "
+                                   "%invoke %param %param_size %param_align",
+                                   SPV_ENV_UNIVERSAL_1_1),
+              Eq(MakeInstruction(spv::Op::OpGetKernelMaxNumSubgroups,
+                                 {1, 2, 3, 4, 5, 6})));
   EXPECT_THAT(CompileFailure("%res = OpGetKernelMaxNumSubgroups %type %invoke "
                              "%param %param_size %param_align %extra",
                              SPV_ENV_UNIVERSAL_1_1),
diff --git a/test/text_to_binary.type_declaration_test.cpp b/test/text_to_binary.type_declaration_test.cpp
index 65a2355..770f298 100644
--- a/test/text_to_binary.type_declaration_test.cpp
+++ b/test/text_to_binary.type_declaration_test.cpp
@@ -32,32 +32,34 @@
 // Test Dim enums via OpTypeImage
 
 using DimTest =
-    spvtest::TextToBinaryTestBase<::testing::TestWithParam<EnumCase<SpvDim>>>;
+    spvtest::TextToBinaryTestBase<::testing::TestWithParam<EnumCase<spv::Dim>>>;
 
 TEST_P(DimTest, AnyDim) {
   const std::string input =
       "%1 = OpTypeImage %2 " + GetParam().name() + " 2 3 0 4 Rgba8\n";
-  EXPECT_THAT(
-      CompiledInstructions(input),
-      Eq(MakeInstruction(SpvOpTypeImage, {1, 2, GetParam().value(), 2, 3, 0, 4,
-                                          SpvImageFormatRgba8})));
+  EXPECT_THAT(CompiledInstructions(input),
+              Eq(MakeInstruction(spv::Op::OpTypeImage,
+                                 {1, 2, (uint32_t)GetParam().value(), 2, 3, 0,
+                                  4, (uint32_t)spv::ImageFormat::Rgba8})));
 
   // Check the disassembler as well.
   EXPECT_THAT(EncodeAndDecodeSuccessfully(input), Eq(input));
 }
 
 // clang-format off
-#define CASE(NAME) {SpvDim##NAME, #NAME}
+#define CASE(NAME) {spv::Dim::NAME, #NAME}
+#define CASE1(DIM, NAME) {spv::Dim::DIM, #NAME}
 INSTANTIATE_TEST_SUITE_P(
     TextToBinaryDim, DimTest,
-    ::testing::ValuesIn(std::vector<EnumCase<SpvDim>>{
-        CASE(1D),
-        CASE(2D),
-        CASE(3D),
+    ::testing::ValuesIn(std::vector<EnumCase<spv::Dim>>{
+        CASE1(Dim1D, 1D),
+        CASE1(Dim2D, 2D),
+        CASE1(Dim3D, 3D),
         CASE(Cube),
         CASE(Rect),
         CASE(Buffer),
         CASE(SubpassData),
+        CASE(TileImageDataEXT),
     }));
 #undef CASE
 // clang-format on
@@ -70,23 +72,24 @@
 // Test ImageFormat enums via OpTypeImage
 
 using ImageFormatTest = spvtest::TextToBinaryTestBase<
-    ::testing::TestWithParam<EnumCase<SpvImageFormat>>>;
+    ::testing::TestWithParam<EnumCase<spv::ImageFormat>>>;
 
 TEST_P(ImageFormatTest, AnyImageFormatAndNoAccessQualifier) {
   const std::string input =
       "%1 = OpTypeImage %2 1D 2 3 0 4 " + GetParam().name() + "\n";
   EXPECT_THAT(CompiledInstructions(input),
-              Eq(MakeInstruction(SpvOpTypeImage, {1, 2, SpvDim1D, 2, 3, 0, 4,
-                                                  GetParam().value()})));
+              Eq(MakeInstruction(spv::Op::OpTypeImage,
+                                 {1, 2, (uint32_t)spv::Dim::Dim1D, 2, 3, 0, 4,
+                                  GetParam().value()})));
   // Check the disassembler as well.
   EXPECT_THAT(EncodeAndDecodeSuccessfully(input), Eq(input));
 }
 
 // clang-format off
-#define CASE(NAME) {SpvImageFormat##NAME, #NAME}
+#define CASE(NAME) {spv::ImageFormat::NAME, #NAME}
 INSTANTIATE_TEST_SUITE_P(
     TextToBinaryImageFormat, ImageFormatTest,
-    ::testing::ValuesIn(std::vector<EnumCase<SpvImageFormat>>{
+    ::testing::ValuesIn(std::vector<EnumCase<spv::ImageFormat>>{
         CASE(Unknown),
         CASE(Rgba32f),
         CASE(Rgba16f),
@@ -138,24 +141,25 @@
 
 // Test AccessQualifier enums via OpTypeImage.
 using ImageAccessQualifierTest = spvtest::TextToBinaryTestBase<
-    ::testing::TestWithParam<EnumCase<SpvAccessQualifier>>>;
+    ::testing::TestWithParam<EnumCase<spv::AccessQualifier>>>;
 
 TEST_P(ImageAccessQualifierTest, AnyAccessQualifier) {
   const std::string input =
       "%1 = OpTypeImage %2 1D 2 3 0 4 Rgba8 " + GetParam().name() + "\n";
   EXPECT_THAT(CompiledInstructions(input),
-              Eq(MakeInstruction(SpvOpTypeImage,
-                                 {1, 2, SpvDim1D, 2, 3, 0, 4,
-                                  SpvImageFormatRgba8, GetParam().value()})));
+              Eq(MakeInstruction(
+                  spv::Op::OpTypeImage,
+                  {1, 2, (uint32_t)spv::Dim::Dim1D, 2, 3, 0, 4,
+                   (uint32_t)spv::ImageFormat::Rgba8, GetParam().value()})));
   // Check the disassembler as well.
   EXPECT_THAT(EncodeAndDecodeSuccessfully(input), Eq(input));
 }
 
 // clang-format off
-#define CASE(NAME) {SpvAccessQualifier##NAME, #NAME}
+#define CASE(NAME) {spv::AccessQualifier::NAME, #NAME}
 INSTANTIATE_TEST_SUITE_P(
     AccessQualifier, ImageAccessQualifierTest,
-    ::testing::ValuesIn(std::vector<EnumCase<SpvAccessQualifier>>{
+    ::testing::ValuesIn(std::vector<EnumCase<spv::AccessQualifier>>{
       CASE(ReadOnly),
       CASE(WriteOnly),
       CASE(ReadWrite),
@@ -166,21 +170,22 @@
 // Test AccessQualifier enums via OpTypePipe.
 
 using OpTypePipeTest = spvtest::TextToBinaryTestBase<
-    ::testing::TestWithParam<EnumCase<SpvAccessQualifier>>>;
+    ::testing::TestWithParam<EnumCase<spv::AccessQualifier>>>;
 
 TEST_P(OpTypePipeTest, AnyAccessQualifier) {
   const std::string input = "%1 = OpTypePipe " + GetParam().name() + "\n";
-  EXPECT_THAT(CompiledInstructions(input),
-              Eq(MakeInstruction(SpvOpTypePipe, {1, GetParam().value()})));
+  EXPECT_THAT(
+      CompiledInstructions(input),
+      Eq(MakeInstruction(spv::Op::OpTypePipe, {1, GetParam().value()})));
   // Check the disassembler as well.
   EXPECT_THAT(EncodeAndDecodeSuccessfully(input), Eq(input));
 }
 
 // clang-format off
-#define CASE(NAME) {SpvAccessQualifier##NAME, #NAME}
+#define CASE(NAME) {spv::AccessQualifier::NAME, #NAME}
 INSTANTIATE_TEST_SUITE_P(
     TextToBinaryTypePipe, OpTypePipeTest,
-    ::testing::ValuesIn(std::vector<EnumCase<SpvAccessQualifier>>{
+    ::testing::ValuesIn(std::vector<EnumCase<spv::AccessQualifier>>{
                             CASE(ReadOnly),
                             CASE(WriteOnly),
                             CASE(ReadWrite),
@@ -195,12 +200,12 @@
 
 using OpTypeForwardPointerTest = spvtest::TextToBinaryTest;
 
-#define CASE(storage_class)                                               \
-  do {                                                                    \
-    EXPECT_THAT(                                                          \
-        CompiledInstructions("OpTypeForwardPointer %pt " #storage_class), \
-        Eq(MakeInstruction(SpvOpTypeForwardPointer,                       \
-                           {1, SpvStorageClass##storage_class})));        \
+#define CASE(storage_class)                                                    \
+  do {                                                                         \
+    EXPECT_THAT(                                                               \
+        CompiledInstructions("OpTypeForwardPointer %pt " #storage_class),      \
+        Eq(MakeInstruction(spv::Op::OpTypeForwardPointer,                      \
+                           {1, (uint32_t)spv::StorageClass::storage_class}))); \
   } while (0)
 
 TEST_F(OpTypeForwardPointerTest, ValidStorageClass) {
@@ -217,6 +222,7 @@
   CASE(AtomicCounter);
   CASE(Image);
   CASE(StorageBuffer);
+  CASE(TileImageEXT);
 }
 
 #undef CASE
@@ -245,7 +251,7 @@
 TEST_F(OpSizeOfTest, OpcodeAssemblesInV10) {
   EXPECT_THAT(
       CompiledInstructions("%1 = OpSizeOf %2 %3", SPV_ENV_UNIVERSAL_1_0),
-      Eq(MakeInstruction(SpvOpSizeOf, {1, 2, 3})));
+      Eq(MakeInstruction(spv::Op::OpSizeOf, {1, 2, 3})));
 }
 
 TEST_F(OpSizeOfTest, ArgumentCount) {
@@ -258,7 +264,7 @@
                  "next instruction instead."));
   EXPECT_THAT(
       CompiledInstructions("%1 = OpSizeOf %2 %3", SPV_ENV_UNIVERSAL_1_1),
-      Eq(MakeInstruction(SpvOpSizeOf, {1, 2, 3})));
+      Eq(MakeInstruction(spv::Op::OpSizeOf, {1, 2, 3})));
   EXPECT_THAT(
       CompileFailure("%1 = OpSizeOf %2 %3 44 55 ", SPV_ENV_UNIVERSAL_1_1),
       Eq("Expected <opcode> or <result-id> at the beginning of an instruction, "
diff --git a/test/text_to_binary_test.cpp b/test/text_to_binary_test.cpp
index 0b348e8..a958097 100644
--- a/test/text_to_binary_test.cpp
+++ b/test/text_to_binary_test.cpp
@@ -180,9 +180,10 @@
 TEST_F(TextToBinaryTest, CRLF) {
   const std::string input =
       "%i32 = OpTypeInt 32 1\r\n%c = OpConstant %i32 123\r\n";
-  EXPECT_THAT(CompiledInstructions(input),
-              Eq(Concatenate({MakeInstruction(SpvOpTypeInt, {1, 32, 1}),
-                              MakeInstruction(SpvOpConstant, {1, 2, 123})})));
+  EXPECT_THAT(
+      CompiledInstructions(input),
+      Eq(Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 32, 1}),
+                      MakeInstruction(spv::Op::OpConstant, {1, 2, 123})})));
 }
 
 using TextToBinaryFloatValueTest = spvtest::TextToBinaryTestBase<
@@ -192,8 +193,8 @@
   const std::string input =
       "%1 = OpTypeFloat 32\n%2 = OpConstant %1 " + GetParam().first;
   EXPECT_THAT(CompiledInstructions(input),
-              Eq(Concatenate({MakeInstruction(SpvOpTypeFloat, {1, 32}),
-                              MakeInstruction(SpvOpConstant,
+              Eq(Concatenate({MakeInstruction(spv::Op::OpTypeFloat, {1, 32}),
+                              MakeInstruction(spv::Op::OpConstant,
                                               {1, 2, GetParam().second})})));
 }
 
@@ -222,8 +223,8 @@
   const std::string input =
       "%1 = OpTypeFloat 16\n%2 = OpConstant %1 " + GetParam().first;
   EXPECT_THAT(CompiledInstructions(input),
-              Eq(Concatenate({MakeInstruction(SpvOpTypeFloat, {1, 16}),
-                              MakeInstruction(SpvOpConstant,
+              Eq(Concatenate({MakeInstruction(spv::Op::OpTypeFloat, {1, 16}),
+                              MakeInstruction(spv::Op::OpConstant,
                                               {1, 2, GetParam().second})})));
 }
 
diff --git a/test/tools/CMakeLists.txt b/test/tools/CMakeLists.txt
index 99f9780..37fe2b9 100644
--- a/test/tools/CMakeLists.txt
+++ b/test/tools/CMakeLists.txt
@@ -13,9 +13,19 @@
 # limitations under the License.
 
 add_test(NAME spirv-tools_expect_unittests
-         COMMAND ${PYTHON_EXECUTABLE} -m unittest expect_unittest.py
+         COMMAND Python3::Interpreter -m unittest expect_unittest.py
          WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
 add_test(NAME spirv-tools_spirv_test_framework_unittests
-         COMMAND ${PYTHON_EXECUTABLE} -m unittest spirv_test_framework_unittest.py
+         COMMAND Python3::Interpreter -m unittest spirv_test_framework_unittest.py
          WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
+
+add_spvtools_unittest(
+  TARGET spirv_unit_test_tools_util
+  SRCS flags_test.cpp ${spirv-tools_SOURCE_DIR}/tools/util/flags.cpp
+  LIBS ${SPIRV_TOOLS_FULL_VISIBILITY}
+  DEFINES TESTING=1)
+
 add_subdirectory(opt)
+if(NOT (${CMAKE_SYSTEM_NAME} STREQUAL "Android"))
+  add_subdirectory(objdump)
+endif ()
diff --git a/test/tools/flags_test.cpp b/test/tools/flags_test.cpp
new file mode 100644
index 0000000..43db996
--- /dev/null
+++ b/test/tools/flags_test.cpp
@@ -0,0 +1,415 @@
+// 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.
+
+#include "tools/util/flags.h"
+
+#include "gmock/gmock.h"
+
+#ifdef UTIL_FLAGS_FLAG
+#undef UTIL_FLAGS_FLAG
+#define UTIL_FLAGS_FLAG(Type, Prefix, Name, Default, Required, IsShort)     \
+  flags::Flag<Type> Name(Default);                                          \
+  flags::FlagRegistration Name##_registration(Name, Prefix #Name, Required, \
+                                              IsShort)
+#else
+#error \
+    "UTIL_FLAGS_FLAG is not defined. Either flags.h is not included of the flag name changed."
+#endif
+
+class FlagTest : public ::testing::Test {
+ protected:
+  void SetUp() override { flags::FlagList::reset(); }
+};
+
+TEST_F(FlagTest, NoFlags) {
+  const char* argv[] = {"binary", nullptr};
+  EXPECT_TRUE(flags::Parse(argv));
+  EXPECT_EQ(flags::positional_arguments.size(), 0);
+}
+
+TEST_F(FlagTest, DashIsPositional) {
+  const char* argv[] = {"binary", "-", nullptr};
+
+  EXPECT_TRUE(flags::Parse(argv));
+
+  EXPECT_EQ(flags::positional_arguments.size(), 1);
+  EXPECT_EQ(flags::positional_arguments[0], "-");
+}
+
+TEST_F(FlagTest, Positional) {
+  const char* argv[] = {"binary", "A", "BCD", nullptr};
+
+  EXPECT_TRUE(flags::Parse(argv));
+
+  EXPECT_EQ(flags::positional_arguments.size(), 2);
+  EXPECT_EQ(flags::positional_arguments[0], "A");
+  EXPECT_EQ(flags::positional_arguments[1], "BCD");
+}
+
+TEST_F(FlagTest, MissingRequired) {
+  FLAG_SHORT_bool(g, false, true);
+
+  const char* argv[] = {"binary", nullptr};
+  EXPECT_FALSE(flags::Parse(argv));
+  EXPECT_EQ(flags::positional_arguments.size(), 0);
+}
+
+TEST_F(FlagTest, BooleanShortValue) {
+  FLAG_SHORT_bool(g, false, false);
+  const char* argv[] = {"binary", "-g", nullptr};
+  EXPECT_FALSE(g.value());
+
+  EXPECT_TRUE(flags::Parse(argv));
+
+  EXPECT_TRUE(g.value());
+  EXPECT_EQ(flags::positional_arguments.size(), 0);
+}
+
+TEST_F(FlagTest, BooleanShortDefaultValue) {
+  FLAG_SHORT_bool(g, true, false);
+  const char* argv[] = {"binary", nullptr};
+  EXPECT_TRUE(g.value());
+
+  EXPECT_TRUE(flags::Parse(argv));
+
+  EXPECT_TRUE(g.value());
+}
+
+TEST_F(FlagTest, BooleanLongValueNotParsed) {
+  FLAG_SHORT_bool(g, false, false);
+  const char* argv[] = {"binary", "-g", "false", nullptr};
+  EXPECT_FALSE(g.value());
+
+  EXPECT_TRUE(flags::Parse(argv));
+
+  EXPECT_TRUE(g.value());
+  EXPECT_EQ(flags::positional_arguments.size(), 1);
+  EXPECT_EQ(flags::positional_arguments[0], "false");
+}
+
+TEST_F(FlagTest, BooleanLongSplitNotParsed) {
+  FLAG_LONG_bool(foo, false, false);
+  const char* argv[] = {"binary", "--foo", "true", nullptr};
+  EXPECT_FALSE(foo.value());
+
+  EXPECT_TRUE(flags::Parse(argv));
+
+  EXPECT_TRUE(foo.value());
+  EXPECT_EQ(flags::positional_arguments.size(), 1);
+  EXPECT_EQ(flags::positional_arguments[0], "true");
+}
+
+TEST_F(FlagTest, BooleanLongExplicitTrue) {
+  FLAG_LONG_bool(foo, false, false);
+  const char* argv[] = {"binary", "--foo=true", nullptr};
+  EXPECT_FALSE(foo.value());
+
+  EXPECT_TRUE(flags::Parse(argv));
+
+  EXPECT_TRUE(foo.value());
+  EXPECT_EQ(flags::positional_arguments.size(), 0);
+}
+
+TEST_F(FlagTest, BooleanLongExplicitFalse) {
+  FLAG_LONG_bool(foo, false, false);
+  const char* argv[] = {"binary", "--foo=false", nullptr};
+  EXPECT_FALSE(foo.value());
+
+  EXPECT_TRUE(flags::Parse(argv));
+
+  EXPECT_FALSE(foo.value());
+  EXPECT_EQ(flags::positional_arguments.size(), 0);
+}
+
+TEST_F(FlagTest, BooleanLongDefaultValue) {
+  FLAG_LONG_bool(foo, true, false);
+  const char* argv[] = {"binary", nullptr};
+  EXPECT_TRUE(foo.value());
+
+  EXPECT_TRUE(flags::Parse(argv));
+
+  EXPECT_TRUE(foo.value());
+  EXPECT_EQ(flags::positional_arguments.size(), 0);
+}
+
+TEST_F(FlagTest, BooleanLongDefaultValueCancelled) {
+  FLAG_LONG_bool(foo, true, false);
+  const char* argv[] = {"binary", "--foo=false", nullptr};
+  EXPECT_TRUE(foo.value());
+
+  EXPECT_TRUE(flags::Parse(argv));
+
+  EXPECT_FALSE(foo.value());
+  EXPECT_EQ(flags::positional_arguments.size(), 0);
+}
+
+TEST_F(FlagTest, StringFlagDefaultValue) {
+  FLAG_SHORT_string(f, "default", false);
+  const char* argv[] = {"binary", nullptr};
+  EXPECT_EQ(f.value(), "default");
+
+  EXPECT_TRUE(flags::Parse(argv));
+  EXPECT_EQ(f.value(), "default");
+  EXPECT_EQ(flags::positional_arguments.size(), 0);
+}
+
+TEST_F(FlagTest, StringFlagShortMissingString) {
+  FLAG_SHORT_string(f, "default", false);
+  const char* argv[] = {"binary", "-f", nullptr};
+  EXPECT_EQ(f.value(), "default");
+
+  EXPECT_FALSE(flags::Parse(argv));
+}
+
+TEST_F(FlagTest, StringFlagDefault) {
+  FLAG_SHORT_string(f, "default", false);
+  const char* argv[] = {"binary", nullptr};
+  EXPECT_EQ(f.value(), "default");
+
+  EXPECT_TRUE(flags::Parse(argv));
+
+  EXPECT_EQ(f.value(), "default");
+  EXPECT_EQ(flags::positional_arguments.size(), 0);
+}
+
+TEST_F(FlagTest, StringFlagSet) {
+  FLAG_SHORT_string(f, "default", false);
+  const char* argv[] = {"binary", "-f", "toto", nullptr};
+  EXPECT_EQ(f.value(), "default");
+
+  EXPECT_TRUE(flags::Parse(argv));
+
+  EXPECT_EQ(f.value(), "toto");
+  EXPECT_EQ(flags::positional_arguments.size(), 0);
+}
+
+TEST_F(FlagTest, StringLongFlagSetSplit) {
+  FLAG_LONG_string(foo, "default", false);
+  const char* argv[] = {"binary", "--foo", "toto", nullptr};
+  EXPECT_EQ(foo.value(), "default");
+
+  EXPECT_TRUE(flags::Parse(argv));
+
+  EXPECT_EQ(foo.value(), "toto");
+  EXPECT_EQ(flags::positional_arguments.size(), 0);
+}
+
+TEST_F(FlagTest, StringLongFlagSetUnified) {
+  FLAG_LONG_string(foo, "default", false);
+  const char* argv[] = {"binary", "--foo=toto", nullptr};
+  EXPECT_EQ(foo.value(), "default");
+
+  EXPECT_TRUE(flags::Parse(argv));
+
+  EXPECT_EQ(foo.value(), "toto");
+  EXPECT_EQ(flags::positional_arguments.size(), 0);
+}
+
+TEST_F(FlagTest, StringLongFlagSetEmpty) {
+  FLAG_LONG_string(foo, "default", false);
+  const char* argv[] = {"binary", "--foo=", nullptr};
+  EXPECT_EQ(foo.value(), "default");
+
+  EXPECT_TRUE(flags::Parse(argv));
+
+  EXPECT_EQ(foo.value(), "");
+  EXPECT_EQ(flags::positional_arguments.size(), 0);
+}
+
+TEST_F(FlagTest, AllPositionalAfterDoubleDash) {
+  FLAG_LONG_string(foo, "default", false);
+  const char* argv[] = {"binary", "--", "--foo=toto", nullptr};
+  EXPECT_EQ(foo.value(), "default");
+
+  EXPECT_TRUE(flags::Parse(argv));
+
+  EXPECT_EQ(foo.value(), "default");
+  EXPECT_EQ(flags::positional_arguments.size(), 1);
+  EXPECT_EQ(flags::positional_arguments[0], "--foo=toto");
+}
+
+TEST_F(FlagTest, NothingAfterDoubleDash) {
+  FLAG_LONG_string(foo, "default", false);
+  const char* argv[] = {"binary", "--", nullptr};
+  EXPECT_EQ(foo.value(), "default");
+
+  EXPECT_TRUE(flags::Parse(argv));
+
+  EXPECT_EQ(foo.value(), "default");
+  EXPECT_EQ(flags::positional_arguments.size(), 0);
+}
+
+TEST_F(FlagTest, FlagDoubleSetNotAllowed) {
+  FLAG_LONG_string(foo, "default", false);
+  const char* argv[] = {"binary", "--foo=abc", "--foo=def", nullptr};
+  EXPECT_EQ(foo.value(), "default");
+
+  EXPECT_FALSE(flags::Parse(argv));
+}
+
+TEST_F(FlagTest, MultipleFlags) {
+  FLAG_LONG_string(foo, "default foo", false);
+  FLAG_LONG_string(bar, "default_bar", false);
+  const char* argv[] = {"binary", "--foo", "abc", "--bar=def", nullptr};
+  EXPECT_EQ(foo.value(), "default foo");
+  EXPECT_EQ(bar.value(), "default_bar");
+
+  EXPECT_TRUE(flags::Parse(argv));
+  EXPECT_EQ(foo.value(), "abc");
+  EXPECT_EQ(bar.value(), "def");
+}
+
+TEST_F(FlagTest, MixedStringAndBool) {
+  FLAG_LONG_string(foo, "default foo", false);
+  FLAG_LONG_string(bar, "default_bar", false);
+  FLAG_SHORT_bool(g, false, false);
+  const char* argv[] = {"binary", "--foo", "abc", "-g", "--bar=def", nullptr};
+  EXPECT_EQ(foo.value(), "default foo");
+  EXPECT_EQ(bar.value(), "default_bar");
+  EXPECT_FALSE(g.value());
+
+  EXPECT_TRUE(flags::Parse(argv));
+  EXPECT_EQ(foo.value(), "abc");
+  EXPECT_EQ(bar.value(), "def");
+  EXPECT_TRUE(g.value());
+}
+
+TEST_F(FlagTest, UintFlagDefaultValue) {
+  FLAG_SHORT_uint(f, 18, false);
+  const char* argv[] = {"binary", nullptr};
+  EXPECT_EQ(f.value(), 18);
+
+  EXPECT_TRUE(flags::Parse(argv));
+  EXPECT_EQ(f.value(), 18);
+  EXPECT_EQ(flags::positional_arguments.size(), 0);
+}
+
+TEST_F(FlagTest, UintFlagShortMissingValue) {
+  FLAG_SHORT_uint(f, 19, false);
+  const char* argv[] = {"binary", "-f", nullptr};
+  EXPECT_EQ(f.value(), 19);
+
+  EXPECT_FALSE(flags::Parse(argv));
+}
+
+TEST_F(FlagTest, UintFlagSet) {
+  FLAG_SHORT_uint(f, 20, false);
+  const char* argv[] = {"binary", "-f", "21", nullptr};
+  EXPECT_EQ(f.value(), 20);
+
+  EXPECT_TRUE(flags::Parse(argv));
+
+  EXPECT_EQ(f.value(), 21);
+  EXPECT_EQ(flags::positional_arguments.size(), 0);
+}
+
+TEST_F(FlagTest, UintLongFlagSetSplit) {
+  FLAG_LONG_uint(foo, 22, false);
+  const char* argv[] = {"binary", "--foo", "23", nullptr};
+  EXPECT_EQ(foo.value(), 22);
+
+  EXPECT_TRUE(flags::Parse(argv));
+
+  EXPECT_EQ(foo.value(), 23);
+  EXPECT_EQ(flags::positional_arguments.size(), 0);
+}
+
+TEST_F(FlagTest, UintLongFlagSetUnified) {
+  FLAG_LONG_uint(foo, 24, false);
+  const char* argv[] = {"binary", "--foo=25", nullptr};
+  EXPECT_EQ(foo.value(), 24);
+
+  EXPECT_TRUE(flags::Parse(argv));
+
+  EXPECT_EQ(foo.value(), 25);
+  EXPECT_EQ(flags::positional_arguments.size(), 0);
+}
+
+TEST_F(FlagTest, UintLongFlagSetEmptyIsWrong) {
+  FLAG_LONG_uint(foo, 26, false);
+  const char* argv[] = {"binary", "--foo=", nullptr};
+  EXPECT_EQ(foo.value(), 26);
+
+  EXPECT_FALSE(flags::Parse(argv));
+}
+
+TEST_F(FlagTest, UintLongFlagSetNegativeFails) {
+  FLAG_LONG_uint(foo, 26, false);
+  const char* argv[] = {"binary", "--foo=-2", nullptr};
+  EXPECT_EQ(foo.value(), 26);
+
+  EXPECT_FALSE(flags::Parse(argv));
+}
+
+TEST_F(FlagTest, UintLongFlagSetOverflowFails) {
+  FLAG_LONG_uint(foo, 27, false);
+  const char* argv[] = {
+      "binary", "--foo=99999999999999999999999999999999999999999999999999999",
+      nullptr};
+  EXPECT_EQ(foo.value(), 27);
+
+  EXPECT_FALSE(flags::Parse(argv));
+}
+
+TEST_F(FlagTest, UintLongFlagSetInvalidCharTrailing) {
+  FLAG_LONG_uint(foo, 28, false);
+  const char* argv[] = {"binary", "--foo=12A", nullptr};
+  EXPECT_EQ(foo.value(), 28);
+
+  EXPECT_FALSE(flags::Parse(argv));
+}
+
+TEST_F(FlagTest, UintLongFlagSetSpaces) {
+  FLAG_LONG_uint(foo, 29, false);
+  const char* argv[] = {"binary", "--foo= 12", nullptr};
+  EXPECT_EQ(foo.value(), 29);
+
+  EXPECT_TRUE(flags::Parse(argv));
+  EXPECT_EQ(foo.value(), 12);
+  EXPECT_EQ(flags::positional_arguments.size(), 0);
+}
+
+TEST_F(FlagTest, UintLongFlagSpacesOnly) {
+  FLAG_LONG_uint(foo, 30, false);
+  const char* argv[] = {"binary", "--foo=  ", nullptr};
+  EXPECT_EQ(foo.value(), 30);
+
+  EXPECT_FALSE(flags::Parse(argv));
+}
+
+TEST_F(FlagTest, UintLongFlagSplitNumber) {
+  FLAG_LONG_uint(foo, 31, false);
+  const char* argv[] = {"binary", "--foo= 2 2", nullptr};
+  EXPECT_EQ(foo.value(), 31);
+
+  EXPECT_FALSE(flags::Parse(argv));
+}
+
+TEST_F(FlagTest, UintLongFlagHex) {
+  FLAG_LONG_uint(foo, 32, false);
+  const char* argv[] = {"binary", "--foo=0xA", nullptr};
+  EXPECT_EQ(foo.value(), 32);
+
+  EXPECT_FALSE(flags::Parse(argv));
+}
+
+TEST_F(FlagTest, UintLongFlagZeros) {
+  FLAG_LONG_uint(foo, 33, false);
+  const char* argv[] = {"binary", "--foo=0000", nullptr};
+  EXPECT_EQ(foo.value(), 33);
+
+  EXPECT_TRUE(flags::Parse(argv));
+  EXPECT_EQ(foo.value(), 0);
+  EXPECT_EQ(flags::positional_arguments.size(), 0);
+}
diff --git a/test/tools/objdump/CMakeLists.txt b/test/tools/objdump/CMakeLists.txt
new file mode 100644
index 0000000..46fae21
--- /dev/null
+++ b/test/tools/objdump/CMakeLists.txt
@@ -0,0 +1,23 @@
+# 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.
+
+add_spvtools_unittest(
+  TARGET spirv_unit_test_tools_objdump
+  SRCS
+    extract_source_test.cpp
+    ${spirv-tools_SOURCE_DIR}/tools/util/flags.cpp
+    ${spirv-tools_SOURCE_DIR}/tools/util/cli_consumer.cpp
+    ${spirv-tools_SOURCE_DIR}/tools/objdump/extract_source.cpp
+    LIBS ${SPIRV_TOOLS_FULL_VISIBILITY} SPIRV-Tools-opt
+  DEFINES TESTING=1)
diff --git a/test/tools/objdump/extract_source_test.cpp b/test/tools/objdump/extract_source_test.cpp
new file mode 100644
index 0000000..0b81caa
--- /dev/null
+++ b/test/tools/objdump/extract_source_test.cpp
@@ -0,0 +1,265 @@
+// 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.
+
+#include "tools/objdump/extract_source.h"
+
+#include <gtest/gtest.h>
+
+#include <string>
+
+#include "source/opt/build_module.h"
+#include "source/opt/ir_context.h"
+#include "spirv-tools/libspirv.hpp"
+#include "tools/util/cli_consumer.h"
+
+namespace {
+
+constexpr auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_6;
+
+std::pair<bool, std::unordered_map<std::string, std::string>> ExtractSource(
+    const std::string& spv_source) {
+  std::unique_ptr<spvtools::opt::IRContext> ctx = spvtools::BuildModule(
+      kDefaultEnvironment, spvtools::utils::CLIMessageConsumer, spv_source,
+      spvtools::SpirvTools::kDefaultAssembleOption |
+          SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  std::vector<uint32_t> binary;
+  ctx->module()->ToBinary(&binary, /* skip_nop = */ false);
+  std::unordered_map<std::string, std::string> output;
+  bool result = ExtractSourceFromModule(binary, &output);
+  return std::make_pair(result, std::move(output));
+}
+
+}  // namespace
+
+TEST(ExtractSourceTest, no_debug) {
+  std::string source = R"(
+           OpCapability Shader
+           OpCapability Linkage
+           OpMemoryModel Logical GLSL450
+   %void = OpTypeVoid
+      %2 = OpTypeFunction %void
+   %bool = OpTypeBool
+      %4 = OpUndef %bool
+      %5 = OpFunction %void None %2
+      %6 = OpLabel
+           OpReturn
+           OpFunctionEnd
+  )";
+
+  auto[success, result] = ExtractSource(source);
+  ASSERT_TRUE(success);
+  ASSERT_TRUE(result.size() == 0);
+}
+
+TEST(ExtractSourceTest, SimpleSource) {
+  std::string source = R"(
+      OpCapability Shader
+      OpMemoryModel Logical GLSL450
+      OpEntryPoint GLCompute %1 "compute_1"
+      OpExecutionMode %1 LocalSize 1 1 1
+ %2 = OpString "compute.hlsl"
+      OpSource HLSL 660 %2 "[numthreads(1, 1, 1)] void compute_1(){ }"
+      OpName %1 "compute_1"
+ %3 = OpTypeVoid
+ %4 = OpTypeFunction %3
+ %1 = OpFunction %3 None %4
+ %5 = OpLabel
+      OpLine %2 1 41
+      OpReturn
+      OpFunctionEnd
+  )";
+
+  auto[success, result] = ExtractSource(source);
+  ASSERT_TRUE(success);
+  ASSERT_TRUE(result.size() == 1);
+  ASSERT_TRUE(result["compute.hlsl"] ==
+              "[numthreads(1, 1, 1)] void compute_1(){ }");
+}
+
+TEST(ExtractSourceTest, SourceContinued) {
+  std::string source = R"(
+      OpCapability Shader
+      OpMemoryModel Logical GLSL450
+      OpEntryPoint GLCompute %1 "compute_1"
+      OpExecutionMode %1 LocalSize 1 1 1
+ %2 = OpString "compute.hlsl"
+      OpSource HLSL 660 %2 "[numthreads(1, 1, 1)] "
+      OpSourceContinued "void compute_1(){ }"
+      OpName %1 "compute_1"
+ %3 = OpTypeVoid
+ %4 = OpTypeFunction %3
+ %1 = OpFunction %3 None %4
+ %5 = OpLabel
+      OpLine %2 1 41
+      OpReturn
+      OpFunctionEnd
+  )";
+
+  auto[success, result] = ExtractSource(source);
+  ASSERT_TRUE(success);
+  ASSERT_TRUE(result.size() == 1);
+  ASSERT_TRUE(result["compute.hlsl"] ==
+              "[numthreads(1, 1, 1)] void compute_1(){ }");
+}
+
+TEST(ExtractSourceTest, OnlyFilename) {
+  std::string source = R"(
+      OpCapability Shader
+      OpMemoryModel Logical GLSL450
+      OpEntryPoint GLCompute %1 "compute_1"
+      OpExecutionMode %1 LocalSize 1 1 1
+ %2 = OpString "compute.hlsl"
+      OpSource HLSL 660 %2
+      OpName %1 "compute_1"
+ %3 = OpTypeVoid
+ %4 = OpTypeFunction %3
+ %1 = OpFunction %3 None %4
+ %5 = OpLabel
+      OpLine %2 1 41
+      OpReturn
+      OpFunctionEnd
+  )";
+
+  auto[success, result] = ExtractSource(source);
+  ASSERT_TRUE(success);
+  ASSERT_TRUE(result.size() == 1);
+  ASSERT_TRUE(result["compute.hlsl"] == "");
+}
+
+TEST(ExtractSourceTest, MultipleFiles) {
+  std::string source = R"(
+      OpCapability Shader
+      OpMemoryModel Logical GLSL450
+      OpEntryPoint GLCompute %1 "compute_1"
+      OpExecutionMode %1 LocalSize 1 1 1
+ %2 = OpString "compute1.hlsl"
+ %3 = OpString "compute2.hlsl"
+      OpSource HLSL 660 %2 "some instruction"
+      OpSource HLSL 660 %3 "some other instruction"
+      OpName %1 "compute_1"
+ %4 = OpTypeVoid
+ %5 = OpTypeFunction %4
+ %1 = OpFunction %4 None %5
+ %6 = OpLabel
+      OpLine %2 1 41
+      OpReturn
+      OpFunctionEnd
+  )";
+
+  auto[success, result] = ExtractSource(source);
+  ASSERT_TRUE(success);
+  ASSERT_TRUE(result.size() == 2);
+  ASSERT_TRUE(result["compute1.hlsl"] == "some instruction");
+  ASSERT_TRUE(result["compute2.hlsl"] == "some other instruction");
+}
+
+TEST(ExtractSourceTest, MultilineCode) {
+  std::string source = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "compute_1"
+               OpExecutionMode %1 LocalSize 1 1 1
+          %2 = OpString "compute.hlsl"
+               OpSource HLSL 660 %2 "[numthreads(1, 1, 1)]
+void compute_1() {
+}
+"
+               OpName %1 "compute_1"
+          %3 = OpTypeVoid
+          %4 = OpTypeFunction %3
+          %1 = OpFunction %3 None %4
+          %5 = OpLabel
+               OpLine %2 3 1
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  auto[success, result] = ExtractSource(source);
+  ASSERT_TRUE(success);
+  ASSERT_TRUE(result.size() == 1);
+  ASSERT_TRUE(result["compute.hlsl"] ==
+              "[numthreads(1, 1, 1)]\nvoid compute_1() {\n}\n");
+}
+
+TEST(ExtractSourceTest, EmptyFilename) {
+  std::string source = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "compute_1"
+               OpExecutionMode %1 LocalSize 1 1 1
+          %2 = OpString ""
+               OpSource HLSL 660 %2 "void compute(){}"
+               OpName %1 "compute_1"
+          %3 = OpTypeVoid
+          %4 = OpTypeFunction %3
+          %1 = OpFunction %3 None %4
+          %5 = OpLabel
+               OpLine %2 3 1
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  auto[success, result] = ExtractSource(source);
+  ASSERT_TRUE(success);
+  ASSERT_TRUE(result.size() == 1);
+  ASSERT_TRUE(result["unnamed-0.hlsl"] == "void compute(){}");
+}
+
+TEST(ExtractSourceTest, EscapeEscaped) {
+  std::string source = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "compute"
+               OpExecutionMode %1 LocalSize 1 1 1
+          %2 = OpString "compute.hlsl"
+               OpSource HLSL 660 %2 "// check \" escape removed"
+               OpName %1 "compute"
+          %3 = OpTypeVoid
+          %4 = OpTypeFunction %3
+          %1 = OpFunction %3 None %4
+          %5 = OpLabel
+               OpLine %2 6 1
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  auto[success, result] = ExtractSource(source);
+  ASSERT_TRUE(success);
+  ASSERT_TRUE(result.size() == 1);
+  ASSERT_TRUE(result["compute.hlsl"] == "// check \" escape removed");
+}
+
+TEST(ExtractSourceTest, OpSourceWithNoSource) {
+  std::string source = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "compute"
+               OpExecutionMode %1 LocalSize 1 1 1
+          %2 = OpString "compute.hlsl"
+               OpSource HLSL 660 %2
+               OpName %1 "compute"
+          %3 = OpTypeVoid
+          %4 = OpTypeFunction %3
+          %1 = OpFunction %3 None %4
+          %5 = OpLabel
+               OpLine %2 6 1
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  auto[success, result] = ExtractSource(source);
+  ASSERT_TRUE(success);
+  ASSERT_TRUE(result.size() == 1);
+  ASSERT_TRUE(result["compute.hlsl"] == "");
+}
diff --git a/test/tools/opt/CMakeLists.txt b/test/tools/opt/CMakeLists.txt
index 21aa247..966ffbb 100644
--- a/test/tools/opt/CMakeLists.txt
+++ b/test/tools/opt/CMakeLists.txt
@@ -13,9 +13,9 @@
 # limitations under the License.
 
 if(NOT ${SPIRV_SKIP_TESTS})
-  if(${PYTHONINTERP_FOUND})
+  if(${Python3_Interpreter_FOUND})
     add_test(NAME spirv_opt_cli_tools_tests
-      COMMAND ${PYTHON_EXECUTABLE}
+      COMMAND Python3::Interpreter
       ${CMAKE_CURRENT_SOURCE_DIR}/../spirv_test_framework.py
       $<TARGET_FILE:spirv-opt> $<TARGET_FILE:spirv-as> $<TARGET_FILE:spirv-dis>
       --test-dir ${CMAKE_CURRENT_SOURCE_DIR})
diff --git a/test/unit_spirv.h b/test/unit_spirv.h
index f0a2958..9e7074c 100644
--- a/test/unit_spirv.h
+++ b/test/unit_spirv.h
@@ -105,7 +105,7 @@
 // Returns a vector of words representing a single instruction with the
 // given opcode and operand words as a vector.
 inline std::vector<uint32_t> MakeInstruction(
-    SpvOp opcode, const std::vector<uint32_t>& args) {
+    spv::Op opcode, const std::vector<uint32_t>& args) {
   std::vector<uint32_t> result{
       spvOpcodeMake(uint16_t(args.size() + 1), opcode)};
   result.insert(result.end(), args.begin(), args.end());
@@ -116,7 +116,7 @@
 // given opcode and whose operands are the concatenation of the two given
 // argument lists.
 inline std::vector<uint32_t> MakeInstruction(
-    SpvOp opcode, std::vector<uint32_t> args,
+    spv::Op opcode, std::vector<uint32_t> args,
     const std::vector<uint32_t>& extra_args) {
   args.insert(args.end(), extra_args.begin(), extra_args.end());
   return MakeInstruction(opcode, args);
@@ -200,11 +200,10 @@
 }
 
 // Returns the capabilities in a CapabilitySet as an ordered vector.
-inline std::vector<SpvCapability> ElementsIn(
+inline std::vector<spv::Capability> ElementsIn(
     const spvtools::CapabilitySet& capabilities) {
-  std::vector<SpvCapability> result;
-  capabilities.ForEach([&result](SpvCapability c) { result.push_back(c); });
-  return result;
+  return std::vector<spv::Capability>(capabilities.cbegin(),
+                                      capabilities.cend());
 }
 
 }  // namespace spvtest
diff --git a/test/val/CMakeLists.txt b/test/val/CMakeLists.txt
index de89b93..62d93bd 100644
--- a/test/val/CMakeLists.txt
+++ b/test/val/CMakeLists.txt
@@ -93,6 +93,7 @@
   SRCS
        val_ray_query_test.cpp
        val_ray_tracing_test.cpp
+       val_ray_tracing_reorder_test.cpp
        val_small_type_uses_test.cpp
        val_ssa_test.cpp
        val_state_test.cpp
diff --git a/test/val/val_annotation_test.cpp b/test/val/val_annotation_test.cpp
index bb30de0..9f85a30 100644
--- a/test/val/val_annotation_test.cpp
+++ b/test/val/val_annotation_test.cpp
@@ -18,7 +18,6 @@
 #include <vector>
 
 #include "gmock/gmock.h"
-#include "test/test_fixture.h"
 #include "test/unit_spirv.h"
 #include "test/val/val_code_generator.h"
 #include "test/val/val_fixtures.h"
diff --git a/test/val/val_arithmetics_test.cpp b/test/val/val_arithmetics_test.cpp
index 631375e..58ac442 100644
--- a/test/val/val_arithmetics_test.cpp
+++ b/test/val/val_arithmetics_test.cpp
@@ -1318,7 +1318,7 @@
   CompileSuccessfully(GenerateCoopMatCode(types, "").c_str());
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("OpTypeCooperativeMatrixNV Component Type <id> "
+              HasSubstr("OpTypeCooperativeMatrix Component Type <id> "
                         "'4[%bool]' is not a scalar numerical type."));
 }
 
@@ -1331,7 +1331,7 @@
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
   EXPECT_THAT(
       getDiagnosticString(),
-      HasSubstr("OpTypeCooperativeMatrixNV Scope <id> '17[%float_1]' is not a "
+      HasSubstr("OpTypeCooperativeMatrix Scope <id> '17[%float_1]' is not a "
                 "constant instruction with scalar integer type."));
 }
 
@@ -1344,7 +1344,7 @@
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
   EXPECT_THAT(
       getDiagnosticString(),
-      HasSubstr("OpTypeCooperativeMatrixNV Rows <id> '17[%float_1]' is not a "
+      HasSubstr("OpTypeCooperativeMatrix Rows <id> '17[%float_1]' is not a "
                 "constant instruction with scalar integer type."));
 }
 
@@ -1357,7 +1357,7 @@
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
   EXPECT_THAT(
       getDiagnosticString(),
-      HasSubstr("OpTypeCooperativeMatrixNV Cols <id> '17[%float_1]' is not a "
+      HasSubstr("OpTypeCooperativeMatrix Cols <id> '17[%float_1]' is not a "
                 "constant instruction with scalar integer type."));
 }
 
@@ -1469,6 +1469,149 @@
                 "SMulExtended"));
 }
 
+std::string GenerateCoopMatKHRCode(const std::string& extra_types,
+                                   const std::string& main_body) {
+  const std::string prefix = R"(
+OpCapability Shader
+OpCapability Float16
+OpCapability CooperativeMatrixKHR
+OpExtension "SPV_KHR_cooperative_matrix"
+OpExtension "SPV_KHR_vulkan_memory_model"
+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_16 = OpConstant %u32 16
+%u32_4 = OpConstant %u32 4
+%subgroup = OpConstant %u32 3
+%useA = OpConstant %u32 0
+%useB = OpConstant %u32 1
+%useC = OpConstant %u32 2
+
+%f16matA = OpTypeCooperativeMatrixKHR %f16 %subgroup %u32_16 %u32_16 %useA
+%u32matA = OpTypeCooperativeMatrixKHR %u32 %subgroup %u32_16 %u32_16 %useA
+%s32matA = OpTypeCooperativeMatrixKHR %s32 %subgroup %u32_16 %u32_16 %useA
+
+%f16matB = OpTypeCooperativeMatrixKHR %f16 %subgroup %u32_16 %u32_16 %useB
+%u32matB = OpTypeCooperativeMatrixKHR %u32 %subgroup %u32_16 %u32_16 %useB
+%s32matB = OpTypeCooperativeMatrixKHR %s32 %subgroup %u32_16 %u32_16 %useB
+
+%f16matC = OpTypeCooperativeMatrixKHR %f16 %subgroup %u32_16 %u32_16 %useC
+%f32matC = OpTypeCooperativeMatrixKHR %f32 %subgroup %u32_16 %u32_16 %useC
+%u32matC = OpTypeCooperativeMatrixKHR %u32 %subgroup %u32_16 %u32_16 %useC
+%s32matC = OpTypeCooperativeMatrixKHR %s32 %subgroup %u32_16 %u32_16 %useC
+
+%f16_1 = OpConstant %f16 1
+%f32_1 = OpConstant %f32 1
+%u32_1 = OpConstant %u32 1
+%s32_1 = OpConstant %s32 1
+
+%f16mat_A_1 = OpConstantComposite %f16matA %f16_1
+%u32mat_A_1 = OpConstantComposite %u32matA %u32_1
+%s32mat_A_1 = OpConstantComposite %s32matA %s32_1
+
+%f16mat_B_1 = OpConstantComposite %f16matB %f16_1
+%u32mat_B_1 = OpConstantComposite %u32matB %u32_1
+%s32mat_B_1 = OpConstantComposite %s32matB %s32_1
+
+%f16mat_C_1 = OpConstantComposite %f16matC %f16_1
+%u32mat_C_1 = OpConstantComposite %u32matC %u32_1
+%s32mat_C_1 = OpConstantComposite %s32matC %s32_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, CoopMatKHRSuccess) {
+  const std::string body = R"(
+%val1 = OpFAdd %f16matA %f16mat_A_1 %f16mat_A_1
+%val2 = OpFSub %f16matA %f16mat_A_1 %f16mat_A_1
+%val3 = OpFMul %f16matA %f16mat_A_1 %f16mat_A_1
+%val4 = OpFDiv %f16matA %f16mat_A_1 %f16mat_A_1
+%val5 = OpFNegate %f16matA %f16mat_A_1
+%val6 = OpIAdd %u32matA %u32mat_A_1 %u32mat_A_1
+%val7 = OpISub %u32matA %u32mat_A_1 %u32mat_A_1
+%val8 = OpUDiv %u32matA %u32mat_A_1 %u32mat_A_1
+%val9 = OpIAdd %s32matA %s32mat_A_1 %s32mat_A_1
+%val10 = OpISub %s32matA %s32mat_A_1 %s32mat_A_1
+%val11 = OpSDiv %s32matA %s32mat_A_1 %s32mat_A_1
+%val12 = OpSNegate %s32matA %s32mat_A_1
+%val13 = OpMatrixTimesScalar %f16matA %f16mat_A_1 %f16_1
+%val14 = OpMatrixTimesScalar %u32matA %u32mat_A_1 %u32_1
+%val15 = OpMatrixTimesScalar %s32matA %s32mat_A_1 %s32_1
+%val16 = OpCooperativeMatrixMulAddKHR %f32matC %f16mat_A_1 %f16mat_B_1 %f16mat_C_1
+%val17 = OpCooperativeMatrixMulAddKHR %s32matC %s32mat_A_1 %s32mat_B_1 %s32mat_C_1
+  MatrixASignedComponentsKHR|MatrixBSignedComponentsKHR|MatrixCSignedComponentsKHR|MatrixResultSignedComponentsKHR
+%val18 = OpCooperativeMatrixMulAddKHR %u32matC %u32mat_A_1 %u32mat_B_1 %u32mat_C_1
+)";
+
+  CompileSuccessfully(GenerateCoopMatKHRCode("", body).c_str());
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateArithmetics, CoopMatMatrixKHRTimesScalarMismatchFail) {
+  const std::string body = R"(
+%val1 = OpMatrixTimesScalar %f16matA %f16mat_A_1 %f32_1
+)";
+
+  CompileSuccessfully(GenerateCoopMatKHRCode("", 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 matrix operand: MatrixTimesScalar"));
+}
+
+TEST_F(ValidateArithmetics, CoopMatKHRScopeFail) {
+  const std::string types = R"(
+%workgroup = OpConstant %u32 2
+%mat16x16_wg = OpTypeCooperativeMatrixKHR %f16 %workgroup %u32_16 %u32_16 %useC
+%f16matwg_16x16_1 = OpConstantComposite %mat16x16_wg %f16_1
+)";
+
+  const std::string body = R"(
+%val1 = OpFAdd %f16matA %f16matwg_16x16_1 %f16mat_A_1
+)";
+
+  CompileSuccessfully(GenerateCoopMatKHRCode(types, body).c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Expected scopes of Matrix and Result Type to be identical"));
+}
+
+TEST_F(ValidateArithmetics, CoopMatKHRDimFail) {
+  const std::string types = R"(
+%mat16x4 = OpTypeCooperativeMatrixKHR %f16 %subgroup %u32_16 %u32_4 %useC
+%mat16x4_C_1 = OpConstantComposite %mat16x4 %f16_1
+)";
+
+  const std::string body = R"(
+%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());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Cooperative matrix 'N' mismatch: CooperativeMatrixMulAddKHR"));
+}
+
 }  // namespace
 }  // namespace val
 }  // namespace spvtools
diff --git a/test/val/val_barriers_test.cpp b/test/val/val_barriers_test.cpp
index c86cdc1..f160904 100644
--- a/test/val/val_barriers_test.cpp
+++ b/test/val/val_barriers_test.cpp
@@ -361,7 +361,7 @@
   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-SubgroupVoteKHR-06997"));
+              AnyVUID("VUID-StandaloneSpirv-SubgroupVoteKHR-07951"));
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr(
@@ -775,7 +775,7 @@
   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-SubgroupVoteKHR-06997"));
+              AnyVUID("VUID-StandaloneSpirv-SubgroupVoteKHR-07951"));
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr(
diff --git a/test/val/val_bitwise_test.cpp b/test/val/val_bitwise_test.cpp
index bebaa84..b849e7b 100644
--- a/test/val/val_bitwise_test.cpp
+++ b/test/val/val_bitwise_test.cpp
@@ -643,6 +643,32 @@
               HasSubstr("Expected 32-bit int type for Base operand: BitCount"));
 }
 
+TEST_F(ValidateBitwise, OpBitCountPointer) {
+  const std::string body = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%ptr_int = OpTypePointer Function %int
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%var = OpVariable %ptr_int Function
+%count = OpBitCount %int %var
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(body);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Expected int scalar or vector type for Base operand: BitCount"));
+}
+
 }  // namespace
 }  // namespace val
 }  // namespace spvtools
diff --git a/test/val/val_cfg_test.cpp b/test/val/val_cfg_test.cpp
index a4d1444..3953057 100644
--- a/test/val/val_cfg_test.cpp
+++ b/test/val/val_cfg_test.cpp
@@ -16,14 +16,12 @@
 
 #include <array>
 #include <functional>
-#include <iterator>
 #include <sstream>
 #include <string>
 #include <utility>
 #include <vector>
 
 #include "gmock/gmock.h"
-#include "source/diagnostic.h"
 #include "source/spirv_target_env.h"
 #include "source/val/validate.h"
 #include "test/test_fixture.h"
@@ -37,7 +35,7 @@
 using ::testing::HasSubstr;
 using ::testing::MatchesRegex;
 
-using ValidateCFG = spvtest::ValidateBase<SpvCapability>;
+using ValidateCFG = spvtest::ValidateBase<spv::Capability>;
 using spvtest::ScopedContext;
 
 std::string nameOps() { return ""; }
@@ -58,7 +56,7 @@
 class Block {
   std::string label_;
   std::string body_;
-  SpvOp type_;
+  spv::Op type_;
   std::vector<Block> successors_;
 
  public:
@@ -66,7 +64,7 @@
   ///
   /// @param[in]: label the label id of the block
   /// @param[in]: type the branch instruction that ends the block
-  explicit Block(std::string label, SpvOp type = SpvOpBranch)
+  explicit Block(std::string label, spv::Op type = spv::Op::OpBranch)
       : label_(label), body_(), type_(type), successors_() {}
 
   /// Sets the instructions which will appear in the body of the block
@@ -89,13 +87,13 @@
     }
 
     switch (type_) {
-      case SpvOpBranchConditional:
+      case spv::Op::OpBranchConditional:
         out << "OpBranchConditional %cond ";
         for (Block& b : successors_) {
           out << "%" + b.label_ + " ";
         }
         break;
-      case SpvOpSwitch: {
+      case spv::Op::OpSwitch: {
         out << "OpSwitch %one %" + successors_.front().label_;
         std::stringstream ss;
         for (size_t i = 1; i < successors_.size(); i++) {
@@ -103,25 +101,25 @@
         }
         out << ss.str();
       } break;
-      case SpvOpLoopMerge: {
+      case spv::Op::OpLoopMerge: {
         assert(successors_.size() == 2);
         out << "OpLoopMerge %" + successors_[0].label_ + " %" +
                    successors_[0].label_ + "None";
       } break;
 
-      case SpvOpReturn:
+      case spv::Op::OpReturn:
         assert(successors_.size() == 0);
         out << "OpReturn\n";
         break;
-      case SpvOpUnreachable:
+      case spv::Op::OpUnreachable:
         assert(successors_.size() == 0);
         out << "OpUnreachable\n";
         break;
-      case SpvOpBranch:
+      case spv::Op::OpBranch:
         assert(successors_.size() == 1);
         out << "OpBranch %" + successors_.front().label_;
         break;
-      case SpvOpKill:
+      case spv::Op::OpKill:
         assert(successors_.size() == 0);
         out << "OpKill\n";
         break;
@@ -138,9 +136,9 @@
 
 /// Assigns the successors for the Block on the lhs
 Block& operator>>(Block& lhs, std::vector<Block> successors) {
-  if (lhs.type_ == SpvOpBranchConditional) {
+  if (lhs.type_ == spv::Op::OpBranchConditional) {
     assert(successors.size() == 2);
-  } else if (lhs.type_ == SpvOpSwitch) {
+  } else if (lhs.type_ == spv::Op::OpSwitch) {
     assert(successors.size() > 1);
   }
   lhs.successors_ = successors;
@@ -149,12 +147,12 @@
 
 /// Assigns the successor for the Block on the lhs
 Block& operator>>(Block& lhs, Block& successor) {
-  assert(lhs.type_ == SpvOpBranch);
+  assert(lhs.type_ == spv::Op::OpBranch);
   lhs.successors_.push_back(successor);
   return lhs;
 }
 
-const std::string& GetDefaultHeader(SpvCapability cap) {
+const std::string& GetDefaultHeader(spv::Capability cap) {
   static const std::string shader_header =
       "OpCapability Shader\n"
       "OpCapability Linkage\n"
@@ -165,7 +163,7 @@
       "OpCapability Linkage\n"
       "OpMemoryModel Logical OpenCL\n";
 
-  return (cap == SpvCapabilityShader) ? shader_header : kernel_header;
+  return (cap == spv::Capability::Shader) ? shader_header : kernel_header;
 }
 
 const std::string& types_consts() {
@@ -181,8 +179,8 @@
 }
 
 INSTANTIATE_TEST_SUITE_P(StructuredControlFlow, ValidateCFG,
-                         ::testing::Values(SpvCapabilityShader,
-                                           SpvCapabilityKernel));
+                         ::testing::Values(spv::Capability::Shader,
+                                           spv::Capability::Kernel));
 
 TEST_P(ValidateCFG, LoopReachableFromEntryButNeverLeadingToReturn) {
   // In this case, the loop is reachable from a node without a predecessor,
@@ -268,11 +266,11 @@
 }
 
 TEST_P(ValidateCFG, Simple) {
-  bool is_shader = GetParam() == SpvCapabilityShader;
+  bool is_shader = GetParam() == spv::Capability::Shader;
   Block entry("entry");
-  Block loop("loop", SpvOpBranchConditional);
+  Block loop("loop", spv::Op::OpBranchConditional);
   Block cont("cont");
-  Block merge("merge", SpvOpReturn);
+  Block merge("merge", spv::Op::OpReturn);
 
   entry.SetBody("%cond    = OpSLessThan %boolt %one %two\n");
   if (is_shader) {
@@ -298,7 +296,7 @@
 TEST_P(ValidateCFG, Variable) {
   Block entry("entry");
   Block cont("cont");
-  Block exit("exit", SpvOpReturn);
+  Block exit("exit", spv::Op::OpReturn);
 
   entry.SetBody("%var = OpVariable %ptrt Function\n");
 
@@ -317,7 +315,7 @@
 TEST_P(ValidateCFG, VariableNotInFirstBlockBad) {
   Block entry("entry");
   Block cont("cont");
-  Block exit("exit", SpvOpReturn);
+  Block exit("exit", spv::Op::OpReturn);
 
   // This operation should only be performed in the entry block
   cont.SetBody("%var = OpVariable %ptrt Function\n");
@@ -339,10 +337,10 @@
 }
 
 TEST_P(ValidateCFG, BlockSelfLoopIsOk) {
-  bool is_shader = GetParam() == SpvCapabilityShader;
+  bool is_shader = GetParam() == spv::Capability::Shader;
   Block entry("entry");
-  Block loop("loop", SpvOpBranchConditional);
-  Block merge("merge", SpvOpReturn);
+  Block loop("loop", spv::Op::OpBranchConditional);
+  Block merge("merge", spv::Op::OpReturn);
 
   entry.SetBody("%cond    = OpSLessThan %boolt %one %two\n");
   if (is_shader) loop.SetBody("OpLoopMerge %merge %loop None\n");
@@ -363,11 +361,11 @@
 }
 
 TEST_P(ValidateCFG, BlockAppearsBeforeDominatorBad) {
-  bool is_shader = GetParam() == SpvCapabilityShader;
+  bool is_shader = GetParam() == spv::Capability::Shader;
   Block entry("entry");
   Block cont("cont");
-  Block branch("branch", SpvOpBranchConditional);
-  Block merge("merge", SpvOpReturn);
+  Block branch("branch", spv::Op::OpBranchConditional);
+  Block merge("merge", spv::Op::OpReturn);
 
   entry.SetBody("%cond    = OpSLessThan %boolt %one %two\n");
   if (is_shader) branch.SetBody("OpSelectionMerge %merge None\n");
@@ -392,11 +390,11 @@
 }
 
 TEST_P(ValidateCFG, MergeBlockTargetedByMultipleHeaderBlocksBad) {
-  bool is_shader = GetParam() == SpvCapabilityShader;
+  bool is_shader = GetParam() == spv::Capability::Shader;
   Block entry("entry");
   Block loop("loop");
-  Block selection("selection", SpvOpBranchConditional);
-  Block merge("merge", SpvOpReturn);
+  Block selection("selection", spv::Op::OpBranchConditional);
+  Block merge("merge", spv::Op::OpReturn);
 
   entry.SetBody("%cond    = OpSLessThan %boolt %one %two\n");
   if (is_shader) loop.SetBody(" OpLoopMerge %merge %loop None\n");
@@ -428,11 +426,11 @@
 }
 
 TEST_P(ValidateCFG, MergeBlockTargetedByMultipleHeaderBlocksSelectionBad) {
-  bool is_shader = GetParam() == SpvCapabilityShader;
+  bool is_shader = GetParam() == spv::Capability::Shader;
   Block entry("entry");
-  Block loop("loop", SpvOpBranchConditional);
-  Block selection("selection", SpvOpBranchConditional);
-  Block merge("merge", SpvOpReturn);
+  Block loop("loop", spv::Op::OpBranchConditional);
+  Block selection("selection", spv::Op::OpBranchConditional);
+  Block merge("merge", spv::Op::OpReturn);
 
   entry.SetBody("%cond    = OpSLessThan %boolt %one %two\n");
   if (is_shader) selection.SetBody(" OpSelectionMerge %merge None\n");
@@ -466,7 +464,7 @@
 TEST_P(ValidateCFG, BranchTargetFirstBlockBadSinceEntryBlock) {
   Block entry("entry");
   Block bad("bad");
-  Block end("end", SpvOpReturn);
+  Block end("end", spv::Op::OpReturn);
   std::string str = GetDefaultHeader(GetParam()) +
                     nameOps("entry", "bad", std::make_pair("func", "Main")) +
                     types_consts() +
@@ -489,7 +487,7 @@
   Block entry("entry");
   entry.SetBody("%undef = OpUndef %boolt\n");
   Block bad("bad");
-  Block end("end", SpvOpReturn);
+  Block end("end", spv::Op::OpReturn);
   Block badvalue("undef");  // This references the OpUndef.
   std::string str = GetDefaultHeader(GetParam()) +
                     nameOps("entry", "bad", std::make_pair("func", "Main")) +
@@ -511,8 +509,8 @@
 
 TEST_P(ValidateCFG, BranchConditionalTrueTargetFirstBlockBad) {
   Block entry("entry");
-  Block bad("bad", SpvOpBranchConditional);
-  Block exit("exit", SpvOpReturn);
+  Block bad("bad", spv::Op::OpBranchConditional);
+  Block exit("exit", spv::Op::OpReturn);
 
   entry.SetBody("%cond    = OpSLessThan %boolt %one %two\n");
   bad.SetBody(" OpLoopMerge %entry %exit None\n");
@@ -538,10 +536,10 @@
 
 TEST_P(ValidateCFG, BranchConditionalFalseTargetFirstBlockBad) {
   Block entry("entry");
-  Block bad("bad", SpvOpBranchConditional);
+  Block bad("bad", spv::Op::OpBranchConditional);
   Block t("t");
   Block merge("merge");
-  Block end("end", SpvOpReturn);
+  Block end("end", spv::Op::OpReturn);
 
   entry.SetBody("%cond    = OpSLessThan %boolt %one %two\n");
   bad.SetBody("OpLoopMerge %merge %cont None\n");
@@ -568,13 +566,13 @@
 
 TEST_P(ValidateCFG, SwitchTargetFirstBlockBad) {
   Block entry("entry");
-  Block bad("bad", SpvOpSwitch);
+  Block bad("bad", spv::Op::OpSwitch);
   Block block1("block1");
   Block block2("block2");
   Block block3("block3");
   Block def("def");  // default block
   Block merge("merge");
-  Block end("end", SpvOpReturn);
+  Block end("end", spv::Op::OpReturn);
 
   entry.SetBody("%cond    = OpSLessThan %boolt %one %two\n");
   bad.SetBody("OpSelectionMerge %merge None\n");
@@ -605,15 +603,15 @@
 
 TEST_P(ValidateCFG, BranchToBlockInOtherFunctionBad) {
   Block entry("entry");
-  Block middle("middle", SpvOpBranchConditional);
-  Block end("end", SpvOpReturn);
+  Block middle("middle", spv::Op::OpBranchConditional);
+  Block end("end", spv::Op::OpReturn);
 
   entry.SetBody("%cond    = OpSLessThan %boolt %one %two\n");
   middle.SetBody("OpSelectionMerge %end None\n");
 
   Block entry2("entry2");
   Block middle2("middle2");
-  Block end2("end2", SpvOpReturn);
+  Block end2("end2", spv::Op::OpReturn);
 
   std::string str = GetDefaultHeader(GetParam()) +
                     nameOps("middle2", std::make_pair("func", "Main")) +
@@ -643,9 +641,9 @@
 TEST_P(ValidateCFG, HeaderDoesntStrictlyDominateMergeBad) {
   // If a merge block is reachable, then it must be strictly dominated by
   // its header block.
-  bool is_shader = GetParam() == SpvCapabilityShader;
-  Block head("head", SpvOpBranchConditional);
-  Block exit("exit", SpvOpReturn);
+  bool is_shader = GetParam() == spv::Capability::Shader;
+  Block head("head", spv::Op::OpBranchConditional);
+  Block exit("exit", spv::Op::OpReturn);
 
   head.SetBody("%cond = OpSLessThan %boolt %one %two\n");
 
@@ -675,16 +673,16 @@
   }
 }
 
-std::string GetUnreachableMergeNoMergeInst(SpvCapability cap) {
+std::string GetUnreachableMergeNoMergeInst(spv::Capability cap) {
   std::string header = GetDefaultHeader(cap);
   Block entry("entry");
-  Block branch("branch", SpvOpBranchConditional);
-  Block t("t", SpvOpReturn);
-  Block f("f", SpvOpReturn);
-  Block merge("merge", SpvOpReturn);
+  Block branch("branch", spv::Op::OpBranchConditional);
+  Block t("t", spv::Op::OpReturn);
+  Block f("f", spv::Op::OpReturn);
+  Block merge("merge", spv::Op::OpReturn);
 
   entry.SetBody("%cond    = OpSLessThan %boolt %one %two\n");
-  if (cap == SpvCapabilityShader)
+  if (cap == spv::Capability::Shader)
     branch.AppendBody("OpSelectionMerge %merge None\n");
 
   std::string str = header;
@@ -705,18 +703,18 @@
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
-std::string GetUnreachableMergeTerminatedBy(SpvCapability cap, SpvOp op) {
+std::string GetUnreachableMergeTerminatedBy(spv::Capability cap, spv::Op op) {
   std::string header = GetDefaultHeader(cap);
 
   Block entry("entry");
-  Block branch("branch", SpvOpBranchConditional);
-  Block t("t", SpvOpReturn);
-  Block f("f", SpvOpReturn);
+  Block branch("branch", spv::Op::OpBranchConditional);
+  Block t("t", spv::Op::OpReturn);
+  Block f("f", spv::Op::OpReturn);
   Block merge("merge", op);
 
   entry.SetBody("%cond    = OpSLessThan %boolt %one %two\n");
   std::string str = header;
-  if (cap == SpvCapabilityShader)
+  if (cap == spv::Capability::Shader)
     branch.AppendBody("OpSelectionMerge %merge None\n");
 
   str += nameOps("branch", "merge", std::make_pair("func", "Main"));
@@ -734,33 +732,35 @@
 
 TEST_P(ValidateCFG, UnreachableMergeTerminatedByOpUnreachable) {
   CompileSuccessfully(
-      GetUnreachableMergeTerminatedBy(GetParam(), SpvOpUnreachable));
+      GetUnreachableMergeTerminatedBy(GetParam(), spv::Op::OpUnreachable));
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
 TEST_F(ValidateCFG, UnreachableMergeTerminatedByOpKill) {
-  CompileSuccessfully(
-      GetUnreachableMergeTerminatedBy(SpvCapabilityShader, SpvOpKill));
+  CompileSuccessfully(GetUnreachableMergeTerminatedBy(spv::Capability::Shader,
+                                                      spv::Op::OpKill));
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
 TEST_P(ValidateCFG, UnreachableMergeTerminatedByOpReturn) {
-  CompileSuccessfully(GetUnreachableMergeTerminatedBy(GetParam(), SpvOpReturn));
+  CompileSuccessfully(
+      GetUnreachableMergeTerminatedBy(GetParam(), spv::Op::OpReturn));
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
-std::string GetUnreachableContinueTerminatedBy(SpvCapability cap, SpvOp op) {
+std::string GetUnreachableContinueTerminatedBy(spv::Capability cap,
+                                               spv::Op op) {
   std::string header = GetDefaultHeader(cap);
 
   Block entry("entry");
-  Block branch("branch", SpvOpBranch);
-  Block merge("merge", SpvOpReturn);
+  Block branch("branch", spv::Op::OpBranch);
+  Block merge("merge", spv::Op::OpReturn);
   Block target("target", op);
 
-  if (op == SpvOpBranch) target >> branch;
+  if (op == spv::Op::OpBranch) target >> branch;
 
   std::string str = header;
-  if (cap == SpvCapabilityShader)
+  if (cap == spv::Capability::Shader)
     branch.AppendBody("OpLoopMerge %merge %target None\n");
 
   str += nameOps("branch", "merge", "target", std::make_pair("func", "Main"));
@@ -775,10 +775,10 @@
   return str;
 }
 
-TEST_P(ValidateCFG, UnreachableContinueTerminatedBySpvOpUnreachable) {
+TEST_P(ValidateCFG, UnreachableContinueTerminatedByOpUnreachable) {
   CompileSuccessfully(
-      GetUnreachableContinueTerminatedBy(GetParam(), SpvOpUnreachable));
-  if (GetParam() == SpvCapabilityShader) {
+      GetUnreachableContinueTerminatedBy(GetParam(), spv::Op::OpUnreachable));
+  if (GetParam() == spv::Capability::Shader) {
     ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
     EXPECT_THAT(getDiagnosticString(),
                 HasSubstr("targeted by 0 back-edge blocks"));
@@ -787,18 +787,18 @@
   }
 }
 
-TEST_F(ValidateCFG, UnreachableContinueTerminatedBySpvOpKill) {
-  CompileSuccessfully(
-      GetUnreachableContinueTerminatedBy(SpvCapabilityShader, SpvOpKill));
+TEST_F(ValidateCFG, UnreachableContinueTerminatedByOpKill) {
+  CompileSuccessfully(GetUnreachableContinueTerminatedBy(
+      spv::Capability::Shader, spv::Op::OpKill));
   ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("targeted by 0 back-edge blocks"));
 }
 
-TEST_P(ValidateCFG, UnreachableContinueTerminatedBySpvOpReturn) {
+TEST_P(ValidateCFG, UnreachableContinueTerminatedByOpReturn) {
   CompileSuccessfully(
-      GetUnreachableContinueTerminatedBy(GetParam(), SpvOpReturn));
-  if (GetParam() == SpvCapabilityShader) {
+      GetUnreachableContinueTerminatedBy(GetParam(), spv::Op::OpReturn));
+  if (GetParam() == spv::Capability::Shader) {
     ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
     EXPECT_THAT(getDiagnosticString(),
                 HasSubstr("targeted by 0 back-edge blocks"));
@@ -807,25 +807,25 @@
   }
 }
 
-TEST_P(ValidateCFG, UnreachableContinueTerminatedBySpvOpBranch) {
+TEST_P(ValidateCFG, UnreachableContinueTerminatedByOpBranch) {
   CompileSuccessfully(
-      GetUnreachableContinueTerminatedBy(GetParam(), SpvOpBranch));
+      GetUnreachableContinueTerminatedBy(GetParam(), spv::Op::OpBranch));
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
-std::string GetUnreachableMergeUnreachableMergeInst(SpvCapability cap) {
+std::string GetUnreachableMergeUnreachableMergeInst(spv::Capability cap) {
   std::string header = GetDefaultHeader(cap);
 
-  Block body("body", SpvOpReturn);
+  Block body("body", spv::Op::OpReturn);
   Block entry("entry");
-  Block branch("branch", SpvOpBranchConditional);
-  Block t("t", SpvOpReturn);
-  Block f("f", SpvOpReturn);
-  Block merge("merge", SpvOpUnreachable);
+  Block branch("branch", spv::Op::OpBranchConditional);
+  Block t("t", spv::Op::OpReturn);
+  Block f("f", spv::Op::OpReturn);
+  Block merge("merge", spv::Op::OpUnreachable);
 
   entry.SetBody("%cond    = OpSLessThan %boolt %one %two\n");
   std::string str = header;
-  if (cap == SpvCapabilityShader)
+  if (cap == spv::Capability::Shader)
     branch.AppendBody("OpSelectionMerge %merge None\n");
 
   str += nameOps("branch", "merge", std::make_pair("func", "Main"));
@@ -847,19 +847,19 @@
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
-std::string GetUnreachableContinueUnreachableLoopInst(SpvCapability cap) {
+std::string GetUnreachableContinueUnreachableLoopInst(spv::Capability cap) {
   std::string header = GetDefaultHeader(cap);
 
-  Block body("body", SpvOpReturn);
+  Block body("body", spv::Op::OpReturn);
   Block entry("entry");
-  Block branch("branch", SpvOpBranch);
-  Block merge("merge", SpvOpReturn);
-  Block target("target", SpvOpBranch);
+  Block branch("branch", spv::Op::OpBranch);
+  Block merge("merge", spv::Op::OpReturn);
+  Block target("target", spv::Op::OpBranch);
 
   target >> branch;
 
   std::string str = header;
-  if (cap == SpvCapabilityShader)
+  if (cap == spv::Capability::Shader)
     branch.AppendBody("OpLoopMerge %merge %target None\n");
 
   str += nameOps("branch", "merge", "target", std::make_pair("func", "Main"));
@@ -880,21 +880,21 @@
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
-std::string GetUnreachableMergeWithComplexBody(SpvCapability cap) {
+std::string GetUnreachableMergeWithComplexBody(spv::Capability cap) {
   std::string header = GetDefaultHeader(cap);
 
   Block entry("entry");
-  Block branch("branch", SpvOpBranchConditional);
-  Block t("t", SpvOpReturn);
-  Block f("f", SpvOpReturn);
-  Block merge("merge", SpvOpUnreachable);
+  Block branch("branch", spv::Op::OpBranchConditional);
+  Block t("t", spv::Op::OpReturn);
+  Block f("f", spv::Op::OpReturn);
+  Block merge("merge", spv::Op::OpUnreachable);
 
   entry.AppendBody("%placeholder   = OpVariable %intptrt Function\n");
   entry.AppendBody("%cond    = OpSLessThan %boolt %one %two\n");
   merge.AppendBody("OpStore %placeholder %one\n");
 
   std::string str = header;
-  if (cap == SpvCapabilityShader)
+  if (cap == spv::Capability::Shader)
     branch.AppendBody("OpSelectionMerge %merge None\n");
 
   str += nameOps("branch", "merge", std::make_pair("func", "Main"));
@@ -916,13 +916,13 @@
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
-std::string GetUnreachableContinueWithComplexBody(SpvCapability cap) {
+std::string GetUnreachableContinueWithComplexBody(spv::Capability cap) {
   std::string header = GetDefaultHeader(cap);
 
   Block entry("entry");
-  Block branch("branch", SpvOpBranch);
-  Block merge("merge", SpvOpReturn);
-  Block target("target", SpvOpBranch);
+  Block branch("branch", spv::Op::OpBranch);
+  Block merge("merge", spv::Op::OpReturn);
+  Block target("target", spv::Op::OpBranch);
 
   target >> branch;
 
@@ -930,7 +930,7 @@
   target.AppendBody("OpStore %placeholder %one\n");
 
   std::string str = header;
-  if (cap == SpvCapabilityShader)
+  if (cap == spv::Capability::Shader)
     branch.AppendBody("OpLoopMerge %merge %target None\n");
 
   str += nameOps("branch", "merge", "target", std::make_pair("func", "Main"));
@@ -951,19 +951,19 @@
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
-std::string GetUnreachableMergeWithBranchUse(SpvCapability cap) {
+std::string GetUnreachableMergeWithBranchUse(spv::Capability cap) {
   std::string header = GetDefaultHeader(cap);
 
   Block entry("entry");
-  Block branch("branch", SpvOpBranchConditional);
-  Block t("t", SpvOpBranch);
-  Block f("f", SpvOpReturn);
-  Block merge("merge", SpvOpUnreachable);
+  Block branch("branch", spv::Op::OpBranchConditional);
+  Block t("t", spv::Op::OpBranch);
+  Block f("f", spv::Op::OpReturn);
+  Block merge("merge", spv::Op::OpUnreachable);
 
   entry.AppendBody("%cond    = OpSLessThan %boolt %one %two\n");
 
   std::string str = header;
-  if (cap == SpvCapabilityShader)
+  if (cap == spv::Capability::Shader)
     branch.AppendBody("OpSelectionMerge %merge None\n");
 
   str += nameOps("branch", "merge", std::make_pair("func", "Main"));
@@ -984,20 +984,20 @@
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
-std::string GetUnreachableMergeWithMultipleUses(SpvCapability cap) {
+std::string GetUnreachableMergeWithMultipleUses(spv::Capability cap) {
   std::string header = GetDefaultHeader(cap);
 
   Block entry("entry");
-  Block branch("branch", SpvOpBranchConditional);
-  Block t("t", SpvOpReturn);
-  Block f("f", SpvOpReturn);
-  Block merge("merge", SpvOpUnreachable);
-  Block duplicate("duplicate", SpvOpBranchConditional);
+  Block branch("branch", spv::Op::OpBranchConditional);
+  Block t("t", spv::Op::OpReturn);
+  Block f("f", spv::Op::OpReturn);
+  Block merge("merge", spv::Op::OpUnreachable);
+  Block duplicate("duplicate", spv::Op::OpBranchConditional);
 
   entry.AppendBody("%cond    = OpSLessThan %boolt %one %two\n");
 
   std::string str = header;
-  if (cap == SpvCapabilityShader) {
+  if (cap == spv::Capability::Shader) {
     branch.AppendBody("OpSelectionMerge %merge None\n");
     duplicate.AppendBody("OpSelectionMerge %merge None\n");
   }
@@ -1018,7 +1018,7 @@
 
 TEST_P(ValidateCFG, UnreachableMergeWithMultipleUses) {
   CompileSuccessfully(GetUnreachableMergeWithMultipleUses(GetParam()));
-  if (GetParam() == SpvCapabilityShader) {
+  if (GetParam() == spv::Capability::Shader) {
     ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
     EXPECT_THAT(getDiagnosticString(),
                 HasSubstr("is already a merge block for another header"));
@@ -1027,20 +1027,20 @@
   }
 }
 
-std::string GetUnreachableContinueWithBranchUse(SpvCapability cap) {
+std::string GetUnreachableContinueWithBranchUse(spv::Capability cap) {
   std::string header = GetDefaultHeader(cap);
 
   Block entry("entry");
-  Block branch("branch", SpvOpBranch);
-  Block merge("merge", SpvOpReturn);
-  Block target("target", SpvOpBranch);
+  Block branch("branch", spv::Op::OpBranch);
+  Block merge("merge", spv::Op::OpReturn);
+  Block target("target", spv::Op::OpBranch);
 
   target >> branch;
 
   entry.AppendBody("%placeholder   = OpVariable %intptrt Function\n");
 
   std::string str = header;
-  if (cap == SpvCapabilityShader)
+  if (cap == spv::Capability::Shader)
     branch.AppendBody("OpLoopMerge %merge %target None\n");
 
   str += nameOps("branch", "merge", "target", std::make_pair("func", "Main"));
@@ -1061,16 +1061,16 @@
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
-std::string GetReachableMergeAndContinue(SpvCapability cap) {
+std::string GetReachableMergeAndContinue(spv::Capability cap) {
   std::string header = GetDefaultHeader(cap);
 
   Block entry("entry");
-  Block branch("branch", SpvOpBranch);
-  Block merge("merge", SpvOpReturn);
-  Block target("target", SpvOpBranch);
-  Block body("body", SpvOpBranchConditional);
-  Block t("t", SpvOpBranch);
-  Block f("f", SpvOpBranch);
+  Block branch("branch", spv::Op::OpBranch);
+  Block merge("merge", spv::Op::OpReturn);
+  Block target("target", spv::Op::OpBranch);
+  Block body("body", spv::Op::OpBranchConditional);
+  Block t("t", spv::Op::OpBranch);
+  Block f("f", spv::Op::OpBranch);
 
   target >> branch;
   body.SetBody("%cond    = OpSLessThan %boolt %one %two\n");
@@ -1078,7 +1078,7 @@
   f >> target;
 
   std::string str = header;
-  if (cap == SpvCapabilityShader) {
+  if (cap == spv::Capability::Shader) {
     branch.AppendBody("OpLoopMerge %merge %target None\n");
     body.AppendBody("OpSelectionMerge %f None\n");
   }
@@ -1104,23 +1104,23 @@
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
-std::string GetUnreachableMergeAndContinue(SpvCapability cap) {
+std::string GetUnreachableMergeAndContinue(spv::Capability cap) {
   std::string header = GetDefaultHeader(cap);
 
   Block entry("entry");
-  Block branch("branch", SpvOpBranch);
-  Block merge("merge", SpvOpReturn);
-  Block target("target", SpvOpBranch);
-  Block body("body", SpvOpBranchConditional);
-  Block t("t", SpvOpReturn);
-  Block f("f", SpvOpReturn);
-  Block pre_target("pre_target", SpvOpBranch);
+  Block branch("branch", spv::Op::OpBranch);
+  Block merge("merge", spv::Op::OpReturn);
+  Block target("target", spv::Op::OpBranch);
+  Block body("body", spv::Op::OpBranchConditional);
+  Block t("t", spv::Op::OpReturn);
+  Block f("f", spv::Op::OpReturn);
+  Block pre_target("pre_target", spv::Op::OpBranch);
 
   target >> branch;
   body.SetBody("%cond    = OpSLessThan %boolt %one %two\n");
 
   std::string str = header;
-  if (cap == SpvCapabilityShader) {
+  if (cap == spv::Capability::Shader) {
     branch.AppendBody("OpLoopMerge %merge %target None\n");
     body.AppendBody("OpSelectionMerge %pre_target None\n");
   }
@@ -1147,12 +1147,12 @@
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
-std::string GetUnreachableBlock(SpvCapability cap) {
+std::string GetUnreachableBlock(spv::Capability cap) {
   std::string header = GetDefaultHeader(cap);
 
   Block entry("entry");
   Block unreachable("unreachable");
-  Block exit("exit", SpvOpReturn);
+  Block exit("exit", spv::Op::OpReturn);
 
   std::string str = header;
   str += nameOps("unreachable", "exit", std::make_pair("func", "Main"));
@@ -1171,18 +1171,18 @@
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
-std::string GetUnreachableBranch(SpvCapability cap) {
+std::string GetUnreachableBranch(spv::Capability cap) {
   std::string header = GetDefaultHeader(cap);
 
   Block entry("entry");
-  Block unreachable("unreachable", SpvOpBranchConditional);
+  Block unreachable("unreachable", spv::Op::OpBranchConditional);
   Block unreachablechildt("unreachablechildt");
   Block unreachablechildf("unreachablechildf");
   Block merge("merge");
-  Block exit("exit", SpvOpReturn);
+  Block exit("exit", spv::Op::OpReturn);
 
   unreachable.SetBody("%cond    = OpSLessThan %boolt %one %two\n");
-  if (cap == SpvCapabilityShader)
+  if (cap == spv::Capability::Shader)
     unreachable.AppendBody("OpSelectionMerge %merge None\n");
 
   std::string str = header;
@@ -1219,10 +1219,10 @@
 }
 
 TEST_P(ValidateCFG, SingleBlockLoop) {
-  bool is_shader = GetParam() == SpvCapabilityShader;
+  bool is_shader = GetParam() == spv::Capability::Shader;
   Block entry("entry");
-  Block loop("loop", SpvOpBranchConditional);
-  Block exit("exit", SpvOpReturn);
+  Block loop("loop", spv::Op::OpBranchConditional);
+  Block exit("exit", spv::Op::OpReturn);
 
   entry.SetBody("%cond    = OpSLessThan %boolt %one %two\n");
   if (is_shader) loop.AppendBody("OpLoopMerge %exit %loop None\n");
@@ -1240,15 +1240,15 @@
 }
 
 TEST_P(ValidateCFG, NestedLoops) {
-  bool is_shader = GetParam() == SpvCapabilityShader;
+  bool is_shader = GetParam() == spv::Capability::Shader;
   Block entry("entry");
   Block loop1("loop1");
   Block loop1_cont_break_block("loop1_cont_break_block",
-                               SpvOpBranchConditional);
-  Block loop2("loop2", SpvOpBranchConditional);
+                               spv::Op::OpBranchConditional);
+  Block loop2("loop2", spv::Op::OpBranchConditional);
   Block loop2_merge("loop2_merge");
   Block loop1_merge("loop1_merge");
-  Block exit("exit", SpvOpReturn);
+  Block exit("exit", spv::Op::OpReturn);
 
   entry.SetBody("%cond    = OpSLessThan %boolt %one %two\n");
   if (is_shader) {
@@ -1275,7 +1275,7 @@
 }
 
 TEST_P(ValidateCFG, NestedSelection) {
-  bool is_shader = GetParam() == SpvCapabilityShader;
+  bool is_shader = GetParam() == spv::Capability::Shader;
   Block entry("entry");
   const int N = 256;
   std::vector<Block> if_blocks;
@@ -1284,18 +1284,18 @@
 
   entry.SetBody("%cond    = OpSLessThan %boolt %one %two\n");
 
-  if_blocks.emplace_back("if0", SpvOpBranchConditional);
+  if_blocks.emplace_back("if0", spv::Op::OpBranchConditional);
 
   if (is_shader) if_blocks[0].SetBody("OpSelectionMerge %if_merge0 None\n");
-  merge_blocks.emplace_back("if_merge0", SpvOpReturn);
+  merge_blocks.emplace_back("if_merge0", spv::Op::OpReturn);
 
   for (int i = 1; i < N; i++) {
     std::stringstream ss;
     ss << i;
-    if_blocks.emplace_back("if" + ss.str(), SpvOpBranchConditional);
+    if_blocks.emplace_back("if" + ss.str(), spv::Op::OpBranchConditional);
     if (is_shader)
       if_blocks[i].SetBody("OpSelectionMerge %if_merge" + ss.str() + " None\n");
-    merge_blocks.emplace_back("if_merge" + ss.str(), SpvOpBranch);
+    merge_blocks.emplace_back("if_merge" + ss.str(), spv::Op::OpBranch);
   }
   std::string str = GetDefaultHeader(GetParam()) + std::string(types_consts()) +
                     "%func    = OpFunction %voidt None %funct\n";
@@ -1318,14 +1318,14 @@
 }
 
 TEST_P(ValidateCFG, BackEdgeBlockDoesntPostDominateContinueTargetBad) {
-  bool is_shader = GetParam() == SpvCapabilityShader;
+  bool is_shader = GetParam() == spv::Capability::Shader;
   Block entry("entry");
-  Block loop1("loop1", SpvOpBranchConditional);
-  Block loop2("loop2", SpvOpBranchConditional);
+  Block loop1("loop1", spv::Op::OpBranchConditional);
+  Block loop2("loop2", spv::Op::OpBranchConditional);
   Block loop2_merge("loop2_merge");
-  Block loop1_cont("loop1_cont", SpvOpBranchConditional);
+  Block loop1_cont("loop1_cont", spv::Op::OpBranchConditional);
   Block be_block("be_block");
-  Block exit("exit", SpvOpReturn);
+  Block exit("exit", spv::Op::OpReturn);
 
   entry.SetBody("%cond    = OpSLessThan %boolt %one %two\n");
   if (is_shader) {
@@ -1348,7 +1348,7 @@
   str += "OpFunctionEnd";
 
   CompileSuccessfully(str);
-  if (GetParam() == SpvCapabilityShader) {
+  if (GetParam() == spv::Capability::Shader) {
     ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
     EXPECT_THAT(
         getDiagnosticString(),
@@ -1363,12 +1363,12 @@
 }
 
 TEST_P(ValidateCFG, BranchingToNonLoopHeaderBlockBad) {
-  bool is_shader = GetParam() == SpvCapabilityShader;
+  bool is_shader = GetParam() == spv::Capability::Shader;
   Block entry("entry");
-  Block split("split", SpvOpBranchConditional);
+  Block split("split", spv::Op::OpBranchConditional);
   Block t("t");
   Block f("f");
-  Block exit("exit", SpvOpReturn);
+  Block exit("exit", spv::Op::OpReturn);
 
   entry.SetBody("%cond    = OpSLessThan %boolt %one %two\n");
   if (is_shader) split.SetBody("OpSelectionMerge %exit None\n");
@@ -1398,10 +1398,10 @@
 }
 
 TEST_P(ValidateCFG, BranchingToSameNonLoopHeaderBlockBad) {
-  bool is_shader = GetParam() == SpvCapabilityShader;
+  bool is_shader = GetParam() == spv::Capability::Shader;
   Block entry("entry");
-  Block split("split", SpvOpBranchConditional);
-  Block exit("exit", SpvOpReturn);
+  Block split("split", spv::Op::OpBranchConditional);
+  Block exit("exit", spv::Op::OpReturn);
 
   entry.SetBody("%cond    = OpSLessThan %boolt %one %two\n");
   if (is_shader) split.SetBody("OpSelectionMerge %exit None\n");
@@ -1429,12 +1429,12 @@
 }
 
 TEST_P(ValidateCFG, MultipleBackEdgeBlocksToLoopHeaderBad) {
-  bool is_shader = GetParam() == SpvCapabilityShader;
+  bool is_shader = GetParam() == spv::Capability::Shader;
   Block entry("entry");
-  Block loop("loop", SpvOpBranchConditional);
+  Block loop("loop", spv::Op::OpBranchConditional);
   Block back0("back0");
   Block back1("back1");
-  Block merge("merge", SpvOpReturn);
+  Block merge("merge", spv::Op::OpReturn);
 
   entry.SetBody("%cond    = OpSLessThan %boolt %one %two\n");
   if (is_shader) loop.SetBody("OpLoopMerge %merge %back0 None\n");
@@ -1465,13 +1465,13 @@
 }
 
 TEST_P(ValidateCFG, ContinueTargetMustBePostDominatedByBackEdge) {
-  bool is_shader = GetParam() == SpvCapabilityShader;
+  bool is_shader = GetParam() == spv::Capability::Shader;
   Block entry("entry");
-  Block loop("loop", SpvOpBranchConditional);
-  Block cheader("cheader", SpvOpBranchConditional);
+  Block loop("loop", spv::Op::OpBranchConditional);
+  Block cheader("cheader", spv::Op::OpBranchConditional);
   Block be_block("be_block");
-  Block merge("merge", SpvOpReturn);
-  Block exit("exit", SpvOpReturn);
+  Block merge("merge", spv::Op::OpReturn);
+  Block exit("exit", spv::Op::OpReturn);
 
   entry.SetBody("%cond    = OpSLessThan %boolt %one %two\n");
   if (is_shader) loop.SetBody("OpLoopMerge %merge %cheader None\n");
@@ -1504,11 +1504,11 @@
 }
 
 TEST_P(ValidateCFG, BranchOutOfConstructToMergeBad) {
-  bool is_shader = GetParam() == SpvCapabilityShader;
+  bool is_shader = GetParam() == spv::Capability::Shader;
   Block entry("entry");
-  Block loop("loop", SpvOpBranchConditional);
-  Block cont("cont", SpvOpBranchConditional);
-  Block merge("merge", SpvOpReturn);
+  Block loop("loop", spv::Op::OpBranchConditional);
+  Block cont("cont", spv::Op::OpBranchConditional);
+  Block merge("merge", spv::Op::OpReturn);
 
   entry.SetBody("%cond    = OpSLessThan %boolt %one %two\n");
   if (is_shader) loop.SetBody("OpLoopMerge %merge %loop None\n");
@@ -1539,12 +1539,12 @@
 }
 
 TEST_P(ValidateCFG, BranchOutOfConstructBad) {
-  bool is_shader = GetParam() == SpvCapabilityShader;
+  bool is_shader = GetParam() == spv::Capability::Shader;
   Block entry("entry");
-  Block loop("loop", SpvOpBranchConditional);
-  Block cont("cont", SpvOpBranchConditional);
+  Block loop("loop", spv::Op::OpBranchConditional);
+  Block cont("cont", spv::Op::OpBranchConditional);
   Block merge("merge");
-  Block exit("exit", SpvOpReturn);
+  Block exit("exit", spv::Op::OpReturn);
 
   entry.SetBody("%cond    = OpSLessThan %boolt %one %two\n");
   if (is_shader) loop.SetBody("OpLoopMerge %merge %loop None\n");
@@ -1575,12 +1575,12 @@
 }
 
 TEST_F(ValidateCFG, OpSwitchToUnreachableBlock) {
-  Block entry("entry", SpvOpSwitch);
+  Block entry("entry", spv::Op::OpSwitch);
   Block case0("case0");
   Block case1("case1");
   Block case2("case2");
-  Block def("default", SpvOpUnreachable);
-  Block phi("phi", SpvOpReturn);
+  Block def("default", spv::Op::OpUnreachable);
+  Block phi("phi", spv::Op::OpReturn);
 
   std::string str = R"(
 OpCapability Shader
@@ -1687,13 +1687,13 @@
   // The nested construct has an unreachable merge block.  In the
   // augmented CFG that merge block
   // we still determine that the
-  bool is_shader = GetParam() == SpvCapabilityShader;
-  Block entry("entry", SpvOpBranchConditional);
-  Block inner_head("inner_head", SpvOpBranchConditional);
-  Block inner_true("inner_true", SpvOpReturn);
-  Block inner_false("inner_false", SpvOpReturn);
+  bool is_shader = GetParam() == spv::Capability::Shader;
+  Block entry("entry", spv::Op::OpBranchConditional);
+  Block inner_head("inner_head", spv::Op::OpBranchConditional);
+  Block inner_true("inner_true", spv::Op::OpReturn);
+  Block inner_false("inner_false", spv::Op::OpReturn);
   Block inner_merge("inner_merge");
-  Block exit("exit", SpvOpReturn);
+  Block exit("exit", spv::Op::OpReturn);
 
   entry.SetBody("%cond    = OpSLessThan %boolt %one %two\n");
   if (is_shader) {
@@ -1721,13 +1721,13 @@
   // The continue construct cannot be the merge target of a nested selection
   // because the loop construct must contain "if_merge" because it contains
   // "if_head".
-  bool is_shader = GetParam() == SpvCapabilityShader;
+  bool is_shader = GetParam() == spv::Capability::Shader;
   Block entry("entry");
   Block loop("loop");
-  Block if_head("if_head", SpvOpBranchConditional);
+  Block if_head("if_head", spv::Op::OpBranchConditional);
   Block if_true("if_true");
-  Block if_merge("if_merge", SpvOpBranchConditional);
-  Block merge("merge", SpvOpReturn);
+  Block if_merge("if_merge", spv::Op::OpBranchConditional);
+  Block merge("merge", spv::Op::OpReturn);
 
   entry.SetBody("%cond    = OpSLessThan %boolt %one %two\n");
   if (is_shader) {
@@ -1765,11 +1765,11 @@
 TEST_P(ValidateCFG, SingleLatchBlockMultipleBranchesToLoopHeader) {
   // This test case ensures we allow both branches of a loop latch block
   // to go back to the loop header.  It still counts as a single back edge.
-  bool is_shader = GetParam() == SpvCapabilityShader;
+  bool is_shader = GetParam() == spv::Capability::Shader;
   Block entry("entry");
-  Block loop("loop", SpvOpBranchConditional);
-  Block latch("latch", SpvOpBranchConditional);
-  Block merge("merge", SpvOpReturn);
+  Block loop("loop", spv::Op::OpBranchConditional);
+  Block latch("latch", spv::Op::OpBranchConditional);
+  Block merge("merge", spv::Op::OpReturn);
 
   entry.SetBody("%cond    = OpSLessThan %boolt %one %two\n");
   if (is_shader) {
@@ -2062,6 +2062,106 @@
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
+TEST_F(ValidateCFG, OpSwitchTargetCannotBeOuterLoopMergeBlock) {
+  std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+
+%1 = OpTypeVoid
+%2 = OpTypeFunction %1
+%3 = OpTypeBool
+%4 = OpUndef %3
+%5 = OpTypeInt 32 0
+%6 = OpConstant %5 0
+
+%7 = OpFunction %1 None %2
+
+%8 = OpLabel
+OpBranch %9
+
+%9 = OpLabel
+OpLoopMerge %10 %11 None
+OpBranch %12
+
+%12 = OpLabel
+OpSelectionMerge %13 None
+OpSwitch %6 %13 0 %10 1 %14
+
+%14 = OpLabel
+OpBranch %13
+
+%13 = OpLabel
+OpBranch %11
+
+%11 = OpLabel
+OpBranch %9
+
+%10 = OpLabel
+OpReturn
+
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text);
+  ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Switch header '12[%12]' does not structurally dominate its case construct '10[%10]'\n"
+          "  %12 = OpLabel"));
+}
+
+TEST_F(ValidateCFG, OpSwitchTargetCannotBeOuterLoopContinueBlock) {
+  std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+
+%1 = OpTypeVoid
+%2 = OpTypeFunction %1
+%3 = OpTypeBool
+%4 = OpUndef %3
+%5 = OpTypeInt 32 0
+%6 = OpConstant %5 0
+
+%7 = OpFunction %1 None %2
+
+%8 = OpLabel
+OpBranch %9
+
+%9 = OpLabel
+OpLoopMerge %10 %11 None
+OpBranch %12
+
+%12 = OpLabel
+OpSelectionMerge %13 None
+OpSwitch %6 %13 0 %11 1 %14
+
+%14 = OpLabel
+OpBranch %13
+
+%13 = OpLabel
+OpBranch %11
+
+%11 = OpLabel
+OpBranch %9
+
+%10 = OpLabel
+OpReturn
+
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text);
+  ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Switch header '12[%12]' does not structurally dominate its case construct '11[%11]'\n"
+          "  %12 = OpLabel"));
+}
+
 TEST_F(ValidateCFG, WrongOperandList) {
   std::string text = R"(
 OpCapability Shader
@@ -3444,6 +3544,37 @@
   EXPECT_THAT(getDiagnosticString(), HasSubstr("Selection must be structured"));
 }
 
+TEST_F(ValidateCFG, LoopConditionalBranchWithoutExitBad) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%bool = OpTypeBool
+%undef = OpUndef %bool
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+OpBranch %loop
+%loop = OpLabel
+OpLoopMerge %exit %continue None
+OpBranchConditional %undef %then %else
+%then = OpLabel
+OpBranch %continue
+%else = OpLabel
+OpBranch %exit
+%continue = OpLabel
+OpBranch %loop
+%exit = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text);
+  EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(), HasSubstr("Selection must be structured"));
+}
+
 TEST_F(ValidateCFG, MissingMergeSwitchBad) {
   const std::string text = R"(
 OpCapability Shader
@@ -4601,6 +4732,77 @@
                 "does not structurally dominate the back-edge block '8[%8]'"));
 }
 
+TEST_F(ValidateCFG, BadLoop) {
+  const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical Simple
+OpEntryPoint Fragment %2 "           "
+OpExecutionMode %2 OriginUpperLeft
+OpName %49 "loop"
+%void = OpTypeVoid
+%12 = OpTypeFunction %void
+%2 = OpFunction %void None %12
+%33 = OpLabel
+OpBranch %49
+%50 = OpLabel
+OpBranch %49
+%49 = OpLabel
+OpLoopMerge %33 %50 Unroll
+OpBranch %49
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text);
+  EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Loop header '2[%loop]' is targeted by 2 back-edge "
+                        "blocks but the standard requires exactly one"));
+}
+
+TEST_F(ValidateCFG, BadSwitch) {
+  const std::string text = R"(
+               OpCapability StorageImageExtendedFormats
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %2 "blah" %58
+               OpExecutionMode %2 OriginUpperLeft
+               OpName %BAD "BAD"
+         %11 = OpTypeVoid
+         %12 = OpTypeFunction %11
+         %19 = OpTypeInt 32 1
+         %21 = OpConstant %19 555758549
+          %2 = OpFunction %11 None %12
+          %4 = OpLabel
+               OpBranch %33
+         %33 = OpLabel
+               OpLoopMerge %34 %35 None
+               OpBranch %55
+        %BAD = OpLabel
+               OpSelectionMerge %53 None
+               OpSwitch %21 %34 196153896 %53 20856160 %34 33570306 %34 593494531 %52
+         %55 = OpLabel
+               OpLoopMerge %52 %58 DontUnroll
+               OpBranch %35
+         %58 = OpLabel
+               OpSelectionMerge %58 None
+               OpSwitch %21 %52 178168 %55 608223677 %34 604111047 %34 -553516825 %34 -106432813 %BAD 6946864 %55 1257373689 %55 973090296 %35 -113180668 %55 537002232 %BAD 13762553 %BAD 1030172152 %35 -553516825 %55 -262137 %35 -1091822332 %BAD 131320 %52 131321 %35 131320 %52 131321 %35 -1091822332 %BAD
+         %53 = OpLabel
+               OpBranch %35
+         %52 = OpLabel
+               OpBranch %34
+         %35 = OpLabel
+               OpBranch %33
+         %34 = OpLabel
+               OpKill
+               OpFunctionEnd
+)";
+
+  CompileSuccessfully(text);
+  EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("exits the selection headed by <ID> '3[%BAD]', but not "
+                        "via a structured exit"));
+}
+
 }  // namespace
 }  // namespace val
 }  // namespace spvtools
diff --git a/test/val/val_composites_test.cpp b/test/val/val_composites_test.cpp
index 0fd1ed6..6e0d7c0 100644
--- a/test/val/val_composites_test.cpp
+++ b/test/val/val_composites_test.cpp
@@ -1486,8 +1486,7 @@
 }
 
 TEST_F(ValidateComposites, CoopMatConstantCompositeMismatchFail) {
-  const std::string body =
-      R"(
+  const std::string body = R"(
 OpCapability Shader
 OpCapability Float16
 OpCapability CooperativeMatrixNV
@@ -1525,8 +1524,7 @@
 }
 
 TEST_F(ValidateComposites, CoopMatCompositeConstructMismatchFail) {
-  const std::string body =
-      R"(
+  const std::string body = R"(
 OpCapability Shader
 OpCapability Float16
 OpCapability CooperativeMatrixNV
@@ -1562,6 +1560,86 @@
       HasSubstr("Expected Constituent type to be equal to the component type"));
 }
 
+TEST_F(ValidateComposites, CoopMatKHRConstantCompositeMismatchFail) {
+  const std::string body = R"(
+OpCapability Shader
+OpCapability Float16
+OpCapability CooperativeMatrixKHR
+OpExtension "SPV_KHR_cooperative_matrix"
+OpExtension "SPV_KHR_vulkan_memory_model"
+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
+
+%f16mat = OpTypeCooperativeMatrixKHR %f16 %subgroup %u32_16 %u32_16 %useA
+
+%f32_1 = OpConstant %f32 1
+
+%f16mat_1 = OpConstantComposite %f16mat %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> '12[%float_1]' type "
+          "does not match the Result Type <id> '11[%11]'s component type."));
+}
+
+TEST_F(ValidateComposites, CoopMatKHRCompositeConstructMismatchFail) {
+  const std::string body = R"(
+OpCapability Shader
+OpCapability Float16
+OpCapability CooperativeMatrixKHR
+OpExtension "SPV_KHR_cooperative_matrix"
+OpExtension "SPV_KHR_vulkan_memory_model"
+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
+
+%f16mat = OpTypeCooperativeMatrixKHR %f16 %subgroup %u32_16 %u32_16 %useA
+
+%f32_1 = OpConstant %f32 1
+
+%main = OpFunction %void None %func
+%main_entry = OpLabel
+
+%f16mat_1 = OpCompositeConstruct %f16mat %f32_1
+
+OpReturn
+OpFunctionEnd)";
+
+  CompileSuccessfully(body.c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Expected Constituent type to be equal to the component type"));
+}
+
 TEST_F(ValidateComposites, ExtractDynamicLabelIndex) {
   const std::string spirv = R"(
 OpCapability Shader
diff --git a/test/val/val_constants_test.cpp b/test/val/val_constants_test.cpp
index 301539d..4727877 100644
--- a/test/val/val_constants_test.cpp
+++ b/test/val/val_constants_test.cpp
@@ -478,6 +478,22 @@
                         "a null value"));
 }
 
+TEST_F(ValidateConstant, VectorMismatchedConstituents) {
+  std::string spirv = kShaderPreamble kBasicTypes R"(
+%int = OpTypeInt 32 1
+%int_0 = OpConstantNull %int
+%const_vector = OpConstantComposite %uint2 %uint_0 %int_0
+)";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "OpConstantComposite Constituent <id> '13[%13]'s type "
+          "does not match Result Type <id> '3[%v2uint]'s vector element type"));
+}
+
 }  // namespace
 }  // namespace val
 }  // namespace spvtools
diff --git a/test/val/val_conversion_test.cpp b/test/val/val_conversion_test.cpp
index 1f8c426..0128aa1 100644
--- a/test/val/val_conversion_test.cpp
+++ b/test/val/val_conversion_test.cpp
@@ -1149,8 +1149,7 @@
 }
 
 TEST_F(ValidateConversion, CoopMatConversionShapesMismatchPass) {
-  const std::string body =
-      R"(
+  const std::string body = R"(
 OpCapability Shader
 OpCapability Float16
 OpCapability Int16
@@ -1191,6 +1190,179 @@
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
+TEST_F(ValidateConversion, CoopMatKHRConversionSuccess) {
+  const std::string body = R"(
+OpCapability Shader
+OpCapability Float16
+OpCapability Int16
+OpCapability CooperativeMatrixKHR
+OpExtension "SPV_KHR_cooperative_matrix"
+OpExtension "SPV_KHR_vulkan_memory_model"
+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
+
+%f16mat = OpTypeCooperativeMatrixKHR %f16 %subgroup %u32_8 %u32_8 %use_A
+%f32mat = OpTypeCooperativeMatrixKHR %f32 %subgroup %u32_8 %u32_8 %use_A
+%u16mat = OpTypeCooperativeMatrixKHR %u16 %subgroup %u32_8 %u32_8 %use_A
+%u32mat = OpTypeCooperativeMatrixKHR %u32 %subgroup %u32_8 %u32_8 %use_A
+%s16mat = OpTypeCooperativeMatrixKHR %s16 %subgroup %u32_8 %u32_8 %use_A
+%s32mat = OpTypeCooperativeMatrixKHR %s32 %subgroup %u32_8 %u32_8 %use_A
+
+%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
+
+%f16mat_1 = OpConstantComposite %f16mat %f16_1
+%f32mat_1 = OpConstantComposite %f32mat %f32_1
+%u16mat_1 = OpConstantComposite %u16mat %u16_1
+%u32mat_1 = OpConstantComposite %u32mat %u32_1
+%s16mat_1 = OpConstantComposite %s16mat %s16_1
+%s32mat_1 = OpConstantComposite %s32mat %s32_1
+
+%main = OpFunction %void None %func
+%main_entry = OpLabel
+
+%val11 = OpConvertFToU %u16mat %f16mat_1
+%val12 = OpConvertFToU %u32mat %f16mat_1
+%val13 = OpConvertFToS %s16mat %f16mat_1
+%val14 = OpConvertFToS %s32mat %f16mat_1
+%val15 = OpFConvert %f32mat %f16mat_1
+
+%val21 = OpConvertFToU %u16mat %f32mat_1
+%val22 = OpConvertFToU %u32mat %f32mat_1
+%val23 = OpConvertFToS %s16mat %f32mat_1
+%val24 = OpConvertFToS %s32mat %f32mat_1
+%val25 = OpFConvert %f16mat %f32mat_1
+
+%val31 = OpConvertUToF %f16mat %u16mat_1
+%val32 = OpConvertUToF %f32mat %u16mat_1
+%val33 = OpUConvert %u32mat %u16mat_1
+%val34 = OpSConvert %s32mat %u16mat_1
+
+%val41 = OpConvertSToF %f16mat %s16mat_1
+%val42 = OpConvertSToF %f32mat %s16mat_1
+%val43 = OpUConvert %u32mat %s16mat_1
+%val44 = OpSConvert %s32mat %s16mat_1
+
+OpReturn
+OpFunctionEnd)";
+
+  CompileSuccessfully(body.c_str());
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateConversion, CoopMatKHRConversionUseMismatchFail) {
+  const std::string body = R"(
+OpCapability Shader
+OpCapability Float16
+OpCapability Int16
+OpCapability CooperativeMatrixKHR
+OpExtension "SPV_KHR_cooperative_matrix"
+OpExtension "SPV_KHR_vulkan_memory_model"
+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
+
+%f16mat = OpTypeCooperativeMatrixKHR %f16 %subgroup %u32_8 %u32_8 %use_A
+%f32mat = OpTypeCooperativeMatrixKHR %f32 %subgroup %u32_8 %u32_8 %use_B
+
+%f16_1 = OpConstant %f16 1
+
+%f16mat_1 = OpConstantComposite %f16mat %f16_1
+
+%main = OpFunction %void None %func
+%main_entry = OpLabel
+
+%val1 = OpFConvert %f32mat %f16mat_1
+
+OpReturn
+OpFunctionEnd)";
+
+  CompileSuccessfully(body.c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Expected Use of Matrix type and Result Type to be identical"));
+}
+
+TEST_F(ValidateConversion, CoopMatKHRConversionScopeMismatchFail) {
+  const std::string body = R"(
+OpCapability Shader
+OpCapability Float16
+OpCapability Int16
+OpCapability CooperativeMatrixKHR
+OpExtension "SPV_KHR_cooperative_matrix"
+OpExtension "SPV_KHR_vulkan_memory_model"
+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
+%workgroup = OpConstant %u32 2
+%use_A = OpConstant %u32 0
+
+%f16mat = OpTypeCooperativeMatrixKHR %f16 %subgroup %u32_8 %u32_8 %use_A
+%f32mat = OpTypeCooperativeMatrixKHR %f32 %workgroup %u32_8 %u32_8 %use_A
+
+%f16_1 = OpConstant %f16 1
+
+%f16mat_1 = OpConstantComposite %f16mat %f16_1
+
+%main = OpFunction %void None %func
+%main_entry = OpLabel
+
+%val1 = OpFConvert %f32mat %f16mat_1
+
+OpReturn
+OpFunctionEnd)";
+
+  CompileSuccessfully(body.c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Expected scopes of Matrix and Result Type to be identical"));
+}
+
 TEST_F(ValidateConversion, BitcastSuccess) {
   const std::string body = R"(
 %ptr = OpVariable %f32ptr_func Function
diff --git a/test/val/val_data_test.cpp b/test/val/val_data_test.cpp
index 6a7f243..349e5e9 100644
--- a/test/val/val_data_test.cpp
+++ b/test/val/val_data_test.cpp
@@ -14,7 +14,6 @@
 
 // Validation tests for Data Rules.
 
-#include <sstream>
 #include <string>
 #include <utility>
 
diff --git a/test/val/val_decoration_test.cpp b/test/val/val_decoration_test.cpp
index 28ee970..7febb69 100644
--- a/test/val/val_decoration_test.cpp
+++ b/test/val/val_decoration_test.cpp
@@ -19,7 +19,6 @@
 
 #include "gmock/gmock.h"
 #include "source/val/decoration.h"
-#include "test/test_fixture.h"
 #include "test/unit_spirv.h"
 #include "test/val/val_code_generator.h"
 #include "test/val/val_fixtures.h"
@@ -62,9 +61,10 @@
   CompileSuccessfully(spirv);
   EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
   // Must have 2 decorations.
-  EXPECT_THAT(vstate_->id_decorations(id),
-              Eq(std::set<Decoration>{Decoration(SpvDecorationLocation, {4}),
-                                      Decoration(SpvDecorationCentroid)}));
+  EXPECT_THAT(
+      vstate_->id_decorations(id),
+      Eq(std::set<Decoration>{Decoration(spv::Decoration::Location, {4}),
+                              Decoration(spv::Decoration::Centroid)}));
 }
 
 TEST_F(ValidateDecorations, ValidateOpMemberDecorateRegistration) {
@@ -89,15 +89,15 @@
   const uint32_t arr_id = 1;
   EXPECT_THAT(
       vstate_->id_decorations(arr_id),
-      Eq(std::set<Decoration>{Decoration(SpvDecorationArrayStride, {4})}));
+      Eq(std::set<Decoration>{Decoration(spv::Decoration::ArrayStride, {4})}));
 
   // The struct must have 3 decorations.
   const uint32_t struct_id = 2;
   EXPECT_THAT(
       vstate_->id_decorations(struct_id),
-      Eq(std::set<Decoration>{Decoration(SpvDecorationNonReadable, {}, 2),
-                              Decoration(SpvDecorationOffset, {2}, 2),
-                              Decoration(SpvDecorationBufferBlock)}));
+      Eq(std::set<Decoration>{Decoration(spv::Decoration::NonReadable, {}, 2),
+                              Decoration(spv::Decoration::Offset, {2}, 2),
+                              Decoration(spv::Decoration::BufferBlock)}));
 }
 
 TEST_F(ValidateDecorations, ValidateOpMemberDecorateOutOfBound) {
@@ -152,9 +152,9 @@
 
   // Decoration group has 3 decorations.
   auto expected_decorations =
-      std::set<Decoration>{Decoration(SpvDecorationDescriptorSet, {0}),
-                           Decoration(SpvDecorationRelaxedPrecision),
-                           Decoration(SpvDecorationRestrict)};
+      std::set<Decoration>{Decoration(spv::Decoration::DescriptorSet, {0}),
+                           Decoration(spv::Decoration::RelaxedPrecision),
+                           Decoration(spv::Decoration::Restrict)};
 
   // Decoration group is applied to id 1, 2, 3, and 4. Note that id 1 (which is
   // the decoration group id) also has all the decorations.
@@ -182,7 +182,7 @@
   EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
   // Decoration group has 1 decoration.
   auto expected_decorations =
-      std::set<Decoration>{Decoration(SpvDecorationOffset, {3}, 3)};
+      std::set<Decoration>{Decoration(spv::Decoration::Offset, {3}, 3)};
 
   // Decoration group is applied to id 2, 3, and 4.
   EXPECT_THAT(vstate_->id_decorations(2), Eq(expected_decorations));
@@ -1218,9 +1218,14 @@
   )";
 
   CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
+  EXPECT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "'GLSLShared' is not valid for the Vulkan execution environment"));
   EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("must not use GLSLShared decoration"));
+              HasSubstr("[VUID-StandaloneSpirv-GLSLShared-04669]"));
 }
 
 TEST_F(ValidateDecorations, BufferBlockGLSLSharedBad) {
@@ -1247,9 +1252,14 @@
   )";
 
   CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
+  EXPECT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "'GLSLShared' is not valid for the Vulkan execution environment"));
   EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("must not use GLSLShared decoration"));
+              HasSubstr("[VUID-StandaloneSpirv-GLSLShared-04669]"));
 }
 
 TEST_F(ValidateDecorations, BlockNestedStructGLSLSharedBad) {
@@ -1282,9 +1292,14 @@
   )";
 
   CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
+  EXPECT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "'GLSLShared' is not valid for the Vulkan execution environment"));
   EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("must not use GLSLShared decoration"));
+              HasSubstr("[VUID-StandaloneSpirv-GLSLShared-04669]"));
 }
 
 TEST_F(ValidateDecorations, BufferBlockNestedStructGLSLSharedBad) {
@@ -1317,9 +1332,14 @@
   )";
 
   CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
+  EXPECT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "'GLSLShared' is not valid for the Vulkan execution environment"));
   EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("must not use GLSLShared decoration"));
+              HasSubstr("[VUID-StandaloneSpirv-GLSLShared-04669]"));
 }
 
 TEST_F(ValidateDecorations, BlockGLSLPackedBad) {
@@ -1346,9 +1366,14 @@
   )";
 
   CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
+  EXPECT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "'GLSLPacked' is not valid for the Vulkan execution environment"));
   EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("must not use GLSLPacked decoration"));
+              HasSubstr("[VUID-StandaloneSpirv-GLSLShared-04669]"));
 }
 
 TEST_F(ValidateDecorations, BufferBlockGLSLPackedBad) {
@@ -1375,9 +1400,14 @@
   )";
 
   CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
+  EXPECT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "'GLSLPacked' is not valid for the Vulkan execution environment"));
   EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("must not use GLSLPacked decoration"));
+              HasSubstr("[VUID-StandaloneSpirv-GLSLShared-04669]"));
 }
 
 TEST_F(ValidateDecorations, BlockNestedStructGLSLPackedBad) {
@@ -1410,9 +1440,14 @@
   )";
 
   CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
+  EXPECT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "'GLSLPacked' is not valid for the Vulkan execution environment"));
   EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("must not use GLSLPacked decoration"));
+              HasSubstr("[VUID-StandaloneSpirv-GLSLShared-04669]"));
 }
 
 TEST_F(ValidateDecorations, BufferBlockNestedStructGLSLPackedBad) {
@@ -1445,9 +1480,14 @@
   )";
 
   CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
+  EXPECT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "'GLSLPacked' is not valid for the Vulkan execution environment"));
   EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("must not use GLSLPacked decoration"));
+              HasSubstr("[VUID-StandaloneSpirv-GLSLShared-04669]"));
 }
 
 TEST_F(ValidateDecorations, BlockMissingArrayStrideBad) {
@@ -1848,7 +1888,8 @@
   )";
 
   CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
+  EXPECT_EQ(SPV_SUCCESS,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
 }
 
 TEST_F(ValidateDecorations, BlockLayoutPermitsTightVec3ScalarPackingGood) {
@@ -1877,7 +1918,7 @@
   )";
 
   CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState())
+  EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0))
       << getDiagnosticString();
 }
 
@@ -2059,7 +2100,8 @@
   )";
 
   CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
+  EXPECT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr("Structure id 2 decorated as Block for variable in Uniform "
@@ -2467,7 +2509,8 @@
   )";
 
   CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
+  EXPECT_EQ(SPV_SUCCESS,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
 }
 
 TEST_F(ValidateDecorations, BlockArrayExtendedAlignmentGood) {
@@ -2531,7 +2574,8 @@
   )";
 
   CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
+  EXPECT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr(
@@ -2728,7 +2772,7 @@
   )";
 
   CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState())
+  EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0))
       << getDiagnosticString();
 }
 
@@ -2760,7 +2804,8 @@
   )";
 
   CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
+  EXPECT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr(
@@ -2794,7 +2839,7 @@
   )";
 
   CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState())
+  EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0))
       << getDiagnosticString();
 }
 
@@ -2823,7 +2868,8 @@
   )";
 
   CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
+  EXPECT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr(
@@ -3675,7 +3721,7 @@
   )";
 
   CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState())
+  EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0))
       << getDiagnosticString();
 }
 
@@ -3710,7 +3756,8 @@
   )";
 
   CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
+  EXPECT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr(
@@ -3777,7 +3824,8 @@
   )";
 
   CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
+  EXPECT_EQ(SPV_SUCCESS,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
 }
 
 TEST_F(ValidateDecorations,
@@ -3808,7 +3856,7 @@
   )";
 
   CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState())
+  EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0))
       << getDiagnosticString();
 }
 
@@ -3840,7 +3888,8 @@
   )";
 
   CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
+  EXPECT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr(
@@ -3908,7 +3957,8 @@
   )";
 
   CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
+  EXPECT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr("Structure id 6 decorated as Block for variable in Uniform "
@@ -3975,7 +4025,8 @@
   )";
 
   CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
+  EXPECT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr("Structure id 8 decorated as Block for variable in Uniform "
@@ -4041,7 +4092,8 @@
   )";
 
   CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
+  EXPECT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr(
@@ -4077,7 +4129,8 @@
   )";
 
   CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
+  EXPECT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr("Structure id 3 decorated as BufferBlock for variable in "
@@ -4116,7 +4169,8 @@
   )";
 
   CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
+  EXPECT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr(
@@ -4132,6 +4186,7 @@
                OpCapability Shader
                OpMemoryModel Logical GLSL450
                OpEntryPoint GLCompute %1 "main"
+               OpExecutionMode %1 LocalSize 1 1 1
                OpMemberDecorate %_struct_6 0 Offset 0
                OpMemberDecorate %_struct_2 0 Offset 0
                OpMemberDecorate %_struct_2 1 Offset 4
@@ -4150,7 +4205,8 @@
   )";
 
   CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
+  EXPECT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr(
@@ -4275,7 +4331,8 @@
   )";
 
   CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
+  EXPECT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr(
@@ -4405,7 +4462,7 @@
   )";
 
   CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState())
+  EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0))
       << getDiagnosticString();
 }
 
@@ -4449,7 +4506,8 @@
   )";
 
   CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
+  EXPECT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr(
@@ -4464,6 +4522,48 @@
   // #version 450
   // layout (set=0,binding=0) buffer S {
   //   uvec3 arr[2][2]; // first 3 elements are 16 bytes, last is 12
+  //   uint i;  // Can't have offset 60 = 3x16 + 12
+  // } B;
+  // void main() {}
+
+  std::string spirv = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %1 "main"
+               OpDecorate %_arr_v3uint_uint_2 ArrayStride 16
+               OpDecorate %_arr__arr_v3uint_uint_2_uint_2 ArrayStride 32
+               OpMemberDecorate %_struct_4 0 Offset 0
+               OpMemberDecorate %_struct_4 1 Offset 64
+               OpDecorate %_struct_4 BufferBlock
+               OpDecorate %5 DescriptorSet 0
+               OpDecorate %5 Binding 0
+       %void = OpTypeVoid
+          %7 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+     %v3uint = OpTypeVector %uint 3
+     %uint_2 = OpConstant %uint 2
+%_arr_v3uint_uint_2 = OpTypeArray %v3uint %uint_2
+%_arr__arr_v3uint_uint_2_uint_2 = OpTypeArray %_arr_v3uint_uint_2 %uint_2
+  %_struct_4 = OpTypeStruct %_arr__arr_v3uint_uint_2_uint_2 %uint
+%_ptr_Uniform__struct_4 = OpTypePointer Uniform %_struct_4
+          %5 = OpVariable %_ptr_Uniform__struct_4 Uniform
+          %1 = OpFunction %void None %7
+         %12 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateDecorations, StorageBufferArraySizeCalculationPackGoodScalar) {
+  // Original GLSL
+
+  // #version 450
+  // layout (set=0,binding=0) buffer S {
+  //   uvec3 arr[2][2]; // first 3 elements are 16 bytes, last is 12
   //   uint i;  // Can have offset 60 = 3x16 + 12
   // } B;
   // void main() {}
@@ -4495,8 +4595,10 @@
                OpFunctionEnd
   )";
 
+  options_->scalar_block_layout = true;
   CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
+  EXPECT_EQ(SPV_SUCCESS,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
 }
 
 TEST_F(ValidateDecorations, StorageBufferArraySizeCalculationPackBad) {
@@ -4509,7 +4611,7 @@
                OpDecorate %_arr_v3uint_uint_2 ArrayStride 16
                OpDecorate %_arr__arr_v3uint_uint_2_uint_2 ArrayStride 32
                OpMemberDecorate %_struct_4 0 Offset 0
-               OpMemberDecorate %_struct_4 1 Offset 56
+               OpMemberDecorate %_struct_4 1 Offset 60
                OpDecorate %_struct_4 BufferBlock
                OpDecorate %5 DescriptorSet 0
                OpDecorate %5 Binding 0
@@ -4530,12 +4632,13 @@
   )";
 
   CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
+  EXPECT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("Structure id 4 decorated as BufferBlock for variable "
                         "in Uniform storage class must follow standard storage "
-                        "buffer layout rules: member 1 at offset 56 overlaps "
-                        "previous member ending at offset 59"));
+                        "buffer layout rules: member 1 at offset 60 overlaps "
+                        "previous member ending at offset 63"));
 }
 
 TEST_F(ValidateDecorations, UniformBufferArraySizeCalculationPackGood) {
@@ -4570,7 +4673,8 @@
   )";
 
   CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
+  EXPECT_EQ(SPV_SUCCESS,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
 }
 
 TEST_F(ValidateDecorations, UniformBufferArraySizeCalculationPackBad) {
@@ -4604,7 +4708,8 @@
   )";
 
   CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
+  EXPECT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr(
@@ -5142,8 +5247,10 @@
 TEST_F(ValidateDecorations, RecurseThroughRuntimeArray) {
   const std::string spirv = R"(
 OpCapability Shader
-OpCapability Linkage
+OpExtension "SPV_KHR_storage_buffer_storage_class"
 OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
 OpDecorate %outer Block
 OpMemberDecorate %inner 0 Offset 0
 OpMemberDecorate %inner 1 Offset 1
@@ -5153,17 +5260,55 @@
 %inner = OpTypeStruct %int %int
 %runtime = OpTypeRuntimeArray %inner
 %outer = OpTypeStruct %runtime
-%outer_ptr = OpTypePointer Uniform %outer
-%var = OpVariable %outer_ptr Uniform
+%outer_ptr = OpTypePointer StorageBuffer %outer
+%var = OpVariable %outer_ptr StorageBuffer
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
 )";
 
   CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
   EXPECT_THAT(
       getDiagnosticString(),
-      HasSubstr("Structure id 2 decorated as Block for variable in Uniform "
-                "storage class must follow standard uniform buffer layout "
-                "rules: member 1 at offset 1 is not aligned to 4"));
+      HasSubstr(
+          "Structure id 3 decorated as Block for variable in StorageBuffer "
+          "storage class must follow standard storage buffer layout "
+          "rules: member 1 at offset 1 is not aligned to 4"));
+}
+
+TEST_F(ValidateDecorations, VulkanStructWithoutDecorationWithRuntimeArray) {
+  std::string str = R"(
+              OpCapability Shader
+              OpMemoryModel Logical GLSL450
+              OpEntryPoint Fragment %func "func"
+              OpExecutionMode %func OriginUpperLeft
+              OpDecorate %array_t ArrayStride 4
+              OpMemberDecorate %struct_t 0 Offset 0
+              OpMemberDecorate %struct_t 1 Offset 4
+     %uint_t = OpTypeInt 32 0
+   %array_t = OpTypeRuntimeArray %uint_t
+  %struct_t = OpTypeStruct %uint_t %array_t
+%struct_ptr = OpTypePointer StorageBuffer %struct_t
+         %2 = OpVariable %struct_ptr StorageBuffer
+      %void = OpTypeVoid
+    %func_t = OpTypeFunction %void
+      %func = OpFunction %void None %func_t
+         %1 = OpLabel
+              OpReturn
+              OpFunctionEnd
+)";
+
+  CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_1);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Vulkan, OpTypeStruct containing an OpTypeRuntimeArray "
+                        "must be decorated with Block or BufferBlock."));
 }
 
 TEST_F(ValidateDecorations, EmptyStructAtNonZeroOffsetGood) {
@@ -6885,6 +7030,8 @@
   CompileSuccessfully(spirv, env);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env));
   EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-Component-04924"));
+  EXPECT_THAT(getDiagnosticString(),
               HasSubstr("Component decoration specified for type"));
   EXPECT_THAT(getDiagnosticString(), HasSubstr("is not a scalar or vector"));
 }
@@ -6893,6 +7040,7 @@
                                           const std::string& decoration) {
   return R"(
 OpCapability Shader
+OpCapability Int64
 OpMemoryModel Logical GLSL450
 OpEntryPoint Fragment %main "main" %entryPointOutput
 OpExecutionMode %main OriginUpperLeft
@@ -6905,6 +7053,9 @@
 %v3float = OpTypeVector %float 3
 %v4float = OpTypeVector %float 4
 %uint = OpTypeInt 32 0
+%uint64 = OpTypeInt 64 0
+%v2uint64 = OpTypeVector %uint64 2
+%v3uint64 = OpTypeVector %uint64 3
 %uint_2 = OpConstant %uint 2
 %arr_v3float_uint_2 = OpTypeArray %v3float %uint_2
 %float_0 = OpConstant %float 0
@@ -6960,8 +7111,10 @@
   CompileSuccessfully(spirv, env);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env));
   EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Sequence of components starting with 4 "
-                        "and ending with 4 gets larger than 3"));
+              AnyVUID("VUID-StandaloneSpirv-Component-04920"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Component decoration value must not be greater than 3"));
 }
 
 TEST_F(ValidateDecorations, ComponentDecorationVector3GoodVulkan) {
@@ -6989,6 +7142,8 @@
   CompileSuccessfully(spirv, env);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env));
   EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-Component-04921"));
+  EXPECT_THAT(getDiagnosticString(),
               HasSubstr("Sequence of components starting with 1 "
                         "and ending with 4 gets larger than 3"));
 }
@@ -7000,6 +7155,8 @@
   CompileSuccessfully(spirv, env);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env));
   EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-Component-04921"));
+  EXPECT_THAT(getDiagnosticString(),
               HasSubstr("Sequence of components starting with 3 "
                         "and ending with 6 gets larger than 3"));
 }
@@ -7022,10 +7179,101 @@
   CompileSuccessfully(spirv, env);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env));
   EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-Component-04921"));
+  EXPECT_THAT(getDiagnosticString(),
               HasSubstr("Sequence of components starting with 2 "
                         "and ending with 4 gets larger than 3"));
 }
 
+TEST_F(ValidateDecorations, ComponentDecoration64ScalarGoodVulkan) {
+  const spv_target_env env = SPV_ENV_VULKAN_1_0;
+  std::string spirv = ShaderWithComponentDecoration("uint64", "Component 0");
+
+  CompileSuccessfully(spirv, env);
+  EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env));
+}
+
+TEST_F(ValidateDecorations, ComponentDecoration64Scalar1BadVulkan) {
+  const spv_target_env env = SPV_ENV_VULKAN_1_0;
+  std::string spirv = ShaderWithComponentDecoration("uint64", "Component 1");
+
+  CompileSuccessfully(spirv, env);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-Component-04923"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Component decoration value must not be 1 or 3 for "
+                        "64-bit data types"));
+}
+
+TEST_F(ValidateDecorations, ComponentDecoration64Scalar2GoodVulkan) {
+  const spv_target_env env = SPV_ENV_VULKAN_1_0;
+  std::string spirv = ShaderWithComponentDecoration("uint64", "Component 2");
+
+  CompileSuccessfully(spirv, env);
+  EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env));
+}
+
+TEST_F(ValidateDecorations, ComponentDecoration64Scalar3BadVulkan) {
+  const spv_target_env env = SPV_ENV_VULKAN_1_0;
+  std::string spirv = ShaderWithComponentDecoration("uint64", "Component 3");
+
+  CompileSuccessfully(spirv, env);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-Component-04923"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Component decoration value must not be 1 or 3 for "
+                        "64-bit data types"));
+}
+
+TEST_F(ValidateDecorations, ComponentDecoration64Vec0GoodVulkan) {
+  const spv_target_env env = SPV_ENV_VULKAN_1_0;
+  std::string spirv = ShaderWithComponentDecoration("v2uint64", "Component 0");
+
+  CompileSuccessfully(spirv, env);
+  EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env));
+}
+
+TEST_F(ValidateDecorations, ComponentDecoration64Vec1BadVulkan) {
+  const spv_target_env env = SPV_ENV_VULKAN_1_0;
+  std::string spirv = ShaderWithComponentDecoration("v2uint64", "Component 1");
+
+  CompileSuccessfully(spirv, env);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-Component-04923"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Component decoration value must not be 1 or 3 for "
+                        "64-bit data types"));
+}
+
+TEST_F(ValidateDecorations, ComponentDecoration64Vec2BadVulkan) {
+  const spv_target_env env = SPV_ENV_VULKAN_1_0;
+  std::string spirv = ShaderWithComponentDecoration("v2uint64", "Component 2");
+
+  CompileSuccessfully(spirv, env);
+  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");
+}
+
+TEST_F(ValidateDecorations, ComponentDecoration64VecWideBadVulkan) {
+  const spv_target_env env = SPV_ENV_VULKAN_1_0;
+  std::string spirv = ShaderWithComponentDecoration("v3uint64", "Component 0");
+
+  CompileSuccessfully(spirv, env);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-Component-07703"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Component decoration only allowed on 64-bit scalar "
+                        "and 2-component vector"));
+}
+
 TEST_F(ValidateDecorations, ComponentDecorationBlockGood) {
   std::string spirv = R"(
 OpCapability Shader
@@ -7097,6 +7345,8 @@
   CompileSuccessfully(spirv, env);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env));
   EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-Component-04921"));
+  EXPECT_THAT(getDiagnosticString(),
               HasSubstr("Sequence of components starting with 2 "
                         "and ending with 4 gets larger than 3"));
 }
@@ -7972,7 +8222,7 @@
 
   CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
   EXPECT_EQ(SPV_ERROR_INVALID_ID,
-            ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4));
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1_SPIRV_1_4));
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr("Block must be explicitly laid out with Offset decorations"));
@@ -8007,13 +8257,12 @@
 
   CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
   EXPECT_EQ(SPV_ERROR_INVALID_ID,
-            ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4));
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1_SPIRV_1_4));
   EXPECT_THAT(
       getDiagnosticString(),
-      HasSubstr(
-          "Block for variable in Workgroup storage class must follow "
-          "standard storage buffer layout rules: "
-          "member 0 at offset 1 is not aligned to 4"));
+      HasSubstr("Block for variable in Workgroup storage class must follow "
+                "relaxed storage buffer layout rules: "
+                "member 0 at offset 1 is not aligned to 4"));
 }
 
 TEST_F(ValidateDecorations, BadMatrixStrideUniform) {
@@ -8043,7 +8292,7 @@
 )";
 
   CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr(
@@ -8080,7 +8329,7 @@
 )";
 
   CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr(
@@ -8099,8 +8348,6 @@
 OpMemberDecorate %block 0 Offset 0
 OpMemberDecorate %block 0 MatrixStride 3
 OpMemberDecorate %block 0 ColMajor
-OpDecorate %var DescriptorSet 0
-OpDecorate %var Binding 0
 %void = OpTypeVoid
 %float = OpTypeFloat 32
 %float4 = OpTypeVector %float 4
@@ -8116,7 +8363,7 @@
 )";
 
   CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr(
@@ -8154,7 +8401,7 @@
 
   options_->scalar_block_layout = true;
   CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr(
@@ -8653,9 +8900,8 @@
               AnyVUID("VUID-StandaloneSpirv-Flat-06201"));
   EXPECT_THAT(
       getDiagnosticString(),
-      HasSubstr(
-          "OpEntryPoint interfaces variable must not be fragment execution "
-          "model with an output storage class for Entry Point id 2."));
+      HasSubstr("decorated variable must not be used in fragment execution "
+                "model as an Output storage class for Entry Point id 2."));
 }
 
 TEST_P(ValidateDecorationString, VulkanVertexInputInvalidInterface) {
@@ -8693,8 +8939,8 @@
               AnyVUID("VUID-StandaloneSpirv-Flat-06202"));
   EXPECT_THAT(
       getDiagnosticString(),
-      HasSubstr("OpEntryPoint interfaces variable must not be vertex execution "
-                "model with an input storage class for Entry Point id 2."));
+      HasSubstr("decorated variable must not be used in vertex execution model "
+                "as an Input storage class for Entry Point id 2."));
 }
 
 INSTANTIATE_TEST_SUITE_P(FragmentInputInterface, ValidateDecorationString,
@@ -9006,6 +9252,124 @@
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
 }
 
+TEST_F(ValidateDecorations, PhysicalStorageBufferWithOffset) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability Int64
+OpCapability PhysicalStorageBufferAddresses
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
+OpEntryPoint GLCompute %main "main" %pc
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %pc_block Block
+OpMemberDecorate %pc_block 0 Offset 0
+OpMemberDecorate %pssbo_struct 0 Offset 0
+%void = OpTypeVoid
+%long = OpTypeInt 64 0
+%float = OpTypeFloat 32
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%pc_block = OpTypeStruct %long
+%pc_block_ptr = OpTypePointer PushConstant %pc_block
+%pc_long_ptr = OpTypePointer PushConstant %long
+%pc = OpVariable %pc_block_ptr PushConstant
+%pssbo_struct = OpTypeStruct %float
+%pssbo_ptr = OpTypePointer PhysicalStorageBuffer %pssbo_struct
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%pc_gep = OpAccessChain %pc_long_ptr %pc %int_0
+%addr = OpLoad %long %pc_gep
+%ptr = OpConvertUToPtr %pssbo_ptr %addr
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+}
+
+TEST_F(ValidateDecorations, PhysicalStorageBufferMissingOffset) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability Int64
+OpCapability PhysicalStorageBufferAddresses
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
+OpEntryPoint GLCompute %main "main" %pc
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %pc_block Block
+OpMemberDecorate %pc_block 0 Offset 0
+%void = OpTypeVoid
+%long = OpTypeInt 64 0
+%float = OpTypeFloat 32
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%pc_block = OpTypeStruct %long
+%pc_block_ptr = OpTypePointer PushConstant %pc_block
+%pc_long_ptr = OpTypePointer PushConstant %long
+%pc = OpVariable %pc_block_ptr PushConstant
+%pssbo_struct = OpTypeStruct %float
+%pssbo_ptr = OpTypePointer PhysicalStorageBuffer %pssbo_struct
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%pc_gep = OpAccessChain %pc_long_ptr %pc %int_0
+%addr = OpLoad %long %pc_gep
+%ptr = OpConvertUToPtr %pssbo_ptr %addr
+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("decorated as Block for variable in PhysicalStorageBuffer "
+                "storage class must follow relaxed storage buffer layout "
+                "rules: member 0 is missing an Offset decoration"));
+}
+
+TEST_F(ValidateDecorations, PhysicalStorageBufferMissingArrayStride) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability Int64
+OpCapability PhysicalStorageBufferAddresses
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
+OpEntryPoint GLCompute %main "main" %pc
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %pc_block Block
+OpMemberDecorate %pc_block 0 Offset 0
+%void = OpTypeVoid
+%long = OpTypeInt 64 0
+%float = OpTypeFloat 32
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_4 = OpConstant %int 4
+%pc_block = OpTypeStruct %long
+%pc_block_ptr = OpTypePointer PushConstant %pc_block
+%pc_long_ptr = OpTypePointer PushConstant %long
+%pc = OpVariable %pc_block_ptr PushConstant
+%pssbo_array = OpTypeArray %float %int_4
+%pssbo_ptr = OpTypePointer PhysicalStorageBuffer %pssbo_array
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%pc_gep = OpAccessChain %pc_long_ptr %pc %int_0
+%addr = OpLoad %long %pc_gep
+%ptr = OpConvertUToPtr %pssbo_ptr %addr
+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(
+          "decorated as Block for variable in PhysicalStorageBuffer storage "
+          "class must follow relaxed storage buffer layout rules: member 0 "
+          "contains an array with stride 0, but with an element size of 4"));
+}
+
 }  // namespace
 }  // namespace val
 }  // namespace spvtools
diff --git a/test/val/val_ext_inst_test.cpp b/test/val/val_ext_inst_test.cpp
index e685acd..8f0bcce 100644
--- a/test/val/val_ext_inst_test.cpp
+++ b/test/val/val_ext_inst_test.cpp
@@ -6239,6 +6239,197 @@
               HasSubstr("Name must match an entry-point for Kernel"));
 }
 
+TEST_F(ValidateClspvReflection, KernelArgumentsVersionGood) {
+  const std::string text = R"(
+OpCapability Shader
+OpExtension "SPV_KHR_non_semantic_info"
+%ext = OpExtInstImport "NonSemantic.ClspvReflection.5"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %foo "foo"
+OpExecutionMode %foo LocalSize 1 1 1
+%foo_name = OpString "foo"
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%int = OpTypeInt 32 0
+%int_1 = OpConstant %int 1
+%foo = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+%decl = OpExtInst %void %ext Kernel %foo %foo_name %int_1
+)";
+
+  CompileSuccessfully(text);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateClspvReflection, KernelArgumentsVersionBad) {
+  const std::string text = R"(
+OpCapability Shader
+OpExtension "SPV_KHR_non_semantic_info"
+%ext = OpExtInstImport "NonSemantic.ClspvReflection.4"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %foo "foo"
+OpExecutionMode %foo LocalSize 1 1 1
+%foo_name = OpString "foo"
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%int = OpTypeInt 32 0
+%int_1 = OpConstant %int 1
+%foo = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+%decl = OpExtInst %void %ext Kernel %foo %foo_name %int_1
+)";
+
+  CompileSuccessfully(text);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Version 4 of the Kernel instruction can only have 2 "
+                        "additional operands"));
+}
+
+TEST_F(ValidateClspvReflection, KernelNumArgumentsNotInt) {
+  const std::string text = R"(
+OpCapability Shader
+OpExtension "SPV_KHR_non_semantic_info"
+%ext = OpExtInstImport "NonSemantic.ClspvReflection.5"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %foo "foo"
+OpExecutionMode %foo LocalSize 1 1 1
+%foo_name = OpString "foo"
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%float = OpTypeFloat 32
+%float_0 = OpConstant %float 0
+%foo = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+%decl = OpExtInst %void %ext Kernel %foo %foo_name %float_0
+)";
+
+  CompileSuccessfully(text);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("NumArguments must be a 32-bit unsigned integer OpConstant"));
+}
+
+TEST_F(ValidateClspvReflection, KernelNumArgumentsNotConstant) {
+  const std::string text = R"(
+OpCapability Shader
+OpExtension "SPV_KHR_non_semantic_info"
+%ext = OpExtInstImport "NonSemantic.ClspvReflection.5"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %foo "foo"
+OpExecutionMode %foo LocalSize 1 1 1
+%foo_name = OpString "foo"
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%null = OpConstantNull %int
+%foo = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+%decl = OpExtInst %void %ext Kernel %foo %foo_name %null
+)";
+
+  CompileSuccessfully(text);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("NumArguments must be a 32-bit unsigned integer OpConstant"));
+}
+
+TEST_F(ValidateClspvReflection, KernelFlagsNotInt) {
+  const std::string text = R"(
+OpCapability Shader
+OpExtension "SPV_KHR_non_semantic_info"
+%ext = OpExtInstImport "NonSemantic.ClspvReflection.5"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %foo "foo"
+OpExecutionMode %foo LocalSize 1 1 1
+%foo_name = OpString "foo"
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%float = OpTypeFloat 32
+%float_0 = OpConstant %float 0
+%foo = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+%decl = OpExtInst %void %ext Kernel %foo %foo_name %int_0 %float_0
+)";
+
+  CompileSuccessfully(text);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Flags must be a 32-bit unsigned integer OpConstant"));
+}
+
+TEST_F(ValidateClspvReflection, KernelFlagsNotConstant) {
+  const std::string text = R"(
+OpCapability Shader
+OpExtension "SPV_KHR_non_semantic_info"
+%ext = OpExtInstImport "NonSemantic.ClspvReflection.5"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %foo "foo"
+OpExecutionMode %foo LocalSize 1 1 1
+%foo_name = OpString "foo"
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%null = OpConstantNull %int
+%foo = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+%decl = OpExtInst %void %ext Kernel %foo %foo_name %int_0 %null
+)";
+
+  CompileSuccessfully(text);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Flags must be a 32-bit unsigned integer OpConstant"));
+}
+
+TEST_F(ValidateClspvReflection, KernelAttributesNotString) {
+  const std::string text = R"(
+OpCapability Shader
+OpExtension "SPV_KHR_non_semantic_info"
+%ext = OpExtInstImport "NonSemantic.ClspvReflection.5"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %foo "foo"
+OpExecutionMode %foo LocalSize 1 1 1
+%foo_name = OpString "foo"
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%float = OpTypeFloat 32
+%float_0 = OpConstant %float 0
+%foo = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+%decl = OpExtInst %void %ext Kernel %foo %foo_name %int_0 %int_0 %int_0
+)";
+
+  CompileSuccessfully(text);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Attributes must be an OpString"));
+}
+
 using ArgumentBasics =
     spvtest::ValidateBase<std::pair<std::string, std::string>>;
 
@@ -6254,7 +6445,11 @@
         std::make_pair("ArgumentSampledImage", "%int_0 %int_0"),
         std::make_pair("ArgumentStorageImage", "%int_0 %int_0"),
         std::make_pair("ArgumentSampler", "%int_0 %int_0"),
-        std::make_pair("ArgumentWorkgroup", "%int_0 %int_0")}));
+        std::make_pair("ArgumentWorkgroup", "%int_0 %int_0"),
+        std::make_pair("ArgumentPointerPushConstant", "%int_0 %int_4"),
+        std::make_pair("ArgumentPointerUniform", "%int_0 %int_0 %int_0 %int_4"),
+        std::make_pair("ArgumentStorageTexelBuffer", "%int_0 %int_0"),
+        std::make_pair("ArgumentUniformTexelBuffer", "%int_0 %int_0")}));
 
 TEST_P(ArgumentBasics, KernelNotAnExtendedInstruction) {
   const std::string ext_inst = std::get<0>(GetParam());
@@ -6262,7 +6457,7 @@
   const std::string text = R"(
 OpCapability Shader
 OpExtension "SPV_KHR_non_semantic_info"
-%ext = OpExtInstImport "NonSemantic.ClspvReflection.1"
+%ext = OpExtInstImport "NonSemantic.ClspvReflection.5"
 OpMemoryModel Logical GLSL450
 OpEntryPoint GLCompute %foo "foo"
 OpExecutionMode %foo LocalSize 1 1 1
@@ -6291,8 +6486,8 @@
   const std::string text = R"(
 OpCapability Shader
 OpExtension "SPV_KHR_non_semantic_info"
-%ext = OpExtInstImport "NonSemantic.ClspvReflection.1"
-%ext2 = OpExtInstImport "NonSemantic.ClspvReflection.1"
+%ext = OpExtInstImport "NonSemantic.ClspvReflection.5"
+%ext2 = OpExtInstImport "NonSemantic.ClspvReflection.5"
 OpMemoryModel Logical GLSL450
 OpEntryPoint GLCompute %foo "foo"
 OpExecutionMode %foo LocalSize 1 1 1
@@ -6323,7 +6518,7 @@
   const std::string text = R"(
 OpCapability Shader
 OpExtension "SPV_KHR_non_semantic_info"
-%ext = OpExtInstImport "NonSemantic.ClspvReflection.1"
+%ext = OpExtInstImport "NonSemantic.ClspvReflection.5"
 OpMemoryModel Logical GLSL450
 OpEntryPoint GLCompute %foo "foo"
 OpExecutionMode %foo LocalSize 1 1 1
@@ -6353,7 +6548,7 @@
   const std::string text = R"(
 OpCapability Shader
 OpExtension "SPV_KHR_non_semantic_info"
-%ext = OpExtInstImport "NonSemantic.ClspvReflection.1"
+%ext = OpExtInstImport "NonSemantic.ClspvReflection.5"
 OpMemoryModel Logical GLSL450
 OpEntryPoint GLCompute %foo "foo"
 OpExecutionMode %foo LocalSize 1 1 1
@@ -6383,7 +6578,7 @@
   const std::string text = R"(
 OpCapability Shader
 OpExtension "SPV_KHR_non_semantic_info"
-%ext = OpExtInstImport "NonSemantic.ClspvReflection.1"
+%ext = OpExtInstImport "NonSemantic.ClspvReflection.5"
 OpMemoryModel Logical GLSL450
 OpEntryPoint GLCompute %foo "foo"
 OpExecutionMode %foo LocalSize 1 1 1
@@ -6414,8 +6609,8 @@
   const std::string text = R"(
 OpCapability Shader
 OpExtension "SPV_KHR_non_semantic_info"
-%ext = OpExtInstImport "NonSemantic.ClspvReflection.1"
-%ext2 = OpExtInstImport "NonSemantic.ClspvReflection.1"
+%ext = OpExtInstImport "NonSemantic.ClspvReflection.5"
+%ext2 = OpExtInstImport "NonSemantic.ClspvReflection.5"
 OpMemoryModel Logical GLSL450
 OpEntryPoint GLCompute %foo "foo"
 OpExecutionMode %foo LocalSize 1 1 1
@@ -6659,7 +6854,269 @@
         std::make_pair(
             "PropertyRequiredWorkgroupSize %decl %int_1 %int_1 %float_0", "Z"),
         std::make_pair(
-            "PropertyRequiredWorkgroupSize %decl %int_1 %int_1 %null", "Z")}));
+            "PropertyRequiredWorkgroupSize %decl %int_1 %int_1 %null", "Z"),
+        std::make_pair("SpecConstantSubgroupMaxSize %float_0", "Size"),
+        std::make_pair("SpecConstantSubgroupMaxSize %null", "Size"),
+        std::make_pair(
+            "ArgumentPointerPushConstant %decl %float_0 %int_0 %int_0",
+            "Ordinal"),
+        std::make_pair("ArgumentPointerPushConstant %decl %null %int_0 %int_0",
+                       "Ordinal"),
+        std::make_pair(
+            "ArgumentPointerPushConstant %decl %int_0 %float_0 %int_0",
+            "Offset"),
+        std::make_pair("ArgumentPointerPushConstant %decl %int_0 %null %int_0",
+                       "Offset"),
+        std::make_pair(
+            "ArgumentPointerPushConstant %decl %int_0 %int_0 %float_0", "Size"),
+        std::make_pair("ArgumentPointerPushConstant %decl %int_0 %int_0 %null",
+                       "Size"),
+        std::make_pair(
+            "ArgumentPointerUniform %decl %float_0 %int_0 %int_0 %int_0 %int_4",
+            "Ordinal"),
+        std::make_pair(
+            "ArgumentPointerUniform %decl %null %int_0 %int_0 %int_0 %int_4",
+            "Ordinal"),
+        std::make_pair(
+            "ArgumentPointerUniform %decl %int_0 %float_0 %int_0 %int_0 %int_4",
+            "DescriptorSet"),
+        std::make_pair(
+            "ArgumentPointerUniform %decl %int_0 %null %int_0 %int_0 %int_4",
+            "DescriptorSet"),
+        std::make_pair(
+            "ArgumentPointerUniform %decl %int_0 %int_0 %float_0 %int_0 %int_4",
+            "Binding"),
+        std::make_pair(
+            "ArgumentPointerUniform %decl %int_0 %int_0 %null %int_0 %int_4",
+            "Binding"),
+        std::make_pair(
+            "ArgumentPointerUniform %decl %int_0 %int_0 %int_0 %float_0 %int_4",
+            "Offset"),
+        std::make_pair(
+            "ArgumentPointerUniform %decl %int_0 %int_0 %int_0 %null %int_4",
+            "Offset"),
+        std::make_pair(
+            "ArgumentPointerUniform %decl %int_0 %int_0 %int_0 %int_0 %float_0",
+            "Size"),
+        std::make_pair(
+            "ArgumentPointerUniform %decl %int_0 %int_0 %int_0 %int_0 %null",
+            "Size"),
+        std::make_pair(
+            "ProgramScopeVariablesStorageBuffer %float_0 %int_0 %data",
+            "DescriptorSet"),
+        std::make_pair("ProgramScopeVariablesStorageBuffer %null %int_0 %data",
+                       "DescriptorSet"),
+        std::make_pair(
+            "ProgramScopeVariablesStorageBuffer %int_0 %float_0 %data",
+            "Binding"),
+        std::make_pair("ProgramScopeVariablesStorageBuffer %int_0 %null %data",
+                       "Binding"),
+        std::make_pair(
+            "ProgramScopeVariablePointerRelocation %float_0 %int_0 %int_4",
+            "ObjectOffset"),
+        std::make_pair(
+            "ProgramScopeVariablePointerRelocation %null %int_0 %int_4",
+            "ObjectOffset"),
+        std::make_pair(
+            "ProgramScopeVariablePointerRelocation %int_0 %float_0 %int_4",
+            "PointerOffset"),
+        std::make_pair(
+            "ProgramScopeVariablePointerRelocation %int_0 %null %int_4",
+            "PointerOffset"),
+        std::make_pair(
+            "ProgramScopeVariablePointerRelocation %int_0 %int_0 %float_0",
+            "PointerSize"),
+        std::make_pair(
+            "ProgramScopeVariablePointerRelocation %int_0 %int_0 %null",
+            "PointerSize"),
+        std::make_pair("ImageArgumentInfoChannelOrderPushConstant %decl "
+                       "%float_0 %int_0 %int_4",
+                       "Ordinal"),
+        std::make_pair("ImageArgumentInfoChannelOrderPushConstant %decl %null "
+                       "%int_0 %int_4",
+                       "Ordinal"),
+        std::make_pair("ImageArgumentInfoChannelOrderPushConstant %decl %int_0 "
+                       "%float_0 %int_4",
+                       "Offset"),
+        std::make_pair("ImageArgumentInfoChannelOrderPushConstant %decl %int_0 "
+                       "%null %int_4",
+                       "Offset"),
+        std::make_pair("ImageArgumentInfoChannelOrderPushConstant %decl %int_0 "
+                       "%int_0 %float_0",
+                       "Size"),
+        std::make_pair("ImageArgumentInfoChannelOrderPushConstant %decl %int_0 "
+                       "%int_0 %null",
+                       "Size"),
+        std::make_pair("ImageArgumentInfoChannelDataTypePushConstant %decl "
+                       "%float_0 %int_0 %int_4",
+                       "Ordinal"),
+        std::make_pair("ImageArgumentInfoChannelDataTypePushConstant %decl "
+                       "%null %int_0 %int_4",
+                       "Ordinal"),
+        std::make_pair("ImageArgumentInfoChannelDataTypePushConstant %decl "
+                       "%int_0 %float_0 %int_4",
+                       "Offset"),
+        std::make_pair("ImageArgumentInfoChannelDataTypePushConstant %decl "
+                       "%int_0 %null %int_4",
+                       "Offset"),
+        std::make_pair("ImageArgumentInfoChannelDataTypePushConstant %decl "
+                       "%int_0 %int_0 %float_0",
+                       "Size"),
+        std::make_pair("ImageArgumentInfoChannelDataTypePushConstant %decl "
+                       "%int_0 %int_0 %null",
+                       "Size"),
+        std::make_pair("ImageArgumentInfoChannelOrderUniform %decl %float_0 "
+                       "%int_0 %int_0 %int_0 %int_4",
+                       "Ordinal"),
+        std::make_pair("ImageArgumentInfoChannelOrderUniform %decl %null "
+                       "%int_0 %int_0 %int_0 %int_4",
+                       "Ordinal"),
+        std::make_pair("ImageArgumentInfoChannelOrderUniform %decl %int_0 "
+                       "%float_0 %int_0 %int_0 %int_4",
+                       "DescriptorSet"),
+        std::make_pair("ImageArgumentInfoChannelOrderUniform %decl %int_0 "
+                       "%null %int_0 %int_0 %int_4",
+                       "DescriptorSet"),
+        std::make_pair("ImageArgumentInfoChannelOrderUniform %decl %int_0 "
+                       "%int_0 %float_0 %int_0 %int_4",
+                       "Binding"),
+        std::make_pair("ImageArgumentInfoChannelOrderUniform %decl %int_0 "
+                       "%int_0 %null %int_0 %int_4",
+                       "Binding"),
+        std::make_pair("ImageArgumentInfoChannelOrderUniform %decl %int_0 "
+                       "%int_0 %int_0 %float_0 %int_4",
+                       "Offset"),
+        std::make_pair("ImageArgumentInfoChannelOrderUniform %decl %int_0 "
+                       "%int_0 %int_0 %null %int_4",
+                       "Offset"),
+        std::make_pair("ImageArgumentInfoChannelOrderUniform %decl %int_0 "
+                       "%int_0 %int_0 %int_0 %float_0",
+                       "Size"),
+        std::make_pair("ImageArgumentInfoChannelOrderUniform %decl %int_0 "
+                       "%int_0 %int_0 %int_0 %null",
+                       "Size"),
+        std::make_pair("ImageArgumentInfoChannelDataTypeUniform %decl %float_0 "
+                       "%int_0 %int_0 %int_0 %int_4",
+                       "Ordinal"),
+        std::make_pair("ImageArgumentInfoChannelDataTypeUniform %decl %null "
+                       "%int_0 %int_0 %int_0 %int_4",
+                       "Ordinal"),
+        std::make_pair("ImageArgumentInfoChannelDataTypeUniform %decl %int_0 "
+                       "%float_0 %int_0 %int_0 %int_4",
+                       "DescriptorSet"),
+        std::make_pair("ImageArgumentInfoChannelDataTypeUniform %decl %int_0 "
+                       "%null %int_0 %int_0 %int_4",
+                       "DescriptorSet"),
+        std::make_pair("ImageArgumentInfoChannelDataTypeUniform %decl %int_0 "
+                       "%int_0 %float_0 %int_0 %int_4",
+                       "Binding"),
+        std::make_pair("ImageArgumentInfoChannelDataTypeUniform %decl %int_0 "
+                       "%int_0 %null %int_0 %int_4",
+                       "Binding"),
+        std::make_pair("ImageArgumentInfoChannelDataTypeUniform %decl %int_0 "
+                       "%int_0 %int_0 %float_0 %int_4",
+                       "Offset"),
+        std::make_pair("ImageArgumentInfoChannelDataTypeUniform %decl %int_0 "
+                       "%int_0 %int_0 %null %int_4",
+                       "Offset"),
+        std::make_pair("ImageArgumentInfoChannelDataTypeUniform %decl %int_0 "
+                       "%int_0 %int_0 %int_0 %float_0",
+                       "Size"),
+        std::make_pair("ImageArgumentInfoChannelDataTypeUniform %decl %int_0 "
+                       "%int_0 %int_0 %int_0 %null",
+                       "Size"),
+        std::make_pair(
+            "ArgumentStorageTexelBuffer %decl %float_0 %int_0 %int_0",
+            "Ordinal"),
+        std::make_pair("ArgumentStorageTexelBuffer %decl %null %int_0 %int_0",
+                       "Ordinal"),
+        std::make_pair(
+            "ArgumentStorageTexelBuffer %decl %int_0 %float_0 %int_0",
+            "DescriptorSet"),
+        std::make_pair("ArgumentStorageTexelBuffer %decl %int_0 %null %int_0",
+                       "DescriptorSet"),
+        std::make_pair(
+            "ArgumentStorageTexelBuffer %decl %int_0 %int_0 %float_0",
+            "Binding"),
+        std::make_pair("ArgumentStorageTexelBuffer %decl %int_0 %int_0 %null",
+                       "Binding"),
+        std::make_pair(
+            "ArgumentUniformTexelBuffer %decl %float_0 %int_0 %int_0",
+            "Ordinal"),
+        std::make_pair("ArgumentUniformTexelBuffer %decl %null %int_0 %int_0",
+                       "Ordinal"),
+        std::make_pair(
+            "ArgumentUniformTexelBuffer %decl %int_0 %float_0 %int_0",
+            "DescriptorSet"),
+        std::make_pair("ArgumentUniformTexelBuffer %decl %int_0 %null %int_0",
+                       "DescriptorSet"),
+        std::make_pair(
+            "ArgumentUniformTexelBuffer %decl %int_0 %int_0 %float_0",
+            "Binding"),
+        std::make_pair("ArgumentUniformTexelBuffer %decl %int_0 %int_0 %null",
+                       "Binding"),
+        std::make_pair("ConstantDataPointerPushConstant %float_0 %int_4 %data",
+                       "Offset"),
+        std::make_pair("ConstantDataPointerPushConstant %null %int_4 %data",
+                       "Offset"),
+        std::make_pair("ConstantDataPointerPushConstant %int_0 %float_0 %data",
+                       "Size"),
+        std::make_pair("ConstantDataPointerPushConstant %int_0 %null %data",
+                       "Size"),
+        std::make_pair(
+            "ProgramScopeVariablePointerPushConstant %float_0 %int_4 %data",
+            "Offset"),
+        std::make_pair(
+            "ProgramScopeVariablePointerPushConstant %null %int_4 %data",
+            "Offset"),
+        std::make_pair(
+            "ProgramScopeVariablePointerPushConstant %int_0 %float_0 %data",
+            "Size"),
+        std::make_pair(
+            "ProgramScopeVariablePointerPushConstant %int_0 %null %data",
+            "Size"),
+        std::make_pair("PrintfInfo %float_0 %data %int_0 %int_0 %int_0",
+                       "PrintfID"),
+        std::make_pair("PrintfInfo %null %data %int_0 %int_0 %int_0",
+                       "PrintfID"),
+        std::make_pair("PrintfInfo %int_0 %data %float_0 %int_0 %int_0",
+                       "ArgumentSizes"),
+        std::make_pair("PrintfInfo %int_0 %data %null %int_0 %int_0",
+                       "ArgumentSizes"),
+        std::make_pair("PrintfInfo %int_0 %data %int_0 %float_0 %int_0",
+                       "ArgumentSizes"),
+        std::make_pair("PrintfInfo %int_0 %data %int_0 %null %int_0",
+                       "ArgumentSizes"),
+        std::make_pair("PrintfInfo %int_0 %data %int_0 %int_0 %null",
+                       "ArgumentSizes"),
+        std::make_pair("PrintfInfo %int_0 %data %int_0 %int_0 %float_0",
+                       "ArgumentSizes"),
+        std::make_pair("PrintfInfo %int_0 %data %int_0 %float_0",
+                       "ArgumentSizes"),
+        std::make_pair("PrintfInfo %int_0 %data %int_0 %null", "ArgumentSizes"),
+        std::make_pair("PrintfBufferStorageBuffer %float_0 %int_0 %int_4",
+                       "DescriptorSet"),
+        std::make_pair("PrintfBufferStorageBuffer %null %int_0 %int_4",
+                       "DescriptorSet"),
+        std::make_pair("PrintfBufferStorageBuffer %int_0 %float_0 %int_4",
+                       "Binding"),
+        std::make_pair("PrintfBufferStorageBuffer %int_0 %null %int_4",
+                       "Binding"),
+        std::make_pair("PrintfBufferStorageBuffer %int_0 %int_0 %float_0",
+                       "Size"),
+        std::make_pair("PrintfBufferStorageBuffer %int_0 %int_0 %null", "Size"),
+        std::make_pair("PrintfBufferPointerPushConstant %float_0 %int_0 %int_4",
+                       "Offset"),
+        std::make_pair("PrintfBufferPointerPushConstant %null %int_0 %int_4",
+                       "Offset"),
+        std::make_pair("PrintfBufferPointerPushConstant %int_0 %float_0 %int_4",
+                       "Size"),
+        std::make_pair("PrintfBufferPointerPushConstant %int_0 %null %int_4",
+                       "Size"),
+        std::make_pair("PrintfBufferPointerPushConstant %int_0 %int_0 %float_0",
+                       "BufferSize"),
+        std::make_pair("PrintfBufferPointerPushConstant %int_0 %int_0 %null",
+                       "BufferSize")}));
 
 TEST_P(Uint32Constant, Invalid) {
   const std::string ext_inst = std::get<0>(GetParam());
@@ -6667,7 +7124,7 @@
   const std::string text = R"(
 OpCapability Shader
 OpExtension "SPV_KHR_non_semantic_info"
-%ext = OpExtInstImport "NonSemantic.ClspvReflection.1"
+%ext = OpExtInstImport "NonSemantic.ClspvReflection.5"
 OpMemoryModel Logical GLSL450
 OpEntryPoint GLCompute %foo "foo"
 OpExecutionMode %foo LocalSize 1 1 1
@@ -6705,7 +7162,15 @@
     ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
         std::make_pair("ConstantDataStorageBuffer %int_0 %int_0 %int_0",
                        "Data"),
-        std::make_pair("ConstantDataUniform %int_0 %int_0 %int_0", "Data")}));
+        std::make_pair("ConstantDataUniform %int_0 %int_0 %int_0", "Data"),
+        std::make_pair(
+            "ProgramScopeVariablesStorageBuffer %int_0 %int_0 %int_0", "Data"),
+        std::make_pair("ConstantDataPointerPushConstant %int_0 %int_0 %int_0",
+                       "Data"),
+        std::make_pair(
+            "ProgramScopeVariablePointerPushConstant %int_0 %int_0 %int_0",
+            "Data"),
+        std::make_pair("PrintfInfo %int_0 %int_0", "FormatString")}));
 
 TEST_P(StringOperand, Invalid) {
   const std::string ext_inst = std::get<0>(GetParam());
@@ -6713,6 +7178,106 @@
   const std::string text = R"(
 OpCapability Shader
 OpExtension "SPV_KHR_non_semantic_info"
+%ext = OpExtInstImport "NonSemantic.ClspvReflection.5"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %foo "foo"
+OpExecutionMode %foo LocalSize 1 1 1
+%foo_name = OpString "foo"
+%data = OpString "1234"
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%int_4 = OpConstant %int 4
+%null = OpConstantNull %int
+%float = OpTypeFloat 32
+%float_0 = OpConstant %float 0
+%void_fn = OpTypeFunction %void
+%foo = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+%decl = OpExtInst %void %ext Kernel %foo %foo_name
+%inst = OpExtInst %void %ext )" +
+                           ext_inst;
+
+  CompileSuccessfully(text);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(), HasSubstr(name + " must be an OpString"));
+}
+
+using VersionCheck = spvtest::ValidateBase<std::pair<std::string, uint32_t>>;
+
+INSTANTIATE_TEST_SUITE_P(
+    ValidateClspvReflectionVersionCheck, VersionCheck,
+    ::testing::ValuesIn(std::vector<std::pair<std::string, uint32_t>>{
+        std::make_pair("ArgumentStorageBuffer %decl %int_0 %int_0 %int_0", 1),
+        std::make_pair("ArgumentUniform %decl %int_0 %int_0 %int_0", 1),
+        std::make_pair(
+            "ArgumentPodStorageBuffer %decl %int_0 %int_0 %int_0 %int_0 %int_0",
+            1),
+        std::make_pair(
+            "ArgumentPodUniform %decl %int_0 %int_0 %int_0 %int_0 %int_0", 1),
+        std::make_pair("ArgumentPodPushConstant %decl %int_0 %int_0 %int_0", 1),
+        std::make_pair("ArgumentSampledImage %decl %int_0 %int_0 %int_0", 1),
+        std::make_pair("ArgumentStorageImage %decl %int_0 %int_0 %int_0", 1),
+        std::make_pair("ArgumentSampler %decl %int_0 %int_0 %int_0", 1),
+        std::make_pair("ArgumentWorkgroup %decl %int_0 %int_0 %int_0", 1),
+        std::make_pair("SpecConstantWorkgroupSize %int_0 %int_0 %int_0", 1),
+        std::make_pair("SpecConstantGlobalOffset %int_0 %int_0 %int_0", 1),
+        std::make_pair("SpecConstantWorkDim %int_0", 1),
+        std::make_pair("PushConstantGlobalOffset %int_0 %int_0", 1),
+        std::make_pair("PushConstantEnqueuedLocalSize %int_0 %int_0", 1),
+        std::make_pair("PushConstantGlobalSize %int_0 %int_0", 1),
+        std::make_pair("PushConstantRegionOffset %int_0 %int_0", 1),
+        std::make_pair("PushConstantNumWorkgroups %int_0 %int_0", 1),
+        std::make_pair("PushConstantRegionGroupOffset %int_0 %int_0", 1),
+        std::make_pair("ConstantDataStorageBuffer %int_0 %int_0 %data", 1),
+        std::make_pair("ConstantDataUniform %int_0 %int_0 %data", 1),
+        std::make_pair("LiteralSampler %int_0 %int_0 %int_0", 1),
+        std::make_pair(
+            "PropertyRequiredWorkgroupSize %decl %int_0 %int_0 %int_0", 1),
+        std::make_pair("SpecConstantSubgroupMaxSize %int_0", 2),
+        std::make_pair("ArgumentPointerPushConstant %decl %int_0 %int_0 %int_0",
+                       3),
+        std::make_pair(
+            "ArgumentPointerUniform %decl %int_0 %int_0 %int_0 %int_0 %int_0",
+            3),
+        std::make_pair("ProgramScopeVariablesStorageBuffer %int_0 %int_0 %data",
+                       3),
+        std::make_pair(
+            "ProgramScopeVariablePointerRelocation %int_0 %int_0 %int_0", 3),
+        std::make_pair("ImageArgumentInfoChannelOrderPushConstant %decl %int_0 "
+                       "%int_0 %int_0",
+                       3),
+        std::make_pair("ImageArgumentInfoChannelDataTypePushConstant %decl "
+                       "%int_0 %int_0 %int_0",
+                       3),
+        std::make_pair("ImageArgumentInfoChannelOrderUniform %decl %int_0 "
+                       "%int_0 %int_0 %int_0 %int_0",
+                       3),
+        std::make_pair("ImageArgumentInfoChannelDataTypeUniform %decl %int_0 "
+                       "%int_0 %int_0 %int_0 %int_0",
+                       3),
+        std::make_pair("ArgumentStorageTexelBuffer %decl %int_0 %int_0 %int_0",
+                       4),
+        std::make_pair("ArgumentUniformTexelBuffer %decl %int_0 %int_0 %int_0",
+                       4),
+        std::make_pair("ConstantDataPointerPushConstant %int_0 %int_0 %data",
+                       5),
+        std::make_pair(
+            "ProgramScopeVariablePointerPushConstant %int_0 %int_0 %data", 5),
+        std::make_pair("PrintfInfo %int_0 %data", 5),
+        std::make_pair("PrintfBufferStorageBuffer %int_0 %int_0 %int_0", 5),
+        std::make_pair("PrintfBufferPointerPushConstant %int_0 %int_0 %int_0",
+                       5)}));
+
+TEST_P(VersionCheck, V1) {
+  const std::string ext_inst = std::get<0>(GetParam());
+  const uint32_t version = std::get<1>(GetParam());
+  const std::string text = R"(
+OpCapability Shader
+OpExtension "SPV_KHR_non_semantic_info"
 %ext = OpExtInstImport "NonSemantic.ClspvReflection.1"
 OpMemoryModel Logical GLSL450
 OpEntryPoint GLCompute %foo "foo"
@@ -6737,8 +7302,174 @@
                            ext_inst;
 
   CompileSuccessfully(text);
-  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(), HasSubstr(name + " must be an OpString"));
+  if (version <= 1) {
+    EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+  } else {
+    EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+    EXPECT_THAT(getDiagnosticString(),
+                HasSubstr("requires version " + std::to_string(version) +
+                          ", but parsed version is 1"));
+  }
+}
+
+TEST_P(VersionCheck, V2) {
+  const std::string ext_inst = std::get<0>(GetParam());
+  const uint32_t version = std::get<1>(GetParam());
+  const std::string text = R"(
+OpCapability Shader
+OpExtension "SPV_KHR_non_semantic_info"
+%ext = OpExtInstImport "NonSemantic.ClspvReflection.2"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %foo "foo"
+OpExecutionMode %foo LocalSize 1 1 1
+%foo_name = OpString "foo"
+%data = OpString "1234"
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%int_4 = OpConstant %int 4
+%null = OpConstantNull %int
+%float = OpTypeFloat 32
+%float_0 = OpConstant %float 0
+%void_fn = OpTypeFunction %void
+%foo = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+%decl = OpExtInst %void %ext Kernel %foo %foo_name
+%inst = OpExtInst %void %ext )" +
+                           ext_inst;
+
+  CompileSuccessfully(text);
+  if (version <= 2) {
+    EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+  } else {
+    EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+    EXPECT_THAT(getDiagnosticString(),
+                HasSubstr("requires version " + std::to_string(version) +
+                          ", but parsed version is 2"));
+  }
+}
+
+TEST_P(VersionCheck, V3) {
+  const std::string ext_inst = std::get<0>(GetParam());
+  const uint32_t version = std::get<1>(GetParam());
+  const std::string text = R"(
+OpCapability Shader
+OpExtension "SPV_KHR_non_semantic_info"
+%ext = OpExtInstImport "NonSemantic.ClspvReflection.3"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %foo "foo"
+OpExecutionMode %foo LocalSize 1 1 1
+%foo_name = OpString "foo"
+%data = OpString "1234"
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%int_4 = OpConstant %int 4
+%null = OpConstantNull %int
+%float = OpTypeFloat 32
+%float_0 = OpConstant %float 0
+%void_fn = OpTypeFunction %void
+%foo = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+%decl = OpExtInst %void %ext Kernel %foo %foo_name
+%inst = OpExtInst %void %ext )" +
+                           ext_inst;
+
+  CompileSuccessfully(text);
+  if (version <= 3) {
+    EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+  } else {
+    EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+    EXPECT_THAT(getDiagnosticString(),
+                HasSubstr("requires version " + std::to_string(version) +
+                          ", but parsed version is 3"));
+  }
+}
+
+TEST_P(VersionCheck, V4) {
+  const std::string ext_inst = std::get<0>(GetParam());
+  const uint32_t version = std::get<1>(GetParam());
+  const std::string text = R"(
+OpCapability Shader
+OpExtension "SPV_KHR_non_semantic_info"
+%ext = OpExtInstImport "NonSemantic.ClspvReflection.4"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %foo "foo"
+OpExecutionMode %foo LocalSize 1 1 1
+%foo_name = OpString "foo"
+%data = OpString "1234"
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%int_4 = OpConstant %int 4
+%null = OpConstantNull %int
+%float = OpTypeFloat 32
+%float_0 = OpConstant %float 0
+%void_fn = OpTypeFunction %void
+%foo = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+%decl = OpExtInst %void %ext Kernel %foo %foo_name
+%inst = OpExtInst %void %ext )" +
+                           ext_inst;
+
+  CompileSuccessfully(text);
+  if (version <= 4) {
+    EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+  } else {
+    EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+    EXPECT_THAT(getDiagnosticString(),
+                HasSubstr("requires version " + std::to_string(version) +
+                          ", but parsed version is 4"));
+  }
+}
+
+TEST_P(VersionCheck, V5) {
+  const std::string ext_inst = std::get<0>(GetParam());
+  const uint32_t version = std::get<1>(GetParam());
+  const std::string text = R"(
+OpCapability Shader
+OpExtension "SPV_KHR_non_semantic_info"
+%ext = OpExtInstImport "NonSemantic.ClspvReflection.5"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %foo "foo"
+OpExecutionMode %foo LocalSize 1 1 1
+%foo_name = OpString "foo"
+%data = OpString "1234"
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%int_4 = OpConstant %int 4
+%null = OpConstantNull %int
+%float = OpTypeFloat 32
+%float_0 = OpConstant %float 0
+%void_fn = OpTypeFunction %void
+%foo = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+%decl = OpExtInst %void %ext Kernel %foo %foo_name
+%inst = OpExtInst %void %ext )" +
+                           ext_inst;
+
+  CompileSuccessfully(text);
+  if (version <= 5) {
+    EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+  } else {
+    EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+    EXPECT_THAT(getDiagnosticString(),
+                HasSubstr("requires version " + std::to_string(version) +
+                          ", but parsed version is 1"));
+  }
 }
 
 }  // namespace
diff --git a/test/val/val_extension_spv_khr_bit_instructions_test.cpp b/test/val/val_extension_spv_khr_bit_instructions_test.cpp
index 0e92671..d23b9b6 100644
--- a/test/val/val_extension_spv_khr_bit_instructions_test.cpp
+++ b/test/val/val_extension_spv_khr_bit_instructions_test.cpp
@@ -18,10 +18,7 @@
 #include <vector>
 
 #include "gmock/gmock.h"
-#include "source/enum_string_mapping.h"
-#include "source/extensions.h"
 #include "source/spirv_target_env.h"
-#include "test/test_fixture.h"
 #include "test/unit_spirv.h"
 #include "test/val/val_fixtures.h"
 
diff --git a/test/val/val_extension_spv_khr_expect_assume_test.cpp b/test/val/val_extension_spv_khr_expect_assume_test.cpp
index 6ece15d..85a484a 100644
--- a/test/val/val_extension_spv_khr_expect_assume_test.cpp
+++ b/test/val/val_extension_spv_khr_expect_assume_test.cpp
@@ -18,10 +18,7 @@
 #include <vector>
 
 #include "gmock/gmock.h"
-#include "source/enum_string_mapping.h"
-#include "source/extensions.h"
 #include "source/spirv_target_env.h"
-#include "test/test_fixture.h"
 #include "test/unit_spirv.h"
 #include "test/val/val_fixtures.h"
 
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 e0e6896..5b3a309 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
@@ -14,15 +14,12 @@
 // limitations under the License.
 
 #include <ostream>
-#include <sstream>
 #include <string>
 #include <vector>
 
 #include "gmock/gmock.h"
-#include "source/enum_string_mapping.h"
 #include "source/extensions.h"
 #include "source/spirv_target_env.h"
-#include "test/test_fixture.h"
 #include "test/unit_spirv.h"
 #include "test/val/val_fixtures.h"
 
@@ -131,7 +128,7 @@
          %char_0 = OpConstant %char 0
          %char_1 = OpConstant %char 1
 
-         %v4uchar_0 = OpConstantComposite %v4uchar %uchar_0 %uint_0 %uchar_0 %uchar_0
+         %v4uchar_0 = OpConstantComposite %v4uchar %uchar_0 %uchar_0 %uchar_0 %uchar_0
          %v4uchar_1 = OpConstantComposite %v4uchar %uchar_1 %uchar_1 %uchar_1 %uchar_1
          %v4char_0 = OpConstantComposite %v4char %char_0 %char_0 %char_0 %char_0
          %v4char_1 = OpConstantComposite %v4char %char_1 %char_1 %char_1 %char_1
diff --git a/test/val/val_extension_spv_khr_linkonce_odr_test.cpp b/test/val/val_extension_spv_khr_linkonce_odr_test.cpp
index ac15558..ed3fb8a 100644
--- a/test/val/val_extension_spv_khr_linkonce_odr_test.cpp
+++ b/test/val/val_extension_spv_khr_linkonce_odr_test.cpp
@@ -18,10 +18,7 @@
 #include <vector>
 
 #include "gmock/gmock.h"
-#include "source/enum_string_mapping.h"
-#include "source/extensions.h"
 #include "source/spirv_target_env.h"
-#include "test/test_fixture.h"
 #include "test/unit_spirv.h"
 #include "test/val/val_fixtures.h"
 
diff --git a/test/val/val_extension_spv_khr_subgroup_uniform_control_flow_test.cpp b/test/val/val_extension_spv_khr_subgroup_uniform_control_flow_test.cpp
index f528cb9..80d5753 100644
--- a/test/val/val_extension_spv_khr_subgroup_uniform_control_flow_test.cpp
+++ b/test/val/val_extension_spv_khr_subgroup_uniform_control_flow_test.cpp
@@ -18,10 +18,7 @@
 #include <vector>
 
 #include "gmock/gmock.h"
-#include "source/enum_string_mapping.h"
-#include "source/extensions.h"
 #include "source/spirv_target_env.h"
-#include "test/test_fixture.h"
 #include "test/unit_spirv.h"
 #include "test/val/val_fixtures.h"
 
diff --git a/test/val/val_extension_spv_khr_terminate_invocation_test.cpp b/test/val/val_extension_spv_khr_terminate_invocation_test.cpp
index 8d92414..4d3e4d6 100644
--- a/test/val/val_extension_spv_khr_terminate_invocation_test.cpp
+++ b/test/val/val_extension_spv_khr_terminate_invocation_test.cpp
@@ -18,10 +18,7 @@
 #include <vector>
 
 #include "gmock/gmock.h"
-#include "source/enum_string_mapping.h"
-#include "source/extensions.h"
 #include "source/spirv_target_env.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 491a808..0ab8c6e 100644
--- a/test/val/val_extensions_test.cpp
+++ b/test/val/val_extensions_test.cpp
@@ -18,10 +18,8 @@
 #include <vector>
 
 #include "gmock/gmock.h"
-#include "source/enum_string_mapping.h"
 #include "source/extensions.h"
 #include "source/spirv_target_env.h"
-#include "test/test_fixture.h"
 #include "test/unit_spirv.h"
 #include "test/val/val_fixtures.h"
 
diff --git a/test/val/val_function_test.cpp b/test/val/val_function_test.cpp
index e7d5cd7..24b5263 100644
--- a/test/val/val_function_test.cpp
+++ b/test/val/val_function_test.cpp
@@ -12,12 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <sstream>
 #include <string>
 #include <tuple>
 
 #include "gmock/gmock.h"
-#include "test/test_fixture.h"
 #include "test/unit_spirv.h"
 #include "test/val/val_fixtures.h"
 
diff --git a/test/val/val_id_test.cpp b/test/val/val_id_test.cpp
index dd040b3..7acac56 100644
--- a/test/val/val_id_test.cpp
+++ b/test/val/val_id_test.cpp
@@ -203,11 +203,12 @@
     // Parse 'num[%name]'
     size_t open_quote = message.find('\'', next);
 
-    // Copy up to the first quote
-    result.write(msg + next, open_quote - next);
     if (open_quote == std::string::npos) {
       break;
     }
+
+    // Copy up to the first quote
+    result.write(msg + next, open_quote - next);
     // Handle apostrophes
     if (!isdigit(message[open_quote + 1])) {
       result << '\'';
@@ -2316,7 +2317,7 @@
           "be used with non-externally visible shader Storage Classes: "
           "Workgroup, CrossWorkgroup, Private, Function, Input, Output, "
           "RayPayloadKHR, IncomingRayPayloadKHR, HitAttributeKHR, "
-          "CallableDataKHR, or IncomingCallableDataKHR")));
+          "CallableDataKHR, IncomingCallableDataKHR, or UniformConstant")));
 }
 
 TEST_P(ValidateIdWithMessage, OpVariableContainsBoolPrivateGood) {
@@ -2338,6 +2339,25 @@
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
+TEST_P(ValidateIdWithMessage, OpVariableContainsBoolUniformConstantGood) {
+  std::string spirv = kGLSL450MemoryModel + R"(
+%bool = OpTypeBool
+%int = OpTypeInt 32 0
+%block = OpTypeStruct %bool %int
+%_ptr_UniformConstant_block = OpTypePointer UniformConstant %block
+%var = OpVariable %_ptr_UniformConstant_block UniformConstant
+%void = OpTypeVoid
+%fnty = OpTypeFunction %void
+%main = OpFunction %void None %fnty
+%entry = OpLabel
+%load = OpLoad %block %var
+OpReturn
+OpFunctionEnd
+)";
+  CompileSuccessfully(spirv.c_str());
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
 TEST_P(ValidateIdWithMessage, OpVariableContainsBoolPointerGood) {
   std::string spirv = kGLSL450MemoryModel + R"(
 %bool = OpTypeBool
@@ -3981,8 +4001,11 @@
 TEST_P(AccessChainInstructionTest, AccessChainTooManyIndexesGood) {
   const std::string instr = GetParam();
   const std::string elem = AccessChainRequiresElemId(instr) ? " %int_0 " : "";
+  const std::string arrayStride =
+      " OpDecorate %_ptr_Uniform_deep_struct ArrayStride 8 ";
   int depth = 255;
-  std::string header = kGLSL450MemoryModel + kDeeplyNestedStructureSetup;
+  std::string header =
+      kGLSL450MemoryModel + arrayStride + kDeeplyNestedStructureSetup;
   header.erase(header.find("%func"));
   std::ostringstream spirv;
   spirv << header << "\n";
@@ -4044,8 +4067,11 @@
 TEST_P(AccessChainInstructionTest, CustomizedAccessChainTooManyIndexesGood) {
   const std::string instr = GetParam();
   const std::string elem = AccessChainRequiresElemId(instr) ? " %int_0 " : "";
+  const std::string arrayStride =
+      " OpDecorate %_ptr_Uniform_deep_struct ArrayStride 8 ";
   int depth = 10;
-  std::string header = kGLSL450MemoryModel + kDeeplyNestedStructureSetup;
+  std::string header =
+      kGLSL450MemoryModel + arrayStride + kDeeplyNestedStructureSetup;
   header.erase(header.find("%func"));
   std::ostringstream spirv;
   spirv << header << "\n";
@@ -4217,8 +4243,11 @@
   // 0 will select the element at the index 0 of the vector. (which is a float).
   const std::string instr = GetParam();
   const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
+  const std::string arrayStride =
+      " OpDecorate %_ptr_Uniform_blockName ArrayStride 8 ";
   std::ostringstream spirv;
-  spirv << kGLSL450MemoryModel << kDeeplyNestedStructureSetup << std::endl;
+  spirv << kGLSL450MemoryModel << arrayStride << kDeeplyNestedStructureSetup
+        << std::endl;
   spirv << "%ss = " << instr << " %_ptr_Uniform_struct_s %blockName_var "
         << elem << "%int_0" << std::endl;
   spirv << "%sa = " << instr << " %_ptr_Uniform_array5_mat4x3 %blockName_var "
@@ -4241,9 +4270,12 @@
 TEST_P(AccessChainInstructionTest, AccessChainIndexIntoRuntimeArrayGood) {
   const std::string instr = GetParam();
   const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
-  std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
-%runtime_arr_entry = )" +
-                      instr + R"( %_ptr_Uniform_float %blockName_var )" + elem +
+  const std::string arrayStride =
+      " OpDecorate %_ptr_Uniform_blockName ArrayStride 8 ";
+  std::string spirv = kGLSL450MemoryModel + arrayStride +
+                      kDeeplyNestedStructureSetup + R"(
+%runtime_arr_entry = )" + instr +
+                      R"( %_ptr_Uniform_float %blockName_var )" + elem +
                       R"(%int_2 %int_0
 OpReturn
 OpFunctionEnd
@@ -5631,6 +5663,7 @@
      OpExtension "SPV_KHR_variable_pointers"
      OpMemoryModel Logical GLSL450
      OpEntryPoint GLCompute %3 ""
+     OpDecorate %ptr ArrayStride 8
 %int = OpTypeInt 32 0
 %int_2 = OpConstant %int 2
 %int_4 = OpConstant %int 4
diff --git a/test/val/val_image_test.cpp b/test/val/val_image_test.cpp
index a97ef7c..9a70409 100644
--- a/test/val/val_image_test.cpp
+++ b/test/val/val_image_test.cpp
@@ -356,7 +356,8 @@
 
 std::string GenerateKernelCode(
     const std::string& body,
-    const std::string& capabilities_and_extensions = "") {
+    const std::string& capabilities_and_extensions = "",
+    const std::string& declarations = "") {
   std::ostringstream ss;
   ss << R"(
 OpCapability Addresses
@@ -436,7 +437,11 @@
 %type_sampler = OpTypeSampler
 %ptr_sampler = OpTypePointer UniformConstant %type_sampler
 %uniform_sampler = OpVariable %ptr_sampler UniformConstant
+)";
 
+  ss << declarations;
+
+  ss << R"(
 %main = OpFunction %void None %func
 %main_entry = OpLabel
 )";
@@ -480,10 +485,10 @@
 OpCapability Float64
 )";
 
-  ss << capabilities_and_extensions;
   if (!include_entry_point) {
-    ss << "OpCapability Linkage";
+    ss << "OpCapability Linkage\n";
   }
+  ss << capabilities_and_extensions;
 
   ss << R"(
 OpMemoryModel Logical GLSL450
@@ -781,6 +786,279 @@
               HasSubstr("Dim SubpassData requires Arrayed to be 0"));
 }
 
+TEST_F(ValidateImage, TypeImageWrongSampledTypeForTileImageDataEXT) {
+  const std::string code = GetShaderHeader(
+                               "OpCapability TileImageColorReadAccessEXT\n"
+                               "OpExtension \"SPV_EXT_shader_tile_image\"\n",
+                               false) +
+                           R"(
+%img_type = OpTypeImage %void TileImageDataEXT 0 0 0 2 Unknown
+)";
+
+  CompileSuccessfully(code.c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Dim TileImageDataEXT requires Sampled Type to be not OpTypeVoid"));
+}
+
+TEST_F(ValidateImage, TypeImageWrongSampledForTileImageDataEXT) {
+  const std::string code = GetShaderHeader(
+                               "OpCapability TileImageColorReadAccessEXT\n"
+                               "OpExtension \"SPV_EXT_shader_tile_image\"\n",
+                               false) +
+                           R"(
+%img_type = OpTypeImage %f32 TileImageDataEXT 0 0 0 1 Unknown
+)";
+
+  CompileSuccessfully(code.c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Dim TileImageDataEXT requires Sampled to be 2"));
+}
+
+TEST_F(ValidateImage, TypeImageWrongFormatForTileImageDataEXT) {
+  const std::string code = GetShaderHeader(
+                               "OpCapability TileImageColorReadAccessEXT\n"
+                               "OpExtension \"SPV_EXT_shader_tile_image\"\n",
+                               false) +
+                           R"(
+%img_type = OpTypeImage %f32 TileImageDataEXT 0 0 0 2 Rgba32f
+)";
+
+  CompileSuccessfully(code.c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Dim TileImageDataEXT requires format Unknown"));
+}
+
+TEST_F(ValidateImage, TypeImageWrongDepthForTileImageDataEXT) {
+  const std::string code = GetShaderHeader(
+                               "OpCapability TileImageColorReadAccessEXT\n"
+                               "OpExtension \"SPV_EXT_shader_tile_image\"\n",
+                               false) +
+                           R"(
+%img_type = OpTypeImage %f32 TileImageDataEXT 1 0 0 2 Unknown
+)";
+
+  CompileSuccessfully(code.c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Dim TileImageDataEXT requires Depth to be 0"));
+}
+
+TEST_F(ValidateImage, TypeImageWrongArrayedForTileImageDataEXT) {
+  const std::string code = GetShaderHeader(
+                               "OpCapability TileImageColorReadAccessEXT\n"
+                               "OpExtension \"SPV_EXT_shader_tile_image\"\n",
+                               false) +
+                           R"(
+%img_type = OpTypeImage %f32 TileImageDataEXT 0 1 0 2 Unknown
+)";
+
+  CompileSuccessfully(code.c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Dim TileImageDataEXT requires Arrayed to be 0"));
+}
+
+TEST_F(ValidateImage, TypeSampledImage_TileImageDataEXT_Error) {
+  const std::string code = GetShaderHeader(
+                               "OpCapability TileImageColorReadAccessEXT\n"
+                               "OpExtension \"SPV_EXT_shader_tile_image\"\n",
+                               false) +
+                           R"(
+%img_type = OpTypeImage %f32 TileImageDataEXT 0 0 0 2 Unknown
+%simg_type = OpTypeSampledImage %img_type
+)";
+
+  CompileSuccessfully(code.c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Sampled image type requires an image type with "
+                        "\"Sampled\" operand set to 0 or 1"));
+}
+
+TEST_F(ValidateImage, ImageTexelPointerImageDimTileImageDataEXTBad) {
+  const std::string body = R"(
+%texel_ptr = OpImageTexelPointer %ptr_Image_u32 %tile_image_u32_tid_0002 %u32_0 %u32_0
+%sum = OpAtomicIAdd %u32 %texel_ptr %u32_1 %u32_0 %u32_1
+)";
+  const std::string decl = R"(
+%type_image_u32_tid_0002 = OpTypeImage %u32 TileImageDataEXT 0 0 0 2 Unknown
+%ptr_image_u32_tid_0002 = OpTypePointer TileImageEXT %type_image_u32_tid_0002
+%tile_image_u32_tid_0002 = OpVariable %ptr_image_u32_tid_0002 TileImageEXT
+)";
+
+  const std::string extra = R"(
+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 TileImageDataEXT cannot be used with "
+                        "OpImageTexelPointer"));
+}
+
+TEST_F(ValidateImage, ReadTileImageDataEXT) {
+  const std::string body = R"(
+%img = OpLoad %type_image_f32_tid_0002 %uniform_image_f32_tid_0002
+%res1 = OpImageRead %f32vec4 %img %u32vec2_01
+)";
+
+  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("Image Dim TileImageDataEXT cannot be used with ImageRead"));
+}
+
+TEST_F(ValidateImage, WriteTileImageDataEXT) {
+  const std::string body = R"(
+%img = OpLoad %type_image_f32_tid_0002 %uniform_image_f32_tid_0002
+OpImageWrite %img %u32vec2_01 %f32vec4_0000
+)";
+
+  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 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' cannot be TileImageDataEXT"));
+}
+
+TEST_F(ValidateImage, QueryFormatTileImageDataEXT) {
+  const std::string body = R"(
+%img = OpLoad %type_image_f32_tid_0002 %uniform_image_f32_tid_0002
+%res1 = OpImageQueryFormat %u32 %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 TileImageColorReadAccessEXT
+OpExtension "SPV_EXT_shader_tile_image"
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body, extra, decl).c_str());
+
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Image 'Dim' cannot be TileImageDataEXT"));
+}
+
+TEST_F(ValidateImage, QueryOrderTileImageDataEXT) {
+  const std::string body = R"(
+%img = OpLoad %type_image_f32_tid_0002 %uniform_image_f32_tid_0002
+%res1 = OpImageQueryOrder %u32 %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 TileImageColorReadAccessEXT
+OpExtension "SPV_EXT_shader_tile_image"
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body, extra, decl).c_str());
+
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Image 'Dim' cannot be TileImageDataEXT"));
+}
+
+TEST_F(ValidateImage, SparseFetchTileImageDataEXT) {
+  const std::string body = R"(
+%img = OpLoad %type_image_f32_tid_0002 %uniform_image_f32_tid_0002
+%res1 = OpImageSparseFetch %struct_u32_f32vec4 %img %u32vec2_01
+)";
+
+  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' parameter to be 1"));
+}
+
+TEST_F(ValidateImage, SparseReadTileImageDataEXT) {
+  const std::string body = R"(
+%img = OpLoad %type_image_f32_tid_0002 %uniform_image_f32_tid_0002
+%res1 = OpImageSparseRead %struct_u32_f32vec4 %img %u32vec2_01
+)";
+
+  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(
+          "Image Dim TileImageDataEXT cannot be used with ImageSparseRead"));
+}
+
 TEST_F(ValidateImage, TypeImage_OpenCL_Sampled0_OK) {
   const std::string code = GetKernelHeader() + R"(
 %img_type = OpTypeImage %void 2D 0 0 0 0 Unknown ReadOnly
@@ -1899,8 +2177,6 @@
   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-04662"));
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr("Image Operands Offset, ConstOffset, ConstOffsets, Offsets "
@@ -5576,10 +5852,12 @@
 %res1 = OpImageRead %u32vec4 %img %u32vec2_01 SignExtend
 )";
 
-  EXPECT_THAT(CompileFailure(GenerateShaderCode(body, "", "Fragment", "",
-                                                SPV_ENV_UNIVERSAL_1_3),
-                             SPV_ENV_UNIVERSAL_1_3, SPV_ERROR_WRONG_VERSION),
-              HasSubstr("Invalid image operand 'SignExtend'"));
+  CompileSuccessfully(
+      GenerateShaderCode(body, "", "Fragment", "", SPV_ENV_UNIVERSAL_1_3));
+  ASSERT_EQ(SPV_ERROR_WRONG_VERSION, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("SignExtend(4096) requires SPIR-V version 1.4 or later"));
 }
 
 TEST_F(ValidateImage, ZeroExtendV13Bad) {
@@ -5588,10 +5866,12 @@
 %res1 = OpImageRead %u32vec4 %img %u32vec2_01 ZeroExtend
 )";
 
-  EXPECT_THAT(CompileFailure(GenerateShaderCode(body, "", "Fragment", "",
-                                                SPV_ENV_UNIVERSAL_1_3),
-                             SPV_ENV_UNIVERSAL_1_3, SPV_ERROR_WRONG_VERSION),
-              HasSubstr("Invalid image operand 'ZeroExtend'"));
+  CompileSuccessfully(
+      GenerateShaderCode(body, "", "Fragment", "", SPV_ENV_UNIVERSAL_1_3));
+  ASSERT_EQ(SPV_ERROR_WRONG_VERSION, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("ZeroExtend(8192) requires SPIR-V version 1.4 or later"));
 }
 
 TEST_F(ValidateImage, SignExtendScalarUIntTexelV14Good) {
@@ -6397,6 +6677,1375 @@
       HasSubstr("OpSamplerImageAddressingModeNV bitwidth should be 64 or 32"));
 }
 
+TEST_F(ValidateImage, QCOMImageProcessingBlockMatchSADNoDecorationA) {
+  std::string text = R"(
+           OpCapability Shader
+           OpCapability TextureBlockMatchQCOM
+           OpExtension "SPV_QCOM_image_processing"
+      %1 = OpExtInstImport "GLSL.std.450"
+           OpMemoryModel Logical GLSL450
+           OpEntryPoint Fragment %2 "main" %3 %4 %5 %6
+           OpExecutionMode %2 OriginUpperLeft
+           OpDecorate %3 Location 0
+           OpDecorate %4 DescriptorSet 0
+           OpDecorate %4 Binding 1
+           OpDecorate %5 DescriptorSet 0
+           OpDecorate %5 Binding 3
+           OpDecorate %6 DescriptorSet 0
+           OpDecorate %6 Binding 2
+           OpDecorate %6 BlockMatchTextureQCOM
+   %void = OpTypeVoid
+      %8 = 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_float = OpTypePointer Input %float
+%_ptr_Function_uint = OpTypePointer Function %uint
+%uint_4 = OpConstant %uint 4
+    %17 = OpConstantComposite %v2uint %uint_4 %uint_4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+     %3 = OpVariable %_ptr_Output_v4float Output
+    %19 = OpTypeImage %float 2D 0 0 0 1 Unknown
+%_ptr_UniformConstant_19 = OpTypePointer UniformConstant %19
+     %4 = OpVariable %_ptr_UniformConstant_19 UniformConstant
+    %21 = OpTypeSampler
+%_ptr_UniformConstant_21 = OpTypePointer UniformConstant %21
+    %5 = OpVariable %_ptr_UniformConstant_21 UniformConstant
+   %23 = OpTypeSampledImage %19
+    %6 = OpVariable %_ptr_UniformConstant_19 UniformConstant
+    %2 = OpFunction %void None %8
+   %24 = OpLabel
+   %25 = OpVariable %_ptr_Function_v2uint Function
+   %26 = OpLoad %19 %4
+   %27 = OpLoad %21 %5
+   %28 = OpSampledImage %23 %26 %27
+   %29 = OpLoad %v2uint %25
+   %30 = OpLoad %19 %6
+   %31 = OpLoad %21 %5
+   %32 = OpSampledImage %23 %30 %31
+   %33 = OpImageBlockMatchSADQCOM %v4float %28 %29 %32 %29 %29
+         OpStore %3 %33
+         OpReturn
+         OpFunctionEnd
+)";
+  CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
+  EXPECT_THAT(getDiagnosticString(), HasSubstr("Missing decoration"));
+}
+
+TEST_F(ValidateImage, QCOMImageProcessingBlockMatchSADNoDecorationB) {
+  std::string text = R"(
+           OpCapability Shader
+           OpCapability TextureBlockMatchQCOM
+           OpExtension "SPV_QCOM_image_processing"
+      %1 = OpExtInstImport "GLSL.std.450"
+           OpMemoryModel Logical GLSL450
+           OpEntryPoint Fragment %2 "main" %3 %4 %5 %6
+           OpExecutionMode %2 OriginUpperLeft
+           OpDecorate %3 Location 0
+           OpDecorate %4 DescriptorSet 0
+           OpDecorate %4 Binding 1
+           OpDecorate %5 DescriptorSet 0
+           OpDecorate %5 Binding 3
+           OpDecorate %5 BlockMatchTextureQCOM
+           OpDecorate %6 DescriptorSet 0
+           OpDecorate %6 Binding 2
+   %void = OpTypeVoid
+      %8 = 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_float = OpTypePointer Input %float
+%_ptr_Function_uint = OpTypePointer Function %uint
+%uint_4 = OpConstant %uint 4
+    %17 = OpConstantComposite %v2uint %uint_4 %uint_4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+     %3 = OpVariable %_ptr_Output_v4float Output
+    %19 = OpTypeImage %float 2D 0 0 0 1 Unknown
+%_ptr_UniformConstant_19 = OpTypePointer UniformConstant %19
+     %4 = OpVariable %_ptr_UniformConstant_19 UniformConstant
+    %21 = OpTypeSampler
+%_ptr_UniformConstant_21 = OpTypePointer UniformConstant %21
+    %5 = OpVariable %_ptr_UniformConstant_21 UniformConstant
+   %23 = OpTypeSampledImage %19
+    %6 = OpVariable %_ptr_UniformConstant_19 UniformConstant
+    %2 = OpFunction %void None %8
+   %24 = OpLabel
+   %25 = OpVariable %_ptr_Function_v2uint Function
+   %26 = OpLoad %19 %4
+   %27 = OpLoad %21 %5
+   %28 = OpSampledImage %23 %26 %27
+   %29 = OpLoad %v2uint %25
+   %30 = OpLoad %19 %6
+   %31 = OpLoad %21 %5
+   %32 = OpSampledImage %23 %30 %31
+   %33 = OpImageBlockMatchSADQCOM %v4float %28 %29 %32 %29 %29
+         OpStore %3 %33
+         OpReturn
+         OpFunctionEnd
+)";
+  CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
+  EXPECT_THAT(getDiagnosticString(), HasSubstr("Missing decoration"));
+}
+
+TEST_F(ValidateImage, QCOMImageProcessingBlockMatchSADNoDecorationC) {
+  std::string text = R"(
+               OpCapability Shader
+               OpCapability TextureBlockMatchQCOM
+               OpExtension "SPV_QCOM_image_processing"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %2 "main" %3 %4 %5
+               OpExecutionMode %2 OriginUpperLeft
+               OpDecorate %3 Location 0
+               OpDecorate %4 DescriptorSet 0
+               OpDecorate %4 Binding 4
+               OpDecorate %5 DescriptorSet 0
+               OpDecorate %5 Binding 5
+               OpDecorate %5 BlockMatchTextureQCOM
+       %void = OpTypeVoid
+          %7 = 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
+%_ptr_Input_float = OpTypePointer Input %float
+%_ptr_Function_uint = OpTypePointer Function %uint
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+          %3 = OpVariable %_ptr_Output_v4float Output
+         %18 = OpTypeImage %float 2D 0 0 0 1 Unknown
+         %19 = OpTypeSampledImage %18
+%_ptr_UniformConstant_19 = OpTypePointer UniformConstant %19
+          %4 = OpVariable %_ptr_UniformConstant_19 UniformConstant
+          %5 = OpVariable %_ptr_UniformConstant_19 UniformConstant
+         %21 = OpTypeImage %float 2D 0 1 0 1 Unknown
+          %2 = OpFunction %void None %7
+         %22 = OpLabel
+         %23 = OpVariable %_ptr_Function_v2uint Function
+         %24 = OpLoad %19 %4
+         %25 = OpLoad %v2uint %23
+         %26 = OpLoad %19 %5
+         %27 = OpLoad %v2uint %23
+         %28 = OpLoad %v2uint %23
+         %29 = OpImageBlockMatchSADQCOM %v4float %24 %25 %26 %27 %28
+               OpStore %3 %29
+               OpReturn
+               OpFunctionEnd
+)";
+  CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
+  EXPECT_THAT(getDiagnosticString(), HasSubstr("Missing decoration"));
+}
+
+TEST_F(ValidateImage, QCOMImageProcessingBlockMatchSADNoDecorationD) {
+  std::string text = R"(
+               OpCapability Shader
+               OpCapability TextureBlockMatchQCOM
+               OpExtension "SPV_QCOM_image_processing"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %2 "main" %3 %4 %5
+               OpExecutionMode %2 OriginUpperLeft
+               OpDecorate %3 Location 0
+               OpDecorate %4 DescriptorSet 0
+               OpDecorate %4 Binding 4
+               OpDecorate %4 BlockMatchTextureQCOM
+               OpDecorate %5 DescriptorSet 0
+               OpDecorate %5 Binding 5
+       %void = OpTypeVoid
+          %7 = 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
+%_ptr_Input_float = OpTypePointer Input %float
+%_ptr_Function_uint = OpTypePointer Function %uint
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+          %3 = OpVariable %_ptr_Output_v4float Output
+         %18 = OpTypeImage %float 2D 0 0 0 1 Unknown
+         %19 = OpTypeSampledImage %18
+%_ptr_UniformConstant_19 = OpTypePointer UniformConstant %19
+          %4 = OpVariable %_ptr_UniformConstant_19 UniformConstant
+          %5 = OpVariable %_ptr_UniformConstant_19 UniformConstant
+         %21 = OpTypeImage %float 2D 0 1 0 1 Unknown
+          %2 = OpFunction %void None %7
+         %22 = OpLabel
+         %23 = OpVariable %_ptr_Function_v2uint Function
+         %24 = OpLoad %19 %4
+         %25 = OpLoad %v2uint %23
+         %26 = OpLoad %19 %5
+         %27 = OpLoad %v2uint %23
+         %28 = OpLoad %v2uint %23
+         %29 = OpImageBlockMatchSADQCOM %v4float %24 %25 %26 %27 %28
+               OpStore %3 %29
+               OpReturn
+               OpFunctionEnd
+)";
+  CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
+  EXPECT_THAT(getDiagnosticString(), HasSubstr("Missing decoration"));
+}
+
+TEST_F(ValidateImage, QCOMImageProcessingBlockMatchSSDNoDecorationA) {
+  std::string text = R"(
+           OpCapability Shader
+           OpCapability TextureBlockMatchQCOM
+           OpExtension "SPV_QCOM_image_processing"
+      %1 = OpExtInstImport "GLSL.std.450"
+           OpMemoryModel Logical GLSL450
+           OpEntryPoint Fragment %2 "main" %3 %4 %5 %6
+           OpExecutionMode %2 OriginUpperLeft
+           OpDecorate %3 Location 0
+           OpDecorate %4 DescriptorSet 0
+           OpDecorate %4 Binding 1
+           OpDecorate %5 DescriptorSet 0
+           OpDecorate %5 Binding 3
+           OpDecorate %6 DescriptorSet 0
+           OpDecorate %6 Binding 2
+           OpDecorate %6 BlockMatchTextureQCOM
+   %void = OpTypeVoid
+      %8 = 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_float = OpTypePointer Input %float
+%_ptr_Function_uint = OpTypePointer Function %uint
+%uint_4 = OpConstant %uint 4
+    %17 = OpConstantComposite %v2uint %uint_4 %uint_4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+     %3 = OpVariable %_ptr_Output_v4float Output
+    %19 = OpTypeImage %float 2D 0 0 0 1 Unknown
+%_ptr_UniformConstant_19 = OpTypePointer UniformConstant %19
+     %4 = OpVariable %_ptr_UniformConstant_19 UniformConstant
+    %21 = OpTypeSampler
+%_ptr_UniformConstant_21 = OpTypePointer UniformConstant %21
+    %5 = OpVariable %_ptr_UniformConstant_21 UniformConstant
+   %23 = OpTypeSampledImage %19
+    %6 = OpVariable %_ptr_UniformConstant_19 UniformConstant
+    %2 = OpFunction %void None %8
+   %24 = OpLabel
+   %25 = OpVariable %_ptr_Function_v2uint Function
+   %26 = OpLoad %19 %4
+   %27 = OpLoad %21 %5
+   %28 = OpSampledImage %23 %26 %27
+   %29 = OpLoad %v2uint %25
+   %30 = OpLoad %19 %6
+   %31 = OpLoad %21 %5
+   %32 = OpSampledImage %23 %30 %31
+   %33 = OpImageBlockMatchSSDQCOM %v4float %28 %29 %32 %29 %29
+         OpStore %3 %33
+         OpReturn
+         OpFunctionEnd
+)";
+  CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
+  EXPECT_THAT(getDiagnosticString(), HasSubstr("Missing decoration"));
+}
+
+TEST_F(ValidateImage, QCOMImageProcessingBlockMatchSSDNoDecorationB) {
+  std::string text = R"(
+           OpCapability Shader
+           OpCapability TextureBlockMatchQCOM
+           OpExtension "SPV_QCOM_image_processing"
+      %1 = OpExtInstImport "GLSL.std.450"
+           OpMemoryModel Logical GLSL450
+           OpEntryPoint Fragment %2 "main" %3 %4 %5 %6
+           OpExecutionMode %2 OriginUpperLeft
+           OpDecorate %3 Location 0
+           OpDecorate %4 DescriptorSet 0
+           OpDecorate %4 Binding 1
+           OpDecorate %5 DescriptorSet 0
+           OpDecorate %5 Binding 3
+           OpDecorate %5 BlockMatchTextureQCOM
+           OpDecorate %6 DescriptorSet 0
+           OpDecorate %6 Binding 2
+   %void = OpTypeVoid
+      %8 = 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_float = OpTypePointer Input %float
+%_ptr_Function_uint = OpTypePointer Function %uint
+%uint_4 = OpConstant %uint 4
+    %17 = OpConstantComposite %v2uint %uint_4 %uint_4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+     %3 = OpVariable %_ptr_Output_v4float Output
+    %19 = OpTypeImage %float 2D 0 0 0 1 Unknown
+%_ptr_UniformConstant_19 = OpTypePointer UniformConstant %19
+     %4 = OpVariable %_ptr_UniformConstant_19 UniformConstant
+    %21 = OpTypeSampler
+%_ptr_UniformConstant_21 = OpTypePointer UniformConstant %21
+    %5 = OpVariable %_ptr_UniformConstant_21 UniformConstant
+   %23 = OpTypeSampledImage %19
+    %6 = OpVariable %_ptr_UniformConstant_19 UniformConstant
+    %2 = OpFunction %void None %8
+   %24 = OpLabel
+   %25 = OpVariable %_ptr_Function_v2uint Function
+   %26 = OpLoad %19 %4
+   %27 = OpLoad %21 %5
+   %28 = OpSampledImage %23 %26 %27
+   %29 = OpLoad %v2uint %25
+   %30 = OpLoad %19 %6
+   %31 = OpLoad %21 %5
+   %32 = OpSampledImage %23 %30 %31
+   %33 = OpImageBlockMatchSSDQCOM %v4float %28 %29 %32 %29 %29
+         OpStore %3 %33
+         OpReturn
+         OpFunctionEnd
+)";
+  CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
+  EXPECT_THAT(getDiagnosticString(), HasSubstr("Missing decoration"));
+}
+
+TEST_F(ValidateImage, QCOMImageProcessingBlockMatchSSDNoDecorationC) {
+  std::string text = R"(
+               OpCapability Shader
+               OpCapability TextureBlockMatchQCOM
+               OpExtension "SPV_QCOM_image_processing"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %2 "main" %3 %4 %5
+               OpExecutionMode %2 OriginUpperLeft
+               OpDecorate %3 Location 0
+               OpDecorate %4 DescriptorSet 0
+               OpDecorate %4 Binding 4
+               OpDecorate %5 DescriptorSet 0
+               OpDecorate %5 Binding 5
+               OpDecorate %5 BlockMatchTextureQCOM
+       %void = OpTypeVoid
+          %7 = 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
+%_ptr_Input_float = OpTypePointer Input %float
+%_ptr_Function_uint = OpTypePointer Function %uint
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+          %3 = OpVariable %_ptr_Output_v4float Output
+         %18 = OpTypeImage %float 2D 0 0 0 1 Unknown
+         %19 = OpTypeSampledImage %18
+%_ptr_UniformConstant_19 = OpTypePointer UniformConstant %19
+          %4 = OpVariable %_ptr_UniformConstant_19 UniformConstant
+          %5 = OpVariable %_ptr_UniformConstant_19 UniformConstant
+         %21 = OpTypeImage %float 2D 0 1 0 1 Unknown
+          %2 = OpFunction %void None %7
+         %22 = OpLabel
+         %23 = OpVariable %_ptr_Function_v2uint Function
+         %24 = OpLoad %19 %4
+         %25 = OpLoad %v2uint %23
+         %26 = OpLoad %19 %5
+         %27 = OpLoad %v2uint %23
+         %28 = OpLoad %v2uint %23
+         %29 = OpImageBlockMatchSSDQCOM %v4float %24 %25 %26 %27 %28
+               OpStore %3 %29
+               OpReturn
+               OpFunctionEnd
+)";
+  CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
+  EXPECT_THAT(getDiagnosticString(), HasSubstr("Missing decoration"));
+}
+
+TEST_F(ValidateImage, QCOMImageProcessingBlockMatchSSDNoDecorationD) {
+  std::string text = R"(
+               OpCapability Shader
+               OpCapability TextureBlockMatchQCOM
+               OpExtension "SPV_QCOM_image_processing"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %2 "main" %3 %4 %5
+               OpExecutionMode %2 OriginUpperLeft
+               OpDecorate %3 Location 0
+               OpDecorate %4 DescriptorSet 0
+               OpDecorate %4 Binding 4
+               OpDecorate %4 BlockMatchTextureQCOM
+               OpDecorate %5 DescriptorSet 0
+               OpDecorate %5 Binding 5
+       %void = OpTypeVoid
+          %7 = 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
+%_ptr_Input_float = OpTypePointer Input %float
+%_ptr_Function_uint = OpTypePointer Function %uint
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+          %3 = OpVariable %_ptr_Output_v4float Output
+         %18 = OpTypeImage %float 2D 0 0 0 1 Unknown
+         %19 = OpTypeSampledImage %18
+%_ptr_UniformConstant_19 = OpTypePointer UniformConstant %19
+          %4 = OpVariable %_ptr_UniformConstant_19 UniformConstant
+          %5 = OpVariable %_ptr_UniformConstant_19 UniformConstant
+         %21 = OpTypeImage %float 2D 0 1 0 1 Unknown
+          %2 = OpFunction %void None %7
+         %22 = OpLabel
+         %23 = OpVariable %_ptr_Function_v2uint Function
+         %24 = OpLoad %19 %4
+         %25 = OpLoad %v2uint %23
+         %26 = OpLoad %19 %5
+         %27 = OpLoad %v2uint %23
+         %28 = OpLoad %v2uint %23
+         %29 = OpImageBlockMatchSSDQCOM %v4float %24 %25 %26 %27 %28
+               OpStore %3 %29
+               OpReturn
+               OpFunctionEnd
+)";
+  CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
+  EXPECT_THAT(getDiagnosticString(), HasSubstr("Missing decoration"));
+}
+
+TEST_F(ValidateImage, QCOMImageProcessingSampleWeightedNoDecorationA) {
+  std::string text = R"(
+               OpCapability Shader
+               OpCapability TextureSampleWeightedQCOM
+               OpExtension "SPV_QCOM_image_processing"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %2 "main" %3 %4 %5 %6 %7
+               OpExecutionMode %2 OriginUpperLeft
+               OpDecorate %3 Location 0
+               OpDecorate %4 DescriptorSet 0
+               OpDecorate %4 Binding 1
+               OpDecorate %5 DescriptorSet 0
+               OpDecorate %5 Binding 3
+               OpDecorate %6 Location 0
+               OpDecorate %7 DescriptorSet 0
+               OpDecorate %7 Binding 0
+       %void = OpTypeVoid
+          %9 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+          %3 = OpVariable %_ptr_Output_v4float Output
+         %13 = OpTypeImage %float 2D 0 0 0 1 Unknown
+%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13
+          %4 = OpVariable %_ptr_UniformConstant_13 UniformConstant
+         %15 = OpTypeSampler
+%_ptr_UniformConstant_15 = OpTypePointer UniformConstant %15
+          %5 = OpVariable %_ptr_UniformConstant_15 UniformConstant
+         %17 = OpTypeSampledImage %13
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+          %6 = OpVariable %_ptr_Input_v4float Input
+    %v2float = OpTypeVector %float 2
+         %20 = OpTypeImage %float 2D 0 1 0 1 Unknown
+%_ptr_UniformConstant_20 = OpTypePointer UniformConstant %20
+          %7 = OpVariable %_ptr_UniformConstant_20 UniformConstant
+         %22 = OpTypeSampledImage %20
+%_ptr_UniformConstant_17 = OpTypePointer UniformConstant %17
+          %2 = OpFunction %void None %9
+         %24 = OpLabel
+         %25 = OpLoad %13 %4
+         %26 = OpLoad %15 %5
+         %27 = OpSampledImage %17 %25 %26
+         %28 = OpLoad %v4float %6
+         %29 = OpVectorShuffle %v2float %28 %28 0 1
+         %30 = OpLoad %20 %7
+         %31 = OpLoad %15 %5
+         %32 = OpSampledImage %22 %30 %31
+         %33 = OpImageSampleWeightedQCOM %v4float %27 %29 %32
+               OpStore %3 %33
+               OpReturn
+               OpFunctionEnd
+)";
+  CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
+  EXPECT_THAT(getDiagnosticString(), HasSubstr("Missing decoration"));
+}
+
+TEST_F(ValidateImage, QCOMImageProcessingSampleWeightedNoDecorationB) {
+  std::string text = R"(
+               OpCapability Shader
+               OpCapability TextureSampleWeightedQCOM
+               OpExtension "SPV_QCOM_image_processing"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %2 "main" %3 %4 %5 %6
+               OpExecutionMode %2 OriginUpperLeft
+               OpDecorate %3 Location 0
+               OpDecorate %4 Location 0
+               OpDecorate %5 DescriptorSet 0
+               OpDecorate %5 Binding 4
+               OpDecorate %6 DescriptorSet 0
+               OpDecorate %6 Binding 5
+       %void = OpTypeVoid
+          %8 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+          %3 = OpVariable %_ptr_Output_v4float Output
+         %12 = OpTypeImage %float 2D 0 0 0 1 Unknown
+%_ptr_UniformConstant_12 = OpTypePointer UniformConstant %12
+         %14 = OpTypeSampler
+%_ptr_UniformConstant_14 = OpTypePointer UniformConstant %14
+         %16 = OpTypeSampledImage %12
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+          %4 = OpVariable %_ptr_Input_v4float Input
+    %v2float = OpTypeVector %float 2
+         %19 = OpTypeImage %float 2D 0 1 0 1 Unknown
+%_ptr_UniformConstant_19 = OpTypePointer UniformConstant %19
+         %21 = OpTypeSampledImage %19
+%_ptr_UniformConstant_16 = OpTypePointer UniformConstant %16
+          %5 = OpVariable %_ptr_UniformConstant_16 UniformConstant
+%_ptr_UniformConstant_21 = OpTypePointer UniformConstant %21
+          %6 = OpVariable %_ptr_UniformConstant_21 UniformConstant
+          %2 = OpFunction %void None %8
+         %24 = OpLabel
+         %25 = OpLoad %16 %5
+         %26 = OpLoad %v4float %4
+         %27 = OpVectorShuffle %v2float %26 %26 0 1
+         %28 = OpLoad %21 %6
+         %29 = OpImageSampleWeightedQCOM %v4float %25 %27 %28
+               OpStore %3 %29
+               OpReturn
+               OpFunctionEnd
+)";
+  CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
+  EXPECT_THAT(getDiagnosticString(), HasSubstr("Missing decoration"));
+}
+
+TEST_F(ValidateImage, QCOMImageProcessingBlockMatchSADInvalidUseA) {
+  std::string text = R"(
+; SPIR-V
+; Version: 1.0
+; Generator: Khronos Glslang Reference Front End; 11
+; Bound: 79
+; Schema: 0
+               OpCapability Shader
+               OpCapability TextureBlockMatchQCOM
+               OpExtension "SPV_QCOM_image_processing"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main" %100 %101 %102 %103 %104
+               OpExecutionMode %main OriginUpperLeft
+               OpDecorate %100 Location 0
+               OpDecorate %101 Location 0
+               OpDecorate %102 DescriptorSet 0
+               OpDecorate %102 Binding 1
+               OpDecorate %103 DescriptorSet 0
+               OpDecorate %103 Binding 3
+               OpDecorate %104 DescriptorSet 0
+               OpDecorate %104 Binding 2
+               OpDecorate %102 BlockMatchTextureQCOM
+               OpDecorate %104 BlockMatchTextureQCOM
+       %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
+ %100 = OpVariable %_ptr_Input_v4float Input
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+  %101 = OpVariable %_ptr_Output_v4float Output
+         %42 = OpTypeImage %float 2D 0 0 0 1 Unknown
+%_ptr_UniformConstant_42 = OpTypePointer UniformConstant %42
+ %102 = OpVariable %_ptr_UniformConstant_42 UniformConstant
+         %46 = OpTypeSampler
+%_ptr_UniformConstant_46 = OpTypePointer UniformConstant %46
+       %103 = OpVariable %_ptr_UniformConstant_46 UniformConstant
+         %50 = OpTypeSampledImage %42
+ %104 = OpVariable %_ptr_UniformConstant_42 UniformConstant
+    %v2float = OpTypeVector %float 2
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %15 = OpVariable %_ptr_Function_v2uint Function
+         %45 = OpLoad %42 %102
+         %49 = OpLoad %46 %103
+         %51 = OpSampledImage %50 %45 %49
+         %52 = OpLoad %v2uint %15
+         %54 = OpLoad %42 %104
+         %55 = OpLoad %46 %103
+         %56 = OpSampledImage %50 %54 %55
+         %57 = OpLoad %v2uint %15
+         %58 = OpLoad %v2uint %15
+         %59 = OpImageBlockMatchSADQCOM %v4float %51 %52 %56 %57 %58
+               OpStore %101 %59
+         %69 = OpLoad %42 %102
+         %70 = OpLoad %46 %103
+         %71 = OpSampledImage %50 %69 %70
+         %73 = OpLoad %v4float %100
+         %74 = OpVectorShuffle %v2float %73 %73 0 0
+         %75 = OpImageSampleImplicitLod %v4float %71 %74
+               OpStore %101 %75
+               OpReturn
+               OpFunctionEnd
+)";
+  CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Illegal use of QCOM image processing decorated texture"));
+}
+
+TEST_F(ValidateImage, QCOMImageProcessingBlockMatchSADInvalidUseB) {
+  std::string text = R"(
+; SPIR-V
+; Version: 1.0
+; Generator: Khronos Glslang Reference Front End; 11
+; Bound: 79
+; Schema: 0
+               OpCapability Shader
+               OpCapability TextureBlockMatchQCOM
+               OpExtension "SPV_QCOM_image_processing"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main" %100 %101 %102 %103 %104
+               OpExecutionMode %main OriginUpperLeft
+               OpDecorate %100 Location 0
+               OpDecorate %101 Location 0
+               OpDecorate %102 DescriptorSet 0
+               OpDecorate %102 Binding 1
+               OpDecorate %103 DescriptorSet 0
+               OpDecorate %103 Binding 3
+               OpDecorate %104 DescriptorSet 0
+               OpDecorate %104 Binding 2
+               OpDecorate %102 BlockMatchTextureQCOM
+               OpDecorate %104 BlockMatchTextureQCOM
+       %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
+ %100 = OpVariable %_ptr_Input_v4float Input
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+  %101 = OpVariable %_ptr_Output_v4float Output
+         %42 = OpTypeImage %float 2D 0 0 0 1 Unknown
+%_ptr_UniformConstant_42 = OpTypePointer UniformConstant %42
+ %102 = OpVariable %_ptr_UniformConstant_42 UniformConstant
+         %46 = OpTypeSampler
+%_ptr_UniformConstant_46 = OpTypePointer UniformConstant %46
+       %103 = OpVariable %_ptr_UniformConstant_46 UniformConstant
+         %50 = OpTypeSampledImage %42
+ %104 = OpVariable %_ptr_UniformConstant_42 UniformConstant
+    %v2float = OpTypeVector %float 2
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %15 = OpVariable %_ptr_Function_v2uint Function
+         %45 = OpLoad %42 %102
+         %49 = OpLoad %46 %103
+         %51 = OpSampledImage %50 %45 %49
+         %52 = OpLoad %v2uint %15
+         %54 = OpLoad %42 %104
+         %55 = OpLoad %46 %103
+         %56 = OpSampledImage %50 %54 %55
+         %57 = OpLoad %v2uint %15
+         %58 = OpLoad %v2uint %15
+         %59 = OpImageBlockMatchSADQCOM %v4float %51 %52 %56 %57 %58
+               OpStore %101 %59
+         %69 = OpLoad %42 %104
+         %70 = OpLoad %46 %103
+         %71 = OpSampledImage %50 %69 %70
+         %73 = OpLoad %v4float %100
+         %74 = OpVectorShuffle %v2float %73 %73 0 0
+         %75 = OpImageSampleImplicitLod %v4float %71 %74
+               OpStore %101 %75
+               OpReturn
+               OpFunctionEnd
+)";
+  CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Illegal use of QCOM image processing decorated texture"));
+}
+
+TEST_F(ValidateImage, QCOMImageProcessingBlockMatchSADInvalidUseC) {
+  std::string text = R"(
+               OpCapability Shader
+               OpCapability TextureBlockMatchQCOM
+               OpExtension "SPV_QCOM_image_processing"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %2 "main" %3 %4 %5 %6
+               OpExecutionMode %2 OriginUpperLeft
+               OpDecorate %3 Location 0
+               OpDecorate %4 Location 0
+               OpDecorate %5 DescriptorSet 0
+               OpDecorate %5 Binding 4
+               OpDecorate %6 DescriptorSet 0
+               OpDecorate %6 Binding 5
+               OpDecorate %5 BlockMatchTextureQCOM
+               OpDecorate %6 BlockMatchTextureQCOM
+       %void = OpTypeVoid
+          %8 = 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
+          %3 = OpVariable %_ptr_Input_v4float Input
+     %uint_4 = OpConstant %uint 4
+         %16 = OpConstantComposite %v2uint %uint_4 %uint_4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+          %4 = OpVariable %_ptr_Output_v4float Output
+         %18 = OpTypeImage %float 2D 0 0 0 1 Unknown
+%_ptr_UniformConstant_18 = OpTypePointer UniformConstant %18
+         %20 = OpTypeSampledImage %18
+%_ptr_UniformConstant_20 = OpTypePointer UniformConstant %20
+          %5 = OpVariable %_ptr_UniformConstant_20 UniformConstant
+          %6 = OpVariable %_ptr_UniformConstant_20 UniformConstant
+    %v2float = OpTypeVector %float 2
+         %23 = OpTypeImage %float 2D 0 1 0 1 Unknown
+          %2 = OpFunction %void None %8
+         %24 = OpLabel
+         %25 = OpVariable %_ptr_Function_v2uint Function
+               OpStore %25 %16
+         %26 = OpLoad %20 %5
+         %27 = OpLoad %v2uint %25
+         %28 = OpLoad %20 %6
+         %29 = OpLoad %v2uint %25
+         %30 = OpLoad %v2uint %25
+         %31 = OpImageBlockMatchSADQCOM %v4float %26 %27 %28 %29 %30
+               OpStore %4 %31
+         %32 = OpLoad %20 %5
+         %33 = OpLoad %v4float %3
+         %34 = OpVectorShuffle %v2float %33 %33 0 2
+         %35 = OpImageSampleImplicitLod %v4float %32 %34
+               OpStore %4 %35
+               OpReturn
+               OpFunctionEnd
+)";
+  CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Illegal use of QCOM image processing decorated texture"));
+}
+
+TEST_F(ValidateImage, QCOMImageProcessingBlockMatchSADInvalidUseD) {
+  std::string text = R"(
+               OpCapability Shader
+               OpCapability TextureBlockMatchQCOM
+               OpExtension "SPV_QCOM_image_processing"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %2 "main" %3 %4 %5 %6
+               OpExecutionMode %2 OriginUpperLeft
+               OpDecorate %3 Location 0
+               OpDecorate %4 Location 0
+               OpDecorate %5 DescriptorSet 0
+               OpDecorate %5 Binding 4
+               OpDecorate %6 DescriptorSet 0
+               OpDecorate %6 Binding 5
+               OpDecorate %5 BlockMatchTextureQCOM
+               OpDecorate %6 BlockMatchTextureQCOM
+       %void = OpTypeVoid
+          %8 = 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
+          %3 = OpVariable %_ptr_Input_v4float Input
+     %uint_4 = OpConstant %uint 4
+         %16 = OpConstantComposite %v2uint %uint_4 %uint_4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+          %4 = OpVariable %_ptr_Output_v4float Output
+         %18 = OpTypeImage %float 2D 0 0 0 1 Unknown
+%_ptr_UniformConstant_18 = OpTypePointer UniformConstant %18
+         %20 = OpTypeSampledImage %18
+%_ptr_UniformConstant_20 = OpTypePointer UniformConstant %20
+          %5 = OpVariable %_ptr_UniformConstant_20 UniformConstant
+          %6 = OpVariable %_ptr_UniformConstant_20 UniformConstant
+    %v2float = OpTypeVector %float 2
+         %23 = OpTypeImage %float 2D 0 1 0 1 Unknown
+          %2 = OpFunction %void None %8
+         %24 = OpLabel
+         %25 = OpVariable %_ptr_Function_v2uint Function
+               OpStore %25 %16
+         %26 = OpLoad %20 %5
+         %27 = OpLoad %v2uint %25
+         %28 = OpLoad %20 %6
+         %29 = OpLoad %v2uint %25
+         %30 = OpLoad %v2uint %25
+         %31 = OpImageBlockMatchSADQCOM %v4float %26 %27 %28 %29 %30
+               OpStore %4 %31
+         %32 = OpLoad %20 %6
+         %33 = OpLoad %v4float %3
+         %34 = OpVectorShuffle %v2float %33 %33 0 2
+         %35 = OpImageSampleImplicitLod %v4float %32 %34
+               OpStore %4 %35
+               OpReturn
+               OpFunctionEnd
+)";
+  CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Illegal use of QCOM image processing decorated texture"));
+}
+
+TEST_F(ValidateImage, QCOMImageProcessingBlockMatchSSDInvalidUseA) {
+  std::string text = R"(
+; SPIR-V
+; Version: 1.0
+; Generator: Khronos Glslang Reference Front End; 11
+; Bound: 79
+; Schema: 0
+               OpCapability Shader
+               OpCapability TextureBlockMatchQCOM
+               OpExtension "SPV_QCOM_image_processing"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main" %100 %101 %102 %103 %104
+               OpExecutionMode %main OriginUpperLeft
+               OpDecorate %100 Location 0
+               OpDecorate %101 Location 0
+               OpDecorate %102 DescriptorSet 0
+               OpDecorate %102 Binding 1
+               OpDecorate %103 DescriptorSet 0
+               OpDecorate %103 Binding 3
+               OpDecorate %104 DescriptorSet 0
+               OpDecorate %104 Binding 2
+               OpDecorate %102 BlockMatchTextureQCOM
+               OpDecorate %104 BlockMatchTextureQCOM
+       %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
+ %100 = OpVariable %_ptr_Input_v4float Input
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+  %101 = OpVariable %_ptr_Output_v4float Output
+         %42 = OpTypeImage %float 2D 0 0 0 1 Unknown
+%_ptr_UniformConstant_42 = OpTypePointer UniformConstant %42
+ %102 = OpVariable %_ptr_UniformConstant_42 UniformConstant
+         %46 = OpTypeSampler
+%_ptr_UniformConstant_46 = OpTypePointer UniformConstant %46
+       %103 = OpVariable %_ptr_UniformConstant_46 UniformConstant
+         %50 = OpTypeSampledImage %42
+ %104 = OpVariable %_ptr_UniformConstant_42 UniformConstant
+    %v2float = OpTypeVector %float 2
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %15 = OpVariable %_ptr_Function_v2uint Function
+         %45 = OpLoad %42 %102
+         %49 = OpLoad %46 %103
+         %51 = OpSampledImage %50 %45 %49
+         %52 = OpLoad %v2uint %15
+         %54 = OpLoad %42 %104
+         %55 = OpLoad %46 %103
+         %56 = OpSampledImage %50 %54 %55
+         %57 = OpLoad %v2uint %15
+         %58 = OpLoad %v2uint %15
+         %59 = OpImageBlockMatchSSDQCOM %v4float %51 %52 %56 %57 %58
+               OpStore %101 %59
+         %69 = OpLoad %42 %102
+         %70 = OpLoad %46 %103
+         %71 = OpSampledImage %50 %69 %70
+         %73 = OpLoad %v4float %100
+         %74 = OpVectorShuffle %v2float %73 %73 0 0
+         %75 = OpImageSampleImplicitLod %v4float %71 %74
+               OpStore %101 %75
+               OpReturn
+               OpFunctionEnd
+)";
+  CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Illegal use of QCOM image processing decorated texture"));
+}
+
+TEST_F(ValidateImage, QCOMImageProcessingBlockMatchSSDInvalidUseB) {
+  std::string text = R"(
+; SPIR-V
+; Version: 1.0
+; Generator: Khronos Glslang Reference Front End; 11
+; Bound: 79
+; Schema: 0
+               OpCapability Shader
+               OpCapability TextureBlockMatchQCOM
+               OpExtension "SPV_QCOM_image_processing"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main" %100 %101 %102 %103 %104
+               OpExecutionMode %main OriginUpperLeft
+               OpDecorate %100 Location 0
+               OpDecorate %101 Location 0
+               OpDecorate %102 DescriptorSet 0
+               OpDecorate %102 Binding 1
+               OpDecorate %103 DescriptorSet 0
+               OpDecorate %103 Binding 3
+               OpDecorate %104 DescriptorSet 0
+               OpDecorate %104 Binding 2
+               OpDecorate %102 BlockMatchTextureQCOM
+               OpDecorate %104 BlockMatchTextureQCOM
+       %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
+ %100 = OpVariable %_ptr_Input_v4float Input
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+  %101 = OpVariable %_ptr_Output_v4float Output
+         %42 = OpTypeImage %float 2D 0 0 0 1 Unknown
+%_ptr_UniformConstant_42 = OpTypePointer UniformConstant %42
+ %102 = OpVariable %_ptr_UniformConstant_42 UniformConstant
+         %46 = OpTypeSampler
+%_ptr_UniformConstant_46 = OpTypePointer UniformConstant %46
+       %103 = OpVariable %_ptr_UniformConstant_46 UniformConstant
+         %50 = OpTypeSampledImage %42
+ %104 = OpVariable %_ptr_UniformConstant_42 UniformConstant
+    %v2float = OpTypeVector %float 2
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %15 = OpVariable %_ptr_Function_v2uint Function
+         %45 = OpLoad %42 %102
+         %49 = OpLoad %46 %103
+         %51 = OpSampledImage %50 %45 %49
+         %52 = OpLoad %v2uint %15
+         %54 = OpLoad %42 %104
+         %55 = OpLoad %46 %103
+         %56 = OpSampledImage %50 %54 %55
+         %57 = OpLoad %v2uint %15
+         %58 = OpLoad %v2uint %15
+         %59 = OpImageBlockMatchSSDQCOM %v4float %51 %52 %56 %57 %58
+               OpStore %101 %59
+         %69 = OpLoad %42 %104
+         %70 = OpLoad %46 %103
+         %71 = OpSampledImage %50 %69 %70
+         %73 = OpLoad %v4float %100
+         %74 = OpVectorShuffle %v2float %73 %73 0 0
+         %75 = OpImageSampleImplicitLod %v4float %71 %74
+               OpStore %101 %75
+               OpReturn
+               OpFunctionEnd
+)";
+  CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Illegal use of QCOM image processing decorated texture"));
+}
+
+TEST_F(ValidateImage, QCOMImageProcessingBlockMatchSSDInvalidUseC) {
+  std::string text = R"(
+               OpCapability Shader
+               OpCapability TextureBlockMatchQCOM
+               OpExtension "SPV_QCOM_image_processing"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %2 "main" %3 %4 %5 %6
+               OpExecutionMode %2 OriginUpperLeft
+               OpDecorate %3 Location 0
+               OpDecorate %4 Location 0
+               OpDecorate %5 DescriptorSet 0
+               OpDecorate %5 Binding 4
+               OpDecorate %6 DescriptorSet 0
+               OpDecorate %6 Binding 5
+               OpDecorate %5 BlockMatchTextureQCOM
+               OpDecorate %6 BlockMatchTextureQCOM
+       %void = OpTypeVoid
+          %8 = 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
+          %3 = OpVariable %_ptr_Input_v4float Input
+     %uint_4 = OpConstant %uint 4
+         %16 = OpConstantComposite %v2uint %uint_4 %uint_4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+          %4 = OpVariable %_ptr_Output_v4float Output
+         %18 = OpTypeImage %float 2D 0 0 0 1 Unknown
+%_ptr_UniformConstant_18 = OpTypePointer UniformConstant %18
+         %20 = OpTypeSampledImage %18
+%_ptr_UniformConstant_20 = OpTypePointer UniformConstant %20
+          %5 = OpVariable %_ptr_UniformConstant_20 UniformConstant
+          %6 = OpVariable %_ptr_UniformConstant_20 UniformConstant
+    %v2float = OpTypeVector %float 2
+         %23 = OpTypeImage %float 2D 0 1 0 1 Unknown
+          %2 = OpFunction %void None %8
+         %24 = OpLabel
+         %25 = OpVariable %_ptr_Function_v2uint Function
+               OpStore %25 %16
+         %26 = OpLoad %20 %5
+         %27 = OpLoad %v2uint %25
+         %28 = OpLoad %20 %6
+         %29 = OpLoad %v2uint %25
+         %30 = OpLoad %v2uint %25
+         %31 = OpImageBlockMatchSSDQCOM %v4float %26 %27 %28 %29 %30
+               OpStore %4 %31
+         %32 = OpLoad %20 %5
+         %33 = OpLoad %v4float %3
+         %34 = OpVectorShuffle %v2float %33 %33 0 2
+         %35 = OpImageSampleImplicitLod %v4float %32 %34
+               OpStore %4 %35
+               OpReturn
+               OpFunctionEnd
+)";
+  CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Illegal use of QCOM image processing decorated texture"));
+}
+
+TEST_F(ValidateImage, QCOMImageProcessingBlockMatchSSDInvalidUseD) {
+  std::string text = R"(
+               OpCapability Shader
+               OpCapability TextureBlockMatchQCOM
+               OpExtension "SPV_QCOM_image_processing"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %2 "main" %3 %4 %5 %6
+               OpExecutionMode %2 OriginUpperLeft
+               OpDecorate %3 Location 0
+               OpDecorate %4 Location 0
+               OpDecorate %5 DescriptorSet 0
+               OpDecorate %5 Binding 4
+               OpDecorate %6 DescriptorSet 0
+               OpDecorate %6 Binding 5
+               OpDecorate %5 BlockMatchTextureQCOM
+               OpDecorate %6 BlockMatchTextureQCOM
+       %void = OpTypeVoid
+          %8 = 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
+          %3 = OpVariable %_ptr_Input_v4float Input
+     %uint_4 = OpConstant %uint 4
+         %16 = OpConstantComposite %v2uint %uint_4 %uint_4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+          %4 = OpVariable %_ptr_Output_v4float Output
+         %18 = OpTypeImage %float 2D 0 0 0 1 Unknown
+%_ptr_UniformConstant_18 = OpTypePointer UniformConstant %18
+         %20 = OpTypeSampledImage %18
+%_ptr_UniformConstant_20 = OpTypePointer UniformConstant %20
+          %5 = OpVariable %_ptr_UniformConstant_20 UniformConstant
+          %6 = OpVariable %_ptr_UniformConstant_20 UniformConstant
+    %v2float = OpTypeVector %float 2
+         %23 = OpTypeImage %float 2D 0 1 0 1 Unknown
+          %2 = OpFunction %void None %8
+         %24 = OpLabel
+         %25 = OpVariable %_ptr_Function_v2uint Function
+               OpStore %25 %16
+         %26 = OpLoad %20 %5
+         %27 = OpLoad %v2uint %25
+         %28 = OpLoad %20 %6
+         %29 = OpLoad %v2uint %25
+         %30 = OpLoad %v2uint %25
+         %31 = OpImageBlockMatchSSDQCOM %v4float %26 %27 %28 %29 %30
+               OpStore %4 %31
+         %32 = OpLoad %20 %6
+         %33 = OpLoad %v4float %3
+         %34 = OpVectorShuffle %v2float %33 %33 0 2
+         %35 = OpImageSampleImplicitLod %v4float %32 %34
+               OpStore %4 %35
+               OpReturn
+               OpFunctionEnd
+)";
+  CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Illegal use of QCOM image processing decorated texture"));
+}
+
+TEST_F(ValidateImage, QCOMImageProcessingSampleWeightedInvalidUseA) {
+  std::string text = R"(
+               OpCapability Shader
+               OpCapability TextureSampleWeightedQCOM
+               OpExtension "SPV_QCOM_image_processing"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %2 "main" %3 %4 %5 %6
+               OpExecutionMode %2 OriginUpperLeft
+               OpDecorate %3 Location 0
+               OpDecorate %4 Location 0
+               OpDecorate %5 DescriptorSet 0
+               OpDecorate %5 Binding 4
+               OpDecorate %6 DescriptorSet 0
+               OpDecorate %6 Binding 5
+               OpDecorate %6 WeightTextureQCOM
+       %void = OpTypeVoid
+          %8 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+          %3 = OpVariable %_ptr_Output_v4float Output
+         %12 = OpTypeImage %float 2D 0 0 0 1 Unknown
+%_ptr_UniformConstant_12 = OpTypePointer UniformConstant %12
+         %14 = OpTypeSampledImage %12
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+          %4 = OpVariable %_ptr_Input_v4float Input
+    %v2float = OpTypeVector %float 2
+         %17 = OpTypeImage %float 2D 0 1 0 1 Unknown
+%_ptr_UniformConstant_17 = OpTypePointer UniformConstant %17
+         %19 = OpTypeSampledImage %17
+%_ptr_UniformConstant_14 = OpTypePointer UniformConstant %14
+          %5 = OpVariable %_ptr_UniformConstant_14 UniformConstant
+%_ptr_UniformConstant_19 = OpTypePointer UniformConstant %19
+          %6 = OpVariable %_ptr_UniformConstant_19 UniformConstant
+    %v3float = OpTypeVector %float 3
+          %2 = OpFunction %void None %8
+         %23 = OpLabel
+         %24 = OpLoad %v4float %4
+         %25 = OpVectorShuffle %v2float %24 %24 0 1
+         %26 = OpLoad %14 %5
+         %27 = OpLoad %v4float %4
+         %28 = OpVectorShuffle %v2float %27 %27 0 1
+         %29 = OpLoad %19 %6
+         %30 = OpImageSampleWeightedQCOM %v4float %26 %28 %29
+               OpStore %3 %30
+         %31 = OpLoad %19 %6
+         %32 = OpLoad %v4float %4
+         %33 = OpVectorShuffle %v3float %32 %32 0 1 0
+         %34 = OpCompositeExtract %float %33 0
+         %35 = OpCompositeExtract %float %33 1
+         %36 = OpCompositeExtract %float %33 2
+         %37 = OpCompositeConstruct %v3float %34 %35 %36
+         %38 = OpImageSampleImplicitLod %v4float %31 %37
+               OpStore %3 %38
+               OpReturn
+               OpFunctionEnd
+)";
+  CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Illegal use of QCOM image processing decorated texture"));
+}
+
+TEST_F(ValidateImage, QCOMImageProcessingSampleWeightedInvalidUseB) {
+  std::string text = R"(
+               OpCapability Shader
+               OpCapability TextureSampleWeightedQCOM
+               OpExtension "SPV_QCOM_image_processing"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %2 "main" %3 %4 %5 %6 %7
+               OpExecutionMode %2 OriginUpperLeft
+               OpDecorate %3 Location 0
+               OpDecorate %5 DescriptorSet 0
+               OpDecorate %5 Binding 1
+               OpDecorate %6 DescriptorSet 0
+               OpDecorate %6 Binding 3
+               OpDecorate %4 Location 0
+               OpDecorate %7 DescriptorSet 0
+               OpDecorate %7 Binding 0
+               OpDecorate %7 WeightTextureQCOM
+       %void = OpTypeVoid
+          %9 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+          %3 = OpVariable %_ptr_Output_v4float Output
+         %13 = OpTypeImage %float 2D 0 0 0 1 Unknown
+%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13
+          %5 = OpVariable %_ptr_UniformConstant_13 UniformConstant
+         %15 = OpTypeSampler
+%_ptr_UniformConstant_15 = OpTypePointer UniformConstant %15
+          %6 = OpVariable %_ptr_UniformConstant_15 UniformConstant
+         %17 = OpTypeSampledImage %13
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+          %4 = OpVariable %_ptr_Input_v4float Input
+    %v2float = OpTypeVector %float 2
+         %20 = OpTypeImage %float 2D 0 1 0 1 Unknown
+%_ptr_UniformConstant_20 = OpTypePointer UniformConstant %20
+          %7 = OpVariable %_ptr_UniformConstant_20 UniformConstant
+         %22 = OpTypeSampledImage %20
+    %v3float = OpTypeVector %float 3
+          %2 = OpFunction %void None %9
+         %24 = OpLabel
+         %25 = OpLoad %13 %5
+         %26 = OpLoad %15 %6
+         %27 = OpSampledImage %17 %25 %26
+         %28 = OpLoad %v4float %4
+         %29 = OpVectorShuffle %v2float %28 %28 0 1
+         %30 = OpLoad %20 %7
+         %31 = OpLoad %15 %6
+         %32 = OpSampledImage %22 %30 %31
+         %33 = OpImageSampleWeightedQCOM %v4float %27 %29 %32
+               OpStore %3 %33
+         %34 = OpLoad %20 %7
+         %35 = OpLoad %15 %6
+         %36 = OpSampledImage %22 %34 %35
+         %37 = OpLoad %v4float %4
+         %38 = OpVectorShuffle %v3float %37 %37 0 1 0
+         %39 = OpCompositeExtract %float %38 0
+         %40 = OpCompositeExtract %float %38 1
+         %41 = OpCompositeExtract %float %38 2
+         %42 = OpCompositeConstruct %v3float %39 %40 %41
+         %43 = OpImageSampleImplicitLod %v4float %36 %42
+               OpStore %3 %43
+               OpReturn
+               OpFunctionEnd
+)";
+  CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Illegal use of QCOM image processing decorated texture"));
+}
+
+TEST_F(ValidateImage, ImageMSArray_ArrayedSampledTypeRequiresCapability) {
+  const std::string code = R"(
+               OpCapability Shader
+               OpCapability StorageImageMultisample
+               OpCapability StorageImageReadWithoutFormat
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpDecorate %var_image DescriptorSet 0
+               OpDecorate %var_image Binding 1
+       %void = OpTypeVoid
+       %func = OpTypeFunction %void
+        %f32 = OpTypeFloat 32
+        %u32 = OpTypeInt 32 0
+     %uint_2 = OpConstant %u32 2
+     %uint_1 = OpConstant %u32 1
+     %v2uint = OpTypeVector %u32 2
+    %v4float = OpTypeVector %f32 4
+    %image = OpTypeImage %f32 2D 2 1 1 2 Unknown
+%ptr_image = OpTypePointer UniformConstant %image
+       %10 = OpConstantComposite %v2uint %uint_1 %uint_2
+%var_image = OpVariable %ptr_image UniformConstant
+     %main = OpFunction %void None %func
+ %main_lab = OpLabel
+       %18 = OpLoad %image %var_image
+       %19 = OpImageRead %v4float %18 %10 Sample %uint_2
+             OpReturn
+             OpFunctionEnd
+)";
+
+  const spv_target_env env = SPV_ENV_VULKAN_1_0;
+  CompileSuccessfully(code, env);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(env));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Capability ImageMSArray is required to access storage image"));
+}
+
+TEST_F(ValidateImage, ImageMSArray_SampledTypeDoesNotRequireCapability) {
+  const std::string code = R"(
+               OpCapability Shader
+               OpCapability StorageImageMultisample
+               OpCapability StorageImageReadWithoutFormat
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpDecorate %var_image DescriptorSet 0
+               OpDecorate %var_image Binding 1
+       %void = OpTypeVoid
+       %func = OpTypeFunction %void
+        %f32 = OpTypeFloat 32
+        %u32 = OpTypeInt 32 0
+     %uint_2 = OpConstant %u32 2
+     %uint_1 = OpConstant %u32 1
+     %v2uint = OpTypeVector %u32 2
+    %v4float = OpTypeVector %f32 4
+    %image = OpTypeImage %f32 2D 2 0 1 2 Unknown
+%ptr_image = OpTypePointer UniformConstant %image
+       %10 = OpConstantComposite %v2uint %uint_1 %uint_2
+%var_image = OpVariable %ptr_image UniformConstant
+     %main = OpFunction %void None %func
+ %main_lab = OpLabel
+       %18 = OpLoad %image %var_image
+       %19 = OpImageRead %v4float %18 %10 Sample %uint_2
+             OpReturn
+             OpFunctionEnd
+)";
+
+  const spv_target_env env = SPV_ENV_VULKAN_1_0;
+  CompileSuccessfully(code, env);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(env));
+  EXPECT_THAT(getDiagnosticString(), Eq(""));
+}
+
+TEST_F(ValidateImage, ImageMSArray_ArrayedTypeDoesNotRequireCapability) {
+  const std::string code = R"(
+               OpCapability Shader
+               OpCapability StorageImageReadWithoutFormat
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpDecorate %var_image DescriptorSet 0
+               OpDecorate %var_image Binding 1
+       %void = OpTypeVoid
+       %func = OpTypeFunction %void
+        %f32 = OpTypeFloat 32
+        %u32 = OpTypeInt 32 0
+     %uint_3 = OpConstant %u32 3
+     %uint_2 = OpConstant %u32 2
+     %uint_1 = OpConstant %u32 1
+     %v3uint = OpTypeVector %u32 3
+    %v4float = OpTypeVector %f32 4
+    %image = OpTypeImage %f32 2D 2 1 0 2 Unknown
+%ptr_image = OpTypePointer UniformConstant %image
+       %10 = OpConstantComposite %v3uint %uint_1 %uint_2 %uint_3
+%var_image = OpVariable %ptr_image UniformConstant
+     %main = OpFunction %void None %func
+ %main_lab = OpLabel
+       %18 = OpLoad %image %var_image
+       %19 = OpImageRead %v4float %18 %10
+             OpReturn
+             OpFunctionEnd
+)";
+
+  const spv_target_env env = SPV_ENV_VULKAN_1_0;
+  CompileSuccessfully(code, env);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(env));
+  EXPECT_THAT(getDiagnosticString(), Eq(""));
+}
+
 }  // namespace
 }  // namespace val
 }  // namespace spvtools
diff --git a/test/val/val_interfaces_test.cpp b/test/val/val_interfaces_test.cpp
index 22a0e7c..d75c20c 100644
--- a/test/val/val_interfaces_test.cpp
+++ b/test/val/val_interfaces_test.cpp
@@ -584,6 +584,8 @@
   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 0"));
 }
@@ -611,6 +613,8 @@
 
   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 "
@@ -699,6 +703,8 @@
   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"));
 }
@@ -732,6 +738,8 @@
   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"));
 }
@@ -762,6 +770,8 @@
   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"));
 }
@@ -792,6 +802,8 @@
   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 2"));
 }
@@ -822,6 +834,8 @@
   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 3"));
 }
@@ -854,6 +868,8 @@
   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"));
 }
@@ -886,6 +902,8 @@
   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 5"));
 }
@@ -918,6 +936,8 @@
   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 7"));
 }
@@ -950,6 +970,8 @@
   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"));
 }
@@ -982,6 +1004,8 @@
   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 3"));
 }
@@ -1016,6 +1040,8 @@
   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 15"));
 }
@@ -1075,6 +1101,8 @@
   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, component 1"));
 }
@@ -1189,6 +1217,8 @@
 
   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 "
@@ -1359,6 +1389,8 @@
   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, component 1"));
 }
@@ -1456,7 +1488,7 @@
 OpMemberDecorate %struct 0 Location 0
 OpMemberDecorate %struct 0 Component 0
 OpMemberDecorate %struct 1 Location 0
-OpMemberDecorate %struct 1 Component 1
+OpMemberDecorate %struct 1 Component 2
 %void = OpTypeVoid
 %void_fn = OpTypeFunction %void
 %float = OpTypeFloat 32
@@ -1538,6 +1570,58 @@
           "Interface struct has no Block decoration but has BuiltIn members."));
 }
 
+TEST_F(ValidateInterfacesTest, InvalidLocationTypePointer) {
+  const std::string text = R"(
+               OpCapability Shader
+               OpMemoryModel Logical Simple
+               OpEntryPoint Vertex %1 "Aiqn0" %2 %3
+               OpDecorate %2 Location 0
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+%_ptr_Private_void = OpTypePointer Private %void
+       %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
+          %1 = OpFunction %void None %5
+         %15 = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_1);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Invalid type to assign a location"));
+}
+
+TEST_F(ValidateInterfacesTest, ValidLocationTypePhysicalStorageBufferPointer) {
+  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 0
+%ptr = OpTypePointer PhysicalStorageBuffer %int
+%ptr2 = OpTypePointer Input %ptr
+%var = OpVariable %ptr2 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_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+}
+
 }  // namespace
 }  // namespace val
 }  // namespace spvtools
diff --git a/test/val/val_layout_test.cpp b/test/val/val_layout_test.cpp
index 8cca96f..e809abf 100644
--- a/test/val/val_layout_test.cpp
+++ b/test/val/val_layout_test.cpp
@@ -14,9 +14,7 @@
 
 // Validation tests for Logical Layout
 
-#include <algorithm>
 #include <functional>
-#include <sstream>
 #include <string>
 #include <tuple>
 #include <utility>
@@ -57,13 +55,6 @@
   bool inverse_;
 };
 
-template <typename... T>
-spv_result_t InvalidSet(int order) {
-  for (spv_result_t val : {T(true)(order)...})
-    if (val != SPV_SUCCESS) return val;
-  return SPV_SUCCESS;
-}
-
 // SPIRV source used to test the logical layout
 const std::vector<std::string>& getInstructions() {
   // clang-format off
diff --git a/test/val/val_limits_test.cpp b/test/val/val_limits_test.cpp
index 364d514..66a6ff7 100644
--- a/test/val/val_limits_test.cpp
+++ b/test/val/val_limits_test.cpp
@@ -16,7 +16,6 @@
 
 #include <sstream>
 #include <string>
-#include <utility>
 
 #include "gmock/gmock.h"
 #include "test/unit_spirv.h"
diff --git a/test/val/val_memory_test.cpp b/test/val/val_memory_test.cpp
index 4299eda..8d0a94d 100644
--- a/test/val/val_memory_test.cpp
+++ b/test/val/val_memory_test.cpp
@@ -23,12 +23,14 @@
 #include "test/val/val_fixtures.h"
 
 // For pretty-printing tuples with spv_target_env.
-std::ostream& operator<<(std::ostream& stream, spv_target_env target)
-{
+std::ostream& operator<<(std::ostream& stream, spv_target_env target) {
   switch (target) {
-    case SPV_ENV_UNIVERSAL_1_3: return stream << "SPV_ENV_UNIVERSAL_1_3";
-    case SPV_ENV_UNIVERSAL_1_4: return stream << "SPV_ENV_UNIVERSAL_1_4";
-    default:                    return stream << (unsigned)target;
+    case SPV_ENV_UNIVERSAL_1_3:
+      return stream << "SPV_ENV_UNIVERSAL_1_3";
+    case SPV_ENV_UNIVERSAL_1_4:
+      return stream << "SPV_ENV_UNIVERSAL_1_4";
+    default:
+      return stream << (unsigned)target;
   }
 }
 
@@ -2346,6 +2348,186 @@
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
+TEST_F(ValidateMemory, CoopMatKHRLoadStoreSuccess) {
+  std::string spirv =
+      GenCoopMatLoadStoreShader("MakePointerAvailableKHR|NonPrivatePointerKHR",
+                                "MakePointerVisibleKHR|NonPrivatePointerKHR");
+
+  CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+}
+
+TEST_F(ValidateMemory, CoopMatKHRStoreMemoryAccessFail) {
+  std::string spirv =
+      GenCoopMatLoadStoreShader("MakePointerVisibleKHR|NonPrivatePointerKHR",
+                                "MakePointerVisibleKHR|NonPrivatePointerKHR");
+
+  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("MakePointerVisibleKHR cannot be used with OpStore"));
+}
+
+TEST_F(ValidateMemory, CoopMatKHRLoadMemoryAccessFail) {
+  std::string spirv =
+      GenCoopMatLoadStoreShader("MakePointerAvailableKHR|NonPrivatePointerKHR",
+                                "MakePointerAvailableKHR|NonPrivatePointerKHR");
+
+  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("MakePointerAvailableKHR cannot be used with OpLoad"));
+}
+
+TEST_F(ValidateMemory, CoopMatKHRInvalidStorageClassFail) {
+  const std::string body = R"(
+OpCapability Shader
+OpCapability Float16
+OpCapability CooperativeMatrixKHR
+OpExtension "SPV_KHR_cooperative_matrix"
+OpExtension "SPV_KHR_vulkan_memory_model"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+%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
+
+%f16mat = OpTypeCooperativeMatrixKHR %f16 %subgroup %u32_8 %u32_8 %use_A
+
+%str = OpTypeStruct %f16mat
+%str_ptr = OpTypePointer Workgroup %str
+%sh = OpVariable %str_ptr Workgroup
+
+%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(
+          "Cooperative matrix types (or types containing them) can only be "
+          "allocated in Function or Private storage classes or as function "
+          "parameters"));
+}
+
+TEST_F(ValidateMemory, CoopMatMatrixKHRLengthResultTypeBad) {
+  const std::string body = R"(
+OpCapability Shader
+OpCapability Float16
+OpCapability CooperativeMatrixKHR
+OpExtension "SPV_KHR_cooperative_matrix"
+OpExtension "SPV_KHR_vulkan_memory_model"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+%void = OpTypeVoid
+%func = OpTypeFunction %void
+%f16 = OpTypeFloat 16
+%u32 = OpTypeInt 32 0
+%i32 = OpTypeInt 32 1
+
+%u32_8 = OpConstant %u32 8
+%use_A = OpConstant %u32 0
+%subgroup = OpConstant %u32 3
+
+%f16mat = OpTypeCooperativeMatrixKHR %f16 %subgroup %u32_8 %u32_8 %use_A
+
+%main = OpFunction %void None %func
+%main_entry = OpLabel
+
+%1 = OpCooperativeMatrixLengthKHR %i32 %f16mat
+
+OpReturn
+OpFunctionEnd)";
+
+  CompileSuccessfully(body.c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("The Result Type of OpCooperativeMatrixLengthKHR <id> "
+                "'12[%12]' must be OpTypeInt with width 32 and signedness 0"));
+}
+
+TEST_F(ValidateMemory, CoopMatMatrixKHRLengthOperandTypeBad) {
+  const std::string body =
+      R"(
+OpCapability Shader
+OpCapability Float16
+OpCapability CooperativeMatrixKHR
+OpExtension "SPV_KHR_cooperative_matrix"
+OpExtension "SPV_KHR_vulkan_memory_model"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+%void = OpTypeVoid
+%func = OpTypeFunction %void
+%f16 = OpTypeFloat 16
+%u32 = OpTypeInt 32 0
+%i32 = OpTypeInt 32 1
+
+%u32_8 = OpConstant %u32 8
+%use_A = OpConstant %u32 0
+%subgroup = OpConstant %u32 3
+
+%f16mat = OpTypeCooperativeMatrixKHR %f16 %subgroup %u32_8 %u32_8 %use_A
+
+%main = OpFunction %void None %func
+%main_entry = OpLabel
+
+%1 = OpCooperativeMatrixLengthKHR %u32 %u32
+
+OpReturn
+OpFunctionEnd)";
+
+  CompileSuccessfully(body.c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("The type in OpCooperativeMatrixLengthKHR <id> '5[%uint]' "
+                "must be OpTypeCooperativeMatrixKHR"));
+}
+
+TEST_F(ValidateMemory, CoopMatMatrixKHRLengthGood) {
+  const std::string body =
+      R"(
+OpCapability Shader
+OpCapability Float16
+OpCapability CooperativeMatrixKHR
+OpExtension "SPV_KHR_cooperative_matrix"
+OpExtension "SPV_KHR_vulkan_memory_model"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+%void = OpTypeVoid
+%func = OpTypeFunction %void
+%f16 = OpTypeFloat 16
+%u32 = OpTypeInt 32 0
+%i32 = OpTypeInt 32 1
+
+%u32_8 = OpConstant %u32 8
+%use_A = OpConstant %u32 0
+%subgroup = OpConstant %u32 3
+
+%f16mat = OpTypeCooperativeMatrixKHR %f16 %subgroup %u32_8 %u32_8 %use_A
+
+%main = OpFunction %void None %func
+%main_entry = OpLabel
+
+%1 = OpCooperativeMatrixLengthKHR %u32 %f16mat
+
+OpReturn
+OpFunctionEnd)";
+
+  CompileSuccessfully(body.c_str());
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
 TEST_F(ValidateMemory, VulkanRTAOutsideOfStructBad) {
   std::string spirv = R"(
 OpCapability Shader
@@ -2475,6 +2657,7 @@
 OpMemoryModel Logical GLSL450
 OpEntryPoint Fragment %func "func"
 OpExecutionMode %func OriginUpperLeft
+OpDecorate %struct_t Block
 %uint_t = OpTypeInt 32 0
 %array_t = OpTypeRuntimeArray %uint_t
 %struct_t = OpTypeStruct %array_t
@@ -2498,7 +2681,7 @@
           "For Vulkan, OpTypeStruct variables containing OpTypeRuntimeArray "
           "must have storage class of StorageBuffer, PhysicalStorageBuffer, or "
           "Uniform.\n  %6 = "
-          "OpVariable %_ptr_Workgroup__struct_4 Workgroup\n"));
+          "OpVariable %_ptr_Workgroup__struct_2 Workgroup\n"));
 }
 
 TEST_F(ValidateMemory, VulkanRTAInsideStorageBufferStructWithoutBlockBad) {
@@ -2507,6 +2690,7 @@
 OpMemoryModel Logical GLSL450
 OpEntryPoint Fragment %func "func"
 OpExecutionMode %func OriginUpperLeft
+OpDecorate %struct_t BufferBlock
 %uint_t = OpTypeInt 32 0
 %array_t = OpTypeRuntimeArray %uint_t
 %struct_t = OpTypeStruct %array_t
@@ -2529,7 +2713,7 @@
                         "OpTypeRuntimeArray must be decorated with Block if it "
                         "has storage class StorageBuffer or "
                         "PhysicalStorageBuffer.\n  %6 = OpVariable "
-                        "%_ptr_StorageBuffer__struct_4 StorageBuffer\n"));
+                        "%_ptr_StorageBuffer__struct_2 StorageBuffer\n"));
 }
 
 TEST_F(ValidateMemory, VulkanRTAInsideUniformStructGood) {
@@ -2564,6 +2748,7 @@
 OpMemoryModel Logical GLSL450
 OpEntryPoint Fragment %func "func"
 OpExecutionMode %func OriginUpperLeft
+OpDecorate %struct_t Block
 %uint_t = OpTypeInt 32 0
 %array_t = OpTypeRuntimeArray %uint_t
 %struct_t = OpTypeStruct %array_t
@@ -2585,7 +2770,7 @@
               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_4 Uniform\n"));
+                        "%_ptr_Uniform__struct_2 Uniform\n"));
 }
 
 TEST_F(ValidateMemory, VulkanRTAInsideRTABad) {
@@ -3762,9 +3947,8 @@
       HasSubstr("In the Vulkan environment, cannot store to Uniform Blocks"));
 }
 
-using ValidateSizedVariable =
-    spvtest::ValidateBase<std::tuple<std::string, std::string,
-                                     std::string, spv_target_env>>;
+using ValidateSizedVariable = spvtest::ValidateBase<
+    std::tuple<std::string, std::string, std::string, spv_target_env>>;
 
 CodeGenerator GetSizedVariableCodeGenerator(bool is_8bit, bool buffer_block) {
   CodeGenerator generator;
@@ -3774,7 +3958,8 @@
       "\"SPV_KHR_8bit_storage\"\n";
   generator.memory_model_ = "OpMemoryModel Logical GLSL450\n";
   if (is_8bit) {
-    generator.before_types_ = "OpMemberDecorate %char_buffer_block 0 Offset 0\n";
+    generator.before_types_ =
+        "OpMemberDecorate %char_buffer_block 0 Offset 0\n";
     if (buffer_block)
       generator.before_types_ += "OpDecorate %char_buffer_block BufferBlock\n";
 
@@ -4712,6 +4897,224 @@
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
+TEST_F(ValidateMemory, PtrAccessChainArrayStrideBad) {
+  const std::string spirv = R"(
+               OpCapability Shader
+               OpCapability VariablePointersStorageBuffer
+               OpExtension "SPV_KHR_storage_buffer_storage_class"
+               OpExtension "SPV_KHR_variable_pointers"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "foo" %var
+               OpExecutionMode %main LocalSize 1 1 1
+               OpDecorate %var DescriptorSet 0
+               OpDecorate %var Binding 0
+       %uint = OpTypeInt 32 0
+     %uint_0 = OpConstant %uint 0
+     %uint_1 = OpConstant %uint 1
+        %ptr = OpTypePointer StorageBuffer %uint
+       %void = OpTypeVoid
+       %func = OpTypeFunction %void
+        %var = OpVariable %ptr StorageBuffer
+       %main = OpFunction %void None %func
+      %label = OpLabel
+     %access = OpAccessChain %ptr %var
+ %ptr_access = OpPtrAccessChain %ptr %access %uint_1
+               OpReturn
+               OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_5);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_5));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpPtrAccessChain must have a Base whose type is "
+                        "decorated with ArrayStride"));
+}
+
+TEST_F(ValidateMemory, PtrAccessChainArrayStrideSuccess) {
+  const std::string spirv = R"(
+               OpCapability Shader
+               OpCapability VariablePointersStorageBuffer
+               OpExtension "SPV_KHR_storage_buffer_storage_class"
+               OpExtension "SPV_KHR_variable_pointers"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "foo" %var
+               OpExecutionMode %main LocalSize 1 1 1
+               OpDecorate %var DescriptorSet 0
+               OpDecorate %var Binding 00
+               OpDecorate %ptr ArrayStride 4
+       %uint = OpTypeInt 32 0
+     %uint_0 = OpConstant %uint 0
+     %uint_1 = OpConstant %uint 1
+        %ptr = OpTypePointer StorageBuffer %uint
+       %void = OpTypeVoid
+       %func = OpTypeFunction %void
+        %var = OpVariable %ptr StorageBuffer
+       %main = OpFunction %void None %func
+      %label = OpLabel
+     %access = OpAccessChain %ptr %var
+ %ptr_access = OpPtrAccessChain %ptr %access %uint_1
+               OpReturn
+               OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_5);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5));
+}
+
+TEST_F(ValidateMemory, VulkanPtrAccessChainStorageBufferSuccess) {
+  const std::string spirv = R"(
+               OpCapability Shader
+               OpCapability VariablePointersStorageBuffer
+               OpExtension "SPV_KHR_storage_buffer_storage_class"
+               OpExtension "SPV_KHR_variable_pointers"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "foo" %var
+               OpExecutionMode %main LocalSize 1 1 1
+               OpDecorate %_runtimearr_uint ArrayStride 4
+               OpMemberDecorate %_struct_10 0 Offset 0
+               OpDecorate %_struct_10 Block
+               OpDecorate %var DescriptorSet 0
+               OpDecorate %var Binding 0
+               OpDecorate %_ptr_StorageBuffer_uint ArrayStride 4
+       %uint = OpTypeInt 32 0
+     %uint_0 = OpConstant %uint 0
+     %uint_1 = OpConstant %uint 1
+%_runtimearr_uint = OpTypeRuntimeArray %uint
+ %_struct_10 = OpTypeStruct %_runtimearr_uint
+%_ptr_StorageBuffer__struct_10 = OpTypePointer StorageBuffer %_struct_10
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+       %void = OpTypeVoid
+      %func2 = OpTypeFunction %void %_ptr_StorageBuffer_uint
+      %func1 = OpTypeFunction %void
+         %var = OpVariable %_ptr_StorageBuffer__struct_10 StorageBuffer
+     %called = OpFunction %void None %func2
+      %param = OpFunctionParameter %_ptr_StorageBuffer_uint
+     %label2 = OpLabel
+ %ptr_access = OpPtrAccessChain %_ptr_StorageBuffer_uint %param %uint_1
+               OpReturn
+               OpFunctionEnd
+       %main = OpFunction %void None %func1
+     %label1 = OpLabel
+     %access = OpAccessChain %_ptr_StorageBuffer_uint %var %uint_0 %uint_0
+       %call = OpFunctionCall %void %called %access
+               OpReturn
+               OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateMemory, VulkanPtrAccessChainStorageBufferCapability) {
+  const std::string spirv = R"(
+               OpCapability Shader
+               OpCapability PhysicalStorageBufferAddresses
+               OpExtension "SPV_KHR_storage_buffer_storage_class"
+               OpExtension "SPV_KHR_variable_pointers"
+               OpMemoryModel PhysicalStorageBuffer64 GLSL450
+               OpEntryPoint GLCompute %main "foo" %var
+               OpExecutionMode %main LocalSize 1 1 1
+               OpDecorate %_runtimearr_uint ArrayStride 4
+               OpMemberDecorate %_struct_10 0 Offset 0
+               OpDecorate %_struct_10 Block
+               OpDecorate %var DescriptorSet 0
+               OpDecorate %var Binding 0
+               OpDecorate %_ptr_StorageBuffer_uint ArrayStride 4
+       %uint = OpTypeInt 32 0
+     %uint_0 = OpConstant %uint 0
+     %uint_1 = OpConstant %uint 1
+%_runtimearr_uint = OpTypeRuntimeArray %uint
+ %_struct_10 = OpTypeStruct %_runtimearr_uint
+%_ptr_StorageBuffer__struct_10 = OpTypePointer StorageBuffer %_struct_10
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+       %void = OpTypeVoid
+       %func = OpTypeFunction %void
+         %var = OpVariable %_ptr_StorageBuffer__struct_10 StorageBuffer
+       %main = OpFunction %void None %func
+      %label = OpLabel
+     %access = OpAccessChain %_ptr_StorageBuffer_uint %var %uint_0 %uint_0
+ %ptr_access = OpPtrAccessChain %_ptr_StorageBuffer_uint %access %uint_1
+               OpReturn
+               OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-Base-07652"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpPtrAccessChain Base operand pointing to "
+                        "StorageBuffer storage class must use VariablePointers "
+                        "or VariablePointersStorageBuffer capability"));
+}
+
+TEST_F(ValidateMemory, VulkanPtrAccessChainWorkgroupCapability) {
+  const std::string spirv = R"(
+               OpCapability Shader
+               OpCapability VariablePointersStorageBuffer
+               OpExtension "SPV_KHR_storage_buffer_storage_class"
+               OpExtension "SPV_KHR_variable_pointers"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "foo" %var
+               OpExecutionMode %main LocalSize 1 1 1
+               OpDecorate %_ptr_Workgroup_uint ArrayStride 4
+       %uint = OpTypeInt 32 0
+     %uint_0 = OpConstant %uint 0
+     %uint_1 = OpConstant %uint 1
+%_arr_uint = OpTypeArray %uint %uint_1
+%_ptr_Workgroup__arr_uint = OpTypePointer Workgroup %_arr_uint
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+       %void = OpTypeVoid
+       %func = OpTypeFunction %void
+        %var = OpVariable %_ptr_Workgroup__arr_uint Workgroup
+       %main = OpFunction %void None %func
+      %label = OpLabel
+     %access = OpAccessChain %_ptr_Workgroup_uint %var %uint_0
+ %ptr_access = OpPtrAccessChain %_ptr_Workgroup_uint %access %uint_1
+               OpReturn
+               OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-Base-07651"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpPtrAccessChain Base operand pointing to Workgroup "
+                        "storage class must use VariablePointers capability"));
+}
+
+TEST_F(ValidateMemory, VulkanPtrAccessChainWorkgroupNoArrayStrideSuccess) {
+  const std::string spirv = R"(
+               OpCapability Shader
+               OpCapability VariablePointers
+               OpExtension "SPV_KHR_storage_buffer_storage_class"
+               OpExtension "SPV_KHR_variable_pointers"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "foo" %var
+               OpExecutionMode %main LocalSize 1 1 1
+       %uint = OpTypeInt 32 0
+     %uint_0 = OpConstant %uint 0
+     %uint_1 = OpConstant %uint 1
+%_arr_uint = OpTypeArray %uint %uint_1
+%_ptr_Workgroup__arr_uint = OpTypePointer Workgroup %_arr_uint
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+       %void = OpTypeVoid
+       %func = OpTypeFunction %void
+        %var = OpVariable %_ptr_Workgroup__arr_uint Workgroup
+       %main = OpFunction %void None %func
+      %label = OpLabel
+     %access = OpAccessChain %_ptr_Workgroup_uint %var %uint_0
+ %ptr_access = OpPtrAccessChain %_ptr_Workgroup_uint %access %uint_1
+               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_mesh_shading_test.cpp b/test/val/val_mesh_shading_test.cpp
index ce6999d..a7b96a4 100644
--- a/test/val/val_mesh_shading_test.cpp
+++ b/test/val/val_mesh_shading_test.cpp
@@ -14,7 +14,6 @@
 
 // Tests instructions from SPV_EXT_mesh_shader
 
-#include <sstream>
 #include <string>
 
 #include "gmock/gmock.h"
diff --git a/test/val/val_modes_test.cpp b/test/val/val_modes_test.cpp
index 689f0ba..8dc0fbc 100644
--- a/test/val/val_modes_test.cpp
+++ b/test/val/val_modes_test.cpp
@@ -18,7 +18,6 @@
 
 #include "gmock/gmock.h"
 #include "source/spirv_target_env.h"
-#include "test/test_fixture.h"
 #include "test/unit_spirv.h"
 #include "test/val/val_fixtures.h"
 
@@ -579,6 +578,11 @@
     sstr << "OpCapability Kernel\n";
     if (env == SPV_ENV_UNIVERSAL_1_3) {
       sstr << "OpCapability SubgroupDispatch\n";
+    } else if (env == SPV_ENV_UNIVERSAL_1_5) {
+      sstr << "OpCapability TileImageColorReadAccessEXT\n";
+      sstr << "OpCapability TileImageDepthReadAccessEXT\n";
+      sstr << "OpCapability TileImageStencilReadAccessEXT\n";
+      sstr << "OpExtension \"SPV_EXT_shader_tile_image\"\n";
     }
   }
   sstr << "OpMemoryModel Logical GLSL450\n";
@@ -702,6 +706,27 @@
                    "DepthLess", "DepthUnchanged"),
             Values(SPV_ENV_UNIVERSAL_1_0)));
 
+INSTANTIATE_TEST_SUITE_P(ValidateModeFragmentOnlyGoodSpv15,
+                         ValidateModeExecution,
+                         Combine(Values(SPV_SUCCESS), Values(""),
+                                 Values("Fragment"),
+                                 Values("NonCoherentColorAttachmentReadEXT",
+                                        "NonCoherentDepthAttachmentReadEXT",
+                                        "NonCoherentStencilAttachmentReadEXT"),
+                                 Values(SPV_ENV_UNIVERSAL_1_5)));
+
+INSTANTIATE_TEST_SUITE_P(
+    ValidateModeFragmentOnlyBadSpv15, ValidateModeExecution,
+    Combine(Values(SPV_ERROR_INVALID_DATA),
+            Values("Execution mode can only be used with the Fragment "
+                   "execution model."),
+            Values("Geometry", "TessellationControl", "TessellationEvaluation",
+                   "GLCompute", "Vertex", "Kernel"),
+            Values("NonCoherentColorAttachmentReadEXT",
+                   "NonCoherentDepthAttachmentReadEXT",
+                   "NonCoherentStencilAttachmentReadEXT"),
+            Values(SPV_ENV_UNIVERSAL_1_5)));
+
 INSTANTIATE_TEST_SUITE_P(ValidateModeKernelOnlyGoodSpv13, ValidateModeExecution,
                          Combine(Values(SPV_SUCCESS), Values(""),
                                  Values("Kernel"),
diff --git a/test/val/val_non_uniform_test.cpp b/test/val/val_non_uniform_test.cpp
index 3840eec..a020500 100644
--- a/test/val/val_non_uniform_test.cpp
+++ b/test/val/val_non_uniform_test.cpp
@@ -44,6 +44,8 @@
 OpCapability GroupNonUniformArithmetic
 OpCapability GroupNonUniformClustered
 OpCapability GroupNonUniformQuad
+OpCapability GroupNonUniformPartitionedNV
+OpExtension "SPV_NV_shader_subgroup_partitioned"
 )";
 
   ss << capabilities_and_extensions;
@@ -62,16 +64,27 @@
 %float = OpTypeFloat 32
 %u32vec4 = OpTypeVector %u32 4
 %u32vec3 = OpTypeVector %u32 3
+%v2bool = OpTypeVector %bool 2
+%v4float = OpTypeVector %float 4
+%struct = OpTypeStruct %int
+%v4int = OpTypeVector %int 4
 
 %true = OpConstantTrue %bool
 %false = OpConstantFalse %bool
 
 %u32_0 = OpConstant %u32 0
+%int_0 = OpConstant %int 0
 
 %float_0 = OpConstant %float 0
 
 %u32vec4_null = OpConstantComposite %u32vec4 %u32_0 %u32_0 %u32_0 %u32_0
 %u32vec3_null = OpConstantComposite %u32vec3 %u32_0 %u32_0 %u32_0
+%v2bool_false = OpConstantNull %v2bool
+%v4float_null = OpConstantNull %v4float
+%struct_null = OpConstantNull %struct
+%v4int_null = OpConstantComposite %v4int %int_0 %int_0 %int_0 %int_0
+
+%u32_undef = OpUndef %u32
 
 %cross_device = OpConstant %u32 0
 %device = OpConstant %u32 1
@@ -97,37 +110,71 @@
   return ss.str();
 }
 
-SpvScope scopes[] = {SpvScopeCrossDevice, SpvScopeDevice, SpvScopeWorkgroup,
-                     SpvScopeSubgroup, SpvScopeInvocation};
+spv::Scope scopes[] = {spv::Scope::CrossDevice, spv::Scope::Device,
+                       spv::Scope::Workgroup, spv::Scope::Subgroup,
+                       spv::Scope::Invocation};
 
 using ValidateGroupNonUniform = spvtest::ValidateBase<bool>;
 using GroupNonUniform = spvtest::ValidateBase<
-    std::tuple<std::string, std::string, SpvScope, std::string, std::string>>;
+    std::tuple<std::string, std::string, spv::Scope, std::string, std::string>>;
 
-std::string ConvertScope(SpvScope scope) {
+std::string ConvertScope(spv::Scope scope) {
   switch (scope) {
-    case SpvScopeCrossDevice:
+    case spv::Scope::CrossDevice:
       return "%cross_device";
-    case SpvScopeDevice:
+    case spv::Scope::Device:
       return "%device";
-    case SpvScopeWorkgroup:
+    case spv::Scope::Workgroup:
       return "%workgroup";
-    case SpvScopeSubgroup:
+    case spv::Scope::Subgroup:
       return "%subgroup";
-    case SpvScopeInvocation:
+    case spv::Scope::Invocation:
       return "%invocation";
     default:
       return "";
   }
 }
 
+std::string ConvertMatch(const std::string& type) {
+  if (type == "%bool") {
+    return "%true";
+  } else if (type == "%u32") {
+    return "%u32_0";
+  } else if (type == "%int") {
+    return "%int_0";
+  } else if (type == "%float") {
+    return "%float_0";
+  } else if (type == "%u32vec4") {
+    return "%u32vec4_null";
+  } else if (type == "%u32vec3") {
+    return "%u32vec3_null";
+  } else if (type == "%v2bool") {
+    return "%v2bool_false";
+  } else if (type == "%v4float") {
+    return "%v4float_null";
+  } else if (type == "%struct") {
+    return "%struct_null";
+  } else if (type == "%v4int") {
+    return "%v4int_null";
+  }
+
+  return "INVALID";
+}
+
 TEST_P(GroupNonUniform, Vulkan1p1) {
   std::string opcode = std::get<0>(GetParam());
   std::string type = std::get<1>(GetParam());
-  SpvScope execution_scope = std::get<2>(GetParam());
+  spv::Scope execution_scope = std::get<2>(GetParam());
   std::string args = std::get<3>(GetParam());
   std::string error = std::get<4>(GetParam());
 
+  const std::string match = "match_res";
+  size_t pos = std::string::npos;
+  while ((pos = args.find(match)) != std::string::npos) {
+    const std::string replace = ConvertMatch(type);
+    args = args.substr(0, pos) + replace + args.substr(pos + match.size());
+  }
+
   std::ostringstream sstr;
   sstr << "%result = " << opcode << " ";
   sstr << type << " ";
@@ -137,7 +184,7 @@
   CompileSuccessfully(GenerateShaderCode(sstr.str()), SPV_ENV_VULKAN_1_1);
   spv_result_t result = ValidateInstructions(SPV_ENV_VULKAN_1_1);
   if (error == "") {
-    if (execution_scope == SpvScopeSubgroup) {
+    if (execution_scope == spv::Scope::Subgroup) {
       EXPECT_EQ(SPV_SUCCESS, result);
     } else {
       EXPECT_EQ(SPV_ERROR_INVALID_DATA, result);
@@ -157,10 +204,17 @@
 TEST_P(GroupNonUniform, Spirv1p3) {
   std::string opcode = std::get<0>(GetParam());
   std::string type = std::get<1>(GetParam());
-  SpvScope execution_scope = std::get<2>(GetParam());
+  spv::Scope execution_scope = std::get<2>(GetParam());
   std::string args = std::get<3>(GetParam());
   std::string error = std::get<4>(GetParam());
 
+  const std::string match = "match_res";
+  size_t pos = std::string::npos;
+  while ((pos = args.find(match)) != std::string::npos) {
+    const std::string replace = ConvertMatch(type);
+    args = args.substr(0, pos) + replace + args.substr(pos + match.size());
+  }
+
   std::ostringstream sstr;
   sstr << "%result = " << opcode << " ";
   sstr << type << " ";
@@ -170,8 +224,8 @@
   CompileSuccessfully(GenerateShaderCode(sstr.str()), SPV_ENV_UNIVERSAL_1_3);
   spv_result_t result = ValidateInstructions(SPV_ENV_UNIVERSAL_1_3);
   if (error == "") {
-    if (execution_scope == SpvScopeSubgroup ||
-        execution_scope == SpvScopeWorkgroup) {
+    if (execution_scope == spv::Scope::Subgroup ||
+        execution_scope == spv::Scope::Workgroup) {
       EXPECT_EQ(SPV_SUCCESS, result);
     } else {
       EXPECT_EQ(SPV_ERROR_INVALID_DATA, result);
@@ -280,17 +334,541 @@
     GroupNonUniformBallotBitCountBadResultType, GroupNonUniform,
     Combine(
         Values("OpGroupNonUniformBallotBitCount"), Values("%float", "%int"),
-        Values(SpvScopeSubgroup), Values("Reduce %u32vec4_null"),
+        Values(spv::Scope::Subgroup), Values("Reduce %u32vec4_null"),
         Values("Expected Result Type to be an unsigned integer type scalar.")));
 
 INSTANTIATE_TEST_SUITE_P(GroupNonUniformBallotBitCountBadValue, GroupNonUniform,
                          Combine(Values("OpGroupNonUniformBallotBitCount"),
-                                 Values("%u32"), Values(SpvScopeSubgroup),
+                                 Values("%u32"), Values(spv::Scope::Subgroup),
                                  Values("Reduce %u32vec3_null", "Reduce %u32_0",
                                         "Reduce %float_0"),
                                  Values("Expected Value to be a vector of four "
                                         "components of integer type scalar")));
 
+INSTANTIATE_TEST_SUITE_P(GroupNonUniformElectGood, GroupNonUniform,
+                         Combine(Values("OpGroupNonUniformElect"),
+                                 Values("%bool"), Values(spv::Scope::Subgroup),
+                                 Values(""), Values("")));
+
+INSTANTIATE_TEST_SUITE_P(
+    GroupNonUniformElectBadResultType, GroupNonUniform,
+    Combine(Values("OpGroupNonUniformElect"),
+            Values("%void", "%u32", "%int", "%float", "%u32vec4", "%u32vec3",
+                   "%v2bool", "%v4float", "%struct"),
+            Values(spv::Scope::Subgroup), Values(""),
+            Values("Result must be a boolean scalar type")));
+
+INSTANTIATE_TEST_SUITE_P(GroupNonUniformAnyAllGood, GroupNonUniform,
+                         Combine(Values("OpGroupNonUniformAny",
+                                        "OpGroupNonUniformAll"),
+                                 Values("%bool"), Values(spv::Scope::Subgroup),
+                                 Values("%true", "%false"), Values("")));
+
+INSTANTIATE_TEST_SUITE_P(
+    GroupNonUniformAnyAllBadResultType, GroupNonUniform,
+    Combine(Values("OpGroupNonUniformAny", "OpGroupNonUniformAll"),
+            Values("%void", "%u32", "%int", "%float", "%u32vec4", "%u32vec3",
+                   "%v2bool", "%v4float", "%struct"),
+            Values(spv::Scope::Subgroup), Values("%true"),
+            Values("Result must be a boolean scalar type")));
+
+INSTANTIATE_TEST_SUITE_P(
+    GroupNonUniformAnyAllBadOperand, GroupNonUniform,
+    Combine(Values("OpGroupNonUniformAny", "OpGroupNonUniformAll"),
+            Values("%bool"), Values(spv::Scope::Subgroup),
+            Values("%u32_0", "%int_0", "%float_0", "%u32vec4_null",
+                   "%u32vec3_null", "%v2bool_false", "%v4float_null",
+                   "%struct_null"),
+            Values("Predicate must be a boolean scalar type")));
+
+INSTANTIATE_TEST_SUITE_P(GroupNonUniformAllEqualGood, GroupNonUniform,
+                         Combine(Values("OpGroupNonUniformAllEqual"),
+                                 Values("%bool"), Values(spv::Scope::Subgroup),
+                                 Values("%true", "%false"), Values("")));
+
+INSTANTIATE_TEST_SUITE_P(
+    GroupNonUniformAllEqualBadResultType, GroupNonUniform,
+    Combine(Values("OpGroupNonUniformAllEqual"),
+            Values("%void", "%u32", "%int", "%float", "%u32vec4", "%u32vec3",
+                   "%v2bool", "%v4float", "%struct"),
+            Values(spv::Scope::Subgroup), Values("%true"),
+            Values("Result must be a boolean scalar type")));
+
+INSTANTIATE_TEST_SUITE_P(
+    GroupNonUniformAllEqualBadOperand, GroupNonUniform,
+    Combine(Values("OpGroupNonUniformAllEqual"), Values("%bool"),
+            Values(spv::Scope::Subgroup), Values("%struct_null"),
+            Values("Value must be a scalar or vector of integer, "
+                   "floating-point, or boolean type")));
+
+INSTANTIATE_TEST_SUITE_P(GroupNonUniformBroadcastGood, GroupNonUniform,
+                         Combine(Values("OpGroupNonUniformBroadcast",
+                                        "OpGroupNonUniformQuadBroadcast",
+                                        "OpGroupNonUniformQuadSwap"),
+                                 Values("%bool", "%u32", "%int", "%float",
+                                        "%u32vec4", "%u32vec3", "%v2bool",
+                                        "%v4float", "%v4int"),
+                                 Values(spv::Scope::Subgroup),
+                                 Values("match_res %u32_0"), Values("")));
+
+INSTANTIATE_TEST_SUITE_P(
+    GroupNonUniformBroadcastShuffleBadResultType, GroupNonUniform,
+    Combine(Values("OpGroupNonUniformBroadcast", "OpGroupNonUniformShuffle",
+                   "OpGroupNonUniformShuffleXor", "OpGroupNonUniformShuffleUp",
+                   "OpGroupNonUniformShuffleDown",
+                   "OpGroupNonUniformQuadBroadcast",
+                   "OpGroupNonUniformQuadSwap"),
+            Values("%void", "%struct"), Values(spv::Scope::Subgroup),
+            Values("%u32_0 %u32_0"),
+            Values("Result must be a scalar or vector of integer, "
+                   "floating-point, or boolean type")));
+
+INSTANTIATE_TEST_SUITE_P(
+    GroupNonUniformBroadcastShuffleBadOperand1, GroupNonUniform,
+    Combine(Values("OpGroupNonUniformBroadcast", "OpGroupNonUniformShuffle",
+                   "OpGroupNonUniformShuffleXor", "OpGroupNonUniformShuffleUp",
+                   "OpGroupNonUniformShuffleDown",
+                   "OpGroupNonUniformQuadBroadcast",
+                   "OpGroupNonUniformQuadSwap"),
+            Values("%bool"), Values(spv::Scope::Subgroup),
+            Values("%u32_0 %u32_0", "%int_0 %u32_0", "%float_0 %u32_0",
+                   "%u32vec4_null %u32_0", "%u32vec3_null %u32_0",
+                   "%v2bool_false %u32_0", "%v4float_null %u32_0",
+                   "%struct_null %u32_0", "%v4int_null %u32_0"),
+            Values("The type of Value must match the Result type")));
+
+INSTANTIATE_TEST_SUITE_P(
+    GroupNonUniformBroadcastShuffleBadOperand2, GroupNonUniform,
+    Combine(Values("OpGroupNonUniformBroadcast", "OpGroupNonUniformShuffle",
+                   "OpGroupNonUniformShuffleXor", "OpGroupNonUniformShuffleUp",
+                   "OpGroupNonUniformShuffleDown",
+                   "OpGroupNonUniformQuadBroadcast",
+                   "OpGroupNonUniformQuadSwap"),
+            Values("%bool"), Values(spv::Scope::Subgroup),
+            Values("%true %true", "%true %int_0", "%true %float_0",
+                   "%true %u32vec4_null", "%true %u32vec3_null",
+                   "%true %v4float_null", "%true %v2bool_false",
+                   "%true %struct_null", "%true %v4int_null"),
+            Values("must be an unsigned integer scalar")));
+
+INSTANTIATE_TEST_SUITE_P(GroupNonUniformBroadcastShuffleOperand2NotConstant,
+                         GroupNonUniform,
+                         Combine(Values("OpGroupNonUniformBroadcast",
+                                        "OpGroupNonUniformQuadBroadcast",
+                                        "OpGroupNonUniformQuadSwap"),
+                                 Values("%bool"), Values(spv::Scope::Subgroup),
+                                 Values("%true %u32_undef"),
+                                 Values("must be a constant instruction")));
+
+INSTANTIATE_TEST_SUITE_P(GroupNonUniformBroadcastFirstGood, GroupNonUniform,
+                         Combine(Values("OpGroupNonUniformBroadcastFirst"),
+                                 Values("%bool", "%u32", "%int", "%float",
+                                        "%u32vec4", "%u32vec3", "%v2bool",
+                                        "%v4float", "%v4int"),
+                                 Values(spv::Scope::Subgroup),
+                                 Values("match_res"), Values("")));
+
+INSTANTIATE_TEST_SUITE_P(
+    GroupNonUniformBroadcasFirsttBadResultType, GroupNonUniform,
+    Combine(Values("OpGroupNonUniformBroadcastFirst"),
+            Values("%void", "%struct"), Values(spv::Scope::Subgroup),
+            Values("%u32_0"),
+            Values("Result must be a scalar or vector of integer, "
+                   "floating-point, or boolean type")));
+
+INSTANTIATE_TEST_SUITE_P(
+    GroupNonUniformBroadcastBadOperand, GroupNonUniform,
+    Combine(Values("OpGroupNonUniformBroadcastFirst"), Values("%bool"),
+            Values(spv::Scope::Subgroup),
+            Values("%u32_0", "%int_0", "%float_0", "%u32vec4_null",
+                   "%u32vec3_null", "%v2bool_false", "%v4float_null",
+                   "%struct_null", "%v4int_null"),
+            Values("The type of Value must match the Result type")));
+
+INSTANTIATE_TEST_SUITE_P(GroupNonUniformBallotGood, GroupNonUniform,
+                         Combine(Values("OpGroupNonUniformBallot"),
+                                 Values("%u32vec4"),
+                                 Values(spv::Scope::Subgroup),
+                                 Values("%true", "%false"), Values("")));
+
+INSTANTIATE_TEST_SUITE_P(
+    GroupNonUniformBallotBadResultType, GroupNonUniform,
+    Combine(Values("OpGroupNonUniformBallot"),
+            Values("%void", "%bool", "%u32", "%int", "%float", "%u32vec3",
+                   "%v2bool", "%v4float", "%struct", "%v4int"),
+            Values(spv::Scope::Subgroup), Values("%true", "%false"),
+            Values("Result must be a 4-component unsigned integer vector")));
+
+INSTANTIATE_TEST_SUITE_P(GroupNonUniformBallotBadOperand, GroupNonUniform,
+                         Combine(Values("OpGroupNonUniformBallot"),
+                                 Values("%u32vec4"),
+                                 Values(spv::Scope::Subgroup),
+                                 Values("%u32_0", "%int_0", "%float_0",
+                                        "%u32vec4_null", "%u32vec3_null",
+                                        "%v2bool_false", "%v4float_null",
+                                        "%struct_null", "%v4int_null"),
+                                 Values("Predicate must be a boolean scalar")));
+
+INSTANTIATE_TEST_SUITE_P(GroupNonUniformInverseBallotGood, GroupNonUniform,
+                         Combine(Values("OpGroupNonUniformInverseBallot"),
+                                 Values("%bool"), Values(spv::Scope::Subgroup),
+                                 Values("%u32vec4_null"), Values("")));
+
+INSTANTIATE_TEST_SUITE_P(
+    GroupNonUniformInverseBallotBadResultType, GroupNonUniform,
+    Combine(Values("OpGroupNonUniformInverseBallot"),
+            Values("%void", "%u32", "%int", "%float", "%u32vec4", "%u32vec3",
+                   "%v2bool", "%v4float", "%struct", "%v4int"),
+            Values(spv::Scope::Subgroup), Values("%u32vec4_null"),
+            Values("Result must be a boolean scalar")));
+
+INSTANTIATE_TEST_SUITE_P(
+    GroupNonUniformInverseBallotBadOperand, GroupNonUniform,
+    Combine(Values("OpGroupNonUniformInverseBallot"), Values("%bool"),
+            Values(spv::Scope::Subgroup),
+            Values("%true", "%false", "%u32_0", "%int_0", "%float_0",
+                   "%u32vec3_null", "%v2bool_false", "%v4float_null",
+                   "%struct_null", "%v4int_null"),
+            Values("Value must be a 4-component unsigned integer vector")));
+
+INSTANTIATE_TEST_SUITE_P(GroupNonUniformBallotBitExtractGood, GroupNonUniform,
+                         Combine(Values("OpGroupNonUniformBallotBitExtract"),
+                                 Values("%bool"), Values(spv::Scope::Subgroup),
+                                 Values("%u32vec4_null %u32_0"), Values("")));
+
+INSTANTIATE_TEST_SUITE_P(
+    GroupNonUniformBallotBitExtractBadResultType, GroupNonUniform,
+    Combine(Values("OpGroupNonUniformBallotBitExtract"),
+            Values("%void", "%u32", "%int", "%float", "%u32vec4", "%u32vec3",
+                   "%v2bool", "%v4float", "%struct", "%v4int"),
+            Values(spv::Scope::Subgroup), Values("%u32vec4_null %u32_0"),
+            Values("Result must be a boolean scalar")));
+
+INSTANTIATE_TEST_SUITE_P(
+    GroupNonUniformBallotBitExtractBadOperand1, GroupNonUniform,
+    Combine(Values("OpGroupNonUniformBallotBitExtract"), Values("%bool"),
+            Values(spv::Scope::Subgroup),
+            Values("%true %u32_0", "%false %u32_0", "%u32_0 %u32_0",
+                   "%int_0 %u32_0", "%float_0 %u32_0", "%u32vec3_null %u32_0",
+                   "%v2bool_false %u32_0", "%v4float_null %u32_0",
+                   "%struct_null %u32_0", "%v4int_null %u32_0"),
+            Values("Value must be a 4-component unsigned integer vector")));
+
+INSTANTIATE_TEST_SUITE_P(
+    GroupNonUniformBallotBitExtractBadOperand2, GroupNonUniform,
+    Combine(Values("OpGroupNonUniformBallotBitExtract"), Values("%bool"),
+            Values(spv::Scope::Subgroup),
+            Values("%u32vec4_null %true", "%u32vec4_null %false",
+                   "%u32vec4_null %int_0", "%u32vec4_null %float_0",
+                   "%u32vec4_null %u32vec3_null", "%u32vec4_null %v2bool_false",
+                   "%u32vec4_null %v4float_null", "%u32vec4_null %struct_null",
+                   "%u32vec4_null %v4int_null"),
+            Values("Id must be an unsigned integer scalar")));
+
+INSTANTIATE_TEST_SUITE_P(GroupNonUniformBallotFindGood, GroupNonUniform,
+                         Combine(Values("OpGroupNonUniformBallotFindLSB",
+                                        "OpGroupNonUniformBallotFindMSB"),
+                                 Values("%u32"), Values(spv::Scope::Subgroup),
+                                 Values("%u32vec4_null"), Values("")));
+
+INSTANTIATE_TEST_SUITE_P(
+    GroupNonUniformBallotFindBadResultType, GroupNonUniform,
+    Combine(Values("OpGroupNonUniformBallotFindLSB",
+                   "OpGroupNonUniformBallotFindMSB"),
+            Values("%void", "%bool", "%int", "%float", "%u32vec4", "%u32vec3",
+                   "%v2bool", "%v4float", "%struct", "%v4int"),
+            Values(spv::Scope::Subgroup), Values("%u32vec4_null"),
+            Values("Result must be an unsigned integer scalar")));
+
+INSTANTIATE_TEST_SUITE_P(
+    GroupNonUniformBallotFindBadOperand, GroupNonUniform,
+    Combine(Values("OpGroupNonUniformBallotFindLSB",
+                   "OpGroupNonUniformBallotFindMSB"),
+            Values("%u32"), Values(spv::Scope::Subgroup),
+            Values("%true", "%false", "%u32_0", "%int_0", "%float_0",
+                   "%u32vec3_null", "%v2bool_false", "%v4float_null",
+                   "%struct_null", "%v4int_null"),
+            Values("Value must be a 4-component unsigned integer vector")));
+
+INSTANTIATE_TEST_SUITE_P(
+    GroupNonUniformIntegerArithmeticGood, GroupNonUniform,
+    Combine(Values("OpGroupNonUniformIAdd", "OpGroupNonUniformIMul",
+                   "OpGroupNonUniformSMin", "OpGroupNonUniformSMax",
+                   "OpGroupNonUniformBitwiseAnd", "OpGroupNonUniformBitwiseOr",
+                   "OpGroupNonUniformBitwiseXor"),
+            Values("%u32", "%int", "%u32vec4", "%u32vec3", "%v4int"),
+            Values(spv::Scope::Subgroup),
+            Values("Reduce match_res", "InclusiveScan match_res",
+                   "ExclusiveScan match_res",
+                   "ClusteredReduce match_res %u32_0",
+                   "PartitionedReduceNV match_res %u32vec4_null",
+                   "PartitionedInclusiveScanNV match_res %u32vec4_null",
+                   "PartitionedExclusiveScanNV match_res %v4int_null"),
+            Values("")));
+
+INSTANTIATE_TEST_SUITE_P(
+    GroupNonUniformIntegerArithmeticBadResultType, GroupNonUniform,
+    Combine(Values("OpGroupNonUniformIAdd", "OpGroupNonUniformIMul",
+                   "OpGroupNonUniformSMin", "OpGroupNonUniformSMax",
+                   "OpGroupNonUniformBitwiseAnd", "OpGroupNonUniformBitwiseOr",
+                   "OpGroupNonUniformBitwiseXor"),
+            Values("%bool", "%float", "%v4float", "%struct"),
+            Values(spv::Scope::Subgroup),
+            Values("Reduce match_res", "InclusiveScan match_res",
+                   "ExclusiveScan match_res",
+                   "ClusteredReduce match_res %u32_0"),
+            Values("Result must be an integer scalar or vector")));
+
+INSTANTIATE_TEST_SUITE_P(
+    GroupNonUniformIntegerArithmeticBadValue, GroupNonUniform,
+    Combine(Values("OpGroupNonUniformIAdd", "OpGroupNonUniformIMul",
+                   "OpGroupNonUniformSMin", "OpGroupNonUniformSMax",
+                   "OpGroupNonUniformBitwiseAnd", "OpGroupNonUniformBitwiseOr",
+                   "OpGroupNonUniformBitwiseXor"),
+            Values("%int", "%u32vec4", "%u32vec3", "%v4int"),
+            Values(spv::Scope::Subgroup),
+            Values("Reduce %u32_0", "InclusiveScan %u32_0",
+                   "ExclusiveScan %u32_0", "ClusteredReduce %u32_0 %u32_0"),
+            Values("The type of Value must match the Result type")));
+
+INSTANTIATE_TEST_SUITE_P(
+    GroupNonUniformIntegerArithmeticMissingClusterSize, GroupNonUniform,
+    Combine(
+        Values("OpGroupNonUniformIAdd", "OpGroupNonUniformIMul",
+               "OpGroupNonUniformSMin", "OpGroupNonUniformUMin",
+               "OpGroupNonUniformSMax", "OpGroupNonUniformUMax",
+               "OpGroupNonUniformBitwiseAnd", "OpGroupNonUniformBitwiseOr",
+               "OpGroupNonUniformBitwiseXor"),
+        Values("%u32"), Values(spv::Scope::Subgroup),
+        Values("ClusteredReduce match_res"),
+        Values(
+            "ClusterSize must be present when Operation is ClusteredReduce")));
+
+INSTANTIATE_TEST_SUITE_P(
+    GroupNonUniformIntegerArithmeticMissingBallot, GroupNonUniform,
+    Combine(
+        Values("OpGroupNonUniformIAdd", "OpGroupNonUniformIMul",
+               "OpGroupNonUniformSMin", "OpGroupNonUniformUMin",
+               "OpGroupNonUniformSMax", "OpGroupNonUniformUMax",
+               "OpGroupNonUniformBitwiseAnd", "OpGroupNonUniformBitwiseOr",
+               "OpGroupNonUniformBitwiseXor"),
+        Values("%u32"), Values(spv::Scope::Subgroup),
+        Values("PartitionedReduceNV match_res",
+               "PartitionedInclusiveScanNV match_res",
+               "PartitionedExclusiveScanNV match_res"),
+        Values("Ballot must be present when Operation is PartitionedReduceNV, "
+               "PartitionedInclusiveScanNV, or PartitionedExclusiveScanNV")));
+
+INSTANTIATE_TEST_SUITE_P(
+    GroupNonUniformIntegerArithmeticBadClusterSizeType, GroupNonUniform,
+    Combine(Values("OpGroupNonUniformIAdd", "OpGroupNonUniformIMul",
+                   "OpGroupNonUniformSMin", "OpGroupNonUniformUMin",
+                   "OpGroupNonUniformSMax", "OpGroupNonUniformUMax",
+                   "OpGroupNonUniformBitwiseAnd", "OpGroupNonUniformBitwiseOr",
+                   "OpGroupNonUniformBitwiseXor"),
+            Values("%u32"), Values(spv::Scope::Subgroup),
+            Values("ClusteredReduce match_res %true",
+                   "ClusteredReduce match_res %false",
+                   "ClusteredReduce match_res %int_0",
+                   "ClusteredReduce match_res %float_0",
+                   "ClusteredReduce match_res %u32vec4_null",
+                   "ClusteredReduce match_res %u32vec3_null",
+                   "ClusteredReduce match_res %v2bool_false",
+                   "ClusteredReduce match_res %v4float_null",
+                   "ClusteredReduce match_res %struct_null",
+                   "ClusteredReduce match_res %v4int_null"),
+            Values("ClusterSize must be an unsigned integer scalar")));
+
+INSTANTIATE_TEST_SUITE_P(
+    GroupNonUniformIntegerArithmeticBadBallotType, GroupNonUniform,
+    Combine(Values("OpGroupNonUniformIAdd", "OpGroupNonUniformIMul",
+                   "OpGroupNonUniformSMin", "OpGroupNonUniformUMin",
+                   "OpGroupNonUniformSMax", "OpGroupNonUniformUMax",
+                   "OpGroupNonUniformBitwiseAnd", "OpGroupNonUniformBitwiseOr",
+                   "OpGroupNonUniformBitwiseXor"),
+            Values("%u32"), Values(spv::Scope::Subgroup),
+            Values("PartitionedReduceNV match_res %true",
+                   "PartitionedReduceNV match_res %false",
+                   "PartitionedReduceNV match_res %int_0",
+                   "PartitionedReduceNV match_res %float_0",
+                   "PartitionedReduceNV match_res %u32_0",
+                   "PartitionedReduceNV match_res %u32vec3_null",
+                   "PartitionedReduceNV match_res %v2bool_false",
+                   "PartitionedReduceNV match_res %v4float_null",
+                   "PartitionedReduceNV match_res %struct_null"),
+            Values("Ballot must be a 4-component integer vector")));
+
+INSTANTIATE_TEST_SUITE_P(
+    GroupNonUniformIntegerArithmeticClusterSizeNotConstant, GroupNonUniform,
+    Combine(Values("OpGroupNonUniformIAdd", "OpGroupNonUniformIMul",
+                   "OpGroupNonUniformSMin", "OpGroupNonUniformUMin",
+                   "OpGroupNonUniformSMax", "OpGroupNonUniformUMax",
+                   "OpGroupNonUniformBitwiseAnd", "OpGroupNonUniformBitwiseOr",
+                   "OpGroupNonUniformBitwiseXor"),
+            Values("%u32"), Values(spv::Scope::Subgroup),
+            Values("ClusteredReduce match_res %u32_undef"),
+            Values("ClusterSize must be a constant instruction")));
+
+INSTANTIATE_TEST_SUITE_P(
+    GroupNonUniformUnsignedIntegerArithmeticGood, GroupNonUniform,
+    Combine(Values("OpGroupNonUniformUMin", "OpGroupNonUniformUMax"),
+            Values("%u32", "%u32vec4", "%u32vec3"),
+            Values(spv::Scope::Subgroup),
+            Values("Reduce match_res", "InclusiveScan match_res",
+                   "ExclusiveScan match_res",
+                   "ClusteredReduce match_res %u32_0"),
+            Values("")));
+
+INSTANTIATE_TEST_SUITE_P(
+    GroupNonUniformUnsignedIntegerArithmeticBadResultType, GroupNonUniform,
+    Combine(Values("OpGroupNonUniformUMin", "OpGroupNonUniformUMax"),
+            Values("%bool", "%int", "%float", "%v4float", "%struct", "%v4int"),
+            Values(spv::Scope::Subgroup),
+            Values("Reduce match_res", "InclusiveScan match_res",
+                   "ExclusiveScan match_res",
+                   "ClusteredReduce match_res %u32_0"),
+            Values("Result must be an unsigned integer scalar or vector")));
+
+INSTANTIATE_TEST_SUITE_P(
+    GroupNonUniformUnsignedIntegerArithmeticBadValue, GroupNonUniform,
+    Combine(Values("OpGroupNonUniformUMin", "OpGroupNonUniformUMax"),
+            Values("%u32vec4", "%u32vec3"), Values(spv::Scope::Subgroup),
+            Values("Reduce %u32_0", "InclusiveScan %u32_0",
+                   "ExclusiveScan %u32_0", "ClusteredReduce %u32_0 %u32_0"),
+            Values("The type of Value must match the Result type")));
+
+INSTANTIATE_TEST_SUITE_P(
+    GroupNonUniformFloatArithmeticGood, GroupNonUniform,
+    Combine(Values("OpGroupNonUniformFAdd", "OpGroupNonUniformFMul",
+                   "OpGroupNonUniformFMin", "OpGroupNonUniformFMax"),
+            Values("%float", "%v4float"), Values(spv::Scope::Subgroup),
+            Values("Reduce match_res", "InclusiveScan match_res",
+                   "ExclusiveScan match_res",
+                   "ClusteredReduce match_res %u32_0"),
+            Values("")));
+
+INSTANTIATE_TEST_SUITE_P(
+    GroupNonUniformFloatArithmeticBadResultType, GroupNonUniform,
+    Combine(Values("OpGroupNonUniformFAdd", "OpGroupNonUniformFMul",
+                   "OpGroupNonUniformFMin", "OpGroupNonUniformFMax"),
+            Values("%bool", "%u32", "%int", "%u32vec4", "%u32vec3", "%struct",
+                   "%v4int"),
+            Values(spv::Scope::Subgroup),
+            Values("Reduce match_res", "InclusiveScan match_res",
+                   "ExclusiveScan match_res",
+                   "ClusteredReduce match_res %u32_0"),
+            Values("Result must be a floating-point scalar or vector")));
+
+INSTANTIATE_TEST_SUITE_P(
+    GroupNonUniformFloatArithmeticBadValue, GroupNonUniform,
+    Combine(Values("OpGroupNonUniformFAdd", "OpGroupNonUniformFMul",
+                   "OpGroupNonUniformFMin", "OpGroupNonUniformFMax"),
+            Values("%v4float"), Values(spv::Scope::Subgroup),
+            Values("Reduce %float_0", "InclusiveScan %float_0",
+                   "ExclusiveScan %float_0", "ClusteredReduce %float_0 %u32_0"),
+            Values("The type of Value must match the Result type")));
+
+INSTANTIATE_TEST_SUITE_P(
+    GroupNonUniformFloatArithmeticMissingClusterSize, GroupNonUniform,
+    Combine(
+        Values("OpGroupNonUniformFAdd", "OpGroupNonUniformFMul",
+               "OpGroupNonUniformFMin", "OpGroupNonUniformFMax"),
+        Values("%float"), Values(spv::Scope::Subgroup),
+        Values("ClusteredReduce match_res"),
+        Values(
+            "ClusterSize must be present when Operation is ClusteredReduce")));
+
+INSTANTIATE_TEST_SUITE_P(
+    GroupNonUniformFloatArithmeticBadClusterSizeType, GroupNonUniform,
+    Combine(Values("OpGroupNonUniformFAdd", "OpGroupNonUniformFMul",
+                   "OpGroupNonUniformFMin", "OpGroupNonUniformFMax"),
+            Values("%float"), Values(spv::Scope::Subgroup),
+            Values("ClusteredReduce match_res %true",
+                   "ClusteredReduce match_res %false",
+                   "ClusteredReduce match_res %int_0",
+                   "ClusteredReduce match_res %float_0",
+                   "ClusteredReduce match_res %u32vec4_null",
+                   "ClusteredReduce match_res %u32vec3_null",
+                   "ClusteredReduce match_res %v2bool_false",
+                   "ClusteredReduce match_res %v4float_null",
+                   "ClusteredReduce match_res %struct_null",
+                   "ClusteredReduce match_res %v4int_null"),
+            Values("ClusterSize must be an unsigned integer scalar")));
+
+INSTANTIATE_TEST_SUITE_P(
+    GroupNonUniformFloatArithmeticClusterSizeNotConstant, GroupNonUniform,
+    Combine(Values("OpGroupNonUniformFAdd", "OpGroupNonUniformFMul",
+                   "OpGroupNonUniformFMin", "OpGroupNonUniformFMax"),
+            Values("%float"), Values(spv::Scope::Subgroup),
+            Values("ClusteredReduce match_res %u32_undef"),
+            Values("ClusterSize must be a constant instruction")));
+
+INSTANTIATE_TEST_SUITE_P(
+    GroupNonUniformBooleanArithmeticGood, GroupNonUniform,
+    Combine(Values("OpGroupNonUniformLogicalAnd", "OpGroupNonUniformLogicalOr",
+                   "OpGroupNonUniformLogicalXor"),
+            Values("%bool", "%v2bool"), Values(spv::Scope::Subgroup),
+            Values("Reduce match_res", "InclusiveScan match_res",
+                   "ExclusiveScan match_res",
+                   "ClusteredReduce match_res %u32_0"),
+            Values("")));
+
+INSTANTIATE_TEST_SUITE_P(
+    GroupNonUniformBooleanArithmeticBadResultType, GroupNonUniform,
+    Combine(Values("OpGroupNonUniformLogicalAnd", "OpGroupNonUniformLogicalOr",
+                   "OpGroupNonUniformLogicalXor"),
+            Values("%u32", "%int", "%float", "%u32vec4", "%u32vec3", "%struct",
+                   "%v4float", "%v4int"),
+            Values(spv::Scope::Subgroup),
+            Values("Reduce match_res", "InclusiveScan match_res",
+                   "ExclusiveScan match_res",
+                   "ClusteredReduce match_res %u32_0"),
+            Values("Result must be a boolean scalar or vector")));
+
+INSTANTIATE_TEST_SUITE_P(
+    GroupNonUniformBooleanArithmeticBadValue, GroupNonUniform,
+    Combine(Values("OpGroupNonUniformLogicalAnd", "OpGroupNonUniformLogicalOr",
+                   "OpGroupNonUniformLogicalXor"),
+            Values("%v2bool"), Values(spv::Scope::Subgroup),
+            Values("Reduce %true", "InclusiveScan %true",
+                   "ExclusiveScan %false", "ClusteredReduce %false %u32_0"),
+            Values("The type of Value must match the Result type")));
+
+INSTANTIATE_TEST_SUITE_P(
+    GroupNonUniformBooleanArithmeticMissingClusterSize, GroupNonUniform,
+    Combine(
+        Values("OpGroupNonUniformLogicalAnd", "OpGroupNonUniformLogicalOr",
+               "OpGroupNonUniformLogicalXor"),
+        Values("%bool"), Values(spv::Scope::Subgroup),
+        Values("ClusteredReduce match_res"),
+        Values(
+            "ClusterSize must be present when Operation is ClusteredReduce")));
+
+INSTANTIATE_TEST_SUITE_P(
+    GroupNonUniformBooleanArithmeticBadClusterSizeType, GroupNonUniform,
+    Combine(Values("OpGroupNonUniformLogicalAnd", "OpGroupNonUniformLogicalOr",
+                   "OpGroupNonUniformLogicalXor"),
+            Values("%bool"), Values(spv::Scope::Subgroup),
+            Values("ClusteredReduce match_res %true",
+                   "ClusteredReduce match_res %false",
+                   "ClusteredReduce match_res %int_0",
+                   "ClusteredReduce match_res %float_0",
+                   "ClusteredReduce match_res %u32vec4_null",
+                   "ClusteredReduce match_res %u32vec3_null",
+                   "ClusteredReduce match_res %v2bool_false",
+                   "ClusteredReduce match_res %v4float_null",
+                   "ClusteredReduce match_res %struct_null",
+                   "ClusteredReduce match_res %v4int_null"),
+            Values("ClusterSize must be an unsigned integer scalar")));
+
+INSTANTIATE_TEST_SUITE_P(
+    GroupNonUniformBooleanArithmeticClusterSizeNotConstant, GroupNonUniform,
+    Combine(Values("OpGroupNonUniformLogicalAnd", "OpGroupNonUniformLogicalOr",
+                   "OpGroupNonUniformLogicalXor"),
+            Values("%bool"), Values(spv::Scope::Subgroup),
+            Values("ClusteredReduce match_res %u32_undef"),
+            Values("ClusterSize must be a constant instruction")));
+
 TEST_F(ValidateGroupNonUniform, VulkanGroupNonUniformBallotBitCountOperation) {
   std::string test = R"(
 OpCapability Shader
@@ -326,6 +904,146 @@
           "be only: Reduce, InclusiveScan, or ExclusiveScan."));
 }
 
+TEST_F(ValidateGroupNonUniform, BroadcastNonConstantSpv1p4) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability GroupNonUniformBallot
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main" %var
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+OpDecorate %struct Block
+OpMemberDecorate %struct 0 Offset 0
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%subgroup = OpConstant %int 3
+%struct = OpTypeStruct %int
+%ptr_struct = OpTypePointer StorageBuffer %struct
+%ptr_int = OpTypePointer StorageBuffer %int
+%var = OpVariable %ptr_struct StorageBuffer
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%gep = OpAccessChain %ptr_int %var %int_0
+%ld = OpLoad %int %gep
+%broadcast = OpGroupNonUniformBroadcast %int %subgroup %int_0 %ld
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Before SPIR-V 1.5, Id must be a constant instruction"));
+}
+
+TEST_F(ValidateGroupNonUniform, BroadcastNonConstantSpv1p5) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability GroupNonUniformBallot
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main" %var
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+OpDecorate %struct Block
+OpMemberDecorate %struct 0 Offset 0
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%subgroup = OpConstant %int 3
+%struct = OpTypeStruct %int
+%ptr_struct = OpTypePointer StorageBuffer %struct
+%ptr_int = OpTypePointer StorageBuffer %int
+%var = OpVariable %ptr_struct StorageBuffer
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%gep = OpAccessChain %ptr_int %var %int_0
+%ld = OpLoad %int %gep
+%broadcast = OpGroupNonUniformBroadcast %int %subgroup %int_0 %ld
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_5);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5));
+}
+
+TEST_F(ValidateGroupNonUniform, QuadBroadcastNonConstantSpv1p4) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability GroupNonUniformQuad
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main" %var
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+OpDecorate %struct Block
+OpMemberDecorate %struct 0 Offset 0
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%subgroup = OpConstant %int 3
+%struct = OpTypeStruct %int
+%ptr_struct = OpTypePointer StorageBuffer %struct
+%ptr_int = OpTypePointer StorageBuffer %int
+%var = OpVariable %ptr_struct StorageBuffer
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%gep = OpAccessChain %ptr_int %var %int_0
+%ld = OpLoad %int %gep
+%broadcast = OpGroupNonUniformQuadBroadcast %int %subgroup %int_0 %ld
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Before SPIR-V 1.5, Index must be a constant instruction"));
+}
+
+TEST_F(ValidateGroupNonUniform, QuadBroadcastNonConstantSpv1p5) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability GroupNonUniformQuad
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main" %var
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+OpDecorate %struct Block
+OpMemberDecorate %struct 0 Offset 0
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%subgroup = OpConstant %int 3
+%struct = OpTypeStruct %int
+%ptr_struct = OpTypePointer StorageBuffer %struct
+%ptr_int = OpTypePointer StorageBuffer %int
+%var = OpVariable %ptr_struct StorageBuffer
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%gep = OpAccessChain %ptr_int %var %int_0
+%ld = OpLoad %int %gep
+%broadcast = OpGroupNonUniformQuadBroadcast %int %subgroup %int_0 %ld
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_5);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5));
+}
+
 }  // 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
new file mode 100644
index 0000000..6038c38
--- /dev/null
+++ b/test/val/val_ray_tracing_reorder_test.cpp
@@ -0,0 +1,598 @@
+// Copyright (c) 2022 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 instructions from SPV_NV_shader_invocation_reorder.
+
+#include <sstream>
+#include <string>
+
+#include "gmock/gmock.h"
+#include "test/val/val_fixtures.h"
+
+namespace spvtools {
+namespace val {
+namespace {
+
+using ::testing::HasSubstr;
+using ::testing::Values;
+
+using ValidateRayTracingReorderNV = spvtest::ValidateBase<bool>;
+
+std::string GenerateReorderThreadCode(const std::string& body = "",
+                                      const std::string& declarations = "") {
+  std::ostringstream ss;
+  ss << R"(
+            OpCapability RayTracingKHR
+            OpCapability ShaderInvocationReorderNV
+            OpExtension "SPV_KHR_ray_tracing"
+            OpExtension "SPV_NV_shader_invocation_reorder"
+       %1 = OpExtInstImport "GLSL.std.450"
+            OpMemoryModel Logical GLSL450
+            OpEntryPoint RayGenerationNV %main "main" %hObj
+            OpSourceExtension "GL_EXT_ray_tracing"
+            OpSourceExtension "GL_NV_shader_invocation_reorder"
+            OpName %main "main"
+    %void = OpTypeVoid
+       %3 = OpTypeFunction %void
+       %6 = OpTypeHitObjectNV
+%_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();
+}
+
+TEST_F(ValidateRayTracingReorderNV, ReorderThreadWithHintNV) {
+  const std::string declarations = R"(
+             %uint = OpTypeInt 32 0
+           %uint_4 = OpConstant %uint 4
+  )";
+
+  const std::string body = R"(
+    OpReorderThreadWithHintNV %uint_4 %uint_4
+  )";
+
+  CompileSuccessfully(GenerateReorderThreadCode(body, declarations).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderNV, ReorderThreadWithHitObjectNV) {
+  const std::string declarations = R"(
+      %uint = OpTypeInt 32 0
+    %uint_4 = OpConstant %uint 4
+    %uint_2 = OpConstant %uint 2
+  )";
+
+  const std::string body = R"(
+             OpReorderThreadWithHitObjectNV %hObj
+             OpReorderThreadWithHitObjectNV %hObj %uint_4 %uint_2
+  )";
+
+  CompileSuccessfully(GenerateReorderThreadCode(body, declarations).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+std::string GenerateReorderShaderCode(
+    const std::string& body = "", const std::string& declarations = "",
+    const std::string execution_model = "RayGenerationKHR") {
+  std::ostringstream ss;
+  ss << R"(
+               OpCapability RayTracingKHR
+               OpCapability ShaderInvocationReorderNV
+               OpExtension "SPV_KHR_ray_tracing"
+               OpExtension "SPV_NV_shader_invocation_reorder"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint )"
+     << execution_model
+     << R"( %main "main" %attr %_ %hObj %payload %__0 %as %__1
+               OpSource GLSL 460
+               OpSourceExtension "GL_EXT_ray_tracing"
+               OpSourceExtension "GL_NV_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_HitObjectAttributeNV_v2float = OpTypePointer HitObjectAttributeNV %v2float
+       %attr = OpVariable %_ptr_HitObjectAttributeNV_v2float HitObjectAttributeNV
+    %float_1 = OpConstant %float 1
+         %11 = OpConstantComposite %v2float %float_1 %float_1
+     %hBlock = OpTypeStruct %float
+%_ptr_HitObjectAttributeNV_hBlock = OpTypePointer HitObjectAttributeNV %hBlock
+          %_ = OpVariable %_ptr_HitObjectAttributeNV_hBlock HitObjectAttributeNV
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+    %float_2 = OpConstant %float 2
+%_ptr_HitObjectAttributeNV_float = OpTypePointer HitObjectAttributeNV %float
+             %20 = OpTypeHitObjectNV
+    %_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_RayPayloadNV_v4float = OpTypePointer RayPayloadNV %v4float
+    %payload = OpVariable %_ptr_RayPayloadNV_v4float RayPayloadNV
+     %pBlock = OpTypeStruct %v2float %v2float
+%_ptr_RayPayloadNV_pBlock = OpTypePointer RayPayloadNV %pBlock
+        %__0 = OpVariable %_ptr_RayPayloadNV_pBlock RayPayloadNV
+      %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(ValidateRayTracingReorderNV, HitObjectTraceRayNV) {
+  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
+  OpHitObjectTraceRayNV %hObj %26 %uint_1 %uint_1 %uint_1 %uint_1 %uint_1 %31 %float_0_5 %32 %float_1 %payload
+  )";
+
+  CompileSuccessfully(GenerateReorderShaderCode(body, declarations).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderNV, HitObjectTraceRayMotionNV) {
+  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
+      OpHitObjectTraceRayMotionNV %hObj %26 %uint_1 %uint_1 %uint_1 %uint_1 %uint_1 %31 %float_0_5 %32 %float_1 %float_10 %__0
+  )";
+
+  CompileSuccessfully(GenerateReorderShaderCode(body, declarations).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderNV, HitObjectRecordHitNV) {
+  const std::string declarations = R"(
+        %int_1 = OpConstant %int 1
+        %uint = OpTypeInt 32 0
+      %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
+      OpHitObjectRecordHitNV %hObj %26 %int_1 %int_1 %int_1 %uint_2 %uint_2 %uint_2 %31 %float_1 %32 %float_2 %attr
+  )";
+
+  CompileSuccessfully(GenerateReorderShaderCode(body, declarations).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderNV, HitObjectRecordHitWithIndexNV) {
+  const std::string declarations = R"(
+        %int_1 = OpConstant %int 1
+        %uint = OpTypeInt 32 0
+      %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
+      OpHitObjectRecordHitWithIndexNV %hObj %26 %int_1 %int_1 %int_1 %uint_2 %uint_2 %31 %float_1 %32 %float_2 %_
+  )";
+
+  CompileSuccessfully(GenerateReorderShaderCode(body, declarations).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderNV, HitObjectRecordEmptyNV) {
+  const std::string body = R"(
+      OpHitObjectRecordEmptyNV %hObj
+  )";
+
+  CompileSuccessfully(GenerateReorderShaderCode(body).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderNV, HitObjectRecordMissNV) {
+  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"(
+      OpHitObjectRecordMissNV %hObj %uint_1 %29 %float_2 %31 %float_5
+  )";
+
+  CompileSuccessfully(GenerateReorderShaderCode(body, declarations).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderNV, HitObjectIsHitNV) {
+  const std::string declarations = R"(
+        %bool = OpTypeBool
+        %_ptr_StorageBuffer_float = OpTypePointer StorageBuffer %float
+  )";
+
+  const std::string body = R"(
+      %26 = OpHitObjectIsHitNV %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(GenerateReorderShaderCode(body, declarations).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderNV, HitObjectGetRayTMaxNV) {
+  const std::string declarations = R"(
+        %_ptr_Function_float = OpTypePointer Function %float
+  )";
+
+  const std::string body = R"(
+      %tmin = OpVariable %_ptr_Function_float Function
+      %12 = OpHitObjectGetRayTMaxNV %float %hObj
+      OpStore %tmin %12
+  )";
+
+  CompileSuccessfully(GenerateReorderShaderCode(body, declarations).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderNV, HitObjectGetRayTMinNV) {
+  const std::string declarations = R"(
+        %_ptr_Function_float = OpTypePointer Function %float
+  )";
+
+  const std::string body = R"(
+      %tmin = OpVariable %_ptr_Function_float Function
+      %12 = OpHitObjectGetRayTMinNV %float %hObj
+      OpStore %tmin %12
+  )";
+
+  CompileSuccessfully(GenerateReorderShaderCode(body, declarations).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderNV, HitObjectGetWorldRayOriginNV) {
+  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 = OpHitObjectGetWorldRayOriginNV %v3float %hObj
+      OpStore %orig %13
+  )";
+
+  CompileSuccessfully(GenerateReorderShaderCode(body, declarations).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderNV, HitObjectGetObjectRayOriginNV) {
+  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 = OpHitObjectGetObjectRayOriginNV %v3float %hObj
+      OpStore %oorig %13
+  )";
+
+  CompileSuccessfully(GenerateReorderShaderCode(body, declarations).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderNV, HitObjectGetWorldRayDirectionNV) {
+  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 = OpHitObjectGetWorldRayDirectionNV %v3float %hObj
+      OpStore %dir %13
+  )";
+
+  CompileSuccessfully(GenerateReorderShaderCode(body, declarations).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderNV, HitObjectGetObjectRayDirectionNV) {
+  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 = OpHitObjectGetObjectRayDirectionNV %v3float %hObj
+      OpStore %odir %13
+  )";
+
+  CompileSuccessfully(GenerateReorderShaderCode(body, declarations).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderNV, HitObjectGetObjectToWorldNV) {
+  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 = OpHitObjectGetObjectToWorldNV %mat4v3float %hObj
+      OpStore %otw %14
+  )";
+
+  CompileSuccessfully(GenerateReorderShaderCode(body, declarations).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderNV, HitObjectGetWorldToObjectNV) {
+  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 = OpHitObjectGetWorldToObjectNV %mat4v3float %hObj
+      OpStore %wto %14
+  )";
+
+  CompileSuccessfully(GenerateReorderShaderCode(body, declarations).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderNV, HitObjectGetInstanceCustomIndexNV) {
+  const std::string declarations = R"(
+        %_ptr_Function_int = OpTypePointer Function %int
+  )";
+
+  const std::string body = R"(
+      %id = OpVariable %_ptr_Function_int Function
+      %12 = OpHitObjectGetInstanceCustomIndexNV %int %hObj
+      OpStore %id %12
+  )";
+
+  CompileSuccessfully(GenerateReorderShaderCode(body, declarations).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderNV, HitObjectGetInstanceIdNV) {
+  const std::string declarations = R"(
+        %_ptr_Function_int = OpTypePointer Function %int
+  )";
+
+  const std::string body = R"(
+      %id = OpVariable %_ptr_Function_int Function
+      %12 = OpHitObjectGetInstanceIdNV %int %hObj
+      OpStore %id %12
+  )";
+
+  CompileSuccessfully(GenerateReorderShaderCode(body, declarations).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderNV, HitObjectGetPrimitiveIndexNV) {
+  const std::string declarations = R"(
+        %_ptr_Function_int = OpTypePointer Function %int
+  )";
+
+  const std::string body = R"(
+      %id = OpVariable %_ptr_Function_int Function
+      %12 = OpHitObjectGetPrimitiveIndexNV %int %hObj
+      OpStore %id %12
+  )";
+
+  CompileSuccessfully(GenerateReorderShaderCode(body, declarations).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderNV, HitObjectGetGeometryIndexNV) {
+  const std::string declarations = R"(
+        %_ptr_Function_int = OpTypePointer Function %int
+  )";
+
+  const std::string body = R"(
+      %id = OpVariable %_ptr_Function_int Function
+      %12 = OpHitObjectGetGeometryIndexNV %int %hObj
+      OpStore %id %12
+  )";
+
+  CompileSuccessfully(GenerateReorderShaderCode(body, declarations).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderNV, HitObjectGetHitKindNV) {
+  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 = OpHitObjectGetHitKindNV %uint %hObj
+    OpStore %uid %12
+  )";
+
+  CompileSuccessfully(GenerateReorderShaderCode(body, declarations).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderNV, HitObjectGetAttributesNV) {
+  const std::string body = R"(
+    OpHitObjectGetAttributesNV %hObj %attr
+  )";
+
+  CompileSuccessfully(GenerateReorderShaderCode(body).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderNV, HitObjectGetShaderRecordBufferHandleNV) {
+  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 = OpHitObjectGetShaderRecordBufferHandleNV %v2uint %hObj
+              OpStore %handle %13
+  )";
+
+  CompileSuccessfully(GenerateReorderShaderCode(body, declarations).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderNV,
+       HitObjectGetShaderBindingTableRecordIndexNV) {
+  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 = OpHitObjectGetShaderBindingTableRecordIndexNV %uint %hObj
+           OpStore %rid %12
+  )";
+
+  CompileSuccessfully(GenerateReorderShaderCode(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_state_test.cpp b/test/val/val_state_test.cpp
index 65cb1c3..51064ab 100644
--- a/test/val/val_state_test.cpp
+++ b/test/val/val_state_test.cpp
@@ -18,14 +18,10 @@
 #include <vector>
 
 #include "gtest/gtest.h"
-#include "source/latest_version_spirv_header.h"
-
 #include "source/enum_set.h"
 #include "source/extensions.h"
+#include "source/latest_version_spirv_header.h"
 #include "source/spirv_validator_options.h"
-#include "source/val/construct.h"
-#include "source/val/function.h"
-#include "source/val/validate.h"
 #include "source/val/validation_state.h"
 
 namespace spvtools {
@@ -59,40 +55,41 @@
 
 TEST_F(ValidationState_HasAnyOfCapabilities, EmptyMask) {
   EXPECT_TRUE(state_.HasAnyOfCapabilities({}));
-  state_.RegisterCapability(SpvCapabilityMatrix);
+  state_.RegisterCapability(spv::Capability::Matrix);
   EXPECT_TRUE(state_.HasAnyOfCapabilities({}));
-  state_.RegisterCapability(SpvCapabilityImageMipmap);
+  state_.RegisterCapability(spv::Capability::ImageMipmap);
   EXPECT_TRUE(state_.HasAnyOfCapabilities({}));
-  state_.RegisterCapability(SpvCapabilityPipes);
+  state_.RegisterCapability(spv::Capability::Pipes);
   EXPECT_TRUE(state_.HasAnyOfCapabilities({}));
-  state_.RegisterCapability(SpvCapabilityStorageImageArrayDynamicIndexing);
+  state_.RegisterCapability(spv::Capability::StorageImageArrayDynamicIndexing);
   EXPECT_TRUE(state_.HasAnyOfCapabilities({}));
-  state_.RegisterCapability(SpvCapabilityClipDistance);
+  state_.RegisterCapability(spv::Capability::ClipDistance);
   EXPECT_TRUE(state_.HasAnyOfCapabilities({}));
-  state_.RegisterCapability(SpvCapabilityStorageImageWriteWithoutFormat);
+  state_.RegisterCapability(spv::Capability::StorageImageWriteWithoutFormat);
   EXPECT_TRUE(state_.HasAnyOfCapabilities({}));
 }
 
 TEST_F(ValidationState_HasAnyOfCapabilities, SingleCapMask) {
-  EXPECT_FALSE(state_.HasAnyOfCapabilities({SpvCapabilityMatrix}));
-  EXPECT_FALSE(state_.HasAnyOfCapabilities({SpvCapabilityImageMipmap}));
-  state_.RegisterCapability(SpvCapabilityMatrix);
-  EXPECT_TRUE(state_.HasAnyOfCapabilities({SpvCapabilityMatrix}));
-  EXPECT_FALSE(state_.HasAnyOfCapabilities({SpvCapabilityImageMipmap}));
-  state_.RegisterCapability(SpvCapabilityImageMipmap);
-  EXPECT_TRUE(state_.HasAnyOfCapabilities({SpvCapabilityMatrix}));
-  EXPECT_TRUE(state_.HasAnyOfCapabilities({SpvCapabilityImageMipmap}));
+  EXPECT_FALSE(state_.HasAnyOfCapabilities({spv::Capability::Matrix}));
+  EXPECT_FALSE(state_.HasAnyOfCapabilities({spv::Capability::ImageMipmap}));
+  state_.RegisterCapability(spv::Capability::Matrix);
+  EXPECT_TRUE(state_.HasAnyOfCapabilities({spv::Capability::Matrix}));
+  EXPECT_FALSE(state_.HasAnyOfCapabilities({spv::Capability::ImageMipmap}));
+  state_.RegisterCapability(spv::Capability::ImageMipmap);
+  EXPECT_TRUE(state_.HasAnyOfCapabilities({spv::Capability::Matrix}));
+  EXPECT_TRUE(state_.HasAnyOfCapabilities({spv::Capability::ImageMipmap}));
 }
 
 TEST_F(ValidationState_HasAnyOfCapabilities, MultiCapMask) {
   const auto set1 =
-      CapabilitySet{SpvCapabilitySampledRect, SpvCapabilityImageBuffer};
-  const auto set2 = CapabilitySet{SpvCapabilityStorageImageWriteWithoutFormat,
-                                  SpvCapabilityStorageImageReadWithoutFormat,
-                                  SpvCapabilityGeometryStreams};
+      CapabilitySet{spv::Capability::SampledRect, spv::Capability::ImageBuffer};
+  const auto set2 =
+      CapabilitySet{spv::Capability::StorageImageWriteWithoutFormat,
+                    spv::Capability::StorageImageReadWithoutFormat,
+                    spv::Capability::GeometryStreams};
   EXPECT_FALSE(state_.HasAnyOfCapabilities(set1));
   EXPECT_FALSE(state_.HasAnyOfCapabilities(set2));
-  state_.RegisterCapability(SpvCapabilityImageBuffer);
+  state_.RegisterCapability(spv::Capability::ImageBuffer);
   EXPECT_TRUE(state_.HasAnyOfCapabilities(set1));
   EXPECT_FALSE(state_.HasAnyOfCapabilities(set2));
 }
@@ -139,50 +136,52 @@
 
 TEST_F(ValidationState_InLayoutState, Variable) {
   state_.SetCurrentLayoutSectionForTesting(kLayoutTypes);
-  EXPECT_TRUE(state_.IsOpcodeInCurrentLayoutSection(SpvOpVariable));
+  EXPECT_TRUE(state_.IsOpcodeInCurrentLayoutSection(spv::Op::OpVariable));
 
   state_.SetCurrentLayoutSectionForTesting(kLayoutFunctionDefinitions);
-  EXPECT_TRUE(state_.IsOpcodeInCurrentLayoutSection(SpvOpVariable));
+  EXPECT_TRUE(state_.IsOpcodeInCurrentLayoutSection(spv::Op::OpVariable));
 }
 
 TEST_F(ValidationState_InLayoutState, ExtInst) {
   state_.SetCurrentLayoutSectionForTesting(kLayoutTypes);
-  EXPECT_TRUE(state_.IsOpcodeInCurrentLayoutSection(SpvOpExtInst));
+  EXPECT_TRUE(state_.IsOpcodeInCurrentLayoutSection(spv::Op::OpExtInst));
 
   state_.SetCurrentLayoutSectionForTesting(kLayoutFunctionDefinitions);
-  EXPECT_TRUE(state_.IsOpcodeInCurrentLayoutSection(SpvOpExtInst));
+  EXPECT_TRUE(state_.IsOpcodeInCurrentLayoutSection(spv::Op::OpExtInst));
 }
 
 TEST_F(ValidationState_InLayoutState, Undef) {
   state_.SetCurrentLayoutSectionForTesting(kLayoutTypes);
-  EXPECT_TRUE(state_.IsOpcodeInCurrentLayoutSection(SpvOpUndef));
+  EXPECT_TRUE(state_.IsOpcodeInCurrentLayoutSection(spv::Op::OpUndef));
 
   state_.SetCurrentLayoutSectionForTesting(kLayoutFunctionDefinitions);
-  EXPECT_TRUE(state_.IsOpcodeInCurrentLayoutSection(SpvOpUndef));
+  EXPECT_TRUE(state_.IsOpcodeInCurrentLayoutSection(spv::Op::OpUndef));
 }
 
 TEST_F(ValidationState_InLayoutState, Function) {
   state_.SetCurrentLayoutSectionForTesting(kLayoutFunctionDeclarations);
-  EXPECT_TRUE(state_.IsOpcodeInCurrentLayoutSection(SpvOpFunction));
+  EXPECT_TRUE(state_.IsOpcodeInCurrentLayoutSection(spv::Op::OpFunction));
 
   state_.SetCurrentLayoutSectionForTesting(kLayoutFunctionDefinitions);
-  EXPECT_TRUE(state_.IsOpcodeInCurrentLayoutSection(SpvOpFunction));
+  EXPECT_TRUE(state_.IsOpcodeInCurrentLayoutSection(spv::Op::OpFunction));
 }
 
 TEST_F(ValidationState_InLayoutState, FunctionParameter) {
   state_.SetCurrentLayoutSectionForTesting(kLayoutFunctionDeclarations);
-  EXPECT_TRUE(state_.IsOpcodeInCurrentLayoutSection(SpvOpFunctionParameter));
+  EXPECT_TRUE(
+      state_.IsOpcodeInCurrentLayoutSection(spv::Op::OpFunctionParameter));
 
   state_.SetCurrentLayoutSectionForTesting(kLayoutFunctionDefinitions);
-  EXPECT_TRUE(state_.IsOpcodeInCurrentLayoutSection(SpvOpFunctionParameter));
+  EXPECT_TRUE(
+      state_.IsOpcodeInCurrentLayoutSection(spv::Op::OpFunctionParameter));
 }
 
 TEST_F(ValidationState_InLayoutState, FunctionEnd) {
   state_.SetCurrentLayoutSectionForTesting(kLayoutFunctionDeclarations);
-  EXPECT_TRUE(state_.IsOpcodeInCurrentLayoutSection(SpvOpFunctionEnd));
+  EXPECT_TRUE(state_.IsOpcodeInCurrentLayoutSection(spv::Op::OpFunctionEnd));
 
   state_.SetCurrentLayoutSectionForTesting(kLayoutFunctionDefinitions);
-  EXPECT_TRUE(state_.IsOpcodeInCurrentLayoutSection(SpvOpFunctionEnd));
+  EXPECT_TRUE(state_.IsOpcodeInCurrentLayoutSection(spv::Op::OpFunctionEnd));
 }
 
 }  // namespace
diff --git a/test/val/val_storage_test.cpp b/test/val/val_storage_test.cpp
index 8693e80..6a3e4bd 100644
--- a/test/val/val_storage_test.cpp
+++ b/test/val/val_storage_test.cpp
@@ -28,8 +28,6 @@
 using ::testing::HasSubstr;
 using ::testing::Values;
 using ValidateStorage = spvtest::ValidateBase<std::string>;
-using ValidateStorageClass =
-    spvtest::ValidateBase<std::tuple<std::string, bool, bool, std::string>>;
 using ValidateStorageExecutionModel = spvtest::ValidateBase<std::string>;
 
 TEST_F(ValidateStorage, FunctionStorageInsideFunction) {
diff --git a/test/val/val_type_unique_test.cpp b/test/val/val_type_unique_test.cpp
index 45a4d50..31ad3a6 100644
--- a/test/val/val_type_unique_test.cpp
+++ b/test/val/val_type_unique_test.cpp
@@ -90,7 +90,7 @@
 
 // Returns expected error string if |opcode| produces a duplicate type
 // declaration.
-std::string GetErrorString(SpvOp opcode) {
+std::string GetErrorString(spv::Op opcode) {
   return "Duplicate non-aggregate type declarations are not allowed. Opcode: " +
          std::string(spvOpcodeString(opcode));
 }
@@ -107,7 +107,8 @@
 )" + GetBody();
   CompileSuccessfully(str.c_str());
   ASSERT_EQ(kDuplicateTypeError, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(), HasSubstr(GetErrorString(SpvOpTypeVoid)));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr(GetErrorString(spv::Op::OpTypeVoid)));
 }
 
 TEST_F(ValidateTypeUnique, duplicate_bool) {
@@ -116,7 +117,8 @@
 )" + GetBody();
   CompileSuccessfully(str.c_str());
   ASSERT_EQ(kDuplicateTypeError, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(), HasSubstr(GetErrorString(SpvOpTypeBool)));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr(GetErrorString(spv::Op::OpTypeBool)));
 }
 
 TEST_F(ValidateTypeUnique, duplicate_int) {
@@ -125,7 +127,8 @@
 )" + GetBody();
   CompileSuccessfully(str.c_str());
   ASSERT_EQ(kDuplicateTypeError, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(), HasSubstr(GetErrorString(SpvOpTypeInt)));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr(GetErrorString(spv::Op::OpTypeInt)));
 }
 
 TEST_F(ValidateTypeUnique, duplicate_float) {
@@ -134,7 +137,8 @@
 )" + GetBody();
   CompileSuccessfully(str.c_str());
   ASSERT_EQ(kDuplicateTypeError, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(), HasSubstr(GetErrorString(SpvOpTypeFloat)));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr(GetErrorString(spv::Op::OpTypeFloat)));
 }
 
 TEST_F(ValidateTypeUnique, duplicate_vec3) {
@@ -144,7 +148,7 @@
   CompileSuccessfully(str.c_str());
   ASSERT_EQ(kDuplicateTypeError, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
-              HasSubstr(GetErrorString(SpvOpTypeVector)));
+              HasSubstr(GetErrorString(spv::Op::OpTypeVector)));
 }
 
 TEST_F(ValidateTypeUnique, duplicate_mat33) {
@@ -154,7 +158,7 @@
   CompileSuccessfully(str.c_str());
   ASSERT_EQ(kDuplicateTypeError, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
-              HasSubstr(GetErrorString(SpvOpTypeMatrix)));
+              HasSubstr(GetErrorString(spv::Op::OpTypeMatrix)));
 }
 
 TEST_F(ValidateTypeUnique, duplicate_vfunc) {
@@ -164,7 +168,7 @@
   CompileSuccessfully(str.c_str());
   ASSERT_EQ(kDuplicateTypeError, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
-              HasSubstr(GetErrorString(SpvOpTypeFunction)));
+              HasSubstr(GetErrorString(spv::Op::OpTypeFunction)));
 }
 
 TEST_F(ValidateTypeUnique, duplicate_pipe_storage) {
@@ -181,7 +185,7 @@
   CompileSuccessfully(str.c_str(), SPV_ENV_UNIVERSAL_1_1);
   ASSERT_EQ(kDuplicateTypeError, ValidateInstructions(SPV_ENV_UNIVERSAL_1_1));
   EXPECT_THAT(getDiagnosticString(),
-              HasSubstr(GetErrorString(SpvOpTypePipeStorage)));
+              HasSubstr(GetErrorString(spv::Op::OpTypePipeStorage)));
 }
 
 TEST_F(ValidateTypeUnique, duplicate_named_barrier) {
@@ -197,7 +201,7 @@
   CompileSuccessfully(str.c_str(), SPV_ENV_UNIVERSAL_1_1);
   ASSERT_EQ(kDuplicateTypeError, ValidateInstructions(SPV_ENV_UNIVERSAL_1_1));
   EXPECT_THAT(getDiagnosticString(),
-              HasSubstr(GetErrorString(SpvOpTypeNamedBarrier)));
+              HasSubstr(GetErrorString(spv::Op::OpTypeNamedBarrier)));
 }
 
 TEST_F(ValidateTypeUnique, duplicate_forward_pointer) {
@@ -234,7 +238,7 @@
   CompileSuccessfully(str.c_str());
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
-              Not(HasSubstr(GetErrorString(SpvOpTypeVoid))));
+              Not(HasSubstr(GetErrorString(spv::Op::OpTypeVoid))));
 }
 
 TEST_F(ValidateTypeUnique, DuplicatePointerTypesNoExtension) {
@@ -263,7 +267,7 @@
   CompileSuccessfully(str.c_str());
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
-              Not(HasSubstr(GetErrorString(SpvOpTypePointer))));
+              Not(HasSubstr(GetErrorString(spv::Op::OpTypePointer))));
 }
 
 }  // namespace
diff --git a/test/val/val_validation_state_test.cpp b/test/val/val_validation_state_test.cpp
index 3dd9e64..a5e88de 100644
--- a/test/val/val_validation_state_test.cpp
+++ b/test/val/val_validation_state_test.cpp
@@ -180,7 +180,7 @@
   CompileSuccessfully(spirv);
   EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
   EXPECT_EQ(size_t(1), vstate_->entry_points().size());
-  EXPECT_EQ(SpvOpFunction,
+  EXPECT_EQ(spv::Op::OpFunction,
             vstate_->FindDef(vstate_->entry_points()[0])->opcode());
 }
 
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
index 86d0bc4..a93f640 100644
--- a/tools/CMakeLists.txt
+++ b/tools/CMakeLists.txt
@@ -39,26 +39,47 @@
   set_property(TARGET ${ARG_TARGET} PROPERTY FOLDER "SPIRV-Tools executables")
 endfunction()
 
+set(COMMON_TOOLS_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/util/flags.cpp")
+
 if (NOT ${SPIRV_SKIP_EXECUTABLES})
-  add_spvtools_tool(TARGET spirv-as SRCS as/as.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY})
-  add_spvtools_tool(TARGET spirv-diff SRCS diff/diff.cpp util/cli_consumer.cpp LIBS SPIRV-Tools-diff SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY})
-  add_spvtools_tool(TARGET spirv-dis SRCS dis/dis.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY})
-  add_spvtools_tool(TARGET spirv-val SRCS val/val.cpp util/cli_consumer.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY})
-  add_spvtools_tool(TARGET spirv-opt SRCS opt/opt.cpp util/cli_consumer.cpp LIBS SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY})
+  add_spvtools_tool(TARGET spirv-diff SRCS ${COMMON_TOOLS_SRCS} diff/diff.cpp util/cli_consumer.cpp LIBS SPIRV-Tools-diff SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY})
+  add_spvtools_tool(TARGET spirv-dis  SRCS ${COMMON_TOOLS_SRCS} dis/dis.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY})
+  add_spvtools_tool(TARGET spirv-val  SRCS ${COMMON_TOOLS_SRCS} val/val.cpp util/cli_consumer.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY})
+  add_spvtools_tool(TARGET spirv-opt  SRCS ${COMMON_TOOLS_SRCS} opt/opt.cpp util/cli_consumer.cpp LIBS SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY})
   if(NOT (${CMAKE_SYSTEM_NAME} STREQUAL "iOS")) # iOS does not allow std::system calls which spirv-reduce requires
-    add_spvtools_tool(TARGET spirv-reduce SRCS reduce/reduce.cpp util/cli_consumer.cpp LIBS SPIRV-Tools-reduce ${SPIRV_TOOLS_FULL_VISIBILITY})
+    add_spvtools_tool(TARGET spirv-reduce SRCS ${COMMON_TOOLS_SRCS} reduce/reduce.cpp util/cli_consumer.cpp LIBS SPIRV-Tools-reduce ${SPIRV_TOOLS_FULL_VISIBILITY})
   endif()
-  add_spvtools_tool(TARGET spirv-link SRCS link/linker.cpp LIBS SPIRV-Tools-link ${SPIRV_TOOLS_FULL_VISIBILITY})
-  add_spvtools_tool(TARGET spirv-lint SRCS lint/lint.cpp util/cli_consumer.cpp LIBS SPIRV-Tools-lint SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY})
+  add_spvtools_tool(TARGET spirv-link SRCS ${COMMON_TOOLS_SRCS} link/linker.cpp LIBS SPIRV-Tools-link ${SPIRV_TOOLS_FULL_VISIBILITY})
+  add_spvtools_tool(TARGET spirv-lint SRCS ${COMMON_TOOLS_SRCS} lint/lint.cpp util/cli_consumer.cpp LIBS SPIRV-Tools-lint SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY})
+  add_spvtools_tool(TARGET spirv-as
+                    SRCS as/as.cpp
+                         ${COMMON_TOOLS_SRCS}
+                    LIBS ${SPIRV_TOOLS_FULL_VISIBILITY})
+  target_include_directories(spirv-as PRIVATE ${spirv-tools_SOURCE_DIR}
+                                              ${SPIRV_HEADER_INCLUDE_DIR})
   add_spvtools_tool(TARGET spirv-cfg
                     SRCS cfg/cfg.cpp
                          cfg/bin_to_dot.h
                          cfg/bin_to_dot.cpp
+                         ${COMMON_TOOLS_SRCS}
                     LIBS ${SPIRV_TOOLS_FULL_VISIBILITY})
   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)
+
+  if(NOT (${CMAKE_SYSTEM_NAME} STREQUAL "Android"))
+    add_spvtools_tool(TARGET spirv-objdump
+                      SRCS objdump/objdump.cpp
+                           objdump/extract_source.cpp
+                           util/cli_consumer.cpp
+                           ${COMMON_TOOLS_SRCS}
+                      LIBS ${SPIRV_TOOLS_FULL_VISIBILITY})
+    target_include_directories(spirv-objdump PRIVATE ${spirv-tools_SOURCE_DIR}
+                                                     ${SPIRV_HEADER_INCLUDE_DIR})
+    set(SPIRV_INSTALL_TARGETS ${SPIRV_INSTALL_TARGETS} spirv-objdump)
+  endif()
+
   if(NOT (${CMAKE_SYSTEM_NAME} STREQUAL "iOS"))
     set(SPIRV_INSTALL_TARGETS ${SPIRV_INSTALL_TARGETS} spirv-reduce)
   endif()
@@ -69,6 +90,19 @@
   endif(SPIRV_BUILD_FUZZER)
 
   if(ENABLE_SPIRV_TOOLS_INSTALL)
-    install(TARGETS ${SPIRV_INSTALL_TARGETS} DESTINATION ${CMAKE_INSTALL_BINDIR})
+    install(TARGETS ${SPIRV_INSTALL_TARGETS} EXPORT SPIRV-Tools-toolsTargets)
+    export(EXPORT SPIRV-Tools-toolsTargets FILE SPIRV-Tools-toolsTargets.cmake)
+
+    spvtools_config_package_dir(SPIRV-Tools-tools PACKAGE_DIR)
+    install(EXPORT SPIRV-Tools-toolsTargets FILE SPIRV-Tools-toolsTargets.cmake
+            DESTINATION ${PACKAGE_DIR})
+
+    file(WRITE ${CMAKE_BINARY_DIR}/SPIRV-Tools-toolsConfig.cmake
+      "include(CMakeFindDependencyMacro)\n"
+      "find_dependency(${SPIRV_TOOLS})\n"
+      "include(\${CMAKE_CURRENT_LIST_DIR}/SPIRV-Tools-toolsTargets.cmake)\n"
+      )
+
+    install(FILES ${CMAKE_BINARY_DIR}/SPIRV-Tools-toolsConfig.cmake DESTINATION ${PACKAGE_DIR})
   endif(ENABLE_SPIRV_TOOLS_INSTALL)
 endif()
diff --git a/tools/as/as.cpp b/tools/as/as.cpp
index 506b058..2a000cf 100644
--- a/tools/as/as.cpp
+++ b/tools/as/as.cpp
@@ -12,6 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include <cassert>
 #include <cstdio>
 #include <cstring>
 #include <vector>
@@ -19,11 +20,11 @@
 #include "source/spirv_target_env.h"
 #include "spirv-tools/libspirv.h"
 #include "tools/io.h"
+#include "tools/util/flags.h"
 
-void print_usage(char* argv0) {
-  std::string target_env_list = spvTargetEnvList(19, 80);
-  printf(
-      R"(%s - Create a SPIR-V binary module from SPIR-V assembly text
+static const auto kDefaultEnvironment = "spv1.6";
+static const std::string kHelpText =
+    R"(%s - Create a SPIR-V binary module from SPIR-V assembly text
 
 Usage: %s [options] [<filename>]
 
@@ -42,94 +43,70 @@
                   Numeric IDs in the binary will have the same values as in the
                   source. Non-numeric IDs are allocated by filling in the gaps,
                   starting with 1 and going up.
-  --target-env    {%s}
+  --target-env    %s
                   Use specified environment.
-)",
-      argv0, argv0, target_env_list.c_str());
-}
+)";
 
-static const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_6;
+// clang-format off
+FLAG_SHORT_bool(  h,                    /* default_value= */ false,               /* required= */ false);
+FLAG_LONG_bool(   help,                 /* default_value= */ false,               /* required= */false);
+FLAG_LONG_bool(   version,              /* default_value= */ false,               /* required= */ false);
+FLAG_LONG_bool(   preserve_numeric_ids, /* default_value= */ false,               /* required= */ false);
+FLAG_SHORT_string(o,                    /* default_value= */ "",                  /* required= */ false);
+FLAG_LONG_string( target_env,           /* default_value= */ kDefaultEnvironment, /* required= */ false);
+// clang-format on
 
-int main(int argc, char** argv) {
-  const char* inFile = nullptr;
-  const char* outFile = nullptr;
-  uint32_t options = 0;
-  spv_target_env target_env = kDefaultEnvironment;
-  for (int argi = 1; argi < argc; ++argi) {
-    if ('-' == argv[argi][0]) {
-      switch (argv[argi][1]) {
-        case 'h': {
-          print_usage(argv[0]);
-          return 0;
-        }
-        case 'o': {
-          if (!outFile && argi + 1 < argc) {
-            outFile = argv[++argi];
-          } else {
-            print_usage(argv[0]);
-            return 1;
-          }
-        } break;
-        case 0: {
-          // Setting a filename of "-" to indicate stdin.
-          if (!inFile) {
-            inFile = argv[argi];
-          } else {
-            fprintf(stderr, "error: More than one input file specified\n");
-            return 1;
-          }
-        } break;
-        case '-': {
-          // Long options
-          if (0 == strcmp(argv[argi], "--version")) {
-            printf("%s\n", spvSoftwareVersionDetailsString());
-            printf("Target: %s\n",
-                   spvTargetEnvDescription(kDefaultEnvironment));
-            return 0;
-          } else if (0 == strcmp(argv[argi], "--help")) {
-            print_usage(argv[0]);
-            return 0;
-          } else if (0 == strcmp(argv[argi], "--preserve-numeric-ids")) {
-            options |= SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS;
-          } else if (0 == strcmp(argv[argi], "--target-env")) {
-            if (argi + 1 < argc) {
-              const auto env_str = argv[++argi];
-              if (!spvParseTargetEnv(env_str, &target_env)) {
-                fprintf(stderr, "error: Unrecognized target env: %s\n",
-                        env_str);
-                return 1;
-              }
-            } else {
-              fprintf(stderr, "error: Missing argument to --target-env\n");
-              return 1;
-            }
-          } else {
-            fprintf(stderr, "error: Unrecognized option: %s\n\n", argv[argi]);
-            print_usage(argv[0]);
-            return 1;
-          }
-        } break;
-        default:
-          fprintf(stderr, "error: Unrecognized option: %s\n\n", argv[argi]);
-          print_usage(argv[0]);
-          return 1;
-      }
-    } else {
-      if (!inFile) {
-        inFile = argv[argi];
-      } else {
-        fprintf(stderr, "error: More than one input file specified\n");
-        return 1;
-      }
-    }
+int main(int, const char** argv) {
+  if (!flags::Parse(argv)) {
+    return 1;
   }
 
-  if (!outFile) {
+  if (flags::h.value() || flags::help.value()) {
+    const std::string target_env_list = spvTargetEnvList(19, 80);
+    printf(kHelpText.c_str(), argv[0], argv[0], target_env_list.c_str());
+    return 0;
+  }
+
+  if (flags::version.value()) {
+    spv_target_env target_env;
+    bool success = spvParseTargetEnv(kDefaultEnvironment, &target_env);
+    assert(success && "Default environment should always parse.");
+    if (!success) {
+      fprintf(stderr,
+              "error: invalid default target environment. Please report this "
+              "issue.");
+      return 1;
+    }
+    printf("%s\n", spvSoftwareVersionDetailsString());
+    printf("Target: %s\n", spvTargetEnvDescription(target_env));
+    return 0;
+  }
+
+  std::string outFile = flags::o.value();
+  if (outFile.empty()) {
     outFile = "out.spv";
   }
 
+  uint32_t options = 0;
+  if (flags::preserve_numeric_ids.value()) {
+    options |= SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS;
+  }
+
+  spv_target_env target_env;
+  if (!spvParseTargetEnv(flags::target_env.value().c_str(), &target_env)) {
+    fprintf(stderr, "error: Unrecognized target env: %s\n",
+            flags::target_env.value().c_str());
+    return 1;
+  }
+
+  if (flags::positional_arguments.size() != 1) {
+    fprintf(stderr, "error: exactly one input file must be specified.\n");
+    return 1;
+  }
+  std::string inFile = flags::positional_arguments[0];
+
   std::vector<char> contents;
-  if (!ReadTextFile<char>(inFile, &contents)) return 1;
+  if (!ReadTextFile<char>(inFile.c_str(), &contents)) return 1;
 
   spv_binary binary;
   spv_diagnostic diagnostic = nullptr;
@@ -143,7 +120,8 @@
     return error;
   }
 
-  if (!WriteFile<uint32_t>(outFile, "wb", binary->code, binary->wordCount)) {
+  if (!WriteFile<uint32_t>(outFile.c_str(), "wb", binary->code,
+                           binary->wordCount)) {
     spvBinaryDestroy(binary);
     return 1;
   }
diff --git a/tools/cfg/bin_to_dot.cpp b/tools/cfg/bin_to_dot.cpp
index 72e7693..40a7dc4 100644
--- a/tools/cfg/bin_to_dot.cpp
+++ b/tools/cfg/bin_to_dot.cpp
@@ -81,26 +81,26 @@
 
 spv_result_t DotConverter::HandleInstruction(
     const spv_parsed_instruction_t& inst) {
-  switch (inst.opcode) {
-    case SpvOpFunction:
+  switch (spv::Op(inst.opcode)) {
+    case spv::Op::OpFunction:
       current_function_id_ = inst.result_id;
       seen_function_entry_block_ = false;
       break;
-    case SpvOpFunctionEnd:
+    case spv::Op::OpFunctionEnd:
       current_function_id_ = 0;
       break;
 
-    case SpvOpLabel:
+    case spv::Op::OpLabel:
       current_block_id_ = inst.result_id;
       break;
 
-    case SpvOpBranch:
+    case spv::Op::OpBranch:
       FlushBlock({inst.words[1]});
       break;
-    case SpvOpBranchConditional:
+    case spv::Op::OpBranchConditional:
       FlushBlock({inst.words[2], inst.words[3]});
       break;
-    case SpvOpSwitch: {
+    case spv::Op::OpSwitch: {
       std::vector<uint32_t> successors{inst.words[2]};
       for (size_t i = 3; i < inst.num_operands; i += 2) {
         successors.push_back(inst.words[inst.operands[i].offset]);
@@ -108,18 +108,18 @@
       FlushBlock(successors);
     } break;
 
-    case SpvOpKill:
-    case SpvOpReturn:
-    case SpvOpUnreachable:
-    case SpvOpReturnValue:
+    case spv::Op::OpKill:
+    case spv::Op::OpReturn:
+    case spv::Op::OpUnreachable:
+    case spv::Op::OpReturnValue:
       FlushBlock({});
       break;
 
-    case SpvOpLoopMerge:
+    case spv::Op::OpLoopMerge:
       merge_ = inst.words[1];
       continue_target_ = inst.words[2];
       break;
-    case SpvOpSelectionMerge:
+    case spv::Op::OpSelectionMerge:
       merge_ = inst.words[1];
       break;
     default:
diff --git a/tools/cfg/cfg.cpp b/tools/cfg/cfg.cpp
index 5380c21..2d11e6f 100644
--- a/tools/cfg/cfg.cpp
+++ b/tools/cfg/cfg.cpp
@@ -21,11 +21,11 @@
 #include "spirv-tools/libspirv.h"
 #include "tools/cfg/bin_to_dot.h"
 #include "tools/io.h"
+#include "tools/util/flags.h"
 
-// Prints a program usage message to stdout.
-static void print_usage(const char* argv0) {
-  printf(
-      R"(%s - Show the control flow graph in GraphiViz "dot" form. EXPERIMENTAL
+static const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_6;
+static const std::string kHelpText =
+    R"(%s - Show the control flow graph in GraphiViz "dot" form. EXPERIMENTAL
 
 Usage: %s [options] [<filename>]
 
@@ -40,71 +40,42 @@
   -o <filename>   Set the output filename.
                   Output goes to standard output if this option is
                   not specified, or if the filename is "-".
-)",
-      argv0, argv0);
-}
+)";
 
-static const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_6;
+// clang-format off
+FLAG_SHORT_bool(  h,       /* default_value= */ false, /* required= */ false);
+FLAG_LONG_bool(   help,    /* default_value= */ false, /* required= */false);
+FLAG_LONG_bool(   version, /* default_value= */ false, /* required= */ false);
+FLAG_SHORT_string(o,       /* default_value= */ "",    /* required= */ false);
+// clang-format on
 
-int main(int argc, char** argv) {
-  const char* inFile = nullptr;
-  const char* outFile = nullptr;  // Stays nullptr if printing to stdout.
-
-  for (int argi = 1; argi < argc; ++argi) {
-    if ('-' == argv[argi][0]) {
-      switch (argv[argi][1]) {
-        case 'h':
-          print_usage(argv[0]);
-          return 0;
-        case 'o': {
-          if (!outFile && argi + 1 < argc) {
-            outFile = argv[++argi];
-          } else {
-            print_usage(argv[0]);
-            return 1;
-          }
-        } break;
-        case '-': {
-          // Long options
-          if (0 == strcmp(argv[argi], "--help")) {
-            print_usage(argv[0]);
-            return 0;
-          }
-          if (0 == strcmp(argv[argi], "--version")) {
-            printf("%s EXPERIMENTAL\n", spvSoftwareVersionDetailsString());
-            printf("Target: %s\n",
-                   spvTargetEnvDescription(kDefaultEnvironment));
-            return 0;
-          }
-          print_usage(argv[0]);
-          return 1;
-        }
-        case 0: {
-          // Setting a filename of "-" to indicate stdin.
-          if (!inFile) {
-            inFile = argv[argi];
-          } else {
-            fprintf(stderr, "error: More than one input file specified\n");
-            return 1;
-          }
-        } break;
-        default:
-          print_usage(argv[0]);
-          return 1;
-      }
-    } else {
-      if (!inFile) {
-        inFile = argv[argi];
-      } else {
-        fprintf(stderr, "error: More than one input file specified\n");
-        return 1;
-      }
-    }
+int main(int, const char** argv) {
+  if (!flags::Parse(argv)) {
+    return 1;
   }
 
+  if (flags::h.value() || flags::help.value()) {
+    printf(kHelpText.c_str(), argv[0], argv[0]);
+    return 0;
+  }
+
+  if (flags::version.value()) {
+    printf("%s EXPERIMENTAL\n", spvSoftwareVersionDetailsString());
+    printf("Target: %s\n", spvTargetEnvDescription(kDefaultEnvironment));
+    return 0;
+  }
+
+  if (flags::positional_arguments.size() != 1) {
+    fprintf(stderr, "error: exactly one input file must be specified.\n");
+    return 1;
+  }
+
+  std::string inFile = flags::positional_arguments[0];
+  std::string outFile = flags::o.value();
+
   // Read the input binary.
   std::vector<uint32_t> contents;
-  if (!ReadBinaryFile<uint32_t>(inFile, &contents)) return 1;
+  if (!ReadBinaryFile<uint32_t>(inFile.c_str(), &contents)) return 1;
   spv_context context = spvContextCreate(kDefaultEnvironment);
   spv_diagnostic diagnostic = nullptr;
 
@@ -118,7 +89,8 @@
     return error;
   }
   std::string str = ss.str();
-  WriteFile(outFile, "w", str.data(), str.size());
+  WriteFile(outFile.empty() ? nullptr : outFile.c_str(), "w", str.data(),
+            str.size());
 
   spvDiagnosticDestroy(diagnostic);
   spvContextDestroy(context);
diff --git a/tools/diff/diff.cpp b/tools/diff/diff.cpp
index d3cad04..2217896 100644
--- a/tools/diff/diff.cpp
+++ b/tools/diff/diff.cpp
@@ -17,14 +17,25 @@
 #endif
 
 #include "source/diff/diff.h"
-
 #include "source/opt/build_module.h"
 #include "source/opt/ir_context.h"
 #include "spirv-tools/libspirv.hpp"
 #include "tools/io.h"
 #include "tools/util/cli_consumer.h"
+#include "tools/util/flags.h"
 
-static void print_usage(char* argv0) {
+namespace {
+
+constexpr auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_6;
+
+constexpr bool kColorIsPossible =
+#if SPIRV_COLOR_TERMINAL
+    true;
+#else
+    false;
+#endif
+
+void print_usage(const char* argv0) {
   printf(R"(%s - Compare two SPIR-V files
 
 Usage: %s <src_filename> <dst_filename>
@@ -38,11 +49,12 @@
   -h, --help      Print this help.
   --version       Display diff version information.
 
-  --color         Force color output.  The default when printing to a terminal.
-                  Overrides a previous --no-color option.
-  --no-color      Don't print in color.  Overrides a previous --color option.
-                  The default when output goes to something other than a
-                  terminal (e.g. a pipe, or a shell redirection).
+  --color         Force color output. The default when printing to a terminal.
+                  If both --color and --no-color is present, --no-color prevails.
+  --no-color      Don't print in color. The default when output goes to
+                  something other than a terminal (e.g. a pipe, or a shell
+                  redirection).
+                  If both --color and --no-color is present, --no-color prevails.
 
   --no-indent     Don't indent instructions.
 
@@ -58,9 +70,7 @@
          argv0, argv0);
 }
 
-static const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_6;
-
-static bool is_assembly(const char* path) {
+bool is_assembly(const char* path) {
   const char* suffix = strrchr(path, '.');
   if (suffix == nullptr) {
     return false;
@@ -69,7 +79,7 @@
   return strcmp(suffix, ".spvasm") == 0;
 }
 
-static std::unique_ptr<spvtools::opt::IRContext> load_module(const char* path) {
+std::unique_ptr<spvtools::opt::IRContext> load_module(const char* path) {
   if (is_assembly(path)) {
     std::vector<char> contents;
     if (!ReadTextFile<char>(path, &contents)) return {};
@@ -89,101 +99,62 @@
                                contents.data(), contents.size());
 }
 
-int main(int argc, char** argv) {
-  const char* src_file = nullptr;
-  const char* dst_file = nullptr;
-  bool color_is_possible =
-#if SPIRV_COLOR_TERMINAL
-      true;
-#else
-      false;
-#endif
-  bool force_color = false;
-  bool force_no_color = false;
-  bool allow_indent = true;
-  bool no_header = false;
-  bool dump_id_map = false;
-  bool ignore_set_binding = false;
-  bool ignore_location = false;
+}  // namespace
 
-  for (int argi = 1; argi < argc; ++argi) {
-    if ('-' == argv[argi][0]) {
-      switch (argv[argi][1]) {
-        case 'h':
-          print_usage(argv[0]);
-          return 0;
-        case '-': {
-          // Long options
-          if (strcmp(argv[argi], "--no-color") == 0) {
-            force_no_color = true;
-            force_color = false;
-          } else if (strcmp(argv[argi], "--color") == 0) {
-            force_no_color = false;
-            force_color = true;
-          } else if (strcmp(argv[argi], "--no-indent") == 0) {
-            allow_indent = false;
-          } else if (strcmp(argv[argi], "--no-header") == 0) {
-            no_header = true;
-          } else if (strcmp(argv[argi], "--with-id-map") == 0) {
-            dump_id_map = true;
-          } else if (strcmp(argv[argi], "--ignore-set-binding") == 0) {
-            ignore_set_binding = true;
-          } else if (strcmp(argv[argi], "--ignore-location") == 0) {
-            ignore_location = true;
-          } else if (strcmp(argv[argi], "--help") == 0) {
-            print_usage(argv[0]);
-            return 0;
-          } else if (strcmp(argv[argi], "--version") == 0) {
-            printf("%s\n", spvSoftwareVersionDetailsString());
-            printf("Target: %s\n",
-                   spvTargetEnvDescription(kDefaultEnvironment));
-            return 0;
-          } else {
-            print_usage(argv[0]);
-            return 1;
-          }
-        } break;
-        default:
-          print_usage(argv[0]);
-          return 1;
-      }
-    } else {
-      if (src_file == nullptr) {
-        src_file = argv[argi];
-      } else if (dst_file == nullptr) {
-        dst_file = argv[argi];
-      } else {
-        fprintf(stderr, "error: More than two input files specified\n");
-        return 1;
-      }
-    }
-  }
+// clang-format off
+FLAG_SHORT_bool(h,                  /* default_value= */ false, /* required= */ false);
+FLAG_LONG_bool( help,               /* default_value= */ false, /* required= */false);
+FLAG_LONG_bool( version,            /* default_value= */ false, /* required= */ false);
+FLAG_LONG_bool( color,              /* default_value= */ false, /* required= */ false);
+FLAG_LONG_bool( no_color,           /* default_value= */ false, /* required= */ false);
+FLAG_LONG_bool( no_indent,          /* default_value= */ false, /* required= */ false);
+FLAG_LONG_bool( no_header,          /* default_value= */ false, /* required= */ false);
+FLAG_LONG_bool( with_id_map,        /* default_value= */ false, /* required= */ false);
+FLAG_LONG_bool( ignore_set_binding, /* default_value= */ false, /* required= */ false);
+FLAG_LONG_bool( ignore_location,    /* default_value= */ false, /* required= */ false);
+// clang-format on
 
-  if (src_file == nullptr || dst_file == nullptr) {
-    print_usage(argv[0]);
+int main(int, const char* argv[]) {
+  if (!flags::Parse(argv)) {
     return 1;
   }
 
-  spvtools::diff::Options options;
-
-  if (allow_indent) options.indent = true;
-  if (no_header) options.no_header = true;
-  if (dump_id_map) options.dump_id_map = true;
-  if (ignore_set_binding) options.ignore_set_binding = true;
-  if (ignore_location) options.ignore_location = true;
-
-  if (color_is_possible && !force_no_color) {
-    bool output_is_tty = true;
-#if defined(_POSIX_VERSION)
-    output_is_tty = isatty(fileno(stdout));
-#endif
-    if (output_is_tty || force_color) {
-      options.color_output = true;
-    }
+  if (flags::h.value() || flags::help.value()) {
+    print_usage(argv[0]);
+    return 0;
   }
 
-  std::unique_ptr<spvtools::opt::IRContext> src = load_module(src_file);
-  std::unique_ptr<spvtools::opt::IRContext> dst = load_module(dst_file);
+  if (flags::version.value()) {
+    printf("%s\n", spvSoftwareVersionDetailsString());
+    printf("Target: %s\n", spvTargetEnvDescription(kDefaultEnvironment));
+    return 0;
+  }
+
+  if (flags::positional_arguments.size() != 2) {
+    fprintf(stderr, "error: two input files required.\n");
+    return 1;
+  }
+
+#if defined(_POSIX_VERSION)
+  const bool output_is_tty = isatty(fileno(stdout));
+#else
+  const bool output_is_tty = true;
+#endif
+
+  const std::string& src_file = flags::positional_arguments[0];
+  const std::string& dst_file = flags::positional_arguments[1];
+
+  spvtools::diff::Options options;
+  options.color_output = (output_is_tty || flags::color.value()) &&
+                         !flags::no_color.value() && kColorIsPossible;
+  options.indent = !flags::no_indent.value();
+  options.no_header = flags::no_header.value();
+  options.dump_id_map = flags::with_id_map.value();
+  options.ignore_set_binding = flags::ignore_set_binding.value();
+  options.ignore_location = flags::ignore_location.value();
+
+  std::unique_ptr<spvtools::opt::IRContext> src = load_module(src_file.c_str());
+  std::unique_ptr<spvtools::opt::IRContext> dst = load_module(dst_file.c_str());
 
   if (!src) {
     fprintf(stderr, "error: Loading src file\n");
diff --git a/tools/dis/dis.cpp b/tools/dis/dis.cpp
index 64380db..aacd37f 100644
--- a/tools/dis/dis.cpp
+++ b/tools/dis/dis.cpp
@@ -24,10 +24,9 @@
 
 #include "spirv-tools/libspirv.h"
 #include "tools/io.h"
+#include "tools/util/flags.h"
 
-static void print_usage(char* argv0) {
-  printf(
-      R"(%s - Disassemble a SPIR-V binary module
+static const std::string kHelpText = R"(%s - Disassemble a SPIR-V binary module
 
 Usage: %s [options] [<filename>]
 
@@ -58,15 +57,49 @@
   --offsets       Show byte offsets for each instruction.
 
   --comment       Add comments to make reading easier
-)",
-      argv0, argv0);
-}
+)";
+
+// clang-format off
+FLAG_SHORT_bool  (h,         /* default_value= */ false, /* required= */ false);
+FLAG_SHORT_string(o,         /* default_value= */ "-",   /* required= */ false);
+FLAG_LONG_bool   (help,      /* default_value= */ false, /* required= */false);
+FLAG_LONG_bool   (version,   /* default_value= */ false, /* required= */ false);
+FLAG_LONG_bool   (color,     /* default_value= */ false, /* required= */ false);
+FLAG_LONG_bool   (no_color,  /* default_value= */ false, /* required= */ false);
+FLAG_LONG_bool   (no_indent, /* default_value= */ false, /* required= */ false);
+FLAG_LONG_bool   (no_header, /* default_value= */ false, /* required= */ false);
+FLAG_LONG_bool   (raw_id,    /* default_value= */ false, /* required= */ false);
+FLAG_LONG_bool   (offsets,   /* default_value= */ false, /* required= */ false);
+FLAG_LONG_bool   (comment,   /* default_value= */ false, /* required= */ false);
+// clang-format on
 
 static const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_5;
 
-int main(int argc, char** argv) {
-  const char* inFile = nullptr;
-  const char* outFile = nullptr;
+int main(int, const char** argv) {
+  if (!flags::Parse(argv)) {
+    return 1;
+  }
+
+  if (flags::h.value() || flags::help.value()) {
+    printf(kHelpText.c_str(), argv[0], argv[0]);
+    return 0;
+  }
+
+  if (flags::version.value()) {
+    printf("%s\n", spvSoftwareVersionDetailsString());
+    printf("Target: %s\n", spvTargetEnvDescription(kDefaultEnvironment));
+    return 0;
+  }
+
+  if (flags::positional_arguments.size() > 1) {
+    fprintf(stderr, "error: more than one input file specified.\n");
+    return 1;
+  }
+
+  const std::string inFile = flags::positional_arguments.size() == 0
+                                 ? "-"
+                                 : flags::positional_arguments[0];
+  const std::string outFile = flags::o.value();
 
   bool color_is_possible =
 #if SPIRV_COLOR_TERMINAL
@@ -74,105 +107,30 @@
 #else
       false;
 #endif
-  bool force_color = false;
-  bool force_no_color = false;
-
-  bool allow_indent = true;
-  bool show_byte_offsets = false;
-  bool no_header = false;
-  bool friendly_names = true;
-  bool comments = false;
-
-  for (int argi = 1; argi < argc; ++argi) {
-    if ('-' == argv[argi][0]) {
-      switch (argv[argi][1]) {
-        case 'h':
-          print_usage(argv[0]);
-          return 0;
-        case 'o': {
-          if (!outFile && argi + 1 < argc) {
-            outFile = argv[++argi];
-          } else {
-            print_usage(argv[0]);
-            return 1;
-          }
-        } break;
-        case '-': {
-          // Long options
-          if (0 == strcmp(argv[argi], "--no-color")) {
-            force_no_color = true;
-            force_color = false;
-          } else if (0 == strcmp(argv[argi], "--color")) {
-            force_no_color = false;
-            force_color = true;
-          } else if (0 == strcmp(argv[argi], "--comment")) {
-            comments = true;
-          } else if (0 == strcmp(argv[argi], "--no-indent")) {
-            allow_indent = false;
-          } else if (0 == strcmp(argv[argi], "--offsets")) {
-            show_byte_offsets = true;
-          } else if (0 == strcmp(argv[argi], "--no-header")) {
-            no_header = true;
-          } else if (0 == strcmp(argv[argi], "--raw-id")) {
-            friendly_names = false;
-          } else if (0 == strcmp(argv[argi], "--help")) {
-            print_usage(argv[0]);
-            return 0;
-          } else if (0 == strcmp(argv[argi], "--version")) {
-            printf("%s\n", spvSoftwareVersionDetailsString());
-            printf("Target: %s\n",
-                   spvTargetEnvDescription(kDefaultEnvironment));
-            return 0;
-          } else {
-            print_usage(argv[0]);
-            return 1;
-          }
-        } break;
-        case 0: {
-          // Setting a filename of "-" to indicate stdin.
-          if (!inFile) {
-            inFile = argv[argi];
-          } else {
-            fprintf(stderr, "error: More than one input file specified\n");
-            return 1;
-          }
-        } break;
-        default:
-          print_usage(argv[0]);
-          return 1;
-      }
-    } else {
-      if (!inFile) {
-        inFile = argv[argi];
-      } else {
-        fprintf(stderr, "error: More than one input file specified\n");
-        return 1;
-      }
-    }
-  }
 
   uint32_t options = SPV_BINARY_TO_TEXT_OPTION_NONE;
 
-  if (allow_indent) options |= SPV_BINARY_TO_TEXT_OPTION_INDENT;
+  if (!flags::no_indent.value()) options |= SPV_BINARY_TO_TEXT_OPTION_INDENT;
 
-  if (show_byte_offsets) options |= SPV_BINARY_TO_TEXT_OPTION_SHOW_BYTE_OFFSET;
+  if (flags::offsets.value())
+    options |= SPV_BINARY_TO_TEXT_OPTION_SHOW_BYTE_OFFSET;
 
-  if (no_header) options |= SPV_BINARY_TO_TEXT_OPTION_NO_HEADER;
+  if (flags::no_header.value()) options |= SPV_BINARY_TO_TEXT_OPTION_NO_HEADER;
 
-  if (friendly_names) options |= SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES;
+  if (!flags::raw_id.value())
+    options |= SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES;
 
-  if (comments) options |= SPV_BINARY_TO_TEXT_OPTION_COMMENT;
+  if (flags::comment.value()) options |= SPV_BINARY_TO_TEXT_OPTION_COMMENT;
 
-  if (!outFile || (0 == strcmp("-", outFile))) {
+  if (flags::o.value() == "-") {
     // Print to standard output.
     options |= SPV_BINARY_TO_TEXT_OPTION_PRINT;
-
-    if (color_is_possible && !force_no_color) {
+    if (color_is_possible && !flags::no_color.value()) {
       bool output_is_tty = true;
 #if defined(_POSIX_VERSION)
       output_is_tty = isatty(fileno(stdout));
 #endif
-      if (output_is_tty || force_color) {
+      if (output_is_tty || flags::color.value()) {
         options |= SPV_BINARY_TO_TEXT_OPTION_COLOR;
       }
     }
@@ -180,7 +138,7 @@
 
   // Read the input binary.
   std::vector<uint32_t> contents;
-  if (!ReadBinaryFile<uint32_t>(inFile, &contents)) return 1;
+  if (!ReadBinaryFile<uint32_t>(inFile.c_str(), &contents)) return 1;
 
   // If printing to standard output, then spvBinaryToText should
   // do the printing.  In particular, colour printing on Windows is
@@ -205,7 +163,7 @@
   }
 
   if (!print_to_stdout) {
-    if (!WriteFile<char>(outFile, "w", text->str, text->length)) {
+    if (!WriteFile<char>(outFile.c_str(), "w", text->str, text->length)) {
       spvTextDestroy(text);
       return 1;
     }
diff --git a/tools/io.h b/tools/io.h
index 9dc834e..8bbee3a 100644
--- a/tools/io.h
+++ b/tools/io.h
@@ -127,7 +127,7 @@
  public:
   // Opens |filename| in the given mode.  If |filename| is nullptr, the empty
   // string or "-", stdout will be set to the given mode.
-  OutputFile(const char* filename, const char* mode) {
+  OutputFile(const char* filename, const char* mode) : old_mode_(0) {
     const bool use_stdout =
         !filename || (filename[0] == '-' && filename[1] == '\0');
     if (use_stdout) {
diff --git a/tools/link/linker.cpp b/tools/link/linker.cpp
index bdddeb8..f3898aa 100644
--- a/tools/link/linker.cpp
+++ b/tools/link/linker.cpp
@@ -14,6 +14,7 @@
 
 #include "spirv-tools/linker.hpp"
 
+#include <cassert>
 #include <cstring>
 #include <iostream>
 #include <vector>
@@ -22,10 +23,11 @@
 #include "source/table.h"
 #include "spirv-tools/libspirv.hpp"
 #include "tools/io.h"
+#include "tools/util/flags.h"
 
 namespace {
 
-const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_6;
+constexpr auto kDefaultEnvironment = "spv1.6";
 
 void print_usage(const char* program) {
   std::string target_env_list = spvTargetEnvList(16, 80);
@@ -57,6 +59,13 @@
                NOTE: The SPIR-V version used by the linked binary module
                depends only on the version of the inputs, and is not affected
                by this option.
+  --use-highest-version
+               Upgrade the output SPIR-V version to the highest of the input
+               files, instead of requiring all of them to have the same
+               version.
+               NOTE: If one of the older input files uses an instruction that
+               is deprecated in the highest SPIR-V version, the output will
+               be invalid.
   --verify-ids
                Verify that IDs in the resulting modules are truly unique.
   --version
@@ -67,65 +76,60 @@
 
 }  // namespace
 
-int main(int argc, char** argv) {
-  std::vector<const char*> inFiles;
-  const char* outFile = nullptr;
-  spv_target_env target_env = kDefaultEnvironment;
-  spvtools::LinkerOptions options;
+// clang-format off
+FLAG_SHORT_bool(  h,                     /* default_value= */ false,               /* required= */ false);
+FLAG_LONG_bool(   help,                  /* default_value= */ false,               /* required= */false);
+FLAG_LONG_bool(   version,               /* default_value= */ false,               /* required= */ false);
+FLAG_LONG_bool(   verify_ids,            /* default_value= */ false,               /* required= */ false);
+FLAG_LONG_bool(   create_library,        /* default_value= */ false,               /* required= */ false);
+FLAG_LONG_bool(   allow_partial_linkage, /* 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_bool(   use_highest_version,   /* default_value= */ false,               /* required= */ false);
+// clang-format on
 
-  for (int argi = 1; argi < argc; ++argi) {
-    const char* cur_arg = argv[argi];
-    if ('-' == cur_arg[0]) {
-      if (0 == strcmp(cur_arg, "-o")) {
-        if (argi + 1 < argc) {
-          if (!outFile) {
-            outFile = argv[++argi];
-          } else {
-            fprintf(stderr, "error: More than one output file specified\n");
-            return 1;
-          }
-        } else {
-          fprintf(stderr, "error: Missing argument to %s\n", cur_arg);
-          return 1;
-        }
-      } else if (0 == strcmp(cur_arg, "--allow-partial-linkage")) {
-        options.SetAllowPartialLinkage(true);
-      } else if (0 == strcmp(cur_arg, "--create-library")) {
-        options.SetCreateLibrary(true);
-      } else if (0 == strcmp(cur_arg, "--help") || 0 == strcmp(cur_arg, "-h")) {
-        print_usage(argv[0]);
-        return 0;
-      } else if (0 == strcmp(cur_arg, "--target-env")) {
-        if (argi + 1 < argc) {
-          const auto env_str = argv[++argi];
-          if (!spvParseTargetEnv(env_str, &target_env)) {
-            fprintf(stderr, "error: Unrecognized target env: %s\n", env_str);
-            return 1;
-          }
-        } else {
-          fprintf(stderr, "error: Missing argument to --target-env\n");
-          return 1;
-        }
-      } else if (0 == strcmp(cur_arg, "--verify-ids")) {
-        options.SetVerifyIds(true);
-      } else if (0 == strcmp(cur_arg, "--version")) {
-        printf("%s\n", spvSoftwareVersionDetailsString());
-        printf("Target: %s\n", spvTargetEnvDescription(target_env));
-        return 0;
-      } else {
-        fprintf(stderr, "error: Unrecognized option: %s\n\n", argv[argi]);
-        print_usage(argv[0]);
-        return 1;
-      }
-    } else {
-      inFiles.push_back(cur_arg);
+int main(int, const char* argv[]) {
+  if (!flags::Parse(argv)) {
+    return 1;
+  }
+
+  if (flags::h.value() || flags::help.value()) {
+    print_usage(argv[0]);
+    return 0;
+  }
+
+  if (flags::version.value()) {
+    spv_target_env target_env;
+    bool success = spvParseTargetEnv(kDefaultEnvironment, &target_env);
+    assert(success && "Default environment should always parse.");
+    if (!success) {
+      fprintf(stderr,
+              "error: invalid default target environment. Please report this "
+              "issue.");
+      return 1;
     }
+    printf("%s\n", spvSoftwareVersionDetailsString());
+    printf("Target: %s\n", spvTargetEnvDescription(target_env));
+    return 0;
   }
 
-  if (!outFile) {
-    outFile = "out.spv";
+  spv_target_env target_env;
+  if (!spvParseTargetEnv(flags::target_env.value().c_str(), &target_env)) {
+    fprintf(stderr, "error: Unrecognized target env: %s\n",
+            flags::target_env.value().c_str());
+    return 1;
   }
 
+  const std::string outFile =
+      flags::o.value().empty() ? "out.spv" : flags::o.value();
+  const std::vector<std::string>& inFiles = flags::positional_arguments;
+
+  spvtools::LinkerOptions options;
+  options.SetAllowPartialLinkage(flags::allow_partial_linkage.value());
+  options.SetCreateLibrary(flags::create_library.value());
+  options.SetVerifyIds(flags::verify_ids.value());
+  options.SetUseHighestVersion(flags::use_highest_version.value());
+
   if (inFiles.empty()) {
     fprintf(stderr, "error: No input file specified\n");
     return 1;
@@ -133,7 +137,7 @@
 
   std::vector<std::vector<uint32_t>> contents(inFiles.size());
   for (size_t i = 0u; i < inFiles.size(); ++i) {
-    if (!ReadBinaryFile<uint32_t>(inFiles[i], &contents[i])) return 1;
+    if (!ReadBinaryFile<uint32_t>(inFiles[i].c_str(), &contents[i])) return 1;
   }
 
   const spvtools::MessageConsumer consumer = [](spv_message_level_t level,
@@ -165,7 +169,7 @@
   spv_result_t status = Link(context, contents, &linkingResult, options);
   if (status != SPV_SUCCESS && status != SPV_WARNING) return 1;
 
-  if (!WriteFile<uint32_t>(outFile, "wb", linkingResult.data(),
+  if (!WriteFile<uint32_t>(outFile.c_str(), "wb", linkingResult.data(),
                            linkingResult.size()))
     return 1;
 
diff --git a/tools/lint/lint.cpp b/tools/lint/lint.cpp
index d37df83..00c6cd2 100644
--- a/tools/lint/lint.cpp
+++ b/tools/lint/lint.cpp
@@ -18,58 +18,57 @@
 #include "spirv-tools/linter.hpp"
 #include "tools/io.h"
 #include "tools/util/cli_consumer.h"
-
-const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_6;
+#include "tools/util/flags.h"
 
 namespace {
-// Status and actions to perform after parsing command-line arguments.
-enum LintActions { LINT_CONTINUE, LINT_STOP };
 
-struct LintStatus {
-  LintActions action;
-  int code;
-};
+constexpr auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_6;
+constexpr auto kHelpTextFmt =
+    R"(%s - Lint a SPIR-V binary module.
 
-// Parses command-line flags. |argc| contains the number of command-line flags.
-// |argv| points to an array of strings holding the flags.
-//
-// On return, this function stores the name of the input program in |in_file|.
-// The return value indicates whether optimization should continue and a status
-// code indicating an error or success.
-LintStatus ParseFlags(int argc, const char** argv, const char** in_file) {
-  // TODO (dongja): actually parse flags, etc.
-  if (argc != 2) {
-    spvtools::Error(spvtools::utils::CLIMessageConsumer, nullptr, {},
-                    "expected exactly one argument: in_file");
-    return {LINT_STOP, 1};
-  }
+Usage: %s [options] <filename>
 
-  *in_file = argv[1];
+Options:
 
-  return {LINT_CONTINUE, 0};
-}
+  -h, --help      Print this help.
+  --version       Display assembler version information.
+)";
+
 }  // namespace
 
-int main(int argc, const char** argv) {
-  const char* in_file = nullptr;
+// clang-format off
+FLAG_SHORT_bool(  h,       /* default_value= */ false, /* required= */ false);
+FLAG_LONG_bool(   help,    /* default_value= */ false, /* required= */ false);
+FLAG_LONG_bool(   version, /* default_value= */ false, /* required= */ false);
+// clang-format on
 
-  spv_target_env target_env = kDefaultEnvironment;
-
-  spvtools::Linter linter(target_env);
-  linter.SetMessageConsumer(spvtools::utils::CLIMessageConsumer);
-
-  LintStatus status = ParseFlags(argc, argv, &in_file);
-
-  if (status.action == LINT_STOP) {
-    return status.code;
-  }
-
-  std::vector<uint32_t> binary;
-  if (!ReadBinaryFile(in_file, &binary)) {
+int main(int, const char** argv) {
+  if (!flags::Parse(argv)) {
     return 1;
   }
 
-  bool ok = linter.Run(binary.data(), binary.size());
+  if (flags::h.value() || flags::help.value()) {
+    printf(kHelpTextFmt, argv[0], argv[0]);
+    return 0;
+  }
 
-  return ok ? 0 : 1;
+  if (flags::version.value()) {
+    printf("%s\n", spvSoftwareVersionDetailsString());
+    return 0;
+  }
+
+  if (flags::positional_arguments.size() != 1) {
+    spvtools::Error(spvtools::utils::CLIMessageConsumer, nullptr, {},
+                    "expected exactly one input file.");
+    return 1;
+  }
+
+  spvtools::Linter linter(kDefaultEnvironment);
+  linter.SetMessageConsumer(spvtools::utils::CLIMessageConsumer);
+  std::vector<uint32_t> binary;
+  if (!ReadBinaryFile(flags::positional_arguments[0].c_str(), &binary)) {
+    return 1;
+  }
+
+  return linter.Run(binary.data(), binary.size()) ? 0 : 1;
 }
diff --git a/tools/objdump/extract_source.cpp b/tools/objdump/extract_source.cpp
new file mode 100644
index 0000000..0295952
--- /dev/null
+++ b/tools/objdump/extract_source.cpp
@@ -0,0 +1,213 @@
+// 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.
+
+#include "extract_source.h"
+
+#include <cassert>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include "source/opt/log.h"
+#include "spirv-tools/libspirv.hpp"
+#include "spirv/unified1/spirv.hpp"
+#include "tools/util/cli_consumer.h"
+
+namespace {
+
+constexpr auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_6;
+
+// Extract a string literal from a given range.
+// Copies all the characters from `begin` to the first '\0' it encounters, while
+// removing escape patterns.
+// Not finding a '\0' before reaching `end` fails the extraction.
+//
+// Returns `true` if the extraction succeeded.
+// `output` value is undefined if false is returned.
+spv_result_t ExtractStringLiteral(const spv_position_t& loc, const char* begin,
+                                  const char* end, std::string* output) {
+  size_t sourceLength = std::distance(begin, end);
+  std::string escapedString;
+  escapedString.resize(sourceLength);
+
+  size_t writeIndex = 0;
+  size_t readIndex = 0;
+  for (; readIndex < sourceLength; writeIndex++, readIndex++) {
+    const char read = begin[readIndex];
+    if (read == '\0') {
+      escapedString.resize(writeIndex);
+      output->append(escapedString);
+      return SPV_SUCCESS;
+    }
+
+    if (read == '\\') {
+      ++readIndex;
+    }
+    escapedString[writeIndex] = begin[readIndex];
+  }
+
+  spvtools::Error(spvtools::utils::CLIMessageConsumer, "", loc,
+                  "Missing NULL terminator for literal string.");
+  return SPV_ERROR_INVALID_BINARY;
+}
+
+spv_result_t extractOpString(const spv_position_t& loc,
+                             const spv_parsed_instruction_t& instruction,
+                             std::string* output) {
+  assert(output != nullptr);
+  assert(instruction.opcode == spv::Op::OpString);
+  if (instruction.num_operands != 2) {
+    spvtools::Error(spvtools::utils::CLIMessageConsumer, "", loc,
+                    "Missing operands for OpString.");
+    return SPV_ERROR_INVALID_BINARY;
+  }
+
+  const auto& operand = instruction.operands[1];
+  const char* stringBegin =
+      reinterpret_cast<const char*>(instruction.words + operand.offset);
+  const char* stringEnd = reinterpret_cast<const char*>(
+      instruction.words + operand.offset + operand.num_words);
+  return ExtractStringLiteral(loc, stringBegin, stringEnd, output);
+}
+
+spv_result_t extractOpSourceContinued(
+    const spv_position_t& loc, const spv_parsed_instruction_t& instruction,
+    std::string* output) {
+  assert(output != nullptr);
+  assert(instruction.opcode == spv::Op::OpSourceContinued);
+  if (instruction.num_operands != 1) {
+    spvtools::Error(spvtools::utils::CLIMessageConsumer, "", loc,
+                    "Missing operands for OpSourceContinued.");
+    return SPV_ERROR_INVALID_BINARY;
+  }
+
+  const auto& operand = instruction.operands[0];
+  const char* stringBegin =
+      reinterpret_cast<const char*>(instruction.words + operand.offset);
+  const char* stringEnd = reinterpret_cast<const char*>(
+      instruction.words + operand.offset + operand.num_words);
+  return ExtractStringLiteral(loc, stringBegin, stringEnd, output);
+}
+
+spv_result_t extractOpSource(const spv_position_t& loc,
+                             const spv_parsed_instruction_t& instruction,
+                             spv::Id* filename, std::string* code) {
+  assert(filename != nullptr && code != nullptr);
+  assert(instruction.opcode == spv::Op::OpSource);
+  // OpCode [ Source Language | Version | File (optional) | Source (optional) ]
+  if (instruction.num_words < 3) {
+    spvtools::Error(spvtools::utils::CLIMessageConsumer, "", loc,
+                    "Missing operands for OpSource.");
+    return SPV_ERROR_INVALID_BINARY;
+  }
+
+  *filename = 0;
+  *code = "";
+  if (instruction.num_words < 4) {
+    return SPV_SUCCESS;
+  }
+  *filename = instruction.words[3];
+
+  if (instruction.num_words < 5) {
+    return SPV_SUCCESS;
+  }
+
+  const char* stringBegin =
+      reinterpret_cast<const char*>(instruction.words + 4);
+  const char* stringEnd =
+      reinterpret_cast<const char*>(instruction.words + instruction.num_words);
+  return ExtractStringLiteral(loc, stringBegin, stringEnd, code);
+}
+
+}  // namespace
+
+bool ExtractSourceFromModule(
+    const std::vector<uint32_t>& binary,
+    std::unordered_map<std::string, std::string>* output) {
+  auto context = spvtools::SpirvTools(kDefaultEnvironment);
+  context.SetMessageConsumer(spvtools::utils::CLIMessageConsumer);
+
+  // There is nothing valuable in the header.
+  spvtools::HeaderParser headerParser = [](const spv_endianness_t,
+                                           const spv_parsed_header_t&) {
+    return SPV_SUCCESS;
+  };
+
+  std::unordered_map<uint32_t, std::string> stringMap;
+  std::vector<std::pair<spv::Id, std::string>> sources;
+  spv::Op lastOpcode = spv::Op::OpMax;
+  size_t instructionIndex = 0;
+
+  spvtools::InstructionParser instructionParser =
+      [&stringMap, &sources, &lastOpcode,
+       &instructionIndex](const spv_parsed_instruction_t& instruction) {
+        const spv_position_t loc = {0, 0, instructionIndex + 1};
+        spv_result_t result = SPV_SUCCESS;
+
+        if (instruction.opcode == 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) {
+          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) {
+          if (lastOpcode != spv::Op::OpSource) {
+            spvtools::Error(spvtools::utils::CLIMessageConsumer, "", loc,
+                            "OpSourceContinued MUST follow an OpSource.");
+            return SPV_ERROR_INVALID_BINARY;
+          }
+
+          assert(sources.size() > 0);
+          result = extractOpSourceContinued(loc, instruction,
+                                            &sources.back().second);
+        }
+
+        ++instructionIndex;
+        lastOpcode = static_cast<spv::Op>(instruction.opcode);
+        return result;
+      };
+
+  if (!context.Parse(binary, headerParser, instructionParser)) {
+    return false;
+  }
+
+  std::string defaultName = "unnamed-";
+  size_t unnamedCount = 0;
+  for (auto & [ id, code ] : sources) {
+    std::string filename;
+    const auto it = stringMap.find(id);
+    if (it == stringMap.cend() || it->second.empty()) {
+      filename = "unnamed-" + std::to_string(unnamedCount) + ".hlsl";
+      ++unnamedCount;
+    } else {
+      filename = it->second;
+    }
+
+    if (output->count(filename) != 0) {
+      spvtools::Error(spvtools::utils::CLIMessageConsumer, "", {},
+                      "Source file name conflict.");
+      return false;
+    }
+    output->insert({filename, code});
+  }
+
+  return true;
+}
diff --git a/tools/objdump/extract_source.h b/tools/objdump/extract_source.h
new file mode 100644
index 0000000..3e5ecfa
--- /dev/null
+++ b/tools/objdump/extract_source.h
@@ -0,0 +1,39 @@
+// 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.
+
+#ifndef INCLUDE_SPIRV_TOOLS_EXTRACT_SOURCE_HPP_
+#define INCLUDE_SPIRV_TOOLS_EXTRACT_SOURCE_HPP_
+
+#include <stdint.h>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+// Parse a SPIR-V module, and extracts all HLSL source code from it.
+// This function doesn't lift the SPIR-V code, but only relies on debug symbols.
+// This means if the compiler didn't include some files, they won't show up.
+//
+// Returns a map of <filename, source_code> extracted from it.
+// - `binary`: a vector containing the whole SPIR-V binary to extract source
+// from.
+// - `output`: <filename, source_code> mapping, mapping each filename
+//            (if defined) to its code.
+//
+// Returns `true` if the extraction succeeded, `false` otherwise.
+// `output` value is undefined if `false` is returned.
+bool ExtractSourceFromModule(
+    const std::vector<uint32_t>& binary,
+    std::unordered_map<std::string, std::string>* output);
+
+#endif  // INCLUDE_SPIRV_TOOLS_EXTRACT_SOURCE_HPP_
diff --git a/tools/objdump/objdump.cpp b/tools/objdump/objdump.cpp
new file mode 100644
index 0000000..79181b0
--- /dev/null
+++ b/tools/objdump/objdump.cpp
@@ -0,0 +1,174 @@
+// 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.
+
+#include <filesystem>
+#include <iostream>
+
+#include "extract_source.h"
+#include "source/opt/log.h"
+#include "tools/io.h"
+#include "tools/util/cli_consumer.h"
+#include "tools/util/flags.h"
+
+namespace {
+
+constexpr auto kHelpTextFmt =
+    R"(%s - Dumps information from a SPIR-V binary.
+
+Usage: %s [options] <filename>
+
+one of the following switches must be given:
+  --source        Extract source files obtained from debug symbols, output to stdout.
+  --entrypoint    Extracts the entrypoint name of the module, output to stdout.
+  --compiler-cmd  Extracts the command line used to compile this module, output to stdout.
+
+
+General options:
+  -h, --help      Print this help.
+  --version       Display assembler version information.
+  -f,--force      Allow output file overwrite.
+
+Source dump options:
+  --list          Do not extract source code, only print filenames to stdout.
+  --outdir        Where shall the exrtacted HLSL/HLSL files be written to?
+                  File written to stdout if '-' is given. Default is `-`.
+)";
+
+// Removes trailing '/' from `input`.
+// A behavior difference has been observed between libc++ implementations.
+// Fixing path to prevent this edge case to be reached.
+// (https://github.com/llvm/llvm-project/issues/60634)
+std::string fixPathForLLVM(std::string input) {
+  while (!input.empty() && input.back() == '/') input.resize(input.size() - 1);
+  return input;
+}
+
+// Write each HLSL file described in `sources` in a file in `outdirPath`.
+// Doesn't ovewrite existing files, unless `overwrite` is set to true. The
+// created HLSL file's filename is the path's filename obtained from `sources`.
+// Returns true if all files could be written. False otherwise.
+bool OutputSourceFiles(
+    const std::unordered_map<std::string, std::string>& sources,
+    const std::string& outdirPath, bool overwrite) {
+  std::filesystem::path outdir(fixPathForLLVM(outdirPath));
+  if (!std::filesystem::is_directory(outdir)) {
+    if (!std::filesystem::create_directories(outdir)) {
+      std::cerr << "error: could not create output directory " << outdir
+                << std::endl;
+      return false;
+    }
+  }
+
+  for (const auto & [ filepath, code ] : sources) {
+    if (code.empty()) {
+      std::cout << "Ignoring source for " << filepath
+                << ": no code source in debug infos." << std::endl;
+      continue;
+    }
+
+    std::filesystem::path old_path(filepath);
+    std::filesystem::path new_path = outdir / old_path.filename();
+
+    if (!overwrite && std::filesystem::exists(new_path)) {
+      std::cerr << "file " << filepath
+                << " already exists, aborting (use --overwrite to allow it)."
+                << std::endl;
+      return false;
+    }
+
+    std::cout << "Exporting " << new_path << std::endl;
+    if (!WriteFile<char>(new_path.string().c_str(), "w", code.c_str(),
+                         code.size())) {
+      return false;
+    }
+  }
+  return true;
+}
+
+}  // namespace
+
+// clang-format off
+FLAG_SHORT_bool(  h,            /* default_value= */ false, /* required= */ false);
+FLAG_LONG_bool(   help,         /* default_value= */ false, /* required= */ false);
+FLAG_LONG_bool(   version,      /* default_value= */ false, /* required= */ false);
+FLAG_LONG_bool(   source,       /* default_value= */ false, /* required= */ false);
+FLAG_LONG_bool(   entrypoint,   /* default_value= */ false, /* required= */ false);
+FLAG_LONG_bool(   compiler_cmd, /* default_value= */ false, /* required= */ false);
+FLAG_SHORT_bool(  f,            /* default_value= */ false, /* required= */ false);
+FLAG_LONG_bool(   force,        /* default_value= */ false, /* required= */ false);
+FLAG_LONG_string( outdir,       /* default_value= */ "-",   /* required= */ false);
+FLAG_LONG_bool(   list,         /* default_value= */ false, /* required= */ false);
+// clang-format on
+
+int main(int, const char** argv) {
+  if (!flags::Parse(argv)) {
+    return 1;
+  }
+  if (flags::h.value() || flags::help.value()) {
+    printf(kHelpTextFmt, argv[0], argv[0]);
+    return 0;
+  }
+  if (flags::version.value()) {
+    printf("%s\n", spvSoftwareVersionDetailsString());
+    return 0;
+  }
+
+  if (flags::positional_arguments.size() != 1) {
+    std::cerr << "Expected exactly one input file." << std::endl;
+    return 1;
+  }
+  if (flags::entrypoint.value() || flags::compiler_cmd.value()) {
+    std::cerr << "Unimplemented flags." << std::endl;
+    return 1;
+  }
+
+  std::vector<uint32_t> binary;
+  if (!ReadBinaryFile(flags::positional_arguments[0].c_str(), &binary)) {
+    return 1;
+  }
+
+  if (flags::source.value()) {
+    std::unordered_map<std::string, std::string> sourceCode;
+    if (!ExtractSourceFromModule(binary, &sourceCode)) {
+      return 1;
+    }
+
+    if (flags::list.value()) {
+      for (const auto & [ filename, source ] : sourceCode) {
+        printf("%s\n", filename.c_str());
+      }
+      return 0;
+    }
+
+    const bool outputToConsole = flags::outdir.value() == "-";
+
+    if (outputToConsole) {
+      for (const auto & [ filename, source ] : sourceCode) {
+        std::cout << filename << ":" << std::endl
+                  << source << std::endl
+                  << std::endl;
+      }
+      return 0;
+    }
+
+    const std::filesystem::path outdirPath(flags::outdir.value());
+    if (!OutputSourceFiles(sourceCode, outdirPath.string(),
+                           flags::force.value())) {
+      return 1;
+    }
+  }
+
+  // FIXME: implement logic.
+  return 0;
+}
diff --git a/tools/opt/opt.cpp b/tools/opt/opt.cpp
index ce2103c..3dfa021 100644
--- a/tools/opt/opt.cpp
+++ b/tools/opt/opt.cpp
@@ -496,6 +496,10 @@
                covers reflection information defined by
                SPV_GOOGLE_hlsl_functionality1 and SPV_KHR_non_semantic_info)");
   printf(R"(
+  --switch-descriptorset=<from>:<to>
+               Switch any DescriptoSet decorations using the value <from> to
+               the new value <to>.)");
+  printf(R"(
   --target-env=<env>
                Set the target environment. Without this flag the target
                environment defaults to spv1.5. <env> must be one of
diff --git a/tools/sva/package.json b/tools/sva/package.json
index 3072d4c..15feaca 100644
--- a/tools/sva/package.json
+++ b/tools/sva/package.json
@@ -15,11 +15,11 @@
     "bundle": "rollup -c"
   },
   "devDependencies": {
-    "chai": "^4.2.0",
-    "eslint": "^6.3.0",
+    "chai": "^4.3.7",
+    "eslint": "^8.41.0",
     "esm": "^3.2.25",
-    "mocha": "^6.2.0",
-    "rollup": "^1.21.4",
-    "serve": "^11.1.0"
+    "mocha": "^10.2.0",
+    "rollup": "^3.23.0",
+    "serve": "^14.2.0"
   }
 }
diff --git a/tools/sva/yarn.lock b/tools/sva/yarn.lock
index e7b735e..eed94ce 100644
--- a/tools/sva/yarn.lock
+++ b/tools/sva/yarn.lock
@@ -2,158 +2,206 @@
 # yarn lockfile v1
 
 
-"@babel/code-frame@^7.0.0":
-  version "7.5.5"
-  resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.5.5.tgz#bc0782f6d69f7b7d49531219699b988f669a8f9d"
-  integrity sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==
+"@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:
-    "@babel/highlight" "^7.0.0"
+    eslint-visitor-keys "^3.3.0"
 
-"@babel/highlight@^7.0.0":
-  version "7.5.0"
-  resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.5.0.tgz#56d11312bd9248fa619591d02472be6e8cb32540"
-  integrity sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==
+"@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:
-    chalk "^2.0.0"
-    esutils "^2.0.2"
-    js-tokens "^4.0.0"
+    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"
 
-"@types/estree@0.0.39":
-  version "0.0.39"
-  resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f"
-  integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==
+"@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==
 
-"@types/node@^12.7.5":
-  version "12.7.5"
-  resolved "https://registry.yarnpkg.com/@types/node/-/node-12.7.5.tgz#e19436e7f8e9b4601005d73673b6dc4784ffcc2f"
-  integrity sha512-9fq4jZVhPNW8r+UYKnxF1e2HkDWOWKM5bC2/7c9wPV835I0aOrVbS/Hw/pWPk2uKrNXQqg9Z959Kz+IYDd5p3w==
+"@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"
 
-"@zeit/schemas@2.6.0":
-  version "2.6.0"
-  resolved "https://registry.yarnpkg.com/@zeit/schemas/-/schemas-2.6.0.tgz#004e8e553b4cd53d538bd38eac7bcbf58a867fe3"
-  integrity sha512-uUrgZ8AxS+Lio0fZKAipJjAh415JyrOZowliZAzmnJSsf7piVL5w+G0+gFJ0KSu3QRhvui/7zuvpLz03YjXAhg==
+"@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.7"
-  resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd"
-  integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==
+  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.24"
-    negotiator "0.6.2"
+    mime-types "~2.1.34"
+    negotiator "0.6.3"
 
-acorn-jsx@^5.0.2:
+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/acorn-jsx/-/acorn-jsx-5.0.2.tgz#84b68ea44b373c4f8686023a551f61a21b7c4a4f"
-  integrity sha512-tiNTrP1MP0QrChmD2DdupCr6HWSFeKVw5d/dHTu4Y7rkAkRhU/Dt7dphAfIUyxtHpl/eBVip5uTNSpQJHylpAw==
+  resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c"
+  integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==
 
-acorn@^7.0.0:
-  version "7.1.1"
-  resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.1.tgz#e35668de0b402f359de515c5482a1ab9f89a69bf"
-  integrity sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==
-
-ajv@6.5.3:
-  version "6.5.3"
-  resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.5.3.tgz#71a569d189ecf4f4f321224fecb166f071dd90f9"
-  integrity sha512-LqZ9wY+fx3UMiiPd741yB2pj3hhil+hQc8taf4o2QGRFpWgZ2V5C8HA165DY9sS3fJwsk7uT7ZlFEyC3Ig3lLg==
-  dependencies:
-    fast-deep-equal "^2.0.1"
-    fast-json-stable-stringify "^2.0.0"
-    json-schema-traverse "^0.4.1"
-    uri-js "^4.2.2"
-
-ajv@^6.10.0, ajv@^6.10.2:
-  version "6.10.2"
-  resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.2.tgz#d3cea04d6b017b2894ad69040fec8b623eb4bd52"
-  integrity sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==
-  dependencies:
-    fast-deep-equal "^2.0.1"
-    fast-json-stable-stringify "^2.0.0"
-    json-schema-traverse "^0.4.1"
-    uri-js "^4.2.2"
-
-ansi-align@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-2.0.0.tgz#c36aeccba563b89ceb556f3690f0b1d9e3547f7f"
-  integrity sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=
-  dependencies:
-    string-width "^2.0.0"
-
-ansi-colors@3.2.3:
-  version "3.2.3"
-  resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.3.tgz#57d35b8686e851e2cc04c403f1c00203976a1813"
-  integrity sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==
-
-ansi-escapes@^3.2.0:
-  version "3.2.0"
-  resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b"
-  integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==
-
-ansi-regex@^2.0.0:
-  version "2.1.1"
-  resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
-  integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8=
-
-ansi-regex@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998"
-  integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=
-
-ansi-regex@^4.1.0:
-  version "4.1.0"
-  resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997"
-  integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==
-
-ansi-styles@^3.2.0, ansi-styles@^3.2.1:
-  version "3.2.1"
-  resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
-  integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
-  dependencies:
-    color-convert "^1.9.0"
-
-arch@^2.1.0:
-  version "2.1.1"
-  resolved "https://registry.yarnpkg.com/arch/-/arch-2.1.1.tgz#8f5c2731aa35a30929221bb0640eed65175ec84e"
-  integrity sha512-BLM56aPo9vLLFVa8+/+pJLnrZ7QGGTVHWsCwieAWT9o9K8UeGaQbzZbGoabWLOo2ksBCztoXdqBZBplqLDDCSg==
-
-arg@2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/arg/-/arg-2.0.0.tgz#c06e7ff69ab05b3a4a03ebe0407fac4cba657545"
-  integrity sha512-XxNTUzKnz1ctK3ZIcI2XUPlD96wbHP2nGqkPKpvk/HNRlPveYrXIVSTk9m3LcqOgDPg3B1nMvdV/K8wZd7PG4w==
-
-argparse@^1.0.7:
-  version "1.0.10"
-  resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
-  integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==
-  dependencies:
-    sprintf-js "~1.0.2"
+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==
 
-astral-regex@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9"
-  integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==
-
 balanced-match@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
-  integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
+  integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
 
-boxen@1.3.0:
-  version "1.3.0"
-  resolved "https://registry.yarnpkg.com/boxen/-/boxen-1.3.0.tgz#55c6c39a8ba58d9c61ad22cd877532deb665a20b"
-  integrity sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==
+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 "^2.0.0"
-    camelcase "^4.0.0"
-    chalk "^2.0.1"
-    cli-boxes "^1.0.0"
-    string-width "^2.0.0"
-    term-size "^1.2.0"
-    widest-line "^2.0.0"
+    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"
@@ -163,6 +211,20 @@
     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.2"
+  resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
+  integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
+  dependencies:
+    fill-range "^7.0.1"
+
 browser-stdout@1.3.1:
   version "1.3.1"
   resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60"
@@ -171,163 +233,154 @@
 bytes@3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048"
-  integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=
+  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@^4.0.0:
-  version "4.1.0"
-  resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd"
-  integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=
+camelcase@^6.0.0:
+  version "6.3.0"
+  resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a"
+  integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==
 
-camelcase@^5.0.0:
-  version "5.3.1"
-  resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
-  integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
+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.2.0:
-  version "4.2.0"
-  resolved "https://registry.yarnpkg.com/chai/-/chai-4.2.0.tgz#760aa72cf20e3795e84b12877ce0e83737aa29e5"
-  integrity sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==
+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 "^3.0.1"
+    deep-eql "^4.1.2"
     get-func-name "^2.0.0"
-    pathval "^1.1.0"
+    loupe "^2.3.1"
+    pathval "^1.1.1"
     type-detect "^4.0.5"
 
-chalk@2.4.1:
-  version "2.4.1"
-  resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e"
-  integrity sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==
+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:
-    ansi-styles "^3.2.1"
-    escape-string-regexp "^1.0.5"
-    supports-color "^5.3.0"
+    chalk "^4.1.2"
 
-chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.4.2:
-  version "2.4.2"
-  resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
-  integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
+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 "^3.2.1"
-    escape-string-regexp "^1.0.5"
-    supports-color "^5.3.0"
+    ansi-styles "^4.1.0"
+    supports-color "^7.1.0"
 
-chardet@^0.7.0:
-  version "0.7.0"
-  resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
-  integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==
+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 sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=
+  integrity sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==
 
-cli-boxes@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-1.0.0.tgz#4fa917c3e59c94a004cd61f8ee509da651687143"
-  integrity sha1-T6kXw+WclKAEzWH47lCdplFocUM=
-
-cli-cursor@^2.1.0:
-  version "2.1.0"
-  resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5"
-  integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=
+chokidar@3.5.3:
+  version "3.5.3"
+  resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
+  integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==
   dependencies:
-    restore-cursor "^2.0.0"
+    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-width@^2.0.0:
-  version "2.2.0"
-  resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639"
-  integrity sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=
+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@1.2.3:
-  version "1.2.3"
-  resolved "https://registry.yarnpkg.com/clipboardy/-/clipboardy-1.2.3.tgz#0526361bf78724c1f20be248d428e365433c07ef"
-  integrity sha512-2WNImOvCRe6r63Gk9pShfkwXsVtKCroMAevIbiae021mS850UkWPbevxsBz3tnvjZIEGvlwaqCPsw+4ulzNgJA==
+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.1.0"
-    execa "^0.8.0"
+    arch "^2.2.0"
+    execa "^5.1.1"
+    is-wsl "^2.2.0"
 
-cliui@^4.0.0:
-  version "4.1.0"
-  resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.1.0.tgz#348422dbe82d800b3022eef4f6ac10bf2e4d1b49"
-  integrity sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==
+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 "^2.1.1"
-    strip-ansi "^4.0.0"
-    wrap-ansi "^2.0.0"
+    string-width "^4.2.0"
+    strip-ansi "^6.0.0"
+    wrap-ansi "^7.0.0"
 
-code-point-at@^1.0.0:
-  version "1.1.0"
-  resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
-  integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=
-
-color-convert@^1.9.0:
-  version "1.9.3"
-  resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
-  integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
+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.3"
+    color-name "~1.1.4"
 
-color-name@1.1.3:
-  version "1.1.3"
-  resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
-  integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
+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.14:
-  version "2.0.17"
-  resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.17.tgz#6e8c108a16ad58384a977f3a482ca20bff2f38c1"
-  integrity sha512-BGHeLCK1GV7j1bSmQQAi26X+GgWcTjLr/0tzSvMCl3LH1w1IJ4PFSPoV5316b30cneTziC+B1a+3OjoSUcQYmw==
+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.40.0 < 2"
+    mime-db ">= 1.43.0 < 2"
 
-compression@1.7.3:
-  version "1.7.3"
-  resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.3.tgz#27e0e176aaf260f7f2c2813c3e440adb9f1993db"
-  integrity sha512-HSjyBG5N1Nnz7tF2+O7A9XUhyjru71/fwgNb7oIsEVHR0WShfs2tIS/EySLgiTe98aOK18YDlMXpzjCXY/n9mg==
+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.14"
+    compressible "~2.0.16"
     debug "2.6.9"
-    on-headers "~1.0.1"
+    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 sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
+  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 sha1-DPaLud318r55YcOoUXjLhdunjLQ=
+  integrity sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA==
 
-cross-spawn@^5.0.1:
-  version "5.1.0"
-  resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449"
-  integrity sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=
+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:
-    lru-cache "^4.0.1"
-    shebang-command "^1.2.0"
-    which "^1.2.9"
-
-cross-spawn@^6.0.0, cross-spawn@^6.0.5:
-  version "6.0.5"
-  resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
-  integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==
-  dependencies:
-    nice-try "^1.0.4"
-    path-key "^2.0.1"
-    semver "^5.5.0"
-    shebang-command "^1.2.0"
-    which "^1.2.9"
+    path-key "^3.1.0"
+    shebang-command "^2.0.0"
+    which "^2.0.1"
 
 debug@2.6.9:
   version "2.6.9"
@@ -336,29 +389,22 @@
   dependencies:
     ms "2.0.0"
 
-debug@3.2.6:
-  version "3.2.6"
-  resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
-  integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==
+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.1"
+    ms "2.1.2"
 
-debug@^4.0.1:
-  version "4.1.1"
-  resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791"
-  integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==
-  dependencies:
-    ms "^2.1.1"
+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==
 
-decamelize@^1.2.0:
-  version "1.2.0"
-  resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
-  integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=
-
-deep-eql@^3.0.1:
-  version "3.0.1"
-  resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df"
-  integrity sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==
+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"
 
@@ -367,22 +413,15 @@
   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.3"
-  resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
-  integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=
+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==
 
-define-properties@^1.1.2, define-properties@^1.1.3:
-  version "1.1.3"
-  resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1"
-  integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==
-  dependencies:
-    object-keys "^1.0.12"
-
-diff@3.5.0:
-  version "3.5.0"
-  resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12"
-  integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==
+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"
@@ -391,319 +430,254 @@
   dependencies:
     esutils "^2.0.2"
 
-emoji-regex@^7.0.1:
-  version "7.0.3"
-  resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156"
-  integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==
+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==
 
-end-of-stream@^1.1.0:
-  version "1.4.1"
-  resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43"
-  integrity sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==
+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:
-    once "^1.4.0"
+    esrecurse "^4.3.0"
+    estraverse "^5.2.0"
 
-es-abstract@^1.5.1:
-  version "1.14.2"
-  resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.14.2.tgz#7ce108fad83068c8783c3cdf62e504e084d8c497"
-  integrity sha512-DgoQmbpFNOofkjJtKwr87Ma5EW4Dc8fWhD0R+ndq7Oc456ivUfGOOP6oAZTTKl5/CcNMP+EN+e3/iUzgE0veZg==
+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:
-    es-to-primitive "^1.2.0"
-    function-bind "^1.1.1"
-    has "^1.0.3"
-    has-symbols "^1.0.0"
-    is-callable "^1.1.4"
-    is-regex "^1.0.4"
-    object-inspect "^1.6.0"
-    object-keys "^1.1.1"
-    string.prototype.trimleft "^2.0.0"
-    string.prototype.trimright "^2.0.0"
-
-es-to-primitive@^1.2.0:
-  version "1.2.0"
-  resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.0.tgz#edf72478033456e8dda8ef09e00ad9650707f377"
-  integrity sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==
-  dependencies:
-    is-callable "^1.1.4"
-    is-date-object "^1.0.1"
-    is-symbol "^1.0.2"
-
-escape-string-regexp@1.0.5, escape-string-regexp@^1.0.5:
-  version "1.0.5"
-  resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
-  integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
-
-eslint-scope@^5.0.0:
-  version "5.0.0"
-  resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.0.0.tgz#e87c8887c73e8d1ec84f1ca591645c358bfc8fb9"
-  integrity sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==
-  dependencies:
-    esrecurse "^4.1.0"
-    estraverse "^4.1.1"
-
-eslint-utils@^1.4.2:
-  version "1.4.2"
-  resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.2.tgz#166a5180ef6ab7eb462f162fd0e6f2463d7309ab"
-  integrity sha512-eAZS2sEUMlIeCjBeubdj45dmBHQwPHWyBcT1VSYB7o9x9WRRqKxyUoiXlRjyAwzN7YEzHJlYg0NmzDRWx6GP4Q==
-  dependencies:
-    eslint-visitor-keys "^1.0.0"
-
-eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0:
-  version "1.1.0"
-  resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz#e2a82cea84ff246ad6fb57f9bde5b46621459ec2"
-  integrity sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==
-
-eslint@^6.3.0:
-  version "6.4.0"
-  resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.4.0.tgz#5aa9227c3fbe921982b2eda94ba0d7fae858611a"
-  integrity sha512-WTVEzK3lSFoXUovDHEbkJqCVPEPwbhCq4trDktNI6ygs7aO41d4cDT0JFAT5MivzZeVLWlg7vHL+bgrQv/t3vA==
-  dependencies:
-    "@babel/code-frame" "^7.0.0"
+    "@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 "^2.1.0"
-    cross-spawn "^6.0.5"
-    debug "^4.0.1"
+    chalk "^4.0.0"
+    cross-spawn "^7.0.2"
+    debug "^4.3.2"
     doctrine "^3.0.0"
-    eslint-scope "^5.0.0"
-    eslint-utils "^1.4.2"
-    eslint-visitor-keys "^1.1.0"
-    espree "^6.1.1"
-    esquery "^1.0.1"
+    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"
-    file-entry-cache "^5.0.1"
-    functional-red-black-tree "^1.0.1"
-    glob-parent "^5.0.0"
-    globals "^11.7.0"
-    ignore "^4.0.6"
+    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"
-    inquirer "^6.4.1"
     is-glob "^4.0.0"
-    js-yaml "^3.13.1"
+    is-path-inside "^3.0.3"
+    js-yaml "^4.1.0"
     json-stable-stringify-without-jsonify "^1.0.1"
-    levn "^0.3.0"
-    lodash "^4.17.14"
-    minimatch "^3.0.4"
-    mkdirp "^0.5.1"
+    levn "^0.4.1"
+    lodash.merge "^4.6.2"
+    minimatch "^3.1.2"
     natural-compare "^1.4.0"
-    optionator "^0.8.2"
-    progress "^2.0.0"
-    regexpp "^2.0.1"
-    semver "^6.1.2"
-    strip-ansi "^5.2.0"
-    strip-json-comments "^3.0.1"
-    table "^5.2.3"
+    optionator "^0.9.1"
+    strip-ansi "^6.0.1"
+    strip-json-comments "^3.1.0"
     text-table "^0.2.0"
-    v8-compile-cache "^2.0.3"
 
 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@^6.1.1:
-  version "6.1.1"
-  resolved "https://registry.yarnpkg.com/espree/-/espree-6.1.1.tgz#7f80e5f7257fc47db450022d723e356daeb1e5de"
-  integrity sha512-EYbr8XZUhWbYCqQRW0duU5LxzL5bETN6AjKBGy1302qqzPaCH10QbRg3Wvco79Z8x9WbiE8HYB4e75xl6qUYvQ==
+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 "^7.0.0"
-    acorn-jsx "^5.0.2"
-    eslint-visitor-keys "^1.1.0"
+    acorn "^8.8.0"
+    acorn-jsx "^5.3.2"
+    eslint-visitor-keys "^3.4.1"
 
-esprima@^4.0.0:
-  version "4.0.1"
-  resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
-  integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
-
-esquery@^1.0.1:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.1.tgz#406c51658b1f5991a5f9b62b1dc25b00e3e5c708"
-  integrity sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==
+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 "^4.0.0"
+    estraverse "^5.1.0"
 
-esrecurse@^4.1.0:
-  version "4.2.1"
-  resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf"
-  integrity sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==
-  dependencies:
-    estraverse "^4.1.0"
-
-estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1:
+esrecurse@^4.3.0:
   version "4.3.0"
-  resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d"
-  integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==
+  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@^0.7.0:
-  version "0.7.0"
-  resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777"
-  integrity sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=
+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 "^5.0.1"
-    get-stream "^3.0.0"
-    is-stream "^1.1.0"
-    npm-run-path "^2.0.0"
-    p-finally "^1.0.0"
-    signal-exit "^3.0.0"
-    strip-eof "^1.0.0"
+    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"
 
-execa@^0.8.0:
-  version "0.8.0"
-  resolved "https://registry.yarnpkg.com/execa/-/execa-0.8.0.tgz#d8d76bbc1b55217ed190fd6dd49d3c774ecfc8da"
-  integrity sha1-2NdrvBtVIX7RkP1t1J08d07PyNo=
-  dependencies:
-    cross-spawn "^5.0.1"
-    get-stream "^3.0.0"
-    is-stream "^1.1.0"
-    npm-run-path "^2.0.0"
-    p-finally "^1.0.0"
-    signal-exit "^3.0.0"
-    strip-eof "^1.0.0"
-
-execa@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8"
-  integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==
-  dependencies:
-    cross-spawn "^6.0.0"
-    get-stream "^4.0.0"
-    is-stream "^1.1.0"
-    npm-run-path "^2.0.0"
-    p-finally "^1.0.0"
-    signal-exit "^3.0.0"
-    strip-eof "^1.0.0"
-
-external-editor@^3.0.3:
-  version "3.1.0"
-  resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495"
-  integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==
-  dependencies:
-    chardet "^0.7.0"
-    iconv-lite "^0.4.24"
-    tmp "^0.0.33"
-
-fast-deep-equal@^2.0.1:
-  version "2.0.1"
-  resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49"
-  integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=
+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.0.0"
-  resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2"
-  integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I=
+  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.4:
+fast-levenshtein@^2.0.6:
   version "2.0.6"
   resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
-  integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=
+  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 sha1-9K8+qfNNiicc9YrSs3WfQx8LMY0=
+  integrity sha512-5jOCVXADYNuRkKFzNJ0dCCewsZiYo0dz8QNYljkOpFC6r2U4OBmKtvm/Tsuh4w1YYdDqDb31a8TVhBJ2OJKdqQ==
   dependencies:
     punycode "^1.3.2"
 
-figures@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962"
-  integrity sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=
+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:
-    escape-string-regexp "^1.0.5"
+    reusify "^1.0.4"
 
-file-entry-cache@^5.0.1:
-  version "5.0.1"
-  resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c"
-  integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==
+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 "^2.0.1"
+    flat-cache "^3.0.4"
 
-find-up@3.0.0, find-up@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73"
-  integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==
+fill-range@^7.0.1:
+  version "7.0.1"
+  resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
+  integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
   dependencies:
-    locate-path "^3.0.0"
+    to-regex-range "^5.0.1"
 
-flat-cache@^2.0.1:
-  version "2.0.1"
-  resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0"
-  integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==
+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:
-    flatted "^2.0.0"
-    rimraf "2.6.3"
-    write "1.0.3"
+    locate-path "^6.0.0"
+    path-exists "^4.0.0"
 
-flat@^4.1.0:
-  version "4.1.0"
-  resolved "https://registry.yarnpkg.com/flat/-/flat-4.1.0.tgz#090bec8b05e39cba309747f1d588f04dbaf98db2"
-  integrity sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==
+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:
-    is-buffer "~2.0.3"
+    flatted "^3.1.0"
+    rimraf "^3.0.2"
 
-flatted@^2.0.0:
-  version "2.0.1"
-  resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.1.tgz#69e57caa8f0eacbc281d2e2cb458d46fdb449e08"
-  integrity sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==
+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 sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
+  integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
 
-function-bind@^1.1.1:
-  version "1.1.1"
-  resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
-  integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
+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==
 
-functional-red-black-tree@^1.0.1:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
-  integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=
-
-get-caller-file@^1.0.1:
-  version "1.0.3"
-  resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a"
-  integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==
-
-get-caller-file@^2.0.1:
+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.0"
-  resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41"
-  integrity sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=
+  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@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14"
-  integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=
+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==
 
-get-stream@^4.0.0:
-  version "4.1.0"
-  resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5"
-  integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==
+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:
-    pump "^3.0.0"
+    is-glob "^4.0.3"
 
-glob-parent@^5.0.0:
+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.1.3:
-  version "7.1.3"
-  resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1"
-  integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==
+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"
@@ -713,65 +687,53 @@
     path-is-absolute "^1.0.0"
 
 glob@^7.1.3:
-  version "7.1.4"
-  resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255"
-  integrity sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==
+  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.0.4"
+    minimatch "^3.1.1"
     once "^1.3.0"
     path-is-absolute "^1.0.0"
 
-globals@^11.7.0:
-  version "11.12.0"
-  resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
-  integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
-
-growl@1.10.5:
-  version "1.10.5"
-  resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e"
-  integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==
-
-has-flag@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
-  integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
-
-has-symbols@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.0.tgz#ba1a8f1af2a0fc39650f5c850367704122063b44"
-  integrity sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=
-
-has@^1.0.1, has@^1.0.3:
-  version "1.0.3"
-  resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
-  integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
+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:
-    function-bind "^1.1.1"
+    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==
 
-iconv-lite@^0.4.24:
-  version "0.4.24"
-  resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
-  integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
-  dependencies:
-    safer-buffer ">= 2.1.2 < 3"
+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@^4.0.6:
-  version "4.0.6"
-  resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc"
-  integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==
+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:
-  version "3.1.0"
-  resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.1.0.tgz#6d33fa1dcef6df930fae003446f33415af905118"
-  integrity sha512-PpuksHKGt8rXfWEr9m9EHIpgyyaltBy8+eF6GJM0QCAxMgxCfucMF3mjecK2QsJr0amJW7gTqh5/wht0z2UhEQ==
+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"
@@ -779,12 +741,12 @@
 imurmurhash@^0.1.4:
   version "0.1.4"
   resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
-  integrity sha1-khi5srkoojixPcT7a21XbyMUU+o=
+  integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==
 
 inflight@^1.0.4:
   version "1.0.6"
   resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
-  integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=
+  integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==
   dependencies:
     once "^1.3.0"
     wrappy "1"
@@ -795,193 +757,147 @@
   integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
 
 ini@~1.3.0:
-  version "1.3.7"
-  resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.7.tgz#a09363e1911972ea16d7a8851005d84cf09a9a84"
-  integrity sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ==
+  version "1.3.8"
+  resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c"
+  integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==
 
-inquirer@^6.4.1:
-  version "6.5.2"
-  resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.5.2.tgz#ad50942375d036d327ff528c08bd5fab089928ca"
-  integrity sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ==
+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:
-    ansi-escapes "^3.2.0"
-    chalk "^2.4.2"
-    cli-cursor "^2.1.0"
-    cli-width "^2.0.0"
-    external-editor "^3.0.3"
-    figures "^2.0.0"
-    lodash "^4.17.12"
-    mute-stream "0.0.7"
-    run-async "^2.2.0"
-    rxjs "^6.4.0"
-    string-width "^2.1.0"
-    strip-ansi "^5.1.0"
-    through "^2.3.6"
+    binary-extensions "^2.0.0"
 
-invert-kv@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02"
-  integrity sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==
-
-is-buffer@~2.0.3:
-  version "2.0.3"
-  resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.3.tgz#4ecf3fcf749cbd1e472689e109ac66261a25e725"
-  integrity sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==
-
-is-callable@^1.1.4:
-  version "1.1.4"
-  resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75"
-  integrity sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==
-
-is-date-object@^1.0.1:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16"
-  integrity sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=
+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 sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=
+  integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
 
-is-fullwidth-code-point@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb"
-  integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs=
-  dependencies:
-    number-is-nan "^1.0.0"
+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-fullwidth-code-point@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
-  integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=
-
-is-glob@^4.0.0, is-glob@^4.0.1:
-  version "4.0.1"
-  resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc"
-  integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==
+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-promise@^2.1.0:
+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-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa"
-  integrity sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=
+  resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287"
+  integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==
 
-is-regex@^1.0.4:
-  version "1.0.4"
-  resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491"
-  integrity sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=
+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:
-    has "^1.0.1"
-
-is-stream@^1.1.0:
-  version "1.1.0"
-  resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
-  integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ=
-
-is-symbol@^1.0.2:
-  version "1.0.2"
-  resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.2.tgz#a055f6ae57192caee329e7a860118b497a950f38"
-  integrity sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==
-  dependencies:
-    has-symbols "^1.0.0"
+    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 sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
+  integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
 
-js-tokens@^4.0.0:
-  version "4.0.0"
-  resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
-  integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
-
-js-yaml@3.13.1, js-yaml@^3.13.1:
-  version "3.13.1"
-  resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847"
-  integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==
+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 "^1.0.7"
-    esprima "^4.0.0"
+    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 sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=
+  integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==
 
-lcid@^2.0.0:
+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/lcid/-/lcid-2.0.0.tgz#6ef5d2df60e52f82eb228a4c373e8d1f397253cf"
-  integrity sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==
-  dependencies:
-    invert-kv "^2.0.0"
+  resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
+  integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==
 
-levn@^0.3.0, levn@~0.3.0:
-  version "0.3.0"
-  resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee"
-  integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=
-  dependencies:
-    prelude-ls "~1.1.2"
-    type-check "~0.3.2"
-
-locate-path@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e"
-  integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==
-  dependencies:
-    p-locate "^3.0.0"
-    path-exists "^3.0.0"
-
-lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14:
-  version "4.17.21"
-  resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
-  integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
-
-log-symbols@2.2.0:
-  version "2.2.0"
-  resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a"
-  integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==
-  dependencies:
-    chalk "^2.0.1"
-
-lru-cache@^4.0.1:
-  version "4.1.5"
-  resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd"
-  integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==
-  dependencies:
-    pseudomap "^1.0.2"
-    yallist "^2.1.2"
-
-map-age-cleaner@^0.1.1:
-  version "0.1.3"
-  resolved "https://registry.yarnpkg.com/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz#7d583a7306434c055fe474b0f45078e6e1b4b92a"
-  integrity sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==
-  dependencies:
-    p-defer "^1.0.0"
-
-mem@^4.0.0:
-  version "4.3.0"
-  resolved "https://registry.yarnpkg.com/mem/-/mem-4.3.0.tgz#461af497bc4ae09608cdb2e60eefb69bff744178"
-  integrity sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==
-  dependencies:
-    map-age-cleaner "^0.1.1"
-    mimic-fn "^2.0.0"
-    p-is-promise "^2.0.0"
-
-mime-db@1.40.0:
-  version "1.40.0"
-  resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.40.0.tgz#a65057e998db090f732a68f6c276d387d4126c32"
-  integrity sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==
-
-"mime-db@>= 1.40.0 < 2":
-  version "1.41.0"
-  resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.41.0.tgz#9110408e1f6aa1b34aef51f2c9df3caddf46b6a0"
-  integrity sha512-B5gxBI+2K431XW8C2rcc/lhppbuji67nf9v39eH8pkWoZDxnAL0PxdpH32KYRScniF8qDHBDlI+ipgg5WrCUYw==
+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"
@@ -995,237 +911,150 @@
   dependencies:
     mime-db "~1.33.0"
 
-mime-types@~2.1.24:
-  version "2.1.24"
-  resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.24.tgz#b6f8d0b3e951efb77dedeca194cff6d16f676f81"
-  integrity sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==
+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.40.0"
+    mime-db "1.52.0"
 
-mimic-fn@^1.0.0:
-  version "1.2.0"
-  resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022"
-  integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==
-
-mimic-fn@^2.0.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.0.4, minimatch@^3.0.4:
-  version "3.0.4"
-  resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
-  integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
+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"
 
-minimist@0.0.8:
-  version "0.0.8"
-  resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
-  integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=
+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.0"
-  resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
-  integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=
+  version "1.2.8"
+  resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
+  integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
 
-mkdirp@0.5.1, mkdirp@^0.5.1:
-  version "0.5.1"
-  resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
-  integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=
+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:
-    minimist "0.0.8"
-
-mocha@^6.2.0:
-  version "6.2.0"
-  resolved "https://registry.yarnpkg.com/mocha/-/mocha-6.2.0.tgz#f896b642843445d1bb8bca60eabd9206b8916e56"
-  integrity sha512-qwfFgY+7EKAAUAdv7VYMZQknI7YJSGesxHyhn6qD52DV8UcSZs5XwCifcZGMVIE4a5fbmhvbotxC0DLQ0oKohQ==
-  dependencies:
-    ansi-colors "3.2.3"
+    ansi-colors "4.1.1"
     browser-stdout "1.3.1"
-    debug "3.2.6"
-    diff "3.5.0"
-    escape-string-regexp "1.0.5"
-    find-up "3.0.0"
-    glob "7.1.3"
-    growl "1.10.5"
+    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 "3.13.1"
-    log-symbols "2.2.0"
-    minimatch "3.0.4"
-    mkdirp "0.5.1"
-    ms "2.1.1"
-    node-environment-flags "1.0.5"
-    object.assign "4.1.0"
-    strip-json-comments "2.0.1"
-    supports-color "6.0.0"
-    which "1.3.1"
-    wide-align "1.1.3"
-    yargs "13.2.2"
-    yargs-parser "13.0.0"
-    yargs-unparser "1.5.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 sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
+  integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==
 
-ms@2.1.1:
-  version "2.1.1"
-  resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
-  integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==
-
-ms@^2.1.1:
+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==
 
-mute-stream@0.0.7:
-  version "0.0.7"
-  resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab"
-  integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=
+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 sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=
+  integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
 
-negotiator@0.6.2:
-  version "0.6.2"
-  resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb"
-  integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==
+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==
 
-nice-try@^1.0.4:
-  version "1.0.5"
-  resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
-  integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
+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==
 
-node-environment-flags@1.0.5:
-  version "1.0.5"
-  resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.5.tgz#fa930275f5bf5dae188d6192b24b4c8bbac3d76a"
-  integrity sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ==
+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:
-    object.getownpropertydescriptors "^2.0.3"
-    semver "^5.7.0"
+    path-key "^3.0.0"
 
-npm-run-path@^2.0.0:
-  version "2.0.2"
-  resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f"
-  integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=
-  dependencies:
-    path-key "^2.0.0"
-
-number-is-nan@^1.0.0:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
-  integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=
-
-object-inspect@^1.6.0:
-  version "1.6.0"
-  resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.6.0.tgz#c70b6cbf72f274aab4c34c0c82f5167bf82cf15b"
-  integrity sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ==
-
-object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1:
-  version "1.1.1"
-  resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
-  integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==
-
-object.assign@4.1.0:
-  version "4.1.0"
-  resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da"
-  integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==
-  dependencies:
-    define-properties "^1.1.2"
-    function-bind "^1.1.1"
-    has-symbols "^1.0.0"
-    object-keys "^1.0.11"
-
-object.getownpropertydescriptors@^2.0.3:
-  version "2.0.3"
-  resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz#8758c846f5b407adab0f236e0986f14b051caa16"
-  integrity sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=
-  dependencies:
-    define-properties "^1.1.2"
-    es-abstract "^1.5.1"
-
-on-headers@~1.0.1:
+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, once@^1.3.1, once@^1.4.0:
+once@^1.3.0:
   version "1.4.0"
   resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
-  integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
+  integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
   dependencies:
     wrappy "1"
 
-onetime@^2.0.0:
-  version "2.0.1"
-  resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4"
-  integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=
+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 "^1.0.0"
+    mimic-fn "^2.1.0"
 
-optionator@^0.8.2:
-  version "0.8.2"
-  resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64"
-  integrity sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=
+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.4"
-    levn "~0.3.0"
-    prelude-ls "~1.1.2"
-    type-check "~0.3.2"
-    wordwrap "~1.0.0"
+    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"
 
-os-locale@^3.0.0, os-locale@^3.1.0:
+p-limit@^3.0.2:
   version "3.1.0"
-  resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.1.0.tgz#a802a6ee17f24c10483ab9935719cef4ed16bf1a"
-  integrity sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==
+  resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b"
+  integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==
   dependencies:
-    execa "^1.0.0"
-    lcid "^2.0.0"
-    mem "^4.0.0"
+    yocto-queue "^0.1.0"
 
-os-tmpdir@~1.0.2:
-  version "1.0.2"
-  resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
-  integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=
-
-p-defer@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c"
-  integrity sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=
-
-p-finally@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae"
-  integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=
-
-p-is-promise@^2.0.0:
-  version "2.1.0"
-  resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-2.1.0.tgz#918cebaea248a62cf7ffab8e3bca8c5f882fc42e"
-  integrity sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==
-
-p-limit@^2.0.0:
-  version "2.2.1"
-  resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.1.tgz#aa07a788cc3151c939b5131f63570f0dd2009537"
-  integrity sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==
+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-try "^2.0.0"
-
-p-locate@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4"
-  integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==
-  dependencies:
-    p-limit "^2.0.0"
-
-p-try@^2.0.0:
-  version "2.2.0"
-  resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
-  integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
+    p-limit "^3.0.2"
 
 parent-module@^1.0.0:
   version "1.0.1"
@@ -1234,73 +1063,72 @@
   dependencies:
     callsites "^3.0.0"
 
-path-exists@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515"
-  integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=
+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 sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
+  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 sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=
+  integrity sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==
 
-path-key@^2.0.0, path-key@^2.0.1:
-  version "2.0.1"
-  resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
-  integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=
+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@2.2.1:
   version "2.2.1"
   resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-2.2.1.tgz#90b617025a16381a879bc82a38d4e8bdeb2bcf45"
   integrity sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ==
 
-pathval@^1.1.0:
+pathval@^1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d"
   integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==
 
-prelude-ls@~1.1.2:
-  version "1.1.2"
-  resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
-  integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=
+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==
 
-progress@^2.0.0:
-  version "2.0.3"
-  resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
-  integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
-
-pseudomap@^1.0.2:
-  version "1.0.2"
-  resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
-  integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM=
-
-pump@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64"
-  integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==
-  dependencies:
-    end-of-stream "^1.1.0"
-    once "^1.3.1"
+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 sha1-wNWmOycYgArY4esPpSachN1BhF4=
+  integrity sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==
 
 punycode@^2.1.0:
-  version "2.1.1"
-  resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
-  integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
+  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 sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=
+  integrity sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A==
 
 rc@^1.0.1, rc@^1.1.6:
   version "1.2.8"
@@ -1312,10 +1140,12 @@
     minimist "^1.2.0"
     strip-json-comments "~2.0.1"
 
-regexpp@^2.0.1:
-  version "2.0.1"
-  resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f"
-  integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==
+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"
@@ -1328,451 +1158,317 @@
 registry-url@3.1.0:
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-3.1.0.tgz#3d4ef870f73dde1d77f0cf9a381432444e174942"
-  integrity sha1-PU74cPc93h138M+aOBQyRE4XSUI=
+  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 sha1-jGStX9MNqxyXbiNE/+f3kqam30I=
+  integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==
 
-require-main-filename@^1.0.1:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1"
-  integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=
-
-require-main-filename@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b"
-  integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==
+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==
 
-restore-cursor@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf"
-  integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368=
-  dependencies:
-    onetime "^2.0.0"
-    signal-exit "^3.0.2"
+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@2.6.3:
-  version "2.6.3"
-  resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab"
-  integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==
+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@^1.21.4:
-  version "1.21.4"
-  resolved "https://registry.yarnpkg.com/rollup/-/rollup-1.21.4.tgz#00a41a30f90095db890301b226cbe2918e4cf54d"
-  integrity sha512-Pl512XVCmVzgcBz5h/3Li4oTaoDcmpuFZ+kdhS/wLreALz//WuDAMfomD3QEYl84NkDu6Z6wV9twlcREb4qQsw==
-  dependencies:
-    "@types/estree" "0.0.39"
-    "@types/node" "^12.7.5"
-    acorn "^7.0.0"
+rollup@^3.23.0:
+  version "3.23.0"
+  resolved "https://registry.yarnpkg.com/rollup/-/rollup-3.23.0.tgz#b8d6146dac4bf058ee817f92820988e9b358b564"
+  integrity sha512-h31UlwEi7FHihLe1zbk+3Q7z1k/84rb9BSwmBSr/XjOCEaBJ2YyedQDuM0t/kfOS0IxM+vk1/zI9XxYj9V+NJQ==
+  optionalDependencies:
+    fsevents "~2.3.2"
 
-run-async@^2.2.0:
-  version "2.3.0"
-  resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0"
-  integrity sha1-A3GrSuC91yDUFm19/aZP96RFpsA=
+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:
-    is-promise "^2.1.0"
-
-rxjs@^6.4.0:
-  version "6.5.3"
-  resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.3.tgz#510e26317f4db91a7eb1de77d9dd9ba0a4899a3a"
-  integrity sha512-wuYsAYYFdWTAnAaPoKGNhfpWwKZbJW+HgAJ+mImp+Epl7BG8oNWBCTyRM8gba9k4lk8BgWdoYm21Mo/RYhhbgA==
-  dependencies:
-    tslib "^1.9.0"
+    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:
-  version "5.2.0"
-  resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519"
-  integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==
+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==
 
-"safer-buffer@>= 2.1.2 < 3":
-  version "2.1.2"
-  resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
-  integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
+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"
 
-semver@^5.5.0, semver@^5.7.0:
-  version "5.7.1"
-  resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
-  integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
-
-semver@^6.1.2:
-  version "6.3.0"
-  resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
-  integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
-
-serve-handler@6.1.0:
-  version "6.1.0"
-  resolved "https://registry.yarnpkg.com/serve-handler/-/serve-handler-6.1.0.tgz#f1606dc6ff8f9029a1ee042c11dfe7903a5cb92e"
-  integrity sha512-63N075Tn3PsFYcu0NVV7tb367UbiW3gnC+/50ohL4oqOhAG6bmbaWqiRcXQgbzqc0ALBjSAzg7VTfa0Qw4E3hA==
+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.0.4"
+    minimatch "3.1.2"
     path-is-inside "1.0.2"
     path-to-regexp "2.2.1"
     range-parser "1.2.0"
 
-serve@^11.1.0:
-  version "11.1.0"
-  resolved "https://registry.yarnpkg.com/serve/-/serve-11.1.0.tgz#1bfe2f4a08d0130cbf44711cdb7996cb742172e0"
-  integrity sha512-+4wpDtOSS+4ZLyDWMxThutA3iOTawX2+yDovOI8cjOUOmemyvNlHyFAsezBlSgbZKTYChI3tzA1Mh0z6XZ62qA==
+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.6.0"
-    ajv "6.5.3"
-    arg "2.0.0"
-    boxen "1.3.0"
-    chalk "2.4.1"
-    clipboardy "1.2.3"
-    compression "1.7.3"
-    serve-handler "6.1.0"
-    update-check "1.5.2"
+    "@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"
 
-set-blocking@^2.0.0:
+shebang-command@^2.0.0:
   version "2.0.0"
-  resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
-  integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
-
-shebang-command@^1.2.0:
-  version "1.2.0"
-  resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"
-  integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=
+  resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
+  integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==
   dependencies:
-    shebang-regex "^1.0.0"
+    shebang-regex "^3.0.0"
 
-shebang-regex@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
-  integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=
+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.0, signal-exit@^3.0.2:
-  version "3.0.2"
-  resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
-  integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=
+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==
 
-slice-ansi@^2.1.0:
-  version "2.1.0"
-  resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636"
-  integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==
+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:
-    ansi-styles "^3.2.0"
-    astral-regex "^1.0.0"
-    is-fullwidth-code-point "^2.0.0"
+    emoji-regex "^8.0.0"
+    is-fullwidth-code-point "^3.0.0"
+    strip-ansi "^6.0.1"
 
-sprintf-js@~1.0.2:
-  version "1.0.3"
-  resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
-  integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
-
-string-width@^1.0.1:
-  version "1.0.2"
-  resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
-  integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=
+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:
-    code-point-at "^1.0.0"
-    is-fullwidth-code-point "^1.0.0"
-    strip-ansi "^3.0.0"
+    eastasianwidth "^0.2.0"
+    emoji-regex "^9.2.2"
+    strip-ansi "^7.0.1"
 
-"string-width@^1.0.2 || 2", string-width@^2.0.0, string-width@^2.1.0, string-width@^2.1.1:
-  version "2.1.1"
-  resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e"
-  integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==
+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:
-    is-fullwidth-code-point "^2.0.0"
-    strip-ansi "^4.0.0"
+    ansi-regex "^5.0.1"
 
-string-width@^3.0.0:
-  version "3.1.0"
-  resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961"
-  integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==
+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:
-    emoji-regex "^7.0.1"
-    is-fullwidth-code-point "^2.0.0"
-    strip-ansi "^5.1.0"
+    ansi-regex "^6.0.1"
 
-string.prototype.trimleft@^2.0.0:
-  version "2.1.0"
-  resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz#6cc47f0d7eb8d62b0f3701611715a3954591d634"
-  integrity sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw==
-  dependencies:
-    define-properties "^1.1.3"
-    function-bind "^1.1.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==
 
-string.prototype.trimright@^2.0.0:
-  version "2.1.0"
-  resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz#669d164be9df9b6f7559fa8e89945b168a5a6c58"
-  integrity sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg==
-  dependencies:
-    define-properties "^1.1.3"
-    function-bind "^1.1.1"
+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-ansi@^3.0.0, strip-ansi@^3.0.1:
-  version "3.0.1"
-  resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf"
-  integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=
-  dependencies:
-    ansi-regex "^2.0.0"
-
-strip-ansi@^4.0.0:
-  version "4.0.0"
-  resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f"
-  integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8=
-  dependencies:
-    ansi-regex "^3.0.0"
-
-strip-ansi@^5.1.0, strip-ansi@^5.2.0:
-  version "5.2.0"
-  resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae"
-  integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==
-  dependencies:
-    ansi-regex "^4.1.0"
-
-strip-eof@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf"
-  integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=
-
-strip-json-comments@2.0.1, strip-json-comments@~2.0.1:
+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 sha1-PFMZQukIwml8DsNEhYwobHygpgo=
+  integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==
 
-strip-json-comments@^3.0.1:
-  version "3.0.1"
-  resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.0.1.tgz#85713975a91fb87bf1b305cca77395e40d2a64a7"
-  integrity sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==
-
-supports-color@6.0.0:
-  version "6.0.0"
-  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.0.0.tgz#76cfe742cf1f41bb9b1c29ad03068c05b4c0e40a"
-  integrity sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==
+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 "^3.0.0"
+    has-flag "^4.0.0"
 
-supports-color@^5.3.0:
-  version "5.5.0"
-  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
-  integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
+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 "^3.0.0"
-
-table@^5.2.3:
-  version "5.4.6"
-  resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e"
-  integrity sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==
-  dependencies:
-    ajv "^6.10.2"
-    lodash "^4.17.14"
-    slice-ansi "^2.1.0"
-    string-width "^3.0.0"
-
-term-size@^1.2.0:
-  version "1.2.0"
-  resolved "https://registry.yarnpkg.com/term-size/-/term-size-1.2.0.tgz#458b83887f288fc56d6fffbfad262e26638efa69"
-  integrity sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=
-  dependencies:
-    execa "^0.7.0"
+    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 sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=
+  integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==
 
-through@^2.3.6:
-  version "2.3.8"
-  resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
-  integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=
-
-tmp@^0.0.33:
-  version "0.0.33"
-  resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
-  integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==
+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:
-    os-tmpdir "~1.0.2"
+    is-number "^7.0.0"
 
-tslib@^1.9.0:
-  version "1.10.0"
-  resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a"
-  integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==
-
-type-check@~0.3.2:
-  version "0.3.2"
-  resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72"
-  integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=
+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.1.2"
+    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==
 
-update-check@1.5.2:
-  version "1.5.2"
-  resolved "https://registry.yarnpkg.com/update-check/-/update-check-1.5.2.tgz#2fe09f725c543440b3d7dabe8971f2d5caaedc28"
-  integrity sha512-1TrmYLuLj/5ZovwUS7fFd1jMH3NnFDN1y1A8dboedIDt7zs/zJMo6TwwlhYKkSeEwzleeiSBV5/3c9ufAQWDaQ==
+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.2.2"
-  resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0"
-  integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==
+  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"
 
-v8-compile-cache@^2.0.3:
-  version "2.1.0"
-  resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz#e14de37b31a6d194f5690d67efc4e7f6fc6ab30e"
-  integrity sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g==
-
 vary@~1.1.2:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
-  integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=
+  integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==
 
-which-module@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
-  integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=
-
-which@1.3.1, which@^1.2.9:
-  version "1.3.1"
-  resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
-  integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
+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"
 
-wide-align@1.1.3:
-  version "1.1.3"
-  resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457"
-  integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==
+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 "^1.0.2 || 2"
+    string-width "^5.0.1"
 
-widest-line@^2.0.0:
-  version "2.0.1"
-  resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-2.0.1.tgz#7438764730ec7ef4381ce4df82fb98a53142a3fc"
-  integrity sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==
+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:
-    string-width "^2.1.1"
+    ansi-styles "^4.0.0"
+    string-width "^4.1.0"
+    strip-ansi "^6.0.0"
 
-wordwrap@~1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"
-  integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=
-
-wrap-ansi@^2.0.0:
-  version "2.1.0"
-  resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85"
-  integrity sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=
+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:
-    string-width "^1.0.1"
-    strip-ansi "^3.0.1"
+    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 sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
+  integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
 
-write@1.0.3:
-  version "1.0.3"
-  resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3"
-  integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==
+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:
-    mkdirp "^0.5.1"
+    camelcase "^6.0.0"
+    decamelize "^4.0.0"
+    flat "^5.0.2"
+    is-plain-obj "^2.1.0"
 
-"y18n@^3.2.1 || ^4.0.0", y18n@^4.0.0:
-  version "4.0.0"
-  resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b"
-  integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==
-
-yallist@^2.1.2:
-  version "2.1.2"
-  resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"
-  integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=
-
-yargs-parser@13.0.0:
-  version "13.0.0"
-  resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.0.0.tgz#3fc44f3e76a8bdb1cc3602e860108602e5ccde8b"
-  integrity sha512-w2LXjoL8oRdRQN+hOyppuXs+V/fVAYtpcrRxZuF7Kt/Oc+Jr2uAcVntaUTNT6w5ihoWfFDpNY8CPx1QskxZ/pw==
+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:
-    camelcase "^5.0.0"
-    decamelize "^1.2.0"
-
-yargs-parser@^11.1.1:
-  version "11.1.1"
-  resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-11.1.1.tgz#879a0865973bca9f6bab5cbdf3b1c67ec7d3bcf4"
-  integrity sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==
-  dependencies:
-    camelcase "^5.0.0"
-    decamelize "^1.2.0"
-
-yargs-parser@^13.0.0:
-  version "13.1.1"
-  resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.1.tgz#d26058532aa06d365fe091f6a1fc06b2f7e5eca0"
-  integrity sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==
-  dependencies:
-    camelcase "^5.0.0"
-    decamelize "^1.2.0"
-
-yargs-unparser@1.5.0:
-  version "1.5.0"
-  resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-1.5.0.tgz#f2bb2a7e83cbc87bb95c8e572828a06c9add6e0d"
-  integrity sha512-HK25qidFTCVuj/D1VfNiEndpLIeJN78aqgR23nL3y4N0U/91cOAzqfHlF8n2BvoNDcZmJKin3ddNSvOxSr8flw==
-  dependencies:
-    flat "^4.1.0"
-    lodash "^4.17.11"
-    yargs "^12.0.5"
-
-yargs@13.2.2:
-  version "13.2.2"
-  resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.2.2.tgz#0c101f580ae95cea7f39d927e7770e3fdc97f993"
-  integrity sha512-WyEoxgyTD3w5XRpAQNYUB9ycVH/PQrToaTXdYXRdOXvEy1l19br+VJsc0vcO8PTGg5ro/l/GY7F/JMEBmI0BxA==
-  dependencies:
-    cliui "^4.0.0"
-    find-up "^3.0.0"
-    get-caller-file "^2.0.1"
-    os-locale "^3.1.0"
+    cliui "^7.0.2"
+    escalade "^3.1.1"
+    get-caller-file "^2.0.5"
     require-directory "^2.1.1"
-    require-main-filename "^2.0.0"
-    set-blocking "^2.0.0"
-    string-width "^3.0.0"
-    which-module "^2.0.0"
-    y18n "^4.0.0"
-    yargs-parser "^13.0.0"
+    string-width "^4.2.0"
+    y18n "^5.0.5"
+    yargs-parser "^20.2.2"
 
-yargs@^12.0.5:
-  version "12.0.5"
-  resolved "https://registry.yarnpkg.com/yargs/-/yargs-12.0.5.tgz#05f5997b609647b64f66b81e3b4b10a368e7ad13"
-  integrity sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==
-  dependencies:
-    cliui "^4.0.0"
-    decamelize "^1.2.0"
-    find-up "^3.0.0"
-    get-caller-file "^1.0.1"
-    os-locale "^3.0.0"
-    require-directory "^2.1.1"
-    require-main-filename "^1.0.1"
-    set-blocking "^2.0.0"
-    string-width "^2.0.0"
-    which-module "^2.0.0"
-    y18n "^3.2.1 || ^4.0.0"
-    yargs-parser "^11.1.1"
+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
new file mode 100644
index 0000000..11b8967
--- /dev/null
+++ b/tools/util/flags.cpp
@@ -0,0 +1,242 @@
+// 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.
+
+#include "flags.h"
+
+#include <cstdlib>
+#include <cstring>
+#include <iostream>
+#include <regex>
+#include <string>
+#include <unordered_set>
+#include <variant>
+#include <vector>
+
+namespace flags {
+
+std::vector<std::string> positional_arguments;
+
+namespace {
+
+using token_t = const char*;
+using token_iterator_t = token_t*;
+
+// Extracts the flag name from a potential token.
+// This function only looks for a '=', to split the flag name from the value for
+// long-form flags. Returns the name of the flag, prefixed with the hyphen(s).
+inline std::string get_flag_name(const std::string& flag, bool is_short_flag) {
+  if (is_short_flag) {
+    return flag;
+  }
+
+  size_t equal_index = flag.find('=');
+  if (equal_index == std::string::npos) {
+    return flag;
+  }
+  return flag.substr(0, equal_index);
+}
+
+// Parse a boolean flag. Returns `true` if the parsing succeeded, `false`
+// otherwise.
+bool parse_bool_flag(Flag<bool>& flag, bool is_short_flag,
+                     const std::string& token) {
+  if (is_short_flag) {
+    flag.value() = true;
+    return true;
+  }
+
+  const std::string raw_flag(token);
+  size_t equal_index = raw_flag.find('=');
+  if (equal_index == std::string::npos) {
+    flag.value() = true;
+    return true;
+  }
+
+  const std::string value = raw_flag.substr(equal_index + 1);
+  if (value == "true") {
+    flag.value() = true;
+    return true;
+  }
+
+  if (value == "false") {
+    flag.value() = false;
+    return true;
+  }
+
+  return false;
+}
+
+// Parse a uint32_t flag value.
+bool parse_flag_value(Flag<uint32_t>& flag, const std::string& value) {
+  std::regex unsigned_pattern("^ *[0-9]+ *$");
+  if (!std::regex_match(value, unsigned_pattern)) {
+    std::cerr << "'" << value << "' is not a unsigned number." << std::endl;
+    return false;
+  }
+
+  errno = 0;
+  char* end_ptr = nullptr;
+  const uint64_t number = strtoull(value.c_str(), &end_ptr, 10);
+  if (end_ptr == nullptr || end_ptr != value.c_str() + value.size() ||
+      errno == EINVAL) {
+    std::cerr << "'" << value << "' is not a unsigned number." << std::endl;
+    return false;
+  }
+
+  if (errno == ERANGE || number > static_cast<size_t>(UINT32_MAX)) {
+    std::cerr << "'" << value << "' cannot be represented as a 32bit unsigned."
+              << std::endl;
+    return false;
+  }
+
+  flag.value() = static_cast<uint32_t>(number);
+  return true;
+}
+
+// "Parse" a string flag value (assigns it, cannot fail).
+bool parse_flag_value(Flag<std::string>& flag, const std::string& value) {
+  flag.value() = value;
+  return true;
+}
+
+// Parse a potential multi-token flag. Moves the iterator to the last flag's
+// token if it's a multi-token flag. Returns `true` if the parsing succeeded.
+// The iterator is moved to the last parsed token.
+template <typename T>
+bool parse_flag(Flag<T>& flag, bool is_short_flag, const char*** iterator) {
+  const std::string raw_flag(**iterator);
+  std::string raw_value;
+  const size_t equal_index = raw_flag.find('=');
+
+  if (is_short_flag || equal_index == std::string::npos) {
+    if ((*iterator)[1] == nullptr) {
+      return false;
+    }
+
+    // This is a bi-token flag. Moving iterator to the last parsed token.
+    raw_value = (*iterator)[1];
+    *iterator += 1;
+  } else {
+    // This is a mono-token flag, no need to move the iterator.
+    raw_value = raw_flag.substr(equal_index + 1);
+  }
+
+  return parse_flag_value(flag, raw_value);
+}
+
+}  // namespace
+
+// This is the function to expand if you want to support a new type.
+bool FlagList::parse_flag_info(FlagInfo& info, token_iterator_t* iterator) {
+  bool success = false;
+
+  std::visit(
+      [&](auto&& item) {
+        using T = std::decay_t<decltype(item.get())>;
+        if constexpr (std::is_same_v<T, Flag<bool>>) {
+          success = parse_bool_flag(item.get(), info.is_short, **iterator);
+        } else if constexpr (std::is_same_v<T, Flag<std::string>>) {
+          success = parse_flag(item.get(), info.is_short, iterator);
+        } else if constexpr (std::is_same_v<T, Flag<uint32_t>>) {
+          success = parse_flag(item.get(), info.is_short, iterator);
+        } else {
+          static_assert(always_false_v<T>, "Unsupported flag type.");
+        }
+      },
+      info.flag);
+
+  return success;
+}
+
+bool FlagList::parse(token_t* argv) {
+  flags::positional_arguments.clear();
+  std::unordered_set<const FlagInfo*> parsed_flags;
+
+  bool ignore_flags = false;
+  for (const char** it = argv + 1; *it != nullptr; it++) {
+    if (ignore_flags) {
+      flags::positional_arguments.emplace_back(*it);
+      continue;
+    }
+
+    // '--' alone is used to mark the end of the flags.
+    if (std::strcmp(*it, "--") == 0) {
+      ignore_flags = true;
+      continue;
+    }
+
+    // '-' alone is not a flag, but often used to say 'stdin'.
+    if (std::strcmp(*it, "-") == 0) {
+      flags::positional_arguments.emplace_back(*it);
+      continue;
+    }
+
+    const std::string raw_flag(*it);
+    if (raw_flag.size() == 0) {
+      continue;
+    }
+
+    if (raw_flag[0] != '-') {
+      flags::positional_arguments.emplace_back(*it);
+      continue;
+    }
+
+    // Only case left: flags (long and shorts).
+    if (raw_flag.size() < 2) {
+      std::cerr << "Unknown flag " << raw_flag << std::endl;
+      return false;
+    }
+    const bool is_short_flag = std::strncmp(*it, "--", 2) != 0;
+    const std::string flag_name = get_flag_name(raw_flag, is_short_flag);
+
+    auto needle = std::find_if(
+        get_flags().begin(), get_flags().end(),
+        [&flag_name](const auto& item) { return item.name == flag_name; });
+    if (needle == get_flags().end()) {
+      std::cerr << "Unknown flag " << flag_name << std::endl;
+      return false;
+    }
+
+    if (parsed_flags.count(&*needle) != 0) {
+      std::cerr << "The flag " << flag_name << " was specified multiple times."
+                << std::endl;
+      return false;
+    }
+    parsed_flags.insert(&*needle);
+
+    if (!parse_flag_info(*needle, &it)) {
+      std::cerr << "Invalid usage for flag " << flag_name << std::endl;
+      return false;
+    }
+  }
+
+  // Check that we parsed all required flags.
+  for (const auto& flag : get_flags()) {
+    if (!flag.required) {
+      continue;
+    }
+
+    if (parsed_flags.count(&flag) == 0) {
+      std::cerr << "Missing required flag " << flag.name << std::endl;
+      return false;
+    }
+  }
+
+  return true;
+}
+
+// Just the public wrapper around the parse function.
+bool Parse(const char** argv) { return FlagList::parse(argv); }
+
+}  // namespace flags
diff --git a/tools/util/flags.h b/tools/util/flags.h
new file mode 100644
index 0000000..20bb369
--- /dev/null
+++ b/tools/util/flags.h
@@ -0,0 +1,262 @@
+// 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.
+
+#ifndef INCLUDE_SPIRV_TOOLS_UTIL_FLAGS_HPP_
+#define INCLUDE_SPIRV_TOOLS_UTIL_FLAGS_HPP_
+
+#include <stdint.h>
+
+#include <functional>
+#include <string>
+#include <variant>
+#include <vector>
+
+// This file provides some utils to define a command-line interface with
+// required and optional flags.
+//  - Flag order is not checked.
+//  - Currently supported flag types: BOOLEAN, STRING
+//  - As with most nix tools, using '--' in the command-line means all following
+//  tokens will be considered positional
+//    arguments.
+//    Example: binary -g -- -g --some-other-flag
+//      - the first `-g` is a flag.
+//      - the second `-g` is not a flag.
+//      - `--some-other-flag` is not a flag.
+//  - Both long-form and short-form flags are supported, but boolean flags don't
+//    support split boolean literals (short and long form).
+//    Example:
+//        -g              : allowed, sets g to true.
+//        --my-flag       : allowed, sets --my-flag to true.
+//        --my-flag=true  : allowed, sets --my-flag to true.
+//        --my-flag true  : NOT allowed.
+//        -g true         : NOT allowed.
+//        --my-flag=TRUE  : NOT allowed.
+//
+//  - This implementation also supports string flags:
+//        -o myfile.spv       : allowed, sets -o to `myfile.spv`.
+//        --output=myfile.spv : allowed, sets --output to `myfile.spv`.
+//        --output myfile.spv : allowd, sets --output to `myfile.spv`.
+//
+//    Note: then second token is NOT checked for hyphens.
+//          --output -file.spv
+//          flag name:  `output`
+//          flag value: `-file.spv`
+//
+//  - This implementation generates flag at compile time. Meaning flag names
+//  must be valid C++ identifiers.
+//    However, flags are usually using hyphens for word separation. Hence
+//    renaming is done behind the scenes. Example:
+//      // Declaring a long-form flag.
+//      FLAG_LONG_bool(my_flag, [...])
+//
+//      ->  in the code: flags::my_flag.value()
+//      -> command-line: --my-flag
+//
+//  - The only additional lexing done is around '='. Otherwise token list is
+//  processed as received in the Parse()
+//    function.
+//    Lexing the '=' sign:
+//      - This is only done when parsing a long-form flag name.
+//      - the first '=' found is considered a marker for long-form, splitting
+//      the token into 2.
+//        Example: --option=value=abc -> [--option, value=abc]
+//
+// In most cases, you want to define some flags, parse them, and query them.
+// Here is a small code sample:
+//
+// ```c
+//  // Defines a '-h' boolean flag for help printing, optional.
+//  FLAG_SHORT_bool(h, /*default=*/ false, "Print the help.", false);
+//  // Defines a '--my-flag' string flag, required.
+//  FLAG_LONG_string(my_flag, /*default=*/ "", "A magic flag!", true);
+//
+//  int main(int argc, const char** argv) {
+//    if (!flags::Parse(argv)) {
+//      return -1;
+//    }
+//
+//    if (flags::h.value()) {
+//      printf("usage: my-bin --my-flag=<value>\n");
+//      return 0;
+//    }
+//
+//    printf("flag value: %s\n", flags::my_flag.value().c_str());
+//    for (const std::string& arg : flags::positional_arguments) {
+//      printf("arg: %s\n", arg.c_str());
+//    }
+//    return 0;
+//  }
+// ```c
+
+// Those macros can be used to define flags.
+// - They should be used in the global scope.
+// - Underscores in the flag variable name are replaced with hyphens ('-').
+//
+// Example:
+//  FLAG_SHORT_bool(my_flag, false, "some help", false);
+//    -  in the code: flags::my_flag
+//    - command line: --my-flag=true
+//
+#define FLAG_LONG_string(Name, Default, Required) \
+  UTIL_FLAGS_FLAG_LONG(std::string, Name, Default, Required)
+#define FLAG_LONG_bool(Name, Default, Required) \
+  UTIL_FLAGS_FLAG_LONG(bool, Name, Default, Required)
+#define FLAG_LONG_uint(Name, Default, Required) \
+  UTIL_FLAGS_FLAG_LONG(uint32_t, Name, Default, Required)
+
+#define FLAG_SHORT_string(Name, Default, Required) \
+  UTIL_FLAGS_FLAG_SHORT(std::string, Name, Default, Required)
+#define FLAG_SHORT_bool(Name, Default, Required) \
+  UTIL_FLAGS_FLAG_SHORT(bool, Name, Default, Required)
+#define FLAG_SHORT_uint(Name, Default, Required) \
+  UTIL_FLAGS_FLAG_SHORT(uint32_t, Name, Default, Required)
+
+namespace flags {
+
+// Parse the command-line arguments, checking flags, and separating positional
+// arguments from flags.
+//
+// * argv: the argv array received in the main function. This utility expects
+// the last pointer to
+//         be NULL, as it should if coming from the main() function.
+//
+// Returns `true` if the parsing succeeds, `false` otherwise.
+bool Parse(const char** argv);
+
+}  // namespace flags
+
+// ===================== BEGIN NON-PUBLIC SECTION =============================
+// All the code below belongs to the implementation, and there is no guaranteed
+// around the API stability. Please do not use it directly.
+
+// Defines the static variable holding the flag, allowing access like
+// flags::my_flag.
+// By creating the FlagRegistration object, the flag can be added to
+// the global list.
+// The final `extern` definition is ONLY useful for clang-format:
+//  - if the macro doesn't ends with a semicolon, clang-format goes wild.
+//  - cannot disable clang-format for those macros on clang < 16.
+//    (https://github.com/llvm/llvm-project/issues/54522)
+//  - cannot allow trailing semi (-Wextra-semi).
+#define UTIL_FLAGS_FLAG(Type, Prefix, Name, Default, Required, IsShort)     \
+  namespace flags {                                                         \
+  Flag<Type> Name(Default);                                                 \
+  namespace {                                                               \
+  static FlagRegistration Name##_registration(Name, Prefix #Name, Required, \
+                                              IsShort);                     \
+  }                                                                         \
+  }                                                                         \
+  extern flags::Flag<Type> flags::Name
+
+#define UTIL_FLAGS_FLAG_LONG(Type, Name, Default, Required) \
+  UTIL_FLAGS_FLAG(Type, "--", Name, Default, Required, false)
+#define UTIL_FLAGS_FLAG_SHORT(Type, Name, Default, Required) \
+  UTIL_FLAGS_FLAG(Type, "-", Name, Default, Required, true)
+
+namespace flags {
+
+// Just a wrapper around the flag value.
+template <typename T>
+struct Flag {
+ public:
+  Flag(T&& default_value) : value_(default_value) {}
+  Flag(Flag&& other) = delete;
+  Flag(const Flag& other) = delete;
+
+  const T& value() const { return value_; }
+  T& value() { return value_; }
+
+ private:
+  T value_;
+};
+
+// To add support for new flag-types, this needs to be extended, and the visitor
+// below.
+using FlagType = std::variant<std::reference_wrapper<Flag<std::string>>,
+                              std::reference_wrapper<Flag<bool>>,
+                              std::reference_wrapper<Flag<uint32_t>>>;
+
+template <class>
+inline constexpr bool always_false_v = false;
+
+extern std::vector<std::string> positional_arguments;
+
+// Static class keeping track of the flags/arguments values.
+class FlagList {
+  struct FlagInfo {
+    FlagInfo(FlagType&& flag_, std::string&& name_, bool required_,
+             bool is_short_)
+        : flag(std::move(flag_)),
+          name(std::move(name_)),
+          required(required_),
+          is_short(is_short_) {}
+
+    FlagType flag;
+    std::string name;
+    bool required;
+    bool is_short;
+  };
+
+ public:
+  template <typename T>
+  static void register_flag(Flag<T>& flag, std::string&& name, bool required,
+                            bool is_short) {
+    get_flags().emplace_back(flag, std::move(name), required, is_short);
+  }
+
+  static bool parse(const char** argv);
+
+#ifdef TESTING
+  // Flags are supposed to be constant for the whole app execution, hence the
+  // static storage. Gtest doesn't fork before running a test, meaning we have
+  // to manually clear the context at teardown.
+  static void reset() {
+    get_flags().clear();
+    positional_arguments.clear();
+  }
+#endif
+
+ private:
+  static std::vector<FlagInfo>& get_flags() {
+    static std::vector<FlagInfo> flags;
+    return flags;
+  }
+
+  static bool parse_flag_info(FlagInfo& info, const char*** iterator);
+  static void print_usage(const char* binary_name,
+                          const std::string& usage_format);
+};
+
+template <typename T>
+struct FlagRegistration {
+  FlagRegistration(Flag<T>& flag, std::string&& name, bool required,
+                   bool is_short) {
+    std::string fixed_name = name;
+    for (auto& c : fixed_name) {
+      if (c == '_') {
+        c = '-';
+      }
+    }
+
+    FlagList::register_flag(flag, std::move(fixed_name), required, is_short);
+  }
+};
+
+// Explicit deduction guide to avoid `-Wctad-maybe-unsupported`.
+template <typename T>
+FlagRegistration(Flag<T>&, std::string&&, bool, bool) -> FlagRegistration<T>;
+
+}  // namespace flags
+
+#endif  // INCLUDE_SPIRV_TOOLS_UTIL_FLAGS_HPP_
diff --git a/utils/check_code_format.sh b/utils/check_code_format.sh
index 7994740..da5e019 100755
--- a/utils/check_code_format.sh
+++ b/utils/check_code_format.sh
@@ -18,7 +18,7 @@
 #
 # This script assumes to be invoked at the project root directory.
 
-BASE_BRANCH=${1:-master}
+BASE_BRANCH=${1:-main}
 
 FILES_TO_CHECK=$(git diff --name-only ${BASE_BRANCH} | grep -E ".*\.(cpp|cc|c\+\+|cxx|c|h|hpp)$")
 
diff --git a/utils/check_copyright.py b/utils/check_copyright.py
index aa647af..e3e74bc 100755
--- a/utils/check_copyright.py
+++ b/utils/check_copyright.py
@@ -41,8 +41,9 @@
            'Alastair F. Donaldson',
            'Mostafa Ashraf',
            'Shiyu Liu',
-           'ZHOU He']
-CURRENT_YEAR = 2022
+           'ZHOU He',
+           'Nintendo']
+CURRENT_YEAR = 2023
 
 FIRST_YEAR = 2014
 FINAL_YEAR = CURRENT_YEAR + 5
diff --git a/utils/check_symbol_exports.py b/utils/check_symbol_exports.py
index 7795d72..e1ca0b7 100755
--- a/utils/check_symbol_exports.py
+++ b/utils/check_symbol_exports.py
@@ -67,7 +67,7 @@
     # by the protobuf compiler:
     #   - AddDescriptors_spvtoolsfuzz_2eproto()
     #   - InitDefaults_spvtoolsfuzz_2eproto()
-    symbol_allowlist_pattern = re.compile(r'_Z[0-9]+(InitDefaults|AddDescriptors)_spvtoolsfuzz_2eprotov')
+    symbol_allowlist_pattern = re.compile(r'_Z[0-9]+.*spvtoolsfuzz_2eproto.*')
 
     symbol_is_new_or_delete = re.compile(r'^(_Zna|_Znw|_Zdl|_Zda)')
     # Compilaion for Arm has various thunks for constructors, destructors, vtables.
diff --git a/utils/generate_changelog.py b/utils/generate_changelog.py
new file mode 100644
index 0000000..54db728
--- /dev/null
+++ b/utils/generate_changelog.py
@@ -0,0 +1,98 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2023 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.
+
+# Args: <CHANGES-file> <tag> <output-file>
+# Updates an output file with changelog from the given CHANGES file and tag.
+#  - search for first line matching <tag> in file <CHANGES-file>
+#  - search for the next line with a tag
+#  - writes all the lines in between those 2 tags into <output-file>
+
+import errno
+import os
+import os.path
+import re
+import subprocess
+import logging
+import sys
+
+# Regex to match the SPIR-V version tag.
+# Example of matching tags:
+#  - v2020.1
+#  - v2020.1-dev
+#  - v2020.1.rc1
+VERSION_REGEX = re.compile(r'^(v\d+\.\d+) +[0-9]+-[0-9]+-[0-9]+$')
+
+def mkdir_p(directory):
+    """Make the directory, and all its ancestors as required.  Any of the
+    directories are allowed to already exist."""
+
+    if directory == "":
+        # We're being asked to make the current directory.
+        return
+
+    try:
+        os.makedirs(directory)
+    except OSError as e:
+        if e.errno == errno.EEXIST and os.path.isdir(directory):
+            pass
+        else:
+            raise
+
+def main():
+    FORMAT = '%(asctime)s %(message)s'
+    logging.basicConfig(format="[%(asctime)s][%(levelname)-8s] %(message)s", datefmt="%H:%M:%S")
+    if len(sys.argv) != 4:
+        logging.error("usage: {} <CHANGES-path> <tag> <output-file>".format(sys.argv[0]))
+        sys.exit(1)
+
+    changes_path = sys.argv[1]
+    start_tag = sys.argv[2]
+    output_file_path = sys.argv[3]
+
+    changelog = []
+    has_found_start = False
+    with open(changes_path, "r") as file:
+      for line in file.readlines():
+        m = VERSION_REGEX.match(line)
+        if m:
+          print(m.groups()[0])
+          print(start_tag)
+          if has_found_start:
+            break;
+          if start_tag == m.groups()[0]:
+            has_found_start = True
+          continue
+
+        if has_found_start:
+          changelog.append(line)
+
+    if not has_found_start:
+      logging.error("No tag matching {} found.".format(start_tag))
+      sys.exit(1)
+
+    content = "".join(changelog)
+    if os.path.isfile(output_file_path):
+      with open(output_file_path, 'r') as f:
+        if content == f.read():
+          sys.exit(0)
+
+    mkdir_p(os.path.dirname(output_file_path))
+    with open(output_file_path, 'w') as f:
+        f.write(content)
+    sys.exit(0)
+
+if __name__ == '__main__':
+    main()
diff --git a/utils/generate_grammar_tables.py b/utils/generate_grammar_tables.py
index 74aa282..e6a1455 100755
--- a/utils/generate_grammar_tables.py
+++ b/utils/generate_grammar_tables.py
@@ -33,6 +33,7 @@
 SPV_KHR_non_semantic_info
 """
 
+OUTPUT_LANGUAGE = 'c'
 
 def make_path_to_file(f):
     """Makes all ancestor directories to the given file, if they don't yet
@@ -76,9 +77,14 @@
       - caps: a sequence of capability names
 
     Returns:
-      a string containing the braced list of SpvCapability* enums named by caps.
+      a string containing the braced list of SpvCapability* or spv::Capability:: enums named by caps.
     """
-    return '{' + ', '.join(['SpvCapability{}'.format(c) for c in 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):
@@ -99,8 +105,12 @@
       - 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 SpvCapability {}[] = {};'.format(
+        'static const ' + cap_str + ' {}[] = {};'.format(
             get_capability_array_name(c), compose_capability_list(c))
         for c in caps]
     return '\n'.join(arrays)
@@ -255,7 +265,12 @@
             self.operands.pop()
 
     def __str__(self):
-        template = ['{{"{opname}"', 'SpvOp{opname}',
+        global OUTPUT_LANGUAGE
+        base_str = 'SpvOp'
+        if OUTPUT_LANGUAGE == 'c++':
+            base_str = 'spv::Op::Op'
+
+        template = ['{{"{opname}"', base_str + '{opname}',
                     '{num_caps}', '{caps_mask}',
                     '{num_operands}', '{{{operands}}}',
                     '{def_result_id}', '{ref_type_id}',
@@ -525,7 +540,7 @@
 
     # We have a few operand kinds that require their optional counterpart to
     # exist in the operand info table.
-    optional_enums = ['ImageOperands', 'AccessQualifier', 'MemoryAccess', 'PackedVectorFormat']
+    optional_enums = ['ImageOperands', 'AccessQualifier', 'MemoryAccess', 'PackedVectorFormat', 'CooperativeMatrixOperands']
     optional_enums = [e for e in enums if e[0] in optional_enums]
     enums.extend(optional_enums)
 
@@ -634,9 +649,16 @@
 
     We take care to avoid emitting duplicate values.
     """
-    function = 'const char* CapabilityToString(SpvCapability capability) {\n'
+    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 SpvCapability{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):
@@ -644,8 +666,8 @@
         if value not in emitted:
             emitted.add(value)
             function += template.format(capability=capability.get('enumerant'))
-    function += '    case SpvCapabilityMax:\n' \
-        '      assert(0 && "Attempting to convert SpvCapabilityMax to string");\n' \
+    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
@@ -734,6 +756,10 @@
                         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,
@@ -765,6 +791,9 @@
                         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...":
diff --git a/utils/generate_language_headers.py b/utils/generate_language_headers.py
index 83fa99e..18a8d5e 100755
--- a/utils/generate_language_headers.py
+++ b/utils/generate_language_headers.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright (c) 2017 Google Inc.
 
 # Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/utils/generate_registry_tables.py b/utils/generate_registry_tables.py
index 28152ef..14d4909 100755
--- a/utils/generate_registry_tables.py
+++ b/utils/generate_registry_tables.py
@@ -15,8 +15,9 @@
 """Generates the vendor tool table from the SPIR-V XML registry."""
 
 import errno
+import io
 import os.path
-import xml.etree.ElementTree
+from xml.etree.ElementTree import XML, XMLParser, TreeBuilder
 
 
 def mkdir_p(directory):
@@ -78,8 +79,9 @@
                         help='output file for SPIR-V generators table')
     args = parser.parse_args()
 
-    with open(args.xml) as xml_in:
-       registry = xml.etree.ElementTree.fromstring(xml_in.read())
+    with io.open(args.xml, encoding='utf-8') as xml_in:
+      parser = XMLParser(target=TreeBuilder(), encoding='utf-8')
+      registry = XML(xml_in.read(), parser=parser)
 
     mkdir_p(os.path.dirname(args.generator_output))
     with open(args.generator_output, 'w') as f:
diff --git a/utils/git-sync-deps b/utils/git-sync-deps
index 7a7e606..6549afb 100755
--- a/utils/git-sync-deps
+++ b/utils/git-sync-deps
@@ -28,10 +28,9 @@
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 """Parse a DEPS file and git checkout all of the dependencies.
+"""
 
-Args:
-  An optional list of deps_os values.
-
+EXTRA_HELP = """
 Environment Variables:
   GIT_EXECUTABLE: path to "git" binary; if unset, will look for one of
   ['git', 'git.exe', 'git.bat'] in your default path.
@@ -52,6 +51,7 @@
 """
 
 
+import argparse
 import os
 import re
 import subprocess
@@ -59,12 +59,14 @@
 import threading
 from builtins import bytes
 
-
 def git_executable():
   """Find the git executable.
 
   Returns:
-      A string suitable for passing to subprocess functions, or None.
+      A triple:
+        A string suitable for passing to subprocess functions, or None.
+        The major version number
+        The minor version number
   """
   envgit = os.environ.get('GIT_EXECUTABLE')
   searchlist = ['git', 'git.exe', 'git.bat']
@@ -72,30 +74,36 @@
     searchlist.insert(0, envgit)
   with open(os.devnull, 'w') as devnull:
     for git in searchlist:
+      major=None
+      minor=None
       try:
-        subprocess.call([git, '--version'], stdout=devnull)
+        version_info = subprocess.check_output([git, '--version']).decode('utf-8')
+        match = re.search("^git version (\d+)\.(\d+)",version_info)
+        print("Using {}".format(version_info))
+        if match:
+          major = int(match.group(1))
+          minor = int(match.group(2))
+        else:
+          continue
       except (OSError,):
         continue
-      return git
-  return None
+      return (git,major,minor)
+  return (None,0,0)
 
 
 DEFAULT_DEPS_PATH = os.path.normpath(
   os.path.join(os.path.dirname(__file__), os.pardir, 'DEPS'))
 
+def get_deps_os_str(deps_file):
+  parsed_deps = parse_file_to_dict(deps_file)
+  parts = []
+  if 'deps_os' in parsed_deps:
+    for deps_os in parsed_deps['deps_os']:
+      parts.append(' [{}]]'.format(deps_os))
+  return "\n".join(parts)
 
-def usage(deps_file_path = None):
-  sys.stderr.write(
-    'Usage: run to grab dependencies, with optional platform support:\n')
-  sys.stderr.write('  %s %s' % (sys.executable, __file__))
-  if deps_file_path:
-    parsed_deps = parse_file_to_dict(deps_file_path)
-    if 'deps_os' in parsed_deps:
-      for deps_os in parsed_deps['deps_os']:
-        sys.stderr.write(' [%s]' % deps_os)
-  sys.stderr.write('\n\n')
-  sys.stderr.write(__doc__)
-
+def looks_like_raw_commit(commit):
+  return re.match('^[a-f0-9]{40}$', commit) is not None
 
 def git_repository_sync_is_disabled(git, directory):
   try:
@@ -125,14 +133,14 @@
 
 def status(directory, checkoutable):
   def truncate(s, length):
-    return s if len(s) <= length else s[:(length - 3)] + '...'
+    return s if len(s) <= length else '...' + s[-(length - 3):]
   dlen = 36
   directory = truncate(directory, dlen)
   checkoutable = truncate(checkoutable, 40)
   sys.stdout.write('%-*s @ %s\n' % (dlen, directory, checkoutable))
 
 
-def git_checkout_to_directory(git, repo, checkoutable, directory, verbose):
+def git_checkout_to_directory(git, repo, checkoutable, directory, verbose, treeless):
   """Checkout (and clone if needed) a Git repository.
 
   Args:
@@ -147,13 +155,22 @@
     directory (string) the path into which the repository
               should be checked out.
 
-    verbose (boolean)
+    verbose (boolean): emit status info to stdout
+
+    treeless (boolean): when true, clone without any trees.
 
   Raises an exception if any calls to git fail.
   """
   if not os.path.isdir(directory):
+    # Use blobless or treeless checkouts for faster downloads.
+    # This defers some work to checkout time.
+    # https://github.blog/2020-12-21-get-up-to-speed-with-partial-clone-and-shallow-clone/
+    filter = ['--filter=tree:0'] if treeless else ['--filter=blob:none']
+    # If the thing to check out looks like a tag (and not like a commit),
+    # then limit the checkout to that branch.
+    branch = [] if looks_like_raw_commit(checkoutable) else ['--branch={}'.format(checkoutable)]
     subprocess.check_call(
-      [git, 'clone', '--quiet', repo, directory])
+        [git, 'clone', '--quiet', '--single-branch'] + filter + branch + [repo, directory])
 
   if not is_git_toplevel(git, directory):
     # if the directory exists, but isn't a git repo, you will modify
@@ -200,7 +217,7 @@
   return dictionary
 
 
-def git_sync_deps(deps_file_path, command_line_os_requests, verbose):
+def git_sync_deps(deps_file_path, command_line_os_requests, verbose, treeless):
   """Grab dependencies, with optional platform support.
 
   Args:
@@ -210,11 +227,20 @@
         List of strings that should each be a key in the deps_os
         dictionary in the DEPS file.
 
+    verbose (boolean): emit status info to stdout
+
+    treeless (boolean): when true, clone as treeless instead of blobless
+
   Raises git Exceptions.
   """
-  git = git_executable()
+  (git,git_major,git_minor) = git_executable()
   assert git
 
+  # --filter=tree:0 is available in git 2.20 and later
+  if (git_major,git_minor) < (2,20):
+    print("disabling --treeless: git is older than v2.20")
+    treeless = False
+
   deps_file_directory = os.path.dirname(deps_file_path)
   deps_file = parse_file_to_dict(deps_file_path)
   dependencies = deps_file['deps'].copy()
@@ -241,7 +267,7 @@
     relative_directory = os.path.join(deps_file_directory, directory)
 
     list_of_arg_lists.append(
-      (git, repo, checkoutable, relative_directory, verbose))
+      (git, repo, checkoutable, relative_directory, verbose, treeless))
 
   multithread(git_checkout_to_directory, list_of_arg_lists)
 
@@ -264,17 +290,47 @@
 
 
 def main(argv):
-  deps_file_path = os.environ.get('GIT_SYNC_DEPS_PATH', DEFAULT_DEPS_PATH)
-  verbose = not bool(os.environ.get('GIT_SYNC_DEPS_QUIET', False))
+  argparser = argparse.ArgumentParser(
+          prog = "git-sync-deps",
+          description = "Checkout git-based dependencies as specified by the DEPS file",
+          add_help=False # Because we want to print deps_os with -h option
+          )
+  argparser.add_argument("--help", "-h",
+                         action='store_true',
+                         help="show this help message and exit")
+  argparser.add_argument("--deps",
+                         default = os.environ.get('GIT_SYNC_DEPS_PATH', DEFAULT_DEPS_PATH),
+                         help="location of the the DEPS file")
+  argparser.add_argument("--verbose",
+                         default=not bool(os.environ.get('GIT_SYNC_DEPS_QUIET', False)),
+                         action='store_true',
+                         help="be verbose: print status messages")
+  argparser.add_argument("--treeless",
+                         default=False,
+                         action='store_true',
+                         help="""
+    Clone repos without trees (--filter=tree:0).
+    This is the fastest option for a build machine,
+    when you only need a single commit.
+    Defers getting objects until checking out a commit.
 
-  if '--help' in argv or '-h' in argv:
-    usage(deps_file_path)
-    return 1
+    The default is to clone with trees but without blobs.
 
-  git_sync_deps(deps_file_path, argv, verbose)
-  # subprocess.check_call(
-  #     [sys.executable,
-  #      os.path.join(os.path.dirname(deps_file_path), 'bin', 'fetch-gn')])
+    Only takes effect if using git 2.20 or later.
+
+    See https://github.blog/2020-12-21-get-up-to-speed-with-partial-clone-and-shallow-clone/
+                              """)
+  argparser.add_argument("os_requests",nargs="*",
+                         help="OS requests, as keys in the deps_os dictionariy in the DEPS file")
+
+  args = argparser.parse_args()
+  if args.help:
+    print(argparser.format_help())
+    print(EXTRA_HELP)
+    print(get_deps_os_str(args.deps))
+    return 0
+
+  git_sync_deps(args.deps, args.os_requests, args.verbose, args.treeless)
   return 0
 
 
diff --git a/utils/roll_deps.sh b/utils/roll_deps.sh
index 20c061f..d19ee00 100755
--- a/utils/roll_deps.sh
+++ b/utils/roll_deps.sh
@@ -20,14 +20,22 @@
 
 set -eo pipefail
 
-effcee_dir="external/effcee/"
-effcee_trunk="origin/main"
-googletest_dir="external/googletest/"
-googletest_trunk="origin/main"
-re2_dir="external/re2/"
-re2_trunk="origin/main"
-spirv_headers_dir="external/spirv-headers/"
-spirv_headers_trunk="origin/master"
+function ExitIfIsInterestingError() {
+  local return_code=$1
+  if [[ ${return_code} -ne 0 && ${return_code} -ne 2 ]]; then
+    exit ${return_code}
+  fi
+  return 0
+}
+
+
+dependencies=("external/effcee/"
+              "external/googletest/"
+              "external/re2/"
+              "external/spirv-headers/")
+
+
+branch="origin/main"
 
 # This script assumes it's parent directory is the repo root.
 repo_path=$(dirname "$0")/..
@@ -44,10 +52,9 @@
 old_head=$(git rev-parse HEAD)
 
 set +e
-roll-dep --ignore-dirty-tree --roll-to="${effcee_trunk}" "${effcee_dir}"
-roll-dep --ignore-dirty-tree --roll-to="${googletest_trunk}" "${googletest_dir}"
-roll-dep --ignore-dirty-tree --roll-to="${re2_trunk}" "${re2_dir}"
-roll-dep --ignore-dirty-tree --roll-to="${spirv_headers_trunk}" "${spirv_headers_dir}"
 
-git rebase --interactive "${old_head}"
-
+for dep in ${dependencies[@]}; do
+  echo "Rolling $dep"
+  roll-dep --ignore-dirty-tree --roll-to="${branch}" "${dep}"
+  ExitIfIsInterestingError $?
+done
diff --git a/utils/update_build_version.py b/utils/update_build_version.py
index 2a1ca60..1d7f565 100755
--- a/utils/update_build_version.py
+++ b/utils/update_build_version.py
@@ -35,9 +35,13 @@
 import os.path
 import re
 import subprocess
+import logging
 import sys
 import time
 
+# Format of the output generated by this script. Example:
+# "v2023.1", "SPIRV-Tools v2023.1 0fc5526f2b01a0cc89192c10cf8bef77f1007a62, 2023-01-18T14:51:49"
+OUTPUT_FORMAT = '"{version_tag}", "SPIRV-Tools {version_tag} {description}"\n'
 
 def mkdir_p(directory):
     """Make the directory, and all its ancestors as required.  Any of the
@@ -55,7 +59,6 @@
         else:
             raise
 
-
 def command_output(cmd, directory):
     """Runs a command in a directory and returns its standard output stream.
 
@@ -63,23 +66,32 @@
 
     Raises a RuntimeError if the command fails to launch or otherwise fails.
     """
-    p = subprocess.Popen(cmd,
-                         cwd=directory,
-                         stdout=subprocess.PIPE,
-                         stderr=subprocess.PIPE)
-    (stdout, _) = p.communicate()
-    if p.returncode != 0:
-        raise RuntimeError('Failed to run %s in %s' % (cmd, directory))
-    return stdout
-
+    try:
+      # Set shell=True on Windows so that Chromium's git.bat can be found when
+      # 'git' is invoked.
+      p = subprocess.Popen(cmd,
+                           cwd=directory,
+                           stdout=subprocess.PIPE,
+                           stderr=subprocess.PIPE,
+                           shell=os.name == 'nt')
+      (stdout, stderr) = p.communicate()
+      if p.returncode != 0:
+        logging.error('Failed to run "{}" in "{}": {}'.format(cmd, directory, stderr.decode()))
+    except Exception as e:
+        logging.error('Failed to run "{}" in "{}": {}'.format(cmd, directory, str(e)))
+        return False, None
+    return p.returncode == 0, stdout
 
 def deduce_software_version(changes_file):
-    """Returns a software version number parsed from the given CHANGES file.
+    """Returns a tuple (success, software version number) parsed from the
+    given CHANGES file.
 
-    The CHANGES file describes most recent versions first.
+    Success is set to True if the software version could be deduced.
+    Software version is undefined if success if False.
+    Function expects the CHANGES file to describes most recent versions first.
     """
 
-    # Match the first well-formed version-and-date line.
+    # Match the first well-formed version-and-date line
     # Allow trailing whitespace in the checked-out source code has
     # unexpected carriage returns on a linefeed-only system such as
     # Linux.
@@ -88,60 +100,71 @@
         for line in f.readlines():
             match = pattern.match(line)
             if match:
-                return match.group(1)
-    raise Exception('No version number found in {}'.format(changes_file))
+                return True, match.group(1)
+    return False, None
 
 
-def describe(directory):
+def describe(repo_path):
     """Returns a string describing the current Git HEAD version as descriptively
     as possible.
 
     Runs 'git describe', or alternately 'git rev-parse HEAD', in directory.  If
     successful, returns the output; otherwise returns 'unknown hash, <date>'."""
-    try:
-        # decode() is needed here for Python3 compatibility. In Python2,
-        # str and bytes are the same type, but not in Python3.
-        # Popen.communicate() returns a bytes instance, which needs to be
-        # decoded into text data first in Python3. And this decode() won't
-        # hurt Python2.
-        return command_output(['git', 'describe'], directory).rstrip().decode()
-    except:
-        try:
-            return command_output(
-                ['git', 'rev-parse', 'HEAD'], directory).rstrip().decode()
-        except:
-            # This is the fallback case where git gives us no information,
-            # e.g. because the source tree might not be in a git tree.
-            # In this case, usually use a timestamp.  However, to ensure
-            # reproducible builds, allow the builder to override the wall
-            # clock time with environment variable SOURCE_DATE_EPOCH
-            # containing a (presumably) fixed timestamp.
-            timestamp = int(os.environ.get('SOURCE_DATE_EPOCH', time.time()))
-            formatted = datetime.datetime.utcfromtimestamp(timestamp).isoformat()
-            return 'unknown hash, {}'.format(formatted)
 
+    # if we're in a git repository, attempt to extract version info
+    if os.path.exists(".git"):
+        success, output = command_output(["git", "describe"], repo_path)
+        if not success:
+            output = command_output(["git", "rev-parse", "HEAD"], repo_path)
+
+        if success:
+            # decode() is needed here for Python3 compatibility. In Python2,
+            # str and bytes are the same type, but not in Python3.
+            # Popen.communicate() returns a bytes instance, which needs to be
+            # decoded into text data first in Python3. And this decode() won't
+            # hurt Python2.
+            return output.rstrip().decode()
+
+    # This is the fallback case where git gives us no information,
+    # e.g. because the source tree might not be in a git tree.
+    # In this case, usually use a timestamp.  However, to ensure
+    # reproducible builds, allow the builder to override the wall
+    # clock time with environment variable SOURCE_DATE_EPOCH
+    # containing a (presumably) fixed timestamp.
+    timestamp = int(os.environ.get('SOURCE_DATE_EPOCH', time.time()))
+    iso_date = datetime.datetime.utcfromtimestamp(timestamp).isoformat()
+    return "unknown hash, {}".format(iso_date)
 
 def main():
+    FORMAT = '%(asctime)s %(message)s'
+    logging.basicConfig(format="[%(asctime)s][%(levelname)-8s] %(message)s", datefmt="%H:%M:%S")
     if len(sys.argv) != 3:
-        print('usage: {} <changes-files> <output-file>'.format(sys.argv[0]))
+        logging.error("usage: {} <repo-path> <output-file>".format(sys.argv[0]))
         sys.exit(1)
 
-    output_file = sys.argv[2]
-    mkdir_p(os.path.dirname(output_file))
+    changes_file_path = os.path.realpath(sys.argv[1])
+    output_file_path = sys.argv[2]
 
-    software_version = deduce_software_version(sys.argv[1])
-    directory = os.path.dirname(sys.argv[1])
-    new_content = '"{}", "SPIRV-Tools {} {}"\n'.format(
-        software_version, software_version,
-        describe(directory).replace('"', '\\"'))
+    success, version = deduce_software_version(changes_file_path)
+    if not success:
+      logging.error("Could not deduce latest release version from {}.".format(changes_file_path))
+      sys.exit(1)
 
-    if os.path.isfile(output_file):
-        with open(output_file, 'r') as f:
-            if new_content == f.read():
-                return
+    repo_path = os.path.dirname(changes_file_path)
+    description = describe(repo_path)
+    content = OUTPUT_FORMAT.format(version_tag=version, description=description)
 
-    with open(output_file, 'w') as f:
-        f.write(new_content)
+    # Escape file content.
+    content.replace('"', '\\"')
+
+    if os.path.isfile(output_file_path):
+      with open(output_file_path, 'r') as f:
+        if content == f.read():
+          return
+
+    mkdir_p(os.path.dirname(output_file_path))
+    with open(output_file_path, 'w') as f:
+        f.write(content)
 
 if __name__ == '__main__':
     main()