Squashed 'third_party/SPIRV-Tools/' changes from 65e362b7a..c3f22f7cb

c3f22f7cb Update README (#3047)
1f2af55f2 Update README (#3048)
3da910d55 Avoid uninit warning in GCC (#3044)
cdd6829a3 utils/vscode: Add SPIR-V language server support
b96c9a057 utils/vscode: Add go language server packages.
ab3cdcaef Fix operand access of composite in upgrade memory model (#3021)
1a18d491f Validate array stride does not cause overlap (#3028)
12e54dae1 Update Offset to ConstOffset bitmask if operand is constant. (#3024)
3e4abc9ac Try to fix go linter: exported constant comment (#3026)
c5e6761ac spirv-fuzz: Eliminate spurious semicolons (#3025)
041f0a024 spirv-fuzz: simplify transformation for replacing an id with a synonym (#3020)
528c00c01 Re-enable OpReadClockKHR validation (#3013)
8a35bf030 Python 3: remove deprecated U qualifier on 'open' (#3019)
989f62fb9 Remove semicolon (#3022)
dc59b4b07 spirv-fuzz: vector shuffle transformation (#3015)
87efe0a8b Add basic documentation about spirv-fuzz (#3016)
d9fbf0234 Fix Go lint issues: comment exported symbols (#3014)
3724cfbea spirv-fuzz: better computation of data synonym facts (#3010)
fb6bac889 spirv-fuzz: make equivalence classes deterministic (#3011)
e0d5544c9 Add missing headers to GN. (#3009)
178c4ddea Correct "environment" typo (#3007)
f1e5cd73f spirv-fuzz: improvements to representation of data synonym facts (#3006)
cdee051e2 Add iOS as a supported platform (#3001)
5f6fb2f34 Reset pointers before iterating in fuzzer to avoid double free (#3003)
618ee5094 Fix some clang-tidy issues in graphics_robust_access_pass (#2998)
466908b50 Add description of wrap-opkill. (#3000)
f893d4d41 [opt] Do not compare optimized binary with an invalidated buffer (#2999)
7e2cba6a5 utils/vscode: Change assembly file ext to .spvasm (#2995)
42f885238 utils: Add a vscode extension for SPIR-V disassembly files (#2987)
5ea709937 Add two new simplifications. (#2984)
fac166162 spirv-fuzz: Transformation to extract from a composite object (#2991)
ec12de913 spirv-fuzz: rename class, and fix bug related to dominance (#2990)
0dbd4e358 spirv-fuzz: Rework management of data synonyms (#2989)
b34fa7319 spirv-fuzz: add class to represent equivalence relation (#2988)
4517c7b88 Update DEPS (#2986)
570582d8d spirv-fuzz: fuzzer pass to adjust memory access operands (#2968)
02910ffdf Instrument: Add missing def-use analysis. (#2985)
8357b878d spirv-fuzz: add missing functionality for matrix composites (#2974)
615918c91 Update CHANGES
e8c3f9b0b Ensure timestamp does not vary with timezone. (#2982)
6a9be627c Keep NOPs when comparing with original binary (#2931)
2a3cbe7c3 Check that derivatives operate on 32-bit values (#2983)
3cdd6444c Check text->str before destroying (#2981)
e3da3143b Disallow use of OpCompositeExtract/OpCompositeInsert with no indices (#2980)
2ca4fcfdc Add fuzzer for spirv-dis call path (#2977)
024417d83 Check binary->code existence before destroying (#2979)
9c0ae6bb8 Improved CMake install step. (#2963)
e99b91822 Support constant-folding UConvert and SConvert (#2960)
8e8977853 Add fuzzer for spirv-as call path (#2976)
00170cc5e spirv-fuzz: Refactor 'copy object' and 'construct composite' transformations (#2966)
964dc52df Update SPIR-V binary header test for SPIR-V 1.5 (#2967)
1b6fd37fa spirv-fuzz: Refactor 'split blocks' to identify instructions differently (#2961)
2276e5978 Validate that selections are structured (#2962)
3eda1b9ff spirv-fuzz: Rework id descriptors (#2959)
eba98c4eb spirv-fuzz: Add fuzzer pass to add NoContraction decorations (#2950)
91232f7f7 spirv-fuzz: Add fuzzer pass to change function controls (#2951)
feb154921 reduce: add large tests and fix (#2947)
253806adc spirv-fuzz: Add fuzzer pass to change loop controls (#2949)
1cea3b785 Fixed include paths and order according to Google style. (#2957)
82f84c4b8 Use a longer timeout for Bazel tests. (#2956)
c705032b2 Remove non-existent files from BUILD.gn (#2955)
c1d42038f Disable scope validation for OpReadClockKHR (#2953)
df15a4a3c CMake: Add support for building with emscripten (#2948)
c3ca04741 Update CHANGES
32f76efa6 Link cfg and dominator analysis in the context (#2946)
5910bb8e9 spirv-fuzz: add transformation and pass to construct composites (#2941)
2f6a87f61 reduce: improve remove unref instr pass (#2945)
81d227f36 spirv-fuzz: add disabled test to document known issue (#2942)
26dba32c4 spirv-fuzz: Add fuzzer pass to change selection controls (#2944)
3c7ff8d4f Enable OpTypeCooperativeMatrix specialization (#2927)
c18c9ff6b Handle OpKill better (#2933)
ad3d23f47 Generate null pointer by converting uint64 zero to pointer. (#2935)
8fa0f1a65 spirv-fuzz: option to convert shader into a form that renders red (#2934)
bc37fd585 Add SPV_KHR_shader_clock validation (#2879)
bd839ca6b reduce/fuzz: improve command line args (#2932)
9d7428b05 Validate physical storage buffer restrictions (#2930)
44b32176e fuzz: add shrinker-temp-file-prefix (#2928)
9eb1c9a4c Add continue construct analysis to struct cfg analysis (#2922)
85c67b5e0 Record trailing line dbg instructions (#2926)
4075b921f Add removing references to debug instructions when removing them (#2923)
ef4679a58 spirv-fuzz: allow interestingness script arguments (#2925)
bc84daff2 Add Kokoro bots for building with Bazel. (#2914)
10951a7c9 Refactor the InstructionPass (#2924)
84b197606 spirv-fuzz: do not allow a dead break to target an unreachable block (#2917)
27238bcca spirv-fuzz: preserve some analyses when permuting blocks (#2918)
510ca9d61 Only allow previously declared forward refs in structs (#2920)
2a11f365b Handle id overflow in wrap-opkill (#2916)
70097c776 spirv-fuzz: do not replace struct indices with synonyms (#2915)
c1e03834e spirv-fuzz: Fixes to preconditions for adding dead break/continue edges (#2904)
7bc114ba2 spirv-fuzz: do not replace a pointer argument to a function call with a synonym (#2901)
290f6a820 spirv-fuzz: do not replace boolean constant argument to OpPhi instruction (#2903)
527a68930 Remove validate_datarules.cpp (#2911)
55ea57a78 Handle extract with no indexes (#2910)
a03cd1f3e Add Bazel build configuration. (#2891)
6f26d9ad8 Handle id overflow in convert local access chains (#2908)
71e0ba606 Add OpCopyMemory test to SVA. (#2885)
3a762d54f Add missing GN dependency (#2899)
958f7e72a Employ the "swarm testing" idea in spirv-fuzz (#2890)
b83535da5 Fix operand index in spirv-fuzz (#2895)
f62ee4a4a Update DEPS: effcee, re2, googletest (#2881)
8d0ca43da Add method comment for opt::Function::WhileEachInst (#2867)
6b0721265 Use OpReturn* in wrap-opkill (#2886)
465312726 Fix to CMakeLists for spirv-fuzz tests (#2888)
7275a7165 Allow validation during spirv-fuzz replay (#2873)
4eee71e78 Disable long-running fuzzer tests by default (#2887)
61edde52a Revert "Use OpReturn* in wrap-opkill"
87f0fa432 Use OpReturn* in wrap-opkill
08fcf8a4a Fix header include syntax. (#2882)
248c80b04 Handle OpConstantNull in copy-prop-arrays. (#2870)
d06fe0848 Fix comment typo found by protobufs linter (#2884)
47e354b0c Move docs into docs/ folder (#2872)
605c2e3c0 Add WebGPU SPIR-V Assembler in JavaScript. (#2876)
acf20302c Android.mk: Add dependency from optimizer file to amd-shader-ballot-insts.inc (#2883)
c02a6191a Update SPIRV-Headers in DEPS (#2880)
e59b60de0  Fix detection of blocks bypassed by new edge (#2874)
ccd7bf167 Fix CMake issue related to spirv-fuzz (#2877)
0a07cd1c9 Add fuzzer pass to replace ids with synonyms (#2857)
bbb29870b Relaxed bitcast with pointers (#2878)
36c1c0e22 Fix Fuchsia build. (#2868)
99793fa67 Adding valilidation checks for OpEntryPoint duplicate names and execution mode (#2862)
932561935 Extra resource interface validation (#2864)
1e146e8a3 Split capability tests (#2866)
5a48c0da1 SPIRV-Tools support for SPIR-V 1.5 (#2865)
ad7f2c5c4 Add fuzzer pass to copy objects (#2853)
67b87f22c Handle another case where creating a constant can fail (#2854)
c7a39bc40 Don't inline function containing OpKill (#2842)
4f9256db3 Handle id overflow in wrap op kill. (#2851)
9f188e337 Assembler: Can't set an ID in instruction without result ID (#2852)
c0e980709 Handle creating a new constant failing gracefully (#2848)
e2e95172d Rework management of probabilities in spirv-fuzz (#2839)
7ee8f443e Fix add-dead-break and add-dead-continue passes to respect dominance (#2838)
35c9518c4 Handle id overflow in the ssa rewriter. (#2845)
7f7236f1e Handle id overflow in the constant manager. (#2844)
a464ac1a2 Add generic builtin validation of target (#2843)
6797173cf Don't register duplicate decoration in validator. (#2841)
76261e2a7 Replace CubeFaceCoord and CubeFaceIndexAMD (#2840)
b218ad199 Fold Min, Max, and Clamp instructions. (#2836)
a41520eaa Replace uses of SPV_AMD_shader_trinary_minmax extension (#2835)
19b256616 For WebGPU<->Vulkan optimization, set correct execution environment (#2834)
1dfb5fc12 Export SPIRV-Tools targets on installation (#2785)
635b583cf GN: Add Chromium GoogleTest deps. (#2832)
9b3cc3e05 Upadate CHANGES
c77045b4a Instrument: Be sure Float16 capability on when generating float16 null (#2831)
d11725b1d Add --relax-float-ops and --convert-relaxed-to-half (#2808)
1c9ca422d GN: Make SPIRV-Tools target use public_deps. (#2828)
b54d95029 Fold Fmix should accept vector operands. (#2826)
2c5ed16ba Fix end comments in header files (#2829)

git-subtree-dir: third_party/SPIRV-Tools
git-subtree-split: c3f22f7cba60e55d178b8184851d17697a996143
diff --git a/.gitignore b/.gitignore
index a965a99..196c63c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,6 +16,11 @@
 /testing
 /tools/clang/
 /utils/clang-format-diff.py
+bazel-bin
+bazel-genfiles
+bazel-out
+bazel-spirv-tools
+bazel-testlogs
 
 # Vim
 [._]*.s[a-w][a-z]
diff --git a/Android.mk b/Android.mk
index 9428116..cb7062c 100644
--- a/Android.mk
+++ b/Android.mk
@@ -49,7 +49,6 @@
 		source/val/validate_composites.cpp \
 		source/val/validate_constants.cpp \
 		source/val/validate_conversion.cpp \
-		source/val/validate_datarules.cpp \
 		source/val/validate_debug.cpp \
 		source/val/validate_decorations.cpp \
 		source/val/validate_derivatives.cpp \
@@ -89,6 +88,7 @@
 		source/opt/composite.cpp \
 		source/opt/const_folding_rules.cpp \
 		source/opt/constants.cpp \
+		source/opt/convert_to_half_pass.cpp \
 		source/opt/copy_prop_arrays.cpp \
 		source/opt/dead_branch_elim_pass.cpp \
 		source/opt/dead_insert_elim_pass.cpp \
@@ -130,7 +130,6 @@
 		source/opt/local_redundancy_elimination.cpp \
 		source/opt/local_single_block_elim_pass.cpp \
 		source/opt/local_single_store_elim_pass.cpp \
-		source/opt/local_ssa_elim_pass.cpp \
 		source/opt/loop_dependence.cpp \
 		source/opt/loop_dependence_helpers.cpp \
 		source/opt/loop_descriptor.cpp \
@@ -153,6 +152,7 @@
 		source/opt/reduce_load_size.cpp \
 		source/opt/redundancy_elimination.cpp \
 		source/opt/register_pressure.cpp \
+		source/opt/relax_float_ops_pass.cpp \
 		source/opt/remove_duplicates_pass.cpp \
 		source/opt/replace_invalid_opc.cpp \
 		source/opt/scalar_analysis.cpp \
@@ -249,6 +249,8 @@
 	$(1)/spv-amd-shader-ballot.insts.inc \
 	$(1)/spv-amd-shader-explicit-vertex-parameter.insts.inc \
 	$(1)/spv-amd-shader-trinary-minmax.insts.inc
+$(LOCAL_PATH)/source/opt/amd_ext_to_khr.cpp: \
+	$(1)/spv-amd-shader-ballot.insts.inc
 endef
 $(eval $(call gen_spvtools_grammar_tables,$(SPVTOOLS_OUT_PATH)))
 
diff --git a/BUILD.bazel b/BUILD.bazel
new file mode 100644
index 0000000..dc1ca60
--- /dev/null
+++ b/BUILD.bazel
@@ -0,0 +1,513 @@
+load(
+    ":build_defs.bzl",
+    "COMMON_COPTS",
+    "DEBUGINFO_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",
+    "opt_test",
+    "reduce_test",
+    "util_test",
+    "val_test",
+)
+
+package(
+    default_visibility = ["//visibility:private"],
+)
+
+licenses(["notice"])
+
+exports_files([
+    "CHANGES",
+    "LICENSE",
+])
+
+py_binary(
+    name = "generate_grammar_tables",
+    srcs = ["utils/generate_grammar_tables.py"],
+)
+
+py_binary(
+    name = "generate_language_headers",
+    srcs = ["utils/generate_language_headers.py"],
+)
+
+generate_core_tables("1.0")
+
+generate_core_tables("1.1")
+
+generate_core_tables("1.2")
+
+generate_core_tables("unified1")
+
+generate_enum_string_mapping("unified1")
+
+generate_opencl_tables("unified1")
+
+generate_glsl_tables("unified1")
+
+generate_vendor_tables("spv-amd-shader-explicit-vertex-parameter")
+
+generate_vendor_tables("spv-amd-shader-trinary-minmax")
+
+generate_vendor_tables("spv-amd-gcn-shader")
+
+generate_vendor_tables("spv-amd-shader-ballot")
+
+generate_vendor_tables("debuginfo")
+
+generate_extinst_lang_headers("DebugInfo", DEBUGINFO_GRAMMAR_JSON_FILE)
+
+py_binary(
+    name = "generate_registry_tables",
+    srcs = ["utils/generate_registry_tables.py"],
+)
+
+genrule(
+    name = "gen_registry_tables",
+    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)",
+    tools = [":generate_registry_tables"],
+)
+
+py_binary(
+    name = "update_build_version",
+    srcs = ["utils/update_build_version.py"],
+)
+
+genrule(
+    name = "gen_build_version",
+    srcs = ["CHANGES"],
+    outs = ["build-version.inc"],
+    cmd = "SOURCE_DATE_EPOCH=0 $(location update_build_version) $$(dirname $(location CHANGES)) $(location build-version.inc)",
+    tools = [":update_build_version"],
+)
+
+# Libraries
+
+cc_library(
+    name = "generated_headers",
+    hdrs = [
+        ":gen_build_version",
+        ":gen_core_tables_1.0",
+        ":gen_core_tables_1.1",
+        ":gen_core_tables_1.2",
+        ":gen_core_tables_unified1",
+        ":gen_enum_string_mapping",
+        ":gen_extinst_lang_headers_DebugInfo",
+        ":gen_glsl_tables_unified1",
+        ":gen_opencl_tables_unified1",
+        ":gen_registry_tables",
+        ":gen_vendor_tables_debuginfo",
+        ":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",
+    ],
+    copts = COMMON_COPTS,
+)
+
+cc_library(
+    name = "spirv_tools_headers",
+    hdrs = glob([
+        "include/spirv-tools/libspirv.h",
+        "include/spirv-tools/libspirv.hpp",
+        "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",
+    ],
+)
+
+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_headers//:spirv_common_headers",
+    ],
+)
+
+cc_library(
+    name = "spirv_tools_reduce",
+    srcs = glob(["source/reduce/*.cpp"]),
+    hdrs = glob(["source/reduce/*.h"]),
+    copts = COMMON_COPTS,
+    linkstatic = 1,
+    visibility = ["//visibility:public"],
+    deps = [
+        ":spirv_tools",
+        ":spirv_tools_opt",
+    ],
+)
+
+cc_library(
+    name = "spirv_tools_link",
+    srcs = glob(["source/link/*.cpp"]),
+    hdrs = ["include/spirv-tools/linker.hpp"],
+    copts = COMMON_COPTS,
+    linkstatic = 1,
+    visibility = ["//visibility:public"],
+    deps = [
+        ":spirv_tools",
+        ":spirv_tools_opt",
+    ],
+)
+
+cc_library(
+    name = "tools_util",
+    srcs = glob(["tools/util/*.cpp"]),
+    hdrs = glob(["tools/util/*.h"]),
+    copts = COMMON_COPTS,
+    linkstatic = 1,
+    visibility = ["//visibility:public"],
+    deps = [":spirv_tools"],
+)
+
+# Tools
+
+cc_binary(
+    name = "spirv-as",
+    srcs = [
+        "tools/as/as.cpp",
+        "tools/io.h",
+    ],
+    copts = COMMON_COPTS,
+    visibility = ["//visibility:public"],
+    deps = [
+        ":spirv_tools",
+    ],
+)
+
+cc_binary(
+    name = "spirv-dis",
+    srcs = [
+        "tools/dis/dis.cpp",
+        "tools/io.h",
+    ],
+    copts = COMMON_COPTS,
+    visibility = ["//visibility:public"],
+    deps = [
+        ":spirv_tools",
+    ],
+)
+
+cc_binary(
+    name = "spirv-val",
+    srcs = [
+        "tools/io.h",
+        "tools/val/val.cpp",
+    ],
+    copts = COMMON_COPTS,
+    visibility = ["//visibility:public"],
+    deps = [
+        ":spirv_tools",
+        ":tools_util",
+    ],
+)
+
+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",
+        ":tools_util",
+    ],
+)
+
+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_reduce",
+        ":tools_util",
+    ],
+)
+
+cc_binary(
+    name = "spirv-link",
+    srcs = [
+        "tools/io.h",
+        "tools/link/linker.cpp",
+    ],
+    copts = COMMON_COPTS,
+    visibility = ["//visibility:public"],
+    deps = [
+        ":spirv_tools",
+        ":spirv_tools_link",
+    ],
+)
+
+cc_binary(
+    name = "spirv-cfg",
+    srcs = [
+        "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"],
+)
+
+# Unit tests
+
+cc_library(
+    name = "test_common",
+    testonly = 1,
+    srcs = [
+        "test/test_fixture.h",
+        "test/unit_spirv.cpp",
+        "test/unit_spirv.h",
+    ],
+    compatible_with = [],
+    copts = TEST_COPTS,
+    includes = ["test"],
+    linkstatic = 1,
+    deps = [
+        ":spirv_tools",
+        "@com_google_googletest//:gtest",
+    ],
+)
+
+cc_library(
+    name = "link_test_common",
+    testonly = 1,
+    srcs = ["test/link/linker_fixture.h"],
+    compatible_with = [],
+    copts = TEST_COPTS,
+    linkstatic = 1,
+    deps = [
+        ":spirv_tools_link",
+        ":test_common",
+    ],
+)
+
+cc_library(
+    name = "opt_test_common",
+    testonly = 1,
+    srcs = ["test/opt/pass_utils.cpp"],
+    hdrs = [
+        "test/opt/assembly_builder.h",
+        "test/opt/function_utils.h",
+        "test/opt/module_utils.h",
+        "test/opt/pass_fixture.h",
+        "test/opt/pass_utils.h",
+    ],
+    compatible_with = [],
+    copts = TEST_COPTS,
+    linkstatic = 1,
+    deps = [
+        ":spirv_tools_opt",
+        ":test_common",
+    ],
+)
+
+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 = [],
+    copts = TEST_COPTS,
+    linkstatic = 1,
+    deps = [
+        ":spirv_tools_reduce",
+        ":test_common",
+    ],
+)
+
+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"],
+)]
+
+[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
+    srcs = [f],
+) 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"])]
+
+[util_test(
+    name = f[10:-4],  # strip test/util/, .cpp
+    srcs = [f],
+) for f in glob(["test/util/*.cpp"])]
+
+[val_test(
+    name = f[9:-4],  # strip test/val/, .cpp
+    srcs = [f],
+) for f in glob(
+    ["test/val/*.cpp"],
+    exclude = [
+        "test/val/pch_test_val.cpp", # pch tests are skipped.
+    ],
+)]
+
diff --git a/BUILD.gn b/BUILD.gn
index d62aaab..5c8eb9a 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -288,10 +288,21 @@
   configs = [ ":spvtools_public_config" ]
 
   if (is_clang) {
-    cflags = [ "-Wno-implicit-fallthrough" ]
+    cflags = [
+      "-Wno-implicit-fallthrough",
+      "-Wno-newline-eof",
+    ]
   }
 }
 
+source_set("spv_headers") {
+  sources = [
+    "$spirv_headers/include/spirv/1.2/GLSL.std.450.h",
+    "$spirv_headers/include/spirv/unified1/OpenCL.std.h",
+    "$spirv_headers/include/spirv/unified1/spirv.h",
+  ]
+}
+
 source_set("spvtools_headers") {
   sources = [
     "include/spirv-tools/instrument.hpp",
@@ -321,16 +332,22 @@
     "source/assembly_grammar.h",
     "source/binary.cpp",
     "source/binary.h",
+    "source/cfa.h",
     "source/diagnostic.cpp",
     "source/diagnostic.h",
     "source/disassemble.cpp",
+    "source/disassemble.h",
     "source/enum_set.h",
     "source/enum_string_mapping.cpp",
+    "source/enum_string_mapping.h",
     "source/ext_inst.cpp",
     "source/ext_inst.h",
     "source/extensions.cpp",
     "source/extensions.h",
     "source/instruction.h",
+    "source/latest_version_glsl_std_450_header.h",
+    "source/latest_version_opencl_std_header.h",
+    "source/latest_version_spirv_header.h",
     "source/libspirv.cpp",
     "source/macro.h",
     "source/name_mapper.cpp",
@@ -376,6 +393,7 @@
   ]
 
   public_deps = [
+    ":spv_headers",
     ":spvtools_core_enums_unified1",
     ":spvtools_headers",
   ]
@@ -390,8 +408,12 @@
 static_library("spvtools_val") {
   sources = [
     "source/val/basic_block.cpp",
+    "source/val/basic_block.h",
     "source/val/construct.cpp",
+    "source/val/construct.h",
+    "source/val/decoration.h",
     "source/val/function.cpp",
+    "source/val/function.h",
     "source/val/instruction.cpp",
     "source/val/validate.cpp",
     "source/val/validate.h",
@@ -407,7 +429,6 @@
     "source/val/validate_composites.cpp",
     "source/val/validate_constants.cpp",
     "source/val/validate_conversion.cpp",
-    "source/val/validate_datarules.cpp",
     "source/val/validate_debug.cpp",
     "source/val/validate_decorations.cpp",
     "source/val/validate_derivatives.cpp",
@@ -423,14 +444,17 @@
     "source/val/validate_logicals.cpp",
     "source/val/validate_memory.cpp",
     "source/val/validate_memory_semantics.cpp",
+    "source/val/validate_memory_semantics.h",
     "source/val/validate_misc.cpp",
     "source/val/validate_mode_setting.cpp",
     "source/val/validate_non_uniform.cpp",
     "source/val/validate_primitives.cpp",
     "source/val/validate_scopes.cpp",
+    "source/val/validate_scopes.h",
     "source/val/validate_small_type_uses.cpp",
     "source/val/validate_type.cpp",
     "source/val/validation_state.cpp",
+    "source/val/validation_state.h",
   ]
 
   deps = [
@@ -479,6 +503,8 @@
     "source/opt/const_folding_rules.h",
     "source/opt/constants.cpp",
     "source/opt/constants.h",
+    "source/opt/convert_to_half_pass.cpp",
+    "source/opt/convert_to_half_pass.h",
     "source/opt/copy_prop_arrays.cpp",
     "source/opt/copy_prop_arrays.h",
     "source/opt/dead_branch_elim_pass.cpp",
@@ -563,8 +589,6 @@
     "source/opt/local_single_block_elim_pass.h",
     "source/opt/local_single_store_elim_pass.cpp",
     "source/opt/local_single_store_elim_pass.h",
-    "source/opt/local_ssa_elim_pass.cpp",
-    "source/opt/local_ssa_elim_pass.h",
     "source/opt/log.h",
     "source/opt/loop_dependence.cpp",
     "source/opt/loop_dependence.h",
@@ -611,6 +635,8 @@
     "source/opt/reflect.h",
     "source/opt/register_pressure.cpp",
     "source/opt/register_pressure.h",
+    "source/opt/relax_float_ops_pass.cpp",
+    "source/opt/relax_float_ops_pass.h",
     "source/opt/remove_duplicates_pass.cpp",
     "source/opt/remove_duplicates_pass.h",
     "source/opt/replace_invalid_opc.cpp",
@@ -716,6 +742,7 @@
     "source/reduce/reducer.h",
     "source/reduce/reduction_opportunity.cpp",
     "source/reduce/reduction_opportunity.h",
+    "source/reduce/reduction_opportunity_finder.h",
     "source/reduce/reduction_pass.cpp",
     "source/reduce/reduction_pass.h",
     "source/reduce/reduction_util.cpp",
@@ -730,10 +757,6 @@
     "source/reduce/remove_function_reduction_opportunity_finder.h",
     "source/reduce/remove_instruction_reduction_opportunity.cpp",
     "source/reduce/remove_instruction_reduction_opportunity.h",
-    "source/reduce/remove_opname_instruction_reduction_opportunity_finder.cpp",
-    "source/reduce/remove_opname_instruction_reduction_opportunity_finder.h",
-    "source/reduce/remove_relaxed_precision_decoration_opportunity_finder.cpp",
-    "source/reduce/remove_relaxed_precision_decoration_opportunity_finder.h",
     "source/reduce/remove_selection_reduction_opportunity.cpp",
     "source/reduce/remove_selection_reduction_opportunity.h",
     "source/reduce/remove_selection_reduction_opportunity_finder.cpp",
@@ -766,7 +789,7 @@
 }
 
 group("SPIRV-Tools") {
-  deps = [
+  public_deps = [
     ":spvtools",
     ":spvtools_link",
     ":spvtools_opt",
@@ -846,6 +869,8 @@
       "//testing/gmock",
       "//testing/gtest",
       "//testing/gtest:gtest_main",
+      "//third_party/googletest:gmock",
+      "//third_party/googletest:gtest",
     ]
 
     if (is_clang) {
@@ -870,6 +895,9 @@
     "tools/util/cli_consumer.cpp",
     "tools/util/cli_consumer.h",
   ]
+  deps = [
+    ":spvtools_headers",
+  ]
   configs += [ ":spvtools_internal_config" ]
 }
 
diff --git a/CHANGES b/CHANGES
index 57afc63..5263f12 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,7 +1,69 @@
 Revision history for SPIRV-Tools
 
-v2019.5-dev 2019-08-08
- - Start v2019.5-dev
+v2019.5-dev 2019-10-21
+ - General:
+   - Export SPIRV-Tools targets on installation
+   - SPIRV-Tools support for SPIR-V 1.5 (#2865)
+   - Add WebGPU SPIR-V Assembler in JavaScript. (#2876)
+   - Add Bazel build configuration. (#2891)
+   - Add support for building with emscripten (#2948)
+   - Update SPIR-V binary header test for SPIR-V 1.5 (#2967)
+   - Add fuzzer for spirv-as call path (#2976)
+   - Improved CMake install step. (#2963)
+   - Add fuzzer for spirv-dis call path (#2977)
+   - Ensure timestamp does not vary with timezone. (#2982)
+ - Optimizer
+   - Add descriptor array scalar replacement (#2742)
+   - Add pass to wrap OpKill in a function call (#2790)
+   - Fold FMix during constant folding. (#2818)
+   - Add pass to replace AMD shader ballot extension (#2811)
+   - Add pass to make Float32 operation relax precision (#2808)
+   - Add pass to make relax precision operation Float16 (#2808)
+   - Add pass to replace uses of 3 AMD extensions (#2814)
+   - Fold Min, Max, and Clamp instructions. (#2836)
+   - Better handling of OpKill in continues (#2842,#2922,#2933)
+   - Enable OpTypeCooperativeMatrix specialization (#2927)
+   - Support constant-folding UConvert and SConvert (#2960)
+   Fixes:
+   - Instrument: Fix version 2 output record write for tess eval shaders. (#2782)
+   - Instrument: Add support for Buffer Device Address extension (#2792)
+   - Fix check for changed binary in API call. (#2798)
+   - For WebGPU<->Vulkan optimization, set correct execution environment (#2834)
+   - Handle OpConstantNull in copy-prop-arrays. (#2870)
+   - Use OpReturn* in wrap-opkill (#2886)
+ - Validator
+   - Add generic builtin validation of target (#2843)
+   - Extra resource interface validation (#2864)
+   - Adding valilidation checks for OpEntryPoint duplicate names and execution mode (#2862)
+   - Relaxed bitcast with pointers (#2878)
+   - Validate physical storage buffer restrictions (#2930)
+   - Add SPV_KHR_shader_clock validation (#2879)
+   - Validate that selections are structured (#2962)
+   - Disallow use of OpCompositeExtract/OpCompositeInsert with no indices (#2980)
+   - Check that derivatives operate on 32-bit values (#2983)
+   Fixes:
+   - Fix validation of constant matrices (#2794)
+   - Update "remquor" validation
+   - Only allow previously declared forward refs in structs (#2920)
+ - Reduce
+   - Remove relaxed precision decorations (#2797)
+   - Reduce/fuzz: improve command line args (#2932)
+   - Improve remove unref instr pass (#2945)
+   Fixes:
+ - Fuzz
+   - Fix add-dead-break and add-dead-continue passes to respect dominance (#2838)
+   - Add fuzzer pass to copy objects (#2853)
+   - Add fuzzer pass to replace ids with synonyms (#2857)
+   - Allow validation during spirv-fuzz replay (#2873)
+   - Employ the "swarm testing" idea in spirv-fuzz (#2890)
+   - reduce/fuzz: improve command line args (#2932)
+   - option to convert shader into a form that renders red (#2934)
+   - Add fuzzer pass to change selection controls (#2944)
+   - add transformation and pass to construct composites (#2941)
+   - Add fuzzer pass to change loop controls (#2949)
+   - Add fuzzer pass to change function controls (#2951)
+   - Add fuzzer pass to add NoContraction decorations (#2950)
+
 
 v2019.4 2019-08-08
  - General:
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b95b714..19108f3 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -39,12 +39,16 @@
 if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
   add_definitions(-DSPIRV_LINUX)
   set(SPIRV_TIMER_ENABLED ${SPIRV_ALLOW_TIMERS})
+elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Emscripten")
+    add_definitions(-DSPIRV_EMSCRIPTEN)
 elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Windows")
   add_definitions(-DSPIRV_WINDOWS)
 elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "CYGWIN")
   add_definitions(-DSPIRV_WINDOWS)
 elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin")
   add_definitions(-DSPIRV_MAC)
+elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "iOS")
+  add_definitions(-DSPIRV_IOS)
 elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Android")
   add_definitions(-DSPIRV_ANDROID)
   set(SPIRV_TIMER_ENABLED ${SPIRV_ALLOW_TIMERS})
@@ -204,6 +208,27 @@
   endmacro()
 endif()
 
+if(ENABLE_SPIRV_TOOLS_INSTALL)
+  if(WIN32)
+    macro(spvtools_config_package_dir TARGET PATH)
+      set(${PATH} ${TARGET}/cmake)
+    endmacro()
+  else()
+    macro(spvtools_config_package_dir TARGET PATH)
+      set(${PATH} lib/cmake/${TARGET})
+    endmacro()
+  endif()
+
+  macro(spvtools_generate_config_file TARGET)
+    file(WRITE ${CMAKE_BINARY_DIR}/${TARGET}Config.cmake
+      "include(CMakeFindDependencyMacro)\n"
+      "find_dependency(${SPIRV_TOOLS})\n"
+      "include(\${CMAKE_CURRENT_LIST_DIR}/${TARGET}Targets.cmake)\n"
+      "set(${TARGET}_LIBRARIES ${TARGET})\n"
+      "get_target_property(${TARGET}_INCLUDE_DIRS ${TARGET} INTERFACE_INCLUDE_DIRECTORIES)\n")
+  endmacro()
+endif()
+
 # Defaults to OFF if the user didn't set it.
 option(SPIRV_SKIP_EXECUTABLES
   "Skip building the executable and tests along with the library"
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 93a5610..b46ae31 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -4,7 +4,7 @@
 
 We organize known future work in GitHub projects. See [Tracking SPIRV-Tools work
 with GitHub
-projects](https://github.com/KhronosGroup/SPIRV-Tools/blob/master/projects.md)
+projects](https://github.com/KhronosGroup/SPIRV-Tools/blob/master/docs/projects.md)
 for more.
 
 To report a new bug or request a new feature, please file a GitHub issue. Please
diff --git a/DEPS b/DEPS
index 70685ad..311b7ed 100644
--- a/DEPS
+++ b/DEPS
@@ -3,10 +3,10 @@
 vars = {
   'github': 'https://github.com',
 
-  'effcee_revision': 'b83b58d177b797edd1f94c5f10837f2cc2863f0a',
-  'googletest_revision': '2f42d769ad1b08742f7ccb5ad4dd357fc5ff248c',
-  're2_revision': 'e356bd3f80e0c15c1050323bb5a2d0f8ea4845f4',
-  'spirv_headers_revision': '123dc278f204f8e833e1a88d31c46d0edf81d4b2',
+  'effcee_revision': 'cd25ec17e9382f99a895b9ef53ff3c277464d07d',
+  'googletest_revision': 'f2fb48c3b3d79a75a88a99fba6576b25d42ec528',
+  're2_revision': '5bd613749fd530b576b890283bfb6bc6ea6246cb',
+  'spirv_headers_revision': 'af64a9e826bf5bb5fcd2434dd71be1e41e922563',
 }
 
 deps = {
diff --git a/README.md b/README.md
index 8a5c85e..5714976 100644
--- a/README.md
+++ b/README.md
@@ -26,14 +26,14 @@
 <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)
 
-[More downloads](downloads.md)
+[More downloads](docs/downloads.md)
 
 ## Versioning SPIRV-Tools
 
 See [`CHANGES`](CHANGES) for a high level summary of recent changes, by version.
 
 SPIRV-Tools project version numbers are of the form `v`*year*`.`*index* and with
-an optional `-dev` suffix to indicate work in progress.  For exampe, the
+an optional `-dev` suffix to indicate work in progress.  For example, the
 following versions are ordered from oldest to newest:
 
 * `v2016.0`
@@ -49,9 +49,11 @@
 
 ### Assembler, binary parser, and disassembler
 
-* Support for SPIR-V 1.0, 1.1, 1.2, and 1.3
+* Support for SPIR-V 1.0, through 1.5
   * Based on SPIR-V syntax described by JSON grammar files in the
     [SPIRV-Headers](https://github.com/KhronosGroup/SPIRV-Headers) repository.
+  * Usually, support for a new version of SPIR-V is ready within days after
+    publication.
 * Support for extended instruction sets:
   * GLSL std450 version 1.0 Rev 3
   * OpenCL version 1.0 Rev 2
@@ -59,7 +61,7 @@
   IDs or types is performed, except to check literal arguments to
   `OpConstant`, `OpSpecConstant`, and `OpSwitch`.
 
-See [`syntax.md`](syntax.md) for the assembly language syntax.
+See [`docs/syntax.md`](docs/syntax.md) for the assembly language syntax.
 
 ### Validator
 
@@ -88,14 +90,22 @@
 
 ### Optimizer
 
-*Note:* The optimizer is still under development.
+The optimizer is a collection of code transforms, or "passes".
+Transforms are written for a diverse set of reasons:
 
-Currently supported optimizations:
-* General
+* To restructure, simplify, or normalize the code for further processing.
+* To eliminate undesirable code.
+* To improve code quality in some metric such as size or performance.
+  **Note**: These transforms are not guaranteed to actually improve any
+  given metric. Users should always measure results for their own situation.
+
+As of this writing, there are 67 transforms including examples such as:
+* Simplification
   * Strip debug info
+  * Strip reflection info
 * Specialization Constants
   * Set spec constant default value
-  * Freeze spec constant
+  * Freeze spec constant to default value
   * Fold `OpSpecConstantOp` and `OpSpecConstantComposite`
   * Unify constants
   * Eliminate dead constant
@@ -112,6 +122,29 @@
   * Eliminate common uniform loads
   * Remove duplicates: Capabilities, extended instruction imports, types, and
     decorations.
+* Normalization
+  * Compact IDs
+  * CFG cleanup
+  * Flatten decorations
+  * Merge returns
+  * Convert AMD-specific instructions to KHR instructions
+* Code improvement
+  * Conditional constant propagation
+  * If-conversion
+  * Loop fission
+  * Loop fusion
+  * Loop-invariant code motion
+  * Loop unroll
+* Other
+  * Generate WebGPU initializers
+  * Graphics robust access
+  * Upgrade memory model to VulkanKHR
+
+Additionally, certain sets of transformations have been packaged into
+higher-level recipes.  These include:
+
+* Optimization for size (`spirv-opt -Os`)
+* Optimization for performance (`spirv-opt -O`)
 
 For the latest list with detailed documentation, please refer to
 [`include/spirv-tools/optimizer.hpp`](include/spirv-tools/optimizer.hpp).
@@ -149,6 +182,24 @@
 "Reducer:" as the start of its title.
 
 
+### Fuzzer
+
+*Note:* The fuzzer is still under development.
+
+The fuzzer applies semantics-preserving transformations to a SPIR-V binary
+module, to produce an equivalent module.  The original and transformed modules
+should produce essentially identical results when executed on identical inputs:
+their results should differ only due to floating-point round-off, if at all.
+Significant differences in results can pinpoint bugs in tools that process
+SPIR-V binaries, such as miscompilations.  This *metamorphic testing* approach
+is similar to the method used by the [GraphicsFuzz
+project](https://github.com/google/graphicsfuzz) for fuzzing of GLSL shaders.
+
+To suggest an additional capability for the fuzzer, [file an
+issue](https://github.com/KhronosGroup/SPIRV-Tools/issues]) with
+"Fuzzer:" as the start of its title.
+
+
 ### Extras
 
 * [Utility filters](#utility-filters)
@@ -171,7 +222,7 @@
 (To provide feedback on the SPIR-V _specification_, file an issue on the
 [SPIRV-Headers][spirv-headers] GitHub repository.)
 
-See [`projects.md`](projects.md) to see how we use the
+See [`docs/projects.md`](docs/projects.md) to see how we use the
 [GitHub Project
 feature](https://help.github.com/articles/tracking-the-progress-of-your-work-with-projects/)
 to organize planned and in-progress work.
@@ -255,9 +306,9 @@
 testing and they always reflect the current top of the tree of the master
 branch.
 
-The project uses [CMake][cmake] to generate platform-specific build
-configurations. Assume that `<spirv-dir>` is the root directory of the checked
-out code:
+In order to build the code, you first need to sync the external repositories
+that it depends on. Assume that `<spirv-dir>` is the root directory of the
+checked out code:
 
 ```sh
 cd <spirv-dir>
@@ -266,28 +317,46 @@
 git clone https://github.com/google/re2.git external/re2
 git clone https://github.com/google/googletest.git external/googletest # optional
 
-mkdir build && cd build
-cmake [-G <platform-generator>] <spirv-dir>
 ```
 
 *Note*:
 The script `utils/git-sync-deps` can be used to checkout and/or update the
 contents of the repos under `external/` instead of manually maintaining them.
 
+### Build using CMake
+You can build The project using [CMake][cmake] to generate platform-specific
+build configurations.
+
+```sh
+cd <spirv-dir>
+mkdir build && cd build
+cmake [-G <platform-generator>] <spirv-dir>
+```
+
 Once the build files have been generated, build using your preferred
 development environment.
 
+### Build using Bazel
+You can also use [Bazel](https://bazel.build/) to build the project.
+```sh
+cd <spirv-dir>
+bazel build :all
+```
+
 ### Tools you'll need
 
 For building and testing SPIRV-Tools, the following tools should be
 installed regardless of your OS:
 
-- [CMake](http://www.cmake.org/): for generating compilation targets.  Version
-  2.8.12 or later.
+- [CMake](http://www.cmake.org/): if using CMake for generating compilation
+targets, you need to install CMake Version 2.8.12 or later.
 - [Python 3](http://www.python.org/): for utility scripts and running the test
 suite.
+- [Bazel](https://bazel.build/) (optional): if building the source with Bazel,
+you need to install Bazel Version 0.29.1 on your machine. Other versions may
+also work, but are not verified.
 
-SPIRV-Tools is regularly tested with the the following compilers:
+SPIRV-Tools is regularly tested with the following compilers:
 
 On Linux
 - GCC version 4.8.5
@@ -306,6 +375,7 @@
 
 The following CMake options are supported:
 
+* `SPIRV_BUILD_FUZZER={ON|OFF}`, default `OFF` - Build the spirv-fuzz tool.
 * `SPIRV_COLOR_TERMINAL={ON|OFF}`, default `ON` - Enables color console output.
 * `SPIRV_SKIP_TESTS={ON|OFF}`, default `OFF`- Build only the library and
   the command line tools.  This will prevent the tests from being built.
@@ -482,6 +552,19 @@
 
 Run `spirv-reduce --help` to see how to specify interestingness.
 
+### Fuzzer tool
+
+The fuzzer transforms a SPIR-V binary module into a semantically-equivalent
+SPIR-V binary module by applying transformations in a randomized fashion.
+
+This is a work in progress, with initially only a few semantics-preserving
+transformations.
+
+* `spirv-fuzz` - the standalone fuzzer
+  * `<spirv-dir>/tools/fuzz`
+
+Run `spirv-fuzz --help` for a detailed list of options.
+
 ### Control flow dumper tool
 
 The control flow dumper prints the control flow graph for a SPIR-V module as a
diff --git a/WORKSPACE b/WORKSPACE
new file mode 100644
index 0000000..5abfc98
--- /dev/null
+++ b/WORKSPACE
@@ -0,0 +1,19 @@
+local_repository(
+    name = "spirv_headers",
+    path = "external/spirv-headers",
+)
+
+local_repository(
+    name = "com_google_googletest",
+    path = "external/googletest",
+)
+
+local_repository(
+    name = "com_googlesource_code_re2",
+    path = "external/re2",
+)
+
+local_repository(
+    name = "com_google_effcee",
+    path = "external/effcee",
+)
diff --git a/build_defs.bzl b/build_defs.bzl
new file mode 100644
index 0000000..483fd2a
--- /dev/null
+++ b/build_defs.bzl
@@ -0,0 +1,284 @@
+COMMON_COPTS = [
+        "-DSPIRV_CHECK_CONTEXT",
+        "-DSPIRV_COLOR_TERMINAL",
+    ] + select({
+    "@bazel_tools//src/conditions:windows": [""],
+    "//conditions:default": [
+        "-DSPIRV_LINUX",
+        "-DSPIRV_TIMER_ENABLED",
+        "-Wall",
+        "-Wextra",
+        "-Wnon-virtual-dtor",
+        "-Wno-missing-field-initializers",
+        "-Werror",
+        "-std=c++11",
+        "-fvisibility=hidden",
+        "-fno-exceptions",
+        "-fno-rtti",
+        "-Wno-long-long",
+        "-Wshadow",
+        "-Wundef",
+        "-Wconversion",
+        "-Wno-sign-conversion",
+    ],
+})
+
+TEST_COPTS = COMMON_COPTS + select({
+    "@bazel_tools//src/conditions: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"
+    ],
+    "//conditions:default": [
+        "-Wno-undef",
+        "-Wno-self-assign",
+        "-Wno-shadow",
+        "-Wno-unused-parameter"
+    ],
+})
+
+DEBUGINFO_GRAMMAR_JSON_FILE = "source/extinst.debuginfo.grammar.json"
+
+def generate_core_tables(version = None):
+    if not version:
+        fail("Must specify version", "version")
+    grammars = [
+        "@spirv_headers//:spirv_core_grammar_" + version,
+        DEBUGINFO_GRAMMAR_JSON_FILE,
+    ]
+    outs = [
+        "core.insts-{}.inc".format(version),
+        "operand.kinds-{}.inc".format(version),
+    ]
+    fmtargs = 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}) " +
+            "--core-insts-output=$(location {2}) " +
+            "--operand-kinds-output=$(location {3})"
+        ).format(*fmtargs),
+        tools = [":generate_grammar_tables"],
+        visibility = ["//visibility:private"],
+    )
+
+def generate_enum_string_mapping(version = None):
+    if not version:
+        fail("Must specify version", "version")
+    grammars = [
+        "@spirv_headers//:spirv_core_grammar_" + version,
+        DEBUGINFO_GRAMMAR_JSON_FILE,
+    ]
+    outs = [
+        "extension_enum.inc",
+        "enum_string_mapping.inc",
+    ]
+    fmtargs = 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}) " +
+            "--extension-enum-output=$(location {2}) " +
+            "--enum-string-mapping-output=$(location {3})"
+        ).format(*fmtargs),
+        tools = [":generate_grammar_tables"],
+        visibility = ["//visibility:private"],
+    )
+
+def generate_opencl_tables(version = None):
+    if not version:
+        fail("Must specify version", "version")
+    grammars = [
+        "@spirv_headers//:spirv_opencl_grammar_" + version,
+    ]
+    outs = ["opencl.std.insts.inc"]
+    fmtargs = 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),
+        tools = [":generate_grammar_tables"],
+        visibility = ["//visibility:private"],
+    )
+
+def generate_glsl_tables(version = None):
+    if not version:
+        fail("Must specify version", "version")
+    grammars = [
+        "@spirv_headers//:spirv_glsl_grammar_" + version,
+    ]
+    outs = ["glsl.std.450.insts.inc"]
+    fmtargs = 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),
+        tools = [":generate_grammar_tables"],
+        visibility = ["//visibility:private"],
+    )
+
+def generate_vendor_tables(extension = None):
+    if not extension:
+        fail("Must specify extension", "extension")
+    extension_rule = extension.replace("-", "_")
+    grammars = ["source/extinst.{}.grammar.json".format(extension)]
+    outs = ["{}.insts.inc".format(extension)]
+    fmtargs = 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})"
+        ).format(*fmtargs),
+        tools = [":generate_grammar_tables"],
+        visibility = ["//visibility:private"],
+    )
+
+def generate_extinst_lang_headers(name, grammar = None):
+    if not grammar:
+        fail("Must specify grammar", "grammar")
+    fmtargs = [name]
+    native.genrule(
+        name = "gen_extinst_lang_headers_" + name,
+        srcs = [grammar],
+        outs = [name + ".h"],
+        cmd = (
+            "$(location :generate_language_headers) " +
+            "--extinst-name={0} " +
+            "--extinst-grammar=$< " +
+            "--extinst-output-base=$(@D)/{0}"
+        ).format(*fmtargs),
+        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 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/downloads.md b/docs/downloads.md
similarity index 100%
rename from downloads.md
rename to docs/downloads.md
diff --git a/projects.md b/docs/projects.md
similarity index 100%
rename from projects.md
rename to docs/projects.md
diff --git a/syntax.md b/docs/syntax.md
similarity index 100%
rename from syntax.md
rename to docs/syntax.md
diff --git a/include/spirv-tools/libspirv.h b/include/spirv-tools/libspirv.h
index e21b058..dd2526b 100644
--- a/include/spirv-tools/libspirv.h
+++ b/include/spirv-tools/libspirv.h
@@ -433,6 +433,7 @@
   SPV_ENV_WEBGPU_0,       // Work in progress WebGPU 1.0.
   SPV_ENV_UNIVERSAL_1_4,  // SPIR-V 1.4 latest revision, no other restrictions.
   SPV_ENV_VULKAN_1_1_SPIRV_1_4,  // Vulkan 1.1 with SPIR-V 1.4 binary.
+  SPV_ENV_UNIVERSAL_1_5,  // SPIR-V 1.5 latest revision, no other restrictions.
 } spv_target_env;
 
 // SPIR-V Validator can be parameterized with the following Universal Limits.
@@ -611,6 +612,11 @@
 // Destroys the given fuzzer options object.
 SPIRV_TOOLS_EXPORT void spvFuzzerOptionsDestroy(spv_fuzzer_options options);
 
+// Enables running the validator after every transformation is applied during
+// a replay.
+SPIRV_TOOLS_EXPORT void spvFuzzerOptionsEnableReplayValidation(
+    spv_fuzzer_options options);
+
 // Sets the seed with which the random number generator used by the fuzzer
 // should be initialized.
 SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetRandomSeed(
diff --git a/include/spirv-tools/libspirv.hpp b/include/spirv-tools/libspirv.hpp
index 2da1152..b09fdd4 100644
--- a/include/spirv-tools/libspirv.hpp
+++ b/include/spirv-tools/libspirv.hpp
@@ -214,6 +214,11 @@
     return options_;
   }
 
+  // See spvFuzzerOptionsEnableReplayValidation.
+  void enable_replay_validation() {
+    spvFuzzerOptionsEnableReplayValidation(options_);
+  }
+
   // See spvFuzzerOptionsSetRandomSeed.
   void set_random_seed(uint32_t seed) {
     spvFuzzerOptionsSetRandomSeed(options_, seed);
diff --git a/include/spirv-tools/optimizer.hpp b/include/spirv-tools/optimizer.hpp
index 4e54b1a..509051d 100644
--- a/include/spirv-tools/optimizer.hpp
+++ b/include/spirv-tools/optimizer.hpp
@@ -68,6 +68,11 @@
   // The constructed instance will have an empty message consumer, which just
   // ignores all messages from the library. Use SetMessageConsumer() to supply
   // one if messages are of concern.
+  //
+  // For collections of passes that are meant to transform the input into
+  // another execution environment, then the source environment should be
+  // supplied. e.g. for VulkanToWebGPUPasses the environment should be
+  // SPV_ENV_VULKAN_1_1 not SPV_ENV_WEBGPU_0.
   explicit Optimizer(spv_target_env env);
 
   // Disables copy/move constructor/assignment operations.
@@ -674,6 +679,22 @@
 // processed (see IsSSATargetVar for details).
 Optimizer::PassToken CreateSSARewritePass();
 
+// Create pass to convert relaxed precision instructions to half precision.
+// This pass converts as many relaxed float32 arithmetic operations to half as
+// possible. It converts any float32 operands to half if needed. It converts
+// any resulting half precision values back to float32 as needed. No variables
+// are changed. No image operations are changed.
+//
+// Best if run late since it will generate better code with unneeded function
+// scope loads and stores and composite inserts and extracts removed. Also best
+// if followed by instruction simplification, redundancy elimination and DCE.
+Optimizer::PassToken CreateConvertRelaxedToHalfPass();
+
+// Create relax float ops pass.
+// This pass decorates all float32 result instructions with RelaxedPrecision
+// if not already so decorated.
+Optimizer::PassToken CreateRelaxFloatOpsPass();
+
 // Create copy propagate arrays pass.
 // This pass looks to copy propagate memory references for arrays.  It looks
 // for specific code patterns to recognize array copies.
diff --git a/kokoro/linux-clang-release-bazel/build.sh b/kokoro/linux-clang-release-bazel/build.sh
new file mode 100644
index 0000000..cc38bd4
--- /dev/null
+++ b/kokoro/linux-clang-release-bazel/build.sh
@@ -0,0 +1,42 @@
+#!/bin/bash
+# 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.
+#
+# Linux Build Script.
+
+# Fail on any error.
+set -e
+# Display commands being run.
+set -x
+
+CC=clang
+CXX=clang++
+SRC=$PWD/github/SPIRV-Tools
+
+cd $SRC
+git clone --depth=1 https://github.com/KhronosGroup/SPIRV-Headers external/spirv-headers
+git clone --depth=1 https://github.com/google/googletest          external/googletest
+git clone --depth=1 https://github.com/google/effcee              external/effcee
+git clone --depth=1 https://github.com/google/re2                 external/re2
+
+gsutil cp gs://bazel/0.29.1/release/bazel-0.29.1-linux-x86_64 .
+chmod +x bazel-0.29.1-linux-x86_64
+
+echo $(date): Build everything...
+./bazel-0.29.1-linux-x86_64 build :all
+echo $(date): Build completed.
+
+echo $(date): Starting bazel test...
+./bazel-0.29.1-linux-x86_64 test :all
+echo $(date): Bazel test completed.
diff --git a/kokoro/linux-clang-release-bazel/continuous.cfg b/kokoro/linux-clang-release-bazel/continuous.cfg
new file mode 100644
index 0000000..8b33a24
--- /dev/null
+++ b/kokoro/linux-clang-release-bazel/continuous.cfg
@@ -0,0 +1,16 @@
+# 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/linux-clang-release-bazel/build.sh"
diff --git a/kokoro/linux-clang-release-bazel/presubmit.cfg b/kokoro/linux-clang-release-bazel/presubmit.cfg
new file mode 100644
index 0000000..c7a2e6f
--- /dev/null
+++ b/kokoro/linux-clang-release-bazel/presubmit.cfg
@@ -0,0 +1,16 @@
+# 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/linux-clang-release-bazel/build.sh"
diff --git a/kokoro/macos-clang-release-bazel/build.sh b/kokoro/macos-clang-release-bazel/build.sh
new file mode 100644
index 0000000..e92fa74
--- /dev/null
+++ b/kokoro/macos-clang-release-bazel/build.sh
@@ -0,0 +1,43 @@
+#!/bin/bash
+# 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.
+#
+# Linux Build Script.
+
+# Fail on any error.
+set -e
+# Display commands being run.
+set -x
+
+CC=clang
+CXX=clang++
+SRC=$PWD/github/SPIRV-Tools
+
+cd $SRC
+git clone --depth=1 https://github.com/KhronosGroup/SPIRV-Headers external/spirv-headers
+git clone --depth=1 https://github.com/google/googletest          external/googletest
+git clone --depth=1 https://github.com/google/effcee              external/effcee
+git clone --depth=1 https://github.com/google/re2                 external/re2
+
+# Get bazel 0.29.1.
+gsutil cp gs://bazel/0.29.1/release/bazel-0.29.1-darwin-x86_64 .
+chmod +x bazel-0.29.1-darwin-x86_64
+
+echo $(date): Build everything...
+./bazel-0.29.1-darwin-x86_64 build :all
+echo $(date): Build completed.
+
+echo $(date): Starting bazel test...
+./bazel-0.29.1-darwin-x86_64 test :all
+echo $(date): Bazel test completed.
diff --git a/kokoro/macos-clang-release-bazel/continuous.cfg b/kokoro/macos-clang-release-bazel/continuous.cfg
new file mode 100644
index 0000000..9765489
--- /dev/null
+++ b/kokoro/macos-clang-release-bazel/continuous.cfg
@@ -0,0 +1,16 @@
+# 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/macos-clang-release-bazel/build.sh"
diff --git a/kokoro/macos-clang-release-bazel/presubmit.cfg b/kokoro/macos-clang-release-bazel/presubmit.cfg
new file mode 100644
index 0000000..3b13602
--- /dev/null
+++ b/kokoro/macos-clang-release-bazel/presubmit.cfg
@@ -0,0 +1,16 @@
+# 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/macos-clang-release-bazel/build.sh"
diff --git a/kokoro/windows-msvc-2015-release-bazel/build.bat b/kokoro/windows-msvc-2015-release-bazel/build.bat
new file mode 100644
index 0000000..ddb4f54
--- /dev/null
+++ b/kokoro/windows-msvc-2015-release-bazel/build.bat
@@ -0,0 +1,59 @@
+:: 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 --depth=1 https://github.com/google/googletest          external/googletest
+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/0.29.1/bazel-0.29.1-windows-x86_64.zip
+unzip -q bazel-0.29.1-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_SH=c:\tools\msys64\usr\bin\bash.exe
+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
new file mode 100644
index 0000000..f72cf05
--- /dev/null
+++ b/kokoro/windows-msvc-2015-release-bazel/continuous.cfg
@@ -0,0 +1,16 @@
+# 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
new file mode 100644
index 0000000..148972c
--- /dev/null
+++ b/kokoro/windows-msvc-2015-release-bazel/presubmit.cfg
@@ -0,0 +1,16 @@
+# 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/source/CMakeLists.txt b/source/CMakeLists.txt
index cb63ff0..f3b5942 100644
--- a/source/CMakeLists.txt
+++ b/source/CMakeLists.txt
@@ -283,7 +283,6 @@
   ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_composites.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_constants.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_conversion.cpp
-  ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_datarules.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_debug.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_decorations.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_derivatives.cpp
@@ -339,7 +338,9 @@
 add_library(${SPIRV_TOOLS} ${SPIRV_SOURCES})
 spvtools_default_compile_options(${SPIRV_TOOLS})
 target_include_directories(${SPIRV_TOOLS}
-  PUBLIC ${spirv-tools_SOURCE_DIR}/include
+  PUBLIC
+    $<BUILD_INTERFACE:${spirv-tools_SOURCE_DIR}/include>
+    $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
   PRIVATE ${spirv-tools_BINARY_DIR}
   PRIVATE ${SPIRV_HEADER_INCLUDE_DIR}
   )
@@ -350,7 +351,9 @@
 add_library(${SPIRV_TOOLS}-shared SHARED ${SPIRV_SOURCES})
 spvtools_default_compile_options(${SPIRV_TOOLS}-shared)
 target_include_directories(${SPIRV_TOOLS}-shared
-  PUBLIC ${spirv-tools_SOURCE_DIR}/include
+  PUBLIC
+    $<BUILD_INTERFACE:${spirv-tools_SOURCE_DIR}/include>
+    $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
   PRIVATE ${spirv-tools_BINARY_DIR}
   PRIVATE ${SPIRV_HEADER_INCLUDE_DIR}
   )
@@ -372,10 +375,21 @@
 endif()
 
 if(ENABLE_SPIRV_TOOLS_INSTALL)
-  install(TARGETS ${SPIRV_TOOLS} ${SPIRV_TOOLS}-shared
+  install(TARGETS ${SPIRV_TOOLS} ${SPIRV_TOOLS}-shared EXPORT ${SPIRV_TOOLS}Targets
     RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
     LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
     ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
+  export(EXPORT ${SPIRV_TOOLS}Targets FILE ${SPIRV_TOOLS}Target.cmake)
+
+  spvtools_config_package_dir(${SPIRV_TOOLS} PACKAGE_DIR)
+  install(EXPORT ${SPIRV_TOOLS}Targets FILE ${SPIRV_TOOLS}Target.cmake DESTINATION ${PACKAGE_DIR})
+
+  # Special config file for root library compared to other libs.
+  file(WRITE ${CMAKE_BINARY_DIR}/${SPIRV_TOOLS}Config.cmake
+    "include(\${CMAKE_CURRENT_LIST_DIR}/${SPIRV_TOOLS}Target.cmake)\n"
+    "set(${SPIRV_TOOLS}_LIBRARIES ${SPIRV_TOOLS})\n"
+    "get_target_property(${SPIRV_TOOLS}_INCLUDE_DIRS ${SPIRV_TOOLS} INTERFACE_INCLUDE_DIRECTORIES)\n")
+  install(FILES ${CMAKE_BINARY_DIR}/${SPIRV_TOOLS}Config.cmake DESTINATION ${PACKAGE_DIR})
 endif(ENABLE_SPIRV_TOOLS_INSTALL)
 
 if(MSVC)
diff --git a/source/binary.cpp b/source/binary.cpp
index 636dac8..1d31283 100644
--- a/source/binary.cpp
+++ b/source/binary.cpp
@@ -781,9 +781,10 @@
 // TODO(dneto): This probably belongs in text.cpp since that's the only place
 // that a spv_binary_t value is created.
 void spvBinaryDestroy(spv_binary binary) {
-  if (!binary) return;
-  delete[] binary->code;
-  delete binary;
+  if (binary) {
+    if (binary->code) delete[] binary->code;
+    delete binary;
+  }
 }
 
 size_t spv_strnlen_s(const char* str, size_t strsz) {
diff --git a/source/ext_inst.cpp b/source/ext_inst.cpp
index 1198f76..0499e23 100644
--- a/source/ext_inst.cpp
+++ b/source/ext_inst.cpp
@@ -84,6 +84,7 @@
     case SPV_ENV_VULKAN_1_1_SPIRV_1_4:
     case SPV_ENV_WEBGPU_0:
     case SPV_ENV_UNIVERSAL_1_4:
+    case SPV_ENV_UNIVERSAL_1_5:
       *pExtInstTable = &kTable_1_0;
       return SPV_SUCCESS;
     default:
diff --git a/source/fuzz/CMakeLists.txt b/source/fuzz/CMakeLists.txt
index 49ee843..97f8976 100644
--- a/source/fuzz/CMakeLists.txt
+++ b/source/fuzz/CMakeLists.txt
@@ -13,6 +13,9 @@
 # limitations under the License.
 
 if(SPIRV_BUILD_FUZZER)
+
+  file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/protobufs)
+
   set(PROTOBUF_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/protobufs/spvtoolsfuzz.proto)
 
   add_custom_command(
@@ -27,18 +30,29 @@
 
   set(SPIRV_TOOLS_FUZZ_SOURCES
         data_descriptor.h
+        equivalence_relation.h
         fact_manager.h
+        force_render_red.h
         fuzzer.h
         fuzzer_context.h
         fuzzer_pass.h
         fuzzer_pass_add_dead_breaks.h
         fuzzer_pass_add_dead_continues.h
+        fuzzer_pass_add_no_contraction_decorations.h
         fuzzer_pass_add_useful_constructs.h
+        fuzzer_pass_adjust_function_controls.h
+        fuzzer_pass_adjust_loop_controls.h
+        fuzzer_pass_adjust_memory_operands_masks.h
+        fuzzer_pass_adjust_selection_controls.h
+        fuzzer_pass_apply_id_synonyms.h
+        fuzzer_pass_construct_composites.h
+        fuzzer_pass_copy_objects.h
         fuzzer_pass_obfuscate_constants.h
         fuzzer_pass_permute_blocks.h
         fuzzer_pass_split_blocks.h
         fuzzer_util.h
         id_use_descriptor.h
+        instruction_descriptor.h
         protobufs/spirvfuzz_protobufs.h
         pseudo_random_generator.h
         random_generator.h
@@ -49,31 +63,50 @@
         transformation_add_constant_scalar.h
         transformation_add_dead_break.h
         transformation_add_dead_continue.h
+        transformation_add_no_contraction_decoration.h
         transformation_add_type_boolean.h
         transformation_add_type_float.h
         transformation_add_type_int.h
         transformation_add_type_pointer.h
+        transformation_composite_construct.h
+        transformation_composite_extract.h
         transformation_copy_object.h
         transformation_move_block_down.h
         transformation_replace_boolean_constant_with_constant_binary.h
         transformation_replace_constant_with_uniform.h
+        transformation_replace_id_with_synonym.h
+        transformation_set_function_control.h
+        transformation_set_loop_control.h
+        transformation_set_memory_operands_mask.h
+        transformation_set_selection_control.h
         transformation_split_block.h
+        transformation_vector_shuffle.h
         uniform_buffer_element_descriptor.h
         ${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.h
 
         data_descriptor.cpp
         fact_manager.cpp
+        force_render_red.cpp
         fuzzer.cpp
         fuzzer_context.cpp
         fuzzer_pass.cpp
         fuzzer_pass_add_dead_breaks.cpp
         fuzzer_pass_add_dead_continues.cpp
+        fuzzer_pass_add_no_contraction_decorations.cpp
         fuzzer_pass_add_useful_constructs.cpp
+        fuzzer_pass_adjust_function_controls.cpp
+        fuzzer_pass_adjust_loop_controls.cpp
+        fuzzer_pass_adjust_memory_operands_masks.cpp
+        fuzzer_pass_adjust_selection_controls.cpp
+        fuzzer_pass_apply_id_synonyms.cpp
+        fuzzer_pass_construct_composites.cpp
+        fuzzer_pass_copy_objects.cpp
         fuzzer_pass_obfuscate_constants.cpp
         fuzzer_pass_permute_blocks.cpp
         fuzzer_pass_split_blocks.cpp
         fuzzer_util.cpp
         id_use_descriptor.cpp
+        instruction_descriptor.cpp
         pseudo_random_generator.cpp
         random_generator.cpp
         replayer.cpp
@@ -83,15 +116,24 @@
         transformation_add_constant_scalar.cpp
         transformation_add_dead_break.cpp
         transformation_add_dead_continue.cpp
+        transformation_add_no_contraction_decoration.cpp
         transformation_add_type_boolean.cpp
         transformation_add_type_float.cpp
         transformation_add_type_int.cpp
         transformation_add_type_pointer.cpp
+        transformation_composite_construct.cpp
+        transformation_composite_extract.cpp
         transformation_copy_object.cpp
         transformation_move_block_down.cpp
         transformation_replace_boolean_constant_with_constant_binary.cpp
         transformation_replace_constant_with_uniform.cpp
+        transformation_replace_id_with_synonym.cpp
+        transformation_set_function_control.cpp
+        transformation_set_loop_control.cpp
+        transformation_set_memory_operands_mask.cpp
+        transformation_set_selection_control.cpp
         transformation_split_block.cpp
+        transformation_vector_shuffle.cpp
         uniform_buffer_element_descriptor.cpp
         ${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.cc
         )
@@ -118,8 +160,10 @@
   endif()
 
   target_include_directories(SPIRV-Tools-fuzz
-        PUBLIC ${spirv-tools_SOURCE_DIR}/include
-        PUBLIC ${SPIRV_HEADER_INCLUDE_DIR}
+		PUBLIC
+			$<BUILD_INTERFACE:${spirv-tools_SOURCE_DIR}/include>
+			$<BUILD_INTERFACE:${SPIRV_HEADER_INCLUDE_DIR}>
+			$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
         PRIVATE ${spirv-tools_BINARY_DIR}
         PRIVATE ${CMAKE_BINARY_DIR})
 
@@ -133,10 +177,18 @@
   spvtools_check_symbol_exports(SPIRV-Tools-fuzz)
 
   if(ENABLE_SPIRV_TOOLS_INSTALL)
-      install(TARGETS SPIRV-Tools-fuzz
+      install(TARGETS SPIRV-Tools-fuzz EXPORT SPIRV-Tools-fuzzTargets
             RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
             LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
             ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
+      export(EXPORT SPIRV-Tools-fuzzTargets FILE SPIRV-Tools-fuzzTarget.cmake)
+
+      spvtools_config_package_dir(SPIRV-Tools-fuzz PACKAGE_DIR)
+      install(EXPORT SPIRV-Tools-fuzzTargets FILE SPIRV-Tools-fuzzTarget.cmake
+            DESTINATION ${PACKAGE_DIR})
+
+      spvtools_generate_config_file(SPIRV-Tools-fuzz)
+      install(FILES ${CMAKE_BINARY_DIR}/SPIRV-Tools-fuzzConfig.cmake DESTINATION ${PACKAGE_DIR})
   endif(ENABLE_SPIRV_TOOLS_INSTALL)
 
 endif(SPIRV_BUILD_FUZZER)
diff --git a/source/fuzz/data_descriptor.cpp b/source/fuzz/data_descriptor.cpp
index 9cdb2c5..86e5325 100644
--- a/source/fuzz/data_descriptor.cpp
+++ b/source/fuzz/data_descriptor.cpp
@@ -29,6 +29,16 @@
   return result;
 }
 
+size_t DataDescriptorHash::operator()(
+    const protobufs::DataDescriptor* data_descriptor) const {
+  std::u32string hash;
+  hash.push_back(data_descriptor->object());
+  for (auto an_index : data_descriptor->index()) {
+    hash.push_back(an_index);
+  }
+  return std::hash<std::u32string>()(hash);
+}
+
 bool DataDescriptorEquals::operator()(
     const protobufs::DataDescriptor* first,
     const protobufs::DataDescriptor* second) const {
@@ -38,5 +48,22 @@
                     second->index().begin());
 }
 
+std::ostream& operator<<(std::ostream& out,
+                         const protobufs::DataDescriptor& data_descriptor) {
+  out << data_descriptor.object();
+  out << "[";
+  bool first = true;
+  for (auto index : data_descriptor.index()) {
+    if (first) {
+      first = false;
+    } else {
+      out << ", ";
+    }
+    out << index;
+  }
+  out << "]";
+  return out;
+}
+
 }  // namespace fuzz
 }  // namespace spvtools
diff --git a/source/fuzz/data_descriptor.h b/source/fuzz/data_descriptor.h
index 731bd21..c569ac8 100644
--- a/source/fuzz/data_descriptor.h
+++ b/source/fuzz/data_descriptor.h
@@ -17,6 +17,7 @@
 
 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
 
+#include <ostream>
 #include <vector>
 
 namespace spvtools {
@@ -27,13 +28,21 @@
 protobufs::DataDescriptor MakeDataDescriptor(uint32_t object,
                                              std::vector<uint32_t>&& indices);
 
+// Hash function for data descriptors.
+struct DataDescriptorHash {
+  size_t operator()(const protobufs::DataDescriptor* data_descriptor) const;
+};
+
 // Equality function for data descriptors.
 struct DataDescriptorEquals {
   bool operator()(const protobufs::DataDescriptor* first,
                   const protobufs::DataDescriptor* second) const;
 };
 
+std::ostream& operator<<(std::ostream& out,
+                         const protobufs::DataDescriptor& data_descriptor);
+
 }  // namespace fuzz
 }  // namespace spvtools
 
-#endif  // #define SOURCE_FUZZ_DATA_DESCRIPTOR_H_
+#endif  // SOURCE_FUZZ_DATA_DESCRIPTOR_H_
diff --git a/source/fuzz/equivalence_relation.h b/source/fuzz/equivalence_relation.h
new file mode 100644
index 0000000..046536f
--- /dev/null
+++ b/source/fuzz/equivalence_relation.h
@@ -0,0 +1,237 @@
+// 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.
+
+#ifndef SOURCE_FUZZ_EQUIVALENCE_RELATION_H_
+#define SOURCE_FUZZ_EQUIVALENCE_RELATION_H_
+
+#include <memory>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+#include "source/util/make_unique.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// A class for representing an equivalence relation on objects of type |T|,
+// which should be a value type.  The type |T| is required to have a copy
+// constructor, and |PointerHashT| and |PointerEqualsT| must be functors
+// providing hashing and equality testing functionality for pointers to objects
+// of type |T|.
+//
+// A disjoint-set (a.k.a. union-find or merge-find) data structure is used to
+// represent the equivalence relation.  Path compression is used.  Union by
+// rank/size is not used.
+//
+// Each disjoint set is represented as a tree, rooted at the representative
+// of the set.
+//
+// Getting the representative of a value simply requires chasing parent pointers
+// from the value until you reach the root.
+//
+// Checking equivalence of two elements requires checking that the
+// representatives are equal.
+//
+// Traversing the tree rooted at a value's representative visits the value's
+// equivalence class.
+//
+// |PointerHashT| and |PointerEqualsT| are used to define *equality* between
+// values, and otherwise are *not* used to define the equivalence relation
+// (except that equal values are equivalent).  The equivalence relation is
+// constructed by repeatedly adding pairs of (typically non-equal) values that
+// are deemed to be equivalent.
+//
+// For example in an equivalence relation on integers, 1 and 5 might be added
+// as equivalent, so that IsEquivalent(1, 5) holds, because they represent
+// IDs in a SPIR-V binary that are known to contain the same value at run time,
+// but clearly 1 != 5.  Since 1 and 1 are equal, IsEquivalent(1, 1) will also
+// hold.
+//
+// Each unique (up to equality) value added to the relation is copied into
+// |owned_values_|, so there is one canonical memory address per unique value.
+// Uniqueness is ensured by storing (and checking) a set of pointers to these
+// values in |value_set_|, which uses |PointerHashT| and |PointerEqualsT|.
+//
+// |parent_| and |children_| encode the equivalence relation, i.e., the trees.
+template <typename T, typename PointerHashT, typename PointerEqualsT>
+class EquivalenceRelation {
+ public:
+  // Merges the equivalence classes associated with |value1| and |value2|.
+  // If any of these values was not previously in the equivalence relation, it
+  // is added to the pool of values known to be in the relation.
+  void MakeEquivalent(const T& value1, const T& value2) {
+    // Register each value if necessary.
+    for (auto value : {value1, value2}) {
+      if (!Exists(value)) {
+        // Register the value in the equivalence relation.  This relies on
+        // T having a copy constructor.
+        auto unique_pointer_to_value = MakeUnique<T>(value);
+        auto pointer_to_value = unique_pointer_to_value.get();
+        owned_values_.push_back(std::move(unique_pointer_to_value));
+        value_set_.insert(pointer_to_value);
+
+        // Initially say that the value is its own parent and that it has no
+        // children.
+        assert(pointer_to_value && "Representatives should never be null.");
+        parent_[pointer_to_value] = pointer_to_value;
+        children_[pointer_to_value] = std::vector<const T*>();
+      }
+    }
+
+    // Look up canonical pointers to each of the values in the value pool.
+    const T* value1_ptr = *value_set_.find(&value1);
+    const T* value2_ptr = *value_set_.find(&value2);
+
+    // If the values turn out to be identical, they are already in the same
+    // equivalence class so there is nothing to do.
+    if (value1_ptr == value2_ptr) {
+      return;
+    }
+
+    // Find the representative for each value's equivalence class, and if they
+    // are not already in the same class, make one the parent of the other.
+    const T* representative1 = Find(value1_ptr);
+    const T* representative2 = Find(value2_ptr);
+    assert(representative1 && "Representatives should never be null.");
+    assert(representative2 && "Representatives should never be null.");
+    if (representative1 != representative2) {
+      parent_[representative1] = representative2;
+      children_[representative2].push_back(representative1);
+    }
+  }
+
+  // Returns exactly one representative per equivalence class.
+  std::vector<const T*> GetEquivalenceClassRepresentatives() const {
+    std::vector<const T*> result;
+    for (auto& value : owned_values_) {
+      if (parent_[value.get()] == value.get()) {
+        result.push_back(value.get());
+      }
+    }
+    return result;
+  }
+
+  // Returns pointers to all values in the equivalence class of |value|, which
+  // must already be part of the equivalence relation.
+  std::vector<const T*> GetEquivalenceClass(const T& value) const {
+    assert(Exists(value));
+
+    std::vector<const T*> result;
+
+    // Traverse the tree of values rooted at the representative of the
+    // equivalence class to which |value| belongs, and collect up all the values
+    // that are encountered.  This constitutes the whole equivalence class.
+    std::vector<const T*> stack;
+    stack.push_back(Find(*value_set_.find(&value)));
+    while (!stack.empty()) {
+      const T* item = stack.back();
+      result.push_back(item);
+      stack.pop_back();
+      for (auto child : children_[item]) {
+        stack.push_back(child);
+      }
+    }
+    return result;
+  }
+
+  // Returns true if and only if |value1| and |value2| are in the same
+  // equivalence class.  Both values must already be known to the equivalence
+  // relation.
+  bool IsEquivalent(const T& value1, const T& value2) const {
+    return Find(&value1) == Find(&value2);
+  }
+
+  // Returns all values known to be part of the equivalence relation.
+  std::vector<const T*> GetAllKnownValues() const {
+    std::vector<const T*> result;
+    for (auto& value : owned_values_) {
+      result.push_back(value.get());
+    }
+    return result;
+  }
+
+  // Returns true if and only if |value| is known to be part of the equivalence
+  // relation.
+  bool Exists(const T& value) const {
+    return value_set_.find(&value) != value_set_.end();
+  }
+
+ private:
+  // Returns the representative of the equivalence class of |value|, which must
+  // already be known to the equivalence relation.  This is the 'Find' operation
+  // in a classic union-find data structure.
+  const T* Find(const T* value) const {
+    assert(Exists(*value));
+
+    // Get the canonical pointer to the value from the value pool.
+    const T* known_value = *value_set_.find(value);
+    assert(parent_[known_value] && "Every known value should have a parent.");
+
+    // Compute the result by chasing parents until we find a value that is its
+    // own parent.
+    const T* result = known_value;
+    while (parent_[result] != result) {
+      result = parent_[result];
+    }
+    assert(result && "Representatives should never be null.");
+
+    // At this point, |result| is the representative of the equivalence class.
+    // Now perform the 'path compression' optimization by doing another pass up
+    // the parent chain, setting the parent of each node to be the
+    // representative, and rewriting children correspondingly.
+    const T* current = known_value;
+    while (parent_[current] != result) {
+      const T* next = parent_[current];
+      parent_[current] = result;
+      children_[result].push_back(current);
+      auto child_iterator =
+          std::find(children_[next].begin(), children_[next].end(), current);
+      assert(child_iterator != children_[next].end() &&
+             "'next' is the parent of 'current', so 'current' should be a "
+             "child of 'next'");
+      children_[next].erase(child_iterator);
+      current = next;
+    }
+    return result;
+  }
+
+  // Maps every value to a parent.  The representative of an equivalence class
+  // is its own parent.  A value's representative can be found by walking its
+  // chain of ancestors.
+  //
+  // Mutable because the intuitively const method, 'Find', performs path
+  // compression.
+  mutable std::unordered_map<const T*, const T*> parent_;
+
+  // Stores the children of each value.  This allows the equivalence class of
+  // a value to be calculated by traversing all descendents of the class's
+  // representative.
+  //
+  // Mutable because the intuitively const method, 'Find', performs path
+  // compression.
+  mutable std::unordered_map<const T*, std::vector<const T*>> children_;
+
+  // The values known to the equivalence relation are allocated in
+  // |owned_values_|, and |value_pool_| provides (via |PointerHashT| and
+  // |PointerEqualsT|) a means for mapping a value of interest to a pointer
+  // into an equivalent value in |owned_values_|.
+  std::unordered_set<const T*, PointerHashT, PointerEqualsT> value_set_;
+  std::vector<std::unique_ptr<T>> owned_values_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_EQUIVALENCE_RELATION_H_
diff --git a/source/fuzz/fact_manager.cpp b/source/fuzz/fact_manager.cpp
index 61daa64..ac3ea30 100644
--- a/source/fuzz/fact_manager.cpp
+++ b/source/fuzz/fact_manager.cpp
@@ -15,7 +15,11 @@
 #include "source/fuzz/fact_manager.h"
 
 #include <sstream>
+#include <unordered_map>
+#include <unordered_set>
 
+#include "source/fuzz/equivalence_relation.h"
+#include "source/fuzz/fuzzer_util.h"
 #include "source/fuzz/uniform_buffer_element_descriptor.h"
 #include "source/opt/ir_context.h"
 
@@ -26,7 +30,7 @@
 
 std::string ToString(const protobufs::Fact& fact) {
   assert(fact.fact_case() == protobufs::Fact::kConstantUniformFact &&
-         "Right now this is the only fact.");
+         "Right now this is the only fact we know how to stringify.");
   std::stringstream stream;
   stream << "("
          << fact.constant_uniform_fact()
@@ -71,9 +75,10 @@
 //=======================
 // Constant uniform facts
 
-// The purpose of this struct is to group the fields and data used to represent
+// The purpose of this class is to group the fields and data used to represent
 // facts about uniform constants.
-struct FactManager::ConstantUniformFacts {
+class FactManager::ConstantUniformFacts {
+ public:
   // See method in FactManager which delegates to this method.
   bool AddFact(const protobufs::FactConstantUniform& fact,
                opt::IRContext* context);
@@ -96,6 +101,11 @@
   // See method in FactManager which delegates to this method.
   std::vector<uint32_t> GetTypesForWhichUniformValuesAreKnown() const;
 
+  // See method in FactManager which delegates to this method.
+  const std::vector<std::pair<protobufs::FactConstantUniform, uint32_t>>&
+  GetConstantUniformFactsAndTypes() const;
+
+ private:
   // Returns true if and only if the words associated with
   // |constant_instruction| exactly match the words for the constant associated
   // with |constant_uniform_fact|.
@@ -121,7 +131,7 @@
                                     uint32_t width) const;
 
   std::vector<std::pair<protobufs::FactConstantUniform, uint32_t>>
-      facts_and_type_ids;
+      facts_and_type_ids_;
 };
 
 uint32_t FactManager::ConstantUniformFacts::GetConstantId(
@@ -160,7 +170,7 @@
     opt::IRContext* ir_context, uint32_t type_id) const {
   std::vector<uint32_t> result;
   std::set<uint32_t> already_seen;
-  for (auto& fact_and_type_id : facts_and_type_ids) {
+  for (auto& fact_and_type_id : facts_and_type_ids_) {
     if (fact_and_type_id.second != type_id) {
       continue;
     }
@@ -183,7 +193,7 @@
   assert(constant_inst->opcode() == SpvOpConstant &&
          "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) {
+  for (auto& fact_and_type_id : facts_and_type_ids_) {
     if (fact_and_type_id.second != type_id) {
       continue;
     }
@@ -199,7 +209,7 @@
     opt::IRContext* context,
     const protobufs::UniformBufferElementDescriptor& uniform_descriptor) const {
   // Consider each fact.
-  for (auto& fact_and_type : facts_and_type_ids) {
+  for (auto& fact_and_type : facts_and_type_ids_) {
     // Check whether the uniform descriptor associated with the fact matches
     // |uniform_descriptor|.
     if (UniformBufferElementDescriptorEquals()(
@@ -216,7 +226,7 @@
 FactManager::ConstantUniformFacts::GetTypesForWhichUniformValuesAreKnown()
     const {
   std::vector<uint32_t> result;
-  for (auto& fact_and_type : facts_and_type_ids) {
+  for (auto& fact_and_type : facts_and_type_ids_) {
     if (std::find(result.begin(), result.end(), fact_and_type.second) ==
         result.end()) {
       result.push_back(fact_and_type.second);
@@ -276,42 +286,20 @@
   }
   auto should_be_uniform_pointer_instruction =
       context->get_def_use_mgr()->GetDef(uniform_variable->type_id());
-  auto element_type =
+  auto composite_type =
       should_be_uniform_pointer_instruction->GetSingleWordInOperand(1);
 
-  for (auto index : fact.uniform_buffer_element_descriptor().index()) {
-    auto should_be_composite_type =
-        context->get_def_use_mgr()->GetDef(element_type);
-    if (SpvOpTypeStruct == should_be_composite_type->opcode()) {
-      if (index >= should_be_composite_type->NumInOperands()) {
-        return false;
-      }
-      element_type = should_be_composite_type->GetSingleWordInOperand(index);
-    } else if (SpvOpTypeArray == should_be_composite_type->opcode()) {
-      auto array_length_constant =
-          context->get_constant_mgr()
-              ->GetConstantFromInst(context->get_def_use_mgr()->GetDef(
-                  should_be_composite_type->GetSingleWordInOperand(1)))
-              ->AsIntConstant();
-      if (array_length_constant->words().size() != 1) {
-        return false;
-      }
-      auto array_length = array_length_constant->GetU32();
-      if (index >= array_length) {
-        return false;
-      }
-      element_type = should_be_composite_type->GetSingleWordInOperand(0);
-    } else if (SpvOpTypeVector == should_be_composite_type->opcode()) {
-      auto vector_length = should_be_composite_type->GetSingleWordInOperand(1);
-      if (index >= vector_length) {
-        return false;
-      }
-      element_type = should_be_composite_type->GetSingleWordInOperand(0);
-    } else {
-      return false;
-    }
+  auto final_element_type_id = fuzzerutil::WalkCompositeTypeIndices(
+      context, composite_type,
+      fact.uniform_buffer_element_descriptor().index());
+  if (!final_element_type_id) {
+    return false;
   }
-  auto final_element_type = context->get_type_mgr()->GetType(element_type);
+  auto final_element_type =
+      context->get_type_mgr()->GetType(final_element_type_id);
+  assert(final_element_type &&
+         "There should be a type corresponding to this id.");
+
   if (!(final_element_type->AsFloat() || final_element_type->AsInteger())) {
     return false;
   }
@@ -328,48 +316,493 @@
   if (static_cast<uint32_t>(fact.constant_word().size()) != required_words) {
     return false;
   }
-  facts_and_type_ids.emplace_back(
-      std::pair<protobufs::FactConstantUniform, uint32_t>(fact, element_type));
+  facts_and_type_ids_.emplace_back(
+      std::pair<protobufs::FactConstantUniform, uint32_t>(
+          fact, final_element_type_id));
   return true;
 }
 
+const std::vector<std::pair<protobufs::FactConstantUniform, uint32_t>>&
+FactManager::ConstantUniformFacts::GetConstantUniformFactsAndTypes() const {
+  return facts_and_type_ids_;
+}
+
 // End of uniform constant facts
 //==============================
 
 //==============================
-// Id synonym facts
+// Data synonym facts
 
-// The purpose of this struct is to group the fields and data used to represent
-// facts about id synonyms.
-struct FactManager::IdSynonymFacts {
+// The purpose of this class is to group the fields and data used to represent
+// facts about data synonyms.
+class FactManager::DataSynonymFacts {
+ public:
   // See method in FactManager which delegates to this method.
-  void AddFact(const protobufs::FactIdSynonym& fact);
+  void AddFact(const protobufs::FactDataSynonym& fact, opt::IRContext* context);
 
-  // A record of all the synonyms that are available.
-  std::map<uint32_t, std::vector<protobufs::DataDescriptor>> synonyms;
+  // See method in FactManager which delegates to this method.
+  std::vector<const protobufs::DataDescriptor*> GetSynonymsForDataDescriptor(
+      const protobufs::DataDescriptor& data_descriptor,
+      opt::IRContext* context) const;
 
-  // The set of keys to the above map; useful if you just want to know which ids
-  // have synonyms.
-  std::set<uint32_t> ids_with_synonyms;
+  // See method in FactManager which delegates to this method.
+  std::vector<uint32_t> GetIdsForWhichSynonymsAreKnown(
+      opt::IRContext* context) const;
+
+  // See method in FactManager which delegates to this method.
+  bool IsSynonymous(const protobufs::DataDescriptor& data_descriptor1,
+                    const protobufs::DataDescriptor& data_descriptor2,
+                    opt::IRContext* context) const;
+
+ private:
+  // Adds |fact| to the set of managed facts, and recurses into sub-components
+  // of the data descriptors referenced in |fact|, if they are composites, to
+  // record that their components are pairwise-synonymous.
+  void AddFactRecursive(const protobufs::FactDataSynonym& fact,
+                        opt::IRContext* context);
+
+  // Inspects all known facts and adds corollary facts; e.g. if we know that
+  // a.x == b.x and a.y == b.y, where a and b have vec2 type, we can record
+  // that a == b holds.
+  //
+  // This method is expensive, and is thus called on demand: rather than
+  // computing the closure of facts each time a data synonym fact is added, we
+  // compute the closure only when a data synonym fact is *queried*.
+  void ComputeClosureOfFacts(opt::IRContext* context) const;
+
+  // Returns true if and only if |dd1| and |dd2| are valid data descriptors
+  // whose associated data have the same type.
+  bool DataDescriptorsAreWellFormedAndComparable(
+      opt::IRContext* context, const protobufs::DataDescriptor& dd1,
+      const protobufs::DataDescriptor& dd2) const;
+
+  // The data descriptors that are known to be synonymous with one another are
+  // captured by this equivalence relation.
+  //
+  // This member is mutable in order to allow the closure of facts captured by
+  // the relation to be computed lazily when a question about data synonym
+  // facts is asked.
+  mutable EquivalenceRelation<protobufs::DataDescriptor, DataDescriptorHash,
+                              DataDescriptorEquals>
+      synonymous_;
+
+  // When a new synonym fact is added, it may be possible to deduce further
+  // synonym facts by computing a closure of all known facts.  However, there is
+  // no point computing this closure until a question regarding synonym facts is
+  // actually asked: if several facts are added in succession with no questions
+  // asked in between, we can avoid computing fact closures multiple times.
+  //
+  // This boolean tracks whether a closure computation is required - i.e.,
+  // whether a new fact has been added since the last time such a computation
+  // was performed.
+  //
+  // It is mutable so faciliate having const methods, that provide answers to
+  // questions about data synonym facts, triggering closure computation on
+  // demand.
+  mutable bool closure_computation_required = false;
 };
 
-void FactManager::IdSynonymFacts::AddFact(
-    const protobufs::FactIdSynonym& fact) {
-  if (synonyms.count(fact.id()) == 0) {
-    assert(ids_with_synonyms.count(fact.id()) == 0);
-    ids_with_synonyms.insert(fact.id());
-    synonyms[fact.id()] = std::vector<protobufs::DataDescriptor>();
-  }
-  assert(ids_with_synonyms.count(fact.id()) == 1);
-  synonyms[fact.id()].push_back(fact.data_descriptor());
+void FactManager::DataSynonymFacts::AddFact(
+    const protobufs::FactDataSynonym& fact, opt::IRContext* context) {
+  // Add the fact, including all facts relating sub-components of the data
+  // descriptors that are involved.
+  AddFactRecursive(fact, context);
 }
 
-// End of id synonym facts
+void FactManager::DataSynonymFacts::AddFactRecursive(
+    const protobufs::FactDataSynonym& fact, opt::IRContext* context) {
+  assert(DataDescriptorsAreWellFormedAndComparable(context, fact.data1(),
+                                                   fact.data2()));
+
+  // Record that the data descriptors provided in the fact are equivalent.
+  synonymous_.MakeEquivalent(fact.data1(), fact.data2());
+  // As we have updated the equivalence relation, we might be able to deduce
+  // more facts by performing a closure computation, so we record that such a
+  // computation is required; it will be performed next time a method answering
+  // a data synonym fact-related question is invoked.
+  closure_computation_required = true;
+
+  // We now check whether this is a synonym about composite objects.  If it is,
+  // we can recursively add synonym facts about their associated sub-components.
+
+  // Get the type of the object referred to by the first data descriptor in the
+  // synonym fact.
+  uint32_t type_id = fuzzerutil::WalkCompositeTypeIndices(
+      context,
+      context->get_def_use_mgr()->GetDef(fact.data1().object())->type_id(),
+      fact.data1().index());
+  auto type = context->get_type_mgr()->GetType(type_id);
+  auto type_instruction = context->get_def_use_mgr()->GetDef(type_id);
+  assert(type != nullptr &&
+         "Invalid data synonym fact: one side has an unknown type.");
+
+  // Check whether the type is composite, recording the number of elements
+  // associated with the composite if so.
+  uint32_t num_composite_elements;
+  if (type->AsArray()) {
+    num_composite_elements =
+        fuzzerutil::GetArraySize(*type_instruction, context);
+  } else if (type->AsMatrix()) {
+    num_composite_elements = type->AsMatrix()->element_count();
+  } else if (type->AsStruct()) {
+    num_composite_elements =
+        fuzzerutil::GetNumberOfStructMembers(*type_instruction);
+  } else if (type->AsVector()) {
+    num_composite_elements = type->AsVector()->element_count();
+  } else {
+    // The type is not a composite, so return.
+    return;
+  }
+
+  // If the fact has the form:
+  //   obj_1[a_1, ..., a_m] == obj_2[b_1, ..., b_n]
+  // then for each composite index i, we add a fact of the form:
+  //   obj_1[a_1, ..., a_m, i] == obj_2[b_1, ..., b_n, i]
+  for (uint32_t i = 0; i < num_composite_elements; i++) {
+    std::vector<uint32_t> extended_indices1 =
+        fuzzerutil::RepeatedFieldToVector(fact.data1().index());
+    extended_indices1.push_back(i);
+    std::vector<uint32_t> extended_indices2 =
+        fuzzerutil::RepeatedFieldToVector(fact.data2().index());
+    extended_indices2.push_back(i);
+    protobufs::FactDataSynonym extended_data_synonym_fact;
+    *extended_data_synonym_fact.mutable_data1() =
+        MakeDataDescriptor(fact.data1().object(), std::move(extended_indices1));
+    *extended_data_synonym_fact.mutable_data2() =
+        MakeDataDescriptor(fact.data2().object(), std::move(extended_indices2));
+    AddFactRecursive(extended_data_synonym_fact, context);
+  }
+}
+
+void FactManager::DataSynonymFacts::ComputeClosureOfFacts(
+    opt::IRContext* context) const {
+  // Suppose that obj_1[a_1, ..., a_m] and obj_2[b_1, ..., b_n] are distinct
+  // data descriptors that describe objects of the same composite type, and that
+  // the composite type is comprised of k components.
+  //
+  // For example, if m is a mat4x4 and v a vec4, we might consider:
+  //   m[2]: describes the 2nd column of m, a vec4
+  //   v[]: describes all of v, a vec4
+  //
+  // Suppose that we know, for every 0 <= i < k, that the fact:
+  //   obj_1[a_1, ..., a_m, i] == obj_2[b_1, ..., b_n, i]
+  // holds - i.e. that the children of the two data descriptors are synonymous.
+  //
+  // Then we can conclude that:
+  //   obj_1[a_1, ..., a_m] == obj_2[b_1, ..., b_n]
+  // holds.
+  //
+  // For instance, if we have the facts:
+  //   m[2, 0] == v[0]
+  //   m[2, 1] == v[1]
+  //   m[2, 2] == v[2]
+  //   m[2, 3] == v[3]
+  // then we can conclude that:
+  //   m[2] == v.
+  //
+  // This method repeatedly searches the equivalence relation of data
+  // descriptors, deducing and adding such facts, until a pass over the
+  // relation leads to no further facts being deduced.
+
+  // The method relies on working with pairs of data descriptors, and in
+  // particular being able to hash and compare such pairs.
+
+  using DataDescriptorPair =
+      std::pair<protobufs::DataDescriptor, protobufs::DataDescriptor>;
+
+  struct DataDescriptorPairHash {
+    std::size_t operator()(const DataDescriptorPair& pair) const {
+      return DataDescriptorHash()(&pair.first) ^
+             DataDescriptorHash()(&pair.second);
+    }
+  };
+
+  struct DataDescriptorPairEquals {
+    bool operator()(const DataDescriptorPair& first,
+                    const DataDescriptorPair& second) const {
+      return DataDescriptorEquals()(&first.first, &second.first) &&
+             DataDescriptorEquals()(&first.second, &second.second);
+    }
+  };
+
+  // This map records, for a given pair of composite data descriptors of the
+  // same type, all the indices at which the data descriptors are known to be
+  // synonymous.  A pair is a key to this map only if we have observed that
+  // the pair are synonymous at *some* index, but not at *all* indices.
+  // Once we find that a pair of data descriptors are equivalent at all indices
+  // we record the fact that they are synonymous and remove them from the map.
+  //
+  // Using the m and v example from above, initially the pair (m[2], v) would
+  // not be a key to the map.  If we find that m[2, 2] == v[2] holds, we would
+  // add an entry:
+  //   (m[2], v) -> [false, false, true, false]
+  // to record that they are synonymous at index 2.  If we then find that
+  // m[2, 0] == v[0] holds, we would update this entry to:
+  //   (m[2], v) -> [true, false, true, false]
+  // If we then find that m[2, 3] == v[3] holds, we would update this entry to:
+  //   (m[2], v) -> [true, false, true, true]
+  // Finally, if we then find that m[2, 1] == v[1] holds, which would make the
+  // boolean vector true at every index, we would add the fact:
+  //   m[2] == v
+  // to the equivalence relation and remove (m[2], v) from the map.
+  std::unordered_map<DataDescriptorPair, std::vector<bool>,
+                     DataDescriptorPairHash, DataDescriptorPairEquals>
+      candidate_composite_synonyms;
+
+  // We keep looking for new facts until we perform a complete pass over the
+  // equivalence relation without finding any new facts.
+  while (closure_computation_required) {
+    // We have not found any new facts yet during this pass; we set this to
+    // 'true' if we do find a new fact.
+    closure_computation_required = false;
+
+    // Consider each class in the equivalence relation.
+    for (auto representative :
+         synonymous_.GetEquivalenceClassRepresentatives()) {
+      auto equivalence_class = synonymous_.GetEquivalenceClass(*representative);
+
+      // Consider every data descriptor in the equivalence class.
+      for (auto dd1_it = equivalence_class.begin();
+           dd1_it != equivalence_class.end(); ++dd1_it) {
+        // If this data descriptor has no indices then it does not have the form
+        // obj_1[a_1, ..., a_m, i], so move on.
+        auto dd1 = *dd1_it;
+        if (dd1->index_size() == 0) {
+          continue;
+        }
+
+        // Consider every other data descriptor later in the equivalence class
+        // (due to symmetry, there is no need to compare with previous data
+        // descriptors).
+        auto dd2_it = dd1_it;
+        for (++dd2_it; dd2_it != equivalence_class.end(); ++dd2_it) {
+          auto dd2 = *dd2_it;
+          // If this data descriptor has no indices then it does not have the
+          // form obj_2[b_1, ..., b_n, i], so move on.
+          if (dd2->index_size() == 0) {
+            continue;
+          }
+
+          // At this point we know that:
+          // - |dd1| has the form obj_1[a_1, ..., a_m, i]
+          // - |dd2| has the form obj_2[b_1, ..., b_n, j]
+          assert(dd1->index_size() > 0 && dd2->index_size() > 0 &&
+                 "Control should not reach here if either data descriptor has "
+                 "no indices.");
+
+          // We are only interested if i == j.
+          if (dd1->index(dd1->index_size() - 1) !=
+              dd2->index(dd2->index_size() - 1)) {
+            continue;
+          }
+
+          const uint32_t common_final_index = dd1->index(dd1->index_size() - 1);
+
+          // Make data descriptors |dd1_prefix| and |dd2_prefix| for
+          //   obj_1[a_1, ..., a_m]
+          // and
+          //   obj_2[b_1, ..., b_n]
+          // These are the two data descriptors we might be getting closer to
+          // deducing as being synonymous, due to knowing that they are
+          // synonymous when extended by a particular index.
+          protobufs::DataDescriptor dd1_prefix;
+          dd1_prefix.set_object(dd1->object());
+          for (uint32_t i = 0; i < static_cast<uint32_t>(dd1->index_size() - 1);
+               i++) {
+            dd1_prefix.add_index(dd1->index(i));
+          }
+          protobufs::DataDescriptor dd2_prefix;
+          dd2_prefix.set_object(dd2->object());
+          for (uint32_t i = 0; i < static_cast<uint32_t>(dd2->index_size() - 1);
+               i++) {
+            dd2_prefix.add_index(dd2->index(i));
+          }
+          assert(!DataDescriptorEquals()(&dd1_prefix, &dd2_prefix) &&
+                 "By construction these prefixes should be different.");
+
+          // If we already know that these prefixes are synonymous, move on.
+          if (synonymous_.Exists(dd1_prefix) &&
+              synonymous_.Exists(dd2_prefix) &&
+              synonymous_.IsEquivalent(dd1_prefix, dd2_prefix)) {
+            continue;
+          }
+
+          // Get the type of obj_1
+          auto dd1_root_type_id =
+              context->get_def_use_mgr()->GetDef(dd1->object())->type_id();
+          // Use this type, together with a_1, ..., a_m, to get the type of
+          // obj_1[a_1, ..., a_m].
+          auto dd1_prefix_type = fuzzerutil::WalkCompositeTypeIndices(
+              context, dd1_root_type_id, dd1_prefix.index());
+
+          // Similarly, get the type of obj_2 and use it to get the type of
+          // obj_2[b_1, ..., b_n].
+          auto dd2_root_type_id =
+              context->get_def_use_mgr()->GetDef(dd2->object())->type_id();
+          auto dd2_prefix_type = fuzzerutil::WalkCompositeTypeIndices(
+              context, dd2_root_type_id, dd2_prefix.index());
+
+          // If the types of dd1_prefix and dd2_prefix are not the same, they
+          // cannot be synonymous.
+          if (dd1_prefix_type != dd2_prefix_type) {
+            continue;
+          }
+
+          // At this point, we know we have synonymous data descriptors of the
+          // form:
+          //   obj_1[a_1, ..., a_m, i]
+          //   obj_2[b_1, ..., b_n, i]
+          // with the same last_index i, such that:
+          //   obj_1[a_1, ..., a_m]
+          // and
+          //   obj_2[b_1, ..., b_n]
+          // have the same type.
+
+          // Work out how many components there are in the (common) commposite
+          // type associated with obj_1[a_1, ..., a_m] and obj_2[b_1, ..., b_n].
+          // This depends on whether the composite type is array, matrix, struct
+          // or vector.
+          uint32_t num_components_in_composite;
+          auto composite_type =
+              context->get_type_mgr()->GetType(dd1_prefix_type);
+          auto composite_type_instruction =
+              context->get_def_use_mgr()->GetDef(dd1_prefix_type);
+          if (composite_type->AsArray()) {
+            num_components_in_composite =
+                fuzzerutil::GetArraySize(*composite_type_instruction, context);
+            if (num_components_in_composite == 0) {
+              // This indicates that the array has an unknown size, in which
+              // case we cannot be sure we have matched all of its elements with
+              // synonymous elements of another array.
+              continue;
+            }
+          } else if (composite_type->AsMatrix()) {
+            num_components_in_composite =
+                composite_type->AsMatrix()->element_count();
+          } else if (composite_type->AsStruct()) {
+            num_components_in_composite = fuzzerutil::GetNumberOfStructMembers(
+                *composite_type_instruction);
+          } else {
+            assert(composite_type->AsVector());
+            num_components_in_composite =
+                composite_type->AsVector()->element_count();
+          }
+
+          // We are one step closer to being able to say that |dd1_prefix| and
+          // |dd2_prefix| are synonymous.
+          DataDescriptorPair candidate_composite_synonym(dd1_prefix,
+                                                         dd2_prefix);
+
+          // We look up what we already know about this pair.
+          auto existing_entry =
+              candidate_composite_synonyms.find(candidate_composite_synonym);
+
+          if (existing_entry == candidate_composite_synonyms.end()) {
+            // If this is the first time we have seen the pair, we make a vector
+            // of size |num_components_in_composite| that is 'true' at the
+            // common final index associated with |dd1| and |dd2|, and 'false'
+            // everywhere else, and register this vector as being associated
+            // with the pair.
+            std::vector<bool> entry;
+            for (uint32_t i = 0; i < num_components_in_composite; i++) {
+              entry.push_back(i == common_final_index);
+            }
+            candidate_composite_synonyms[candidate_composite_synonym] = entry;
+            existing_entry =
+                candidate_composite_synonyms.find(candidate_composite_synonym);
+          } else {
+            // We have seen this pair of data descriptors before, and we now
+            // know that they are synonymous at one further index, so we
+            // update the entry to record that.
+            existing_entry->second[common_final_index] = true;
+          }
+          assert(existing_entry != candidate_composite_synonyms.end());
+
+          // Check whether |dd1_prefix| and |dd2_prefix| are now known to match
+          // at every sub-component.
+          bool all_components_match = true;
+          for (uint32_t i = 0; i < num_components_in_composite; i++) {
+            if (!existing_entry->second[i]) {
+              all_components_match = false;
+              break;
+            }
+          }
+          if (all_components_match) {
+            // The two prefixes match on all sub-components, so we know that
+            // they are synonymous.  We add this fact *non-recursively*, as we
+            // have deduced that |dd1_prefix| and |dd2_prefix| are synonymous
+            // by observing that all their sub-components are already
+            // synonymous.
+            assert(DataDescriptorsAreWellFormedAndComparable(
+                context, dd1_prefix, dd2_prefix));
+            synonymous_.MakeEquivalent(dd1_prefix, dd2_prefix);
+            // As we have added a new synonym fact, we might benefit from doing
+            // another pass over the equivalence relation.
+            closure_computation_required = true;
+            // Now that we know this pair of data descriptors are synonymous,
+            // there is no point recording how close they are to being
+            // synonymous.
+            candidate_composite_synonyms.erase(candidate_composite_synonym);
+          }
+        }
+      }
+    }
+  }
+}
+
+bool FactManager::DataSynonymFacts::DataDescriptorsAreWellFormedAndComparable(
+    opt::IRContext* context, const protobufs::DataDescriptor& dd1,
+    const protobufs::DataDescriptor& dd2) const {
+  auto end_type_1 = fuzzerutil::WalkCompositeTypeIndices(
+      context, context->get_def_use_mgr()->GetDef(dd1.object())->type_id(),
+      dd1.index());
+  auto end_type_2 = fuzzerutil::WalkCompositeTypeIndices(
+      context, context->get_def_use_mgr()->GetDef(dd2.object())->type_id(),
+      dd2.index());
+  return end_type_1 && end_type_1 == end_type_2;
+}
+
+std::vector<const protobufs::DataDescriptor*>
+FactManager::DataSynonymFacts::GetSynonymsForDataDescriptor(
+    const protobufs::DataDescriptor& data_descriptor,
+    opt::IRContext* context) const {
+  ComputeClosureOfFacts(context);
+  if (synonymous_.Exists(data_descriptor)) {
+    return synonymous_.GetEquivalenceClass(data_descriptor);
+  }
+  return std::vector<const protobufs::DataDescriptor*>();
+}
+
+std::vector<uint32_t>
+FactManager::DataSynonymFacts ::GetIdsForWhichSynonymsAreKnown(
+    opt::IRContext* context) const {
+  ComputeClosureOfFacts(context);
+  std::vector<uint32_t> result;
+  for (auto& data_descriptor : synonymous_.GetAllKnownValues()) {
+    if (data_descriptor->index().empty()) {
+      result.push_back(data_descriptor->object());
+    }
+  }
+  return result;
+}
+
+bool FactManager::DataSynonymFacts::IsSynonymous(
+    const protobufs::DataDescriptor& data_descriptor1,
+    const protobufs::DataDescriptor& data_descriptor2,
+    opt::IRContext* context) const {
+  const_cast<FactManager::DataSynonymFacts*>(this)->ComputeClosureOfFacts(
+      context);
+  return synonymous_.Exists(data_descriptor1) &&
+         synonymous_.Exists(data_descriptor2) &&
+         synonymous_.IsEquivalent(data_descriptor1, data_descriptor2);
+}
+
+// End of data synonym facts
 //==============================
 
 FactManager::FactManager()
     : uniform_constant_facts_(MakeUnique<ConstantUniformFacts>()),
-      id_synonym_facts_(MakeUnique<IdSynonymFacts>()) {}
+      data_synonym_facts_(MakeUnique<DataSynonymFacts>()) {}
 
 FactManager::~FactManager() = default;
 
@@ -385,14 +818,14 @@
   }
 }
 
-bool FactManager::AddFact(const spvtools::fuzz::protobufs::Fact& fact,
-                          spvtools::opt::IRContext* context) {
+bool FactManager::AddFact(const fuzz::protobufs::Fact& fact,
+                          opt::IRContext* context) {
   switch (fact.fact_case()) {
     case protobufs::Fact::kConstantUniformFact:
       return uniform_constant_facts_->AddFact(fact.constant_uniform_fact(),
                                               context);
-    case protobufs::Fact::kIdSynonymFact:
-      id_synonym_facts_->AddFact(fact.id_synonym_fact());
+    case protobufs::Fact::kDataSynonymFact:
+      data_synonym_facts_->AddFact(fact.data_synonym_fact(), context);
       return true;
     default:
       assert(false && "Unknown fact type.");
@@ -400,6 +833,15 @@
   }
 }
 
+void FactManager::AddFactDataSynonym(const protobufs::DataDescriptor& data1,
+                                     const protobufs::DataDescriptor& data2,
+                                     opt::IRContext* context) {
+  protobufs::FactDataSynonym fact;
+  *fact.mutable_data1() = data1;
+  *fact.mutable_data2() = data2;
+  data_synonym_facts_->AddFact(fact, context);
+}
+
 std::vector<uint32_t> FactManager::GetConstantsAvailableFromUniformsForType(
     opt::IRContext* ir_context, uint32_t type_id) const {
   return uniform_constant_facts_->GetConstantsAvailableFromUniformsForType(
@@ -427,16 +869,33 @@
 
 const std::vector<std::pair<protobufs::FactConstantUniform, uint32_t>>&
 FactManager::GetConstantUniformFactsAndTypes() const {
-  return uniform_constant_facts_->facts_and_type_ids;
+  return uniform_constant_facts_->GetConstantUniformFactsAndTypes();
 }
 
-const std::set<uint32_t>& FactManager::GetIdsForWhichSynonymsAreKnown() const {
-  return id_synonym_facts_->ids_with_synonyms;
+std::vector<uint32_t> FactManager::GetIdsForWhichSynonymsAreKnown(
+    opt::IRContext* context) const {
+  return data_synonym_facts_->GetIdsForWhichSynonymsAreKnown(context);
 }
 
-const std::vector<protobufs::DataDescriptor>& FactManager::GetSynonymsForId(
-    uint32_t id) const {
-  return id_synonym_facts_->synonyms.at(id);
+std::vector<const protobufs::DataDescriptor*>
+FactManager::GetSynonymsForDataDescriptor(
+    const protobufs::DataDescriptor& data_descriptor,
+    opt::IRContext* context) const {
+  return data_synonym_facts_->GetSynonymsForDataDescriptor(data_descriptor,
+                                                           context);
+}
+
+std::vector<const protobufs::DataDescriptor*> FactManager::GetSynonymsForId(
+    uint32_t id, opt::IRContext* context) const {
+  return GetSynonymsForDataDescriptor(MakeDataDescriptor(id, {}), context);
+}
+
+bool FactManager::IsSynonymous(
+    const protobufs::DataDescriptor& data_descriptor1,
+    const protobufs::DataDescriptor& data_descriptor2,
+    opt::IRContext* context) const {
+  return data_synonym_facts_->IsSynonymous(data_descriptor1, data_descriptor2,
+                                           context);
 }
 
 }  // namespace fuzz
diff --git a/source/fuzz/fact_manager.h b/source/fuzz/fact_manager.h
index f6ea247..62d9dac 100644
--- a/source/fuzz/fact_manager.h
+++ b/source/fuzz/fact_manager.h
@@ -20,6 +20,7 @@
 #include <utility>
 #include <vector>
 
+#include "source/fuzz/data_descriptor.h"
 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
 #include "source/opt/constants.h"
 
@@ -52,6 +53,11 @@
   // fact manager.
   bool AddFact(const protobufs::Fact& fact, opt::IRContext* context);
 
+  // Record the fact that |data1| and |data2| are synonymous.
+  void AddFactDataSynonym(const protobufs::DataDescriptor& data1,
+                          const protobufs::DataDescriptor& data2,
+                          opt::IRContext* context);
+
   // The fact manager is responsible for managing a few distinct categories of
   // facts. In principle there could be different fact managers for each kind
   // of fact, but in practice providing one 'go to' place for facts is
@@ -99,33 +105,46 @@
   //==============================
   // Querying facts about id synonyms
 
-  // Returns every id for which a fact of the form "this id is synonymous
-  // with this piece of data" is known.
-  const std::set<uint32_t>& GetIdsForWhichSynonymsAreKnown() const;
+  // Returns every id for which a fact of the form "this id is synonymous with
+  // this piece of data" is known.
+  std::vector<uint32_t> GetIdsForWhichSynonymsAreKnown(
+      opt::IRContext* context) const;
 
-  // Requires that at least one synonym for |id| is known, and returns the
-  // sequence of all known synonyms.
-  const std::vector<protobufs::DataDescriptor>& GetSynonymsForId(
-      uint32_t id) const;
+  // Returns the equivalence class of all known synonyms of |id|, or an empty
+  // set if no synonyms are known.
+  std::vector<const protobufs::DataDescriptor*> GetSynonymsForId(
+      uint32_t id, opt::IRContext* context) const;
+
+  // Returns the equivalence class of all known synonyms of |data_descriptor|,
+  // or empty if no synonyms are known.
+  std::vector<const protobufs::DataDescriptor*> GetSynonymsForDataDescriptor(
+      const protobufs::DataDescriptor& data_descriptor,
+      opt::IRContext* context) const;
+
+  // Returns true if and ony if |data_descriptor1| and |data_descriptor2| are
+  // known to be synonymous.
+  bool IsSynonymous(const protobufs::DataDescriptor& data_descriptor1,
+                    const protobufs::DataDescriptor& data_descriptor2,
+                    opt::IRContext* context) const;
 
   // End of id synonym facts
   //==============================
 
  private:
   // For each distinct kind of fact to be managed, we use a separate opaque
-  // struct type.
+  // class type.
 
-  struct ConstantUniformFacts;  // Opaque struct for holding data about uniform
-                                // buffer elements.
+  class ConstantUniformFacts;  // Opaque class for management of
+                               // constant uniform facts.
   std::unique_ptr<ConstantUniformFacts>
       uniform_constant_facts_;  // Unique pointer to internal data.
 
-  struct IdSynonymFacts;  // Opaque struct for holding data about id synonyms.
-  std::unique_ptr<IdSynonymFacts>
-      id_synonym_facts_;  // Unique pointer to internal data.
+  class DataSynonymFacts;  // Opaque class for management of data synonym facts.
+  std::unique_ptr<DataSynonymFacts>
+      data_synonym_facts_;  // Unique pointer to internal data.
 };
 
 }  // namespace fuzz
 }  // namespace spvtools
 
-#endif  // #define SOURCE_FUZZ_FACT_MANAGER_H_
+#endif  // SOURCE_FUZZ_FACT_MANAGER_H_
diff --git a/source/fuzz/force_render_red.cpp b/source/fuzz/force_render_red.cpp
new file mode 100644
index 0000000..46e23e8
--- /dev/null
+++ b/source/fuzz/force_render_red.cpp
@@ -0,0 +1,370 @@
+// 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.
+
+#include "source/fuzz/force_render_red.h"
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation_replace_constant_with_uniform.h"
+#include "source/fuzz/uniform_buffer_element_descriptor.h"
+#include "source/opt/build_module.h"
+#include "source/opt/ir_context.h"
+#include "source/opt/types.h"
+#include "source/util/make_unique.h"
+#include "tools/util/cli_consumer.h"
+
+#include <algorithm>
+#include <utility>
+
+namespace spvtools {
+namespace fuzz {
+
+namespace {
+
+// Helper method to find the fragment shader entry point, complaining if there
+// is no shader or if there is no fragment entry point.
+opt::Function* FindFragmentShaderEntryPoint(opt::IRContext* ir_context,
+                                            MessageConsumer message_consumer) {
+  // 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) {
+      found_capability_shader = true;
+      break;
+    }
+  }
+  if (!found_capability_shader) {
+    message_consumer(
+        SPV_MSG_ERROR, nullptr, {},
+        "Forcing of red rendering requires the Shader capability.");
+    return nullptr;
+  }
+
+  opt::Instruction* fragment_entry_point = nullptr;
+  for (auto& entry_point : ir_context->module()->entry_points()) {
+    if (entry_point.GetSingleWordInOperand(0) == SpvExecutionModelFragment) {
+      fragment_entry_point = &entry_point;
+      break;
+    }
+  }
+  if (fragment_entry_point == nullptr) {
+    message_consumer(SPV_MSG_ERROR, nullptr, {},
+                     "Forcing of red rendering requires an entry point with "
+                     "the Fragment execution model.");
+    return nullptr;
+  }
+
+  for (auto& function : *ir_context->module()) {
+    if (function.result_id() ==
+        fragment_entry_point->GetSingleWordInOperand(1)) {
+      return &function;
+    }
+  }
+  assert(
+      false &&
+      "A valid module must have a function associate with each entry point.");
+  return nullptr;
+}
+
+// Helper method to check that there is a single vec4 output variable and get a
+// pointer to it.
+opt::Instruction* FindVec4OutputVariable(opt::IRContext* ir_context,
+                                         MessageConsumer message_consumer) {
+  opt::Instruction* output_variable = nullptr;
+  for (auto& inst : ir_context->types_values()) {
+    if (inst.opcode() == SpvOpVariable &&
+        inst.GetSingleWordInOperand(0) == SpvStorageClassOutput) {
+      if (output_variable != nullptr) {
+        message_consumer(SPV_MSG_ERROR, nullptr, {},
+                         "Only one output variable can be handled at present; "
+                         "found multiple.");
+        return nullptr;
+      }
+      output_variable = &inst;
+      // Do not break, as we want to check for multiple output variables.
+    }
+  }
+  if (output_variable == nullptr) {
+    message_consumer(SPV_MSG_ERROR, nullptr, {},
+                     "No output variable to which to write red was found.");
+    return nullptr;
+  }
+
+  auto output_variable_base_type = ir_context->get_type_mgr()
+                                       ->GetType(output_variable->type_id())
+                                       ->AsPointer()
+                                       ->pointee_type()
+                                       ->AsVector();
+  if (!output_variable_base_type ||
+      output_variable_base_type->element_count() != 4 ||
+      !output_variable_base_type->element_type()->AsFloat()) {
+    message_consumer(SPV_MSG_ERROR, nullptr, {},
+                     "The output variable must have type vec4.");
+    return nullptr;
+  }
+
+  return output_variable;
+}
+
+// Helper to get the ids of float constants 0.0 and 1.0, creating them if
+// necessary.
+std::pair<uint32_t, uint32_t> FindOrCreateFloatZeroAndOne(
+    opt::IRContext* ir_context, opt::analysis::Float* float_type) {
+  float one = 1.0;
+  uint32_t one_as_uint;
+  memcpy(&one_as_uint, &one, sizeof(float));
+  std::vector<uint32_t> zero_bytes = {0};
+  std::vector<uint32_t> one_bytes = {one_as_uint};
+  auto constant_zero = ir_context->get_constant_mgr()->RegisterConstant(
+      MakeUnique<opt::analysis::FloatConstant>(float_type, zero_bytes));
+  auto constant_one = ir_context->get_constant_mgr()->RegisterConstant(
+      MakeUnique<opt::analysis::FloatConstant>(float_type, one_bytes));
+  auto constant_zero_id = ir_context->get_constant_mgr()
+                              ->GetDefiningInstruction(constant_zero)
+                              ->result_id();
+  auto constant_one_id = ir_context->get_constant_mgr()
+                             ->GetDefiningInstruction(constant_one)
+                             ->result_id();
+  return std::pair<uint32_t, uint32_t>(constant_zero_id, constant_one_id);
+}
+
+std::unique_ptr<TransformationReplaceConstantWithUniform>
+MakeConstantUniformReplacement(opt::IRContext* ir_context,
+                               const FactManager& fact_manager,
+                               uint32_t constant_id,
+                               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),
+      fact_manager.GetUniformDescriptorsForConstant(ir_context, constant_id)[0],
+      ir_context->TakeNextId(), ir_context->TakeNextId());
+}
+
+}  // namespace
+
+bool ForceRenderRed(
+    const spv_target_env& target_env, const std::vector<uint32_t>& binary_in,
+    const spvtools::fuzz::protobufs::FactSequence& initial_facts,
+    std::vector<uint32_t>* binary_out) {
+  auto message_consumer = spvtools::utils::CLIMessageConsumer;
+  spvtools::SpirvTools tools(target_env);
+  if (!tools.IsValid()) {
+    message_consumer(SPV_MSG_ERROR, nullptr, {},
+                     "Failed to create SPIRV-Tools interface; stopping.");
+    return false;
+  }
+
+  // Initial binary should be valid.
+  if (!tools.Validate(&binary_in[0], binary_in.size())) {
+    message_consumer(SPV_MSG_ERROR, nullptr, {},
+                     "Initial binary is invalid; stopping.");
+    return false;
+  }
+
+  // Build the module from the input binary.
+  std::unique_ptr<opt::IRContext> ir_context = BuildModule(
+      target_env, message_consumer, binary_in.data(), binary_in.size());
+  assert(ir_context);
+
+  // Set up a fact manager with any given initial facts.
+  FactManager fact_manager;
+  for (auto& fact : initial_facts.fact()) {
+    fact_manager.AddFact(fact, ir_context.get());
+  }
+
+  auto entry_point_function =
+      FindFragmentShaderEntryPoint(ir_context.get(), message_consumer);
+  auto output_variable =
+      FindVec4OutputVariable(ir_context.get(), message_consumer);
+  if (entry_point_function == nullptr || output_variable == nullptr) {
+    return false;
+  }
+
+  opt::analysis::Float temp_float_type(32);
+  opt::analysis::Float* float_type = ir_context->get_type_mgr()
+                                         ->GetRegisteredType(&temp_float_type)
+                                         ->AsFloat();
+  std::pair<uint32_t, uint32_t> zero_one_float_ids =
+      FindOrCreateFloatZeroAndOne(ir_context.get(), float_type);
+
+  // 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 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()));
+    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 new_entry_block = MakeUnique<opt::BasicBlock>(std::move(label));
+
+    // Make an instruction to construct vec4(1.0, 0.0, 0.0, 1.0), representing
+    // the colour red.
+    opt::Operand zero_float = {SPV_OPERAND_TYPE_ID, {zero_one_float_ids.first}};
+    opt::Operand one_float = {SPV_OPERAND_TYPE_ID, {zero_one_float_ids.second}};
+    opt::Instruction::OperandList op_composite_construct_operands = {
+        one_float, zero_float, zero_float, one_float};
+    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->TakeNextId(), op_composite_construct_operands);
+    auto red_id = red->result_id();
+    new_entry_block->AddInstruction(std::move(red));
+
+    // Make an instruction to store red into the output color.
+    opt::Operand variable_to_store_into = {SPV_OPERAND_TYPE_ID,
+                                           {output_variable->result_id()}};
+    opt::Operand value_to_be_stored = {SPV_OPERAND_TYPE_ID, {red_id}};
+    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));
+
+    // 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
+    // with a uniform of the same value - we can only do that replacement once
+    // we have added the entry block to the module.
+    std::unique_ptr<TransformationReplaceConstantWithUniform>
+        first_greater_then_operand_replacement = nullptr;
+    std::unique_ptr<TransformationReplaceConstantWithUniform>
+        second_greater_then_operand_replacement = nullptr;
+    uint32_t id_guaranteed_to_be_false = 0;
+
+    opt::analysis::Bool temp_bool_type;
+    opt::analysis::Bool* registered_bool_type =
+        ir_context->get_type_mgr()
+            ->GetRegisteredType(&temp_bool_type)
+            ->AsBool();
+
+    auto float_type_id = ir_context->get_type_mgr()->GetId(float_type);
+    auto types_for_which_uniforms_are_known =
+        fact_manager.GetTypesForWhichUniformValuesAreKnown();
+
+    // Check whether we have any float uniforms.
+    if (std::find(types_for_which_uniforms_are_known.begin(),
+                  types_for_which_uniforms_are_known.end(),
+                  float_type_id) != types_for_which_uniforms_are_known.end()) {
+      // We have at least one float uniform; let's see whether we have at least
+      // two.
+      auto available_constants =
+          fact_manager.GetConstantsAvailableFromUniformsForType(
+              ir_context.get(), float_type_id);
+      if (available_constants.size() > 1) {
+        // Grab the float constants associated with the first two known float
+        // uniforms.
+        auto first_constant =
+            ir_context->get_constant_mgr()
+                ->GetConstantFromInst(ir_context->get_def_use_mgr()->GetDef(
+                    available_constants[0]))
+                ->AsFloatConstant();
+        auto second_constant =
+            ir_context->get_constant_mgr()
+                ->GetConstantFromInst(ir_context->get_def_use_mgr()->GetDef(
+                    available_constants[1]))
+                ->AsFloatConstant();
+
+        // Now work out which of the two constants is larger than the other.
+        uint32_t larger_constant_index = 0;
+        uint32_t smaller_constant_index = 0;
+        if (first_constant->GetFloat() > second_constant->GetFloat()) {
+          larger_constant_index = 0;
+          smaller_constant_index = 1;
+        } else if (first_constant->GetFloat() < second_constant->GetFloat()) {
+          larger_constant_index = 1;
+          smaller_constant_index = 0;
+        }
+
+        // Only proceed with these constants if they have turned out to be
+        // distinct.
+        if (larger_constant_index != smaller_constant_index) {
+          // We are in a position to create 'false' as 'literal1 > literal2', so
+          // reserve an id for this computation; this id will end up being
+          // guaranteed to be 'false'.
+          id_guaranteed_to_be_false = ir_context->TakeNextId();
+
+          auto smaller_constant = available_constants[smaller_constant_index];
+          auto larger_constant = available_constants[larger_constant_index];
+
+          opt::Instruction::OperandList greater_than_operands = {
+              {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_type_mgr()->GetId(registered_bool_type),
+              id_guaranteed_to_be_false, greater_than_operands));
+
+          first_greater_then_operand_replacement =
+              MakeConstantUniformReplacement(ir_context.get(), fact_manager,
+                                             smaller_constant,
+                                             id_guaranteed_to_be_false, 0);
+          second_greater_then_operand_replacement =
+              MakeConstantUniformReplacement(ir_context.get(), fact_manager,
+                                             larger_constant,
+                                             id_guaranteed_to_be_false, 1);
+        }
+      }
+    }
+
+    if (id_guaranteed_to_be_false == 0) {
+      auto constant_false = ir_context->get_constant_mgr()->RegisterConstant(
+          MakeUnique<opt::analysis::BoolConstant>(registered_bool_type, false));
+      id_guaranteed_to_be_false = ir_context->get_constant_mgr()
+                                      ->GetDefiningInstruction(constant_false)
+                                      ->result_id();
+    }
+
+    opt::Operand false_condition = {SPV_OPERAND_TYPE_ID,
+                                    {id_guaranteed_to_be_false}};
+    opt::Operand then_block = {SPV_OPERAND_TYPE_ID,
+                               {entry_point_function->entry()->id()}};
+    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));
+
+    entry_point_function->InsertBasicBlockBefore(
+        std::move(new_entry_block), entry_point_function->entry().get());
+
+    for (auto& replacement : {first_greater_then_operand_replacement.get(),
+                              second_greater_then_operand_replacement.get()}) {
+      if (replacement) {
+        assert(replacement->IsApplicable(ir_context.get(), fact_manager));
+        replacement->Apply(ir_context.get(), &fact_manager);
+      }
+    }
+  }
+
+  // Write out the module as a binary.
+  ir_context->module()->ToBinary(binary_out, false);
+  return true;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/source/fuzz/force_render_red.h b/source/fuzz/force_render_red.h
new file mode 100644
index 0000000..2484d27
--- /dev/null
+++ b/source/fuzz/force_render_red.h
@@ -0,0 +1,48 @@
+// 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.
+
+#ifndef SOURCE_FORCE_RENDER_RED_H_
+#define SOURCE_FORCE_RENDER_RED_H_
+
+#include <vector>
+
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "spirv-tools/libspirv.hpp"
+
+namespace spvtools {
+namespace fuzz {
+
+// Requires |binary_in| to be a valid SPIR-V module with Shader capability,
+// containing an entry point with the Fragment execution model, and a single
+// output variable of type vec4.
+//
+// Turns the body of this entry point into effectively:
+//
+// output_variable = vec4(1.0, 0.0, 0.0, 1.0);
+// if (false) {
+//    original_body
+// }
+//
+// If suitable facts about values of uniforms are available, the 'false' will
+// instead become: 'u > v', where 'u' and 'v' are pieces of uniform data for
+// which it is known that 'u < v' holds.
+bool ForceRenderRed(
+    const spv_target_env& target_env, const std::vector<uint32_t>& binary_in,
+    const spvtools::fuzz::protobufs::FactSequence& initial_facts,
+    std::vector<uint32_t>* binary_out);
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FORCE_RENDER_RED_H_
diff --git a/source/fuzz/fuzzer.cpp b/source/fuzz/fuzzer.cpp
index 89250a0..01b4258 100644
--- a/source/fuzz/fuzzer.cpp
+++ b/source/fuzz/fuzzer.cpp
@@ -15,13 +15,22 @@
 #include "source/fuzz/fuzzer.h"
 
 #include <cassert>
+#include <memory>
 #include <sstream>
 
+#include "fuzzer_pass_adjust_memory_operands_masks.h"
 #include "source/fuzz/fact_manager.h"
 #include "source/fuzz/fuzzer_context.h"
 #include "source/fuzz/fuzzer_pass_add_dead_breaks.h"
 #include "source/fuzz/fuzzer_pass_add_dead_continues.h"
+#include "source/fuzz/fuzzer_pass_add_no_contraction_decorations.h"
 #include "source/fuzz/fuzzer_pass_add_useful_constructs.h"
+#include "source/fuzz/fuzzer_pass_adjust_function_controls.h"
+#include "source/fuzz/fuzzer_pass_adjust_loop_controls.h"
+#include "source/fuzz/fuzzer_pass_adjust_selection_controls.h"
+#include "source/fuzz/fuzzer_pass_apply_id_synonyms.h"
+#include "source/fuzz/fuzzer_pass_construct_composites.h"
+#include "source/fuzz/fuzzer_pass_copy_objects.h"
 #include "source/fuzz/fuzzer_pass_obfuscate_constants.h"
 #include "source/fuzz/fuzzer_pass_permute_blocks.h"
 #include "source/fuzz/fuzzer_pass_split_blocks.h"
@@ -36,8 +45,25 @@
 
 namespace {
 const uint32_t kIdBoundGap = 100;
+
+const uint32_t kTransformationLimit = 500;
+
+const uint32_t kChanceOfApplyingAnotherPass = 85;
+
+template <typename T>
+void MaybeAddPass(
+    std::vector<std::unique_ptr<FuzzerPass>>* passes,
+    opt::IRContext* ir_context, FactManager* fact_manager,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformation_sequence_out) {
+  if (fuzzer_context->ChooseEven()) {
+    passes->push_back(MakeUnique<T>(ir_context, fact_manager, fuzzer_context,
+                                    transformation_sequence_out));
+  }
 }
 
+}  // namespace
+
 struct Fuzzer::Impl {
   explicit Impl(spv_target_env env) : target_env(env) {}
 
@@ -107,23 +133,69 @@
       .Apply();
 
   // Apply some semantics-preserving passes.
-  FuzzerPassSplitBlocks(ir_context.get(), &fact_manager, &fuzzer_context,
-                        transformation_sequence_out)
-      .Apply();
-  FuzzerPassAddDeadBreaks(ir_context.get(), &fact_manager, &fuzzer_context,
-                          transformation_sequence_out)
-      .Apply();
-  FuzzerPassAddDeadContinues(ir_context.get(), &fact_manager, &fuzzer_context,
-                             transformation_sequence_out)
-      .Apply();
-  FuzzerPassObfuscateConstants(ir_context.get(), &fact_manager, &fuzzer_context,
-                               transformation_sequence_out)
-      .Apply();
+  std::vector<std::unique_ptr<FuzzerPass>> passes;
+  while (passes.empty()) {
+    MaybeAddPass<FuzzerPassAddDeadBreaks>(&passes, ir_context.get(),
+                                          &fact_manager, &fuzzer_context,
+                                          transformation_sequence_out);
+    MaybeAddPass<FuzzerPassAddDeadContinues>(&passes, ir_context.get(),
+                                             &fact_manager, &fuzzer_context,
+                                             transformation_sequence_out);
+    MaybeAddPass<FuzzerPassApplyIdSynonyms>(&passes, ir_context.get(),
+                                            &fact_manager, &fuzzer_context,
+                                            transformation_sequence_out);
+    MaybeAddPass<FuzzerPassConstructComposites>(&passes, ir_context.get(),
+                                                &fact_manager, &fuzzer_context,
+                                                transformation_sequence_out);
+    MaybeAddPass<FuzzerPassCopyObjects>(&passes, ir_context.get(),
+                                        &fact_manager, &fuzzer_context,
+                                        transformation_sequence_out);
+    MaybeAddPass<FuzzerPassObfuscateConstants>(&passes, ir_context.get(),
+                                               &fact_manager, &fuzzer_context,
+                                               transformation_sequence_out);
+    MaybeAddPass<FuzzerPassPermuteBlocks>(&passes, ir_context.get(),
+                                          &fact_manager, &fuzzer_context,
+                                          transformation_sequence_out);
+    MaybeAddPass<FuzzerPassSplitBlocks>(&passes, ir_context.get(),
+                                        &fact_manager, &fuzzer_context,
+                                        transformation_sequence_out);
+  }
 
-  // Finally, give the blocks in the module a good shake-up.
-  FuzzerPassPermuteBlocks(ir_context.get(), &fact_manager, &fuzzer_context,
-                          transformation_sequence_out)
-      .Apply();
+  bool is_first = true;
+  while (static_cast<uint32_t>(
+             transformation_sequence_out->transformation_size()) <
+             kTransformationLimit &&
+         (is_first ||
+          fuzzer_context.ChoosePercentage(kChanceOfApplyingAnotherPass))) {
+    is_first = false;
+    passes[fuzzer_context.RandomIndex(passes)]->Apply();
+  }
+
+  // Now apply some passes that it does not make sense to apply repeatedly,
+  // as they do not unlock other passes.
+  std::vector<std::unique_ptr<FuzzerPass>> final_passes;
+  MaybeAddPass<FuzzerPassAdjustFunctionControls>(
+      &final_passes, ir_context.get(), &fact_manager, &fuzzer_context,
+      transformation_sequence_out);
+  MaybeAddPass<FuzzerPassAdjustLoopControls>(&final_passes, ir_context.get(),
+                                             &fact_manager, &fuzzer_context,
+                                             transformation_sequence_out);
+  MaybeAddPass<FuzzerPassAdjustMemoryOperandsMasks>(
+      &final_passes, ir_context.get(), &fact_manager, &fuzzer_context,
+      transformation_sequence_out);
+  MaybeAddPass<FuzzerPassAdjustSelectionControls>(
+      &final_passes, ir_context.get(), &fact_manager, &fuzzer_context,
+      transformation_sequence_out);
+  for (auto& pass : final_passes) {
+    pass->Apply();
+  }
+
+  if (fuzzer_context.ChooseEven()) {
+    FuzzerPassAddNoContractionDecorations(ir_context.get(), &fact_manager,
+                                          &fuzzer_context,
+                                          transformation_sequence_out)
+        .Apply();
+  }
 
   // Encode the module as a binary.
   ir_context->module()->ToBinary(binary_out, false);
diff --git a/source/fuzz/fuzzer_context.cpp b/source/fuzz/fuzzer_context.cpp
index c8e7719..356cb35 100644
--- a/source/fuzz/fuzzer_context.cpp
+++ b/source/fuzz/fuzzer_context.cpp
@@ -20,15 +20,31 @@
 namespace fuzz {
 
 namespace {
-// Default probabilities for applying various transformations.
-// All values are percentages.
-// Keep them in alphabetical order.
+// Default <minimum, maximum> pairs of probabilities for applying various
+// transformations. All values are percentages. Keep them in alphabetical order.
 
-const uint32_t kDefaultChanceOfAddingDeadBreak = 20;
-const uint32_t kDefaultChanceOfAddingDeadContinue = 20;
-const uint32_t kDefaultChanceOfMovingBlockDown = 25;
-const uint32_t kDefaultChanceOfObfuscatingConstant = 20;
-const uint32_t kDefaultChanceOfSplittingBlock = 20;
+const std::pair<uint32_t, uint32_t> kChanceOfAddingDeadBreak = {5, 80};
+const std::pair<uint32_t, uint32_t> kChanceOfAddingDeadContinue = {5, 80};
+const std::pair<uint32_t, uint32_t> kChanceOfAddingNoContractionDecoration = {
+    5, 70};
+const std::pair<uint32_t, uint32_t> kChanceOfAdjustingFunctionControl = {20,
+                                                                         70};
+const std::pair<uint32_t, uint32_t> kChanceOfAdjustingLoopControl = {20, 90};
+const std::pair<uint32_t, uint32_t> kChanceOfAdjustingMemoryOperandsMask = {20,
+                                                                            90};
+const std::pair<uint32_t, uint32_t> kChanceOfAdjustingSelectionControl = {20,
+                                                                          90};
+const std::pair<uint32_t, uint32_t> kChanceOfCopyingObject = {20, 50};
+const std::pair<uint32_t, uint32_t> kChanceOfConstructingComposite = {20, 50};
+const std::pair<uint32_t, uint32_t> kChanceOfMovingBlockDown = {20, 50};
+const std::pair<uint32_t, uint32_t> kChanceOfObfuscatingConstant = {10, 90};
+const std::pair<uint32_t, uint32_t> kChanceOfReplacingIdWithSynonym = {10, 90};
+const std::pair<uint32_t, uint32_t> kChanceOfSplittingBlock = {40, 95};
+
+// Default limits for various quantities that are chosen during fuzzing.
+// Keep them in alphabetical order.
+const uint32_t kDefaultMaxLoopControlPartialCount = 100;
+const uint32_t kDefaultMaxLoopControlPeelCount = 100;
 
 // Default functions for controlling how deep to go during recursive
 // generation/transformation. Keep them in alphabetical order.
@@ -46,20 +62,52 @@
                              uint32_t min_fresh_id)
     : random_generator_(random_generator),
       next_fresh_id_(min_fresh_id),
-      chance_of_adding_dead_break_(kDefaultChanceOfAddingDeadBreak),
-      chance_of_adding_dead_continue_(kDefaultChanceOfAddingDeadContinue),
-      chance_of_moving_block_down_(kDefaultChanceOfMovingBlockDown),
-      chance_of_obfuscating_constant_(kDefaultChanceOfObfuscatingConstant),
-      chance_of_splitting_block_(kDefaultChanceOfSplittingBlock),
       go_deeper_in_constant_obfuscation_(
-          kDefaultGoDeeperInConstantObfuscation) {}
+          kDefaultGoDeeperInConstantObfuscation) {
+  chance_of_adding_dead_break_ =
+      ChooseBetweenMinAndMax(kChanceOfAddingDeadBreak);
+  chance_of_adding_dead_continue_ =
+      ChooseBetweenMinAndMax(kChanceOfAddingDeadContinue);
+  chance_of_adding_no_contraction_decoration_ =
+      ChooseBetweenMinAndMax(kChanceOfAddingNoContractionDecoration);
+  chance_of_adjusting_function_control_ =
+      ChooseBetweenMinAndMax(kChanceOfAdjustingFunctionControl);
+  chance_of_adjusting_loop_control_ =
+      ChooseBetweenMinAndMax(kChanceOfAdjustingLoopControl);
+  chance_of_adjusting_memory_operands_mask_ =
+      ChooseBetweenMinAndMax(kChanceOfAdjustingMemoryOperandsMask);
+  chance_of_adjusting_selection_control_ =
+      ChooseBetweenMinAndMax(kChanceOfAdjustingSelectionControl);
+  chance_of_constructing_composite_ =
+      ChooseBetweenMinAndMax(kChanceOfConstructingComposite);
+  chance_of_copying_object_ = ChooseBetweenMinAndMax(kChanceOfCopyingObject);
+  chance_of_moving_block_down_ =
+      ChooseBetweenMinAndMax(kChanceOfMovingBlockDown);
+  chance_of_obfuscating_constant_ =
+      ChooseBetweenMinAndMax(kChanceOfObfuscatingConstant);
+  chance_of_replacing_id_with_synonym_ =
+      ChooseBetweenMinAndMax(kChanceOfReplacingIdWithSynonym);
+  chance_of_splitting_block_ = ChooseBetweenMinAndMax(kChanceOfSplittingBlock);
+  max_loop_control_partial_count_ = kDefaultMaxLoopControlPartialCount;
+  max_loop_control_peel_count_ = kDefaultMaxLoopControlPeelCount;
+}
 
 FuzzerContext::~FuzzerContext() = default;
 
 uint32_t FuzzerContext::GetFreshId() { return next_fresh_id_++; }
 
-RandomGenerator* FuzzerContext::GetRandomGenerator() {
-  return random_generator_;
+bool FuzzerContext::ChooseEven() { return random_generator_->RandomBool(); }
+
+bool FuzzerContext::ChoosePercentage(uint32_t percentage_chance) {
+  assert(percentage_chance <= 100);
+  return random_generator_->RandomPercentage() < percentage_chance;
+}
+
+uint32_t FuzzerContext::ChooseBetweenMinAndMax(
+    const std::pair<uint32_t, uint32_t>& min_max) {
+  assert(min_max.first <= min_max.second);
+  return min_max.first +
+         random_generator_->RandomUint32(min_max.second - min_max.first + 1);
 }
 
 }  // namespace fuzz
diff --git a/source/fuzz/fuzzer_context.h b/source/fuzz/fuzzer_context.h
index 3eaefc7..c8242e6 100644
--- a/source/fuzz/fuzzer_context.h
+++ b/source/fuzz/fuzzer_context.h
@@ -16,6 +16,7 @@
 #define SOURCE_FUZZ_FUZZER_CONTEXT_H_
 
 #include <functional>
+#include <utility>
 
 #include "source/fuzz/random_generator.h"
 #include "source/opt/function.h"
@@ -34,8 +35,22 @@
 
   ~FuzzerContext();
 
-  // Provides the random generator used to control fuzzing.
-  RandomGenerator* GetRandomGenerator();
+  // Returns a random boolean.
+  bool ChooseEven();
+
+  // Returns true if and only if a randomly-chosen integer in the range [0, 100]
+  // is less than |percentage_chance|.
+  bool ChoosePercentage(uint32_t percentage_chance);
+
+  // Returns a random index into |sequence|, which is expected to have a 'size'
+  // method, and which must be non-empty.  Typically 'HasSizeMethod' will be an
+  // std::vector.
+  template <typename HasSizeMethod>
+  uint32_t RandomIndex(const HasSizeMethod& sequence) {
+    assert(sequence.size() > 0);
+    return random_generator_->RandomUint32(
+        static_cast<uint32_t>(sequence.size()));
+  }
 
   // Yields an id that is guaranteed not to be used in the module being fuzzed,
   // or to have been issued before.
@@ -47,17 +62,44 @@
   uint32_t GetChanceOfAddingDeadContinue() {
     return chance_of_adding_dead_continue_;
   }
+  uint32_t GetChanceOfAddingNoContractionDecoration() {
+    return chance_of_adding_no_contraction_decoration_;
+  }
+  uint32_t GetChanceOfAdjustingFunctionControl() {
+    return chance_of_adjusting_function_control_;
+  }
+  uint32_t GetChanceOfAdjustingLoopControl() {
+    return chance_of_adjusting_loop_control_;
+  }
+  uint32_t GetChanceOfAdjustingMemoryOperandsMask() {
+    return chance_of_adjusting_memory_operands_mask_;
+  }
+  uint32_t GetChanceOfAdjustingSelectionControl() {
+    return chance_of_adjusting_selection_control_;
+  }
+  uint32_t GetChanceOfConstructingComposite() {
+    return chance_of_constructing_composite_;
+  }
+  uint32_t GetChanceOfCopyingObject() { return chance_of_copying_object_; }
   uint32_t GetChanceOfMovingBlockDown() { return chance_of_moving_block_down_; }
   uint32_t GetChanceOfObfuscatingConstant() {
     return chance_of_obfuscating_constant_;
   }
+  uint32_t GetChanceOfReplacingIdWithSynonym() {
+    return chance_of_replacing_id_with_synonym_;
+  }
   uint32_t GetChanceOfSplittingBlock() { return chance_of_splitting_block_; }
+  uint32_t GetRandomLoopControlPeelCount() {
+    return random_generator_->RandomUint32(max_loop_control_peel_count_);
+  }
+  uint32_t GetRandomLoopControlPartialCount() {
+    return random_generator_->RandomUint32(max_loop_control_partial_count_);
+  }
 
-  // Probability distributions to control how deeply to recurse.
+  // Functions to control how deeply to recurse.
   // Keep them in alphabetical order.
-  const std::function<bool(uint32_t, RandomGenerator*)>&
-  GoDeeperInConstantObfuscation() {
-    return go_deeper_in_constant_obfuscation_;
+  bool GoDeeperInConstantObfuscation(uint32_t depth) {
+    return go_deeper_in_constant_obfuscation_(depth, random_generator_);
   }
 
  private:
@@ -70,14 +112,32 @@
   // Keep them in alphabetical order.
   uint32_t chance_of_adding_dead_break_;
   uint32_t chance_of_adding_dead_continue_;
+  uint32_t chance_of_adding_no_contraction_decoration_;
+  uint32_t chance_of_adjusting_function_control_;
+  uint32_t chance_of_adjusting_loop_control_;
+  uint32_t chance_of_adjusting_memory_operands_mask_;
+  uint32_t chance_of_adjusting_selection_control_;
+  uint32_t chance_of_constructing_composite_;
+  uint32_t chance_of_copying_object_;
   uint32_t chance_of_moving_block_down_;
   uint32_t chance_of_obfuscating_constant_;
+  uint32_t chance_of_replacing_id_with_synonym_;
   uint32_t chance_of_splitting_block_;
 
+  // Limits associated with various quantities for which random values are
+  // chosen during fuzzing.
+  // Keep them in alphabetical order.
+  uint32_t max_loop_control_partial_count_;
+  uint32_t max_loop_control_peel_count_;
+
   // Functions to determine with what probability to go deeper when generating
   // or mutating constructs recursively.
   const std::function<bool(uint32_t, RandomGenerator*)>&
       go_deeper_in_constant_obfuscation_;
+
+  // Requires |min_max.first| <= |min_max.second|, and returns a value in the
+  // range [ |min_max.first|, |min_max.second| ]
+  uint32_t ChooseBetweenMinAndMax(const std::pair<uint32_t, uint32_t>& min_max);
 };
 
 }  // namespace fuzz
diff --git a/source/fuzz/fuzzer_pass.cpp b/source/fuzz/fuzzer_pass.cpp
index 823f2e0..1da53f4 100644
--- a/source/fuzz/fuzzer_pass.cpp
+++ b/source/fuzz/fuzzer_pass.cpp
@@ -14,6 +14,8 @@
 
 #include "source/fuzz/fuzzer_pass.h"
 
+#include "source/fuzz/instruction_descriptor.h"
+
 namespace spvtools {
 namespace fuzz {
 
@@ -27,5 +29,104 @@
 
 FuzzerPass::~FuzzerPass() = default;
 
+std::vector<opt::Instruction*> FuzzerPass::FindAvailableInstructions(
+    const opt::Function& function, opt::BasicBlock* block,
+    opt::BasicBlock::iterator inst_it,
+    std::function<bool(opt::IRContext*, opt::Instruction*)>
+        instruction_is_relevant) {
+  // TODO(afd) The following is (relatively) simple, but may end up being
+  //  prohibitively inefficient, as it walks the whole dominator tree for
+  //  every instruction that is considered.
+
+  std::vector<opt::Instruction*> result;
+  // Consider all global declarations
+  for (auto& global : GetIRContext()->module()->types_values()) {
+    if (instruction_is_relevant(GetIRContext(), &global)) {
+      result.push_back(&global);
+    }
+  }
+
+  // Consider all previous instructions in this block
+  for (auto prev_inst_it = block->begin(); prev_inst_it != inst_it;
+       ++prev_inst_it) {
+    if (instruction_is_relevant(GetIRContext(), &*prev_inst_it)) {
+      result.push_back(&*prev_inst_it);
+    }
+  }
+
+  // Walk the dominator tree to consider all instructions from dominating
+  // blocks
+  auto dominator_analysis = GetIRContext()->GetDominatorAnalysis(&function);
+  for (auto next_dominator = dominator_analysis->ImmediateDominator(block);
+       next_dominator != nullptr;
+       next_dominator =
+           dominator_analysis->ImmediateDominator(next_dominator)) {
+    for (auto& dominating_inst : *next_dominator) {
+      if (instruction_is_relevant(GetIRContext(), &dominating_inst)) {
+        result.push_back(&dominating_inst);
+      }
+    }
+  }
+  return result;
+}
+
+void FuzzerPass::MaybeAddTransformationBeforeEachInstruction(
+    std::function<
+        void(const opt::Function& function, opt::BasicBlock* block,
+             opt::BasicBlock::iterator inst_it,
+             const protobufs::InstructionDescriptor& instruction_descriptor)>
+        maybe_apply_transformation) {
+  // Consider every block in every function.
+  for (auto& function : *GetIRContext()->module()) {
+    for (auto& block : function) {
+      // We now consider every instruction in the block, randomly deciding
+      // whether to apply a transformation before it.
+
+      // In order for transformations to insert new instructions, they need to
+      // be able to identify the instruction to insert before.  We describe an
+      // instruction via its opcode, 'opc', a base instruction 'base' that has a
+      // result id, and the number of instructions with opcode 'opc' that we
+      // 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;
+
+      // 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;
+
+      // 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
+      // in the block does not have a result id.
+      for (auto inst_it = block.begin(); inst_it != block.end(); ++inst_it) {
+        if (inst_it->HasResultId()) {
+          // In the case that the instruction has a result id, we use the
+          // instruction as its own base, and clear the skip counts we have
+          // collected.
+          base = inst_it->result_id();
+          skip_count.clear();
+        }
+        const SpvOp opcode = inst_it->opcode();
+
+        // Invoke the provided function, which might apply a transformation.
+        maybe_apply_transformation(
+            function, &block, inst_it,
+            MakeInstructionDescriptor(
+                base, opcode,
+                skip_count.count(opcode) ? skip_count.at(opcode) : 0));
+
+        if (!inst_it->HasResultId()) {
+          skip_count[opcode] =
+              skip_count.count(opcode) ? skip_count.at(opcode) + 1 : 1;
+        }
+      }
+    }
+  }
+}
+
 }  // namespace fuzz
 }  // namespace spvtools
diff --git a/source/fuzz/fuzzer_pass.h b/source/fuzz/fuzzer_pass.h
index 4d0861e..cf56e24 100644
--- a/source/fuzz/fuzzer_pass.h
+++ b/source/fuzz/fuzzer_pass.h
@@ -15,9 +15,13 @@
 #ifndef SOURCE_FUZZ_FUZZER_PASS_H_
 #define SOURCE_FUZZ_FUZZER_PASS_H_
 
+#include <functional>
+#include <vector>
+
 #include "source/fuzz/fact_manager.h"
 #include "source/fuzz/fuzzer_context.h"
 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/opt/ir_context.h"
 
 namespace spvtools {
 namespace fuzz {
@@ -48,6 +52,43 @@
     return transformations_;
   }
 
+  // Returns all instructions that are *available* at |inst_it|, which is
+  // required to be inside block |block| of function |function| - that is, all
+  // instructions at global scope and all instructions that strictly dominate
+  // |inst_it|.
+  //
+  // Filters said instructions to return only those that satisfy the
+  // |instruction_is_relevant| predicate.  This, for instance, could ignore all
+  // instructions that have a particular decoration.
+  std::vector<opt::Instruction*> FindAvailableInstructions(
+      const opt::Function& function, opt::BasicBlock* block,
+      opt::BasicBlock::iterator inst_it,
+      std::function<bool(opt::IRContext*, opt::Instruction*)>
+          instruction_is_relevant);
+
+  // A helper method that iterates through each instruction in each block, at
+  // all times tracking an instruction descriptor that allows the latest
+  // instruction to be located even if it has no result id.
+  //
+  // The code to manipulate the instruction descriptor is a bit fiddly, and the
+  // point of this method is to avoiding having to duplicate it in multiple
+  // transformation passes.
+  //
+  // The function |maybe_apply_transformation| is invoked for each instruction
+  // |inst_it| in block |block| of function |function| that is encountered.  The
+  // |instruction_descriptor| parameter to the function object allows |inst_it|
+  // to be identified.
+  //
+  // The job of |maybe_apply_transformation| is to randomly decide whether to
+  // try to apply some transformation, and then - if selected - to attempt to
+  // apply it.
+  void MaybeAddTransformationBeforeEachInstruction(
+      std::function<
+          void(const opt::Function& function, opt::BasicBlock* block,
+               opt::BasicBlock::iterator inst_it,
+               const protobufs::InstructionDescriptor& instruction_descriptor)>
+          maybe_apply_transformation);
+
  private:
   opt::IRContext* ir_context_;
   FactManager* fact_manager_;
@@ -58,4 +99,4 @@
 }  // namespace fuzz
 }  // namespace spvtools
 
-#endif  // #define SOURCE_FUZZ_FUZZER_PASS_H_
+#endif  // SOURCE_FUZZ_FUZZER_PASS_H_
diff --git a/source/fuzz/fuzzer_pass_add_dead_breaks.cpp b/source/fuzz/fuzzer_pass_add_dead_breaks.cpp
index 72cd17b..fa6b098 100644
--- a/source/fuzz/fuzzer_pass_add_dead_breaks.cpp
+++ b/source/fuzz/fuzzer_pass_add_dead_breaks.cpp
@@ -47,12 +47,11 @@
     // ones that turn out to be no good.
     for (auto& block : function) {
       for (auto merge_block_id : merge_block_ids) {
-        // TODO(afd): right now we completely ignore OpPhi instructions at
-        //  merge blocks.  This will lead to interesting opportunities being
-        //  missed.
+        // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2856): right
+        //  now we completely ignore OpPhi instructions at merge blocks.  This
+        //  will lead to interesting opportunities being missed.
         auto candidate_transformation = TransformationAddDeadBreak(
-            block.id(), merge_block_id,
-            GetFuzzerContext()->GetRandomGenerator()->RandomBool(), {});
+            block.id(), merge_block_id, GetFuzzerContext()->ChooseEven(), {});
         if (candidate_transformation.IsApplicable(GetIRContext(),
                                                   *GetFactManager())) {
           // Only consider a transformation as a candidate if it is applicable.
@@ -77,16 +76,15 @@
   while (!candidate_transformations.empty()) {
     // Choose a random index into the sequence of remaining candidate
     // transformations.
-    auto index = GetFuzzerContext()->GetRandomGenerator()->RandomUint32(
-        static_cast<uint32_t>(candidate_transformations.size()));
+    auto index = GetFuzzerContext()->RandomIndex(candidate_transformations);
     // Remove the transformation at the chosen index from the sequence.
     auto transformation = std::move(candidate_transformations[index]);
     candidate_transformations.erase(candidate_transformations.begin() + index);
     // Probabilistically decide whether to try to apply it vs. ignore it, in the
     // case that it is applicable.
     if (transformation.IsApplicable(GetIRContext(), *GetFactManager()) &&
-        GetFuzzerContext()->GetRandomGenerator()->RandomPercentage() >
-            GetFuzzerContext()->GetChanceOfAddingDeadBreak()) {
+        GetFuzzerContext()->ChoosePercentage(
+            GetFuzzerContext()->GetChanceOfAddingDeadBreak())) {
       transformation.Apply(GetIRContext(), GetFactManager());
       *GetTransformations()->add_transformation() = transformation.ToMessage();
     }
diff --git a/source/fuzz/fuzzer_pass_add_dead_breaks.h b/source/fuzz/fuzzer_pass_add_dead_breaks.h
index ad19856..12a5095 100644
--- a/source/fuzz/fuzzer_pass_add_dead_breaks.h
+++ b/source/fuzz/fuzzer_pass_add_dead_breaks.h
@@ -35,4 +35,4 @@
 }  // namespace fuzz
 }  // namespace spvtools
 
-#endif  // #define SOURCE_FUZZ_FUZZER_PASS_ADD_DEAD_BREAKS_H_
+#endif  // SOURCE_FUZZ_FUZZER_PASS_ADD_DEAD_BREAKS_H_
diff --git a/source/fuzz/fuzzer_pass_add_dead_continues.cpp b/source/fuzz/fuzzer_pass_add_dead_continues.cpp
index 2156d36..51bcb91 100644
--- a/source/fuzz/fuzzer_pass_add_dead_continues.cpp
+++ b/source/fuzz/fuzzer_pass_add_dead_continues.cpp
@@ -36,18 +36,17 @@
       // node turns out to be inappropriate (e.g. by not being in a loop) the
       // precondition for the transformation will fail and it will be ignored.
       //
-      // TODO(afd): right now we completely ignore OpPhi instructions at
-      //  merge blocks.  This will lead to interesting opportunities being
-      //  missed.
+      // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2856): right
+      //  now we completely ignore OpPhi instructions at continue targets.
+      //  This will lead to interesting opportunities being missed.
       auto candidate_transformation = TransformationAddDeadContinue(
-          block.id(), GetFuzzerContext()->GetRandomGenerator()->RandomBool(),
-          {});
+          block.id(), GetFuzzerContext()->ChooseEven(), {});
       // Probabilistically decide whether to apply the transformation in the
       // case that it is applicable.
       if (candidate_transformation.IsApplicable(GetIRContext(),
                                                 *GetFactManager()) &&
-          GetFuzzerContext()->GetRandomGenerator()->RandomPercentage() >
-              GetFuzzerContext()->GetChanceOfAddingDeadContinue()) {
+          GetFuzzerContext()->ChoosePercentage(
+              GetFuzzerContext()->GetChanceOfAddingDeadContinue())) {
         candidate_transformation.Apply(GetIRContext(), GetFactManager());
         *GetTransformations()->add_transformation() =
             candidate_transformation.ToMessage();
diff --git a/source/fuzz/fuzzer_pass_add_dead_continues.h b/source/fuzz/fuzzer_pass_add_dead_continues.h
index 6cadc97..d067f1c 100644
--- a/source/fuzz/fuzzer_pass_add_dead_continues.h
+++ b/source/fuzz/fuzzer_pass_add_dead_continues.h
@@ -36,4 +36,4 @@
 }  // namespace fuzz
 }  // namespace spvtools
 
-#endif  // #define SOURCE_FUZZ_FUZZER_PASS_ADD_DEAD_CONTINUES_H_
+#endif  // SOURCE_FUZZ_FUZZER_PASS_ADD_DEAD_CONTINUES_H_
diff --git a/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp b/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp
new file mode 100644
index 0000000..ead8c5c
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp
@@ -0,0 +1,61 @@
+// 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.
+
+#include "source/fuzz/fuzzer_pass_add_no_contraction_decorations.h"
+
+#include "source/fuzz/transformation_add_no_contraction_decoration.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassAddNoContractionDecorations::FuzzerPassAddNoContractionDecorations(
+    opt::IRContext* ir_context, FactManager* fact_manager,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+
+FuzzerPassAddNoContractionDecorations::
+    ~FuzzerPassAddNoContractionDecorations() = default;
+
+void FuzzerPassAddNoContractionDecorations::Apply() {
+  // Consider every instruction in every block in every function.
+  for (auto& function : *GetIRContext()->module()) {
+    for (auto& block : function) {
+      for (auto& inst : block) {
+        // Restrict attention to arithmetic instructions (as defined in the
+        // SPIR-V specification).
+        if (TransformationAddNoContractionDecoration::IsArithmetic(
+                inst.opcode())) {
+          // Randomly choose whether to apply the NoContraction decoration to
+          // this arithmetic instruction.
+          if (GetFuzzerContext()->ChoosePercentage(
+                  GetFuzzerContext()
+                      ->GetChanceOfAddingNoContractionDecoration())) {
+            TransformationAddNoContractionDecoration transformation(
+                inst.result_id());
+            assert(transformation.IsApplicable(GetIRContext(),
+                                               *GetFactManager()) &&
+                   "Transformation should be applicable by construction.");
+            transformation.Apply(GetIRContext(), GetFactManager());
+            *GetTransformations()->add_transformation() =
+                transformation.ToMessage();
+          }
+        }
+      }
+    }
+  }
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/source/fuzz/fuzzer_pass_add_no_contraction_decorations.h b/source/fuzz/fuzzer_pass_add_no_contraction_decorations.h
new file mode 100644
index 0000000..abe5bd7
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_add_no_contraction_decorations.h
@@ -0,0 +1,39 @@
+// 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.
+
+#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_NO_CONTRACTION_DECORATIONS_
+#define SOURCE_FUZZ_FUZZER_PASS_ADD_NO_CONTRACTION_DECORATIONS_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// A pass that applies the NoContraction decoration to arithmetic instructions.
+class FuzzerPassAddNoContractionDecorations : public FuzzerPass {
+ public:
+  FuzzerPassAddNoContractionDecorations(
+      opt::IRContext* ir_context, FactManager* fact_manager,
+      FuzzerContext* fuzzer_context,
+      protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassAddNoContractionDecorations() override;
+
+  void Apply() override;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_ADD_NO_CONTRACTION_DECORATIONS_
diff --git a/source/fuzz/fuzzer_pass_add_useful_constructs.cpp b/source/fuzz/fuzzer_pass_add_useful_constructs.cpp
index 6ac4ae9..8552dfd 100644
--- a/source/fuzz/fuzzer_pass_add_useful_constructs.cpp
+++ b/source/fuzz/fuzzer_pass_add_useful_constructs.cpp
@@ -24,13 +24,11 @@
 namespace spvtools {
 namespace fuzz {
 
-using opt::IRContext;
-
 FuzzerPassAddUsefulConstructs::FuzzerPassAddUsefulConstructs(
     opt::IRContext* ir_context, FactManager* fact_manager,
     FuzzerContext* fuzzer_context,
     protobufs::TransformationSequence* transformations)
-    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations){};
+    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
 
 FuzzerPassAddUsefulConstructs::~FuzzerPassAddUsefulConstructs() = default;
 
diff --git a/source/fuzz/fuzzer_pass_add_useful_constructs.h b/source/fuzz/fuzzer_pass_add_useful_constructs.h
index a8ac9a3..7dc00f1 100644
--- a/source/fuzz/fuzzer_pass_add_useful_constructs.h
+++ b/source/fuzz/fuzzer_pass_add_useful_constructs.h
@@ -43,4 +43,4 @@
 }  // namespace fuzz
 }  // namespace spvtools
 
-#endif  // #define SOURCE_FUZZ_FUZZER_PASS_ADD_USEFUL_CONSTRUCTS_
+#endif  // SOURCE_FUZZ_FUZZER_PASS_ADD_USEFUL_CONSTRUCTS_
diff --git a/source/fuzz/fuzzer_pass_adjust_function_controls.cpp b/source/fuzz/fuzzer_pass_adjust_function_controls.cpp
new file mode 100644
index 0000000..2a11988
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_adjust_function_controls.cpp
@@ -0,0 +1,73 @@
+// 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.
+
+#include "source/fuzz/fuzzer_pass_adjust_function_controls.h"
+
+#include "source/fuzz/transformation_set_function_control.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassAdjustFunctionControls::FuzzerPassAdjustFunctionControls(
+    opt::IRContext* ir_context, FactManager* fact_manager,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+
+FuzzerPassAdjustFunctionControls::~FuzzerPassAdjustFunctionControls() = default;
+
+void FuzzerPassAdjustFunctionControls::Apply() {
+  // Consider every function in the module.
+  for (auto& function : *GetIRContext()->module()) {
+    // Randomly decide whether to adjust this function's controls.
+    if (GetFuzzerContext()->ChoosePercentage(
+            GetFuzzerContext()->GetChanceOfAdjustingFunctionControl())) {
+      // Grab the function control mask for the function in its present form.
+      uint32_t existing_function_control_mask =
+          function.DefInst().GetSingleWordInOperand(0);
+
+      // 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};
+      uint32_t new_function_control_mask =
+          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) &&
+            GetFuzzerContext()->ChooseEven()) {
+          new_function_control_mask |= mask_bit;
+        }
+      }
+
+      // Create and add a transformation.
+      TransformationSetFunctionControl transformation(
+          function.DefInst().result_id(), new_function_control_mask);
+      assert(transformation.IsApplicable(GetIRContext(), *GetFactManager()) &&
+             "Transformation should be applicable by construction.");
+      transformation.Apply(GetIRContext(), GetFactManager());
+      *GetTransformations()->add_transformation() = transformation.ToMessage();
+    }
+  }
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/source/fuzz/fuzzer_pass_adjust_function_controls.h b/source/fuzz/fuzzer_pass_adjust_function_controls.h
new file mode 100644
index 0000000..02d3600
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_adjust_function_controls.h
@@ -0,0 +1,39 @@
+// 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.
+
+#ifndef SOURCE_FUZZ_FUZZER_PASS_ADJUST_FUNCTION_CONTROLS_
+#define SOURCE_FUZZ_FUZZER_PASS_ADJUST_FUNCTION_CONTROLS_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// A pass that adjusts the function controls on OpFunction instructions.
+class FuzzerPassAdjustFunctionControls : public FuzzerPass {
+ public:
+  FuzzerPassAdjustFunctionControls(
+      opt::IRContext* ir_context, FactManager* fact_manager,
+      FuzzerContext* fuzzer_context,
+      protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassAdjustFunctionControls() override;
+
+  void Apply() override;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_ADJUST_FUNCTION_CONTROLS_
diff --git a/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp b/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp
new file mode 100644
index 0000000..ac2408a
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp
@@ -0,0 +1,121 @@
+// 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.
+
+#include "source/fuzz/fuzzer_pass_adjust_loop_controls.h"
+
+#include "source/fuzz/transformation_set_loop_control.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassAdjustLoopControls::FuzzerPassAdjustLoopControls(
+    opt::IRContext* ir_context, FactManager* fact_manager,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+
+FuzzerPassAdjustLoopControls::~FuzzerPassAdjustLoopControls() = default;
+
+void FuzzerPassAdjustLoopControls::Apply() {
+  // Consider every merge instruction in the module (via looking through all
+  // functions and blocks).
+  for (auto& function : *GetIRContext()->module()) {
+    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) {
+          continue;
+        }
+
+        // Decide randomly whether to adjust this loop merge.
+        if (!GetFuzzerContext()->ChoosePercentage(
+                GetFuzzerContext()->GetChanceOfAdjustingLoopControl())) {
+          continue;
+        }
+
+        uint32_t existing_mask = merge_inst->GetSingleWordOperand(
+            TransformationSetLoopControl::kLoopControlMaskInOperandIndex);
+
+        // First, set the new mask to one of None, Unroll or DontUnroll.
+        std::vector<uint32_t> basic_masks = {SpvLoopControlMaskNone,
+                                             SpvLoopControlUnrollMask,
+                                             SpvLoopControlDontUnrollMask};
+        uint32_t new_mask =
+            basic_masks[GetFuzzerContext()->RandomIndex(basic_masks)];
+
+        // For the loop controls that depend on guarantees about what the loop
+        // 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()) {
+            // 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;
+          }
+        }
+
+        // We use 0 for peel count and partial count in the case that we choose
+        // not to set these controls.
+        uint32_t peel_count = 0;
+        uint32_t partial_count = 0;
+
+        // PeelCount and PartialCount are not compatible with DontUnroll, so
+        // we check whether DontUnroll is set.
+        if (!(new_mask & SpvLoopControlDontUnrollMask)) {
+          // 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;
+            // The peel count is chosen randomly - if PeelCount was already set
+            // this will overwrite whatever peel count was previously used.
+            peel_count = GetFuzzerContext()->GetRandomLoopControlPeelCount();
+          }
+          // Similar, but for PartialCount.
+          if (TransformationSetLoopControl::PartialCountIsSupported(
+                  GetIRContext()) &&
+              GetFuzzerContext()->ChooseEven()) {
+            new_mask |= SpvLoopControlPartialCountMask;
+            partial_count =
+                GetFuzzerContext()->GetRandomLoopControlPartialCount();
+          }
+        }
+
+        // Apply the transformation and add it to the output transformation
+        // sequence.
+        TransformationSetLoopControl transformation(block.id(), new_mask,
+                                                    peel_count, partial_count);
+        assert(transformation.IsApplicable(GetIRContext(), *GetFactManager()) &&
+               "Transformation should be applicable by construction.");
+        transformation.Apply(GetIRContext(), GetFactManager());
+        *GetTransformations()->add_transformation() =
+            transformation.ToMessage();
+      }
+    }
+  }
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/source/fuzz/fuzzer_pass_adjust_loop_controls.h b/source/fuzz/fuzzer_pass_adjust_loop_controls.h
new file mode 100644
index 0000000..e945606
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_adjust_loop_controls.h
@@ -0,0 +1,39 @@
+// 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.
+
+#ifndef SOURCE_FUZZ_FUZZER_PASS_ADJUST_LOOP_CONTROLS_
+#define SOURCE_FUZZ_FUZZER_PASS_ADJUST_LOOP_CONTROLS_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// A pass that adjusts the loop controls on OpLoopMerge instructions.
+class FuzzerPassAdjustLoopControls : public FuzzerPass {
+ public:
+  FuzzerPassAdjustLoopControls(
+      opt::IRContext* ir_context, FactManager* fact_manager,
+      FuzzerContext* fuzzer_context,
+      protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassAdjustLoopControls() override;
+
+  void Apply() override;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_ADJUST_LOOP_CONTROLS_
diff --git a/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp b/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp
new file mode 100644
index 0000000..a9d4b32
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp
@@ -0,0 +1,113 @@
+// 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.
+
+#include "source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h"
+
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_set_memory_operands_mask.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassAdjustMemoryOperandsMasks::FuzzerPassAdjustMemoryOperandsMasks(
+    opt::IRContext* ir_context, FactManager* fact_manager,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+
+FuzzerPassAdjustMemoryOperandsMasks::~FuzzerPassAdjustMemoryOperandsMasks() =
+    default;
+
+void FuzzerPassAdjustMemoryOperandsMasks::Apply() {
+  // Consider every block in every function.
+  for (auto& function : *GetIRContext()->module()) {
+    for (auto& block : function) {
+      // Consider every instruction in this block, using an explicit iterator so
+      // that when we find an instruction of interest we can search backwards to
+      // create an id descriptor for it.
+      for (auto inst_it = block.cbegin(); inst_it != block.cend(); ++inst_it) {
+        if (!TransformationSetMemoryOperandsMask::IsMemoryAccess(*inst_it)) {
+          // We are only interested in memory access instructions.
+          continue;
+        }
+
+        std::vector<uint32_t> indices_of_available_masks_to_adjust;
+        // All memory instructions have at least one memory operands mask.
+        indices_of_available_masks_to_adjust.push_back(0);
+        // From SPIR-V 1.4 onwards, OpCopyMemory and OpCopyMemorySized have a
+        // second mask.
+        switch (inst_it->opcode()) {
+          case SpvOpCopyMemory:
+          case SpvOpCopyMemorySized:
+            if (TransformationSetMemoryOperandsMask::
+                    MultipleMemoryOperandMasksAreSupported(GetIRContext())) {
+              indices_of_available_masks_to_adjust.push_back(1);
+            }
+            break;
+          default:
+            break;
+        }
+
+        // Consider the available masks
+        for (auto mask_index : indices_of_available_masks_to_adjust) {
+          // Randomly decide whether to adjust this mask.
+          if (!GetFuzzerContext()->ChoosePercentage(
+                  GetFuzzerContext()
+                      ->GetChanceOfAdjustingMemoryOperandsMask())) {
+            continue;
+          }
+          // Get the existing mask, using None if there was no mask present at
+          // all.
+          auto existing_mask_in_operand_index =
+              TransformationSetMemoryOperandsMask::GetInOperandIndexForMask(
+                  *inst_it, mask_index);
+          auto existing_mask =
+              existing_mask_in_operand_index < inst_it->NumInOperands()
+                  ? inst_it->GetSingleWordOperand(
+                        existing_mask_in_operand_index)
+                  : static_cast<uint32_t>(SpvMemoryAccessMaskNone);
+
+          // 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 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);
+
+          TransformationSetMemoryOperandsMask transformation(
+              MakeInstructionDescriptor(block, inst_it), new_mask, mask_index);
+          assert(
+              transformation.IsApplicable(GetIRContext(), *GetFactManager()) &&
+              "Transformation should be applicable by construction.");
+          transformation.Apply(GetIRContext(), GetFactManager());
+          *GetTransformations()->add_transformation() =
+              transformation.ToMessage();
+        }
+      }
+    }
+  }
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h b/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h
new file mode 100644
index 0000000..c3d7118
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h
@@ -0,0 +1,40 @@
+// 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.
+
+#ifndef SOURCE_FUZZ_FUZZER_PASS_ADJUST_MEMORY_OPERANDS_MASKS_H_
+#define SOURCE_FUZZ_FUZZER_PASS_ADJUST_MEMORY_OPERANDS_MASKS_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// A fuzzer pass to adjust the memory operand masks in memory access
+// instructions.
+class FuzzerPassAdjustMemoryOperandsMasks : public FuzzerPass {
+ public:
+  FuzzerPassAdjustMemoryOperandsMasks(
+      opt::IRContext* ir_context, FactManager* fact_manager,
+      FuzzerContext* fuzzer_context,
+      protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassAdjustMemoryOperandsMasks();
+
+  void Apply() override;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_ADJUST_MEMORY_OPERANDS_MASKS_H_
diff --git a/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp b/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp
new file mode 100644
index 0000000..22654f2
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp
@@ -0,0 +1,76 @@
+// 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.
+
+#include "source/fuzz/fuzzer_pass_adjust_selection_controls.h"
+
+#include "source/fuzz/transformation_set_selection_control.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassAdjustSelectionControls::FuzzerPassAdjustSelectionControls(
+    opt::IRContext* ir_context, FactManager* fact_manager,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+
+FuzzerPassAdjustSelectionControls::~FuzzerPassAdjustSelectionControls() =
+    default;
+
+void FuzzerPassAdjustSelectionControls::Apply() {
+  // Consider every merge instruction in the module (via looking through all
+  // functions and blocks).
+  for (auto& function : *GetIRContext()->module()) {
+    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) {
+          continue;
+        }
+
+        // Choose randomly whether to change the selection control for this
+        // instruction.
+        if (!GetFuzzerContext()->ChoosePercentage(
+                GetFuzzerContext()->GetChanceOfAdjustingSelectionControl())) {
+          continue;
+        }
+
+        // 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)) {
+            continue;
+          }
+          choices.push_back(control);
+        }
+
+        // Apply the transformation and add it to the output transformation
+        // sequence.
+        TransformationSetSelectionControl transformation(
+            block.id(), choices[GetFuzzerContext()->RandomIndex(choices)]);
+        assert(transformation.IsApplicable(GetIRContext(), *GetFactManager()) &&
+               "Transformation should be applicable by construction.");
+        transformation.Apply(GetIRContext(), GetFactManager());
+        *GetTransformations()->add_transformation() =
+            transformation.ToMessage();
+      }
+    }
+  }
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/source/fuzz/fuzzer_pass_adjust_selection_controls.h b/source/fuzz/fuzzer_pass_adjust_selection_controls.h
new file mode 100644
index 0000000..b5b255c
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_adjust_selection_controls.h
@@ -0,0 +1,39 @@
+// 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.
+
+#ifndef SOURCE_FUZZ_FUZZER_PASS_ADJUST_SELECTION_CONTROLS_
+#define SOURCE_FUZZ_FUZZER_PASS_ADJUST_SELECTION_CONTROLS_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// A pass that adjusts the selection controls on OpSelectionMerge instructions.
+class FuzzerPassAdjustSelectionControls : public FuzzerPass {
+ public:
+  FuzzerPassAdjustSelectionControls(
+      opt::IRContext* ir_context, FactManager* fact_manager,
+      FuzzerContext* fuzzer_context,
+      protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassAdjustSelectionControls() override;
+
+  void Apply() override;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_ADJUST_SELECTION_CONTROLS_
diff --git a/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp b/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp
new file mode 100644
index 0000000..6ff42ca
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp
@@ -0,0 +1,147 @@
+// 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.
+
+#include "source/fuzz/fuzzer_pass_apply_id_synonyms.h"
+
+#include "source/fuzz/data_descriptor.h"
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/id_use_descriptor.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_composite_extract.h"
+#include "source/fuzz/transformation_replace_id_with_synonym.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassApplyIdSynonyms::FuzzerPassApplyIdSynonyms(
+    opt::IRContext* ir_context, FactManager* fact_manager,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+
+FuzzerPassApplyIdSynonyms::~FuzzerPassApplyIdSynonyms() = default;
+
+void FuzzerPassApplyIdSynonyms::Apply() {
+  for (auto id_with_known_synonyms :
+       GetFactManager()->GetIdsForWhichSynonymsAreKnown(GetIRContext())) {
+    // Gather up all uses of |id_with_known_synonym|, and then subsequently
+    // iterate over these uses.  We use this separation because, when
+    // considering a given use, we might apply a transformation that will
+    // invalidate the def-use manager.
+    std::vector<std::pair<opt::Instruction*, uint32_t>> uses;
+    GetIRContext()->get_def_use_mgr()->ForEachUse(
+        id_with_known_synonyms,
+        [&uses](opt::Instruction* use_inst, uint32_t use_index) -> void {
+          uses.emplace_back(
+              std::pair<opt::Instruction*, uint32_t>(use_inst, use_index));
+        });
+
+    for (auto& use : uses) {
+      auto use_inst = use.first;
+      auto use_index = use.second;
+      auto block_containing_use = GetIRContext()->get_instr_block(use_inst);
+      // The use might not be in a block; e.g. it could be a decoration.
+      if (!block_containing_use) {
+        continue;
+      }
+      if (!GetFuzzerContext()->ChoosePercentage(
+              GetFuzzerContext()->GetChanceOfReplacingIdWithSynonym())) {
+        continue;
+      }
+      // |use_index| is the absolute index of the operand.  We require
+      // the index of the operand restricted to input operands only, so
+      // we subtract the number of non-input operands from |use_index|.
+      uint32_t use_in_operand_index =
+          use_index - use_inst->NumOperands() + use_inst->NumInOperands();
+      if (!TransformationReplaceIdWithSynonym::UseCanBeReplacedWithSynonym(
+              GetIRContext(), use_inst, use_in_operand_index)) {
+        continue;
+      }
+
+      std::vector<const protobufs::DataDescriptor*> synonyms_to_try;
+      for (auto& data_descriptor : GetFactManager()->GetSynonymsForId(
+               id_with_known_synonyms, GetIRContext())) {
+        protobufs::DataDescriptor descriptor_for_this_id =
+            MakeDataDescriptor(id_with_known_synonyms, {});
+        if (DataDescriptorEquals()(data_descriptor, &descriptor_for_this_id)) {
+          // Exclude the fact that the id is synonymous with itself.
+          continue;
+        }
+        synonyms_to_try.push_back(data_descriptor);
+      }
+      while (!synonyms_to_try.empty()) {
+        auto synonym_index = GetFuzzerContext()->RandomIndex(synonyms_to_try);
+        auto synonym_to_try = synonyms_to_try[synonym_index];
+        synonyms_to_try.erase(synonyms_to_try.begin() + synonym_index);
+
+        if (synonym_to_try->index_size() > 0 &&
+            use_inst->opcode() == SpvOpPhi) {
+          // We are trying to replace an operand to an OpPhi.  This means
+          // we cannot use a composite synonym, because that requires
+          // extracting a component from a composite and we cannot insert
+          // an extract instruction before an OpPhi.
+          //
+          // TODO(afd): We could consider inserting the extract instruction
+          //  into the relevant parent block of the OpPhi.
+          continue;
+        }
+
+        if (!TransformationReplaceIdWithSynonym::IdsIsAvailableAtUse(
+                GetIRContext(), use_inst, use_in_operand_index,
+                synonym_to_try->object())) {
+          continue;
+        }
+
+        // We either replace the use with an id known to be synonymous, or
+        // an id that will hold the result of extracting a synonym from a
+        // composite.
+        uint32_t id_with_which_to_replace_use;
+        if (synonym_to_try->index_size() == 0) {
+          id_with_which_to_replace_use = synonym_to_try->object();
+        } else {
+          id_with_which_to_replace_use = GetFuzzerContext()->GetFreshId();
+          protobufs::InstructionDescriptor instruction_to_insert_before =
+              MakeInstructionDescriptor(GetIRContext(), use_inst);
+          TransformationCompositeExtract composite_extract_transformation(
+              instruction_to_insert_before, id_with_which_to_replace_use,
+              synonym_to_try->object(),
+              fuzzerutil::RepeatedFieldToVector(synonym_to_try->index()));
+          assert(composite_extract_transformation.IsApplicable(
+                     GetIRContext(), *GetFactManager()) &&
+                 "Transformation should be applicable by construction.");
+          composite_extract_transformation.Apply(GetIRContext(),
+                                                 GetFactManager());
+          *GetTransformations()->add_transformation() =
+              composite_extract_transformation.ToMessage();
+        }
+
+        TransformationReplaceIdWithSynonym replace_id_transformation(
+            MakeIdUseDescriptorFromUse(GetIRContext(), use_inst,
+                                       use_in_operand_index),
+            id_with_which_to_replace_use);
+
+        // The transformation should be applicable by construction.
+        assert(replace_id_transformation.IsApplicable(GetIRContext(),
+                                                      *GetFactManager()));
+        replace_id_transformation.Apply(GetIRContext(), GetFactManager());
+        *GetTransformations()->add_transformation() =
+            replace_id_transformation.ToMessage();
+        break;
+      }
+    }
+  }
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/source/fuzz/fuzzer_pass_apply_id_synonyms.h b/source/fuzz/fuzzer_pass_apply_id_synonyms.h
new file mode 100644
index 0000000..1a0748e
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_apply_id_synonyms.h
@@ -0,0 +1,42 @@
+// 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.
+
+#ifndef SOURCE_FUZZ_FUZZER_PASS_APPLY_ID_SYNONYMS_
+#define SOURCE_FUZZ_FUZZER_PASS_APPLY_ID_SYNONYMS_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// A pass that replaces ids with other ids, or accesses into structures, that
+// are known to hold the same values.
+class FuzzerPassApplyIdSynonyms : public FuzzerPass {
+ public:
+  FuzzerPassApplyIdSynonyms(opt::IRContext* ir_context,
+                            FactManager* fact_manager,
+                            FuzzerContext* fuzzer_context,
+                            protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassApplyIdSynonyms() override;
+
+  void Apply() override;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_APPLY_ID_SYNONYMS_
diff --git a/source/fuzz/fuzzer_pass_construct_composites.cpp b/source/fuzz/fuzzer_pass_construct_composites.cpp
new file mode 100644
index 0000000..9eb5631
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_construct_composites.cpp
@@ -0,0 +1,363 @@
+// 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.
+
+#include "source/fuzz/fuzzer_pass_construct_composites.h"
+
+#include <cmath>
+#include <memory>
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/transformation_composite_construct.h"
+#include "source/util/make_unique.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassConstructComposites::FuzzerPassConstructComposites(
+    opt::IRContext* ir_context, FactManager* fact_manager,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+
+FuzzerPassConstructComposites::~FuzzerPassConstructComposites() = default;
+
+void FuzzerPassConstructComposites::Apply() {
+  // Gather up the ids of all composite types.
+  std::vector<uint32_t> composite_type_ids;
+  for (auto& inst : GetIRContext()->types_values()) {
+    if (fuzzerutil::IsCompositeType(
+            GetIRContext()->get_type_mgr()->GetType(inst.result_id()))) {
+      composite_type_ids.push_back(inst.result_id());
+    }
+  }
+
+  MaybeAddTransformationBeforeEachInstruction(
+      [this, &composite_type_ids](
+          const opt::Function& function, opt::BasicBlock* block,
+          opt::BasicBlock::iterator inst_it,
+          const protobufs::InstructionDescriptor& instruction_descriptor)
+          -> void {
+        // Check whether it is legitimate to insert a composite construction
+        // before the instruction.
+        if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(
+                SpvOpCompositeConstruct, inst_it)) {
+          return;
+        }
+
+        // Randomly decide whether to try inserting an object copy here.
+        if (!GetFuzzerContext()->ChoosePercentage(
+                GetFuzzerContext()->GetChanceOfConstructingComposite())) {
+          return;
+        }
+
+        // For each instruction that is available at this program point (i.e. an
+        // instruction that is global or whose definition strictly dominates the
+        // program point) and suitable for making a synonym of, associate it
+        // with the id of its result type.
+        TypeIdToInstructions type_id_to_available_instructions;
+        for (auto instruction : FindAvailableInstructions(
+                 function, block, inst_it, fuzzerutil::CanMakeSynonymOf)) {
+          RecordAvailableInstruction(instruction,
+                                     &type_id_to_available_instructions);
+        }
+
+        // At this point, |composite_type_ids| captures all the composite types
+        // we could try to create, while |type_id_to_available_instructions|
+        // captures all the available result ids we might use, organized by
+        // type.
+
+        // Now we try to find a composite that we can construct.  We might not
+        // manage, if there is a paucity of available ingredients in the module
+        // (e.g. if our only available composite was a boolean vector and we had
+        // no instructions generating boolean result types available).
+        //
+        // If we succeed, |chosen_composite_type| will end up being non-zero,
+        // and |constructor_arguments| will end up giving us result ids suitable
+        // for constructing a composite of that type.  Otherwise these variables
+        // will remain 0 and null respectively.
+        uint32_t chosen_composite_type = 0;
+        std::unique_ptr<std::vector<uint32_t>> constructor_arguments = nullptr;
+
+        // Initially, all composite type ids are available for us to try.  Keep
+        // trying until we run out of options.
+        auto composites_to_try_constructing = composite_type_ids;
+        while (!composites_to_try_constructing.empty()) {
+          // Remove a composite type from the composite types left for us to
+          // try.
+          auto index =
+              GetFuzzerContext()->RandomIndex(composites_to_try_constructing);
+          auto next_composite_to_try_constructing =
+              composites_to_try_constructing[index];
+          composites_to_try_constructing.erase(
+              composites_to_try_constructing.begin() + index);
+
+          // Now try to construct a composite of this type, using an appropriate
+          // helper method depending on the kind of composite type.
+          auto composite_type = GetIRContext()->get_type_mgr()->GetType(
+              next_composite_to_try_constructing);
+          if (auto array_type = composite_type->AsArray()) {
+            constructor_arguments = TryConstructingArrayComposite(
+                *array_type, type_id_to_available_instructions);
+          } else if (auto matrix_type = composite_type->AsMatrix()) {
+            constructor_arguments = TryConstructingMatrixComposite(
+                *matrix_type, type_id_to_available_instructions);
+          } else if (auto struct_type = composite_type->AsStruct()) {
+            constructor_arguments = TryConstructingStructComposite(
+                *struct_type, type_id_to_available_instructions);
+          } else {
+            auto vector_type = composite_type->AsVector();
+            assert(vector_type &&
+                   "The space of possible composite types should be covered by "
+                   "the above cases.");
+            constructor_arguments = TryConstructingVectorComposite(
+                *vector_type, type_id_to_available_instructions);
+          }
+          if (constructor_arguments != nullptr) {
+            // We succeeded!  Note the composite type we finally settled on, and
+            // exit from the loop.
+            chosen_composite_type = next_composite_to_try_constructing;
+            break;
+          }
+        }
+
+        if (!chosen_composite_type) {
+          // We did not manage to make a composite; return 0 to indicate that no
+          // instructions were added.
+          assert(constructor_arguments == nullptr);
+          return;
+        }
+        assert(constructor_arguments != nullptr);
+
+        // Make and apply a transformation.
+        TransformationCompositeConstruct transformation(
+            chosen_composite_type, *constructor_arguments,
+            instruction_descriptor, GetFuzzerContext()->GetFreshId());
+        assert(transformation.IsApplicable(GetIRContext(), *GetFactManager()) &&
+               "This transformation should be applicable by construction.");
+        transformation.Apply(GetIRContext(), GetFactManager());
+        *GetTransformations()->add_transformation() =
+            transformation.ToMessage();
+        // Indicate that one instruction was added.
+      });
+}
+
+void FuzzerPassConstructComposites::RecordAvailableInstruction(
+    opt::Instruction* inst,
+    TypeIdToInstructions* type_id_to_available_instructions) {
+  if (type_id_to_available_instructions->count(inst->type_id()) == 0) {
+    (*type_id_to_available_instructions)[inst->type_id()] = {};
+  }
+  type_id_to_available_instructions->at(inst->type_id()).push_back(inst);
+}
+
+std::unique_ptr<std::vector<uint32_t>>
+FuzzerPassConstructComposites::TryConstructingArrayComposite(
+    const opt::analysis::Array& array_type,
+    const TypeIdToInstructions& type_id_to_available_instructions) {
+  // At present we assume arrays have a constant size.
+  assert(array_type.length_info().words.size() == 2);
+  assert(array_type.length_info().words[0] ==
+         opt::analysis::Array::LengthInfo::kConstant);
+
+  auto result = MakeUnique<std::vector<uint32_t>>();
+
+  // Get the element type for the array.
+  auto element_type_id =
+      GetIRContext()->get_type_mgr()->GetId(array_type.element_type());
+
+  // Get all instructions at our disposal that compute something of this element
+  // type.
+  auto available_instructions =
+      type_id_to_available_instructions.find(element_type_id);
+
+  if (available_instructions == type_id_to_available_instructions.cend()) {
+    // If there are not any instructions available that compute the element type
+    // of the array then we are not in a position to construct a composite with
+    // this array type.
+    return nullptr;
+  }
+  for (uint32_t index = 0; index < array_type.length_info().words[1]; index++) {
+    result->push_back(available_instructions
+                          ->second[GetFuzzerContext()->RandomIndex(
+                              available_instructions->second)]
+                          ->result_id());
+  }
+  return result;
+}
+
+std::unique_ptr<std::vector<uint32_t>>
+FuzzerPassConstructComposites::TryConstructingMatrixComposite(
+    const opt::analysis::Matrix& matrix_type,
+    const TypeIdToInstructions& type_id_to_available_instructions) {
+  auto result = MakeUnique<std::vector<uint32_t>>();
+
+  // Get the element type for the matrix.
+  auto element_type_id =
+      GetIRContext()->get_type_mgr()->GetId(matrix_type.element_type());
+
+  // Get all instructions at our disposal that compute something of this element
+  // type.
+  auto available_instructions =
+      type_id_to_available_instructions.find(element_type_id);
+
+  if (available_instructions == type_id_to_available_instructions.cend()) {
+    // If there are not any instructions available that compute the element type
+    // of the matrix then we are not in a position to construct a composite with
+    // this matrix type.
+    return nullptr;
+  }
+  for (uint32_t index = 0; index < matrix_type.element_count(); index++) {
+    result->push_back(available_instructions
+                          ->second[GetFuzzerContext()->RandomIndex(
+                              available_instructions->second)]
+                          ->result_id());
+  }
+  return result;
+}
+
+std::unique_ptr<std::vector<uint32_t>>
+FuzzerPassConstructComposites::TryConstructingStructComposite(
+    const opt::analysis::Struct& struct_type,
+    const TypeIdToInstructions& type_id_to_available_instructions) {
+  auto result = MakeUnique<std::vector<uint32_t>>();
+  // Consider the type of each field of the struct.
+  for (auto element_type : struct_type.element_types()) {
+    auto element_type_id = GetIRContext()->get_type_mgr()->GetId(element_type);
+    // Find the instructions at our disposal that compute something of the field
+    // type.
+    auto available_instructions =
+        type_id_to_available_instructions.find(element_type_id);
+    if (available_instructions == type_id_to_available_instructions.cend()) {
+      // If there are no such instructions, we cannot construct a composite of
+      // this struct type.
+      return nullptr;
+    }
+    result->push_back(available_instructions
+                          ->second[GetFuzzerContext()->RandomIndex(
+                              available_instructions->second)]
+                          ->result_id());
+  }
+  return result;
+}
+
+std::unique_ptr<std::vector<uint32_t>>
+FuzzerPassConstructComposites::TryConstructingVectorComposite(
+    const opt::analysis::Vector& vector_type,
+    const TypeIdToInstructions& type_id_to_available_instructions) {
+  // Get details of the type underlying the vector, and the width of the vector,
+  // for convenience.
+  auto element_type = vector_type.element_type();
+  auto element_count = vector_type.element_count();
+
+  // Collect a mapping, from type id to width, for scalar/vector types that are
+  // smaller in width than |vector_type|, but that have the same underlying
+  // type.  For example, if |vector_type| is vec4, the mapping will be:
+  //   { float -> 1, vec2 -> 2, vec3 -> 3 }
+  // The mapping will have missing entries if some of these types do not exist.
+
+  std::map<uint32_t, uint32_t> smaller_vector_type_id_to_width;
+  // Add the underlying type.  This id must exist, in order for |vector_type| to
+  // exist.
+  auto scalar_type_id = GetIRContext()->get_type_mgr()->GetId(element_type);
+  smaller_vector_type_id_to_width[scalar_type_id] = 1;
+
+  // Now add every vector type with width at least 2, and less than the width of
+  // |vector_type|.
+  for (uint32_t width = 2; width < element_count; width++) {
+    opt::analysis::Vector smaller_vector_type(vector_type.element_type(),
+                                              width);
+    auto smaller_vector_type_id =
+        GetIRContext()->get_type_mgr()->GetId(&smaller_vector_type);
+    // We might find that there is no declared type of this smaller width.
+    // For example, a module can declare vec4 without having declared vec2 or
+    // vec3.
+    if (smaller_vector_type_id) {
+      smaller_vector_type_id_to_width[smaller_vector_type_id] = width;
+    }
+  }
+
+  // Now we know the types that are available to us, we set about populating a
+  // vector of the right length.  We do this by deciding, with no order in mind,
+  // which instructions we will use to populate the vector, and subsequently
+  // randomly choosing an order.  This is to avoid biasing construction of
+  // vectors with smaller vectors to the left and scalars to the right.  That is
+  // a concern because, e.g. in the case of populating a vec4, if we populate
+  // the constructor instructions left-to-right, we can always choose a vec3 to
+  // construct the first three elements, but can only choose a vec3 to construct
+  // the last three elements if we chose a float to construct the first element
+  // (otherwise there will not be space left for a vec3).
+
+  uint32_t vector_slots_used = 0;
+  // The instructions we will use to construct the vector, in no particular
+  // order at this stage.
+  std::vector<opt::Instruction*> instructions_to_use;
+
+  while (vector_slots_used < vector_type.element_count()) {
+    std::vector<opt::Instruction*> instructions_to_choose_from;
+    for (auto& entry : smaller_vector_type_id_to_width) {
+      if (entry.second >
+          std::min(vector_type.element_count() - 1,
+                   vector_type.element_count() - vector_slots_used)) {
+        continue;
+      }
+      auto available_instructions =
+          type_id_to_available_instructions.find(entry.first);
+      if (available_instructions == type_id_to_available_instructions.cend()) {
+        continue;
+      }
+      instructions_to_choose_from.insert(instructions_to_choose_from.end(),
+                                         available_instructions->second.begin(),
+                                         available_instructions->second.end());
+    }
+    if (instructions_to_choose_from.empty()) {
+      // We may get unlucky and find that there are not any instructions to
+      // choose from.  In this case we give up constructing a composite of this
+      // vector type.  It might be that we could construct the composite in
+      // another manner, so we could opt to retry a few times here, but it is
+      // simpler to just give up on the basis that this will not happen
+      // frequently.
+      return nullptr;
+    }
+    auto instruction_to_use =
+        instructions_to_choose_from[GetFuzzerContext()->RandomIndex(
+            instructions_to_choose_from)];
+    instructions_to_use.push_back(instruction_to_use);
+    auto chosen_type =
+        GetIRContext()->get_type_mgr()->GetType(instruction_to_use->type_id());
+    if (chosen_type->AsVector()) {
+      assert(chosen_type->AsVector()->element_type() == element_type);
+      assert(chosen_type->AsVector()->element_count() < element_count);
+      assert(chosen_type->AsVector()->element_count() <=
+             element_count - vector_slots_used);
+      vector_slots_used += chosen_type->AsVector()->element_count();
+    } else {
+      assert(chosen_type == element_type);
+      vector_slots_used += 1;
+    }
+  }
+  assert(vector_slots_used == vector_type.element_count());
+
+  auto result = MakeUnique<std::vector<uint32_t>>();
+  std::vector<uint32_t> operands;
+  while (!instructions_to_use.empty()) {
+    auto index = GetFuzzerContext()->RandomIndex(instructions_to_use);
+    result->push_back(instructions_to_use[index]->result_id());
+    instructions_to_use.erase(instructions_to_use.begin() + index);
+  }
+  assert(result->size() > 1);
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/source/fuzz/fuzzer_pass_construct_composites.h b/source/fuzz/fuzzer_pass_construct_composites.h
new file mode 100644
index 0000000..99ef31f
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_construct_composites.h
@@ -0,0 +1,79 @@
+// 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.
+
+#ifndef SOURCE_FUZZ_FUZZER_PASS_CONSTRUCT_COMPOSITES_H_
+#define SOURCE_FUZZ_FUZZER_PASS_CONSTRUCT_COMPOSITES_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+#include <map>
+#include <vector>
+
+namespace spvtools {
+namespace fuzz {
+
+// A fuzzer pass for constructing composite objects from smaller objects.
+class FuzzerPassConstructComposites : public FuzzerPass {
+ public:
+  FuzzerPassConstructComposites(
+      opt::IRContext* ir_context, FactManager* fact_manager,
+      FuzzerContext* fuzzer_context,
+      protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassConstructComposites();
+
+  void Apply() override;
+
+ private:
+  // Used to map a type id to relevant instructions whose result type matches
+  // the type id.
+  typedef std::map<uint32_t, std::vector<opt::Instruction*>>
+      TypeIdToInstructions;
+
+  // Considers all instructions that are available at |inst| - instructions
+  // whose results could be packed into a composite - and updates
+  // |type_id_to_available_instructions| so that each such instruction is
+  // associated with its the id of its result type.
+  void RecordAvailableInstruction(
+      opt::Instruction* inst,
+      TypeIdToInstructions* type_id_to_available_instructions);
+
+  // Attempts to find suitable instruction result ids from the values of
+  // |type_id_to_available_instructions| that would allow a composite of type
+  // |array_type| to be constructed.  Returns said ids if they can be found.
+  // Returns |nullptr| otherwise.
+  std::unique_ptr<std::vector<uint32_t>> TryConstructingArrayComposite(
+      const opt::analysis::Array& array_type,
+      const TypeIdToInstructions& type_id_to_available_instructions);
+
+  // Similar to TryConstructingArrayComposite, but for matrices.
+  std::unique_ptr<std::vector<uint32_t>> TryConstructingMatrixComposite(
+      const opt::analysis::Matrix& matrix_type,
+      const TypeIdToInstructions& type_id_to_available_instructions);
+
+  // Similar to TryConstructingArrayComposite, but for structs.
+  std::unique_ptr<std::vector<uint32_t>> TryConstructingStructComposite(
+      const opt::analysis::Struct& struct_type,
+      const TypeIdToInstructions& type_id_to_available_instructions);
+
+  // Similar to TryConstructingArrayComposite, but for vectors.
+  std::unique_ptr<std::vector<uint32_t>> TryConstructingVectorComposite(
+      const opt::analysis::Vector& vector_type,
+      const TypeIdToInstructions& type_id_to_available_instructions);
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_CONSTRUCT_COMPOSITES_H_
diff --git a/source/fuzz/fuzzer_pass_copy_objects.cpp b/source/fuzz/fuzzer_pass_copy_objects.cpp
new file mode 100644
index 0000000..35b15a3
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_copy_objects.cpp
@@ -0,0 +1,80 @@
+// 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.
+
+#include "source/fuzz/fuzzer_pass_copy_objects.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/transformation_copy_object.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassCopyObjects::FuzzerPassCopyObjects(
+    opt::IRContext* ir_context, FactManager* fact_manager,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+
+FuzzerPassCopyObjects::~FuzzerPassCopyObjects() = default;
+
+void FuzzerPassCopyObjects::Apply() {
+  MaybeAddTransformationBeforeEachInstruction(
+      [this](const opt::Function& function, opt::BasicBlock* block,
+             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");
+
+        // Check whether it is legitimate to insert a copy before this
+        // instruction.
+        if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCopyObject,
+                                                          inst_it)) {
+          return;
+        }
+
+        // Randomly decide whether to try inserting an object copy here.
+        if (!GetFuzzerContext()->ChoosePercentage(
+                GetFuzzerContext()->GetChanceOfCopyingObject())) {
+          return;
+        }
+
+        std::vector<opt::Instruction*> relevant_instructions =
+            FindAvailableInstructions(function, block, inst_it,
+                                      fuzzerutil::CanMakeSynonymOf);
+
+        // At this point, |relevant_instructions| contains all the instructions
+        // we might think of copying.
+        if (relevant_instructions.empty()) {
+          return;
+        }
+
+        // Choose a copyable instruction at random, and create and apply an
+        // object copying transformation based on it.
+        uint32_t index = GetFuzzerContext()->RandomIndex(relevant_instructions);
+        TransformationCopyObject transformation(
+            relevant_instructions[index]->result_id(), instruction_descriptor,
+            GetFuzzerContext()->GetFreshId());
+        assert(transformation.IsApplicable(GetIRContext(), *GetFactManager()) &&
+               "This transformation should be applicable by construction.");
+        transformation.Apply(GetIRContext(), GetFactManager());
+        *GetTransformations()->add_transformation() =
+            transformation.ToMessage();
+      });
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/source/fuzz/fuzzer_pass_copy_objects.h b/source/fuzz/fuzzer_pass_copy_objects.h
new file mode 100644
index 0000000..5419459
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_copy_objects.h
@@ -0,0 +1,38 @@
+// 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.
+
+#ifndef SOURCE_FUZZ_FUZZER_PASS_COPY_OBJECTS_H_
+#define SOURCE_FUZZ_FUZZER_PASS_COPY_OBJECTS_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// A fuzzer pass for adding adding copies of objects to the module.
+class FuzzerPassCopyObjects : public FuzzerPass {
+ public:
+  FuzzerPassCopyObjects(opt::IRContext* ir_context, FactManager* fact_manager,
+                        FuzzerContext* fuzzer_context,
+                        protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassCopyObjects();
+
+  void Apply() override;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_COPY_OBJECTS_H_
diff --git a/source/fuzz/fuzzer_pass_obfuscate_constants.cpp b/source/fuzz/fuzzer_pass_obfuscate_constants.cpp
index ff52ea9..3df11ae 100644
--- a/source/fuzz/fuzzer_pass_obfuscate_constants.cpp
+++ b/source/fuzz/fuzzer_pass_obfuscate_constants.cpp
@@ -16,6 +16,7 @@
 
 #include <cmath>
 
+#include "source/fuzz/instruction_descriptor.h"
 #include "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h"
 #include "source/fuzz/transformation_replace_constant_with_uniform.h"
 #include "source/opt/ir_context.h"
@@ -48,14 +49,12 @@
   // a 'greater than' or 'less than' kind of opcode, and then select a
   // random opcode from the resulting subset.
   SpvOp comparison_opcode;
-  if (GetFuzzerContext()->GetRandomGenerator()->RandomBool()) {
-    comparison_opcode = greater_than_opcodes
-        [GetFuzzerContext()->GetRandomGenerator()->RandomUint32(
-            static_cast<uint32_t>(greater_than_opcodes.size()))];
+  if (GetFuzzerContext()->ChooseEven()) {
+    comparison_opcode = greater_than_opcodes[GetFuzzerContext()->RandomIndex(
+        greater_than_opcodes)];
   } else {
-    comparison_opcode = less_than_opcodes
-        [GetFuzzerContext()->GetRandomGenerator()->RandomUint32(
-            static_cast<uint32_t>(less_than_opcodes.size()))];
+    comparison_opcode =
+        less_than_opcodes[GetFuzzerContext()->RandomIndex(less_than_opcodes)];
   }
 
   // We now need to decide how to order constant_id_1 and constant_id_2 such
@@ -103,12 +102,12 @@
   for (uint32_t index : {0u, 1u}) {
     // We randomly decide, based on the current depth of obfuscation, whether
     // to further obfuscate this operand.
-    if (GetFuzzerContext()->GoDeeperInConstantObfuscation()(
-            depth, GetFuzzerContext()->GetRandomGenerator())) {
-      auto in_operand_use = transformation::MakeIdUseDescriptor(
+    if (GetFuzzerContext()->GoDeeperInConstantObfuscation(depth)) {
+      auto in_operand_use = MakeIdUseDescriptor(
           binary_operator_instruction->GetSingleWordInOperand(index),
-          binary_operator_instruction->opcode(), index,
-          binary_operator_instruction->result_id(), 0);
+          MakeInstructionDescriptor(binary_operator_instruction->result_id(),
+                                    binary_operator_instruction->opcode(), 0),
+          index);
       ObfuscateConstant(depth + 1, in_operand_use);
     }
   }
@@ -252,9 +251,9 @@
     // elements with known values.
     return;
   }
-  auto chosen_type_id = available_types_with_uniforms
-      [GetFuzzerContext()->GetRandomGenerator()->RandomUint32(
-          static_cast<uint32_t>(available_types_with_uniforms.size()))];
+  auto chosen_type_id =
+      available_types_with_uniforms[GetFuzzerContext()->RandomIndex(
+          available_types_with_uniforms)];
   auto available_constants =
       GetFactManager()->GetConstantsAvailableFromUniformsForType(
           GetIRContext(), chosen_type_id);
@@ -269,15 +268,12 @@
 
   // We know we have at least two known-to-be-constant uniforms of the chosen
   // type.  Pick one of them at random.
-  auto constant_index_1 =
-      GetFuzzerContext()->GetRandomGenerator()->RandomUint32(
-          static_cast<uint32_t>(available_constants.size()));
+  auto constant_index_1 = GetFuzzerContext()->RandomIndex(available_constants);
   uint32_t constant_index_2;
 
   // Now choose another one distinct from the first one.
   do {
-    constant_index_2 = GetFuzzerContext()->GetRandomGenerator()->RandomUint32(
-        static_cast<uint32_t>(available_constants.size()));
+    constant_index_2 = GetFuzzerContext()->RandomIndex(available_constants);
   } while (constant_index_1 == constant_index_2);
 
   auto constant_id_1 = available_constants[constant_index_1];
@@ -321,9 +317,7 @@
 
   // Choose a random available uniform known to be equal to the constant.
   protobufs::UniformBufferElementDescriptor uniform_descriptor =
-      uniform_descriptors
-          [GetFuzzerContext()->GetRandomGenerator()->RandomUint32(
-              static_cast<uint32_t>(uniform_descriptors.size()))];
+      uniform_descriptors[GetFuzzerContext()->RandomIndex(uniform_descriptors)];
   // Create, apply and record a transformation to replace the constant use with
   // the result of a load from the chosen uniform.
   auto transformation = TransformationReplaceConstantWithUniform(
@@ -374,14 +368,17 @@
       // it.
       protobufs::IdUseDescriptor id_use_descriptor;
       id_use_descriptor.set_id_of_interest(operand_id);
-      id_use_descriptor.set_target_instruction_opcode(inst.opcode());
+      id_use_descriptor.mutable_enclosing_instruction()
+          ->set_target_instruction_opcode(inst.opcode());
+      id_use_descriptor.mutable_enclosing_instruction()
+          ->set_base_instruction_result_id(base_instruction_result_id);
+      id_use_descriptor.mutable_enclosing_instruction()
+          ->set_num_opcodes_to_ignore(
+              skipped_opcode_count.find(inst.opcode()) ==
+                      skipped_opcode_count.end()
+                  ? 0
+                  : skipped_opcode_count.at(inst.opcode()));
       id_use_descriptor.set_in_operand_index(in_operand_index);
-      id_use_descriptor.set_base_instruction_result_id(
-          base_instruction_result_id);
-      id_use_descriptor.set_num_opcodes_to_ignore(
-          skipped_opcode_count.find(inst.opcode()) == skipped_opcode_count.end()
-              ? 0
-              : skipped_opcode_count.at(inst.opcode()));
       constant_uses->push_back(id_use_descriptor);
     } break;
     default:
@@ -445,13 +442,12 @@
   // Go through the constant uses in a random order by repeatedly pulling out a
   // constant use at a random index.
   while (!constant_uses.empty()) {
-    auto index = GetFuzzerContext()->GetRandomGenerator()->RandomUint32(
-        static_cast<uint32_t>(constant_uses.size()));
+    auto index = GetFuzzerContext()->RandomIndex(constant_uses);
     auto constant_use = std::move(constant_uses[index]);
     constant_uses.erase(constant_uses.begin() + index);
     // Decide probabilistically whether to skip or obfuscate this constant use.
-    if (GetFuzzerContext()->GetRandomGenerator()->RandomPercentage() >
-        GetFuzzerContext()->GetChanceOfObfuscatingConstant()) {
+    if (!GetFuzzerContext()->ChoosePercentage(
+            GetFuzzerContext()->GetChanceOfObfuscatingConstant())) {
       continue;
     }
     ObfuscateConstant(0, constant_use);
diff --git a/source/fuzz/fuzzer_pass_obfuscate_constants.h b/source/fuzz/fuzzer_pass_obfuscate_constants.h
index 03477a5..f34717b 100644
--- a/source/fuzz/fuzzer_pass_obfuscate_constants.h
+++ b/source/fuzz/fuzzer_pass_obfuscate_constants.h
@@ -104,4 +104,4 @@
 }  // namespace fuzz
 }  // namespace spvtools
 
-#endif  // #define SOURCE_FUZZ_FUZZER_PASS_OBFUSCATE_CONSTANTS_
+#endif  // SOURCE_FUZZ_FUZZER_PASS_OBFUSCATE_CONSTANTS_
diff --git a/source/fuzz/fuzzer_pass_permute_blocks.cpp b/source/fuzz/fuzzer_pass_permute_blocks.cpp
index 7ab2ee3..af6d2a5 100644
--- a/source/fuzz/fuzzer_pass_permute_blocks.cpp
+++ b/source/fuzz/fuzzer_pass_permute_blocks.cpp
@@ -57,8 +57,8 @@
     // would provide more freedom for A to move.
     for (auto id = block_ids.rbegin(); id != block_ids.rend(); ++id) {
       // Randomly decide whether to ignore the block id.
-      if (GetFuzzerContext()->GetRandomGenerator()->RandomPercentage() >
-          GetFuzzerContext()->GetChanceOfMovingBlockDown()) {
+      if (!GetFuzzerContext()->ChoosePercentage(
+              GetFuzzerContext()->GetChanceOfMovingBlockDown())) {
         continue;
       }
       // Keep pushing the block down, until pushing down fails.
diff --git a/source/fuzz/fuzzer_pass_permute_blocks.h b/source/fuzz/fuzzer_pass_permute_blocks.h
index d8aed72..6735e95 100644
--- a/source/fuzz/fuzzer_pass_permute_blocks.h
+++ b/source/fuzz/fuzzer_pass_permute_blocks.h
@@ -36,4 +36,4 @@
 }  // namespace fuzz
 }  // namespace spvtools
 
-#endif  // #define SOURCE_FUZZ_FUZZER_PASS_PERMUTE_BLOCKS_
+#endif  // SOURCE_FUZZ_FUZZER_PASS_PERMUTE_BLOCKS_
diff --git a/source/fuzz/fuzzer_pass_split_blocks.cpp b/source/fuzz/fuzzer_pass_split_blocks.cpp
index 39f84ec..6a2ea4d 100644
--- a/source/fuzz/fuzzer_pass_split_blocks.cpp
+++ b/source/fuzz/fuzzer_pass_split_blocks.cpp
@@ -14,9 +14,9 @@
 
 #include "source/fuzz/fuzzer_pass_split_blocks.h"
 
-#include <utility>
 #include <vector>
 
+#include "source/fuzz/instruction_descriptor.h"
 #include "source/fuzz/transformation_split_block.h"
 
 namespace spvtools {
@@ -44,45 +44,54 @@
   // Now go through all the block pointers that were gathered.
   for (auto& block : blocks) {
     // Probabilistically decide whether to try to split this block.
-    if (GetFuzzerContext()->GetRandomGenerator()->RandomPercentage() >
-        GetFuzzerContext()->GetChanceOfSplittingBlock()) {
+    if (!GetFuzzerContext()->ChoosePercentage(
+            GetFuzzerContext()->GetChanceOfSplittingBlock())) {
+      // We are not going to try to split this block.
       continue;
     }
+
+    // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2964): consider
+    //  taking a simpler approach to identifying the instruction before which
+    //  to split a block.
+
     // We are going to try to split this block.  We now need to choose where
-    // to split it.  We do this by finding a base instruction that has a
-    // result id, and an offset from that base instruction.  We would like
-    // offsets to be as small as possible and ideally 0 - we only need offsets
-    // because not all instructions can be identified by a result id (e.g.
-    // OpStore instructions cannot).
-    std::vector<std::pair<uint32_t, uint32_t>> base_offset_pairs;
+    // to split it.  We describe the instruction before which we would like to
+    // split a block via an InstructionDescriptor, details of which are
+    // commented in the protobufs definition file.
+    std::vector<protobufs::InstructionDescriptor> instruction_descriptors;
+
     // The initial base instruction is the block label.
     uint32_t base = block->id();
-    uint32_t offset = 0;
+
+    // Counts the number of times we have seen each opcode since we reset the
+    // base instruction.
+    std::map<SpvOp, 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
     // in the block does not have a result id.
     for (auto& inst : *block) {
       if (inst.HasResultId()) {
         // In the case that the instruction has a result id, we use the
-        // instruction as its own base, with zero offset.
+        // instruction as its own base, and clear the skip counts we have
+        // collected.
         base = inst.result_id();
-        offset = 0;
-      } else {
-        // The instruction does not have a result id, so we need to identify
-        // it via the latest instruction that did have a result id (base), and
-        // an incremented offset.
-        offset++;
+        skip_count.clear();
       }
-      base_offset_pairs.emplace_back(base, offset);
+      const SpvOp opcode = inst.opcode();
+      instruction_descriptors.emplace_back(MakeInstructionDescriptor(
+          base, opcode, skip_count.count(opcode) ? skip_count.at(opcode) : 0));
+      if (!inst.HasResultId()) {
+        skip_count[opcode] =
+            skip_count.count(opcode) ? skip_count.at(opcode) + 1 : 1;
+      }
     }
     // Having identified all the places we might be able to split the block,
     // we choose one of them.
-    auto base_offset = base_offset_pairs
-        [GetFuzzerContext()->GetRandomGenerator()->RandomUint32(
-            static_cast<uint32_t>(base_offset_pairs.size()))];
-    auto transformation =
-        TransformationSplitBlock(base_offset.first, base_offset.second,
-                                 GetFuzzerContext()->GetFreshId());
+    auto transformation = TransformationSplitBlock(
+        instruction_descriptors[GetFuzzerContext()->RandomIndex(
+            instruction_descriptors)],
+        GetFuzzerContext()->GetFreshId());
     // If the position we have chosen turns out to be a valid place to split
     // the block, we apply the split. Otherwise the block just doesn't get
     // split.
diff --git a/source/fuzz/fuzzer_pass_split_blocks.h b/source/fuzz/fuzzer_pass_split_blocks.h
index 951022b..6e56dde 100644
--- a/source/fuzz/fuzzer_pass_split_blocks.h
+++ b/source/fuzz/fuzzer_pass_split_blocks.h
@@ -36,4 +36,4 @@
 }  // namespace fuzz
 }  // namespace spvtools
 
-#endif  // #define SOURCE_FUZZ_FUZZER_PASS_SPLIT_BLOCKS_
+#endif  // SOURCE_FUZZ_FUZZER_PASS_SPLIT_BLOCKS_
diff --git a/source/fuzz/fuzzer_util.cpp b/source/fuzz/fuzzer_util.cpp
index 9972e47..4654682 100644
--- a/source/fuzz/fuzzer_util.cpp
+++ b/source/fuzz/fuzzer_util.cpp
@@ -170,39 +170,233 @@
   return false;
 }
 
-opt::BasicBlock::iterator GetIteratorForBaseInstructionAndOffset(
-    opt::BasicBlock* block, const opt::Instruction* base_inst,
-    uint32_t offset) {
-  // The cases where |base_inst| is the block's label, vs. inside the block,
-  // are dealt with separately.
-  if (base_inst == block->GetLabelInst()) {
-    // |base_inst| is the block's label.
-    if (offset == 0) {
-      // We cannot return an iterator to the block's label.
-      return block->end();
-    }
-    // Conceptually, the first instruction in the block is [label + 1].
-    // We thus start from 1 when applying the offset.
-    auto inst_it = block->begin();
-    for (uint32_t i = 1; i < offset && inst_it != block->end(); i++) {
-      ++inst_it;
-    }
-    // This is either the desired instruction, or the end of the block.
-    return inst_it;
-  }
-  // |base_inst| is inside the block.
+opt::BasicBlock::iterator GetIteratorForInstruction(
+    opt::BasicBlock* block, const opt::Instruction* inst) {
   for (auto inst_it = block->begin(); inst_it != block->end(); ++inst_it) {
-    if (base_inst == &*inst_it) {
-      // We have found the base instruction; we now apply the offset.
-      for (uint32_t i = 0; i < offset && inst_it != block->end(); i++) {
-        ++inst_it;
-      }
-      // This is either the desired instruction, or the end of the block.
+    if (inst == &*inst_it) {
       return inst_it;
     }
   }
-  assert(false && "The base instruction was not found.");
-  return nullptr;
+  return block->end();
+}
+
+bool NewEdgeRespectsUseDefDominance(opt::IRContext* context,
+                                    opt::BasicBlock* bb_from,
+                                    opt::BasicBlock* bb_to) {
+  assert(bb_from->terminator()->opcode() == SpvOpBranch);
+
+  // If there is *already* an edge from |bb_from| to |bb_to|, then adding
+  // another edge is fine from a dominance point of view.
+  if (bb_from->terminator()->GetSingleWordInOperand(0) == bb_to->id()) {
+    return true;
+  }
+
+  // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2919): the
+  //  solution below to determining whether a new edge respects dominance
+  //  rules is incomplete.  Test
+  //  TransformationAddDeadContinueTest::DISABLED_Miscellaneous6 exposes the
+  //  problem.  In practice, this limitation does not bite too often, and the
+  //  worst it does is leads to SPIR-V that spirv-val rejects.
+
+  // Let us assume that the module being manipulated is valid according to the
+  // rules of the SPIR-V language.
+  //
+  // Suppose that some block Y is dominated by |bb_to| (which includes the case
+  // where Y = |bb_to|).
+  //
+  // Suppose that Y uses an id i that is defined in some other block X.
+  //
+  // Because the module is valid, X must dominate Y.  We are concerned about
+  // whether an edge from |bb_from| to |bb_to| could *stop* X from dominating
+  // Y.
+  //
+  // Because |bb_to| dominates Y, a new edge from |bb_from| to |bb_to| can
+  // only affect whether X dominates Y if X dominates |bb_to|.
+  //
+  // So let us assume that X does dominate |bb_to|, so that we have:
+  //
+  //   (X defines i) dominates |bb_to| dominates (Y uses i)
+  //
+  // The new edge from |bb_from| to |bb_to| will stop the definition of i in X
+  // from dominating the use of i in Y exactly when the new edge will stop X
+  // from dominating |bb_to|.
+  //
+  // Now, the block X that we are worried about cannot dominate |bb_from|,
+  // because in that case X would still dominate |bb_to| after we add an edge
+  // from |bb_from| to |bb_to|.
+  //
+  // Also, it cannot be that X = |bb_to|, because nothing can stop a block
+  // from dominating itself.
+  //
+  // So we are looking for a block X such that:
+  //
+  // - X strictly dominates |bb_to|
+  // - X does not dominate |bb_from|
+  // - X defines an id i
+  // - i is used in some block Y
+  // - |bb_to| dominates Y
+
+  // Walk the dominator tree backwards, starting from the immediate dominator
+  // of |bb_to|.  We can stop when we find a block that also dominates
+  // |bb_from|.
+  auto dominator_analysis = context->GetDominatorAnalysis(bb_from->GetParent());
+  for (auto dominator = dominator_analysis->ImmediateDominator(bb_to);
+       dominator != nullptr &&
+       !dominator_analysis->Dominates(dominator, bb_from);
+       dominator = dominator_analysis->ImmediateDominator(dominator)) {
+    // |dominator| is a candidate for block X in the above description.
+    // We now look through the instructions for a candidate instruction i.
+    for (auto& inst : *dominator) {
+      // Consider all the uses of this instruction.
+      if (!context->get_def_use_mgr()->WhileEachUse(
+              &inst,
+              [bb_to, context, dominator_analysis](
+                  opt::Instruction* user, uint32_t operand_index) -> bool {
+                // If this use is in an OpPhi, we need to check that dominance
+                // of the relevant *parent* block is not spoiled.  Otherwise we
+                // need to check that dominance of the block containing the use
+                // is not spoiled.
+                opt::BasicBlock* use_block_or_phi_parent =
+                    user->opcode() == SpvOpPhi
+                        ? context->cfg()->block(
+                              user->GetSingleWordOperand(operand_index + 1))
+                        : context->get_instr_block(user);
+
+                // There might not be any relevant block, e.g. if the use is in
+                // a decoration; in this case the new edge is unproblematic.
+                if (use_block_or_phi_parent == nullptr) {
+                  return true;
+                }
+
+                // With reference to the above discussion,
+                // |use_block_or_phi_parent| is a candidate for the block Y.
+                // If |bb_to| dominates this block, the new edge would be
+                // problematic.
+                return !dominator_analysis->Dominates(bb_to,
+                                                      use_block_or_phi_parent);
+              })) {
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
+bool BlockIsReachableInItsFunction(opt::IRContext* context,
+                                   opt::BasicBlock* bb) {
+  auto enclosing_function = bb->GetParent();
+  return context->GetDominatorAnalysis(enclosing_function)
+      ->Dominates(enclosing_function->entry().get(), bb);
+}
+
+bool CanInsertOpcodeBeforeInstruction(
+    SpvOp 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)) {
+    // We cannot insert directly after a merge instruction.
+    return false;
+  }
+  if (opcode != SpvOpVariable &&
+      instruction_in_block->opcode() == SpvOpVariable) {
+    // 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;
+}
+
+bool CanMakeSynonymOf(opt::IRContext* ir_context, opt::Instruction* inst) {
+  if (!inst->HasResultId()) {
+    // We can only make a synonym of an instruction that generates an id.
+    return false;
+  }
+  if (!inst->type_id()) {
+    // We can only make a synonym of an instruction that has a type.
+    return false;
+  }
+  // We do not make synonyms of objects that have decorations: if the synonym is
+  // not decorated analogously, using the original object vs. its synonymous
+  // form may not be equivalent.
+  return ir_context->get_decoration_mgr()
+      ->GetDecorationsFor(inst->result_id(), true)
+      .empty();
+}
+
+bool IsCompositeType(const opt::analysis::Type* type) {
+  return type && (type->AsArray() || type->AsMatrix() || type->AsStruct() ||
+                  type->AsVector());
+}
+
+std::vector<uint32_t> RepeatedFieldToVector(
+    const google::protobuf::RepeatedField<uint32_t>& repeated_field) {
+  std::vector<uint32_t> result;
+  for (auto i : repeated_field) {
+    result.push_back(i);
+  }
+  return result;
+}
+
+uint32_t WalkCompositeTypeIndices(
+    opt::IRContext* context, uint32_t base_object_type_id,
+    const google::protobuf::RepeatedField<google::protobuf::uint32>& indices) {
+  uint32_t sub_object_type_id = base_object_type_id;
+  for (auto index : indices) {
+    auto should_be_composite_type =
+        context->get_def_use_mgr()->GetDef(sub_object_type_id);
+    assert(should_be_composite_type && "The type should exist.");
+    if (SpvOpTypeArray == should_be_composite_type->opcode()) {
+      auto array_length = GetArraySize(*should_be_composite_type, context);
+      if (array_length == 0 || index >= array_length) {
+        return 0;
+      }
+      sub_object_type_id = should_be_composite_type->GetSingleWordInOperand(0);
+    } else if (SpvOpTypeMatrix == should_be_composite_type->opcode()) {
+      auto matrix_column_count =
+          should_be_composite_type->GetSingleWordInOperand(1);
+      if (index >= matrix_column_count) {
+        return 0;
+      }
+      sub_object_type_id = should_be_composite_type->GetSingleWordInOperand(0);
+    } else if (SpvOpTypeStruct == should_be_composite_type->opcode()) {
+      if (index >= GetNumberOfStructMembers(*should_be_composite_type)) {
+        return 0;
+      }
+      sub_object_type_id =
+          should_be_composite_type->GetSingleWordInOperand(index);
+    } else if (SpvOpTypeVector == should_be_composite_type->opcode()) {
+      auto vector_length = should_be_composite_type->GetSingleWordInOperand(1);
+      if (index >= vector_length) {
+        return 0;
+      }
+      sub_object_type_id = should_be_composite_type->GetSingleWordInOperand(0);
+    } else {
+      return 0;
+    }
+  }
+  return sub_object_type_id;
+}
+
+uint32_t GetNumberOfStructMembers(
+    const opt::Instruction& struct_type_instruction) {
+  assert(struct_type_instruction.opcode() == SpvOpTypeStruct &&
+         "An OpTypeStruct instruction is required here.");
+  return struct_type_instruction.NumInOperands();
+}
+
+uint32_t GetArraySize(const opt::Instruction& array_type_instruction,
+                      opt::IRContext* context) {
+  auto array_length_constant =
+      context->get_constant_mgr()
+          ->GetConstantFromInst(context->get_def_use_mgr()->GetDef(
+              array_type_instruction.GetSingleWordInOperand(1)))
+          ->AsIntConstant();
+  if (array_length_constant->words().size() != 1) {
+    return 0;
+  }
+  return array_length_constant->GetU32();
 }
 
 }  // namespace fuzzerutil
diff --git a/source/fuzz/fuzzer_util.h b/source/fuzz/fuzzer_util.h
index 47588b0..1569df0 100644
--- a/source/fuzz/fuzzer_util.h
+++ b/source/fuzz/fuzzer_util.h
@@ -64,15 +64,58 @@
 bool BlockIsInLoopContinueConstruct(opt::IRContext* context, uint32_t block_id,
                                     uint32_t maybe_loop_header_id);
 
-// Requires that |base_inst| is either the label instruction of |block| or an
-// instruction inside |block|.
-//
-// If the block contains a (non-label, non-terminator) instruction |offset|
-// instructions after |base_inst|, an iterator to this instruction is returned.
-//
+// If |block| contains |inst|, an iterator for |inst| is returned.
 // Otherwise |block|->end() is returned.
-opt::BasicBlock::iterator GetIteratorForBaseInstructionAndOffset(
-    opt::BasicBlock* block, const opt::Instruction* base_inst, uint32_t offset);
+opt::BasicBlock::iterator GetIteratorForInstruction(
+    opt::BasicBlock* block, const opt::Instruction* inst);
+
+// The function determines whether adding an edge from |bb_from| to |bb_to| -
+// is legitimate with respect to the SPIR-V rule that a definition must
+// dominate all of its uses.  This is because adding such an edge can change
+// dominance in the control flow graph, potentially making the module invalid.
+bool NewEdgeRespectsUseDefDominance(opt::IRContext* context,
+                                    opt::BasicBlock* bb_from,
+                                    opt::BasicBlock* bb_to);
+
+// Returns true if and only if there is a path to |bb| from the entry block of
+// the function that contains |bb|.
+bool BlockIsReachableInItsFunction(opt::IRContext* context,
+                                   opt::BasicBlock* bb);
+
+// 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);
+
+// Determines whether it is OK to make a synonym of |inst|.
+bool CanMakeSynonymOf(opt::IRContext* ir_context, opt::Instruction* inst);
+
+// Determines whether the given type is a composite; that is: an array, matrix,
+// struct or vector.
+bool IsCompositeType(const opt::analysis::Type* type);
+
+// Returns a vector containing the same elements as |repeated_field|.
+std::vector<uint32_t> RepeatedFieldToVector(
+    const google::protobuf::RepeatedField<uint32_t>& repeated_field);
+
+// Given a type id, |base_object_type_id|, checks that the given sequence of
+// |indices| is suitable for indexing into this type.  Returns the id of the
+// type of the final sub-object reached via the indices if they are valid, and
+// 0 otherwise.
+uint32_t WalkCompositeTypeIndices(
+    opt::IRContext* context, uint32_t base_object_type_id,
+    const google::protobuf::RepeatedField<google::protobuf::uint32>& indices);
+
+// Returns the number of members associated with |struct_type_instruction|,
+// which must be an OpStructType instruction.
+uint32_t GetNumberOfStructMembers(
+    const opt::Instruction& struct_type_instruction);
+
+// Returns the constant size of the array associated with
+// |array_type_instruction|, which must be an OpArrayType instruction. Returns
+// 0 if there is not a static size.
+uint32_t GetArraySize(const opt::Instruction& array_type_instruction,
+                      opt::IRContext* context);
 
 }  // namespace fuzzerutil
 
diff --git a/source/fuzz/id_use_descriptor.cpp b/source/fuzz/id_use_descriptor.cpp
index 33d2ca0..eb8589d 100644
--- a/source/fuzz/id_use_descriptor.cpp
+++ b/source/fuzz/id_use_descriptor.cpp
@@ -14,69 +14,49 @@
 
 #include "source/fuzz/id_use_descriptor.h"
 
+#include "source/fuzz/instruction_descriptor.h"
+
 namespace spvtools {
 namespace fuzz {
 
-opt::Instruction* transformation::FindInstruction(
-    const protobufs::IdUseDescriptor& descriptor,
-    spvtools::opt::IRContext* context) {
-  for (auto& function : *context->module()) {
-    for (auto& block : function) {
-      bool found_base = block.id() == descriptor.base_instruction_result_id();
-      uint32_t num_ignored = 0;
-      for (auto& instruction : block) {
-        if (instruction.HasResultId() &&
-            instruction.result_id() ==
-                descriptor.base_instruction_result_id()) {
-          assert(!found_base &&
-                 "It should not be possible to find the base instruction "
-                 "multiple times.");
-          found_base = true;
-          assert(num_ignored == 0 &&
-                 "The skipped instruction count should only be incremented "
-                 "after the instruction base has been found.");
-        }
-        if (found_base &&
-            instruction.opcode() == descriptor.target_instruction_opcode()) {
-          if (num_ignored == descriptor.num_opcodes_to_ignore()) {
-            if (descriptor.in_operand_index() >= instruction.NumInOperands()) {
-              return nullptr;
-            }
-            auto in_operand =
-                instruction.GetInOperand(descriptor.in_operand_index());
-            if (in_operand.type != SPV_OPERAND_TYPE_ID) {
-              return nullptr;
-            }
-            if (in_operand.words[0] != descriptor.id_of_interest()) {
-              return nullptr;
-            }
-            return &instruction;
-          }
-          num_ignored++;
-        }
-      }
-      if (found_base) {
-        // We found the base instruction, but did not find the target
-        // instruction in the same block.
-        return nullptr;
-      }
-    }
+opt::Instruction* FindInstructionContainingUse(
+    const protobufs::IdUseDescriptor& id_use_descriptor,
+    opt::IRContext* context) {
+  auto result =
+      FindInstruction(id_use_descriptor.enclosing_instruction(), context);
+  if (!result) {
+    return nullptr;
   }
-  return nullptr;
+  if (id_use_descriptor.in_operand_index() >= result->NumInOperands()) {
+    return nullptr;
+  }
+  if (result->GetSingleWordInOperand(id_use_descriptor.in_operand_index()) !=
+      id_use_descriptor.id_of_interest()) {
+    return nullptr;
+  }
+  return result;
 }
 
-protobufs::IdUseDescriptor transformation::MakeIdUseDescriptor(
-    uint32_t id_of_interest, SpvOp target_instruction_opcode,
-    uint32_t in_operand_index, uint32_t base_instruction_result_id,
-    uint32_t num_opcodes_to_ignore) {
+protobufs::IdUseDescriptor MakeIdUseDescriptor(
+    uint32_t id_of_interest,
+    const protobufs::InstructionDescriptor& enclosing_instruction,
+    uint32_t in_operand_index) {
   protobufs::IdUseDescriptor result;
   result.set_id_of_interest(id_of_interest);
-  result.set_target_instruction_opcode(target_instruction_opcode);
+  *result.mutable_enclosing_instruction() = enclosing_instruction;
   result.set_in_operand_index(in_operand_index);
-  result.set_base_instruction_result_id(base_instruction_result_id);
-  result.set_num_opcodes_to_ignore(num_opcodes_to_ignore);
   return result;
 }
 
+protobufs::IdUseDescriptor MakeIdUseDescriptorFromUse(
+    opt::IRContext* context, opt::Instruction* inst,
+    uint32_t in_operand_index) {
+  const auto& in_operand = inst->GetInOperand(in_operand_index);
+  assert(in_operand.type == SPV_OPERAND_TYPE_ID);
+  return MakeIdUseDescriptor(in_operand.words[0],
+                             MakeInstructionDescriptor(context, inst),
+                             in_operand_index);
+}
+
 }  // namespace fuzz
 }  // namespace spvtools
diff --git a/source/fuzz/id_use_descriptor.h b/source/fuzz/id_use_descriptor.h
index 63016c5..d18bb66 100644
--- a/source/fuzz/id_use_descriptor.h
+++ b/source/fuzz/id_use_descriptor.h
@@ -12,31 +12,36 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef SOURCE_FUZZ_ID_USE_LOCATOR_H_
-#define SOURCE_FUZZ_ID_USE_LOCATOR_H_
+#ifndef SOURCE_FUZZ_ID_USE_DESCRIPTOR_H_
+#define SOURCE_FUZZ_ID_USE_DESCRIPTOR_H_
 
 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
 #include "source/opt/ir_context.h"
 
 namespace spvtools {
 namespace fuzz {
-namespace transformation {
 
-// Looks for an instruction in |context| such that the id use represented by
-// |descriptor| is one of the operands to said instruction.  Returns |nullptr|
-// if no such instruction can be found.
-opt::Instruction* FindInstruction(const protobufs::IdUseDescriptor& descriptor,
-                                  opt::IRContext* context);
+// Looks for an instruction in |context| that contains a use
+// identified by |id_use_descriptor|.
+// Returns |nullptr| if no such instruction can be found.
+opt::Instruction* FindInstructionContainingUse(
+    const protobufs::IdUseDescriptor& id_use_descriptor,
+    opt::IRContext* context);
 
 // Creates an IdUseDescriptor protobuf message from the given components.
 // See the protobuf definition for details of what these components mean.
 protobufs::IdUseDescriptor MakeIdUseDescriptor(
-    uint32_t id_of_interest, SpvOp target_instruction_opcode,
-    uint32_t in_operand_index, uint32_t base_instruction_result_id,
-    uint32_t num_opcodes_to_ignore);
+    uint32_t id_of_interest,
+    const protobufs::InstructionDescriptor& enclosing_instruction,
+    uint32_t in_operand_index);
 
-}  // namespace transformation
+// Given an id use, represented by the instruction |inst| that uses the id, and
+// the input operand index |in_operand_index| associated with the usage, returns
+// an IdUseDescriptor that represents the use.
+protobufs::IdUseDescriptor MakeIdUseDescriptorFromUse(
+    opt::IRContext* context, opt::Instruction* inst, uint32_t in_operand_index);
+
 }  // namespace fuzz
 }  // namespace spvtools
 
-#endif  // SOURCE_FUZZ_ID_USE_LOCATOR_H_
+#endif  // SOURCE_FUZZ_ID_USE_DESCRIPTOR_H_
diff --git a/source/fuzz/instruction_descriptor.cpp b/source/fuzz/instruction_descriptor.cpp
new file mode 100644
index 0000000..c0cc5e5
--- /dev/null
+++ b/source/fuzz/instruction_descriptor.cpp
@@ -0,0 +1,127 @@
+// 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.
+
+#include "source/fuzz/instruction_descriptor.h"
+
+namespace spvtools {
+namespace fuzz {
+
+opt::Instruction* FindInstruction(
+    const protobufs::InstructionDescriptor& instruction_descriptor,
+    spvtools::opt::IRContext* context) {
+  for (auto& function : *context->module()) {
+    for (auto& block : function) {
+      bool found_base =
+          block.id() == instruction_descriptor.base_instruction_result_id();
+      uint32_t num_ignored = 0;
+      for (auto& instruction : block) {
+        if (instruction.HasResultId() &&
+            instruction.result_id() ==
+                instruction_descriptor.base_instruction_result_id()) {
+          assert(!found_base &&
+                 "It should not be possible to find the base instruction "
+                 "multiple times.");
+          found_base = true;
+          assert(num_ignored == 0 &&
+                 "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 (num_ignored == instruction_descriptor.num_opcodes_to_ignore()) {
+            return &instruction;
+          }
+          num_ignored++;
+        }
+      }
+      if (found_base) {
+        // We found the base instruction, but did not find the target
+        // instruction in the same block.
+        return nullptr;
+      }
+    }
+  }
+  return nullptr;
+}
+
+protobufs::InstructionDescriptor MakeInstructionDescriptor(
+    uint32_t base_instruction_result_id, SpvOp 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_num_opcodes_to_ignore(num_opcodes_to_ignore);
+  return result;
+}
+
+protobufs::InstructionDescriptor MakeInstructionDescriptor(
+    const opt::BasicBlock& block,
+    const opt::BasicBlock::const_iterator& inst_it) {
+  const SpvOp 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.
+
+  // Consider instructions in the block in reverse order, starting from
+  // |inst_it|.
+  for (opt::BasicBlock::const_iterator backwards_iterator = inst_it;;
+       --backwards_iterator) {
+    if (backwards_iterator->HasResultId()) {
+      // As soon as we find an instruction with a result id, we can return a
+      // descriptor for |inst_it|.
+      return MakeInstructionDescriptor(backwards_iterator->result_id(), opcode,
+                                       skip_count);
+    }
+    if (backwards_iterator != inst_it &&
+        backwards_iterator->opcode() == opcode) {
+      // We are skipping over an instruction with the same opcode as |inst_it|;
+      // we increase our skip count to reflect this.
+      skip_count++;
+    }
+    if (backwards_iterator == block.begin()) {
+      // We exit the loop when we reach the start of the block, but only after
+      // we have processed the first instruction in the block.
+      break;
+    }
+  }
+  // We did not find an instruction inside the block with a result id, so we use
+  // the block's label's id.
+  return MakeInstructionDescriptor(block.id(), opcode, skip_count);
+}
+
+protobufs::InstructionDescriptor MakeInstructionDescriptor(
+    opt::IRContext* context, opt::Instruction* inst) {
+  auto block = context->get_instr_block(inst);
+  uint32_t base_instruction_result_id = block->id();
+  uint32_t num_opcodes_to_ignore = 0;
+  for (auto& inst_in_block : *block) {
+    if (inst_in_block.HasResultId()) {
+      base_instruction_result_id = inst_in_block.result_id();
+      num_opcodes_to_ignore = 0;
+    }
+    if (&inst_in_block == inst) {
+      return MakeInstructionDescriptor(base_instruction_result_id,
+                                       inst->opcode(), num_opcodes_to_ignore);
+    }
+    if (inst_in_block.opcode() == inst->opcode()) {
+      num_opcodes_to_ignore++;
+    }
+  }
+  assert(false && "No matching instruction was found.");
+  return protobufs::InstructionDescriptor();
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/source/fuzz/instruction_descriptor.h b/source/fuzz/instruction_descriptor.h
new file mode 100644
index 0000000..2ccd15a
--- /dev/null
+++ b/source/fuzz/instruction_descriptor.h
@@ -0,0 +1,53 @@
+// 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.
+
+#ifndef SOURCE_FUZZ_INSTRUCTION_DESCRIPTOR_H_
+#define SOURCE_FUZZ_INSTRUCTION_DESCRIPTOR_H_
+
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/opt/basic_block.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Looks for an instruction in |context| corresponding to |descriptor|.
+// Returns |nullptr| if no such instruction can be found.
+opt::Instruction* FindInstruction(
+    const protobufs::InstructionDescriptor& instruction_descriptor,
+    opt::IRContext* context);
+
+// Creates an InstructionDescriptor protobuf message from the given
+// 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 num_opcodes_to_ignore);
+
+// Returns an instruction descriptor that describing the instruction at
+// |inst_it|, which must be inside |block|.  The descriptor will be with
+// respect to the first instruction at or before |inst_it| that has a result
+// id.
+protobufs::InstructionDescriptor MakeInstructionDescriptor(
+    const opt::BasicBlock& block,
+    const opt::BasicBlock::const_iterator& inst_it);
+
+// Returns an InstructionDescriptor that describes the given instruction |inst|.
+protobufs::InstructionDescriptor MakeInstructionDescriptor(
+    opt::IRContext* context, opt::Instruction* inst);
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_INSTRUCTION_DESCRIPTOR_H_
diff --git a/source/fuzz/protobufs/spvtoolsfuzz.proto b/source/fuzz/protobufs/spvtoolsfuzz.proto
index 4e8dcac..b33c2e5 100644
--- a/source/fuzz/protobufs/spvtoolsfuzz.proto
+++ b/source/fuzz/protobufs/spvtoolsfuzz.proto
@@ -21,17 +21,37 @@
 
 package spvtools.fuzz.protobufs;
 
+message InstructionDescriptor {
+
+  // Describes an instruction in some block of a function with respect to a
+  // base instruction.
+
+  // The id of an instruction after which the instruction being described is
+  // believed to be located.  It might be the using instruction itself.
+  uint32 base_instruction_result_id = 1;
+
+  // The opcode for the instruction being described.
+  uint32 target_instruction_opcode = 2;
+
+  // The number of matching opcodes to skip over when searching from the base
+  // instruction to the instruction being described.
+  uint32 num_opcodes_to_ignore = 3;
+
+}
+
 message IdUseDescriptor {
 
-  // Describes a use of an id as an input operand to an instruction in some block
-  // of a function.
+  // Describes a use of an id as an input operand to an instruction in some
+  // block of a function.
 
   // Example:
   //   - id_of_interest = 42
-  //   - target_instruction_opcode = OpStore
+  //   - enclosing_instruction = (
+  //         base_instruction_result_id = 50,
+  //         target_instruction_opcode = OpStore
+  //         num_opcodes_to_ignore = 7
+  //     )
   //   - in_operand_index = 1
-  //   - base_instruction_result_id = 50
-  //   - num_opcodes_to_ignore = 7
   // represents a use of id 42 as input operand 1 to an OpStore instruction,
   // such that the OpStore instruction can be found in the same basic block as
   // the instruction with result id 50, and in particular is the 8th OpStore
@@ -41,20 +61,11 @@
   // An id that we would like to be able to find a use of.
   uint32 id_of_interest = 1;
 
-  // The opcode for the instruction that uses the id.
-  uint32 target_instruction_opcode = 2;
-
   // The input operand index at which the use is expected.
+  InstructionDescriptor enclosing_instruction = 2;
+
   uint32 in_operand_index = 3;
 
-  // The id of an instruction after which the instruction that contains the use
-  // is believed to occur; it might be the using instruction itself.
-  uint32 base_instruction_result_id = 4;
-
-  // The number of matching opcodes to skip over when searching for the using
-  // instruction from the base instruction.
-  uint32 num_opcodes_to_ignore = 5;
-
 }
 
 message DataDescriptor {
@@ -113,7 +124,7 @@
   oneof fact {
     // Order the fact options by numeric id (rather than alphabetically).
     FactConstantUniform constant_uniform_fact = 1;
-    FactIdSynonym id_synonym_fact = 2;
+    FactDataSynonym data_synonym_fact = 2;
   }
 }
 
@@ -135,19 +146,16 @@
 
 }
 
-message FactIdSynonym {
+message FactDataSynonym {
 
-  // Records the fact that the data held in an id is guaranteed to be equal to
-  // the data held in a data descriptor.  spirv-fuzz can use this to replace
-  // uses of the id with references to the data described by the data
-  // descriptor.
+  // Records the fact that the data held in two data descriptors are guaranteed
+  // to be equal.  spirv-fuzz can use this to replace uses of one piece of data
+  // with a known-to-be-equal piece of data.
 
-  // An id
-  uint32 id = 1;
+  // Data descriptors guaranteed to hold identical data.
+  DataDescriptor data1 = 1;
 
-  // A data descriptor guaranteed to hold a value identical to that held by the
-  // id
-  DataDescriptor data_descriptor = 2;
+  DataDescriptor data2 = 2;
 
 }
 
@@ -167,11 +175,21 @@
     TransformationAddTypeFloat add_type_float = 6;
     TransformationAddTypeInt add_type_int = 7;
     TransformationAddDeadBreak add_dead_break = 8;
-    TransformationReplaceBooleanConstantWithConstantBinary replace_boolean_constant_with_constant_binary = 9;
+    TransformationReplaceBooleanConstantWithConstantBinary
+      replace_boolean_constant_with_constant_binary = 9;
     TransformationAddTypePointer add_type_pointer = 10;
     TransformationReplaceConstantWithUniform replace_constant_with_uniform = 11;
     TransformationAddDeadContinue add_dead_continue = 12;
     TransformationCopyObject copy_object = 13;
+    TransformationReplaceIdWithSynonym replace_id_with_synonym = 14;
+    TransformationSetSelectionControl set_selection_control = 15;
+    TransformationCompositeConstruct composite_construct = 16;
+    TransformationSetLoopControl set_loop_control = 17;
+    TransformationSetFunctionControl set_function_control = 18;
+    TransformationAddNoContractionDecoration add_no_contraction_decoration = 19;
+    TransformationSetMemoryOperandsMask set_memory_operands_mask = 20;
+    TransformationCompositeExtract composite_extract = 21;
+    TransformationVectorShuffle vector_shuffle = 22;
     // Add additional option using the next available number.
   }
 }
@@ -244,6 +262,15 @@
 
 }
 
+message TransformationAddNoContractionDecoration {
+
+  // Applies OpDecorate NoContraction to the given result id
+
+  // Result id to be decorated
+  uint32 result_id = 1;
+
+}
+
 message TransformationAddTypeBoolean {
 
   // Adds OpTypeBool to the module
@@ -296,6 +323,46 @@
 
 }
 
+message TransformationCompositeConstruct {
+
+  // A transformation that introduces an OpCompositeConstruct instruction to
+  // make a composite object.
+
+  // Id of the type of the composite that is to be constructed
+  uint32 composite_type_id = 1;
+
+  // Ids of the objects that will form the components of the composite
+  repeated uint32 component = 2;
+
+  // A descriptor for an instruction in a block before which the new
+  // OpCompositeConstruct instruction should be inserted
+  InstructionDescriptor instruction_to_insert_before = 3;
+
+  // A fresh id for the composite object
+  uint32 fresh_id = 4;
+
+}
+
+message TransformationCompositeExtract {
+
+  // A transformation that adds an instruction to extract an element from a
+  // composite.
+
+  // A descriptor for an instruction in a block before which the new
+  // OpCompositeExtract instruction should be inserted
+  InstructionDescriptor instruction_to_insert_before = 1;
+
+  // Result id for the extract operation.
+  uint32 fresh_id = 2;
+
+  // Id of the composite from which data is to be extracted.
+  uint32 composite_id = 3;
+
+  // Indices that indicate which part of the composite should be extracted.
+  repeated uint32 index = 4;
+
+}
+
 message TransformationCopyObject {
 
   // A transformation that introduces an OpCopyObject instruction to make a
@@ -304,15 +371,12 @@
   // Id of the object to be copied
   uint32 object = 1;
 
-  // The id of an instruction in a block
-  uint32 base_instruction_id = 2;
-
-  // An offset, such that OpCopyObject instruction should be inserted right
-  // before the instruction |offset| instructions after |base_instruction_id|
-  uint32 offset = 3;
+  // A descriptor for an instruction in a block before which the new
+  // OpCopyObject instruction should be inserted
+  InstructionDescriptor instruction_to_insert_before = 2;
 
   // A fresh id for the copied object
-  uint32 fresh_id = 4;
+  uint32 fresh_id = 3;
 
 }
 
@@ -325,25 +389,6 @@
   uint32 block_id = 1;
 }
 
-message TransformationReplaceConstantWithUniform {
-
-  // Replaces a use of a constant id with the the result of a load from an
-  // element of uniform buffer known to hold the same value as the constant
-
-  // A descriptor for the id we would like to replace
-  IdUseDescriptor id_use_descriptor = 1;
-
-  // Uniform descriptor to identify which uniform value to choose
-  UniformBufferElementDescriptor uniform_descriptor = 2;
-
-  // Id that will store the result of an access chain
-  uint32 fresh_id_for_access_chain = 3;
-
-  // Id that will store the result of a load
-  uint32 fresh_id_for_load = 4;
-
-}
-
 message TransformationReplaceBooleanConstantWithConstantBinary {
 
   // A transformation to capture replacing a use of a boolean constant with
@@ -366,17 +411,117 @@
 
 }
 
+message TransformationReplaceConstantWithUniform {
+
+  // Replaces a use of a constant id with the result of a load from an
+  // element of uniform buffer known to hold the same value as the constant
+
+  // A descriptor for the id we would like to replace
+  IdUseDescriptor id_use_descriptor = 1;
+
+  // Uniform descriptor to identify which uniform value to choose
+  UniformBufferElementDescriptor uniform_descriptor = 2;
+
+  // Id that will store the result of an access chain
+  uint32 fresh_id_for_access_chain = 3;
+
+  // Id that will store the result of a load
+  uint32 fresh_id_for_load = 4;
+
+}
+
+message TransformationReplaceIdWithSynonym {
+
+  // Replaces a use of an id with an id that is known to be synonymous, e.g.
+  // because it was obtained via applying OpCopyObject
+
+  // The id use that is to be replaced
+  IdUseDescriptor id_use_descriptor = 1;
+
+  // The synonymous id
+  uint32 synonymous_id = 2;
+
+}
+
+message TransformationSetFunctionControl {
+
+  // A transformation that sets the function control operand of an OpFunction
+  // instruction.
+
+  // The result id of an OpFunction instruction
+  uint32 function_id = 1;
+
+  // The value to which the 'function control' operand should be set.
+  uint32 function_control = 2;
+
+}
+
+message TransformationSetLoopControl {
+
+  // A transformation that sets the loop control operand of an OpLoopMerge
+  // instruction.
+
+  // The id of a basic block that should contain OpLoopMerge
+  uint32 block_id = 1;
+
+  // The value to which the 'loop control' operand should be set.
+  // This must be a legal loop control mask.
+  uint32 loop_control = 2;
+
+  // Provides a peel count value for the loop.  Used if and only if the
+  // PeelCount bit is set.  Must be zero if the PeelCount bit is not set (can
+  // still be zero if this bit is set).
+  uint32 peel_count = 3;
+
+  // Provides a partial count value for the loop.  Used if and only if the
+  // PartialCount bit is set.  Must be zero if the PartialCount bit is not set
+  // (can still be zero if this bit is set).
+  uint32 partial_count = 4;
+
+}
+
+message TransformationSetMemoryOperandsMask {
+
+  // A transformation that sets the memory operands mask of a memory access
+  // instruction.
+
+  // A descriptor for a memory access instruction, e.g. an OpLoad
+  InstructionDescriptor memory_access_instruction = 1;
+
+  // A mask of memory operands to be applied to the instruction.  It must be the
+  // same as the original mask, except that Volatile can be added, and
+  // Nontemporal can be added or removed.
+  uint32 memory_operands_mask = 2;
+
+  // Some memory access instructions allow more than one mask to be specified;
+  // this field indicates which mask should be set
+  uint32 memory_operands_mask_index = 3;
+
+}
+
+message TransformationSetSelectionControl {
+
+  // A transformation that sets the selection control operand of an
+  // OpSelectionMerge instruction.
+
+  // The id of a basic block that should contain OpSelectionMerge
+  uint32 block_id = 1;
+
+  // The value to which the 'selection control' operand should be set.
+  // Although technically 'selection control' is a literal mask that can be
+  // some combination of 'None', 'Flatten' and 'DontFlatten', the combination
+  // 'Flatten | DontFlatten' does not make sense and is not allowed here.
+  uint32 selection_control = 2;
+
+}
+
 message TransformationSplitBlock {
 
   // A transformation that splits a basic block into two basic blocks
 
-  // The result id of an instruction
-  uint32 base_instruction_id = 1;
-
-  // An offset, such that the block containing |base_instruction_id| should be
-  // split right before the instruction |offset| instructions after
-  // |base_instruction_id|
-  uint32 offset = 2;
+  // A descriptor for an instruction such that the block containing the
+  // described instruction should be split right before the instruction.
+  InstructionDescriptor instruction_to_split_before = 1;
 
   // An id that must not yet be used by the module to which this transformation
   // is applied.  Rather than having the transformation choose a suitable id on
@@ -386,6 +531,28 @@
   // transformation, and if we end up changing what that id is, due to removing
   // earlier transformations, it may inhibit later transformations from
   // applying.
-  uint32 fresh_id = 3;
+  uint32 fresh_id = 2;
+
+}
+
+message TransformationVectorShuffle {
+
+  // A transformation that adds a vector shuffle instruction.
+
+  // A descriptor for an instruction in a block before which the new
+  // OpVectorShuffle instruction should be inserted
+  InstructionDescriptor instruction_to_insert_before = 1;
+
+  // Result id for the shuffle operation.
+  uint32 fresh_id = 2;
+
+  // Id of the first vector operand.
+  uint32 vector1 = 3;
+
+  // Id of the second vector operand.
+  uint32 vector2 = 4;
+
+  // Indices that indicate which components of the input vectors should be used.
+  repeated uint32 component = 5;
 
 }
diff --git a/source/fuzz/replayer.cpp b/source/fuzz/replayer.cpp
index b0d4ee2..398ce59 100644
--- a/source/fuzz/replayer.cpp
+++ b/source/fuzz/replayer.cpp
@@ -37,13 +37,18 @@
 namespace fuzz {
 
 struct Replayer::Impl {
-  explicit Impl(spv_target_env env) : target_env(env) {}
+  explicit Impl(spv_target_env env, bool validate)
+      : target_env(env), validate_during_replay(validate) {}
 
   const spv_target_env target_env;  // Target environment.
   MessageConsumer consumer;         // Message consumer.
+
+  const bool validate_during_replay;  // Controls whether the validator should
+                                      // be run after every replay step.
 };
 
-Replayer::Replayer(spv_target_env env) : impl_(MakeUnique<Impl>(env)) {}
+Replayer::Replayer(spv_target_env env, bool validate_during_replay)
+    : impl_(MakeUnique<Impl>(env, validate_during_replay)) {}
 
 Replayer::~Replayer() = default;
 
@@ -80,6 +85,13 @@
       impl_->target_env, impl_->consumer, binary_in.data(), binary_in.size());
   assert(ir_context);
 
+  // For replay validation, we track the last valid SPIR-V binary that was
+  // observed. Initially this is the input binary.
+  std::vector<uint32_t> last_valid_binary;
+  if (impl_->validate_during_replay) {
+    last_valid_binary = binary_in;
+  }
+
   FactManager fact_manager;
   fact_manager.AddFacts(impl_->consumer, initial_facts, ir_context.get());
 
@@ -93,6 +105,23 @@
       // sequence of transformations that were applied.
       transformation->Apply(ir_context.get(), &fact_manager);
       *transformation_sequence_out->add_transformation() = message;
+
+      if (impl_->validate_during_replay) {
+        std::vector<uint32_t> binary_to_validate;
+        ir_context->module()->ToBinary(&binary_to_validate, false);
+
+        // Check whether the latest transformation led to a valid binary.
+        if (!tools.Validate(&binary_to_validate[0],
+                            binary_to_validate.size())) {
+          impl_->consumer(SPV_MSG_INFO, nullptr, {},
+                          "Binary became invalid during replay (set a "
+                          "breakpoint to inspect); stopping.");
+          return Replayer::ReplayerResultStatus::kReplayValidationFailure;
+        }
+
+        // The binary was valid, so it becomes the latest valid binary.
+        last_valid_binary = std::move(binary_to_validate);
+      }
     }
   }
 
diff --git a/source/fuzz/replayer.h b/source/fuzz/replayer.h
index 13391d0..1d58bae 100644
--- a/source/fuzz/replayer.h
+++ b/source/fuzz/replayer.h
@@ -33,10 +33,11 @@
     kComplete,
     kFailedToCreateSpirvToolsInterface,
     kInitialBinaryInvalid,
+    kReplayValidationFailure,
   };
 
   // Constructs a replayer from the given target environment.
-  explicit Replayer(spv_target_env env);
+  explicit Replayer(spv_target_env env, bool validate_during_replay);
 
   // Disables copy/move constructor/assignment operations.
   Replayer(const Replayer&) = delete;
diff --git a/source/fuzz/shrinker.cpp b/source/fuzz/shrinker.cpp
index f8d8aa3..1bb92f1 100644
--- a/source/fuzz/shrinker.cpp
+++ b/source/fuzz/shrinker.cpp
@@ -60,16 +60,20 @@
 }  // namespace
 
 struct Shrinker::Impl {
-  explicit Impl(spv_target_env env, uint32_t limit)
-      : target_env(env), step_limit(limit) {}
+  explicit Impl(spv_target_env env, uint32_t limit, bool validate)
+      : target_env(env), step_limit(limit), validate_during_replay(validate) {}
 
-  const spv_target_env target_env;  // Target environment.
-  MessageConsumer consumer;         // Message consumer.
-  const uint32_t step_limit;        // Step limit for reductions.
+  const spv_target_env target_env;    // Target environment.
+  MessageConsumer consumer;           // Message consumer.
+  const uint32_t step_limit;          // Step limit for reductions.
+  const bool validate_during_replay;  // Determines whether to check for
+                                      // validity during the replaying of
+                                      // transformations.
 };
 
-Shrinker::Shrinker(spv_target_env env, uint32_t step_limit)
-    : impl_(MakeUnique<Impl>(env, step_limit)) {}
+Shrinker::Shrinker(spv_target_env env, uint32_t step_limit,
+                   bool validate_during_replay)
+    : impl_(MakeUnique<Impl>(env, step_limit, validate_during_replay)) {}
 
 Shrinker::~Shrinker() = default;
 
@@ -109,7 +113,7 @@
   // succeeds, (b) get the binary that results from running these
   // transformations, and (c) get the subsequence of the initial transformations
   // that actually apply (in principle this could be a strict subsequence).
-  if (Replayer(impl_->target_env)
+  if (Replayer(impl_->target_env, impl_->validate_during_replay)
           .Run(binary_in, initial_facts, transformation_sequence_in,
                &current_best_binary, &current_best_transformations) !=
       Replayer::ReplayerResultStatus::kComplete) {
@@ -180,7 +184,7 @@
       // transformations inapplicable.
       std::vector<uint32_t> next_binary;
       protobufs::TransformationSequence next_transformation_sequence;
-      if (Replayer(impl_->target_env)
+      if (Replayer(impl_->target_env, false)
               .Run(binary_in, initial_facts, transformations_with_chunk_removed,
                    &next_binary, &next_transformation_sequence) !=
           Replayer::ReplayerResultStatus::kComplete) {
diff --git a/source/fuzz/shrinker.h b/source/fuzz/shrinker.h
index 72dd470..0163a53 100644
--- a/source/fuzz/shrinker.h
+++ b/source/fuzz/shrinker.h
@@ -50,7 +50,8 @@
       const std::vector<uint32_t>& binary, uint32_t counter)>;
 
   // Constructs a shrinker from the given target environment.
-  Shrinker(spv_target_env env, uint32_t step_limit);
+  Shrinker(spv_target_env env, uint32_t step_limit,
+           bool validate_during_replay);
 
   // Disables copy/move constructor/assignment operations.
   Shrinker(const Shrinker&) = delete;
diff --git a/source/fuzz/transformation.cpp b/source/fuzz/transformation.cpp
index a252734..d8fc92f 100644
--- a/source/fuzz/transformation.cpp
+++ b/source/fuzz/transformation.cpp
@@ -16,19 +16,29 @@
 
 #include <cassert>
 
+#include "source/fuzz/transformation_add_constant_boolean.h"
+#include "source/fuzz/transformation_add_constant_scalar.h"
+#include "source/fuzz/transformation_add_dead_break.h"
+#include "source/fuzz/transformation_add_dead_continue.h"
+#include "source/fuzz/transformation_add_no_contraction_decoration.h"
+#include "source/fuzz/transformation_add_type_boolean.h"
+#include "source/fuzz/transformation_add_type_float.h"
+#include "source/fuzz/transformation_add_type_int.h"
+#include "source/fuzz/transformation_add_type_pointer.h"
+#include "source/fuzz/transformation_composite_construct.h"
+#include "source/fuzz/transformation_composite_extract.h"
+#include "source/fuzz/transformation_copy_object.h"
+#include "source/fuzz/transformation_move_block_down.h"
+#include "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h"
+#include "source/fuzz/transformation_replace_constant_with_uniform.h"
+#include "source/fuzz/transformation_replace_id_with_synonym.h"
+#include "source/fuzz/transformation_set_function_control.h"
+#include "source/fuzz/transformation_set_loop_control.h"
+#include "source/fuzz/transformation_set_memory_operands_mask.h"
+#include "source/fuzz/transformation_set_selection_control.h"
+#include "source/fuzz/transformation_split_block.h"
+#include "source/fuzz/transformation_vector_shuffle.h"
 #include "source/util/make_unique.h"
-#include "transformation_add_constant_boolean.h"
-#include "transformation_add_constant_scalar.h"
-#include "transformation_add_dead_break.h"
-#include "transformation_add_dead_continue.h"
-#include "transformation_add_type_boolean.h"
-#include "transformation_add_type_float.h"
-#include "transformation_add_type_int.h"
-#include "transformation_add_type_pointer.h"
-#include "transformation_move_block_down.h"
-#include "transformation_replace_boolean_constant_with_constant_binary.h"
-#include "transformation_replace_constant_with_uniform.h"
-#include "transformation_split_block.h"
 
 namespace spvtools {
 namespace fuzz {
@@ -49,6 +59,10 @@
     case protobufs::Transformation::TransformationCase::kAddDeadContinue:
       return MakeUnique<TransformationAddDeadContinue>(
           message.add_dead_continue());
+    case protobufs::Transformation::TransformationCase::
+        kAddNoContractionDecoration:
+      return MakeUnique<TransformationAddNoContractionDecoration>(
+          message.add_no_contraction_decoration());
     case protobufs::Transformation::TransformationCase::kAddTypeBoolean:
       return MakeUnique<TransformationAddTypeBoolean>(
           message.add_type_boolean());
@@ -59,6 +73,14 @@
     case protobufs::Transformation::TransformationCase::kAddTypePointer:
       return MakeUnique<TransformationAddTypePointer>(
           message.add_type_pointer());
+    case protobufs::Transformation::TransformationCase::kCompositeConstruct:
+      return MakeUnique<TransformationCompositeConstruct>(
+          message.composite_construct());
+    case protobufs::Transformation::TransformationCase::kCompositeExtract:
+      return MakeUnique<TransformationCompositeExtract>(
+          message.composite_extract());
+    case protobufs::Transformation::TransformationCase::kCopyObject:
+      return MakeUnique<TransformationCopyObject>(message.copy_object());
     case protobufs::Transformation::TransformationCase::kMoveBlockDown:
       return MakeUnique<TransformationMoveBlockDown>(message.move_block_down());
     case protobufs::Transformation::TransformationCase::
@@ -69,15 +91,31 @@
         kReplaceConstantWithUniform:
       return MakeUnique<TransformationReplaceConstantWithUniform>(
           message.replace_constant_with_uniform());
+    case protobufs::Transformation::TransformationCase::kReplaceIdWithSynonym:
+      return MakeUnique<TransformationReplaceIdWithSynonym>(
+          message.replace_id_with_synonym());
+    case protobufs::Transformation::TransformationCase::kSetFunctionControl:
+      return MakeUnique<TransformationSetFunctionControl>(
+          message.set_function_control());
+    case protobufs::Transformation::TransformationCase::kSetLoopControl:
+      return MakeUnique<TransformationSetLoopControl>(
+          message.set_loop_control());
+    case protobufs::Transformation::TransformationCase::kSetMemoryOperandsMask:
+      return MakeUnique<TransformationSetMemoryOperandsMask>(
+          message.set_memory_operands_mask());
+    case protobufs::Transformation::TransformationCase::kSetSelectionControl:
+      return MakeUnique<TransformationSetSelectionControl>(
+          message.set_selection_control());
     case protobufs::Transformation::TransformationCase::kSplitBlock:
       return MakeUnique<TransformationSplitBlock>(message.split_block());
-    default:
-      assert(message.transformation_case() ==
-                 protobufs::Transformation::TRANSFORMATION_NOT_SET &&
-             "Unhandled transformation type.");
+    case protobufs::Transformation::TransformationCase::kVectorShuffle:
+      return MakeUnique<TransformationVectorShuffle>(message.vector_shuffle());
+    case protobufs::Transformation::TRANSFORMATION_NOT_SET:
       assert(false && "An unset transformation was encountered.");
       return nullptr;
   }
+  assert(false && "Should be unreachable as all cases must be handled above.");
+  return nullptr;
 }
 
 }  // namespace fuzz
diff --git a/source/fuzz/transformation_add_dead_break.cpp b/source/fuzz/transformation_add_dead_break.cpp
index 229dc90..b244cf4 100644
--- a/source/fuzz/transformation_add_dead_break.cpp
+++ b/source/fuzz/transformation_add_dead_break.cpp
@@ -138,6 +138,13 @@
     return false;
   }
 
+  if (!fuzzerutil::BlockIsReachableInItsFunction(context, bb_to)) {
+    // If the target of the break is unreachable, we conservatively do not
+    // allow adding a dead break, to avoid the compilations that arise due to
+    // the lack of sensible dominance information for unreachable blocks.
+    return false;
+  }
+
   // Check that |message_.from_block| ends with an unconditional branch.
   if (bb_from->terminator()->opcode() != SpvOpBranch) {
     // The block associated with the id does not end with an unconditional
@@ -162,9 +169,15 @@
     return false;
   }
 
-  // Finally, check that adding the break would respect the rules of structured
+  // Check that adding the break would respect the rules of structured
   // control flow.
-  return AddingBreakRespectsStructuredControlFlow(context, bb_from);
+  if (!AddingBreakRespectsStructuredControlFlow(context, bb_from)) {
+    return false;
+  }
+
+  // Check that adding the break would not violate the property that a
+  // definition must dominate all of its uses.
+  return fuzzerutil::NewEdgeRespectsUseDefDominance(context, bb_from, bb_to);
 }
 
 void TransformationAddDeadBreak::Apply(opt::IRContext* context,
diff --git a/source/fuzz/transformation_add_dead_break.h b/source/fuzz/transformation_add_dead_break.h
index aeb4dbb..10d2cec 100644
--- a/source/fuzz/transformation_add_dead_break.h
+++ b/source/fuzz/transformation_add_dead_break.h
@@ -48,6 +48,8 @@
   //   the condition, and the ids in |message_.phi_ids| used to extend
   //   any OpPhi instructions at b as a result of the edge from a, must
   //   maintain validity of the module.
+  //   In particular, the new branch must not lead to violations of the rule
+  //   that a use must be dominated by its definition.
   bool IsApplicable(opt::IRContext* context,
                     const FactManager& fact_manager) const override;
 
diff --git a/source/fuzz/transformation_add_dead_continue.cpp b/source/fuzz/transformation_add_dead_continue.cpp
index e3b3da2..e644b88 100644
--- a/source/fuzz/transformation_add_dead_continue.cpp
+++ b/source/fuzz/transformation_add_dead_continue.cpp
@@ -83,14 +83,22 @@
     return false;
   }
 
+  auto continue_block = context->cfg()->block(loop_header)->ContinueBlockId();
+
+  if (!fuzzerutil::BlockIsReachableInItsFunction(
+          context, context->cfg()->block(continue_block))) {
+    // If the loop's continue block is unreachable, we conservatively do not
+    // allow adding a dead continue, to avoid the compilations that arise due to
+    // the lack of sensible dominance information for unreachable blocks.
+    return false;
+  }
+
   if (fuzzerutil::BlockIsInLoopContinueConstruct(context, message_.from_block(),
                                                  loop_header)) {
     // We cannot jump to the continue target from the continue construct.
     return false;
   }
 
-  auto continue_block = context->cfg()->block(loop_header)->ContinueBlockId();
-
   if (context->GetStructuredCFGAnalysis()->IsMergeBlock(continue_block)) {
     // A branch straight to the continue target that is also a merge block might
     // break the property that a construct header must dominate its merge block
@@ -98,6 +106,13 @@
     return false;
   }
 
+  // Check that adding the continue would not violate the property that a
+  // definition must dominate all of its uses.
+  if (!fuzzerutil::NewEdgeRespectsUseDefDominance(
+          context, bb_from, context->cfg()->block(continue_block))) {
+    return false;
+  }
+
   // The transformation is good if and only if the given phi ids are sufficient
   // to extend relevant OpPhi instructions in the continue block.
   return fuzzerutil::PhiIdsOkForNewEdge(context, bb_from,
diff --git a/source/fuzz/transformation_add_dead_continue.h b/source/fuzz/transformation_add_dead_continue.h
index e49e2a8..df6bb4c 100644
--- a/source/fuzz/transformation_add_dead_continue.h
+++ b/source/fuzz/transformation_add_dead_continue.h
@@ -36,6 +36,8 @@
 
   // - |message_.from_block| must be the id of a block a in the given module.
   // - a must be contained in a loop with continue target b
+  // - The continue target b must be dominated by the head of the loop in which
+  //   it is contained
   // - b must not be the merge block of a selection construct
   // - if |message_.continue_condition_value| holds (does not hold) then
   //   OpConstantTrue (OpConstantFalse) must be present in the module
@@ -47,6 +49,9 @@
   //   as the condition, and the ids in |message_.phi_ids| used to extend any
   //   OpPhi instructions at b as a result of the edge from a, must maintain
   //   validity of the module.
+  //   In particular, adding an edge from somewhere in the loop to the continue
+  //   target must not prevent uses of ids in the continue target from being
+  //   dominated by the definitions of those ids.
   bool IsApplicable(opt::IRContext* context,
                     const FactManager& fact_manager) const override;
 
diff --git a/source/fuzz/transformation_add_no_contraction_decoration.cpp b/source/fuzz/transformation_add_no_contraction_decoration.cpp
new file mode 100644
index 0000000..7f22cc2
--- /dev/null
+++ b/source/fuzz/transformation_add_no_contraction_decoration.cpp
@@ -0,0 +1,110 @@
+// 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.
+
+#include "source/fuzz/transformation_add_no_contraction_decoration.h"
+
+#include "source/fuzz/fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationAddNoContractionDecoration::
+    TransformationAddNoContractionDecoration(
+        const spvtools::fuzz::protobufs::
+            TransformationAddNoContractionDecoration& message)
+    : message_(message) {}
+
+TransformationAddNoContractionDecoration::
+    TransformationAddNoContractionDecoration(uint32_t result_id) {
+  message_.set_result_id(result_id);
+}
+
+bool TransformationAddNoContractionDecoration::IsApplicable(
+    opt::IRContext* context,
+    const spvtools::fuzz::FactManager& /*unused*/) const {
+  // |message_.result_id| must be the id of an instruction.
+  auto instr = context->get_def_use_mgr()->GetDef(message_.result_id());
+  if (!instr) {
+    return false;
+  }
+  // The instruction must be arithmetic.
+  return IsArithmetic(instr->opcode());
+}
+
+void TransformationAddNoContractionDecoration::Apply(
+    opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
+  // Add a NoContraction decoration targeting |message_.result_id|.
+  context->get_decoration_mgr()->AddDecoration(message_.result_id(),
+                                               SpvDecorationNoContraction);
+}
+
+protobufs::Transformation TransformationAddNoContractionDecoration::ToMessage()
+    const {
+  protobufs::Transformation result;
+  *result.mutable_add_no_contraction_decoration() = message_;
+  return result;
+}
+
+bool TransformationAddNoContractionDecoration::IsArithmetic(uint32_t 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:
+      return true;
+    default:
+      return false;
+  }
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/source/fuzz/transformation_add_no_contraction_decoration.h b/source/fuzz/transformation_add_no_contraction_decoration.h
new file mode 100644
index 0000000..cec1b2c
--- /dev/null
+++ b/source/fuzz/transformation_add_no_contraction_decoration.h
@@ -0,0 +1,58 @@
+// 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.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_NO_CONTRACTION_DECORATION_H_
+#define SOURCE_FUZZ_TRANSFORMATION_ADD_NO_CONTRACTION_DECORATION_H_
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationAddNoContractionDecoration : public Transformation {
+ public:
+  explicit TransformationAddNoContractionDecoration(
+      const protobufs::TransformationAddNoContractionDecoration& message);
+
+  explicit TransformationAddNoContractionDecoration(uint32_t fresh_id);
+
+  // - |message_.result_id| must be the result id of an arithmetic instruction,
+  //   as defined by the SPIR-V specification.
+  // - It does not matter whether this instruction is already annotated with the
+  //   NoContraction decoration.
+  bool IsApplicable(opt::IRContext* context,
+                    const FactManager& fact_manager) const override;
+
+  // Adds a decoration of the form:
+  //   'OpDecoration |message_.result_id| NoContraction'
+  // to the module.
+  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+  // 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);
+
+ private:
+  protobufs::TransformationAddNoContractionDecoration message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_ADD_NO_CONTRACTION_DECORATION_H_
diff --git a/source/fuzz/transformation_composite_construct.cpp b/source/fuzz/transformation_composite_construct.cpp
new file mode 100644
index 0000000..7a3aff1
--- /dev/null
+++ b/source/fuzz/transformation_composite_construct.cpp
@@ -0,0 +1,305 @@
+// 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.
+
+#include "source/fuzz/transformation_composite_construct.h"
+
+#include "source/fuzz/data_descriptor.h"
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/opt/instruction.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationCompositeConstruct::TransformationCompositeConstruct(
+    const protobufs::TransformationCompositeConstruct& message)
+    : message_(message) {}
+
+TransformationCompositeConstruct::TransformationCompositeConstruct(
+    uint32_t composite_type_id, std::vector<uint32_t> component,
+    const protobufs::InstructionDescriptor& instruction_to_insert_before,
+    uint32_t fresh_id) {
+  message_.set_composite_type_id(composite_type_id);
+  for (auto a_component : component) {
+    message_.add_component(a_component);
+  }
+  *message_.mutable_instruction_to_insert_before() =
+      instruction_to_insert_before;
+  message_.set_fresh_id(fresh_id);
+}
+
+bool TransformationCompositeConstruct::IsApplicable(
+    opt::IRContext* context, const FactManager& /*fact_manager*/) const {
+  if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+    // We require the id for the composite constructor to be unused.
+    return false;
+  }
+
+  auto insert_before =
+      FindInstruction(message_.instruction_to_insert_before(), context);
+  if (!insert_before) {
+    // The instruction before which the composite should be inserted was not
+    // found.
+    return false;
+  }
+
+  auto composite_type =
+      context->get_type_mgr()->GetType(message_.composite_type_id());
+
+  if (!fuzzerutil::IsCompositeType(composite_type)) {
+    // The type must actually be a composite.
+    return false;
+  }
+
+  // If the type is an array, matrix, struct or vector, the components need to
+  // be suitable for constructing something of that type.
+  if (composite_type->AsArray() && !ComponentsForArrayConstructionAreOK(
+                                       context, *composite_type->AsArray())) {
+    return false;
+  }
+  if (composite_type->AsMatrix() && !ComponentsForMatrixConstructionAreOK(
+                                        context, *composite_type->AsMatrix())) {
+    return false;
+  }
+  if (composite_type->AsStruct() && !ComponentsForStructConstructionAreOK(
+                                        context, *composite_type->AsStruct())) {
+    return false;
+  }
+  if (composite_type->AsVector() && !ComponentsForVectorConstructionAreOK(
+                                        context, *composite_type->AsVector())) {
+    return false;
+  }
+
+  // Now check whether every component being used to initialize the composite is
+  // available at the desired program point.
+  for (auto& component : message_.component()) {
+    auto component_inst = context->get_def_use_mgr()->GetDef(component);
+    if (!context->get_instr_block(component)) {
+      // The component does not have a block; that means it is in global scope,
+      // which is OK. (Whether the component actually corresponds to an
+      // instruction is checked above when determining whether types are
+      // suitable.)
+      continue;
+    }
+    // Check whether the component is available.
+    if (insert_before->HasResultId() &&
+        insert_before->result_id() == component) {
+      // This constitutes trying to use an id right before it is defined.  The
+      // special case is needed due to an instruction always dominating itself.
+      return false;
+    }
+    if (!context
+             ->GetDominatorAnalysis(
+                 context->get_instr_block(&*insert_before)->GetParent())
+             ->Dominates(component_inst, &*insert_before)) {
+      // The instruction defining the component must dominate the instruction we
+      // wish to insert the composite before.
+      return false;
+    }
+  }
+
+  return true;
+}
+
+void TransformationCompositeConstruct::Apply(opt::IRContext* context,
+                                             FactManager* fact_manager) const {
+  // Use the base and offset information from the transformation to determine
+  // where in the module a new instruction should be inserted.
+  auto insert_before_inst =
+      FindInstruction(message_.instruction_to_insert_before(), context);
+  auto destination_block = context->get_instr_block(insert_before_inst);
+  auto insert_before = fuzzerutil::GetIteratorForInstruction(
+      destination_block, insert_before_inst);
+
+  // Prepare the input operands for an OpCompositeConstruct instruction.
+  opt::Instruction::OperandList in_operands;
+  for (auto& component_id : message_.component()) {
+    in_operands.push_back({SPV_OPERAND_TYPE_ID, {component_id}});
+  }
+
+  // Insert an OpCompositeConstruct instruction.
+  insert_before.InsertBefore(MakeUnique<opt::Instruction>(
+      context, SpvOpCompositeConstruct, message_.composite_type_id(),
+      message_.fresh_id(), in_operands));
+
+  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+  context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+
+  // Inform the fact manager that we now have new synonyms: every component of
+  // the composite is synonymous with the id used to construct that component,
+  // except in the case of a vector where a single vector id can span multiple
+  // components.
+  auto composite_type =
+      context->get_type_mgr()->GetType(message_.composite_type_id());
+  uint32_t index = 0;
+  for (auto component : message_.component()) {
+    auto component_type = context->get_type_mgr()->GetType(
+        context->get_def_use_mgr()->GetDef(component)->type_id());
+    if (composite_type->AsVector() && component_type->AsVector()) {
+      // The case where the composite being constructed is a vector and the
+      // component provided for construction is also a vector is special.  It
+      // requires adding a synonym fact relating each element of the sub-vector
+      // to the corresponding element of the composite being constructed.
+      assert(component_type->AsVector()->element_type() ==
+             composite_type->AsVector()->element_type());
+      assert(component_type->AsVector()->element_count() <
+             composite_type->AsVector()->element_count());
+      for (uint32_t subvector_index = 0;
+           subvector_index < component_type->AsVector()->element_count();
+           subvector_index++) {
+        fact_manager->AddFactDataSynonym(
+            MakeDataDescriptor(component, {subvector_index}),
+            MakeDataDescriptor(message_.fresh_id(), {index}), context);
+        index++;
+      }
+    } else {
+      // The other cases are simple: the component is made directly synonymous
+      // with the element of the composite being constructed.
+      fact_manager->AddFactDataSynonym(
+          MakeDataDescriptor(component, {}),
+          MakeDataDescriptor(message_.fresh_id(), {index}), context);
+      index++;
+    }
+  }
+}
+
+bool TransformationCompositeConstruct::ComponentsForArrayConstructionAreOK(
+    opt::IRContext* context, const opt::analysis::Array& array_type) const {
+  if (array_type.length_info().words[0] !=
+      opt::analysis::Array::LengthInfo::kConstant) {
+    // We only handle constant-sized arrays.
+    return false;
+  }
+  if (array_type.length_info().words.size() != 2) {
+    // We only handle the case where the array size can be captured in a single
+    // word.
+    return false;
+  }
+  // Get the array size.
+  auto array_size = array_type.length_info().words[1];
+  if (static_cast<uint32_t>(message_.component().size()) != array_size) {
+    // The number of components must match the array size.
+    return false;
+  }
+  // Check that each component is the result id of an instruction whose type is
+  // the array's element type.
+  for (auto component_id : message_.component()) {
+    auto inst = context->get_def_use_mgr()->GetDef(component_id);
+    if (inst == nullptr || !inst->type_id()) {
+      // The component does not correspond to an instruction with a result
+      // type.
+      return false;
+    }
+    auto component_type = context->get_type_mgr()->GetType(inst->type_id());
+    assert(component_type);
+    if (component_type != array_type.element_type()) {
+      // The component's type does not match the array's element type.
+      return false;
+    }
+  }
+  return true;
+}
+
+bool TransformationCompositeConstruct::ComponentsForMatrixConstructionAreOK(
+    opt::IRContext* context, const opt::analysis::Matrix& matrix_type) const {
+  if (static_cast<uint32_t>(message_.component().size()) !=
+      matrix_type.element_count()) {
+    // The number of components must match the number of columns of the matrix.
+    return false;
+  }
+  // Check that each component is the result id of an instruction whose type is
+  // the matrix's column type.
+  for (auto component_id : message_.component()) {
+    auto inst = context->get_def_use_mgr()->GetDef(component_id);
+    if (inst == nullptr || !inst->type_id()) {
+      // The component does not correspond to an instruction with a result
+      // type.
+      return false;
+    }
+    auto component_type = context->get_type_mgr()->GetType(inst->type_id());
+    assert(component_type);
+    if (component_type != matrix_type.element_type()) {
+      // The component's type does not match the matrix's column type.
+      return false;
+    }
+  }
+  return true;
+}
+
+bool TransformationCompositeConstruct::ComponentsForStructConstructionAreOK(
+    opt::IRContext* context, const opt::analysis::Struct& struct_type) const {
+  if (static_cast<uint32_t>(message_.component().size()) !=
+      struct_type.element_types().size()) {
+    // The number of components must match the number of fields of the struct.
+    return false;
+  }
+  // Check that each component is the result id of an instruction those type
+  // matches the associated field type.
+  for (uint32_t field_index = 0;
+       field_index < struct_type.element_types().size(); field_index++) {
+    auto inst =
+        context->get_def_use_mgr()->GetDef(message_.component()[field_index]);
+    if (inst == nullptr || !inst->type_id()) {
+      // The component does not correspond to an instruction with a result
+      // type.
+      return false;
+    }
+    auto component_type = context->get_type_mgr()->GetType(inst->type_id());
+    assert(component_type);
+    if (component_type != struct_type.element_types()[field_index]) {
+      // The component's type does not match the corresponding field type.
+      return false;
+    }
+  }
+  return true;
+}
+
+bool TransformationCompositeConstruct::ComponentsForVectorConstructionAreOK(
+    opt::IRContext* context, const opt::analysis::Vector& vector_type) const {
+  uint32_t base_element_count = 0;
+  auto element_type = vector_type.element_type();
+  for (auto& component_id : message_.component()) {
+    auto inst = context->get_def_use_mgr()->GetDef(component_id);
+    if (inst == nullptr || !inst->type_id()) {
+      // The component does not correspond to an instruction with a result
+      // type.
+      return false;
+    }
+    auto component_type = context->get_type_mgr()->GetType(inst->type_id());
+    assert(component_type);
+    if (component_type == element_type) {
+      base_element_count++;
+    } else if (component_type->AsVector() &&
+               component_type->AsVector()->element_type() == element_type) {
+      base_element_count += component_type->AsVector()->element_count();
+    } else {
+      // The component was not appropriate; e.g. no type corresponding to the
+      // given id was found, or the type that was found was not compatible
+      // with the vector being constructed.
+      return false;
+    }
+  }
+  // The number of components provided (when vector components are flattened
+  // out) needs to match the length of the vector being constructed.
+  return base_element_count == vector_type.element_count();
+}
+
+protobufs::Transformation TransformationCompositeConstruct::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_composite_construct() = message_;
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/source/fuzz/transformation_composite_construct.h b/source/fuzz/transformation_composite_construct.h
new file mode 100644
index 0000000..5369c4c
--- /dev/null
+++ b/source/fuzz/transformation_composite_construct.h
@@ -0,0 +1,88 @@
+// 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.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_COMPOSITE_CONSTRUCT_H_
+#define SOURCE_FUZZ_TRANSFORMATION_COMPOSITE_CONSTRUCT_H_
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationCompositeConstruct : public Transformation {
+ public:
+  explicit TransformationCompositeConstruct(
+      const protobufs::TransformationCompositeConstruct& message);
+
+  TransformationCompositeConstruct(
+      uint32_t composite_type_id, std::vector<uint32_t> component,
+      const protobufs::InstructionDescriptor& instruction_to_insert_before,
+      uint32_t fresh_id);
+
+  // - |message_.fresh_id| must not be used by the module.
+  // - |message_.composite_type_id| must be the id of a composite type
+  // - The elements of |message_.component| must be result ids that are
+  //   suitable for constructing an element of the given composite type, in
+  //   order
+  // - The elements of |message_.component| must not be the target of any
+  //   decorations.
+  // - |message_.base_instruction_id| must be the result id of an instruction
+  //   'base' in some block 'blk'.
+  // - 'blk' must contain an instruction 'inst' located |message_.offset|
+  //   instructions after 'base' (if |message_.offset| = 0 then 'inst' =
+  //   'base').
+  // - It must be legal to insert an OpCompositeConstruct instruction directly
+  //   before 'inst'.
+  // - Each element of |message_.component| must be available directly before
+  //   'inst'.
+  bool IsApplicable(opt::IRContext* context,
+                    const FactManager& fact_manager) const override;
+
+  // Inserts a new OpCompositeConstruct instruction, with id
+  // |message_.fresh_id|, directly before the instruction identified by
+  // |message_.base_instruction_id| and |message_.offset|.  The instruction
+  // creates a composite of type |message_.composite_type_id| using the ids of
+  // |message_.component|.
+  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  // Helper to decide whether the components of the transformation are suitable
+  // for constructing an array of the given type.
+  bool ComponentsForArrayConstructionAreOK(
+      opt::IRContext* context, const opt::analysis::Array& array_type) const;
+
+  // Similar, but for matrices.
+  bool ComponentsForMatrixConstructionAreOK(
+      opt::IRContext* context, const opt::analysis::Matrix& matrix_type) const;
+
+  // Similar, but for structs.
+  bool ComponentsForStructConstructionAreOK(
+      opt::IRContext* context, const opt::analysis::Struct& struct_type) const;
+
+  // Similar, but for vectors.
+  bool ComponentsForVectorConstructionAreOK(
+      opt::IRContext* context, const opt::analysis::Vector& vector_type) const;
+
+  protobufs::TransformationCompositeConstruct message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_COMPOSITE_CONSTRUCT_H_
diff --git a/source/fuzz/transformation_composite_extract.cpp b/source/fuzz/transformation_composite_extract.cpp
new file mode 100644
index 0000000..5d3a386
--- /dev/null
+++ b/source/fuzz/transformation_composite_extract.cpp
@@ -0,0 +1,125 @@
+// 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.
+
+#include "source/fuzz/transformation_composite_extract.h"
+
+#include <vector>
+
+#include "source/fuzz/data_descriptor.h"
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationCompositeExtract::TransformationCompositeExtract(
+    const spvtools::fuzz::protobufs::TransformationCompositeExtract& message)
+    : message_(message) {}
+
+TransformationCompositeExtract::TransformationCompositeExtract(
+    const protobufs::InstructionDescriptor& instruction_to_insert_before,
+    uint32_t fresh_id, uint32_t composite_id, std::vector<uint32_t>&& index) {
+  *message_.mutable_instruction_to_insert_before() =
+      instruction_to_insert_before;
+  message_.set_fresh_id(fresh_id);
+  message_.set_composite_id(composite_id);
+  for (auto an_index : index) {
+    message_.add_index(an_index);
+  }
+}
+
+bool TransformationCompositeExtract::IsApplicable(
+    opt::IRContext* context,
+    const spvtools::fuzz::FactManager& /*unused*/) const {
+  if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+    return false;
+  }
+  auto instruction_to_insert_before =
+      FindInstruction(message_.instruction_to_insert_before(), context);
+  if (!instruction_to_insert_before) {
+    return false;
+  }
+  auto composite_instruction =
+      context->get_def_use_mgr()->GetDef(message_.composite_id());
+  if (!composite_instruction) {
+    return false;
+  }
+  if (auto block = context->get_instr_block(composite_instruction)) {
+    if (composite_instruction == instruction_to_insert_before ||
+        !context->GetDominatorAnalysis(block->GetParent())
+             ->Dominates(composite_instruction, instruction_to_insert_before)) {
+      return false;
+    }
+  }
+  assert(composite_instruction->type_id() &&
+         "An instruction in a block cannot have a result id but no type id.");
+
+  auto composite_type =
+      context->get_type_mgr()->GetType(composite_instruction->type_id());
+  if (!composite_type) {
+    return false;
+  }
+
+  if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(
+          SpvOpCompositeExtract, instruction_to_insert_before)) {
+    return false;
+  }
+
+  return fuzzerutil::WalkCompositeTypeIndices(
+             context, composite_instruction->type_id(), message_.index()) != 0;
+}
+
+void TransformationCompositeExtract::Apply(
+    opt::IRContext* context, spvtools::fuzz::FactManager* fact_manager) const {
+  opt::Instruction::OperandList extract_operands;
+  extract_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.composite_id()}});
+  for (auto an_index : message_.index()) {
+    extract_operands.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {an_index}});
+  }
+  auto composite_instruction =
+      context->get_def_use_mgr()->GetDef(message_.composite_id());
+  auto extracted_type = fuzzerutil::WalkCompositeTypeIndices(
+      context, composite_instruction->type_id(), message_.index());
+
+  FindInstruction(message_.instruction_to_insert_before(), context)
+      ->InsertBefore(MakeUnique<opt::Instruction>(
+          context, SpvOpCompositeExtract, extracted_type, message_.fresh_id(),
+          extract_operands));
+
+  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+
+  context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+
+  // Add the fact that the id storing the extracted element is synonymous with
+  // the index into the structure.
+  std::vector<uint32_t> indices;
+  for (auto an_index : message_.index()) {
+    indices.push_back(an_index);
+  }
+  protobufs::DataDescriptor data_descriptor_for_extracted_element =
+      MakeDataDescriptor(message_.composite_id(), std::move(indices));
+  protobufs::DataDescriptor data_descriptor_for_result_id =
+      MakeDataDescriptor(message_.fresh_id(), {});
+  fact_manager->AddFactDataSynonym(data_descriptor_for_extracted_element,
+                                   data_descriptor_for_result_id, context);
+}
+
+protobufs::Transformation TransformationCompositeExtract::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_composite_extract() = message_;
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/source/fuzz/transformation_composite_extract.h b/source/fuzz/transformation_composite_extract.h
new file mode 100644
index 0000000..c4c9278
--- /dev/null
+++ b/source/fuzz/transformation_composite_extract.h
@@ -0,0 +1,63 @@
+// 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.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_COMPOSITE_EXTRACT_H_
+#define SOURCE_FUZZ_TRANSFORMATION_COMPOSITE_EXTRACT_H_
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationCompositeExtract : public Transformation {
+ public:
+  explicit TransformationCompositeExtract(
+      const protobufs::TransformationCompositeExtract& message);
+
+  TransformationCompositeExtract(
+      const protobufs::InstructionDescriptor& instruction_to_insert_before,
+      uint32_t fresh_id, uint32_t composite_id, std::vector<uint32_t>&& index);
+
+  // - |message_.fresh_id| must be available
+  // - |message_.instruction_to_insert_before| must identify an instruction
+  //   before which it is valid to place an OpCompositeExtract
+  // - |message_.composite_id| must be the id of an instruction that defines
+  //   a composite object, and this id must be available at the instruction
+  //   identified by |message_.instruction_to_insert_before|
+  // - |message_.index| must be a suitable set of indices for
+  //   |message_.composite_id|, i.e. it must be possible to follow this chain
+  //   of indices to reach a sub-object of |message_.composite_id|
+  bool IsApplicable(opt::IRContext* context,
+                    const FactManager& fact_manager) const override;
+
+  // Adds an OpCompositeConstruct instruction before the instruction identified
+  // by |message_.instruction_to_insert_before|, that extracts from
+  // |message_.composite_id| via indices |message_.index| into
+  // |message_.fresh_id|.  Generates a data synonym fact relating
+  // |message_.fresh_id| to the extracted element.
+  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  protobufs::TransformationCompositeExtract message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_COMPOSITE_EXTRACT_H_
diff --git a/source/fuzz/transformation_copy_object.cpp b/source/fuzz/transformation_copy_object.cpp
index f9ead43..af1e81c 100644
--- a/source/fuzz/transformation_copy_object.cpp
+++ b/source/fuzz/transformation_copy_object.cpp
@@ -14,7 +14,9 @@
 
 #include "source/fuzz/transformation_copy_object.h"
 
+#include "source/fuzz/data_descriptor.h"
 #include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
 #include "source/opt/instruction.h"
 #include "source/util/make_unique.h"
 
@@ -25,13 +27,13 @@
     const protobufs::TransformationCopyObject& message)
     : message_(message) {}
 
-TransformationCopyObject::TransformationCopyObject(uint32_t object,
-                                                   uint32_t base_instruction_id,
-                                                   uint32_t offset,
-                                                   uint32_t fresh_id) {
+TransformationCopyObject::TransformationCopyObject(
+    uint32_t object,
+    const protobufs::InstructionDescriptor& instruction_to_insert_before,
+    uint32_t fresh_id) {
   message_.set_object(object);
-  message_.set_base_instruction_id(base_instruction_id);
-  message_.set_offset(offset);
+  *message_.mutable_instruction_to_insert_before() =
+      instruction_to_insert_before;
   message_.set_fresh_id(fresh_id);
 }
 
@@ -46,57 +48,22 @@
   if (!object_inst) {
     return false;
   }
-  if (!object_inst->type_id()) {
-    // We can only apply OpCopyObject to instructions that have types.
-    return false;
-  }
-  if (!context->get_decoration_mgr()
-           ->GetDecorationsFor(message_.object(), true)
-           .empty()) {
-    // We do not copy objects that have decorations: if the copy is not
-    // decorated analogously, using the original object vs. its copy may not be
-    // equivalent.
-    // TODO(afd): it would be possible to make the copy but not add an id
-    // synonym.
+  if (!fuzzerutil::CanMakeSynonymOf(context, object_inst)) {
     return false;
   }
 
-  auto base_instruction =
-      context->get_def_use_mgr()->GetDef(message_.base_instruction_id());
-  if (!base_instruction) {
-    // The given id to insert after is not defined.
+  auto insert_before =
+      FindInstruction(message_.instruction_to_insert_before(), context);
+  if (!insert_before) {
+    // The instruction before which the copy should be inserted was not found.
     return false;
   }
 
-  auto destination_block = context->get_instr_block(base_instruction);
-  if (!destination_block) {
-    // The given id to insert after is not in a block.
+  if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCopyObject,
+                                                    insert_before)) {
     return false;
   }
 
-  auto insert_before = fuzzerutil::GetIteratorForBaseInstructionAndOffset(
-      destination_block, base_instruction, message_.offset());
-
-  if (insert_before == destination_block->end()) {
-    // The offset was inappropriate.
-    return false;
-  }
-  if (insert_before->PreviousNode() &&
-      (insert_before->PreviousNode()->opcode() == SpvOpLoopMerge ||
-       insert_before->PreviousNode()->opcode() == SpvOpSelectionMerge)) {
-    // We cannot insert a copy directly after a merge instruction.
-    return false;
-  }
-  if (insert_before->opcode() == SpvOpVariable) {
-    // We cannot insert a copy directly before a variable; variables in a
-    // function must be contiguous in the entry block.
-    return false;
-  }
-  // We cannot insert a copy directly before OpPhi, because OpPhi instructions
-  // need to be contiguous at the start of a block.
-  if (insert_before->opcode() == SpvOpPhi) {
-    return false;
-  }
   // |message_object| must be available at the point where we want to add the
   // copy. It is available if it is at global scope (in which case it has no
   // block), or if it dominates the point of insertion but is different from the
@@ -107,28 +74,22 @@
   // insert it before the object's defining instruction.
   return !context->get_instr_block(object_inst) ||
          (object_inst != &*insert_before &&
-          context->GetDominatorAnalysis(destination_block->GetParent())
+          context
+              ->GetDominatorAnalysis(
+                  context->get_instr_block(insert_before)->GetParent())
               ->Dominates(object_inst, &*insert_before));
 }
 
 void TransformationCopyObject::Apply(opt::IRContext* context,
                                      FactManager* fact_manager) const {
-  // - A new instruction,
-  //     %|message_.fresh_id| = OpCopyObject %ty %|message_.object|
-  //   is added directly before the instruction at |message_.insert_after_id| +
-  //   |message_|.offset, where %ty is the type of |message_.object|.
-  // - The fact that |message_.fresh_id| and |message_.object| are synonyms
-  //   is added to the fact manager.
-  // The id of the object to be copied must exist
   auto object_inst = context->get_def_use_mgr()->GetDef(message_.object());
   assert(object_inst && "The object to be copied must exist.");
-  auto base_instruction =
-      context->get_def_use_mgr()->GetDef(message_.base_instruction_id());
-  assert(base_instruction && "The base instruction must exist.");
-  auto destination_block = context->get_instr_block(base_instruction);
+  auto insert_before_inst =
+      FindInstruction(message_.instruction_to_insert_before(), context);
+  auto destination_block = context->get_instr_block(insert_before_inst);
   assert(destination_block && "The base instruction must be in a block.");
-  auto insert_before = fuzzerutil::GetIteratorForBaseInstructionAndOffset(
-      destination_block, base_instruction, message_.offset());
+  auto insert_before = fuzzerutil::GetIteratorForInstruction(
+      destination_block, insert_before_inst);
   assert(insert_before != destination_block->end() &&
          "There must be an instruction before which the copy can be inserted.");
 
@@ -141,11 +102,9 @@
   fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
   context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
 
-  protobufs::Fact fact;
-  fact.mutable_id_synonym_fact()->set_id(message_.object());
-  fact.mutable_id_synonym_fact()->mutable_data_descriptor()->set_object(
-      message_.fresh_id());
-  fact_manager->AddFact(fact, context);
+  fact_manager->AddFactDataSynonym(MakeDataDescriptor(message_.object(), {}),
+                                   MakeDataDescriptor(message_.fresh_id(), {}),
+                                   context);
 }
 
 protobufs::Transformation TransformationCopyObject::ToMessage() const {
diff --git a/source/fuzz/transformation_copy_object.h b/source/fuzz/transformation_copy_object.h
index 6ce72df..3a75ac9 100644
--- a/source/fuzz/transformation_copy_object.h
+++ b/source/fuzz/transformation_copy_object.h
@@ -28,8 +28,10 @@
   explicit TransformationCopyObject(
       const protobufs::TransformationCopyObject& message);
 
-  TransformationCopyObject(uint32_t fresh_id, uint32_t object,
-                           uint32_t insert_after_id, uint32_t offset);
+  TransformationCopyObject(
+      uint32_t object,
+      const protobufs::InstructionDescriptor& instruction_to_insert_before,
+      uint32_t fresh_id);
 
   // - |message_.fresh_id| must not be used by the module.
   // - |message_.object| must be a result id that is a legitimate operand for
@@ -37,14 +39,14 @@
   //   has a result type
   // - |message_.object| must not be the target of any decoration.
   //   TODO(afd): consider copying decorations along with objects.
-  // - |message_.insert_after_id| must be the result id of an instruction
+  // - |message_.base_instruction_id| must be the result id of an instruction
   //   'base' in some block 'blk'.
   // - 'blk' must contain an instruction 'inst' located |message_.offset|
   //   instructions after 'base' (if |message_.offset| = 0 then 'inst' =
   //   'base').
   // - It must be legal to insert an OpCopyObject instruction directly
   //   before 'inst'.
-  // - |message_object| must be available directly before 'inst'.
+  // - |message_.object| must be available directly before 'inst'.
   bool IsApplicable(opt::IRContext* context,
                     const FactManager& fact_manager) const override;
 
diff --git a/source/fuzz/transformation_move_block_down.cpp b/source/fuzz/transformation_move_block_down.cpp
index ebce185..f181855 100644
--- a/source/fuzz/transformation_move_block_down.cpp
+++ b/source/fuzz/transformation_move_block_down.cpp
@@ -84,12 +84,12 @@
                "To be able to move a block down, it needs to have a "
                "program-order successor.");
         function.MoveBasicBlockToAfter(message_.block_id(), &*block_it);
-        // It is prudent to invalidate analyses after changing block ordering in
-        // case any of them depend on it, but the ones that definitely do not
-        // depend on ordering can be preserved. These include the following,
-        // which can likely be extended.
+        // For performance, it is vital to keep the dominator analysis valid
+        // (which due to https://github.com/KhronosGroup/SPIRV-Tools/issues/2889
+        // requires keeping the CFG analysis valid).
         context->InvalidateAnalysesExceptFor(
             opt::IRContext::Analysis::kAnalysisDefUse |
+            opt::IRContext::Analysis::kAnalysisCFG |
             opt::IRContext::Analysis::kAnalysisDominatorAnalysis);
 
         return;
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 91b4007..b097767 100644
--- a/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.cpp
+++ b/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.cpp
@@ -178,7 +178,7 @@
       context->get_constant_mgr()->FindDeclaredConstant(message_.rhs_id());
   bool expected_result = (boolean_constant->opcode() == SpvOpConstantTrue);
 
-  const SpvOp binary_opcode = static_cast<SpvOp>(message_.opcode());
+  const auto binary_opcode = static_cast<SpvOp>(message_.opcode());
 
   // We consider the floating point, signed and unsigned integer cases
   // separately.  In each case the logic is very similar.
@@ -237,8 +237,17 @@
   }
 
   // The id use descriptor must identify some instruction
-  return transformation::FindInstruction(message_.id_use_descriptor(),
-                                         context) != nullptr;
+  auto instruction =
+      FindInstructionContainingUse(message_.id_use_descriptor(), context);
+  if (instruction == nullptr) {
+    return false;
+  }
+
+  // The instruction must not be an OpPhi, as we cannot insert a binary
+  // operator instruction before an OpPhi.
+  // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2902): there is
+  //  scope for being less conservative.
+  return instruction->opcode() != SpvOpPhi;
 }
 
 void TransformationReplaceBooleanConstantWithConstantBinary::Apply(
@@ -259,7 +268,7 @@
       message_.fresh_id_for_binary_operation(), operands);
   opt::Instruction* result = binary_instruction.get();
   auto instruction_containing_constant_use =
-      transformation::FindInstruction(message_.id_use_descriptor(), context);
+      FindInstructionContainingUse(message_.id_use_descriptor(), context);
 
   // We want to insert the new instruction before the instruction that contains
   // the use of the boolean, but we need to go backwards one more instruction if
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 c384093..f74cd8d 100644
--- a/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h
+++ b/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h
@@ -43,6 +43,12 @@
   // - |message_.opcode| must be suitable for applying to |message.lhs_id| and
   //   |message_.rhs_id|, and the result must evaluate to the boolean constant
   //   c.
+  // - The boolean constant usage must not be an argument to OpPhi, because in
+  //   this case it is not legal to insert a binary operator instruction right
+  //   before the OpPhi.
+  //   TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2902): consider
+  //    replacing a boolean in an OpPhi by adding a binary operator instruction
+  //    to the parent block for the OpPhi.
   bool IsApplicable(opt::IRContext* context,
                     const FactManager& fact_manager) const override;
 
diff --git a/source/fuzz/transformation_replace_constant_with_uniform.cpp b/source/fuzz/transformation_replace_constant_with_uniform.cpp
index 48334ba..405776e 100644
--- a/source/fuzz/transformation_replace_constant_with_uniform.cpp
+++ b/source/fuzz/transformation_replace_constant_with_uniform.cpp
@@ -149,7 +149,7 @@
   // The id use descriptor must identify some instruction with respect to the
   // module.
   auto instruction_using_constant =
-      transformation::FindInstruction(message_.id_use_descriptor(), context);
+      FindInstructionContainingUse(message_.id_use_descriptor(), context);
   if (!instruction_using_constant) {
     return false;
   }
@@ -188,7 +188,7 @@
     spvtools::fuzz::FactManager* /*unused*/) const {
   // Get the instruction that contains the id use we wish to replace.
   auto instruction_containing_constant_use =
-      transformation::FindInstruction(message_.id_use_descriptor(), context);
+      FindInstructionContainingUse(message_.id_use_descriptor(), context);
   assert(instruction_containing_constant_use &&
          "Precondition requires that the id use can be found.");
   assert(instruction_containing_constant_use->GetSingleWordInOperand(
diff --git a/source/fuzz/transformation_replace_id_with_synonym.cpp b/source/fuzz/transformation_replace_id_with_synonym.cpp
new file mode 100644
index 0000000..79ba012
--- /dev/null
+++ b/source/fuzz/transformation_replace_id_with_synonym.cpp
@@ -0,0 +1,195 @@
+// 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.
+
+#include "source/fuzz/transformation_replace_id_with_synonym.h"
+
+#include <algorithm>
+
+#include "source/fuzz/data_descriptor.h"
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/id_use_descriptor.h"
+#include "source/opt/types.h"
+#include "source/util/make_unique.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationReplaceIdWithSynonym::TransformationReplaceIdWithSynonym(
+    const spvtools::fuzz::protobufs::TransformationReplaceIdWithSynonym&
+        message)
+    : message_(message) {}
+
+TransformationReplaceIdWithSynonym::TransformationReplaceIdWithSynonym(
+    protobufs::IdUseDescriptor id_use_descriptor, uint32_t synonymous_id) {
+  *message_.mutable_id_use_descriptor() = std::move(id_use_descriptor);
+  message_.set_synonymous_id(synonymous_id);
+}
+
+bool TransformationReplaceIdWithSynonym::IsApplicable(
+    spvtools::opt::IRContext* context,
+    const spvtools::fuzz::FactManager& fact_manager) const {
+  auto id_of_interest = message_.id_use_descriptor().id_of_interest();
+
+  // Does the fact manager know about the synonym?
+  auto data_descriptor_for_synonymous_id =
+      MakeDataDescriptor(message_.synonymous_id(), {});
+  if (!fact_manager.IsSynonymous(MakeDataDescriptor(id_of_interest, {}),
+                                 data_descriptor_for_synonymous_id, context)) {
+    return false;
+  }
+
+  // Does the id use descriptor in the transformation identify an instruction?
+  auto use_instruction =
+      FindInstructionContainingUse(message_.id_use_descriptor(), context);
+  if (!use_instruction) {
+    return false;
+  }
+
+  // Is the use suitable for being replaced in principle?
+  if (!UseCanBeReplacedWithSynonym(
+          context, use_instruction,
+          message_.id_use_descriptor().in_operand_index())) {
+    return false;
+  }
+
+  // The transformation is applicable if the synonymous id is available at the
+  // use point.
+  return IdsIsAvailableAtUse(context, use_instruction,
+                             message_.id_use_descriptor().in_operand_index(),
+                             message_.synonymous_id());
+}
+
+void TransformationReplaceIdWithSynonym::Apply(
+    spvtools::opt::IRContext* context,
+    spvtools::fuzz::FactManager* /*unused*/) const {
+  auto instruction_to_change =
+      FindInstructionContainingUse(message_.id_use_descriptor(), context);
+  instruction_to_change->SetInOperand(
+      message_.id_use_descriptor().in_operand_index(),
+      {message_.synonymous_id()});
+  context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationReplaceIdWithSynonym::ToMessage()
+    const {
+  protobufs::Transformation result;
+  *result.mutable_replace_id_with_synonym() = message_;
+  return result;
+}
+
+bool TransformationReplaceIdWithSynonym::IdsIsAvailableAtUse(
+    opt::IRContext* context, opt::Instruction* use_instruction,
+    uint32_t use_input_operand_index, uint32_t id) {
+  if (!context->get_instr_block(id)) {
+    return true;
+  }
+  auto defining_instruction = context->get_def_use_mgr()->GetDef(id);
+  if (defining_instruction == use_instruction) {
+    return false;
+  }
+  auto dominator_analysis = context->GetDominatorAnalysis(
+      context->get_instr_block(use_instruction)->GetParent());
+  if (use_instruction->opcode() == SpvOpPhi) {
+    // 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.
+    auto parent_block =
+        use_instruction->GetSingleWordInOperand(use_input_operand_index + 1);
+    return dominator_analysis->Dominates(
+        context->get_instr_block(defining_instruction)->id(), parent_block);
+  }
+  return dominator_analysis->Dominates(defining_instruction, use_instruction);
+}
+
+bool TransformationReplaceIdWithSynonym::UseCanBeReplacedWithSynonym(
+    opt::IRContext* context, opt::Instruction* use_instruction,
+    uint32_t use_in_operand_index) {
+  if (use_instruction->opcode() == SpvOpAccessChain &&
+      use_in_operand_index > 0) {
+    // This is an access chain index.  If the (sub-)object being accessed by the
+    // given index has struct type then we cannot replace the use with a
+    // synonym, as the use needs to be an OpConstant.
+
+    // Get the top-level composite type that is being accessed.
+    auto object_being_accessed = context->get_def_use_mgr()->GetDef(
+        use_instruction->GetSingleWordInOperand(0));
+    auto pointer_type =
+        context->get_type_mgr()->GetType(object_being_accessed->type_id());
+    assert(pointer_type->AsPointer());
+    auto composite_type_being_accessed =
+        pointer_type->AsPointer()->pointee_type();
+
+    // Now walk the access chain, tracking the type of each sub-object of the
+    // composite that is traversed, until the index of interest is reached.
+    for (uint32_t index_in_operand = 1; index_in_operand < use_in_operand_index;
+         index_in_operand++) {
+      // For vectors, matrices and arrays, getting the type of the sub-object is
+      // trivial. For the struct case, the sub-object type is field-sensitive,
+      // and depends on the constant index that is used.
+      if (composite_type_being_accessed->AsVector()) {
+        composite_type_being_accessed =
+            composite_type_being_accessed->AsVector()->element_type();
+      } else if (composite_type_being_accessed->AsMatrix()) {
+        composite_type_being_accessed =
+            composite_type_being_accessed->AsMatrix()->element_type();
+      } else if (composite_type_being_accessed->AsArray()) {
+        composite_type_being_accessed =
+            composite_type_being_accessed->AsArray()->element_type();
+      } else {
+        assert(composite_type_being_accessed->AsStruct());
+        auto constant_index_instruction = context->get_def_use_mgr()->GetDef(
+            use_instruction->GetSingleWordInOperand(index_in_operand));
+        assert(constant_index_instruction->opcode() == SpvOpConstant);
+        uint32_t member_index =
+            constant_index_instruction->GetSingleWordInOperand(0);
+        composite_type_being_accessed =
+            composite_type_being_accessed->AsStruct()
+                ->element_types()[member_index];
+      }
+    }
+
+    // We have found the composite type being accessed by the index we are
+    // considering replacing. If it is a struct, then we cannot do the
+    // replacement as struct indices must be constants.
+    if (composite_type_being_accessed->AsStruct()) {
+      return false;
+    }
+  }
+
+  if (use_instruction->opcode() == SpvOpFunctionCall &&
+      use_in_operand_index > 0) {
+    // This is a function call argument.  It is not allowed to have pointer
+    // type.
+
+    // Get the definition of the function being called.
+    auto function = context->get_def_use_mgr()->GetDef(
+        use_instruction->GetSingleWordInOperand(0));
+    // From the function definition, get the function type.
+    auto function_type =
+        context->get_def_use_mgr()->GetDef(function->GetSingleWordInOperand(1));
+    // OpTypeFunction's 0-th input operand is the function return type, and the
+    // function argument types follow. Because the arguments to OpFunctionCall
+    // start from input operand 1, we can use |use_in_operand_index| to get the
+    // type associated with this function argument.
+    auto parameter_type = context->get_type_mgr()->GetType(
+        function_type->GetSingleWordInOperand(use_in_operand_index));
+    if (parameter_type->AsPointer()) {
+      return false;
+    }
+  }
+  return true;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/source/fuzz/transformation_replace_id_with_synonym.h b/source/fuzz/transformation_replace_id_with_synonym.h
new file mode 100644
index 0000000..c21673d
--- /dev/null
+++ b/source/fuzz/transformation_replace_id_with_synonym.h
@@ -0,0 +1,80 @@
+// 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.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_REPLACE_ID_WITH_SYNONYM_H_
+#define SOURCE_FUZZ_TRANSFORMATION_REPLACE_ID_WITH_SYNONYM_H_
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationReplaceIdWithSynonym : public Transformation {
+ public:
+  explicit TransformationReplaceIdWithSynonym(
+      const protobufs::TransformationReplaceIdWithSynonym& message);
+
+  TransformationReplaceIdWithSynonym(
+      protobufs::IdUseDescriptor id_use_descriptor, uint32_t synonymous_id);
+
+  // - The fact manager must know that the id identified by
+  //   |message_.id_use_descriptor| is synonomous with
+  //   |message_.synonymous_id|.
+  // - Replacing the id in |message_.id_use_descriptor| by
+  //   |message_.synonymous_id| must respect SPIR-V's rules about uses being
+  //   dominated by their definitions.
+  // - The id must not be an index into an access chain whose base object has
+  //   struct type, as such indices must be constants.
+  // - The id must not be a pointer argument to a function call (because the
+  //   synonym might not be a memory object declaration).
+  // - |fresh_id_for_temporary| must be 0.
+  bool IsApplicable(opt::IRContext* context,
+                    const FactManager& fact_manager) const override;
+
+  // Replaces the use identified by |message_.id_use_descriptor| with the
+  // synonymous id identified by |message_.synonymous_id|.
+  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+  // Checks whether the |id| is available (according to dominance rules) at the
+  // use point defined by input operand |use_input_operand_index| of
+  // |use_instruction|.
+  static bool IdsIsAvailableAtUse(opt::IRContext* context,
+                                  opt::Instruction* use_instruction,
+                                  uint32_t use_input_operand_index,
+                                  uint32_t id);
+
+  // Checks whether various conditions hold related to the acceptability of
+  // replacing the id use at |use_in_operand_index| of |use_instruction| with
+  // a synonym.  In particular, this checks that:
+  // - the id use is not an index into a struct field in an OpAccessChain - such
+  //   indices must be constants, so it is dangerous to replace them.
+  // - the id use is not a pointer function call argument, on which there are
+  //   restrictions that make replacement problematic.
+  static bool UseCanBeReplacedWithSynonym(opt::IRContext* context,
+                                          opt::Instruction* use_instruction,
+                                          uint32_t use_in_operand_index);
+
+ private:
+  protobufs::TransformationReplaceIdWithSynonym message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_REPLACE_ID_WITH_SYNONYM_H_
diff --git a/source/fuzz/transformation_set_function_control.cpp b/source/fuzz/transformation_set_function_control.cpp
new file mode 100644
index 0000000..d2b61f1
--- /dev/null
+++ b/source/fuzz/transformation_set_function_control.cpp
@@ -0,0 +1,100 @@
+// 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.
+
+#include "source/fuzz/transformation_set_function_control.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationSetFunctionControl::TransformationSetFunctionControl(
+    const spvtools::fuzz::protobufs::TransformationSetFunctionControl& message)
+    : message_(message) {}
+
+TransformationSetFunctionControl::TransformationSetFunctionControl(
+    uint32_t function_id, uint32_t function_control) {
+  message_.set_function_id(function_id);
+  message_.set_function_control(function_control);
+}
+
+bool TransformationSetFunctionControl::IsApplicable(
+    opt::IRContext* context, const FactManager& /*unused*/) const {
+  opt::Instruction* function_def_instruction =
+      FindFunctionDefInstruction(context);
+  if (!function_def_instruction) {
+    // The given function id does not correspond to any function.
+    return false;
+  }
+  uint32_t existing_function_control_mask =
+      function_def_instruction->GetSingleWordInOperand(0);
+
+  // 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;
+  // The following is to keep release-mode compilers happy as this variable is
+  // only used in an assertion.
+  (void)(acceptable_function_control_bits);
+  assert(!(message_.function_control() & ~acceptable_function_control_bits) &&
+         "Nonsensical loop control bits were found.");
+
+  // 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)) &&
+         "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)) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+void TransformationSetFunctionControl::Apply(opt::IRContext* context,
+                                             FactManager* /*unused*/) const {
+  opt::Instruction* function_def_instruction =
+      FindFunctionDefInstruction(context);
+  function_def_instruction->SetInOperand(0, {message_.function_control()});
+}
+
+protobufs::Transformation TransformationSetFunctionControl::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_set_function_control() = message_;
+  return result;
+}
+
+opt::Instruction* TransformationSetFunctionControl ::FindFunctionDefInstruction(
+    opt::IRContext* context) const {
+  // Look through all functions for a function whose defining instruction's
+  // result id matches |message_.function_id|, returning the defining
+  // instruction if found.
+  for (auto& function : *context->module()) {
+    if (function.DefInst().result_id() == message_.function_id()) {
+      return &function.DefInst();
+    }
+  }
+  // A nullptr result indicates that no match was found.
+  return nullptr;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/source/fuzz/transformation_set_function_control.h b/source/fuzz/transformation_set_function_control.h
new file mode 100644
index 0000000..0526bb9
--- /dev/null
+++ b/source/fuzz/transformation_set_function_control.h
@@ -0,0 +1,58 @@
+// 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.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_SET_FUNCTION_CONTROL_H_
+#define SOURCE_FUZZ_TRANSFORMATION_SET_FUNCTION_CONTROL_H_
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationSetFunctionControl : public Transformation {
+ public:
+  explicit TransformationSetFunctionControl(
+      const protobufs::TransformationSetFunctionControl& message);
+
+  TransformationSetFunctionControl(uint32_t function_id,
+                                   uint32_t function_control);
+
+  // - |message_.function_id| must be the result id of an OpFunction
+  //   instruction.
+  // - |message_.function_control| must be a function control mask that sets
+  //   at most one of 'Inline' or 'DontInline', and that may not contain 'Pure'
+  //   (respectively 'Const') unless the existing function control mask contains
+  //   'Pure' (respectively 'Const').
+  bool IsApplicable(opt::IRContext* context,
+                    const FactManager& fact_manager) const override;
+
+  // The function control operand of instruction |message_.function_id| is
+  // over-written with |message_.function_control|.
+  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  opt::Instruction* FindFunctionDefInstruction(opt::IRContext* context) const;
+
+  protobufs::TransformationSetFunctionControl message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_SET_FUNCTION_CONTROL_H_
diff --git a/source/fuzz/transformation_set_loop_control.cpp b/source/fuzz/transformation_set_loop_control.cpp
new file mode 100644
index 0000000..9062f17
--- /dev/null
+++ b/source/fuzz/transformation_set_loop_control.cpp
@@ -0,0 +1,216 @@
+// 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.
+
+#include "source/fuzz/transformation_set_loop_control.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationSetLoopControl::TransformationSetLoopControl(
+    const spvtools::fuzz::protobufs::TransformationSetLoopControl& message)
+    : message_(message) {}
+
+TransformationSetLoopControl::TransformationSetLoopControl(
+    uint32_t block_id, uint32_t loop_control, uint32_t peel_count,
+    uint32_t partial_count) {
+  message_.set_block_id(block_id);
+  message_.set_loop_control(loop_control);
+  message_.set_peel_count(peel_count);
+  message_.set_partial_count(partial_count);
+}
+
+bool TransformationSetLoopControl::IsApplicable(
+    opt::IRContext* context, const FactManager& /*unused*/) const {
+  // |message_.block_id| must identify a block that ends with OpLoopMerge.
+  auto block = context->get_instr_block(message_.block_id());
+  if (!block) {
+    return false;
+  }
+  auto merge_inst = block->GetMergeInst();
+  if (!merge_inst || merge_inst->opcode() != SpvOpLoopMerge) {
+    return false;
+  }
+
+  // We sanity-check 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;
+
+  // The variable is only used in an assertion; the following keeps release-mode
+  // compilers happy.
+  (void)(all_loop_control_mask_bits_set);
+
+  // No additional bits should be set.
+  assert(!(message_.loop_control() & ~all_loop_control_mask_bits_set));
+
+  // Grab the loop control mask currently associated with the OpLoopMerge
+  // instruction.
+  auto existing_loop_control_mask =
+      merge_inst->GetSingleWordInOperand(kLoopControlMaskInOperandIndex);
+
+  // 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}) {
+    // 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,
+                                              existing_loop_control_mask)) {
+      return false;
+    }
+  }
+
+  if ((message_.loop_control() &
+       (SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask)) &&
+      !(PeelCountIsSupported(context) && PartialCountIsSupported(context))) {
+    // At least one of PeelCount or PartialCount is used, but the SPIR-V version
+    // in question does not support these loop controls.
+    return false;
+  }
+
+  if (message_.peel_count() > 0 &&
+      !(message_.loop_control() & SpvLoopControlPeelCountMask)) {
+    // Peel count provided, but peel count mask bit not set.
+    return false;
+  }
+
+  if (message_.partial_count() > 0 &&
+      !(message_.loop_control() & SpvLoopControlPartialCountMask)) {
+    // 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)));
+}
+
+void TransformationSetLoopControl::Apply(opt::IRContext* context,
+                                         FactManager* /*unused*/) const {
+  // Grab the loop merge instruction and its associated loop control mask.
+  auto merge_inst =
+      context->get_instr_block(message_.block_id())->GetMergeInst();
+  auto existing_loop_control_mask =
+      merge_inst->GetSingleWordInOperand(kLoopControlMaskInOperandIndex);
+
+  // We are going to replace the OpLoopMerge's operands with this list.
+  opt::Instruction::OperandList new_operands;
+  // We add the existing merge block and continue target ids.
+  new_operands.push_back(merge_inst->GetInOperand(0));
+  new_operands.push_back(merge_inst->GetInOperand(1));
+  // We use the loop control mask from the transformation.
+  new_operands.push_back(
+      {SPV_OPERAND_TYPE_LOOP_CONTROL, {message_.loop_control()}});
+
+  // It remains to determine what literals to provide, in association with
+  // the new loop control mask.
+  //
+  // For the loop controls that require guarantees to hold about the number
+  // of loop iterations, we need to keep, from the original OpLoopMerge, any
+  // literals associated with loop control bits that are still set.
+
+  uint32_t literal_index = 0;  // Indexes into the literals from the original
+  // instruction.
+  for (SpvLoopControlMask mask :
+       {SpvLoopControlDependencyLengthMask, SpvLoopControlMinIterationsMask,
+        SpvLoopControlMaxIterationsMask, SpvLoopControlIterationMultipleMask}) {
+    // Check whether the bit was set in the original loop control mask.
+    if (existing_loop_control_mask & mask) {
+      // Check whether the bit is set in the new loop control mask.
+      if (message_.loop_control() & mask) {
+        // Add the associated literal to our sequence of replacement operands.
+        new_operands.push_back(
+            {SPV_OPERAND_TYPE_LITERAL_INTEGER,
+             {merge_inst->GetSingleWordInOperand(
+                 kLoopControlFirstLiteralInOperandIndex + literal_index)}});
+      }
+      // Increment our index into the original loop control mask's literals,
+      // whether or not the bit was set in the new mask.
+      literal_index++;
+    }
+  }
+
+  // If PeelCount is set in the new mask, |message_.peel_count| provides the
+  // associated peel count.
+  if (message_.loop_control() & SpvLoopControlPeelCountMask) {
+    new_operands.push_back(
+        {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.peel_count()}});
+  }
+
+  // Similar, but for PartialCount.
+  if (message_.loop_control() & SpvLoopControlPartialCountMask) {
+    new_operands.push_back(
+        {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.partial_count()}});
+  }
+
+  // Replace the input operands of the OpLoopMerge with the new operands we have
+  // accumulated.
+  merge_inst->SetInOperands(std::move(new_operands));
+}
+
+protobufs::Transformation TransformationSetLoopControl::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_set_loop_control() = message_;
+  return result;
+}
+
+bool TransformationSetLoopControl::LoopControlBitIsAddedByTransformation(
+    SpvLoopControlMask 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());
+}
+
+bool TransformationSetLoopControl::PartialCountIsSupported(
+    opt::IRContext* context) {
+  // TODO(afd): We capture the universal environments for which this loop
+  //  control is definitely not supported.  The check should be refined on
+  //  demand for other target environments.
+  switch (context->grammar().target_env()) {
+    case SPV_ENV_UNIVERSAL_1_0:
+    case SPV_ENV_UNIVERSAL_1_1:
+    case SPV_ENV_UNIVERSAL_1_2:
+    case SPV_ENV_UNIVERSAL_1_3:
+      return false;
+    default:
+      return true;
+  }
+}
+
+bool TransformationSetLoopControl::PeelCountIsSupported(
+    opt::IRContext* context) {
+  // TODO(afd): We capture the universal environments for which this loop
+  //  control is definitely not supported.  The check should be refined on
+  //  demand for other target environments.
+  switch (context->grammar().target_env()) {
+    case SPV_ENV_UNIVERSAL_1_0:
+    case SPV_ENV_UNIVERSAL_1_1:
+    case SPV_ENV_UNIVERSAL_1_2:
+    case SPV_ENV_UNIVERSAL_1_3:
+      return false;
+    default:
+      return true;
+  }
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/source/fuzz/transformation_set_loop_control.h b/source/fuzz/transformation_set_loop_control.h
new file mode 100644
index 0000000..28b148c
--- /dev/null
+++ b/source/fuzz/transformation_set_loop_control.h
@@ -0,0 +1,79 @@
+// 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.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_SET_LOOP_CONTROL_H_
+#define SOURCE_FUZZ_TRANSFORMATION_SET_LOOP_CONTROL_H_
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationSetLoopControl : public Transformation {
+ public:
+  const static uint32_t kLoopControlMaskInOperandIndex = 2;
+  const static uint32_t kLoopControlFirstLiteralInOperandIndex = 3;
+
+  explicit TransformationSetLoopControl(
+      const protobufs::TransformationSetLoopControl& message);
+
+  TransformationSetLoopControl(uint32_t block_id, uint32_t loop_control,
+                               uint32_t peel_count, uint32_t partial_count);
+
+  // - |message_.block_id| must be a block containing an OpLoopMerge
+  //   instruction.
+  // - |message_.loop_control| must be a legal loop control mask that
+  //   only uses controls available in the SPIR-V version associated with
+  //   |context|, and must not add loop controls that are only valid in the
+  //   presence of guarantees about what the loop does (e.g. MinIterations).
+  // - |message_.peel_count| (respectively |message_.partial_count|) must be
+  //   zero PeelCount (respectively PartialCount) is set in
+  //   |message_.loop_control|.
+  bool IsApplicable(opt::IRContext* context,
+                    const FactManager& fact_manager) const override;
+
+  // - The loop control operand of the OpLoopMergeInstruction in
+  //   |message_.block_id| is overwritten with |message_.loop_control|.
+  // - The literals associated with the loop control are updated to reflect any
+  //   controls with associated literals that have been removed (e.g.
+  //   MinIterations), and any that have been added (PeelCount and/or
+  //   PartialCount).
+  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+  // Does the version of SPIR-V being used support the PartialCount loop
+  // control?
+  static bool PartialCountIsSupported(opt::IRContext* context);
+
+  // Does the version of SPIR-V being used support the PeelCount loop control?
+  static bool PeelCountIsSupported(opt::IRContext* context);
+
+ private:
+  // 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,
+      uint32_t existing_loop_control_mask) const;
+
+  protobufs::TransformationSetLoopControl message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_SET_LOOP_CONTROL_H_
diff --git a/source/fuzz/transformation_set_memory_operands_mask.cpp b/source/fuzz/transformation_set_memory_operands_mask.cpp
new file mode 100644
index 0000000..a14e1a6
--- /dev/null
+++ b/source/fuzz/transformation_set_memory_operands_mask.cpp
@@ -0,0 +1,201 @@
+// 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.
+
+#include "source/fuzz/transformation_set_memory_operands_mask.h"
+
+#include "source/fuzz/instruction_descriptor.h"
+
+namespace spvtools {
+namespace fuzz {
+
+namespace {
+
+const uint32_t kOpLoadMemoryOperandsMaskIndex = 1;
+const uint32_t kOpStoreMemoryOperandsMaskIndex = 2;
+const uint32_t kOpCopyMemoryFirstMemoryOperandsMaskIndex = 2;
+const uint32_t kOpCopyMemorySizedFirstMemoryOperandsMaskIndex = 3;
+
+}  // namespace
+
+TransformationSetMemoryOperandsMask::TransformationSetMemoryOperandsMask(
+    const spvtools::fuzz::protobufs::TransformationSetMemoryOperandsMask&
+        message)
+    : message_(message) {}
+
+TransformationSetMemoryOperandsMask::TransformationSetMemoryOperandsMask(
+    const protobufs::InstructionDescriptor& memory_access_instruction,
+    uint32_t memory_operands_mask, uint32_t memory_operands_mask_index) {
+  *message_.mutable_memory_access_instruction() = memory_access_instruction;
+  message_.set_memory_operands_mask(memory_operands_mask);
+  message_.set_memory_operands_mask_index(memory_operands_mask_index);
+}
+
+bool TransformationSetMemoryOperandsMask::IsApplicable(
+    opt::IRContext* context,
+    const spvtools::fuzz::FactManager& /*unused*/) const {
+  if (message_.memory_operands_mask_index() != 0) {
+    // The following conditions should never be violated, even if
+    // transformations end up being replayed in a different way to the manner in
+    // 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(MultipleMemoryOperandMasksAreSupported(context));
+  }
+
+  auto instruction =
+      FindInstruction(message_.memory_access_instruction(), context);
+  if (!instruction) {
+    return false;
+  }
+  if (!IsMemoryAccess(*instruction)) {
+    return false;
+  }
+
+  auto original_mask_in_operand_index = GetInOperandIndexForMask(
+      *instruction, message_.memory_operands_mask_index());
+  assert(original_mask_in_operand_index != 0 &&
+         "The given mask index is not valid.");
+  uint32_t original_mask =
+      original_mask_in_operand_index < instruction->NumInOperands()
+          ? instruction->GetSingleWordInOperand(original_mask_in_operand_index)
+          : static_cast<uint32_t>(SpvMemoryAccessMaskNone);
+  uint32_t new_mask = message_.memory_operands_mask();
+
+  // Volatile must not be removed
+  if ((original_mask & SpvMemoryAccessVolatileMask) &&
+      !(new_mask & SpvMemoryAccessVolatileMask)) {
+    return false;
+  }
+
+  // Nontemporal can be added or removed, and no other flag is allowed to
+  // change.  We do this by checking that the masks are equal once we set
+  // 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);
+}
+
+void TransformationSetMemoryOperandsMask::Apply(
+    opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
+  auto instruction =
+      FindInstruction(message_.memory_access_instruction(), context);
+  auto original_mask_in_operand_index = GetInOperandIndexForMask(
+      *instruction, message_.memory_operands_mask_index());
+  // Either add a new operand, if no mask operand was already present, or
+  // replace an existing mask operand.
+  if (original_mask_in_operand_index >= instruction->NumInOperands()) {
+    instruction->AddOperand(
+        {SPV_OPERAND_TYPE_MEMORY_ACCESS, {message_.memory_operands_mask()}});
+
+  } else {
+    instruction->SetInOperand(original_mask_in_operand_index,
+                              {message_.memory_operands_mask()});
+  }
+}
+
+protobufs::Transformation TransformationSetMemoryOperandsMask::ToMessage()
+    const {
+  protobufs::Transformation result;
+  *result.mutable_set_memory_operands_mask() = message_;
+  return result;
+}
+
+bool TransformationSetMemoryOperandsMask::IsMemoryAccess(
+    const opt::Instruction& instruction) {
+  switch (instruction.opcode()) {
+    case SpvOpLoad:
+    case SpvOpStore:
+    case SpvOpCopyMemory:
+    case SpvOpCopyMemorySized:
+      return true;
+    default:
+      return false;
+  }
+}
+
+uint32_t TransformationSetMemoryOperandsMask::GetInOperandIndexForMask(
+    const opt::Instruction& instruction, uint32_t mask_index) {
+  // Get the input operand index associated with the first memory operands mask
+  // for the instruction.
+  uint32_t first_mask_in_operand_index = 0;
+  switch (instruction.opcode()) {
+    case SpvOpLoad:
+      first_mask_in_operand_index = kOpLoadMemoryOperandsMaskIndex;
+      break;
+    case SpvOpStore:
+      first_mask_in_operand_index = kOpStoreMemoryOperandsMaskIndex;
+      break;
+    case SpvOpCopyMemory:
+      first_mask_in_operand_index = kOpCopyMemoryFirstMemoryOperandsMaskIndex;
+      break;
+    case SpvOpCopyMemorySized:
+      first_mask_in_operand_index =
+          kOpCopyMemorySizedFirstMemoryOperandsMaskIndex;
+      break;
+    default:
+      assert(false && "Unknown memory instruction.");
+      break;
+  }
+  // If we are looking for the input operand index of the first mask, return it.
+  if (mask_index == 0) {
+    return first_mask_in_operand_index;
+  }
+  assert(mask_index == 1 && "Memory operands mask index must be 0 or 1.");
+
+  // We are looking for the input operand index of the second mask.  This is a
+  // little complicated because, depending on the contents of the first mask,
+  // there may be some input operands separating the two masks.
+  uint32_t first_mask =
+      instruction.GetSingleWordInOperand(first_mask_in_operand_index);
+
+  // 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) {
+      first_mask_extra_operand_count++;
+    }
+  }
+  return first_mask_in_operand_index + first_mask_extra_operand_count + 1;
+}
+
+bool TransformationSetMemoryOperandsMask::
+    MultipleMemoryOperandMasksAreSupported(opt::IRContext* context) {
+  // TODO(afd): We capture the universal environments for which this loop
+  //  control is definitely not supported.  The check should be refined on
+  //  demand for other target environments.
+  switch (context->grammar().target_env()) {
+    case SPV_ENV_UNIVERSAL_1_0:
+    case SPV_ENV_UNIVERSAL_1_1:
+    case SPV_ENV_UNIVERSAL_1_2:
+    case SPV_ENV_UNIVERSAL_1_3:
+      return false;
+    default:
+      return true;
+  }
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/source/fuzz/transformation_set_memory_operands_mask.h b/source/fuzz/transformation_set_memory_operands_mask.h
new file mode 100644
index 0000000..20ae145
--- /dev/null
+++ b/source/fuzz/transformation_set_memory_operands_mask.h
@@ -0,0 +1,76 @@
+// 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.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_SET_MEMORY_OPERANDS_MASK_H_
+#define SOURCE_FUZZ_TRANSFORMATION_SET_MEMORY_OPERANDS_MASK_H_
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationSetMemoryOperandsMask : public Transformation {
+ public:
+  explicit TransformationSetMemoryOperandsMask(
+      const protobufs::TransformationSetMemoryOperandsMask& message);
+
+  TransformationSetMemoryOperandsMask(
+      const protobufs::InstructionDescriptor& memory_access_instruction,
+      uint32_t memory_operands_mask, uint32_t memory_operands_mask_index);
+
+  // - |message_.memory_access_instruction| must describe a memory access
+  //   instruction.
+  // - |message_.memory_operands_mask_index| must be suitable for this memory
+  //   access instruction, e.g. it must be 0 in the case of OpLoad, and may be
+  //   1 in the case of OpCopyMemory if the SPIR-V version is 1.4 or higher.
+  // - |message_.memory_operands_mask| must be identical to the original memory
+  //   operands mask, except that Volatile may be added, and Nontemporal may be
+  //   toggled.
+  bool IsApplicable(opt::IRContext* context,
+                    const FactManager& fact_manager) const override;
+
+  // Replaces the operands mask identified by
+  // |message_.memory_operands_mask_index| in the instruction described by
+  // |message_.memory_access_instruction| with |message_.memory_operands_mask|,
+  // creating an input operand for the mask if no such operand was present.
+  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+  // Helper function that determines whether |instruction| is a memory
+  // instruction (e.g. OpLoad).
+  static bool IsMemoryAccess(const opt::Instruction& instruction);
+
+  // Does the version of SPIR-V being used support multiple memory operand
+  // masks on relevant memory access instructions?
+  static bool MultipleMemoryOperandMasksAreSupported(opt::IRContext* context);
+
+  // Helper function to get the input operand index associated with mask number
+  // |mask_index|. This is a bit tricky if there are multiple masks, because the
+  // index associated with the second mask depends on whether the first mask
+  // includes any flags such as Aligned that have corresponding operands.
+  static uint32_t GetInOperandIndexForMask(const opt::Instruction& instruction,
+                                           uint32_t mask_index);
+
+ private:
+  protobufs::TransformationSetMemoryOperandsMask message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_SET_MEMORY_OPERANDS_MASK_H_
diff --git a/source/fuzz/transformation_set_selection_control.cpp b/source/fuzz/transformation_set_selection_control.cpp
new file mode 100644
index 0000000..ebabdef
--- /dev/null
+++ b/source/fuzz/transformation_set_selection_control.cpp
@@ -0,0 +1,60 @@
+// 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.
+
+#include "source/fuzz/transformation_set_selection_control.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationSetSelectionControl::TransformationSetSelectionControl(
+    const spvtools::fuzz::protobufs::TransformationSetSelectionControl& message)
+    : message_(message) {}
+
+TransformationSetSelectionControl::TransformationSetSelectionControl(
+    uint32_t block_id, uint32_t selection_control) {
+  message_.set_block_id(block_id);
+  message_.set_selection_control(selection_control);
+}
+
+bool TransformationSetSelectionControl::IsApplicable(
+    opt::IRContext* context, const FactManager& /*unused*/) const {
+  assert((message_.selection_control() == SpvSelectionControlMaskNone ||
+          message_.selection_control() == SpvSelectionControlFlattenMask ||
+          message_.selection_control() == SpvSelectionControlDontFlattenMask) &&
+         "Selection control should never be set to something other than "
+         "'None', 'Flatten' or 'DontFlatten'");
+  if (auto block = context->get_instr_block(message_.block_id())) {
+    if (auto merge_inst = block->GetMergeInst()) {
+      return merge_inst->opcode() == SpvOpSelectionMerge;
+    }
+  }
+  // Either the block did not exit, or did not end with OpSelectionMerge.
+  return false;
+}
+
+void TransformationSetSelectionControl::Apply(opt::IRContext* context,
+                                              FactManager* /*unused*/) const {
+  context->get_instr_block(message_.block_id())
+      ->GetMergeInst()
+      ->SetInOperand(1, {message_.selection_control()});
+}
+
+protobufs::Transformation TransformationSetSelectionControl::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_set_selection_control() = message_;
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/source/fuzz/transformation_set_selection_control.h b/source/fuzz/transformation_set_selection_control.h
new file mode 100644
index 0000000..19e0c3c
--- /dev/null
+++ b/source/fuzz/transformation_set_selection_control.h
@@ -0,0 +1,54 @@
+// 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.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_SET_SELECTION_CONTROL_H_
+#define SOURCE_FUZZ_TRANSFORMATION_SET_SELECTION_CONTROL_H_
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationSetSelectionControl : public Transformation {
+ public:
+  explicit TransformationSetSelectionControl(
+      const protobufs::TransformationSetSelectionControl& message);
+
+  TransformationSetSelectionControl(uint32_t block_id,
+                                    uint32_t selection_control);
+
+  // - |message_.block_id| must be a block containing an OpSelectionMerge
+  //   instruction.
+  // - |message_.selection_control| must be one of None, Flatten or
+  //   DontFlatten.
+  bool IsApplicable(opt::IRContext* context,
+                    const FactManager& fact_manager) const override;
+
+  // - The selection control operand of the OpSelectionMergeInstruction in
+  //   |message_.block_id| is overwritten with |message_.selection_control|.
+  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  protobufs::TransformationSetSelectionControl message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_SET_SELECTION_CONTROL_H_
diff --git a/source/fuzz/transformation_split_block.cpp b/source/fuzz/transformation_split_block.cpp
index a2da371..9f6da7c 100644
--- a/source/fuzz/transformation_split_block.cpp
+++ b/source/fuzz/transformation_split_block.cpp
@@ -17,6 +17,7 @@
 #include <utility>
 
 #include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
 #include "source/util/make_unique.h"
 
 namespace spvtools {
@@ -26,11 +27,10 @@
     const spvtools::fuzz::protobufs::TransformationSplitBlock& message)
     : message_(message) {}
 
-TransformationSplitBlock::TransformationSplitBlock(uint32_t base_instruction_id,
-                                                   uint32_t offset,
-                                                   uint32_t fresh_id) {
-  message_.set_base_instruction_id(base_instruction_id);
-  message_.set_offset(offset);
+TransformationSplitBlock::TransformationSplitBlock(
+    const protobufs::InstructionDescriptor& instruction_to_split_before,
+    uint32_t fresh_id) {
+  *message_.mutable_instruction_to_split_before() = instruction_to_split_before;
   message_.set_fresh_id(fresh_id);
 }
 
@@ -40,31 +40,28 @@
     // We require the id for the new block to be unused.
     return false;
   }
-  auto base_instruction =
-      context->get_def_use_mgr()->GetDef(message_.base_instruction_id());
-  if (!base_instruction) {
+  auto instruction_to_split_before =
+      FindInstruction(message_.instruction_to_split_before(), context);
+  if (!instruction_to_split_before) {
     // The instruction describing the block we should split does not exist.
     return false;
   }
-  auto block_containing_base_instruction =
-      context->get_instr_block(base_instruction);
-  if (!block_containing_base_instruction) {
-    // The instruction describing the block we should split is not contained in
-    // a block.
-    return false;
-  }
+  auto block_to_split = context->get_instr_block(instruction_to_split_before);
+  assert(block_to_split &&
+         "We should not have managed to find the "
+         "instruction if it was not contained in a block.");
 
-  if (block_containing_base_instruction->IsLoopHeader()) {
+  if (block_to_split->IsLoopHeader()) {
     // We cannot split a loop header block: back-edges would become invalid.
     return false;
   }
 
-  auto split_before = fuzzerutil::GetIteratorForBaseInstructionAndOffset(
-      block_containing_base_instruction, base_instruction, message_.offset());
-  if (split_before == block_containing_base_instruction->end()) {
-    // The offset was inappropriate.
-    return false;
-  }
+  auto split_before = fuzzerutil::GetIteratorForInstruction(
+      block_to_split, instruction_to_split_before);
+  assert(split_before != block_to_split->end() &&
+         "At this point we know the"
+         " block split point exists.");
+
   if (split_before->PreviousNode() &&
       split_before->PreviousNode()->opcode() == SpvOpSelectionMerge) {
     // We cannot split directly after a selection merge: this would separate
@@ -84,44 +81,39 @@
 
 void TransformationSplitBlock::Apply(opt::IRContext* context,
                                      FactManager* /*unused*/) const {
-  auto base_instruction =
-      context->get_def_use_mgr()->GetDef(message_.base_instruction_id());
-  assert(base_instruction && "Base instruction must exist");
-  auto block_containing_base_instruction =
-      context->get_instr_block(base_instruction);
-  assert(block_containing_base_instruction &&
-         "Base instruction must be in a block");
-  auto split_before = fuzzerutil::GetIteratorForBaseInstructionAndOffset(
-      block_containing_base_instruction, base_instruction, message_.offset());
-  assert(split_before != block_containing_base_instruction->end() &&
+  opt::Instruction* instruction_to_split_before =
+      FindInstruction(message_.instruction_to_split_before(), context);
+  opt::BasicBlock* block_to_split =
+      context->get_instr_block(instruction_to_split_before);
+  auto split_before = fuzzerutil::GetIteratorForInstruction(
+      block_to_split, instruction_to_split_before);
+  assert(split_before != block_to_split->end() &&
          "If the transformation is applicable, we should have an "
          "instruction to split on.");
+
   // We need to make sure the module's id bound is large enough to add the
   // fresh id.
   fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
   // Split the block.
-  auto new_bb = block_containing_base_instruction->SplitBasicBlock(
-      context, message_.fresh_id(), split_before);
+  auto new_bb = block_to_split->SplitBasicBlock(context, message_.fresh_id(),
+                                                split_before);
   // The split does not automatically add a branch between the two parts of
   // the original block, so we add one.
-  block_containing_base_instruction->AddInstruction(
-      MakeUnique<opt::Instruction>(
-          context, SpvOpBranch, 0, 0,
-          std::initializer_list<opt::Operand>{
-              opt::Operand(spv_operand_type_t::SPV_OPERAND_TYPE_ID,
-                           {message_.fresh_id()})}));
+  block_to_split->AddInstruction(MakeUnique<opt::Instruction>(
+      context, SpvOpBranch, 0, 0,
+      std::initializer_list<opt::Operand>{opt::Operand(
+          spv_operand_type_t::SPV_OPERAND_TYPE_ID, {message_.fresh_id()})}));
   // If we split before OpPhi instructions, we need to update their
   // predecessor operand so that the block they used to be inside is now the
   // predecessor.
-  new_bb->ForEachPhiInst(
-      [block_containing_base_instruction](opt::Instruction* phi_inst) {
-        // The following assertion is a sanity check.  It is guaranteed to hold
-        // if IsApplicable holds.
-        assert(phi_inst->NumInOperands() == 2 &&
-               "We can only split a block before an OpPhi if block has exactly "
-               "one predecessor.");
-        phi_inst->SetInOperand(1, {block_containing_base_instruction->id()});
-      });
+  new_bb->ForEachPhiInst([block_to_split](opt::Instruction* phi_inst) {
+    // The following assertion is a sanity check.  It is guaranteed to hold
+    // if IsApplicable holds.
+    assert(phi_inst->NumInOperands() == 2 &&
+           "We can only split a block before an OpPhi if block has exactly "
+           "one predecessor.");
+    phi_inst->SetInOperand(1, {block_to_split->id()});
+  });
   // Invalidate all analyses
   context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
 }
diff --git a/source/fuzz/transformation_split_block.h b/source/fuzz/transformation_split_block.h
index 4a7095a..63dc7f5 100644
--- a/source/fuzz/transformation_split_block.h
+++ b/source/fuzz/transformation_split_block.h
@@ -28,8 +28,9 @@
   explicit TransformationSplitBlock(
       const protobufs::TransformationSplitBlock& message);
 
-  TransformationSplitBlock(uint32_t base_instruction_id, uint32_t offset,
-                           uint32_t fresh_id);
+  TransformationSplitBlock(
+      const protobufs::InstructionDescriptor& instruction_to_split_before,
+      uint32_t fresh_id);
 
   // - |message_.base_instruction_id| must be the result id of an instruction
   //   'base' in some block 'blk'.
diff --git a/source/fuzz/transformation_vector_shuffle.cpp b/source/fuzz/transformation_vector_shuffle.cpp
new file mode 100644
index 0000000..e2d889d
--- /dev/null
+++ b/source/fuzz/transformation_vector_shuffle.cpp
@@ -0,0 +1,203 @@
+// 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.
+
+#include "source/fuzz/transformation_vector_shuffle.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationVectorShuffle::TransformationVectorShuffle(
+    const spvtools::fuzz::protobufs::TransformationVectorShuffle& message)
+    : message_(message) {}
+
+TransformationVectorShuffle::TransformationVectorShuffle(
+    const protobufs::InstructionDescriptor& instruction_to_insert_before,
+    uint32_t fresh_id, uint32_t vector1, uint32_t vector2,
+    const std::vector<uint32_t>& component) {
+  *message_.mutable_instruction_to_insert_before() =
+      instruction_to_insert_before;
+  message_.set_fresh_id(fresh_id);
+  message_.set_vector1(vector1);
+  message_.set_vector2(vector2);
+  for (auto a_component : component) {
+    message_.add_component(a_component);
+  }
+}
+
+bool TransformationVectorShuffle::IsApplicable(
+    opt::IRContext* context,
+    const spvtools::fuzz::FactManager& /*unused*/) const {
+  // The fresh id must not already be in use.
+  if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+    return false;
+  }
+  // The instruction before which the shuffle will be inserted must exist.
+  auto instruction_to_insert_before =
+      FindInstruction(message_.instruction_to_insert_before(), context);
+  if (!instruction_to_insert_before) {
+    return false;
+  }
+  // The first vector must be an instruction with a type id
+  auto vector1_instruction =
+      context->get_def_use_mgr()->GetDef(message_.vector1());
+  if (!vector1_instruction || !vector1_instruction->type_id()) {
+    return false;
+  }
+  // The second vector must be an instruction with a type id
+  auto vector2_instruction =
+      context->get_def_use_mgr()->GetDef(message_.vector2());
+  if (!vector2_instruction || !vector2_instruction->type_id()) {
+    return false;
+  }
+  auto vector1_type =
+      context->get_type_mgr()->GetType(vector1_instruction->type_id());
+  // The first vector instruction's type must actually be a vector type.
+  if (!vector1_type->AsVector()) {
+    return false;
+  }
+  auto vector2_type =
+      context->get_type_mgr()->GetType(vector2_instruction->type_id());
+  // The second vector instruction's type must actually be a vector type.
+  if (!vector2_type->AsVector()) {
+    return false;
+  }
+  // The element types of the vectors must be the same.
+  if (vector1_type->AsVector()->element_type() !=
+      vector2_type->AsVector()->element_type()) {
+    return false;
+  }
+  uint32_t combined_size = vector1_type->AsVector()->element_count() +
+                           vector2_type->AsVector()->element_count();
+  for (auto a_compoment : message_.component()) {
+    // 0xFFFFFFFF is used to represent an undefined component.  Unless
+    // undefined, a component must be less than the combined size of the
+    // vectors.
+    if (a_compoment != 0xFFFFFFFF && a_compoment >= combined_size) {
+      return false;
+    }
+  }
+  // The module must already declare an appropriate type in which to store the
+  // result of the shuffle.
+  if (!GetResultTypeId(context, *vector1_type->AsVector()->element_type())) {
+    return false;
+  }
+  // Each of the vectors used in the shuffle must be available at the insertion
+  // point.
+  for (auto used_instruction : {vector1_instruction, vector2_instruction}) {
+    if (auto block = context->get_instr_block(used_instruction)) {
+      if (!context->GetDominatorAnalysis(block->GetParent())
+               ->Dominates(used_instruction, instruction_to_insert_before)) {
+        return false;
+      }
+    }
+  }
+
+  // It must be legitimate to insert an OpVectorShuffle before the identified
+  // instruction.
+  return fuzzerutil::CanInsertOpcodeBeforeInstruction(
+      SpvOpVectorShuffle, instruction_to_insert_before);
+}
+
+void TransformationVectorShuffle::Apply(
+    opt::IRContext* context, spvtools::fuzz::FactManager* fact_manager) const {
+  // Make input operands for a shuffle instruction - these comprise the two
+  // vectors being shuffled, followed by the integer literal components.
+  opt::Instruction::OperandList shuffle_operands = {
+      {SPV_OPERAND_TYPE_ID, {message_.vector1()}},
+      {SPV_OPERAND_TYPE_ID, {message_.vector2()}}};
+  for (auto a_component : message_.component()) {
+    shuffle_operands.push_back(
+        {SPV_OPERAND_TYPE_LITERAL_INTEGER, {a_component}});
+  }
+
+  uint32_t result_type_id = GetResultTypeId(
+      context, *GetVectorType(context, message_.vector1())->element_type());
+
+  // Add a shuffle instruction right before the instruction identified by
+  // |message_.instruction_to_insert_before|.
+  FindInstruction(message_.instruction_to_insert_before(), context)
+      ->InsertBefore(MakeUnique<opt::Instruction>(
+          context, SpvOpVectorShuffle, result_type_id, message_.fresh_id(),
+          shuffle_operands));
+  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+  context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+
+  // Add synonym facts relating the defined elements of the shuffle result to
+  // the vector components that they come from.
+  for (uint32_t component_index = 0;
+       component_index < static_cast<uint32_t>(message_.component_size());
+       component_index++) {
+    uint32_t component = message_.component(component_index);
+    if (component == 0xFFFFFFFF) {
+      // This component is undefined, so move on - but first note that the
+      // overall shuffle result cannot be synonymous with any vector.
+      continue;
+    }
+
+    // This describes the element of the result vector associated with
+    // |component_index|.
+    protobufs::DataDescriptor descriptor_for_result_component =
+        MakeDataDescriptor(message_.fresh_id(), {component_index});
+
+    protobufs::DataDescriptor descriptor_for_source_component;
+
+    // Get a data descriptor for the component of the input vector to which
+    // |component| refers.
+    if (component <
+        GetVectorType(context, message_.vector1())->element_count()) {
+      descriptor_for_source_component =
+          MakeDataDescriptor(message_.vector1(), {component});
+    } else {
+      auto index_into_vector_2 =
+          component -
+          GetVectorType(context, message_.vector1())->element_count();
+      assert(index_into_vector_2 <
+                 GetVectorType(context, message_.vector2())->element_count() &&
+             "Vector shuffle index is out of bounds.");
+      descriptor_for_source_component =
+          MakeDataDescriptor(message_.vector2(), {index_into_vector_2});
+    }
+
+    // Add a fact relating this input vector component with the associated
+    // result component.
+    fact_manager->AddFactDataSynonym(descriptor_for_result_component,
+                                     descriptor_for_source_component, context);
+  }
+}
+
+protobufs::Transformation TransformationVectorShuffle::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_vector_shuffle() = message_;
+  return result;
+}
+
+uint32_t TransformationVectorShuffle::GetResultTypeId(
+    opt::IRContext* context, const opt::analysis::Type& element_type) const {
+  opt::analysis::Vector result_type(
+      &element_type, static_cast<uint32_t>(message_.component_size()));
+  return context->get_type_mgr()->GetId(&result_type);
+}
+
+opt::analysis::Vector* TransformationVectorShuffle::GetVectorType(
+    opt::IRContext* context, uint32_t id_of_vector) {
+  return context->get_type_mgr()
+      ->GetType(context->get_def_use_mgr()->GetDef(id_of_vector)->type_id())
+      ->AsVector();
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/source/fuzz/transformation_vector_shuffle.h b/source/fuzz/transformation_vector_shuffle.h
new file mode 100644
index 0000000..81ed227
--- /dev/null
+++ b/source/fuzz/transformation_vector_shuffle.h
@@ -0,0 +1,82 @@
+// 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.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_VECTOR_SHUFFLE_H_
+#define SOURCE_FUZZ_TRANSFORMATION_VECTOR_SHUFFLE_H_
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+#include "source/opt/types.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationVectorShuffle : public Transformation {
+ public:
+  explicit TransformationVectorShuffle(
+      const protobufs::TransformationVectorShuffle& message);
+
+  TransformationVectorShuffle(
+      const protobufs::InstructionDescriptor& instruction_to_insert_before,
+      uint32_t fresh_id, uint32_t vector1, uint32_t vector2,
+      const std::vector<uint32_t>& component);
+
+  // - |message_.fresh_id| must not be in use
+  // - |message_.instruction_to_insert_before| must identify an instruction
+  //   before which it is legitimate to insert an OpVectorShuffle
+  // - |message_.vector1| and |message_.vector2| must be instructions of vector
+  //   type, and the element types of these vectors must be the same
+  // - Each element of |message_.component| must either be 0xFFFFFFFF
+  //   (representing an undefined component), or must be less than the combined
+  //   sizes of the input vectors
+  // - The module must already contain a vector type with the same element type
+  //   as |message_.vector1| and |message_.vector2|, and with the size of
+  //   |message_component| as its element count
+  bool IsApplicable(opt::IRContext* context,
+                    const FactManager& fact_manager) const override;
+
+  // Inserts an OpVectorShuffle instruction before
+  // |message_.instruction_to_insert_before|, shuffles vectors
+  // |message_.vector1| and |message_.vector2| using the indices provided by
+  // |message_.component|, into |message_.fresh_id|.  Adds a fact to the fact
+  // manager recording the fact each element of |message_.fresh_id| is
+  // synonymous with the element of |message_.vector1| or |message_.vector2|
+  // from which it came (with undefined components being ignored).  If the
+  // result vector is a contiguous sub-range of one of the input vectors, a
+  // fact is added to record that |message_.fresh_id| is synonymous with this
+  // sub-range.
+  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  // Returns a type id that already exists in |context| suitable for
+  // representing the result of the shuffle, where |element_type| is known to
+  // be the common element type of the vectors to which the shuffle is being
+  // applied.  Returns 0 if no such id exists.
+  uint32_t GetResultTypeId(opt::IRContext* context,
+                           const opt::analysis::Type& element_type) const;
+
+  static opt::analysis::Vector* GetVectorType(opt::IRContext* context,
+                                              uint32_t id_of_vector);
+
+  protobufs::TransformationVectorShuffle message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_VECTOR_SHUFFLE_H_
diff --git a/source/fuzz/uniform_buffer_element_descriptor.h b/source/fuzz/uniform_buffer_element_descriptor.h
index d35de57..f5d7320 100644
--- a/source/fuzz/uniform_buffer_element_descriptor.h
+++ b/source/fuzz/uniform_buffer_element_descriptor.h
@@ -49,4 +49,4 @@
 }  // namespace fuzz
 }  // namespace spvtools
 
-#endif  // #define SOURCE_FUZZ_UNIFORM_BUFFER_ELEMENT_DESCRIPTOR_H_
+#endif  // SOURCE_FUZZ_UNIFORM_BUFFER_ELEMENT_DESCRIPTOR_H_
diff --git a/source/link/CMakeLists.txt b/source/link/CMakeLists.txt
index 8ca4df3..d308319 100644
--- a/source/link/CMakeLists.txt
+++ b/source/link/CMakeLists.txt
@@ -17,8 +17,10 @@
 
 spvtools_default_compile_options(SPIRV-Tools-link)
 target_include_directories(SPIRV-Tools-link
-  PUBLIC ${spirv-tools_SOURCE_DIR}/include
-  PUBLIC ${SPIRV_HEADER_INCLUDE_DIR}
+  PUBLIC
+    $<BUILD_INTERFACE:${spirv-tools_SOURCE_DIR}/include>
+	$<BUILD_INTERFACE:${SPIRV_HEADER_INCLUDE_DIR}>
+	$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
   PRIVATE ${spirv-tools_BINARY_DIR}
 )
 # We need the IR functionnalities from the optimizer
@@ -29,8 +31,16 @@
 spvtools_check_symbol_exports(SPIRV-Tools-link)
 
 if(ENABLE_SPIRV_TOOLS_INSTALL)
-  install(TARGETS SPIRV-Tools-link
+  install(TARGETS SPIRV-Tools-link EXPORT SPIRV-Tools-linkTargets
     RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
     LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
     ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
+  export(EXPORT SPIRV-Tools-linkTargets FILE SPIRV-Tools-linkTargets.cmake)
+
+  spvtools_config_package_dir(SPIRV-Tools-link PACKAGE_DIR)
+  install(EXPORT SPIRV-Tools-linkTargets FILE SPIRV-Tools-linkTargets.cmake
+  	DESTINATION ${PACKAGE_DIR})
+
+  spvtools_generate_config_file(SPIRV-Tools-link)
+  install(FILES ${CMAKE_BINARY_DIR}/SPIRV-Tools-linkConfig.cmake DESTINATION ${PACKAGE_DIR})
 endif(ENABLE_SPIRV_TOOLS_INSTALL)
diff --git a/source/opt/CMakeLists.txt b/source/opt/CMakeLists.txt
index 2309ca9..0f719cb 100644
--- a/source/opt/CMakeLists.txt
+++ b/source/opt/CMakeLists.txt
@@ -27,6 +27,7 @@
   composite.h
   const_folding_rules.h
   constants.h
+  convert_to_half_pass.h
   copy_prop_arrays.h
   dead_branch_elim_pass.h
   dead_insert_elim_pass.h
@@ -68,7 +69,6 @@
   local_redundancy_elimination.h
   local_single_block_elim_pass.h
   local_single_store_elim_pass.h
-  local_ssa_elim_pass.h
   log.h
   loop_dependence.h
   loop_descriptor.h
@@ -93,6 +93,7 @@
   redundancy_elimination.h
   reflect.h
   register_pressure.h
+  relax_float_ops_pass.h
   remove_duplicates_pass.h
   replace_invalid_opc.h
   scalar_analysis.h
@@ -132,6 +133,7 @@
   composite.cpp
   const_folding_rules.cpp
   constants.cpp
+  convert_to_half_pass.cpp
   copy_prop_arrays.cpp
   dead_branch_elim_pass.cpp
   dead_insert_elim_pass.cpp
@@ -173,7 +175,6 @@
   local_redundancy_elimination.cpp
   local_single_block_elim_pass.cpp
   local_single_store_elim_pass.cpp
-  local_ssa_elim_pass.cpp
   loop_dependence.cpp
   loop_dependence_helpers.cpp
   loop_descriptor.cpp
@@ -196,6 +197,7 @@
   reduce_load_size.cpp
   redundancy_elimination.cpp
   register_pressure.cpp
+  relax_float_ops_pass.cpp
   remove_duplicates_pass.cpp
   replace_invalid_opc.cpp
   scalar_analysis.cpp
@@ -231,8 +233,10 @@
 
 spvtools_default_compile_options(SPIRV-Tools-opt)
 target_include_directories(SPIRV-Tools-opt
-  PUBLIC ${spirv-tools_SOURCE_DIR}/include
-  PUBLIC ${SPIRV_HEADER_INCLUDE_DIR}
+  PUBLIC
+	$<BUILD_INTERFACE:${spirv-tools_SOURCE_DIR}/include>
+	$<BUILD_INTERFACE:${SPIRV_HEADER_INCLUDE_DIR}>
+	$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
   PRIVATE ${spirv-tools_BINARY_DIR}
 )
 # We need the assembling and disassembling functionalities in the main library.
@@ -243,8 +247,16 @@
 spvtools_check_symbol_exports(SPIRV-Tools-opt)
 
 if(ENABLE_SPIRV_TOOLS_INSTALL)
-  install(TARGETS SPIRV-Tools-opt
+  install(TARGETS SPIRV-Tools-opt EXPORT SPIRV-Tools-optTargets
     RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
     LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
     ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
+  export(EXPORT SPIRV-Tools-optTargets FILE SPIRV-Tools-optTargets.cmake)
+
+  spvtools_config_package_dir(SPIRV-Tools-opt PACKAGE_DIR)
+  install(EXPORT SPIRV-Tools-optTargets FILE SPIRV-Tools-optTargets.cmake
+  	DESTINATION ${PACKAGE_DIR})
+
+  spvtools_generate_config_file(SPIRV-Tools-opt)
+  install(FILES ${CMAKE_BINARY_DIR}/SPIRV-Tools-optConfig.cmake DESTINATION ${PACKAGE_DIR})
 endif(ENABLE_SPIRV_TOOLS_INSTALL)
diff --git a/source/opt/amd_ext_to_khr.cpp b/source/opt/amd_ext_to_khr.cpp
index 1cb5ba5..e9b7f86 100644
--- a/source/opt/amd_ext_to_khr.cpp
+++ b/source/opt/amd_ext_to_khr.cpp
@@ -14,6 +14,9 @@
 
 #include "source/opt/amd_ext_to_khr.h"
 
+#include <set>
+#include <string>
+
 #include "ir_builder.h"
 #include "source/opt/ir_context.h"
 #include "spv-amd-shader-ballot.insts.inc"
@@ -24,22 +27,125 @@
 
 namespace {
 
-enum ExtOpcodes {
+enum AmdShaderBallotExtOpcodes {
   AmdShaderBallotSwizzleInvocationsAMD = 1,
   AmdShaderBallotSwizzleInvocationsMaskedAMD = 2,
   AmdShaderBallotWriteInvocationAMD = 3,
   AmdShaderBallotMbcntAMD = 4
 };
 
+enum AmdShaderTrinaryMinMaxExtOpCodes {
+  FMin3AMD = 1,
+  UMin3AMD = 2,
+  SMin3AMD = 3,
+  FMax3AMD = 4,
+  UMax3AMD = 5,
+  SMax3AMD = 6,
+  FMid3AMD = 7,
+  UMid3AMD = 8,
+  SMid3AMD = 9
+};
+
+enum AmdGcnShader { CubeFaceCoordAMD = 2, CubeFaceIndexAMD = 1, TimeAMD = 3 };
+
 analysis::Type* GetUIntType(IRContext* ctx) {
   analysis::Integer int_type(32, false);
   return ctx->get_type_mgr()->GetRegisteredType(&int_type);
 }
 
+bool NotImplementedYet(IRContext*, Instruction*,
+                       const std::vector<const analysis::Constant*>&) {
+  assert(false && "Not implemented.");
+  return false;
+}
+
+// Returns a folding rule that replaces |op(a,b,c)| by |op(op(a,b),c)|, where
+// |op| is either min or max. |opcode| is the binary opcode in the GLSLstd450
+// extended instruction set that corresponds to the trinary instruction being
+// replaced.
+template <GLSLstd450 opcode>
+bool ReplaceTrinaryMinMax(IRContext* ctx, Instruction* inst,
+                          const std::vector<const analysis::Constant*>&) {
+  uint32_t glsl405_ext_inst_id =
+      ctx->get_feature_mgr()->GetExtInstImportId_GLSLstd450();
+  if (glsl405_ext_inst_id == 0) {
+    ctx->AddExtInstImport("GLSL.std.450");
+    glsl405_ext_inst_id =
+        ctx->get_feature_mgr()->GetExtInstImportId_GLSLstd450();
+  }
+
+  InstructionBuilder ir_builder(
+      ctx, inst,
+      IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
+
+  uint32_t op1 = inst->GetSingleWordInOperand(2);
+  uint32_t op2 = inst->GetSingleWordInOperand(3);
+  uint32_t op3 = inst->GetSingleWordInOperand(4);
+
+  Instruction* temp = ir_builder.AddNaryExtendedInstruction(
+      inst->type_id(), glsl405_ext_inst_id, opcode, {op1, op2});
+
+  Instruction::OperandList new_operands;
+  new_operands.push_back({SPV_OPERAND_TYPE_ID, {glsl405_ext_inst_id}});
+  new_operands.push_back({SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
+                          {static_cast<uint32_t>(opcode)}});
+  new_operands.push_back({SPV_OPERAND_TYPE_ID, {temp->result_id()}});
+  new_operands.push_back({SPV_OPERAND_TYPE_ID, {op3}});
+
+  inst->SetInOperands(std::move(new_operands));
+  ctx->UpdateDefUse(inst);
+  return true;
+}
+
+// Returns a folding rule that replaces |mid(a,b,c)| by |clamp(a, min(b,c),
+// max(b,c)|. The three parameters are the opcode that correspond to the min,
+// max, and clamp operations for the type of the instruction being replaced.
+template <GLSLstd450 min_opcode, GLSLstd450 max_opcode, GLSLstd450 clamp_opcode>
+bool ReplaceTrinaryMid(IRContext* ctx, Instruction* inst,
+                       const std::vector<const analysis::Constant*>&) {
+  uint32_t glsl405_ext_inst_id =
+      ctx->get_feature_mgr()->GetExtInstImportId_GLSLstd450();
+  if (glsl405_ext_inst_id == 0) {
+    ctx->AddExtInstImport("GLSL.std.450");
+    glsl405_ext_inst_id =
+        ctx->get_feature_mgr()->GetExtInstImportId_GLSLstd450();
+  }
+
+  InstructionBuilder ir_builder(
+      ctx, inst,
+      IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
+
+  uint32_t op1 = inst->GetSingleWordInOperand(2);
+  uint32_t op2 = inst->GetSingleWordInOperand(3);
+  uint32_t op3 = inst->GetSingleWordInOperand(4);
+
+  Instruction* min = ir_builder.AddNaryExtendedInstruction(
+      inst->type_id(), glsl405_ext_inst_id, static_cast<uint32_t>(min_opcode),
+      {op2, op3});
+  Instruction* max = ir_builder.AddNaryExtendedInstruction(
+      inst->type_id(), glsl405_ext_inst_id, static_cast<uint32_t>(max_opcode),
+      {op2, op3});
+
+  Instruction::OperandList new_operands;
+  new_operands.push_back({SPV_OPERAND_TYPE_ID, {glsl405_ext_inst_id}});
+  new_operands.push_back({SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
+                          {static_cast<uint32_t>(clamp_opcode)}});
+  new_operands.push_back({SPV_OPERAND_TYPE_ID, {op1}});
+  new_operands.push_back({SPV_OPERAND_TYPE_ID, {min->result_id()}});
+  new_operands.push_back({SPV_OPERAND_TYPE_ID, {max->result_id()}});
+
+  inst->SetInOperands(std::move(new_operands));
+  ctx->UpdateDefUse(inst);
+  return true;
+}
+
 // 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.
-FoldingRule ReplaceGroupNonuniformOperationOpCode(SpvOp new_opcode) {
+template <SpvOp new_opcode>
+bool ReplaceGroupNonuniformOperationOpCode(
+    IRContext* ctx, Instruction* inst,
+    const std::vector<const analysis::Constant*>&) {
   switch (new_opcode) {
     case SpvOpGroupNonUniformIAdd:
     case SpvOpGroupNonUniformFAdd:
@@ -56,27 +162,24 @@
           "Should be replacing with a group non uniform arithmetic operation.");
   }
 
-  return [new_opcode](IRContext* ctx, Instruction* inst,
-                      const std::vector<const analysis::Constant*>&) {
-    switch (inst->opcode()) {
-      case SpvOpGroupIAddNonUniformAMD:
-      case SpvOpGroupFAddNonUniformAMD:
-      case SpvOpGroupUMinNonUniformAMD:
-      case SpvOpGroupSMinNonUniformAMD:
-      case SpvOpGroupFMinNonUniformAMD:
-      case SpvOpGroupUMaxNonUniformAMD:
-      case SpvOpGroupSMaxNonUniformAMD:
-      case SpvOpGroupFMaxNonUniformAMD:
-        break;
-      default:
-        assert(false &&
-               "Should be replacing a group non uniform arithmetic operation.");
-    }
+  switch (inst->opcode()) {
+    case SpvOpGroupIAddNonUniformAMD:
+    case SpvOpGroupFAddNonUniformAMD:
+    case SpvOpGroupUMinNonUniformAMD:
+    case SpvOpGroupSMinNonUniformAMD:
+    case SpvOpGroupFMinNonUniformAMD:
+    case SpvOpGroupUMaxNonUniformAMD:
+    case SpvOpGroupSMaxNonUniformAMD:
+    case SpvOpGroupFMaxNonUniformAMD:
+      break;
+    default:
+      assert(false &&
+             "Should be replacing a group non uniform arithmetic operation.");
+  }
 
-    ctx->AddCapability(SpvCapabilityGroupNonUniformArithmetic);
-    inst->SetOpcode(new_opcode);
-    return true;
-  };
+  ctx->AddCapability(SpvCapabilityGroupNonUniformArithmetic);
+  inst->SetOpcode(new_opcode);
+  return true;
 }
 
 // Returns a folding rule that will replace the SwizzleInvocationsAMD extended
@@ -112,84 +215,82 @@
 // clang-format on
 //
 // Also adding the capabilities and builtins that are needed.
-FoldingRule ReplaceSwizzleInvocations() {
-  return [](IRContext* ctx, Instruction* inst,
-            const std::vector<const analysis::Constant*>&) {
-    analysis::TypeManager* type_mgr = ctx->get_type_mgr();
-    analysis::ConstantManager* const_mgr = ctx->get_constant_mgr();
+bool ReplaceSwizzleInvocations(IRContext* ctx, Instruction* inst,
+                               const std::vector<const analysis::Constant*>&) {
+  analysis::TypeManager* type_mgr = ctx->get_type_mgr();
+  analysis::ConstantManager* const_mgr = ctx->get_constant_mgr();
 
-    ctx->AddExtension("SPV_KHR_shader_ballot");
-    ctx->AddCapability(SpvCapabilityGroupNonUniformBallot);
-    ctx->AddCapability(SpvCapabilityGroupNonUniformShuffle);
+  ctx->AddExtension("SPV_KHR_shader_ballot");
+  ctx->AddCapability(SpvCapabilityGroupNonUniformBallot);
+  ctx->AddCapability(SpvCapabilityGroupNonUniformShuffle);
 
-    InstructionBuilder ir_builder(
-        ctx, inst,
-        IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
+  InstructionBuilder ir_builder(
+      ctx, inst,
+      IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
 
-    uint32_t data_id = inst->GetSingleWordInOperand(2);
-    uint32_t offset_id = inst->GetSingleWordInOperand(3);
+  uint32_t data_id = inst->GetSingleWordInOperand(2);
+  uint32_t offset_id = inst->GetSingleWordInOperand(3);
 
-    // Get the subgroup invocation id.
-    uint32_t var_id =
-        ctx->GetBuiltinInputVarId(SpvBuiltInSubgroupLocalInvocationId);
-    assert(var_id != 0 && "Could not get SubgroupLocalInvocationId variable.");
-    Instruction* var_inst = ctx->get_def_use_mgr()->GetDef(var_id);
-    Instruction* var_ptr_type =
-        ctx->get_def_use_mgr()->GetDef(var_inst->type_id());
-    uint32_t uint_type_id = var_ptr_type->GetSingleWordInOperand(1);
+  // Get the subgroup invocation id.
+  uint32_t var_id =
+      ctx->GetBuiltinInputVarId(SpvBuiltInSubgroupLocalInvocationId);
+  assert(var_id != 0 && "Could not get SubgroupLocalInvocationId variable.");
+  Instruction* var_inst = ctx->get_def_use_mgr()->GetDef(var_id);
+  Instruction* var_ptr_type =
+      ctx->get_def_use_mgr()->GetDef(var_inst->type_id());
+  uint32_t uint_type_id = var_ptr_type->GetSingleWordInOperand(1);
 
-    Instruction* id = ir_builder.AddLoad(uint_type_id, var_id);
+  Instruction* id = ir_builder.AddLoad(uint_type_id, var_id);
 
-    uint32_t quad_mask = ir_builder.GetUintConstantId(3);
+  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);
+  // 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);
 
-    // 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());
+  // 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());
 
-    // 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());
+  // 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());
 
-    // 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());
+  // 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());
 
-    // Do the group operations
-    uint32_t uint_max_id = ir_builder.GetUintConstantId(0xFFFFFFFF);
-    uint32_t subgroup_scope = ir_builder.GetUintConstantId(SpvScopeSubgroup);
-    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,
-        {subgroup_scope, ballot_value->result_id(), target_inv->result_id()});
-    Instruction* shuffle = ir_builder.AddNaryOp(
-        inst->type_id(), SpvOpGroupNonUniformShuffle,
-        {subgroup_scope, data_id, target_inv->result_id()});
+  // Do the group operations
+  uint32_t uint_max_id = ir_builder.GetUintConstantId(0xFFFFFFFF);
+  uint32_t subgroup_scope = ir_builder.GetUintConstantId(SpvScopeSubgroup);
+  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,
+      {subgroup_scope, ballot_value->result_id(), target_inv->result_id()});
+  Instruction* shuffle =
+      ir_builder.AddNaryOp(inst->type_id(), SpvOpGroupNonUniformShuffle,
+                           {subgroup_scope, data_id, target_inv->result_id()});
 
-    // Create the null constant to use in the select.
-    const auto* null = const_mgr->GetConstant(
-        type_mgr->GetType(inst->type_id()), std::vector<uint32_t>());
-    Instruction* null_inst = const_mgr->GetDefiningInstruction(null);
+  // Create the null constant to use in the select.
+  const auto* null = const_mgr->GetConstant(type_mgr->GetType(inst->type_id()),
+                                            std::vector<uint32_t>());
+  Instruction* null_inst = const_mgr->GetDefiningInstruction(null);
 
-    // Build the select.
-    inst->SetOpcode(SpvOpSelect);
-    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()}});
-    new_operands.push_back({SPV_OPERAND_TYPE_ID, {null_inst->result_id()}});
+  // Build the select.
+  inst->SetOpcode(SpvOpSelect);
+  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()}});
+  new_operands.push_back({SPV_OPERAND_TYPE_ID, {null_inst->result_id()}});
 
-    inst->SetInOperands(std::move(new_operands));
-    ctx->UpdateDefUse(inst);
-    return true;
-  };
+  inst->SetInOperands(std::move(new_operands));
+  ctx->UpdateDefUse(inst);
+  return true;
 }
 
 // Returns a folding rule that will replace the SwizzleInvocationsMaskedAMD
@@ -225,89 +326,86 @@
 // clang-format on
 //
 // Also adding the capabilities and builtins that are needed.
-FoldingRule ReplaceSwizzleInvocationsMasked() {
-  return [](IRContext* ctx, Instruction* inst,
-            const std::vector<const analysis::Constant*>&) {
-    analysis::TypeManager* type_mgr = ctx->get_type_mgr();
-    analysis::DefUseManager* def_use_mgr = ctx->get_def_use_mgr();
-    analysis::ConstantManager* const_mgr = ctx->get_constant_mgr();
+bool ReplaceSwizzleInvocationsMasked(
+    IRContext* ctx, Instruction* inst,
+    const std::vector<const analysis::Constant*>&) {
+  analysis::TypeManager* type_mgr = ctx->get_type_mgr();
+  analysis::DefUseManager* def_use_mgr = ctx->get_def_use_mgr();
+  analysis::ConstantManager* const_mgr = ctx->get_constant_mgr();
 
-    // ctx->AddCapability(SpvCapabilitySubgroupBallotKHR);
-    ctx->AddCapability(SpvCapabilityGroupNonUniformBallot);
-    ctx->AddCapability(SpvCapabilityGroupNonUniformShuffle);
+  ctx->AddCapability(SpvCapabilityGroupNonUniformBallot);
+  ctx->AddCapability(SpvCapabilityGroupNonUniformShuffle);
 
-    InstructionBuilder ir_builder(
-        ctx, inst,
-        IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
+  InstructionBuilder ir_builder(
+      ctx, inst,
+      IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
 
-    // Get the operands to inst, and the components of the mask
-    uint32_t data_id = inst->GetSingleWordInOperand(2);
+  // Get the operands to inst, and the components of the mask
+  uint32_t data_id = inst->GetSingleWordInOperand(2);
 
-    Instruction* mask_inst =
-        def_use_mgr->GetDef(inst->GetSingleWordInOperand(3));
-    assert(mask_inst->opcode() == SpvOpConstantComposite &&
-           "The mask is suppose to be a vector constant.");
-    assert(mask_inst->NumInOperands() == 3 &&
-           "The mask is suppose to have 3 components.");
+  Instruction* mask_inst = def_use_mgr->GetDef(inst->GetSingleWordInOperand(3));
+  assert(mask_inst->opcode() == SpvOpConstantComposite &&
+         "The mask is suppose to be a vector constant.");
+  assert(mask_inst->NumInOperands() == 3 &&
+         "The mask is suppose to have 3 components.");
 
-    uint32_t uint_x = mask_inst->GetSingleWordInOperand(0);
-    uint32_t uint_y = mask_inst->GetSingleWordInOperand(1);
-    uint32_t uint_z = mask_inst->GetSingleWordInOperand(2);
+  uint32_t uint_x = mask_inst->GetSingleWordInOperand(0);
+  uint32_t uint_y = mask_inst->GetSingleWordInOperand(1);
+  uint32_t uint_z = mask_inst->GetSingleWordInOperand(2);
 
-    // Get the subgroup invocation id.
-    uint32_t var_id =
-        ctx->GetBuiltinInputVarId(SpvBuiltInSubgroupLocalInvocationId);
-    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);
-    Instruction* var_ptr_type =
-        ctx->get_def_use_mgr()->GetDef(var_inst->type_id());
-    uint32_t uint_type_id = var_ptr_type->GetSingleWordInOperand(1);
+  // Get the subgroup invocation id.
+  uint32_t var_id =
+      ctx->GetBuiltinInputVarId(SpvBuiltInSubgroupLocalInvocationId);
+  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);
+  Instruction* var_ptr_type =
+      ctx->get_def_use_mgr()->GetDef(var_inst->type_id());
+  uint32_t uint_type_id = var_ptr_type->GetSingleWordInOperand(1);
 
-    Instruction* id = ir_builder.AddLoad(uint_type_id, var_id);
+  Instruction* id = ir_builder.AddLoad(uint_type_id, var_id);
 
-    // 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* or_result = ir_builder.AddBinaryOp(
-        uint_type_id, SpvOpBitwiseOr, and_result->result_id(), uint_y);
-    Instruction* target_inv = ir_builder.AddBinaryOp(
-        uint_type_id, SpvOpBitwiseXor, or_result->result_id(), uint_z);
+  // 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* or_result = ir_builder.AddBinaryOp(
+      uint_type_id, SpvOpBitwiseOr, and_result->result_id(), uint_y);
+  Instruction* target_inv = ir_builder.AddBinaryOp(
+      uint_type_id, SpvOpBitwiseXor, 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);
-    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,
-        {subgroup_scope, ballot_value->result_id(), target_inv->result_id()});
-    Instruction* shuffle = ir_builder.AddNaryOp(
-        inst->type_id(), SpvOpGroupNonUniformShuffle,
-        {subgroup_scope, data_id, target_inv->result_id()});
+  // Do the group operations
+  uint32_t uint_max_id = ir_builder.GetUintConstantId(0xFFFFFFFF);
+  uint32_t subgroup_scope = ir_builder.GetUintConstantId(SpvScopeSubgroup);
+  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,
+      {subgroup_scope, ballot_value->result_id(), target_inv->result_id()});
+  Instruction* shuffle =
+      ir_builder.AddNaryOp(inst->type_id(), SpvOpGroupNonUniformShuffle,
+                           {subgroup_scope, data_id, target_inv->result_id()});
 
-    // Create the null constant to use in the select.
-    const auto* null = const_mgr->GetConstant(
-        type_mgr->GetType(inst->type_id()), std::vector<uint32_t>());
-    Instruction* null_inst = const_mgr->GetDefiningInstruction(null);
+  // Create the null constant to use in the select.
+  const auto* null = const_mgr->GetConstant(type_mgr->GetType(inst->type_id()),
+                                            std::vector<uint32_t>());
+  Instruction* null_inst = const_mgr->GetDefiningInstruction(null);
 
-    // Build the select.
-    inst->SetOpcode(SpvOpSelect);
-    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()}});
-    new_operands.push_back({SPV_OPERAND_TYPE_ID, {null_inst->result_id()}});
+  // Build the select.
+  inst->SetOpcode(SpvOpSelect);
+  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()}});
+  new_operands.push_back({SPV_OPERAND_TYPE_ID, {null_inst->result_id()}});
 
-    inst->SetInOperands(std::move(new_operands));
-    ctx->UpdateDefUse(inst);
-    return true;
-  };
+  inst->SetInOperands(std::move(new_operands));
+  ctx->UpdateDefUse(inst);
+  return true;
 }
 
 // Returns a folding rule that will replace the WriteInvocationAMD extended
@@ -326,40 +424,38 @@
 // %result = OpSelect %type %cmp %write_value %input_value
 //
 // Also adding the capabilities and builtins that are needed.
-FoldingRule ReplaceWriteInvocation() {
-  return [](IRContext* ctx, Instruction* inst,
-            const std::vector<const analysis::Constant*>&) {
-    uint32_t var_id =
-        ctx->GetBuiltinInputVarId(SpvBuiltInSubgroupLocalInvocationId);
-    ctx->AddCapability(SpvCapabilitySubgroupBallotKHR);
-    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);
-    Instruction* var_ptr_type =
-        ctx->get_def_use_mgr()->GetDef(var_inst->type_id());
+bool ReplaceWriteInvocation(IRContext* ctx, Instruction* inst,
+                            const std::vector<const analysis::Constant*>&) {
+  uint32_t var_id =
+      ctx->GetBuiltinInputVarId(SpvBuiltInSubgroupLocalInvocationId);
+  ctx->AddCapability(SpvCapabilitySubgroupBallotKHR);
+  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);
+  Instruction* var_ptr_type =
+      ctx->get_def_use_mgr()->GetDef(var_inst->type_id());
 
-    InstructionBuilder ir_builder(
-        ctx, inst,
-        IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
-    Instruction* t =
-        ir_builder.AddLoad(var_ptr_type->GetSingleWordInOperand(1), var_id);
-    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(),
-                               inst->GetSingleWordInOperand(4));
+  InstructionBuilder ir_builder(
+      ctx, inst,
+      IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
+  Instruction* t =
+      ir_builder.AddLoad(var_ptr_type->GetSingleWordInOperand(1), var_id);
+  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(),
+                             inst->GetSingleWordInOperand(4));
 
-    // Build a select.
-    inst->SetOpcode(SpvOpSelect);
-    Instruction::OperandList new_operands;
-    new_operands.push_back({SPV_OPERAND_TYPE_ID, {cmp->result_id()}});
-    new_operands.push_back(inst->GetInOperand(3));
-    new_operands.push_back(inst->GetInOperand(2));
+  // Build a select.
+  inst->SetOpcode(SpvOpSelect);
+  Instruction::OperandList new_operands;
+  new_operands.push_back({SPV_OPERAND_TYPE_ID, {cmp->result_id()}});
+  new_operands.push_back(inst->GetInOperand(3));
+  new_operands.push_back(inst->GetInOperand(2));
 
-    inst->SetInOperands(std::move(new_operands));
-    ctx->UpdateDefUse(inst);
-    return true;
-  };
+  inst->SetInOperands(std::move(new_operands));
+  ctx->UpdateDefUse(inst);
+  return true;
 }
 
 // Returns a folding rule that will replace the MbcntAMD extended instruction in
@@ -384,51 +480,324 @@
 //  %result = OpBitCount %uint %and
 //
 // Also adding the capabilities and builtins that are needed.
-FoldingRule ReplaceMbcnt() {
-  return [](IRContext* context, Instruction* inst,
-            const std::vector<const analysis::Constant*>&) {
-    analysis::TypeManager* type_mgr = context->get_type_mgr();
-    analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
+bool ReplaceMbcnt(IRContext* context, Instruction* inst,
+                  const std::vector<const analysis::Constant*>&) {
+  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);
-    assert(var_id != 0 && "Could not get SubgroupLtMask variable.");
-    context->AddCapability(SpvCapabilityGroupNonUniformBallot);
-    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 &&
-           "Variable is suppose to be a vector of 4 ints");
+  uint32_t var_id = context->GetBuiltinInputVarId(SpvBuiltInSubgroupLtMask);
+  assert(var_id != 0 && "Could not get SubgroupLtMask variable.");
+  context->AddCapability(SpvCapabilityGroupNonUniformBallot);
+  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 &&
+         "Variable is suppose to be a vector of 4 ints");
 
-    // Get the type for the shuffle.
-    analysis::Vector temp_type(GetUIntType(context), 2);
-    const analysis::Type* shuffle_type =
-        context->get_type_mgr()->GetRegisteredType(&temp_type);
-    uint32_t shuffle_type_id = type_mgr->GetTypeInstruction(shuffle_type);
+  // Get the type for the shuffle.
+  analysis::Vector temp_type(GetUIntType(context), 2);
+  const analysis::Type* shuffle_type =
+      context->get_type_mgr()->GetRegisteredType(&temp_type);
+  uint32_t shuffle_type_id = type_mgr->GetTypeInstruction(shuffle_type);
 
-    uint32_t mask_id = inst->GetSingleWordInOperand(2);
-    Instruction* mask_inst = def_use_mgr->GetDef(mask_id);
+  uint32_t mask_id = inst->GetSingleWordInOperand(2);
+  Instruction* mask_inst = def_use_mgr->GetDef(mask_id);
 
-    // Testing with amd's shader compiler shows that a 64-bit mask is expected.
-    assert(type_mgr->GetType(mask_inst->type_id())->AsInteger() != nullptr);
-    assert(type_mgr->GetType(mask_inst->type_id())->AsInteger()->width() == 64);
+  // Testing with amd's shader compiler shows that a 64-bit mask is expected.
+  assert(type_mgr->GetType(mask_inst->type_id())->AsInteger() != nullptr);
+  assert(type_mgr->GetType(mask_inst->type_id())->AsInteger()->width() == 64);
 
-    InstructionBuilder ir_builder(
-        context, inst,
-        IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
-    Instruction* load = ir_builder.AddLoad(var_type->result_id(), var_id);
-    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);
+  InstructionBuilder ir_builder(
+      context, inst,
+      IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
+  Instruction* load = ir_builder.AddLoad(var_type->result_id(), var_id);
+  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);
 
-    inst->SetOpcode(SpvOpBitCount);
-    inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {t->result_id()}}});
-    context->UpdateDefUse(inst);
-    return true;
-  };
+  inst->SetOpcode(SpvOpBitCount);
+  inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {t->result_id()}}});
+  context->UpdateDefUse(inst);
+  return true;
+}
+
+// A folding rule that will replace the CubeFaceCoordAMD extended
+// instruction in the SPV_AMD_gcn_shader_ballot.  Returns true if the folding is
+// successful.
+//
+// The instruction
+//
+//  %result = OpExtInst %v2float %1 CubeFaceCoordAMD %input
+//
+// with
+//
+//             %x = OpCompositeExtract %float %input 0
+//             %y = OpCompositeExtract %float %input 1
+//             %z = OpCompositeExtract %float %input 2
+//            %nx = OpFNegate %float %x
+//            %ny = OpFNegate %float %y
+//            %nz = OpFNegate %float %z
+//            %ax = OpExtInst %float %n_1 FAbs %x
+//            %ay = OpExtInst %float %n_1 FAbs %y
+//            %az = OpExtInst %float %n_1 FAbs %z
+//      %amax_x_y = OpExtInst %float %n_1 FMax %ay %ax
+//          %amax = OpExtInst %float %n_1 FMax %az %amax_x_y
+//        %cubema = OpFMul %float %float_2 %amax
+//      %is_z_max = OpFOrdGreaterThanEqual %bool %az %amax_x_y
+//  %not_is_z_max = OpLogicalNot %bool %is_z_max
+//        %y_gt_x = OpFOrdGreaterThanEqual %bool %ay %ax
+//      %is_y_max = OpLogicalAnd %bool %not_is_z_max %y_gt_x
+//      %is_z_neg = OpFOrdLessThan %bool %z %float_0
+// %cubesc_case_1 = OpSelect %float %is_z_neg %nx %x
+//      %is_x_neg = OpFOrdLessThan %bool %x %float_0
+// %cubesc_case_2 = OpSelect %float %is_x_neg %z %nz
+//           %sel = OpSelect %float %is_y_max %x %cubesc_case_2
+//        %cubesc = OpSelect %float %is_z_max %cubesc_case_1 %sel
+//      %is_y_neg = OpFOrdLessThan %bool %y %float_0
+// %cubetc_case_1 = OpSelect %float %is_y_neg %nz %z
+//        %cubetc = OpSelect %float %is_y_max %cubetc_case_1 %ny
+//          %cube = OpCompositeConstruct %v2float %cubesc %cubetc
+//         %denom = OpCompositeConstruct %v2float %cubema %cubema
+//           %div = OpFDiv %v2float %cube %denom
+//        %result = OpFAdd %v2float %div %const
+//
+// Also adding the capabilities and builtins that are needed.
+bool ReplaceCubeFaceCoord(IRContext* ctx, Instruction* inst,
+                          const std::vector<const analysis::Constant*>&) {
+  analysis::TypeManager* type_mgr = ctx->get_type_mgr();
+  analysis::ConstantManager* const_mgr = ctx->get_constant_mgr();
+
+  uint32_t float_type_id = type_mgr->GetFloatTypeId();
+  const analysis::Type* v2_float_type = type_mgr->GetFloatVectorType(2);
+  uint32_t v2_float_type_id = type_mgr->GetId(v2_float_type);
+  uint32_t bool_id = type_mgr->GetBoolTypeId();
+
+  InstructionBuilder ir_builder(
+      ctx, inst,
+      IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
+
+  uint32_t input_id = inst->GetSingleWordInOperand(2);
+  uint32_t glsl405_ext_inst_id =
+      ctx->get_feature_mgr()->GetExtInstImportId_GLSLstd450();
+  if (glsl405_ext_inst_id == 0) {
+    ctx->AddExtInstImport("GLSL.std.450");
+    glsl405_ext_inst_id =
+        ctx->get_feature_mgr()->GetExtInstImportId_GLSLstd450();
+  }
+
+  // Get the constants that will be used.
+  uint32_t f0_const_id = const_mgr->GetFloatConst(0.0);
+  uint32_t f2_const_id = const_mgr->GetFloatConst(2.0);
+  uint32_t f0_5_const_id = const_mgr->GetFloatConst(0.5);
+  const analysis::Constant* vec_const =
+      const_mgr->GetConstant(v2_float_type, {f0_5_const_id, f0_5_const_id});
+  uint32_t vec_const_id =
+      const_mgr->GetDefiningInstruction(vec_const)->result_id();
+
+  // Extract the input values.
+  Instruction* x = ir_builder.AddCompositeExtract(float_type_id, input_id, {0});
+  Instruction* y = ir_builder.AddCompositeExtract(float_type_id, input_id, {1});
+  Instruction* z = ir_builder.AddCompositeExtract(float_type_id, input_id, {2});
+
+  // Negate the input values.
+  Instruction* nx =
+      ir_builder.AddUnaryOp(float_type_id, SpvOpFNegate, x->result_id());
+  Instruction* ny =
+      ir_builder.AddUnaryOp(float_type_id, SpvOpFNegate, y->result_id());
+  Instruction* nz =
+      ir_builder.AddUnaryOp(float_type_id, SpvOpFNegate, z->result_id());
+
+  // Get the abolsute values of the inputs.
+  Instruction* ax = ir_builder.AddNaryExtendedInstruction(
+      float_type_id, glsl405_ext_inst_id, GLSLstd450FAbs, {x->result_id()});
+  Instruction* ay = ir_builder.AddNaryExtendedInstruction(
+      float_type_id, glsl405_ext_inst_id, GLSLstd450FAbs, {y->result_id()});
+  Instruction* az = ir_builder.AddNaryExtendedInstruction(
+      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);
+
+  // Compute cubema
+  Instruction* amax_x_y = ir_builder.AddNaryExtendedInstruction(
+      float_type_id, glsl405_ext_inst_id, GLSLstd450FMax,
+      {ax->result_id(), ay->result_id()});
+  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,
+                                               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,
+                             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());
+
+  // Select the correct value for cubesc.
+  Instruction* cubesc_case_1 = ir_builder.AddSelect(
+      float_type_id, is_z_neg->result_id(), nx->result_id(), x->result_id());
+  Instruction* cubesc_case_2 = ir_builder.AddSelect(
+      float_type_id, is_x_neg->result_id(), z->result_id(), nz->result_id());
+  Instruction* sel =
+      ir_builder.AddSelect(float_type_id, is_y_max->result_id(), x->result_id(),
+                           cubesc_case_2->result_id());
+  Instruction* cubesc =
+      ir_builder.AddSelect(float_type_id, is_z_max->result_id(),
+                           cubesc_case_1->result_id(), sel->result_id());
+
+  // Select the correct value for cubetc.
+  Instruction* cubetc_case_1 = ir_builder.AddSelect(
+      float_type_id, is_y_neg->result_id(), nz->result_id(), z->result_id());
+  Instruction* cubetc =
+      ir_builder.AddSelect(float_type_id, is_y_max->result_id(),
+                           cubetc_case_1->result_id(), ny->result_id());
+
+  // Do the division
+  Instruction* cube = ir_builder.AddCompositeConstruct(
+      v2_float_type_id, {cubesc->result_id(), cubetc->result_id()});
+  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());
+
+  // Get the final result by adding 0.5 to |div|.
+  inst->SetOpcode(SpvOpFAdd);
+  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}});
+
+  inst->SetInOperands(std::move(new_operands));
+  ctx->UpdateDefUse(inst);
+  return true;
+}
+
+// A folding rule that will replace the CubeFaceCoordAMD extended
+// instruction in the SPV_AMD_gcn_shader_ballot.  Returns true if the folding
+// is successful.
+//
+// The instruction
+//
+//  %result = OpExtInst %v2float %1 CubeFaceCoordAMD %input
+//
+// with
+//
+//             %x = OpCompositeExtract %float %input 0
+//             %y = OpCompositeExtract %float %input 1
+//             %z = OpCompositeExtract %float %input 2
+//            %ax = OpExtInst %float %n_1 FAbs %x
+//            %ay = OpExtInst %float %n_1 FAbs %y
+//            %az = OpExtInst %float %n_1 FAbs %z
+//      %is_z_neg = OpFOrdLessThan %bool %z %float_0
+//      %is_y_neg = OpFOrdLessThan %bool %y %float_0
+//      %is_x_neg = OpFOrdLessThan %bool %x %float_0
+//      %amax_x_y = OpExtInst %float %n_1 FMax %ay %ax
+//      %is_z_max = OpFOrdGreaterThanEqual %bool %az %amax_x_y
+//        %y_gt_x = OpFOrdGreaterThanEqual %bool %ay %ax
+//        %case_z = OpSelect %float %is_z_neg %float_5 %float4
+//        %case_y = OpSelect %float %is_y_neg %float_3 %float2
+//        %case_x = OpSelect %float %is_x_neg %float_1 %float0
+//           %sel = OpSelect %float %y_gt_x %case_y %case_x
+//        %result = OpSelect %float %is_z_max %case_z %sel
+//
+// Also adding the capabilities and builtins that are needed.
+bool ReplaceCubeFaceIndex(IRContext* ctx, Instruction* inst,
+                          const std::vector<const analysis::Constant*>&) {
+  analysis::TypeManager* type_mgr = ctx->get_type_mgr();
+  analysis::ConstantManager* const_mgr = ctx->get_constant_mgr();
+
+  uint32_t float_type_id = type_mgr->GetFloatTypeId();
+  uint32_t bool_id = type_mgr->GetBoolTypeId();
+
+  InstructionBuilder ir_builder(
+      ctx, inst,
+      IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
+
+  uint32_t input_id = inst->GetSingleWordInOperand(2);
+  uint32_t glsl405_ext_inst_id =
+      ctx->get_feature_mgr()->GetExtInstImportId_GLSLstd450();
+  if (glsl405_ext_inst_id == 0) {
+    ctx->AddExtInstImport("GLSL.std.450");
+    glsl405_ext_inst_id =
+        ctx->get_feature_mgr()->GetExtInstImportId_GLSLstd450();
+  }
+
+  // Get the constants that will be used.
+  uint32_t f0_const_id = const_mgr->GetFloatConst(0.0);
+  uint32_t f1_const_id = const_mgr->GetFloatConst(1.0);
+  uint32_t f2_const_id = const_mgr->GetFloatConst(2.0);
+  uint32_t f3_const_id = const_mgr->GetFloatConst(3.0);
+  uint32_t f4_const_id = const_mgr->GetFloatConst(4.0);
+  uint32_t f5_const_id = const_mgr->GetFloatConst(5.0);
+
+  // Extract the input values.
+  Instruction* x = ir_builder.AddCompositeExtract(float_type_id, input_id, {0});
+  Instruction* y = ir_builder.AddCompositeExtract(float_type_id, input_id, {1});
+  Instruction* z = ir_builder.AddCompositeExtract(float_type_id, input_id, {2});
+
+  // Get the absolute values of the inputs.
+  Instruction* ax = ir_builder.AddNaryExtendedInstruction(
+      float_type_id, glsl405_ext_inst_id, GLSLstd450FAbs, {x->result_id()});
+  Instruction* ay = ir_builder.AddNaryExtendedInstruction(
+      float_type_id, glsl405_ext_inst_id, GLSLstd450FAbs, {y->result_id()});
+  Instruction* az = ir_builder.AddNaryExtendedInstruction(
+      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);
+
+  // 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,
+                             az->result_id(), amax_x_y->result_id());
+  Instruction* y_gr_x = ir_builder.AddBinaryOp(
+      bool_id, SpvOpFOrdGreaterThanEqual, ay->result_id(), ax->result_id());
+
+  // Get the value for each case.
+  Instruction* case_z = ir_builder.AddSelect(
+      float_type_id, is_z_neg->result_id(), f5_const_id, f4_const_id);
+  Instruction* case_y = ir_builder.AddSelect(
+      float_type_id, is_y_neg->result_id(), f3_const_id, f2_const_id);
+  Instruction* case_x = ir_builder.AddSelect(
+      float_type_id, is_x_neg->result_id(), f1_const_id, f0_const_id);
+
+  // Select the correct case.
+  Instruction* sel =
+      ir_builder.AddSelect(float_type_id, y_gr_x->result_id(),
+                           case_y->result_id(), case_x->result_id());
+
+  // Get the final result by adding 0.5 to |div|.
+  inst->SetOpcode(SpvOpSelect);
+  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()}});
+  new_operands.push_back({SPV_OPERAND_TYPE_ID, {sel->result_id()}});
+
+  inst->SetInOperands(std::move(new_operands));
+  ctx->UpdateDefUse(inst);
+  return true;
 }
 
 class AmdExtFoldingRules : public FoldingRules {
@@ -438,33 +807,70 @@
  protected:
   virtual void AddFoldingRules() override {
     rules_[SpvOpGroupIAddNonUniformAMD].push_back(
-        ReplaceGroupNonuniformOperationOpCode(SpvOpGroupNonUniformIAdd));
+        ReplaceGroupNonuniformOperationOpCode<SpvOpGroupNonUniformIAdd>);
     rules_[SpvOpGroupFAddNonUniformAMD].push_back(
-        ReplaceGroupNonuniformOperationOpCode(SpvOpGroupNonUniformFAdd));
+        ReplaceGroupNonuniformOperationOpCode<SpvOpGroupNonUniformFAdd>);
     rules_[SpvOpGroupUMinNonUniformAMD].push_back(
-        ReplaceGroupNonuniformOperationOpCode(SpvOpGroupNonUniformUMin));
+        ReplaceGroupNonuniformOperationOpCode<SpvOpGroupNonUniformUMin>);
     rules_[SpvOpGroupSMinNonUniformAMD].push_back(
-        ReplaceGroupNonuniformOperationOpCode(SpvOpGroupNonUniformSMin));
+        ReplaceGroupNonuniformOperationOpCode<SpvOpGroupNonUniformSMin>);
     rules_[SpvOpGroupFMinNonUniformAMD].push_back(
-        ReplaceGroupNonuniformOperationOpCode(SpvOpGroupNonUniformFMin));
+        ReplaceGroupNonuniformOperationOpCode<SpvOpGroupNonUniformFMin>);
     rules_[SpvOpGroupUMaxNonUniformAMD].push_back(
-        ReplaceGroupNonuniformOperationOpCode(SpvOpGroupNonUniformUMax));
+        ReplaceGroupNonuniformOperationOpCode<SpvOpGroupNonUniformUMax>);
     rules_[SpvOpGroupSMaxNonUniformAMD].push_back(
-        ReplaceGroupNonuniformOperationOpCode(SpvOpGroupNonUniformSMax));
+        ReplaceGroupNonuniformOperationOpCode<SpvOpGroupNonUniformSMax>);
     rules_[SpvOpGroupFMaxNonUniformAMD].push_back(
-        ReplaceGroupNonuniformOperationOpCode(SpvOpGroupNonUniformFMax));
+        ReplaceGroupNonuniformOperationOpCode<SpvOpGroupNonUniformFMax>);
 
     uint32_t extension_id =
         context()->module()->GetExtInstImportId("SPV_AMD_shader_ballot");
 
-    ext_rules_[{extension_id, AmdShaderBallotSwizzleInvocationsAMD}].push_back(
-        ReplaceSwizzleInvocations());
-    ext_rules_[{extension_id, AmdShaderBallotSwizzleInvocationsMaskedAMD}]
-        .push_back(ReplaceSwizzleInvocationsMasked());
-    ext_rules_[{extension_id, AmdShaderBallotWriteInvocationAMD}].push_back(
-        ReplaceWriteInvocation());
-    ext_rules_[{extension_id, AmdShaderBallotMbcntAMD}].push_back(
-        ReplaceMbcnt());
+    if (extension_id != 0) {
+      ext_rules_[{extension_id, AmdShaderBallotSwizzleInvocationsAMD}]
+          .push_back(ReplaceSwizzleInvocations);
+      ext_rules_[{extension_id, AmdShaderBallotSwizzleInvocationsMaskedAMD}]
+          .push_back(ReplaceSwizzleInvocationsMasked);
+      ext_rules_[{extension_id, AmdShaderBallotWriteInvocationAMD}].push_back(
+          ReplaceWriteInvocation);
+      ext_rules_[{extension_id, AmdShaderBallotMbcntAMD}].push_back(
+          ReplaceMbcnt);
+    }
+
+    extension_id = context()->module()->GetExtInstImportId(
+        "SPV_AMD_shader_trinary_minmax");
+
+    if (extension_id != 0) {
+      ext_rules_[{extension_id, FMin3AMD}].push_back(
+          ReplaceTrinaryMinMax<GLSLstd450FMin>);
+      ext_rules_[{extension_id, UMin3AMD}].push_back(
+          ReplaceTrinaryMinMax<GLSLstd450UMin>);
+      ext_rules_[{extension_id, SMin3AMD}].push_back(
+          ReplaceTrinaryMinMax<GLSLstd450SMin>);
+      ext_rules_[{extension_id, FMax3AMD}].push_back(
+          ReplaceTrinaryMinMax<GLSLstd450FMax>);
+      ext_rules_[{extension_id, UMax3AMD}].push_back(
+          ReplaceTrinaryMinMax<GLSLstd450UMax>);
+      ext_rules_[{extension_id, SMax3AMD}].push_back(
+          ReplaceTrinaryMinMax<GLSLstd450SMax>);
+      ext_rules_[{extension_id, FMid3AMD}].push_back(
+          ReplaceTrinaryMid<GLSLstd450FMin, GLSLstd450FMax, GLSLstd450FClamp>);
+      ext_rules_[{extension_id, UMid3AMD}].push_back(
+          ReplaceTrinaryMid<GLSLstd450UMin, GLSLstd450UMax, GLSLstd450UClamp>);
+      ext_rules_[{extension_id, SMid3AMD}].push_back(
+          ReplaceTrinaryMid<GLSLstd450SMin, GLSLstd450SMax, GLSLstd450SClamp>);
+    }
+
+    extension_id =
+        context()->module()->GetExtInstImportId("SPV_AMD_gcn_shader");
+
+    if (extension_id != 0) {
+      ext_rules_[{extension_id, CubeFaceCoordAMD}].push_back(
+          ReplaceCubeFaceCoord);
+      ext_rules_[{extension_id, CubeFaceIndexAMD}].push_back(
+          ReplaceCubeFaceIndex);
+      ext_rules_[{extension_id, TimeAMD}].push_back(NotImplementedYet);
+    }
   }
 };
 
@@ -497,12 +903,15 @@
 
   // Now that instruction that require the extensions have been removed, we can
   // remove the extension instructions.
+  std::set<std::string> ext_to_remove = {"SPV_AMD_shader_ballot",
+                                         "SPV_AMD_shader_trinary_minmax",
+                                         "SPV_AMD_gcn_shader"};
+
   std::vector<Instruction*> to_be_killed;
   for (Instruction& inst : context()->module()->extensions()) {
     if (inst.opcode() == SpvOpExtension) {
-      if (!strcmp("SPV_AMD_shader_ballot",
-                  reinterpret_cast<const char*>(
-                      &(inst.GetInOperand(0).words[0])))) {
+      if (ext_to_remove.count(reinterpret_cast<const char*>(
+              &(inst.GetInOperand(0).words[0]))) != 0) {
         to_be_killed.push_back(&inst);
       }
     }
@@ -510,9 +919,8 @@
 
   for (Instruction& inst : context()->ext_inst_imports()) {
     if (inst.opcode() == SpvOpExtInstImport) {
-      if (!strcmp("SPV_AMD_shader_ballot",
-                  reinterpret_cast<const char*>(
-                      &(inst.GetInOperand(0).words[0])))) {
+      if (ext_to_remove.count(reinterpret_cast<const char*>(
+              &(inst.GetInOperand(0).words[0]))) != 0) {
         to_be_killed.push_back(&inst);
       }
     }
diff --git a/source/opt/cfg.cpp b/source/opt/cfg.cpp
index 72693cb..ac0fcc3 100644
--- a/source/opt/cfg.cpp
+++ b/source/opt/cfg.cpp
@@ -110,15 +110,26 @@
 
 void CFG::ForEachBlockInReversePostOrder(
     BasicBlock* bb, const std::function<void(BasicBlock*)>& f) {
+  WhileEachBlockInReversePostOrder(bb, [f](BasicBlock* b) {
+    f(b);
+    return true;
+  });
+}
+
+bool CFG::WhileEachBlockInReversePostOrder(
+    BasicBlock* bb, const std::function<bool(BasicBlock*)>& f) {
   std::vector<BasicBlock*> po;
   std::unordered_set<BasicBlock*> seen;
   ComputePostOrderTraversal(bb, &po, &seen);
 
   for (auto current_bb = po.rbegin(); current_bb != po.rend(); ++current_bb) {
     if (!IsPseudoExitBlock(*current_bb) && !IsPseudoEntryBlock(*current_bb)) {
-      f(*current_bb);
+      if (!f(*current_bb)) {
+        return false;
+      }
     }
   }
+  return true;
 }
 
 void CFG::ComputeStructuredSuccessors(Function* func) {
diff --git a/source/opt/cfg.h b/source/opt/cfg.h
index 5ff3aa0..f280682 100644
--- a/source/opt/cfg.h
+++ b/source/opt/cfg.h
@@ -60,23 +60,27 @@
 
   // Compute structured block order into |order| for |func| starting at |root|.
   // This order has the property that dominators come before all blocks they
-  // dominate and merge blocks come after all blocks that are in the control
-  // constructs of their header.
+  // dominate, merge blocks come after all blocks that are in the control
+  // constructs of their header, and continue blocks come after all of the
+  // blocks in the body of their loop.
   void ComputeStructuredOrder(Function* func, BasicBlock* root,
                               std::list<BasicBlock*>* order);
 
-  // Applies |f| to the basic block in post order starting with |bb|.
-  // Note that basic blocks that cannot be reached from |bb| node will not be
-  // processed.
+  // Applies |f| to all blocks that can be reach from |bb| in post order.
   void ForEachBlockInPostOrder(BasicBlock* bb,
                                const std::function<void(BasicBlock*)>& f);
 
-  // Applies |f| to the basic block in reverse post order starting with |bb|.
-  // Note that basic blocks that cannot be reached from |bb| node will not be
-  // processed.
+  // Applies |f| to all blocks that can be reach from |bb| in reverse post
+  // order.
   void ForEachBlockInReversePostOrder(
       BasicBlock* bb, const std::function<void(BasicBlock*)>& f);
 
+  // Applies |f| to all blocks that can be reach from |bb| in reverse post
+  // order.  Return false if |f| return false on any basic block, and stops
+  // processing.
+  bool WhileEachBlockInReversePostOrder(
+      BasicBlock* bb, const std::function<bool(BasicBlock*)>& f);
+
   // Registers |blk| as a basic block in the cfg, this also updates the
   // predecessor lists of each successor of |blk|. |blk| must have a terminator
   // instruction at the end of the block.
diff --git a/source/opt/const_folding_rules.cpp b/source/opt/const_folding_rules.cpp
index 06a1a81..2a2493f 100644
--- a/source/opt/const_folding_rules.cpp
+++ b/source/opt/const_folding_rules.cpp
@@ -296,6 +296,51 @@
   };
 }
 
+// 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.
+const analysis::Constant* FoldFPBinaryOp(
+    BinaryScalarFoldingRule scalar_rule, uint32_t result_type_id,
+    const std::vector<const analysis::Constant*>& constants,
+    IRContext* context) {
+  analysis::ConstantManager* const_mgr = context->get_constant_mgr();
+  analysis::TypeManager* type_mgr = context->get_type_mgr();
+  const analysis::Type* result_type = type_mgr->GetType(result_type_id);
+  const analysis::Vector* vector_type = result_type->AsVector();
+
+  if (constants[0] == nullptr || constants[1] == nullptr) {
+    return nullptr;
+  }
+
+  if (vector_type != nullptr) {
+    std::vector<const analysis::Constant*> a_components;
+    std::vector<const analysis::Constant*> b_components;
+    std::vector<const analysis::Constant*> results_components;
+
+    a_components = constants[0]->GetVectorComponents(const_mgr);
+    b_components = constants[1]->GetVectorComponents(const_mgr);
+
+    // Fold each component of the vector.
+    for (uint32_t i = 0; i < a_components.size(); ++i) {
+      results_components.push_back(scalar_rule(vector_type->element_type(),
+                                               a_components[i], b_components[i],
+                                               const_mgr));
+      if (results_components[i] == nullptr) {
+        return nullptr;
+      }
+    }
+
+    // Build the constant object and return it.
+    std::vector<uint32_t> ids;
+    for (const analysis::Constant* member : results_components) {
+      ids.push_back(const_mgr->GetDefiningInstruction(member)->result_id());
+    }
+    return const_mgr->GetConstant(vector_type, ids);
+  } else {
+    return scalar_rule(result_type, constants[0], constants[1], const_mgr);
+  }
+}
+
 // Returns a |ConstantFoldingRule| that folds floating point scalars using
 // |scalar_rule| and vectors of floating point by applying |scalar_rule| to the
 // elements of the vector.  The |ConstantFoldingRule| that is returned assumes
@@ -305,46 +350,14 @@
   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;
     }
-
-    if (constants[0] == nullptr || constants[1] == nullptr) {
-      return nullptr;
+    if (inst->opcode() == SpvOpExtInst) {
+      return FoldFPBinaryOp(scalar_rule, inst->type_id(),
+                            {constants[1], constants[2]}, context);
     }
-
-    if (vector_type != nullptr) {
-      std::vector<const analysis::Constant*> a_components;
-      std::vector<const analysis::Constant*> b_components;
-      std::vector<const analysis::Constant*> results_components;
-
-      a_components = constants[0]->GetVectorComponents(const_mgr);
-      b_components = constants[1]->GetVectorComponents(const_mgr);
-
-      // Fold each component of the vector.
-      for (uint32_t i = 0; i < a_components.size(); ++i) {
-        results_components.push_back(scalar_rule(vector_type->element_type(),
-                                                 a_components[i],
-                                                 b_components[i], const_mgr));
-        if (results_components[i] == nullptr) {
-          return nullptr;
-        }
-      }
-
-      // Build the constant object and return it.
-      std::vector<uint32_t> ids;
-      for (const analysis::Constant* member : results_components) {
-        ids.push_back(const_mgr->GetDefiningInstruction(member)->result_id());
-      }
-      return const_mgr->GetConstant(vector_type, ids);
-    } else {
-      return scalar_rule(result_type, constants[0], constants[1], const_mgr);
-    }
+    return FoldFPBinaryOp(scalar_rule, inst->type_id(), constants, context);
   };
 }
 
@@ -435,29 +448,33 @@
 
 // This macro defines a |BinaryScalarFoldingRule| that applies |op|.  The
 // operator |op| must work for both float and double, and use syntax "f1 op f2".
-#define FOLD_FPARITH_OP(op)                                                \
-  [](const analysis::Type* result_type, const analysis::Constant* a,       \
-     const analysis::Constant* b,                                          \
-     analysis::ConstantManager* const_mgr_in_macro)                        \
-      -> const analysis::Constant* {                                       \
-    assert(result_type != nullptr && a != nullptr && b != nullptr);        \
-    assert(result_type == a->type() && result_type == b->type());          \
-    const analysis::Float* float_type_in_macro = result_type->AsFloat();   \
-    assert(float_type_in_macro != nullptr);                                \
-    if (float_type_in_macro->width() == 32) {                              \
-      float fa = a->GetFloat();                                            \
-      float fb = b->GetFloat();                                            \
-      utils::FloatProxy<float> result_in_macro(fa op fb);                  \
-      std::vector<uint32_t> words_in_macro = result_in_macro.GetWords();   \
-      return const_mgr_in_macro->GetConstant(result_type, words_in_macro); \
-    } else if (float_type_in_macro->width() == 64) {                       \
-      double fa = a->GetDouble();                                          \
-      double fb = b->GetDouble();                                          \
-      utils::FloatProxy<double> result_in_macro(fa op fb);                 \
-      std::vector<uint32_t> words_in_macro = result_in_macro.GetWords();   \
-      return const_mgr_in_macro->GetConstant(result_type, words_in_macro); \
-    }                                                                      \
-    return nullptr;                                                        \
+#define FOLD_FPARITH_OP(op)                                                   \
+  [](const analysis::Type* result_type_in_macro, const analysis::Constant* a, \
+     const analysis::Constant* b,                                             \
+     analysis::ConstantManager* const_mgr_in_macro)                           \
+      -> const analysis::Constant* {                                          \
+    assert(result_type_in_macro != nullptr && a != nullptr && b != nullptr);  \
+    assert(result_type_in_macro == a->type() &&                               \
+           result_type_in_macro == b->type());                                \
+    const analysis::Float* float_type_in_macro =                              \
+        result_type_in_macro->AsFloat();                                      \
+    assert(float_type_in_macro != nullptr);                                   \
+    if (float_type_in_macro->width() == 32) {                                 \
+      float fa = a->GetFloat();                                               \
+      float fb = b->GetFloat();                                               \
+      utils::FloatProxy<float> result_in_macro(fa op fb);                     \
+      std::vector<uint32_t> words_in_macro = result_in_macro.GetWords();      \
+      return const_mgr_in_macro->GetConstant(result_type_in_macro,            \
+                                             words_in_macro);                 \
+    } else if (float_type_in_macro->width() == 64) {                          \
+      double fa = a->GetDouble();                                             \
+      double fb = b->GetDouble();                                             \
+      utils::FloatProxy<double> result_in_macro(fa op fb);                    \
+      std::vector<uint32_t> words_in_macro = result_in_macro.GetWords();      \
+      return const_mgr_in_macro->GetConstant(result_type_in_macro,            \
+                                             words_in_macro);                 \
+    }                                                                         \
+    return nullptr;                                                           \
   }
 
 // Define the folding rule for conversion between floating point and integer
@@ -834,34 +851,225 @@
     }
 
     const analysis::Constant* one;
-    if (constants[1]->type()->AsFloat()->width() == 32) {
-      one = const_mgr->GetConstant(constants[1]->type(),
+    bool is_vector = false;
+    const analysis::Type* result_type = constants[1]->type();
+    const analysis::Type* base_type = result_type;
+    if (base_type->AsVector()) {
+      is_vector = true;
+      base_type = base_type->AsVector()->element_type();
+    }
+    assert(base_type->AsFloat() != nullptr &&
+           "FMix is suppose to act on floats or vectors of floats.");
+
+    if (base_type->AsFloat()->width() == 32) {
+      one = const_mgr->GetConstant(base_type,
                                    utils::FloatProxy<float>(1.0f).GetWords());
     } else {
-      one = const_mgr->GetConstant(constants[1]->type(),
+      one = const_mgr->GetConstant(base_type,
                                    utils::FloatProxy<double>(1.0).GetWords());
     }
 
-    const analysis::Constant* temp1 =
-        FOLD_FPARITH_OP(-)(constants[1]->type(), one, constants[3], const_mgr);
+    if (is_vector) {
+      uint32_t one_id = const_mgr->GetDefiningInstruction(one)->result_id();
+      one =
+          const_mgr->GetConstant(result_type, std::vector<uint32_t>(4, one_id));
+    }
+
+    const analysis::Constant* temp1 = FoldFPBinaryOp(
+        FOLD_FPARITH_OP(-), inst->type_id(), {one, constants[3]}, context);
     if (temp1 == nullptr) {
       return nullptr;
     }
 
-    const analysis::Constant* temp2 = FOLD_FPARITH_OP(*)(
-        constants[1]->type(), constants[1], temp1, const_mgr);
+    const analysis::Constant* temp2 = FoldFPBinaryOp(
+        FOLD_FPARITH_OP(*), inst->type_id(), {constants[1], temp1}, context);
     if (temp2 == nullptr) {
       return nullptr;
     }
-    const analysis::Constant* temp3 = FOLD_FPARITH_OP(*)(
-        constants[2]->type(), constants[2], constants[3], const_mgr);
+    const analysis::Constant* temp3 =
+        FoldFPBinaryOp(FOLD_FPARITH_OP(*), inst->type_id(),
+                       {constants[2], constants[3]}, context);
     if (temp3 == nullptr) {
       return nullptr;
     }
-    return FOLD_FPARITH_OP(+)(temp2->type(), temp2, temp3, const_mgr);
+    return FoldFPBinaryOp(FOLD_FPARITH_OP(+), inst->type_id(), {temp2, temp3},
+                          context);
   };
 }
 
+template <class IntType>
+IntType FoldIClamp(IntType x, IntType min_val, IntType max_val) {
+  if (x < min_val) {
+    x = min_val;
+  }
+  if (x > max_val) {
+    x = max_val;
+  }
+  return x;
+}
+
+const analysis::Constant* FoldMin(const analysis::Type* result_type,
+                                  const analysis::Constant* a,
+                                  const analysis::Constant* b,
+                                  analysis::ConstantManager*) {
+  if (const analysis::Integer* int_type = result_type->AsInteger()) {
+    if (int_type->width() == 32) {
+      if (int_type->IsSigned()) {
+        int32_t va = a->GetS32();
+        int32_t vb = b->GetS32();
+        return (va < vb ? a : b);
+      } else {
+        uint32_t va = a->GetU32();
+        uint32_t vb = b->GetU32();
+        return (va < vb ? a : b);
+      }
+    } else if (int_type->width() == 64) {
+      if (int_type->IsSigned()) {
+        int64_t va = a->GetS64();
+        int64_t vb = b->GetS64();
+        return (va < vb ? a : b);
+      } else {
+        uint64_t va = a->GetU64();
+        uint64_t vb = b->GetU64();
+        return (va < vb ? a : b);
+      }
+    }
+  } else if (const analysis::Float* float_type = result_type->AsFloat()) {
+    if (float_type->width() == 32) {
+      float va = a->GetFloat();
+      float vb = b->GetFloat();
+      return (va < vb ? a : b);
+    } else if (float_type->width() == 64) {
+      double va = a->GetDouble();
+      double vb = b->GetDouble();
+      return (va < vb ? a : b);
+    }
+  }
+  return nullptr;
+}
+
+const analysis::Constant* FoldMax(const analysis::Type* result_type,
+                                  const analysis::Constant* a,
+                                  const analysis::Constant* b,
+                                  analysis::ConstantManager*) {
+  if (const analysis::Integer* int_type = result_type->AsInteger()) {
+    if (int_type->width() == 32) {
+      if (int_type->IsSigned()) {
+        int32_t va = a->GetS32();
+        int32_t vb = b->GetS32();
+        return (va > vb ? a : b);
+      } else {
+        uint32_t va = a->GetU32();
+        uint32_t vb = b->GetU32();
+        return (va > vb ? a : b);
+      }
+    } else if (int_type->width() == 64) {
+      if (int_type->IsSigned()) {
+        int64_t va = a->GetS64();
+        int64_t vb = b->GetS64();
+        return (va > vb ? a : b);
+      } else {
+        uint64_t va = a->GetU64();
+        uint64_t vb = b->GetU64();
+        return (va > vb ? a : b);
+      }
+    }
+  } else if (const analysis::Float* float_type = result_type->AsFloat()) {
+    if (float_type->width() == 32) {
+      float va = a->GetFloat();
+      float vb = b->GetFloat();
+      return (va > vb ? a : b);
+    } else if (float_type->width() == 64) {
+      double va = a->GetDouble();
+      double vb = b->GetDouble();
+      return (va > vb ? a : b);
+    }
+  }
+  return nullptr;
+}
+
+// Fold an clamp instruction when all three operands are constant.
+const analysis::Constant* FoldClamp1(
+    IRContext* context, Instruction* inst,
+    const std::vector<const analysis::Constant*>& constants) {
+  assert(inst->opcode() == SpvOpExtInst &&
+         "Expecting an extended instruction.");
+  assert(inst->GetSingleWordInOperand(0) ==
+             context->get_feature_mgr()->GetExtInstImportId_GLSLstd450() &&
+         "Expecting a GLSLstd450 extended instruction.");
+
+  // Make sure all Clamp operands are constants.
+  for (uint32_t i = 1; i < 3; i++) {
+    if (constants[i] == nullptr) {
+      return nullptr;
+    }
+  }
+
+  const analysis::Constant* temp = FoldFPBinaryOp(
+      FoldMax, inst->type_id(), {constants[1], constants[2]}, context);
+  if (temp == nullptr) {
+    return nullptr;
+  }
+  return FoldFPBinaryOp(FoldMin, inst->type_id(), {temp, constants[3]},
+                        context);
+}
+
+// Fold a clamp instruction when |x >= min_val|.
+const analysis::Constant* FoldClamp2(
+    IRContext* context, Instruction* inst,
+    const std::vector<const analysis::Constant*>& constants) {
+  assert(inst->opcode() == SpvOpExtInst &&
+         "Expecting an extended instruction.");
+  assert(inst->GetSingleWordInOperand(0) ==
+             context->get_feature_mgr()->GetExtInstImportId_GLSLstd450() &&
+         "Expecting a GLSLstd450 extended instruction.");
+
+  const analysis::Constant* x = constants[1];
+  const analysis::Constant* min_val = constants[2];
+
+  if (x == nullptr || min_val == nullptr) {
+    return nullptr;
+  }
+
+  const analysis::Constant* temp =
+      FoldFPBinaryOp(FoldMax, inst->type_id(), {x, min_val}, context);
+  if (temp == min_val) {
+    // We can assume that |min_val| is less than |max_val|.  Therefore, if the
+    // result of the max operation is |min_val|, we know the result of the min
+    // operation, even if |max_val| is not a constant.
+    return min_val;
+  }
+  return nullptr;
+}
+
+// Fold a clamp instruction when |x >= max_val|.
+const analysis::Constant* FoldClamp3(
+    IRContext* context, Instruction* inst,
+    const std::vector<const analysis::Constant*>& constants) {
+  assert(inst->opcode() == SpvOpExtInst &&
+         "Expecting an extended instruction.");
+  assert(inst->GetSingleWordInOperand(0) ==
+             context->get_feature_mgr()->GetExtInstImportId_GLSLstd450() &&
+         "Expecting a GLSLstd450 extended instruction.");
+
+  const analysis::Constant* x = constants[1];
+  const analysis::Constant* max_val = constants[3];
+
+  if (x == nullptr || max_val == nullptr) {
+    return nullptr;
+  }
+
+  const analysis::Constant* temp =
+      FoldFPBinaryOp(FoldMin, inst->type_id(), {x, max_val}, context);
+  if (temp == max_val) {
+    // We can assume that |min_val| is less than |max_val|.  Therefore, if the
+    // result of the max operation is |min_val|, we know the result of the min
+    // operation, even if |max_val| is not a constant.
+    return max_val;
+  }
+  return nullptr;
+}
+
 }  // namespace
 
 void ConstantFoldingRules::AddFoldingRules() {
@@ -937,6 +1145,36 @@
       feature_manager->GetExtInstImportId_GLSLstd450();
   if (ext_inst_glslstd450_id != 0) {
     ext_rules_[{ext_inst_glslstd450_id, GLSLstd450FMix}].push_back(FoldFMix());
+    ext_rules_[{ext_inst_glslstd450_id, GLSLstd450SMin}].push_back(
+        FoldFPBinaryOp(FoldMin));
+    ext_rules_[{ext_inst_glslstd450_id, GLSLstd450UMin}].push_back(
+        FoldFPBinaryOp(FoldMin));
+    ext_rules_[{ext_inst_glslstd450_id, GLSLstd450FMin}].push_back(
+        FoldFPBinaryOp(FoldMin));
+    ext_rules_[{ext_inst_glslstd450_id, GLSLstd450SMax}].push_back(
+        FoldFPBinaryOp(FoldMax));
+    ext_rules_[{ext_inst_glslstd450_id, GLSLstd450UMax}].push_back(
+        FoldFPBinaryOp(FoldMax));
+    ext_rules_[{ext_inst_glslstd450_id, GLSLstd450FMax}].push_back(
+        FoldFPBinaryOp(FoldMax));
+    ext_rules_[{ext_inst_glslstd450_id, GLSLstd450UClamp}].push_back(
+        FoldClamp1);
+    ext_rules_[{ext_inst_glslstd450_id, GLSLstd450UClamp}].push_back(
+        FoldClamp2);
+    ext_rules_[{ext_inst_glslstd450_id, GLSLstd450UClamp}].push_back(
+        FoldClamp3);
+    ext_rules_[{ext_inst_glslstd450_id, GLSLstd450SClamp}].push_back(
+        FoldClamp1);
+    ext_rules_[{ext_inst_glslstd450_id, GLSLstd450SClamp}].push_back(
+        FoldClamp2);
+    ext_rules_[{ext_inst_glslstd450_id, GLSLstd450SClamp}].push_back(
+        FoldClamp3);
+    ext_rules_[{ext_inst_glslstd450_id, GLSLstd450FClamp}].push_back(
+        FoldClamp1);
+    ext_rules_[{ext_inst_glslstd450_id, GLSLstd450FClamp}].push_back(
+        FoldClamp2);
+    ext_rules_[{ext_inst_glslstd450_id, GLSLstd450FClamp}].push_back(
+        FoldClamp3);
   }
 }
 }  // namespace opt
diff --git a/source/opt/constants.cpp b/source/opt/constants.cpp
index 5c1468b..0887ec2 100644
--- a/source/opt/constants.cpp
+++ b/source/opt/constants.cpp
@@ -389,6 +389,13 @@
   return cst ? RegisterConstant(std::move(cst)) : nullptr;
 }
 
+uint32_t ConstantManager::GetFloatConst(float val) {
+  Type* float_type = context()->get_type_mgr()->GetFloatType();
+  utils::FloatProxy<float> v(val);
+  const Constant* c = GetConstant(float_type, v.GetWords());
+  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 93d0847..5f2fdc7 100644
--- a/source/opt/constants.h
+++ b/source/opt/constants.h
@@ -626,6 +626,9 @@
     }
   }
 
+  // Returns the id of a 32-bit floating point constant with value |val|.
+  uint32_t GetFloatConst(float val);
+
  private:
   // Creates a Constant instance with the given type and a vector of constant
   // defining words. Returns a unique pointer to the created Constant instance
diff --git a/source/opt/convert_to_half_pass.cpp b/source/opt/convert_to_half_pass.cpp
new file mode 100644
index 0000000..4c02c73
--- /dev/null
+++ b/source/opt/convert_to_half_pass.cpp
@@ -0,0 +1,460 @@
+// Copyright (c) 2019 The Khronos Group Inc.
+// Copyright (c) 2019 Valve Corporation
+// Copyright (c) 2019 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 "convert_to_half_pass.h"
+
+#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 {
+
+bool ConvertToHalfPass::IsArithmetic(Instruction* inst) {
+  return target_ops_core_.count(inst->opcode()) != 0 ||
+         (inst->opcode() == SpvOpExtInst &&
+          inst->GetSingleWordInOperand(0) ==
+              context()->get_feature_mgr()->GetExtInstImportId_GLSLstd450() &&
+          target_ops_450_.count(inst->GetSingleWordInOperand(1)) != 0);
+}
+
+bool ConvertToHalfPass::IsFloat(Instruction* inst, uint32_t width) {
+  uint32_t ty_id = inst->type_id();
+  if (ty_id == 0) return false;
+  return Pass::IsFloat(ty_id, width);
+}
+
+bool ConvertToHalfPass::IsRelaxed(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)
+      return true;
+  return false;
+}
+
+analysis::Type* ConvertToHalfPass::FloatScalarType(uint32_t width) {
+  analysis::Float float_ty(width);
+  return context()->get_type_mgr()->GetRegisteredType(&float_ty);
+}
+
+analysis::Type* ConvertToHalfPass::FloatVectorType(uint32_t v_len,
+                                                   uint32_t width) {
+  analysis::Type* reg_float_ty = FloatScalarType(width);
+  analysis::Vector vec_ty(reg_float_ty, v_len);
+  return context()->get_type_mgr()->GetRegisteredType(&vec_ty);
+}
+
+analysis::Type* ConvertToHalfPass::FloatMatrixType(uint32_t v_cnt,
+                                                   uint32_t vty_id,
+                                                   uint32_t width) {
+  Instruction* vty_inst = get_def_use_mgr()->GetDef(vty_id);
+  uint32_t v_len = vty_inst->GetSingleWordInOperand(1);
+  analysis::Type* reg_vec_ty = FloatVectorType(v_len, width);
+  analysis::Matrix mat_ty(reg_vec_ty, v_cnt);
+  return context()->get_type_mgr()->GetRegisteredType(&mat_ty);
+}
+
+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)
+    reg_equiv_ty = FloatMatrixType(ty_inst->GetSingleWordInOperand(1),
+                                   ty_inst->GetSingleWordInOperand(0), width);
+  else if (ty_inst->opcode() == SpvOpTypeVector)
+    reg_equiv_ty = FloatVectorType(ty_inst->GetSingleWordInOperand(1), width);
+  else  // SpvOpTypeFloat
+    reg_equiv_ty = FloatScalarType(width);
+  return context()->get_type_mgr()->GetTypeInstruction(reg_equiv_ty);
+}
+
+void ConvertToHalfPass::GenConvert(uint32_t* val_idp, uint32_t width,
+                                   InstructionBuilder* builder) {
+  Instruction* val_inst = get_def_use_mgr()->GetDef(*val_idp);
+  uint32_t ty_id = val_inst->type_id();
+  uint32_t nty_id = EquivFloatTypeId(ty_id, width);
+  if (nty_id == ty_id) return;
+  Instruction* cvt_inst;
+  if (val_inst->opcode() == SpvOpUndef)
+    cvt_inst = builder->AddNullaryOp(nty_id, SpvOpUndef);
+  else
+    cvt_inst = builder->AddUnaryOp(nty_id, SpvOpFConvert, *val_idp);
+  *val_idp = cvt_inst->result_id();
+}
+
+bool ConvertToHalfPass::MatConvertCleanup(Instruction* inst) {
+  if (inst->opcode() != SpvOpFConvert) 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;
+  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);
+  uint32_t cty_id = vty_inst->GetSingleWordInOperand(0);
+  Instruction* cty_inst = get_def_use_mgr()->GetDef(cty_id);
+  InstructionBuilder builder(
+      context(), inst,
+      IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
+  // Convert each component vector, combine them with OpCompositeConstruct
+  // and replace original instruction.
+  uint32_t orig_width = (cty_inst->GetSingleWordInOperand(0) == 16) ? 32 : 16;
+  uint32_t orig_mat_id = inst->GetSingleWordInOperand(0);
+  uint32_t orig_vty_id = EquivFloatTypeId(vty_id, orig_width);
+  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);
+    Instruction* cvt_inst =
+        builder.AddUnaryOp(vty_id, SpvOpFConvert, 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));
+  (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->SetResultType(EquivFloatTypeId(mty_id, orig_width));
+  get_def_use_mgr()->AnalyzeInstUse(inst);
+  return true;
+}
+
+void ConvertToHalfPass::RemoveRelaxedDecoration(uint32_t id) {
+  context()->get_decoration_mgr()->RemoveDecorationsFrom(
+      id, [](const Instruction& dec) {
+        if (dec.opcode() == SpvOpDecorate &&
+            dec.GetSingleWordInOperand(1u) == SpvDecorationRelaxedPrecision)
+          return true;
+        else
+          return false;
+      });
+}
+
+bool ConvertToHalfPass::GenHalfArith(Instruction* inst) {
+  bool modified = false;
+  // Convert all float32 based operands to float16 equivalent and change
+  // instruction type to float16 equivalent.
+  InstructionBuilder builder(
+      context(), inst,
+      IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
+  inst->ForEachInId([&builder, &modified, this](uint32_t* idp) {
+    Instruction* op_inst = get_def_use_mgr()->GetDef(*idp);
+    if (!IsFloat(op_inst, 32)) return;
+    GenConvert(idp, 16, &builder);
+    modified = true;
+  });
+  if (IsFloat(inst, 32)) {
+    inst->SetResultType(EquivFloatTypeId(inst->type_id(), 16));
+    modified = true;
+  }
+  if (modified) get_def_use_mgr()->AnalyzeInstUse(inst);
+  return modified;
+}
+
+bool ConvertToHalfPass::ProcessPhi(Instruction* inst) {
+  // Skip if not float32
+  if (!IsFloat(inst, 32)) return false;
+  // Skip if no relaxed operands.
+  bool relaxed_found = false;
+  uint32_t ocnt = 0;
+  inst->ForEachInId([&ocnt, &relaxed_found, this](uint32_t* idp) {
+    if (ocnt % 2 == 0) {
+      Instruction* val_inst = get_def_use_mgr()->GetDef(*idp);
+      if (IsRelaxed(val_inst)) relaxed_found = true;
+    }
+    ++ocnt;
+  });
+  if (!relaxed_found) return false;
+  // Add float16 converts of any float32 operands and change type
+  // of phi to float16 equivalent. Operand converts need to be added to
+  // preceeding blocks.
+  ocnt = 0;
+  uint32_t* prev_idp;
+  inst->ForEachInId([&ocnt, &prev_idp, this](uint32_t* idp) {
+    if (ocnt % 2 == 0) {
+      prev_idp = idp;
+    } else {
+      Instruction* val_inst = get_def_use_mgr()->GetDef(*prev_idp);
+      if (IsFloat(val_inst, 32)) {
+        BasicBlock* bp = context()->get_instr_block(*idp);
+        auto insert_before = bp->tail();
+        if (insert_before != bp->begin()) {
+          --insert_before;
+          if (insert_before->opcode() != SpvOpSelectionMerge &&
+              insert_before->opcode() != SpvOpLoopMerge)
+            ++insert_before;
+        }
+        InstructionBuilder builder(context(), &*insert_before,
+                                   IRContext::kAnalysisDefUse |
+                                       IRContext::kAnalysisInstrToBlockMapping);
+        GenConvert(prev_idp, 16, &builder);
+      }
+    }
+    ++ocnt;
+  });
+  inst->SetResultType(EquivFloatTypeId(inst->type_id(), 16));
+  get_def_use_mgr()->AnalyzeInstUse(inst);
+  return true;
+}
+
+bool ConvertToHalfPass::ProcessExtract(Instruction* inst) {
+  bool modified = false;
+  uint32_t comp_id = inst->GetSingleWordInOperand(0);
+  Instruction* comp_inst = get_def_use_mgr()->GetDef(comp_id);
+  // If extract is relaxed float32 based type and the composite is a relaxed
+  // float32 based type, convert it to float16 equivalent. This is slightly
+  // aggressive and pushes any likely conversion to apply to the whole
+  // composite rather than apply to each extracted component later. This
+  // can be a win if the platform can convert the entire composite in the same
+  // time as one component. It risks converting components that may not be
+  // used, although empirical data on a large set of real-world shaders seems
+  // to suggest this is not common and the composite convert is the best choice.
+  if (IsFloat(inst, 32) && IsRelaxed(inst) && IsFloat(comp_inst, 32) &&
+      IsRelaxed(comp_inst)) {
+    InstructionBuilder builder(
+        context(), inst,
+        IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
+    GenConvert(&comp_id, 16, &builder);
+    inst->SetInOperand(0, {comp_id});
+    comp_inst = get_def_use_mgr()->GetDef(comp_id);
+    modified = true;
+  }
+  // If the composite is a float16 based type, make sure the type of the
+  // extract agrees.
+  if (IsFloat(comp_inst, 16) && !IsFloat(inst, 16)) {
+    inst->SetResultType(EquivFloatTypeId(inst->type_id(), 16));
+    modified = true;
+  }
+  if (modified) get_def_use_mgr()->AnalyzeInstUse(inst);
+  return modified;
+}
+
+bool ConvertToHalfPass::ProcessConvert(Instruction* inst) {
+  // If float32 and relaxed, change to float16 convert
+  if (IsFloat(inst, 32) && IsRelaxed(inst)) {
+    inst->SetResultType(EquivFloatTypeId(inst->type_id(), 16));
+    get_def_use_mgr()->AnalyzeInstUse(inst);
+  }
+  // If operand and result types are the same, replace result with operand
+  // and change convert to copy to keep validator happy; DCE will clean it up
+  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()) {
+    context()->ReplaceAllUsesWith(inst->result_id(), val_id);
+    inst->SetOpcode(SpvOpCopyObject);
+  }
+  return true;  // modified
+}
+
+bool ConvertToHalfPass::ProcessImageRef(Instruction* inst) {
+  bool modified = false;
+  // If image reference, only need to convert dref args back to float32
+  if (dref_image_ops_.count(inst->opcode()) != 0) {
+    uint32_t dref_id = inst->GetSingleWordInOperand(kImageSampleDrefIdInIdx);
+    Instruction* dref_inst = get_def_use_mgr()->GetDef(dref_id);
+    if (IsFloat(dref_inst, 16) && IsRelaxed(dref_inst)) {
+      InstructionBuilder builder(
+          context(), inst,
+          IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
+      GenConvert(&dref_id, 32, &builder);
+      inst->SetInOperand(kImageSampleDrefIdInIdx, {dref_id});
+      get_def_use_mgr()->AnalyzeInstUse(inst);
+      modified = true;
+    }
+  }
+  return modified;
+}
+
+bool ConvertToHalfPass::ProcessDefault(Instruction* inst) {
+  bool modified = false;
+  // If non-relaxed instruction has changed operands, need to convert
+  // them back to float32
+  InstructionBuilder builder(
+      context(), inst,
+      IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
+  inst->ForEachInId([&builder, &modified, this](uint32_t* idp) {
+    Instruction* op_inst = get_def_use_mgr()->GetDef(*idp);
+    if (!IsFloat(op_inst, 16)) return;
+    if (!IsRelaxed(op_inst)) return;
+    uint32_t old_id = *idp;
+    GenConvert(idp, 32, &builder);
+    if (*idp != old_id) modified = true;
+  });
+  if (modified) get_def_use_mgr()->AnalyzeInstUse(inst);
+  return modified;
+}
+
+bool ConvertToHalfPass::GenHalfCode(Instruction* inst) {
+  bool modified = false;
+  // Remember id for later deletion of RelaxedPrecision decoration
+  bool inst_relaxed = IsRelaxed(inst);
+  if (inst_relaxed) relaxed_ids_.push_back(inst->result_id());
+  if (IsArithmetic(inst) && inst_relaxed)
+    modified = GenHalfArith(inst);
+  else if (inst->opcode() == SpvOpPhi)
+    modified = ProcessPhi(inst);
+  else if (inst->opcode() == SpvOpCompositeExtract)
+    modified = ProcessExtract(inst);
+  else if (inst->opcode() == SpvOpFConvert)
+    modified = ProcessConvert(inst);
+  else if (image_ops_.count(inst->opcode()) != 0)
+    modified = ProcessImageRef(inst);
+  else
+    modified = ProcessDefault(inst);
+  return modified;
+}
+
+bool ConvertToHalfPass::ProcessFunction(Function* func) {
+  bool modified = false;
+  cfg()->ForEachBlockInReversePostOrder(
+      func->entry().get(), [&modified, this](BasicBlock* bb) {
+        for (auto ii = bb->begin(); ii != bb->end(); ++ii)
+          modified |= GenHalfCode(&*ii);
+      });
+  cfg()->ForEachBlockInReversePostOrder(
+      func->entry().get(), [&modified, this](BasicBlock* bb) {
+        for (auto ii = bb->begin(); ii != bb->end(); ++ii)
+          modified |= MatConvertCleanup(&*ii);
+      });
+  return modified;
+}
+
+Pass::Status ConvertToHalfPass::ProcessImpl() {
+  Pass::ProcessFunction pfn = [this](Function* fp) {
+    return ProcessFunction(fp);
+  };
+  bool modified = context()->ProcessEntryPointCallTree(pfn);
+  // If modified, make sure module has Float16 capability
+  if (modified) context()->AddCapability(SpvCapabilityFloat16);
+  // Remove all RelaxedPrecision decorations from instructions and globals
+  for (auto c_id : relaxed_ids_) RemoveRelaxedDecoration(c_id);
+  for (auto& val : get_module()->types_values()) {
+    uint32_t v_id = val.result_id();
+    if (v_id != 0) RemoveRelaxedDecoration(v_id);
+  }
+  return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
+}
+
+Pass::Status ConvertToHalfPass::Process() {
+  Initialize();
+  return ProcessImpl();
+}
+
+void ConvertToHalfPass::Initialize() {
+  target_ops_core_ = {
+      SpvOpVectorExtractDynamic,
+      SpvOpVectorInsertDynamic,
+      SpvOpVectorShuffle,
+      SpvOpCompositeConstruct,
+      SpvOpCompositeInsert,
+      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,
+  };
+  target_ops_450_ = {
+      GLSLstd450Round, GLSLstd450RoundEven, GLSLstd450Trunc, GLSLstd450FAbs,
+      GLSLstd450FSign, 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,
+      // TODO(greg-lunarg): GLSLstd450ModfStruct,
+      GLSLstd450FMin, GLSLstd450FMax, GLSLstd450FClamp, GLSLstd450FMix,
+      GLSLstd450Step, GLSLstd450SmoothStep, GLSLstd450Fma,
+      // TODO(greg-lunarg): GLSLstd450FrexpStruct,
+      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};
+  dref_image_ops_ = {
+      SpvOpImageSampleDrefImplicitLod,
+      SpvOpImageSampleDrefExplicitLod,
+      SpvOpImageSampleProjDrefImplicitLod,
+      SpvOpImageSampleProjDrefExplicitLod,
+      SpvOpImageDrefGather,
+      SpvOpImageSparseSampleDrefImplicitLod,
+      SpvOpImageSparseSampleDrefExplicitLod,
+      SpvOpImageSparseSampleProjDrefImplicitLod,
+      SpvOpImageSparseSampleProjDrefExplicitLod,
+      SpvOpImageSparseDrefGather,
+  };
+  relaxed_ids_.clear();
+}
+
+}  // namespace opt
+}  // namespace spvtools
diff --git a/source/opt/convert_to_half_pass.h b/source/opt/convert_to_half_pass.h
new file mode 100644
index 0000000..5225848
--- /dev/null
+++ b/source/opt/convert_to_half_pass.h
@@ -0,0 +1,134 @@
+// Copyright (c) 2019 Valve Corporation
+// Copyright (c) 2019 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 LIBSPIRV_OPT_CONVERT_TO_HALF_PASS_H_
+#define LIBSPIRV_OPT_CONVERT_TO_HALF_PASS_H_
+
+#include "source/opt/ir_builder.h"
+#include "source/opt/pass.h"
+
+namespace spvtools {
+namespace opt {
+
+class ConvertToHalfPass : public Pass {
+ public:
+  ConvertToHalfPass() : Pass() {}
+
+  ~ConvertToHalfPass() override = default;
+
+  IRContext::Analysis GetPreservedAnalyses() override {
+    return IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping;
+  }
+
+  // See optimizer.hpp for pass user documentation.
+  Status Process() override;
+
+  const char* name() const override { return "convert-to-half-pass"; }
+
+ private:
+  // Return true if |inst| is an arithmetic op that can be of type float16
+  bool IsArithmetic(Instruction* inst);
+
+  // Return true if |inst| returns scalar, vector or matrix type with base
+  // float and |width|
+  bool IsFloat(Instruction* inst, uint32_t width);
+
+  // Return true if |inst| is decorated with RelaxedPrecision
+  bool IsRelaxed(Instruction* inst);
+
+  // Return type id for float with |width|
+  analysis::Type* FloatScalarType(uint32_t width);
+
+  // Return type id for vector of length |vlen| of float of |width|
+  analysis::Type* FloatVectorType(uint32_t v_len, uint32_t width);
+
+  // Return type id for matrix of |v_cnt| vectors of length identical to
+  // |vty_id| of float of |width|
+  analysis::Type* FloatMatrixType(uint32_t v_cnt, uint32_t vty_id,
+                                  uint32_t width);
+
+  // Return equivalent to float type |ty_id| with |width|
+  uint32_t EquivFloatTypeId(uint32_t ty_id, uint32_t width);
+
+  // Append instructions to builder to convert value |*val_idp| to type
+  // |ty_id| but with |width|. Set |*val_idp| to the new id.
+  void GenConvert(uint32_t* val_idp, uint32_t width,
+                  InstructionBuilder* builder);
+
+  // Remove RelaxedPrecision decoration of |id|.
+  void RemoveRelaxedDecoration(uint32_t id);
+
+  // If |inst| is an arithmetic, phi, extract or convert instruction of float32
+  // base type and decorated with RelaxedPrecision, change it to the equivalent
+  // float16 based type instruction. Specifically, insert instructions to
+  // convert all operands to float16 (if needed) and change its type to the
+  // equivalent float16 type. Otherwise, insert instructions to convert its
+  // operands back to their original types, if needed.
+  bool GenHalfCode(Instruction* inst);
+
+  // Gen code for relaxed arithmetic |inst|
+  bool GenHalfArith(Instruction* inst);
+
+  // Gen code for relaxed phi |inst|
+  bool ProcessPhi(Instruction* inst);
+
+  // Gen code for relaxed extract |inst|
+  bool ProcessExtract(Instruction* inst);
+
+  // Gen code for relaxed convert |inst|
+  bool ProcessConvert(Instruction* inst);
+
+  // Gen code for image reference |inst|
+  bool ProcessImageRef(Instruction* inst);
+
+  // Process default non-relaxed |inst|
+  bool ProcessDefault(Instruction* inst);
+
+  // If |inst| is an FConvert of a matrix type, decompose it to a series
+  // of vector extracts, converts and inserts into an Undef. These are
+  // generated by GenHalfCode because they are easier to manipulate, but are
+  // invalid so we need to clean them up.
+  bool MatConvertCleanup(Instruction* inst);
+
+  // Call GenHalfCode on every instruction in |func|.
+  // If code is generated for an instruction, replace the instruction
+  // with the new instructions that are generated.
+  bool ProcessFunction(Function* func);
+
+  Pass::Status ProcessImpl();
+
+  // Initialize state for converting to half
+  void Initialize();
+
+  // Set of core operations to be processed
+  std::unordered_set<uint32_t> 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 dref sample operations
+  std::unordered_set<uint32_t> dref_image_ops_;
+
+  // Ids of all converted instructions
+  std::vector<uint32_t> relaxed_ids_;
+};
+
+}  // namespace opt
+}  // namespace spvtools
+
+#endif  // LIBSPIRV_OPT_CONVERT_TO_HALF_PASS_H_
diff --git a/source/opt/copy_prop_arrays.cpp b/source/opt/copy_prop_arrays.cpp
index 00757e3..b3b90da 100644
--- a/source/opt/copy_prop_arrays.cpp
+++ b/source/opt/copy_prop_arrays.cpp
@@ -306,8 +306,7 @@
   analysis::ConstantManager* const_mgr = context()->get_constant_mgr();
   const analysis::Constant* last_access =
       const_mgr->FindDeclaredConstant(memory_object->AccessChain().back());
-  if (!last_access ||
-      (!last_access->AsIntConstant() && !last_access->AsNullConstant())) {
+  if (!last_access || !last_access->type()->AsInteger()) {
     return nullptr;
   }
 
@@ -340,7 +339,7 @@
 
     last_access =
         const_mgr->FindDeclaredConstant(member_object->AccessChain().back());
-    if (!last_access || !last_access->AsIntConstant()) {
+    if (!last_access || !last_access->type()->AsInteger()) {
       return nullptr;
     }
 
@@ -368,8 +367,7 @@
   } else if (const analysis::Array* array_type = result_type->AsArray()) {
     const analysis::Constant* length_const =
         const_mgr->FindDeclaredConstant(array_type->LengthId());
-    assert(length_const->AsIntConstant());
-    number_of_elements = length_const->AsIntConstant()->GetU32();
+    number_of_elements = length_const->GetU32();
   } else if (const analysis::Vector* vector_type = result_type->AsVector()) {
     number_of_elements = vector_type->element_count();
   } else if (const analysis::Matrix* matrix_type = result_type->AsMatrix()) {
@@ -401,7 +399,7 @@
 
   const analysis::Constant* last_access =
       const_mgr->FindDeclaredConstant(memory_object->AccessChain().back());
-  if (!last_access || !last_access->AsIntConstant()) {
+  if (!last_access || !last_access->type()->AsInteger()) {
     return nullptr;
   }
 
@@ -449,7 +447,7 @@
     const analysis::Constant* current_last_access =
         const_mgr->FindDeclaredConstant(
             current_memory_object->AccessChain().back());
-    if (!current_last_access || !current_last_access->AsIntConstant()) {
+    if (!current_last_access || !current_last_access->type()->AsInteger()) {
       return nullptr;
     }
 
@@ -513,7 +511,7 @@
           const analysis::Constant* index_const =
               const_mgr->FindDeclaredConstant(use->GetSingleWordInOperand(i));
           if (index_const) {
-            access_chain.push_back(index_const->AsIntConstant()->GetU32());
+            access_chain.push_back(index_const->GetU32());
           } else {
             // Variable index means the type is a type where every element
             // is the same type.  Use element 0 to get the type.
@@ -613,7 +611,7 @@
           const analysis::Constant* index_const =
               const_mgr->FindDeclaredConstant(use->GetSingleWordInOperand(i));
           if (index_const) {
-            access_chain.push_back(index_const->AsIntConstant()->GetU32());
+            access_chain.push_back(index_const->GetU32());
           } else {
             // Variable index means the type is an type where every element
             // is the same type.  Use element 0 to get the type.
@@ -749,8 +747,8 @@
     const analysis::Constant* length_const =
         context->get_constant_mgr()->FindDeclaredConstant(
             array_type->LengthId());
-    assert(length_const->AsIntConstant());
-    return length_const->AsIntConstant()->GetU32();
+    assert(length_const->type()->AsInteger());
+    return length_const->GetU32();
   } else if (const analysis::Vector* vector_type = type->AsVector()) {
     return vector_type->element_count();
   } else if (const analysis::Matrix* matrix_type = type->AsMatrix()) {
@@ -776,8 +774,7 @@
     if (!element_index_const) {
       access_indices.push_back(0);
     } else {
-      assert(element_index_const->AsIntConstant());
-      access_indices.push_back(element_index_const->AsIntConstant()->GetU32());
+      access_indices.push_back(element_index_const->GetU32());
     }
   }
   return access_indices;
diff --git a/source/opt/feature_manager.h b/source/opt/feature_manager.h
index 761a208..2fe3291 100644
--- a/source/opt/feature_manager.h
+++ b/source/opt/feature_manager.h
@@ -57,6 +57,9 @@
   // 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);
@@ -64,9 +67,6 @@
   // Analyzes |module| and records enabled capabilities.
   void AddCapabilities(Module* module);
 
-  // Analyzes |module| and records imported external instruction sets.
-  void AddExtInstImportIds(Module* module);
-
   // Auxiliary object for querying SPIR-V grammar facts.
   const AssemblyGrammar& grammar_;
 
diff --git a/source/opt/fold.cpp b/source/opt/fold.cpp
index 276e835..6550fb4 100644
--- a/source/opt/fold.cpp
+++ b/source/opt/fold.cpp
@@ -56,6 +56,10 @@
       return ~operand;
     case SpvOp::SpvOpLogicalNot:
       return !static_cast<bool>(operand);
+    case SpvOp::SpvOpUConvert:
+      return operand;
+    case SpvOp::SpvOpSConvert:
+      return operand;
     default:
       assert(false &&
              "Unsupported unary operation for OpSpecConstantOp instruction");
@@ -596,6 +600,8 @@
     case SpvOp::SpvOpSMod:
     case SpvOp::SpvOpSNegate:
     case SpvOp::SpvOpSRem:
+    case SpvOp::SpvOpSConvert:
+    case SpvOp::SpvOpUConvert:
     case SpvOp::SpvOpUDiv:
     case SpvOp::SpvOpUGreaterThan:
     case SpvOp::SpvOpUGreaterThanEqual:
@@ -646,6 +652,9 @@
     if (folded_const != nullptr) {
       Instruction* const_inst =
           const_mgr->GetDefiningInstruction(folded_const, inst->type_id());
+      if (const_inst == nullptr) {
+        return nullptr;
+      }
       assert(const_inst->type_id() == inst->type_id());
       // May be a new instruction that needs to be analysed.
       context_->UpdateDefUse(const_inst);
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 56d0137..8ab717e 100644
--- a/source/opt/fold_spec_constant_op_and_composite_pass.cpp
+++ b/source/opt/fold_spec_constant_op_and_composite_pass.cpp
@@ -316,6 +316,59 @@
   }
   return false;
 }
+
+// Encodes the integer |value| of in a word vector format appropriate for
+// representing this value as a operands for a constant definition. Performs
+// zero-extension/sign-extension/truncation when needed, based on the signess of
+// the given target type.
+//
+// Note: type |type| argument must be either Integer or Bool.
+utils::SmallVector<uint32_t, 2> EncodeIntegerAsWords(const analysis::Type& type,
+                                                     uint32_t value) {
+  const uint32_t all_ones = ~0;
+  uint32_t bit_width = 0;
+  uint32_t pad_value = 0;
+  bool result_type_signed = false;
+  if (auto* int_ty = type.AsInteger()) {
+    bit_width = int_ty->width();
+    result_type_signed = int_ty->IsSigned();
+    if (result_type_signed && static_cast<int32_t>(value) < 0) {
+      pad_value = all_ones;
+    }
+  } else if (type.AsBool()) {
+    bit_width = 1;
+  } else {
+    assert(false && "type must be Integer or Bool");
+  }
+
+  assert(bit_width > 0);
+  uint32_t first_word = value;
+  const uint32_t bits_per_word = 32;
+
+  // Truncate first_word if the |type| has width less than uint32.
+  if (bit_width < bits_per_word) {
+    const uint32_t num_high_bits_to_mask = bits_per_word - bit_width;
+    const bool is_negative_after_truncation =
+        result_type_signed &&
+        utils::IsBitAtPositionSet(first_word, bit_width - 1);
+
+    if (is_negative_after_truncation) {
+      // Truncate and sign-extend |first_word|. No padding words will be
+      // added and |pad_value| can be left as-is.
+      first_word = utils::SetHighBits(first_word, num_high_bits_to_mask);
+    } else {
+      first_word = utils::ClearHighBits(first_word, num_high_bits_to_mask);
+    }
+  }
+
+  utils::SmallVector<uint32_t, 2> words = {first_word};
+  for (uint32_t current_bit = bits_per_word; current_bit < bit_width;
+       current_bit += bits_per_word) {
+    words.push_back(pad_value);
+  }
+
+  return words;
+}
 }  // namespace
 
 Instruction* FoldSpecConstantOpAndCompositePass::DoComponentWiseOperation(
@@ -345,10 +398,10 @@
 
   if (result_type->AsInteger() || result_type->AsBool()) {
     // Scalar operation
-    uint32_t result_val =
+    const uint32_t result_val =
         context()->get_instruction_folder().FoldScalars(spec_opcode, operands);
-    auto result_const =
-        context()->get_constant_mgr()->GetConstant(result_type, {result_val});
+    auto result_const = context()->get_constant_mgr()->GetConstant(
+        result_type, EncodeIntegerAsWords(*result_type, result_val));
     return context()->get_constant_mgr()->BuildInstructionAndAddToModule(
         result_const, pos);
   } else if (result_type->AsVector()) {
@@ -360,9 +413,9 @@
         context()->get_instruction_folder().FoldVectors(spec_opcode, num_dims,
                                                         operands);
     std::vector<const analysis::Constant*> result_vector_components;
-    for (uint32_t r : result_vec) {
-      if (auto rc =
-              context()->get_constant_mgr()->GetConstant(element_type, {r})) {
+    for (const uint32_t r : result_vec) {
+      if (auto rc = context()->get_constant_mgr()->GetConstant(
+              element_type, EncodeIntegerAsWords(*element_type, r))) {
         result_vector_components.push_back(rc);
         if (!context()->get_constant_mgr()->BuildInstructionAndAddToModule(
                 rc, pos)) {
diff --git a/source/opt/folding_rules.cpp b/source/opt/folding_rules.cpp
index a125dda..de740ca 100644
--- a/source/opt/folding_rules.cpp
+++ b/source/opt/folding_rules.cpp
@@ -18,6 +18,7 @@
 #include <memory>
 #include <utility>
 
+#include "ir_builder.h"
 #include "source/latest_version_glsl_std_450_header.h"
 #include "source/opt/ir_context.h"
 
@@ -35,6 +36,45 @@
 const uint32_t kFMixAIdInIdx = 4;
 const uint32_t kStoreObjectInIdx = 1;
 
+// Some image instructions may contain an "image operands" argument.
+// Returns the operand index for the "image operands".
+// Returns -1 if the instruction does not have image operands.
+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:
+      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:
+      return inst->NumOperands() > 5 ? 3 : -1;
+    case SpvOpImageWrite:
+      return inst->NumOperands() > 3 ? 3 : -1;
+    default:
+      return -1;
+  }
+}
+
 // Returns the element width of |type|.
 uint32_t ElementWidth(const analysis::Type* type) {
   if (const analysis::Vector* vec_type = type->AsVector()) {
@@ -1239,6 +1279,117 @@
   };
 }
 
+// Helper function for MergeGenericAddSubArithmetic. If |addend| and
+// subtrahend of |sub| is the same, merge to copy of minuend of |sub|.
+bool MergeGenericAddendSub(uint32_t addend, uint32_t sub, Instruction* inst) {
+  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)
+    return false;
+  if (sub_inst->opcode() == SpvOpFSub &&
+      !sub_inst->IsFloatingPointFoldingAllowed())
+    return false;
+  if (addend != sub_inst->GetSingleWordInOperand(1)) return false;
+  inst->SetOpcode(SpvOpCopyObject);
+  inst->SetInOperands(
+      {{SPV_OPERAND_TYPE_ID, {sub_inst->GetSingleWordInOperand(0)}}});
+  context->UpdateDefUse(inst);
+  return true;
+}
+
+// Folds addition of a subtraction where the subtrahend is equal to the
+// other addend. Return a copy of the minuend. Accepts generic (const and
+// non-const) operands.
+// Cases:
+// (a - b) + b = a
+// b + (a - b) = a
+FoldingRule MergeGenericAddSubArithmetic() {
+  return [](IRContext* context, Instruction* inst,
+            const std::vector<const analysis::Constant*>&) {
+    assert(inst->opcode() == SpvOpFAdd || inst->opcode() == SpvOpIAdd);
+    const analysis::Type* type =
+        context->get_type_mgr()->GetType(inst->type_id());
+    bool uses_float = HasFloatingPoint(type);
+    if (uses_float && !inst->IsFloatingPointFoldingAllowed()) return false;
+
+    uint32_t width = ElementWidth(type);
+    if (width != 32 && width != 64) return false;
+
+    uint32_t add_op0 = inst->GetSingleWordInOperand(0);
+    uint32_t add_op1 = inst->GetSingleWordInOperand(1);
+    if (MergeGenericAddendSub(add_op0, add_op1, inst)) return true;
+    return MergeGenericAddendSub(add_op1, add_op0, inst);
+  };
+}
+
+// Helper function for FactorAddMuls. If |factor0_0| is the same as |factor1_0|,
+// generate |factor0_0| * (|factor0_1| + |factor1_1|).
+bool FactorAddMulsOpnds(uint32_t factor0_0, uint32_t factor0_1,
+                        uint32_t factor1_0, uint32_t factor1_1,
+                        Instruction* inst) {
+  IRContext* context = inst->context();
+  if (factor0_0 != factor1_0) return false;
+  InstructionBuilder ir_builder(
+      context, inst,
+      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->SetInOperands({{SPV_OPERAND_TYPE_ID, {factor0_0}},
+                       {SPV_OPERAND_TYPE_ID, {new_add_inst->result_id()}}});
+  context->UpdateDefUse(inst);
+  return true;
+}
+
+// Perform the following factoring identity, handling all operand order
+// combinations: (a * b) + (a * c) = a * (b + c)
+FoldingRule FactorAddMuls() {
+  return [](IRContext* context, Instruction* inst,
+            const std::vector<const analysis::Constant*>&) {
+    assert(inst->opcode() == SpvOpFAdd || inst->opcode() == SpvOpIAdd);
+    const analysis::Type* type =
+        context->get_type_mgr()->GetType(inst->type_id());
+    bool uses_float = HasFloatingPoint(type);
+    if (uses_float && !inst->IsFloatingPointFoldingAllowed()) return false;
+
+    analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
+    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)
+      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)
+      return false;
+
+    // Only perform this optimization if both of the muls only have one use.
+    // Otherwise this is a deoptimization in size and performance.
+    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 &&
+        (!add_op0_inst->IsFloatingPointFoldingAllowed() ||
+         !add_op1_inst->IsFloatingPointFoldingAllowed()))
+      return false;
+
+    for (int i = 0; i < 2; i++) {
+      for (int j = 0; j < 2; j++) {
+        // Check if operand i in add_op0_inst matches operand j in add_op1_inst.
+        if (FactorAddMulsOpnds(add_op0_inst->GetSingleWordInOperand(i),
+                               add_op0_inst->GetSingleWordInOperand(1 - i),
+                               add_op1_inst->GetSingleWordInOperand(j),
+                               add_op1_inst->GetSingleWordInOperand(1 - j),
+                               inst))
+          return true;
+      }
+    }
+    return false;
+  };
+}
+
 FoldingRule IntMultipleBy1() {
   return [](IRContext*, Instruction* inst,
             const std::vector<const analysis::Constant*>& constants) {
@@ -1274,6 +1425,12 @@
            "Wrong opcode.  Should be OpCompositeExtract.");
     analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
     analysis::TypeManager* type_mgr = context->get_type_mgr();
+
+    // If there are no index operands, then this rule cannot do anything.
+    if (inst->NumInOperands() <= 1) {
+      return false;
+    }
+
     uint32_t cid = inst->GetSingleWordInOperand(kExtractCompositeIdInIdx);
     Instruction* cinst = def_use_mgr->GetDef(cid);
 
@@ -2198,6 +2355,64 @@
   };
 }
 
+// If an image instruction's operand is a constant, updates the image operand
+// flag from Offset to ConstOffset.
+FoldingRule UpdateImageOperands() {
+  return [](IRContext*, Instruction* inst,
+            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) &&
+           "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) {
+        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)
+          offset_operand_index += 2;
+        assert(((image_operands & SpvImageOperandsConstOffsetMask) == 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;
+            inst->SetInOperand(operand_index, {image_operands});
+            return true;
+          }
+        }
+      }
+    }
+
+    return false;
+  };
+}
+
 }  // namespace
 
 void FoldingRules::AddFoldingRules() {
@@ -2220,6 +2435,8 @@
   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_[SpvOpFDiv].push_back(RedundantFDiv());
   rules_[SpvOpFDiv].push_back(ReciprocalFDiv());
@@ -2245,6 +2462,8 @@
   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_[SpvOpIMul].push_back(IntMultipleBy1());
   rules_[SpvOpIMul].push_back(MergeMulMulArithmetic());
@@ -2270,6 +2489,38 @@
 
   rules_[SpvOpVectorShuffle].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(
+      UpdateImageOperands());
+  rules_[SpvOpImageSparseSampleDrefExplicitLod].push_back(
+      UpdateImageOperands());
+  rules_[SpvOpImageSparseSampleProjImplicitLod].push_back(
+      UpdateImageOperands());
+  rules_[SpvOpImageSparseSampleProjExplicitLod].push_back(
+      UpdateImageOperands());
+  rules_[SpvOpImageSparseSampleProjDrefImplicitLod].push_back(
+      UpdateImageOperands());
+  rules_[SpvOpImageSparseSampleProjDrefExplicitLod].push_back(
+      UpdateImageOperands());
+  rules_[SpvOpImageSparseFetch].push_back(UpdateImageOperands());
+  rules_[SpvOpImageSparseGather].push_back(UpdateImageOperands());
+  rules_[SpvOpImageSparseDrefGather].push_back(UpdateImageOperands());
+  rules_[SpvOpImageSparseRead].push_back(UpdateImageOperands());
+
   FeatureManager* feature_manager = context_->get_feature_mgr();
   // Add rules for GLSLstd450
   uint32_t ext_inst_glslstd450_id =
diff --git a/source/opt/function.cpp b/source/opt/function.cpp
index 2520052..efda68b 100644
--- a/source/opt/function.cpp
+++ b/source/opt/function.cpp
@@ -13,13 +13,14 @@
 // limitations under the License.
 
 #include "source/opt/function.h"
-#include "function.h"
-#include "ir_context.h"
 
-#include <source/util/bit_vector.h>
 #include <ostream>
 #include <sstream>
 
+#include "function.h"
+#include "ir_context.h"
+#include "source/util/bit_vector.h"
+
 namespace spvtools {
 namespace opt {
 
diff --git a/source/opt/function.h b/source/opt/function.h
index b1317ad..3908568 100644
--- a/source/opt/function.h
+++ b/source/opt/function.h
@@ -104,19 +104,24 @@
     });
   }
 
-  // Runs the given function |f| on each instruction in this function, and
-  // optionally on debug line instructions that might precede them.
+  // Runs the given function |f| on instructions in this function, in order,
+  // and optionally on debug line instructions that might precede them.
   void ForEachInst(const std::function<void(Instruction*)>& f,
                    bool run_on_debug_line_insts = false);
   void ForEachInst(const std::function<void(const Instruction*)>& f,
                    bool run_on_debug_line_insts = false) const;
+  // Runs the given function |f| on instructions in this function, in order,
+  // and optionally on debug line instructions that might precede them.
+  // If |f| returns false, iteration is terminated and this function returns
+  // false.
   bool WhileEachInst(const std::function<bool(Instruction*)>& f,
                      bool run_on_debug_line_insts = false);
   bool WhileEachInst(const std::function<bool(const Instruction*)>& f,
                      bool run_on_debug_line_insts = false) const;
 
   // Runs the given function |f| on each parameter instruction in this function,
-  // and optionally on debug line instructions that might precede them.
+  // in order, and optionally on debug line instructions that might precede
+  // them.
   void ForEachParam(const std::function<void(const Instruction*)>& f,
                     bool run_on_debug_line_insts = false) const;
   void ForEachParam(const std::function<void(Instruction*)>& f,
diff --git a/source/opt/generate_webgpu_initializers_pass.cpp b/source/opt/generate_webgpu_initializers_pass.cpp
index 9334b43..eaed3c2 100644
--- a/source/opt/generate_webgpu_initializers_pass.cpp
+++ b/source/opt/generate_webgpu_initializers_pass.cpp
@@ -59,6 +59,8 @@
     changed = true;
 
     auto* constant_inst = GetNullConstantForVariable(inst);
+    if (!constant_inst) return Status::Failure;
+
     if (seen_null_constants_.find(constant_inst) ==
         seen_null_constants_.end()) {
       constant_inst->InsertBefore(inst);
@@ -78,6 +80,8 @@
 
       changed = true;
       auto* constant_inst = GetNullConstantForVariable(inst);
+      if (!constant_inst) return Status::Failure;
+
       AddNullInitializerToVariable(constant_inst, inst);
     }
   }
diff --git a/source/opt/graphics_robust_access_pass.cpp b/source/opt/graphics_robust_access_pass.cpp
index dd60e8c..e309d3a 100644
--- a/source/opt/graphics_robust_access_pass.cpp
+++ b/source/opt/graphics_robust_access_pass.cpp
@@ -160,7 +160,6 @@
 namespace spvtools {
 namespace opt {
 
-using opt::BasicBlock;
 using opt::Instruction;
 using opt::Operand;
 using spvtools::MakeUnique;
diff --git a/source/opt/graphics_robust_access_pass.h b/source/opt/graphics_robust_access_pass.h
index 215cbf1..b21154e 100644
--- a/source/opt/graphics_robust_access_pass.h
+++ b/source/opt/graphics_robust_access_pass.h
@@ -81,8 +81,8 @@
   // sign extension or zero extension.  The new instruction is inserted
   // immediately before |before_inst|, and is analyzed for definitions and uses.
   // Returns the newly inserted instruction.  Assumes the |value| is an integer
-  // scalar of a narrower type than |bitwidth| bits.
-  Instruction* WidenInteger(bool sign_extend, uint32_t bitwidth,
+  // scalar of a narrower type than |bit_width| bits.
+  Instruction* WidenInteger(bool sign_extend, uint32_t bit_width,
                             Instruction* value, Instruction* before_inst);
 
   // Returns a new instruction that invokes the UClamp GLSL.std.450 extended
@@ -109,7 +109,8 @@
   // the module is modified.  Returns a status code to indicate success
   // or failure.  If assumptions are not met, returns an error status code
   // and emits a diagnostic.
-  spv_result_t ClampCoordinateForImageTexelPointer(opt::Instruction* itp);
+  spv_result_t ClampCoordinateForImageTexelPointer(
+      opt::Instruction* image_texel_pointer);
 
   // Gets the instruction that defines the given id.
   opt::Instruction* GetDef(uint32_t id) {
diff --git a/source/opt/inline_pass.cpp b/source/opt/inline_pass.cpp
index f348bbe..36991ec 100644
--- a/source/opt/inline_pass.cpp
+++ b/source/opt/inline_pass.cpp
@@ -720,9 +720,24 @@
     return false;
   }
 
+  // Do not inline functions with an OpKill if they are called from a continue
+  // construct. If it is inlined into a continue construct it will generate
+  // invalid code.
+  bool func_is_called_from_continue =
+      funcs_called_from_continue_.count(func->result_id()) != 0;
+
+  if (func_is_called_from_continue && ContainsKill(func)) {
+    return false;
+  }
+
   return true;
 }
 
+bool InlinePass::ContainsKill(Function* func) const {
+  return !func->WhileEachInst(
+      [](Instruction* inst) { return inst->opcode() != SpvOpKill; });
+}
+
 void InlinePass::InitializeInline() {
   false_id_ = 0;
 
@@ -732,6 +747,8 @@
   inlinable_.clear();
   no_return_in_loop_.clear();
   early_return_funcs_.clear();
+  funcs_called_from_continue_ =
+      context()->GetStructuredCFGAnalysis()->FindFuncsCalledFromContinue();
 
   for (auto& fn : *get_module()) {
     // Initialize function and block maps.
diff --git a/source/opt/inline_pass.h b/source/opt/inline_pass.h
index ecfe964..bc5f781 100644
--- a/source/opt/inline_pass.h
+++ b/source/opt/inline_pass.h
@@ -139,6 +139,9 @@
   // Return true if |func| is a function that can be inlined.
   bool IsInlinableFunction(Function* func);
 
+  // Returns true if |func| contains an OpKill instruction.
+  bool ContainsKill(Function* func) const;
+
   // Update phis in succeeding blocks to point to new last block
   void UpdateSucceedingPhis(
       std::vector<std::unique_ptr<BasicBlock>>& new_blocks);
@@ -164,6 +167,10 @@
 
   // result id for OpConstantFalse
   uint32_t false_id_;
+
+  // Set of functions that are originally called directly or indirectly from a
+  // continue construct.
+  std::unordered_set<uint32_t> funcs_called_from_continue_;
 };
 
 }  // namespace opt
diff --git a/source/opt/inst_bindless_check_pass.cpp b/source/opt/inst_bindless_check_pass.cpp
index b283354..4587343 100644
--- a/source/opt/inst_bindless_check_pass.cpp
+++ b/source/opt/inst_bindless_check_pass.cpp
@@ -296,7 +296,7 @@
   // reference.
   if (new_ref_id != 0) {
     Instruction* phi_inst = builder.AddPhi(
-        ref_type_id, {new_ref_id, valid_blk_id, builder.GetNullId(ref_type_id),
+        ref_type_id, {new_ref_id, valid_blk_id, GetNullId(ref_type_id),
                       last_invalid_blk_id});
     context()->ReplaceAllUsesWith(ref->ref_inst->result_id(),
                                   phi_inst->result_id());
diff --git a/source/opt/inst_buff_addr_check_pass.cpp b/source/opt/inst_buff_addr_check_pass.cpp
index 03221ef..fa6c2c6 100644
--- a/source/opt/inst_buff_addr_check_pass.cpp
+++ b/source/opt/inst_buff_addr_check_pass.cpp
@@ -96,8 +96,22 @@
       uid2offset_[ref_inst->unique_id()], stage_idx,
       {error_id, lo_uptr_inst->result_id(), hi_uptr_inst->result_id()},
       &builder);
-  // Gen zero for invalid  reference
-  uint32_t ref_type_id = ref_inst->type_id();
+  // 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;
+  if (new_ref_id != 0) {
+    uint32_t ref_type_id = ref_inst->type_id();
+    analysis::TypeManager* type_mgr = context()->get_type_mgr();
+    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);
+      null_id = null_ptr_inst->result_id();
+    } else {
+      null_id = GetNullId(ref_type_id);
+    }
+  }
   (void)builder.AddBranch(merge_blk_id);
   new_blocks->push_back(std::move(new_blk_ptr));
   // Gen merge block
@@ -107,9 +121,9 @@
   // result id of the original reference with that of the Phi. Kill original
   // reference.
   if (new_ref_id != 0) {
-    Instruction* phi_inst = builder.AddPhi(
-        ref_type_id, {new_ref_id, valid_blk_id, builder.GetNullId(ref_type_id),
-                      invalid_blk_id});
+    Instruction* phi_inst =
+        builder.AddPhi(ref_inst->type_id(),
+                       {new_ref_id, valid_blk_id, null_id, invalid_blk_id});
     context()->ReplaceAllUsesWith(ref_inst->result_id(), phi_inst->result_id());
   }
   new_blocks->push_back(std::move(new_blk_ptr));
diff --git a/source/opt/instrument_pass.cpp b/source/opt/instrument_pass.cpp
index 246cdbb..2a3e7f0 100644
--- a/source/opt/instrument_pass.cpp
+++ b/source/opt/instrument_pass.cpp
@@ -325,29 +325,36 @@
     std::unordered_map<uint32_t, uint32_t>* same_blk_post,
     std::unordered_map<uint32_t, Instruction*>* same_blk_pre,
     BasicBlock* block_ptr) {
-  (*inst)->ForEachInId(
-      [&same_blk_post, &same_blk_pre, &block_ptr, this](uint32_t* iid) {
-        const auto map_itr = (*same_blk_post).find(*iid);
-        if (map_itr == (*same_blk_post).end()) {
-          const auto map_itr2 = (*same_blk_pre).find(*iid);
-          if (map_itr2 != (*same_blk_pre).end()) {
-            // Clone pre-call same-block ops, map result id.
-            const Instruction* in_inst = map_itr2->second;
-            std::unique_ptr<Instruction> sb_inst(in_inst->Clone(context()));
-            CloneSameBlockOps(&sb_inst, same_blk_post, same_blk_pre, block_ptr);
-            const uint32_t rid = sb_inst->result_id();
-            const uint32_t nid = this->TakeNextId();
-            get_decoration_mgr()->CloneDecorations(rid, nid);
-            sb_inst->SetResultId(nid);
-            (*same_blk_post)[rid] = nid;
-            *iid = nid;
-            block_ptr->AddInstruction(std::move(sb_inst));
-          }
-        } else {
-          // Reset same-block op operand.
-          *iid = map_itr->second;
-        }
-      });
+  bool changed = false;
+  (*inst)->ForEachInId([&same_blk_post, &same_blk_pre, &block_ptr, &changed,
+                        this](uint32_t* iid) {
+    const auto map_itr = (*same_blk_post).find(*iid);
+    if (map_itr == (*same_blk_post).end()) {
+      const auto map_itr2 = (*same_blk_pre).find(*iid);
+      if (map_itr2 != (*same_blk_pre).end()) {
+        // Clone pre-call same-block ops, map result id.
+        const Instruction* in_inst = map_itr2->second;
+        std::unique_ptr<Instruction> sb_inst(in_inst->Clone(context()));
+        const uint32_t rid = sb_inst->result_id();
+        const uint32_t nid = this->TakeNextId();
+        get_decoration_mgr()->CloneDecorations(rid, nid);
+        sb_inst->SetResultId(nid);
+        get_def_use_mgr()->AnalyzeInstDefUse(&*sb_inst);
+        (*same_blk_post)[rid] = nid;
+        *iid = nid;
+        changed = true;
+        CloneSameBlockOps(&sb_inst, same_blk_post, same_blk_pre, block_ptr);
+        block_ptr->AddInstruction(std::move(sb_inst));
+      }
+    } else {
+      // Reset same-block op operand if necessary
+      if (*iid != map_itr->second) {
+        *iid = map_itr->second;
+        changed = true;
+      }
+    }
+  });
+  if (changed) get_def_use_mgr()->AnalyzeInstUse(&**inst);
 }
 
 void InstrumentPass::UpdateSucceedingPhis(
diff --git a/source/opt/ir_builder.h b/source/opt/ir_builder.h
index a0ca40c..b0c1d2e 100644
--- a/source/opt/ir_builder.h
+++ b/source/opt/ir_builder.h
@@ -59,27 +59,43 @@
                            preserved_analyses) {}
 
   Instruction* AddNullaryOp(uint32_t type_id, SpvOp opcode) {
-    // TODO(1841): Handle id overflow.
-    std::unique_ptr<Instruction> newUnOp(new Instruction(
-        GetContext(), opcode, type_id,
-        opcode == SpvOpReturn ? 0 : GetContext()->TakeNextId(), {}));
-    return AddInstruction(std::move(newUnOp));
+    uint32_t result_id = 0;
+    if (type_id != 0) {
+      result_id = GetContext()->TakeNextId();
+      if (result_id == 0) {
+        return nullptr;
+      }
+    }
+    std::unique_ptr<Instruction> new_inst(
+        new Instruction(GetContext(), opcode, type_id, result_id, {}));
+    return AddInstruction(std::move(new_inst));
   }
 
   Instruction* AddUnaryOp(uint32_t type_id, SpvOp opcode, uint32_t operand1) {
-    // TODO(1841): Handle id overflow.
+    uint32_t result_id = 0;
+    if (type_id != 0) {
+      result_id = GetContext()->TakeNextId();
+      if (result_id == 0) {
+        return nullptr;
+      }
+    }
     std::unique_ptr<Instruction> newUnOp(new Instruction(
-        GetContext(), opcode, type_id, GetContext()->TakeNextId(),
+        GetContext(), opcode, type_id, result_id,
         {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand1}}}));
     return AddInstruction(std::move(newUnOp));
   }
 
   Instruction* AddBinaryOp(uint32_t type_id, SpvOp opcode, uint32_t operand1,
                            uint32_t operand2) {
-    // TODO(1841): Handle id overflow.
+    uint32_t result_id = 0;
+    if (type_id != 0) {
+      result_id = GetContext()->TakeNextId();
+      if (result_id == 0) {
+        return nullptr;
+      }
+    }
     std::unique_ptr<Instruction> newBinOp(new Instruction(
-        GetContext(), opcode, type_id,
-        opcode == SpvOpStore ? 0 : GetContext()->TakeNextId(),
+        GetContext(), opcode, type_id, opcode == SpvOpStore ? 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));
@@ -87,9 +103,15 @@
 
   Instruction* AddTernaryOp(uint32_t type_id, SpvOp opcode, uint32_t operand1,
                             uint32_t operand2, uint32_t operand3) {
-    // TODO(1841): Handle id overflow.
+    uint32_t result_id = 0;
+    if (type_id != 0) {
+      result_id = GetContext()->TakeNextId();
+      if (result_id == 0) {
+        return nullptr;
+      }
+    }
     std::unique_ptr<Instruction> newTernOp(new Instruction(
-        GetContext(), opcode, type_id, GetContext()->TakeNextId(),
+        GetContext(), opcode, type_id, result_id,
         {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand1}},
          {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand2}},
          {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand3}}}));
@@ -99,9 +121,15 @@
   Instruction* AddQuadOp(uint32_t type_id, SpvOp opcode, uint32_t operand1,
                          uint32_t operand2, uint32_t operand3,
                          uint32_t operand4) {
-    // TODO(1841): Handle id overflow.
+    uint32_t result_id = 0;
+    if (type_id != 0) {
+      result_id = GetContext()->TakeNextId();
+      if (result_id == 0) {
+        return nullptr;
+      }
+    }
     std::unique_ptr<Instruction> newQuadOp(new Instruction(
-        GetContext(), opcode, type_id, GetContext()->TakeNextId(),
+        GetContext(), opcode, type_id, result_id,
         {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand1}},
          {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand2}},
          {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand3}},
@@ -109,13 +137,19 @@
     return AddInstruction(std::move(newQuadOp));
   }
 
-  Instruction* AddIdLiteralOp(uint32_t type_id, SpvOp opcode, uint32_t operand1,
-                              uint32_t operand2) {
-    // TODO(1841): Handle id overflow.
+  Instruction* AddIdLiteralOp(uint32_t type_id, SpvOp opcode, uint32_t id,
+                              uint32_t uliteral) {
+    uint32_t result_id = 0;
+    if (type_id != 0) {
+      result_id = GetContext()->TakeNextId();
+      if (result_id == 0) {
+        return nullptr;
+      }
+    }
     std::unique_ptr<Instruction> newBinOp(new Instruction(
-        GetContext(), opcode, type_id, GetContext()->TakeNextId(),
-        {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand1}},
-         {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {operand2}}}));
+        GetContext(), opcode, type_id, result_id,
+        {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {id}},
+         {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {uliteral}}}));
     return AddInstruction(std::move(newBinOp));
   }
 
@@ -358,16 +392,6 @@
     return uint_inst->result_id();
   }
 
-  uint32_t GetNullId(uint32_t type_id) {
-    analysis::TypeManager* type_mgr = GetContext()->get_type_mgr();
-    analysis::ConstantManager* const_mgr = GetContext()->get_constant_mgr();
-    const analysis::Type* type = type_mgr->GetType(type_id);
-    const analysis::Constant* null_const = const_mgr->GetConstant(type, {});
-    Instruction* null_inst =
-        const_mgr->GetDefiningInstruction(null_const, type_id);
-    return null_inst->result_id();
-  }
-
   // Adds either a signed or unsigned 32 bit integer constant to the binary
   // depedning on the |sign|. If |sign| is true then the value is added as a
   // signed constant otherwise as an unsigned constant. If |sign| is false the
@@ -502,6 +526,27 @@
     return AddInstruction(std::move(new_inst));
   }
 
+  Instruction* AddNaryExtendedInstruction(
+      uint32_t result_type, uint32_t set, uint32_t instruction,
+      const std::vector<uint32_t>& ext_operands) {
+    std::vector<Operand> operands;
+    operands.push_back({SPV_OPERAND_TYPE_ID, {set}});
+    operands.push_back(
+        {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, {instruction}});
+    for (uint32_t id : ext_operands) {
+      operands.push_back({SPV_OPERAND_TYPE_ID, {id}});
+    }
+
+    uint32_t result_id = GetContext()->TakeNextId();
+    if (result_id == 0) {
+      return nullptr;
+    }
+
+    std::unique_ptr<Instruction> new_inst(new Instruction(
+        GetContext(), SpvOpExtInst, result_type, result_id, operands));
+    return AddInstruction(std::move(new_inst));
+  }
+
   // Inserts the new instruction before the insertion point.
   Instruction* AddInstruction(std::unique_ptr<Instruction>&& insn) {
     Instruction* insn_ptr = &*insert_before_.InsertBefore(std::move(insn));
diff --git a/source/opt/ir_context.cpp b/source/opt/ir_context.cpp
index 1c747b7..d940180 100644
--- a/source/opt/ir_context.cpp
+++ b/source/opt/ir_context.cpp
@@ -94,6 +94,14 @@
   if (analyses_to_invalidate & kAnalysisTypes) {
     analyses_to_invalidate |= kAnalysisConstants;
   }
+
+  // The dominator analysis hold the psuedo entry and exit nodes from the CFG.
+  // Also if the CFG change the dominators many changed as well, so the
+  // dominator analysis should be invalidated as well.
+  if (analyses_to_invalidate & kAnalysisCFG) {
+    analyses_to_invalidate |= kAnalysisDominatorAnalysis;
+  }
+
   if (analyses_to_invalidate & kAnalysisDefUse) {
     def_use_mgr_.reset(nullptr);
   }
diff --git a/source/opt/ir_context.h b/source/opt/ir_context.h
index e297fb1..45bf129 100644
--- a/source/opt/ir_context.h
+++ b/source/opt/ir_context.h
@@ -187,9 +187,6 @@
   inline IteratorRange<Module::inst_iterator> debugs3();
   inline IteratorRange<Module::const_inst_iterator> debugs3() const;
 
-  // Clears all debug instructions (excluding OpLine & OpNoLine).
-  inline void debug_clear();
-
   // Add |capability| to the module, if it is not already enabled.
   inline void AddCapability(SpvCapability capability);
 
@@ -199,6 +196,7 @@
   inline void AddExtension(const std::string& ext_name);
   inline void AddExtension(std::unique_ptr<Instruction>&& e);
   // Appends an extended instruction set instruction to this module.
+  inline void AddExtInstImport(const std::string& name);
   inline void AddExtInstImport(std::unique_ptr<Instruction>&& e);
   // Set the memory model for this module.
   inline void SetMemoryModel(std::unique_ptr<Instruction>&& m);
@@ -542,7 +540,7 @@
     return GetFunction(inst->result_id());
   }
 
-  // Add to |todo| all ids of functions called in |func|.
+  // Add to |todo| all ids of functions called directly from |func|.
   void AddCalls(const Function* func, std::queue<uint32_t>* todo);
 
   // Applies |pfn| to every function in the call trees that are rooted at the
@@ -927,8 +925,6 @@
   return ((const Module*)module_.get())->debugs3();
 }
 
-void IRContext::debug_clear() { module_->debug_clear(); }
-
 void IRContext::AddCapability(SpvCapability capability) {
   if (!get_feature_mgr()->HasCapability(capability)) {
     std::unique_ptr<Instruction> capability_inst(new Instruction(
@@ -971,9 +967,26 @@
   module()->AddExtension(std::move(e));
 }
 
+void IRContext::AddExtInstImport(const std::string& name) {
+  const auto num_chars = name.size();
+  // Compute num words, accommodate the terminating null character.
+  const auto num_words = (num_chars + 1 + 3) / 4;
+  std::vector<uint32_t> ext_words(num_words, 0u);
+  std::memcpy(ext_words.data(), name.data(), num_chars);
+  AddExtInstImport(std::unique_ptr<Instruction>(
+      new Instruction(this, SpvOpExtInstImport, 0u, TakeNextId(),
+                      {{SPV_OPERAND_TYPE_LITERAL_STRING, ext_words}})));
+}
+
 void IRContext::AddExtInstImport(std::unique_ptr<Instruction>&& e) {
   AddCombinatorsForExtension(e.get());
+  if (AreAnalysesValid(kAnalysisDefUse)) {
+    get_def_use_mgr()->AnalyzeInstDefUse(e.get());
+  }
   module()->AddExtInstImport(std::move(e));
+  if (feature_mgr_ != nullptr) {
+    feature_mgr_->AddExtInstImportIds(module());
+  }
 }
 
 void IRContext::SetMemoryModel(std::unique_ptr<Instruction>&& m) {
diff --git a/source/opt/ir_loader.cpp b/source/opt/ir_loader.cpp
index edd4832..c68b3e2 100644
--- a/source/opt/ir_loader.cpp
+++ b/source/opt/ir_loader.cpp
@@ -159,6 +159,9 @@
   for (auto& function : *module_) {
     for (auto& bb : function) bb.SetParent(&function);
   }
+
+  // Copy any trailing Op*Line instruction into the module
+  module_->SetTrailingDbgLineInfo(std::move(dbg_line_info_));
 }
 
 }  // namespace opt
diff --git a/source/opt/local_access_chain_convert_pass.cpp b/source/opt/local_access_chain_convert_pass.cpp
index 6eb130e..1921596 100644
--- a/source/opt/local_access_chain_convert_pass.cpp
+++ b/source/opt/local_access_chain_convert_pass.cpp
@@ -46,6 +46,10 @@
     const Instruction* ptrInst, uint32_t* varId, uint32_t* varPteTypeId,
     std::vector<std::unique_ptr<Instruction>>* newInsts) {
   const uint32_t ldResultId = TakeNextId();
+  if (ldResultId == 0) {
+    return 0;
+  }
+
   *varId = ptrInst->GetSingleWordInOperand(kAccessChainPtrIdInIdx);
   const Instruction* varInst = get_def_use_mgr()->GetDef(*varId);
   assert(varInst->opcode() == SpvOpVariable);
@@ -70,7 +74,7 @@
   });
 }
 
-void LocalAccessChainConvertPass::ReplaceAccessChainLoad(
+bool LocalAccessChainConvertPass::ReplaceAccessChainLoad(
     const Instruction* address_inst, Instruction* original_load) {
   // Build and append load of variable in ptrInst
   std::vector<std::unique_ptr<Instruction>> new_inst;
@@ -78,6 +82,10 @@
   uint32_t varPteTypeId;
   const uint32_t ldResultId =
       BuildAndAppendVarLoad(address_inst, &varId, &varPteTypeId, &new_inst);
+  if (ldResultId == 0) {
+    return false;
+  }
+
   context()->get_decoration_mgr()->CloneDecorations(
       original_load->result_id(), ldResultId, {SpvDecorationRelaxedPrecision});
   original_load->InsertBefore(std::move(new_inst));
@@ -95,9 +103,10 @@
   original_load->SetOpcode(SpvOpCompositeExtract);
   original_load->ReplaceOperands(new_operands);
   context()->UpdateDefUse(original_load);
+  return true;
 }
 
-void LocalAccessChainConvertPass::GenAccessChainStoreReplacement(
+bool LocalAccessChainConvertPass::GenAccessChainStoreReplacement(
     const Instruction* ptrInst, uint32_t valId,
     std::vector<std::unique_ptr<Instruction>>* newInsts) {
   // Build and append load of variable in ptrInst
@@ -105,11 +114,18 @@
   uint32_t varPteTypeId;
   const uint32_t ldResultId =
       BuildAndAppendVarLoad(ptrInst, &varId, &varPteTypeId, newInsts);
+  if (ldResultId == 0) {
+    return false;
+  }
+
   context()->get_decoration_mgr()->CloneDecorations(
       varId, ldResultId, {SpvDecorationRelaxedPrecision});
 
   // Build and append Insert
   const uint32_t insResultId = TakeNextId();
+  if (insResultId == 0) {
+    return false;
+  }
   std::vector<Operand> ins_in_opnds = {
       {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {valId}},
       {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {ldResultId}}};
@@ -125,6 +141,7 @@
                      {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {varId}},
                       {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {insResultId}}},
                      newInsts);
+  return true;
 }
 
 bool LocalAccessChainConvertPass::IsConstantIndexAccessChain(
@@ -198,7 +215,8 @@
   }
 }
 
-bool LocalAccessChainConvertPass::ConvertLocalAccessChains(Function* func) {
+Pass::Status LocalAccessChainConvertPass::ConvertLocalAccessChains(
+    Function* func) {
   FindTargetVars(func);
   // Replace access chains of all targeted variables with equivalent
   // extract and insert sequences
@@ -213,7 +231,9 @@
           if (!IsNonPtrAccessChain(ptrInst->opcode())) break;
           if (!IsTargetVar(varId)) break;
           std::vector<std::unique_ptr<Instruction>> newInsts;
-          ReplaceAccessChainLoad(ptrInst, &*ii);
+          if (!ReplaceAccessChainLoad(ptrInst, &*ii)) {
+            return Status::Failure;
+          }
           modified = true;
         } break;
         case SpvOpStore: {
@@ -223,7 +243,9 @@
           if (!IsTargetVar(varId)) break;
           std::vector<std::unique_ptr<Instruction>> newInsts;
           uint32_t valId = ii->GetSingleWordInOperand(kStoreValIdInIdx);
-          GenAccessChainStoreReplacement(ptrInst, valId, &newInsts);
+          if (!GenAccessChainStoreReplacement(ptrInst, valId, &newInsts)) {
+            return Status::Failure;
+          }
           dead_instructions.push_back(&*ii);
           ++ii;
           ii = ii.InsertBefore(std::move(newInsts));
@@ -248,7 +270,7 @@
       });
     }
   }
-  return modified;
+  return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange);
 }
 
 void LocalAccessChainConvertPass::Initialize() {
@@ -294,12 +316,16 @@
     if (ai.opcode() == SpvOpGroupDecorate) return Status::SuccessWithoutChange;
   // Do not process if any disallowed extensions are enabled
   if (!AllExtensionsSupported()) return Status::SuccessWithoutChange;
-  // Process all entry point functions.
-  ProcessFunction pfn = [this](Function* fp) {
-    return ConvertLocalAccessChains(fp);
-  };
-  bool modified = context()->ProcessEntryPointCallTree(pfn);
-  return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
+
+  // Process all functions in the module.
+  Status status = Status::SuccessWithoutChange;
+  for (Function& func : *get_module()) {
+    status = CombineStatus(status, ConvertLocalAccessChains(&func));
+    if (status == Status::Failure) {
+      break;
+    }
+  }
+  return status;
 }
 
 LocalAccessChainConvertPass::LocalAccessChainConvertPass() {}
diff --git a/source/opt/local_access_chain_convert_pass.h b/source/opt/local_access_chain_convert_pass.h
index 1c7e3d5..e3592bf 100644
--- a/source/opt/local_access_chain_convert_pass.h
+++ b/source/opt/local_access_chain_convert_pass.h
@@ -82,16 +82,16 @@
 
   // Create a load/insert/store equivalent to a store of
   // |valId| through (constant index) access chaing |ptrInst|.
-  // Append to |newInsts|.
-  void GenAccessChainStoreReplacement(
+  // Append to |newInsts|.  Returns true if successful.
+  bool GenAccessChainStoreReplacement(
       const Instruction* ptrInst, uint32_t valId,
       std::vector<std::unique_ptr<Instruction>>* newInsts);
 
   // For the (constant index) access chain |address_inst|, create an
   // equivalent load and extract that replaces |original_load|.  The result id
   // of the extract will be the same as the original result id of
-  // |original_load|.
-  void ReplaceAccessChainLoad(const Instruction* address_inst,
+  // |original_load|.  Returns true if successful.
+  bool ReplaceAccessChainLoad(const Instruction* address_inst,
                               Instruction* original_load);
 
   // Return true if all indices of access chain |acp| are OpConstant integers
@@ -106,7 +106,9 @@
   //
   // Nested access chains and pointer access chains are not currently
   // converted.
-  bool ConvertLocalAccessChains(Function* func);
+  //
+  // Returns a status to indicate success or failure, and change or no change.
+  Status ConvertLocalAccessChains(Function* func);
 
   // Initialize extensions whitelist
   void InitExtensions();
diff --git a/source/opt/local_ssa_elim_pass.cpp b/source/opt/local_ssa_elim_pass.cpp
deleted file mode 100644
index c3f4ab6..0000000
--- a/source/opt/local_ssa_elim_pass.cpp
+++ /dev/null
@@ -1,113 +0,0 @@
-// Copyright (c) 2017 The Khronos Group Inc.
-// Copyright (c) 2017 Valve Corporation
-// Copyright (c) 2017 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/local_ssa_elim_pass.h"
-
-#include "source/cfa.h"
-#include "source/opt/iterator.h"
-#include "source/opt/ssa_rewrite_pass.h"
-
-namespace spvtools {
-namespace opt {
-
-bool LocalMultiStoreElimPass::AllExtensionsSupported() const {
-  // If any extension not in whitelist, return false
-  for (auto& ei : get_module()->extensions()) {
-    const char* extName =
-        reinterpret_cast<const char*>(&ei.GetInOperand(0).words[0]);
-    if (extensions_whitelist_.find(extName) == extensions_whitelist_.end())
-      return false;
-  }
-  return true;
-}
-
-Pass::Status LocalMultiStoreElimPass::ProcessImpl() {
-  // Assumes relaxed logical addressing only (see instruction.h)
-  // TODO(greg-lunarg): Add support for physical addressing
-  if (context()->get_feature_mgr()->HasCapability(SpvCapabilityAddresses))
-    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;
-  // Do not process if any disallowed extensions are enabled
-  if (!AllExtensionsSupported()) return Status::SuccessWithoutChange;
-  // Process functions
-  ProcessFunction pfn = [this](Function* fp) {
-    return SSARewriter(this).RewriteFunctionIntoSSA(fp);
-  };
-  bool modified = context()->ProcessEntryPointCallTree(pfn);
-  return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
-}
-
-LocalMultiStoreElimPass::LocalMultiStoreElimPass() = default;
-
-Pass::Status LocalMultiStoreElimPass::Process() {
-  // Initialize extension whitelist
-  InitExtensions();
-  return ProcessImpl();
-}
-
-void LocalMultiStoreElimPass::InitExtensions() {
-  extensions_whitelist_.clear();
-  extensions_whitelist_.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_16bit_storage",
-      "SPV_KHR_device_group",
-      "SPV_KHR_multiview",
-      "SPV_NVX_multiview_per_view_attributes",
-      "SPV_NV_viewport_array2",
-      "SPV_NV_stereo_view_rendering",
-      "SPV_NV_sample_mask_override_coverage",
-      "SPV_NV_geometry_shader_passthrough",
-      "SPV_AMD_texture_gather_bias_lod",
-      "SPV_KHR_storage_buffer_storage_class",
-      "SPV_KHR_variable_pointers",
-      "SPV_AMD_gpu_shader_int16",
-      "SPV_KHR_post_depth_coverage",
-      "SPV_KHR_shader_atomic_counter_ops",
-      "SPV_EXT_shader_stencil_export",
-      "SPV_EXT_shader_viewport_index_layer",
-      "SPV_AMD_shader_image_load_store_lod",
-      "SPV_AMD_shader_fragment_mask",
-      "SPV_EXT_fragment_fully_covered",
-      "SPV_AMD_gpu_shader_half_float_fetch",
-      "SPV_GOOGLE_decorate_string",
-      "SPV_GOOGLE_hlsl_functionality1",
-      "SPV_GOOGLE_user_type",
-      "SPV_NV_shader_subgroup_partitioned",
-      "SPV_EXT_descriptor_indexing",
-      "SPV_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_EXT_fragment_invocation_density",
-      "SPV_EXT_physical_storage_buffer",
-  });
-}
-
-}  // namespace opt
-}  // namespace spvtools
diff --git a/source/opt/local_ssa_elim_pass.h b/source/opt/local_ssa_elim_pass.h
deleted file mode 100644
index de80d5a..0000000
--- a/source/opt/local_ssa_elim_pass.h
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright (c) 2017 The Khronos Group Inc.
-// Copyright (c) 2017 Valve Corporation
-// Copyright (c) 2017 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_LOCAL_SSA_ELIM_PASS_H_
-#define SOURCE_OPT_LOCAL_SSA_ELIM_PASS_H_
-
-#include <algorithm>
-#include <map>
-#include <queue>
-#include <string>
-#include <unordered_map>
-#include <unordered_set>
-#include <utility>
-#include <vector>
-
-#include "source/opt/basic_block.h"
-#include "source/opt/def_use_manager.h"
-#include "source/opt/mem_pass.h"
-#include "source/opt/module.h"
-
-namespace spvtools {
-namespace opt {
-
-// See optimizer.hpp for documentation.
-class LocalMultiStoreElimPass : public MemPass {
-  using cbb_ptr = const BasicBlock*;
-
- public:
-  using GetBlocksFunction =
-      std::function<std::vector<BasicBlock*>*(const BasicBlock*)>;
-
-  LocalMultiStoreElimPass();
-
-  const char* name() const override { return "eliminate-local-multi-store"; }
-  Status Process() override;
-
-  IRContext::Analysis GetPreservedAnalyses() override {
-    return IRContext::kAnalysisDefUse |
-           IRContext::kAnalysisInstrToBlockMapping |
-           IRContext::kAnalysisConstants | IRContext::kAnalysisTypes;
-  }
-
- private:
-  // Initialize extensions whitelist
-  void InitExtensions();
-
-  // Return true if all extensions in this module are allowed by this pass.
-  bool AllExtensionsSupported() const;
-
-  Pass::Status ProcessImpl();
-
-  // Extensions supported by this pass.
-  std::unordered_set<std::string> extensions_whitelist_;
-};
-
-}  // namespace opt
-}  // namespace spvtools
-
-#endif  // SOURCE_OPT_LOCAL_SSA_ELIM_PASS_H_
diff --git a/source/opt/mem_pass.cpp b/source/opt/mem_pass.cpp
index cc09767..04e2e8a 100644
--- a/source/opt/mem_pass.cpp
+++ b/source/opt/mem_pass.cpp
@@ -233,6 +233,10 @@
   const auto uitr = type2undefs_.find(type_id);
   if (uitr != type2undefs_.end()) return uitr->second;
   const uint32_t undefId = TakeNextId();
+  if (undefId == 0) {
+    return 0;
+  }
+
   std::unique_ptr<Instruction> undef_inst(
       new Instruction(context(), SpvOpUndef, type_id, undefId, {}));
   get_def_use_mgr()->AnalyzeInstDefUse(&*undef_inst);
diff --git a/source/opt/mem_pass.h b/source/opt/mem_pass.h
index 67ce26b..dcc16b6 100644
--- a/source/opt/mem_pass.h
+++ b/source/opt/mem_pass.h
@@ -121,9 +121,10 @@
     return (op == SpvOpDecorate || op == SpvOpDecorateId);
   }
 
-  // Return undef in function for type. Create and insert an undef after the
-  // first non-variable in the function if it doesn't already exist. Add
-  // undef to function undef map.
+  // Return the id of an undef value with type |type_id|.  Create and insert an
+  // undef after the first non-variable in the function if it doesn't already
+  // exist. Add undef to function undef map.  Returns 0 of the value does not
+  // exist, and cannot be created.
   uint32_t Type2Undef(uint32_t type_id);
 
   // Cache of verified target vars
diff --git a/source/opt/module.cpp b/source/opt/module.cpp
index 04e4e97..c7fc247 100644
--- a/source/opt/module.cpp
+++ b/source/opt/module.cpp
@@ -121,6 +121,9 @@
     static_cast<const Function*>(i.get())->ForEachInst(f,
                                                        run_on_debug_line_insts);
   }
+  if (run_on_debug_line_insts) {
+    for (auto& i : trailing_dbg_line_info_) DELEGATE(i);
+  }
 #undef DELEGATE
 }
 
diff --git a/source/opt/module.h b/source/opt/module.h
index cf7c274..aefa2a5 100644
--- a/source/opt/module.h
+++ b/source/opt/module.h
@@ -192,22 +192,6 @@
   inline IteratorRange<inst_iterator> execution_modes();
   inline IteratorRange<const_inst_iterator> execution_modes() const;
 
-  // Clears all debug instructions (excluding OpLine & OpNoLine).
-  void debug_clear() {
-    debug1_clear();
-    debug2_clear();
-    debug3_clear();
-  }
-
-  // Clears all debug 1 instructions (excluding OpLine & OpNoLine).
-  void debug1_clear() { debugs1_.clear(); }
-
-  // Clears all debug 2 instructions (excluding OpLine & OpNoLine).
-  void debug2_clear() { debugs2_.clear(); }
-
-  // Clears all debug 3 instructions (excluding OpLine & OpNoLine).
-  void debug3_clear() { debugs3_.clear(); }
-
   // Iterators for annotation instructions contained in this module.
   inline inst_iterator annotation_begin();
   inline inst_iterator annotation_end();
@@ -261,6 +245,19 @@
   // Gets the associated context for this module
   IRContext* context() const { return context_; }
 
+  // Sets the trailing debug line info to |dbg_line_info|.
+  void SetTrailingDbgLineInfo(std::vector<Instruction>&& dbg_line_info) {
+    trailing_dbg_line_info_ = std::move(dbg_line_info);
+  }
+
+  std::vector<Instruction>& trailing_dbg_line_info() {
+    return trailing_dbg_line_info_;
+  }
+
+  const std::vector<Instruction>& trailing_dbg_line_info() const {
+    return trailing_dbg_line_info_;
+  }
+
  private:
   ModuleHeader header_;  // Module header
 
@@ -281,6 +278,10 @@
   // Type declarations, constants, and global variable declarations.
   InstructionList types_values_;
   std::vector<std::unique_ptr<Function>> functions_;
+
+  // If the module ends with Op*Line instruction, they will not be attached to
+  // any instruction.  We record them here, so they will not be lost.
+  std::vector<Instruction> trailing_dbg_line_info_;
 };
 
 // Pretty-prints |module| to |str|. Returns |str|.
diff --git a/source/opt/optimizer.cpp b/source/opt/optimizer.cpp
index 635b075..ece1abe 100644
--- a/source/opt/optimizer.cpp
+++ b/source/opt/optimizer.cpp
@@ -21,12 +21,12 @@
 #include <utility>
 #include <vector>
 
-#include <source/spirv_optimizer_options.h>
 #include "source/opt/build_module.h"
 #include "source/opt/graphics_robust_access_pass.h"
 #include "source/opt/log.h"
 #include "source/opt/pass_manager.h"
 #include "source/opt/passes.h"
+#include "source/spirv_optimizer_options.h"
 #include "source/util/make_unique.h"
 #include "source/util/string_utils.h"
 
@@ -415,6 +415,10 @@
   } else if (pass_name == "inst-buff-addr-check") {
     RegisterPass(CreateInstBuffAddrCheckPass(7, 23, 2));
     RegisterPass(CreateAggressiveDCEPass());
+  } else if (pass_name == "convert-relaxed-to-half") {
+    RegisterPass(CreateConvertRelaxedToHalfPass());
+  } else if (pass_name == "relax-float-ops") {
+    RegisterPass(CreateRelaxFloatOpsPass());
   } else if (pass_name == "simplify-instructions") {
     RegisterPass(CreateSimplificationPass());
   } else if (pass_name == "ssa-rewrite") {
@@ -556,20 +560,26 @@
     return false;
   }
 
-  optimized_binary->clear();
-  context->module()->ToBinary(optimized_binary, /* skip_nop = */ true);
-
 #ifndef NDEBUG
   if (status == opt::Pass::Status::SuccessWithoutChange) {
-    auto changed = optimized_binary->size() != original_binary_size ||
-                   memcmp(optimized_binary->data(), original_binary,
-                          original_binary_size) != 0;
-    assert(!changed &&
-           "Binary unexpectedly changed despite optimizer saying there was no "
-           "change");
+    std::vector<uint32_t> optimized_binary_with_nop;
+    context->module()->ToBinary(&optimized_binary_with_nop,
+                                /* skip_nop = */ false);
+    assert(optimized_binary_with_nop.size() == original_binary_size &&
+           "Binary size unexpectedly changed despite the optimizer saying "
+           "there was no change");
+    assert(memcmp(optimized_binary_with_nop.data(), original_binary,
+                  original_binary_size) == 0 &&
+           "Binary content unexpectedly changed despite the optimizer saying "
+           "there was no change");
   }
 #endif  // !NDEBUG
 
+  // Note that |original_binary| and |optimized_binary| may share the same
+  // buffer and the below will invalidate |original_binary|.
+  optimized_binary->clear();
+  context->module()->ToBinary(optimized_binary, /* skip_nop = */ true);
+
   return true;
 }
 
@@ -711,7 +721,7 @@
 
 Optimizer::PassToken CreateLocalMultiStoreElimPass() {
   return MakeUnique<Optimizer::PassToken::Impl>(
-      MakeUnique<opt::LocalMultiStoreElimPass>());
+      MakeUnique<opt::SSARewritePass>());
 }
 
 Optimizer::PassToken CreateAggressiveDCEPass() {
@@ -877,6 +887,16 @@
       MakeUnique<opt::InstBuffAddrCheckPass>(desc_set, shader_id, version));
 }
 
+Optimizer::PassToken CreateConvertRelaxedToHalfPass() {
+  return MakeUnique<Optimizer::PassToken::Impl>(
+      MakeUnique<opt::ConvertToHalfPass>());
+}
+
+Optimizer::PassToken CreateRelaxFloatOpsPass() {
+  return MakeUnique<Optimizer::PassToken::Impl>(
+      MakeUnique<opt::RelaxFloatOpsPass>());
+}
+
 Optimizer::PassToken CreateCodeSinkingPass() {
   return MakeUnique<Optimizer::PassToken::Impl>(
       MakeUnique<opt::CodeSinkingPass>());
diff --git a/source/opt/pass.cpp b/source/opt/pass.cpp
index f9e4a5d..09b78af 100644
--- a/source/opt/pass.cpp
+++ b/source/opt/pass.cpp
@@ -54,6 +54,36 @@
   return ptrTypeInst->GetSingleWordInOperand(kTypePointerTypeIdInIdx);
 }
 
+Instruction* Pass::GetBaseType(uint32_t ty_id) {
+  Instruction* ty_inst = get_def_use_mgr()->GetDef(ty_id);
+  if (ty_inst->opcode() == SpvOpTypeMatrix) {
+    uint32_t vty_id = ty_inst->GetSingleWordInOperand(0);
+    ty_inst = get_def_use_mgr()->GetDef(vty_id);
+  }
+  if (ty_inst->opcode() == SpvOpTypeVector) {
+    uint32_t cty_id = ty_inst->GetSingleWordInOperand(0);
+    ty_inst = get_def_use_mgr()->GetDef(cty_id);
+  }
+  return ty_inst;
+}
+
+bool Pass::IsFloat(uint32_t ty_id, uint32_t width) {
+  Instruction* ty_inst = GetBaseType(ty_id);
+  if (ty_inst->opcode() != SpvOpTypeFloat) return false;
+  return ty_inst->GetSingleWordInOperand(0) == width;
+}
+
+uint32_t Pass::GetNullId(uint32_t type_id) {
+  if (IsFloat(type_id, 16)) context()->AddCapability(SpvCapabilityFloat16);
+  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);
+  const analysis::Constant* null_const = const_mgr->GetConstant(type, {});
+  Instruction* null_inst =
+      const_mgr->GetDefiningInstruction(null_const, type_id);
+  return null_inst->result_id();
+}
+
 uint32_t Pass::GenerateCopy(Instruction* object_to_copy, uint32_t new_type_id,
                             Instruction* insertion_position) {
   analysis::TypeManager* type_mgr = context()->get_type_mgr();
diff --git a/source/opt/pass.h b/source/opt/pass.h
index 686e9fc..a8c9c4b 100644
--- a/source/opt/pass.h
+++ b/source/opt/pass.h
@@ -109,6 +109,16 @@
   // Return type id for |ptrInst|'s pointee
   uint32_t GetPointeeTypeId(const Instruction* ptrInst) const;
 
+  // Return base type of |ty_id| type
+  Instruction* GetBaseType(uint32_t ty_id);
+
+  // Return true if |inst| returns scalar, vector or matrix type with base
+  // float and |width|
+  bool IsFloat(uint32_t ty_id, uint32_t width);
+
+  // Return the id of OpConstantNull of type |type_id|. Create if necessary.
+  uint32_t GetNullId(uint32_t type_id);
+
  protected:
   // Constructs a new pass.
   //
diff --git a/source/opt/passes.h b/source/opt/passes.h
index d53af8f..1a3675c 100644
--- a/source/opt/passes.h
+++ b/source/opt/passes.h
@@ -25,6 +25,7 @@
 #include "source/opt/code_sink.h"
 #include "source/opt/combine_access_chains.h"
 #include "source/opt/compact_ids_pass.h"
+#include "source/opt/convert_to_half_pass.h"
 #include "source/opt/copy_prop_arrays.h"
 #include "source/opt/dead_branch_elim_pass.h"
 #include "source/opt/dead_insert_elim_pass.h"
@@ -51,7 +52,6 @@
 #include "source/opt/local_redundancy_elimination.h"
 #include "source/opt/local_single_block_elim_pass.h"
 #include "source/opt/local_single_store_elim_pass.h"
-#include "source/opt/local_ssa_elim_pass.h"
 #include "source/opt/loop_fission.h"
 #include "source/opt/loop_fusion_pass.h"
 #include "source/opt/loop_peeling.h"
@@ -63,6 +63,7 @@
 #include "source/opt/process_lines_pass.h"
 #include "source/opt/reduce_load_size.h"
 #include "source/opt/redundancy_elimination.h"
+#include "source/opt/relax_float_ops_pass.h"
 #include "source/opt/remove_duplicates_pass.h"
 #include "source/opt/replace_invalid_opc.h"
 #include "source/opt/scalar_replacement_pass.h"
diff --git a/source/opt/relax_float_ops_pass.cpp b/source/opt/relax_float_ops_pass.cpp
new file mode 100644
index 0000000..73f16dd
--- /dev/null
+++ b/source/opt/relax_float_ops_pass.cpp
@@ -0,0 +1,178 @@
+// Copyright (c) 2019 The Khronos Group Inc.
+// Copyright (c) 2019 Valve Corporation
+// Copyright (c) 2019 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 "relax_float_ops_pass.h"
+
+#include "source/opt/ir_builder.h"
+
+namespace spvtools {
+namespace opt {
+
+bool RelaxFloatOpsPass::IsRelaxable(Instruction* inst) {
+  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->GetSingleWordInOperand(0) ==
+              context()->get_feature_mgr()->GetExtInstImportId_GLSLstd450() &&
+          target_ops_450_.count(inst->GetSingleWordInOperand(1)) != 0);
+}
+
+bool RelaxFloatOpsPass::IsFloat32(Instruction* inst) {
+  uint32_t ty_id;
+  if (target_ops_core_f_opnd_.count(inst->opcode()) != 0) {
+    uint32_t opnd_id = inst->GetSingleWordInOperand(0);
+    Instruction* opnd_inst = get_def_use_mgr()->GetDef(opnd_id);
+    ty_id = opnd_inst->type_id();
+  } else {
+    ty_id = inst->type_id();
+    if (ty_id == 0) return false;
+  }
+  return IsFloat(ty_id, 32);
+}
+
+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)
+      return true;
+  return false;
+}
+
+bool RelaxFloatOpsPass::ProcessInst(Instruction* r_inst) {
+  uint32_t r_id = r_inst->result_id();
+  if (r_id == 0) return false;
+  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);
+  return true;
+}
+
+bool RelaxFloatOpsPass::ProcessFunction(Function* func) {
+  bool modified = false;
+  cfg()->ForEachBlockInReversePostOrder(
+      func->entry().get(), [&modified, this](BasicBlock* bb) {
+        for (auto ii = bb->begin(); ii != bb->end(); ++ii)
+          modified |= ProcessInst(&*ii);
+      });
+  return modified;
+}
+
+Pass::Status RelaxFloatOpsPass::ProcessImpl() {
+  Pass::ProcessFunction pfn = [this](Function* fp) {
+    return ProcessFunction(fp);
+  };
+  bool modified = context()->ProcessEntryPointCallTree(pfn);
+  return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
+}
+
+Pass::Status RelaxFloatOpsPass::Process() {
+  Initialize();
+  return ProcessImpl();
+}
+
+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,
+  };
+  target_ops_core_f_opnd_ = {
+      SpvOpFOrdEqual,
+      SpvOpFUnordEqual,
+      SpvOpFOrdNotEqual,
+      SpvOpFUnordNotEqual,
+      SpvOpFOrdLessThan,
+      SpvOpFUnordLessThan,
+      SpvOpFOrdGreaterThan,
+      SpvOpFUnordGreaterThan,
+      SpvOpFOrdLessThanEqual,
+      SpvOpFUnordLessThanEqual,
+      SpvOpFOrdGreaterThanEqual,
+      SpvOpFUnordGreaterThanEqual,
+  };
+  target_ops_450_ = {
+      GLSLstd450Round, GLSLstd450RoundEven, GLSLstd450Trunc, GLSLstd450FAbs,
+      GLSLstd450FSign, 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,
+      // TODO(greg-lunarg): GLSLstd450ModfStruct,
+      GLSLstd450FMin, GLSLstd450FMax, GLSLstd450FClamp, GLSLstd450FMix,
+      GLSLstd450Step, GLSLstd450SmoothStep, GLSLstd450Fma,
+      // TODO(greg-lunarg): GLSLstd450FrexpStruct,
+      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};
+}
+
+}  // namespace opt
+}  // namespace spvtools
diff --git a/source/opt/relax_float_ops_pass.h b/source/opt/relax_float_ops_pass.h
new file mode 100644
index 0000000..5ee3d73
--- /dev/null
+++ b/source/opt/relax_float_ops_pass.h
@@ -0,0 +1,80 @@
+// Copyright (c) 2019 Valve Corporation
+// Copyright (c) 2019 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 LIBSPIRV_OPT_RELAX_FLOAT_OPS_PASS_H_
+#define LIBSPIRV_OPT_RELAX_FLOAT_OPS_PASS_H_
+
+#include "source/opt/ir_builder.h"
+#include "source/opt/pass.h"
+
+namespace spvtools {
+namespace opt {
+
+class RelaxFloatOpsPass : public Pass {
+ public:
+  RelaxFloatOpsPass() : Pass() {}
+
+  ~RelaxFloatOpsPass() override = default;
+
+  IRContext::Analysis GetPreservedAnalyses() override {
+    return IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping;
+  }
+
+  // See optimizer.hpp for pass user documentation.
+  Status Process() override;
+
+  const char* name() const override { return "convert-to-half-pass"; }
+
+ private:
+  // Return true if |inst| can have the RelaxedPrecision decoration applied
+  // to it.
+  bool IsRelaxable(Instruction* inst);
+
+  // Return true if |inst| returns scalar, vector or matrix type with base
+  // float and width 32
+  bool IsFloat32(Instruction* inst);
+
+  // Return true if |r_id| is decorated with RelaxedPrecision
+  bool IsRelaxed(uint32_t r_id);
+
+  // If |inst| is an instruction of float32-based type and is not decorated
+  // RelaxedPrecision, add such a decoration to the module.
+  bool ProcessInst(Instruction* inst);
+
+  // Call ProcessInst on every instruction in |func|.
+  bool ProcessFunction(Function* func);
+
+  Pass::Status ProcessImpl();
+
+  // Initialize state for converting to half
+  void Initialize();
+
+  // Set of float result core operations to be processed
+  std::unordered_set<uint32_t> target_ops_core_f_rslt_;
+
+  // Set of float operand core operations to be processed
+  std::unordered_set<uint32_t> 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_;
+};
+
+}  // namespace opt
+}  // namespace spvtools
+
+#endif  // LIBSPIRV_OPT_RELAX_FLOAT_OPS_PASS_H_
diff --git a/source/opt/scalar_replacement_pass.cpp b/source/opt/scalar_replacement_pass.cpp
index d748e7f..36c0c0d 100644
--- a/source/opt/scalar_replacement_pass.cpp
+++ b/source/opt/scalar_replacement_pass.cpp
@@ -820,7 +820,8 @@
         // 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() != SpvOpCompositeExtract ||
+                  use2->NumInOperands() <= 1) {
                 return false;
               }
               t.push_back(use2->GetSingleWordInOperand(1));
diff --git a/source/opt/simplification_pass.cpp b/source/opt/simplification_pass.cpp
index 7b0887c..001f354 100644
--- a/source/opt/simplification_pass.cpp
+++ b/source/opt/simplification_pass.cpp
@@ -32,6 +32,18 @@
   return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange);
 }
 
+void SimplificationPass::AddNewOperands(
+    Instruction* folded_inst, std::unordered_set<Instruction*>* inst_seen,
+    std::vector<Instruction*>* work_list) {
+  analysis::DefUseManager* def_use_mgr = get_def_use_mgr();
+  folded_inst->ForEachInId(
+      [&inst_seen, &def_use_mgr, &work_list](uint32_t* iid) {
+        Instruction* iid_inst = def_use_mgr->GetDef(*iid);
+        if (!inst_seen->insert(iid_inst).second) return;
+        work_list->push_back(iid_inst);
+      });
+}
+
 bool SimplificationPass::SimplifyFunction(Function* function) {
   bool modified = false;
   // Phase 1: Traverse all instructions in dominance order.
@@ -44,13 +56,15 @@
   std::unordered_set<Instruction*> process_phis;
   std::unordered_set<Instruction*> inst_to_kill;
   std::unordered_set<Instruction*> in_work_list;
+  std::unordered_set<Instruction*> inst_seen;
   const InstructionFolder& folder = context()->get_instruction_folder();
 
   cfg()->ForEachBlockInReversePostOrder(
       function->entry().get(),
       [&modified, &process_phis, &work_list, &in_work_list, &inst_to_kill,
-       &folder, this](BasicBlock* bb) {
+       &folder, &inst_seen, this](BasicBlock* bb) {
         for (Instruction* inst = &*bb->begin(); inst; inst = inst->NextNode()) {
+          inst_seen.insert(inst);
           if (inst->opcode() == SpvOpPhi) {
             process_phis.insert(inst);
           }
@@ -70,6 +84,9 @@
                 work_list.push_back(use);
               }
             });
+
+            AddNewOperands(inst, &inst_seen, &work_list);
+
             if (inst->opcode() == SpvOpCopyObject) {
               context()->ReplaceAllUsesWithPredicate(
                   inst->result_id(), inst->GetSingleWordInOperand(0),
@@ -97,6 +114,7 @@
   for (size_t i = 0; i < work_list.size(); ++i) {
     Instruction* inst = work_list[i];
     in_work_list.erase(inst);
+    inst_seen.insert(inst);
 
     bool is_foldable_copy =
         inst->opcode() == SpvOpCopyObject &&
@@ -114,6 +132,8 @@
             }
           });
 
+      AddNewOperands(inst, &inst_seen, &work_list);
+
       if (inst->opcode() == SpvOpCopyObject) {
         context()->ReplaceAllUsesWithPredicate(
             inst->result_id(), inst->GetSingleWordInOperand(0),
diff --git a/source/opt/simplification_pass.h b/source/opt/simplification_pass.h
index bcb88bf..149874b 100644
--- a/source/opt/simplification_pass.h
+++ b/source/opt/simplification_pass.h
@@ -42,6 +42,14 @@
   // instruction in |function| until nothing else in the function can be
   // simplified.
   bool SimplifyFunction(Function* function);
+
+  // FactorAddMul can create |folded_inst| Mul of new Add. If Mul, push any Add
+  // operand not in |seen_inst| into |worklist|. This is heavily restricted to
+  // improve compile time but can be expanded for future simplifications which
+  // simiarly create new operations.
+  void AddNewOperands(Instruction* folded_inst,
+                      std::unordered_set<Instruction*>* inst_seen,
+                      std::vector<Instruction*>* work_list);
 };
 
 }  // namespace opt
diff --git a/source/opt/ssa_rewrite_pass.cpp b/source/opt/ssa_rewrite_pass.cpp
index 7144ca0..69c3a1f 100644
--- a/source/opt/ssa_rewrite_pass.cpp
+++ b/source/opt/ssa_rewrite_pass.cpp
@@ -274,6 +274,9 @@
   // of the CFG, the variable is not defined, so we use undef.
   if (val_id == 0) {
     val_id = pass_->GetUndefVal(var_id);
+    if (val_id == 0) {
+      return 0;
+    }
   }
 
   WriteVariable(var_id, bb, val_id);
@@ -313,12 +316,15 @@
   }
 }
 
-void SSARewriter::ProcessLoad(Instruction* inst, BasicBlock* bb) {
+bool SSARewriter::ProcessLoad(Instruction* inst, BasicBlock* bb) {
   uint32_t var_id = 0;
   (void)pass_->GetPtr(inst, &var_id);
   if (pass_->IsTargetVar(var_id)) {
     // Get the immediate reaching definition for |var_id|.
     uint32_t val_id = GetReachingDef(var_id, bb);
+    if (val_id == 0) {
+      return false;
+    }
 
     // Schedule a replacement for the result of this load instruction with
     // |val_id|. After all the rewriting decisions are made, every use of
@@ -337,6 +343,7 @@
               << " (replacement for %" << load_id << " is %" << val_id << ")\n";
 #endif
   }
+  return true;
 }
 
 void SSARewriter::PrintPhiCandidates() const {
@@ -356,7 +363,7 @@
   std::cerr << "\n";
 }
 
-void SSARewriter::GenerateSSAReplacements(BasicBlock* bb) {
+bool SSARewriter::GenerateSSAReplacements(BasicBlock* bb) {
 #if SSA_REWRITE_DEBUGGING_LEVEL > 1
   std::cerr << "Generating SSA replacements for block: " << bb->id() << "\n";
   std::cerr << bb->PrettyPrint(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES)
@@ -368,7 +375,9 @@
     if (opcode == SpvOpStore || opcode == SpvOpVariable) {
       ProcessStore(&inst, bb);
     } else if (inst.opcode() == SpvOpLoad) {
-      ProcessLoad(&inst, bb);
+      if (!ProcessLoad(&inst, bb)) {
+        return false;
+      }
     }
   }
 
@@ -381,6 +390,7 @@
   PrintReplacementTable();
   std::cerr << "\n\n";
 #endif
+  return true;
 }
 
 uint32_t SSARewriter::GetReplacement(std::pair<uint32_t, uint32_t> repl) {
@@ -560,7 +570,7 @@
   }
 }
 
-bool SSARewriter::RewriteFunctionIntoSSA(Function* fp) {
+Pass::Status SSARewriter::RewriteFunctionIntoSSA(Function* fp) {
 #if SSA_REWRITE_DEBUGGING_LEVEL > 0
   std::cerr << "Function before SSA rewrite:\n"
             << fp->PrettyPrint(0) << "\n\n\n";
@@ -571,9 +581,17 @@
 
   // Generate all the SSA replacements and Phi candidates. This will
   // generate incomplete and trivial Phis.
-  pass_->cfg()->ForEachBlockInReversePostOrder(
-      fp->entry().get(),
-      [this](BasicBlock* bb) { GenerateSSAReplacements(bb); });
+  bool succeeded = pass_->cfg()->WhileEachBlockInReversePostOrder(
+      fp->entry().get(), [this](BasicBlock* bb) {
+        if (!GenerateSSAReplacements(bb)) {
+          return false;
+        }
+        return true;
+      });
+
+  if (!succeeded) {
+    return Pass::Status::Failure;
+  }
 
   // Remove trivial Phis and add arguments to incomplete Phis.
   FinalizePhiCandidates();
@@ -586,16 +604,20 @@
             << fp->PrettyPrint(0) << "\n";
 #endif
 
-  return modified;
+  return modified ? Pass::Status::SuccessWithChange
+                  : Pass::Status::SuccessWithoutChange;
 }
 
 Pass::Status SSARewritePass::Process() {
-  bool modified = false;
+  Status status = Status::SuccessWithoutChange;
   for (auto& fn : *get_module()) {
-    modified |= SSARewriter(this).RewriteFunctionIntoSSA(&fn);
+    status =
+        CombineStatus(status, SSARewriter(this).RewriteFunctionIntoSSA(&fn));
+    if (status == Status::Failure) {
+      break;
+    }
   }
-  return modified ? Pass::Status::SuccessWithChange
-                  : Pass::Status::SuccessWithoutChange;
+  return status;
 }
 
 }  // namespace opt
diff --git a/source/opt/ssa_rewrite_pass.h b/source/opt/ssa_rewrite_pass.h
index fddbdaf..bbe89c8 100644
--- a/source/opt/ssa_rewrite_pass.h
+++ b/source/opt/ssa_rewrite_pass.h
@@ -46,9 +46,9 @@
   // entry point for the SSA rewrite algorithm.  SSA-target variables are
   // locally defined variables that meet the criteria set by IsSSATargetVar.
   //
-  // It returns true if function |fp| was modified.  Otherwise, it returns
-  // false.
-  bool RewriteFunctionIntoSSA(Function* fp);
+  // Returns whether the function was modified or not, and whether or not the
+  // rewrite was successful.
+  Pass::Status RewriteFunctionIntoSSA(Function* fp);
 
  private:
   class PhiCandidate {
@@ -128,8 +128,8 @@
 
   // Generates all the SSA rewriting decisions for basic block |bb|.  This
   // populates the Phi candidate table (|phi_candidate_|) and the load
-  // replacement table (|load_replacement_).
-  void GenerateSSAReplacements(BasicBlock* bb);
+  // replacement table (|load_replacement_).  Returns true if successful.
+  bool GenerateSSAReplacements(BasicBlock* bb);
 
   // Seals block |bb|.  Sealing a basic block means |bb| and all its
   // predecessors of |bb| have been scanned for loads/stores.
@@ -202,8 +202,8 @@
   // Processes the load operation |inst| in basic block |bb|. This extracts
   // the variable ID being stored into, determines whether the variable is an
   // SSA-target variable, and, if it is, it reads its reaching definition by
-  // calling |GetReachingDef|.
-  void ProcessLoad(Instruction* inst, BasicBlock* bb);
+  // calling |GetReachingDef|.  Returns true if successful.
+  bool ProcessLoad(Instruction* inst, BasicBlock* bb);
 
   // Reads the current definition for variable |var_id| in basic block |bb|.
   // If |var_id| is not defined in block |bb| it walks up the predecessors of
diff --git a/source/opt/strip_debug_info_pass.cpp b/source/opt/strip_debug_info_pass.cpp
index 5d9c5fe..9e7fad0 100644
--- a/source/opt/strip_debug_info_pass.cpp
+++ b/source/opt/strip_debug_info_pass.cpp
@@ -22,13 +22,34 @@
   bool modified = !context()->debugs1().empty() ||
                   !context()->debugs2().empty() ||
                   !context()->debugs3().empty();
-  context()->debug_clear();
+
+  std::vector<Instruction*> to_kill;
+  for (auto& dbg : context()->debugs1()) to_kill.push_back(&dbg);
+  for (auto& dbg : context()->debugs2()) to_kill.push_back(&dbg);
+  for (auto& dbg : context()->debugs3()) to_kill.push_back(&dbg);
+
+  // OpName must come first, since they may refer to other debug instructions.
+  // If they are after the instructions that refer to, then they will be killed
+  // 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)
+                return true;
+              return false;
+            });
+
+  for (auto* inst : to_kill) context()->KillInst(inst);
 
   context()->module()->ForEachInst([&modified](Instruction* inst) {
     modified |= !inst->dbg_line_insts().empty();
     inst->dbg_line_insts().clear();
   });
 
+  if (!get_module()->trailing_dbg_line_info().empty()) {
+    modified = true;
+    get_module()->trailing_dbg_line_info().clear();
+  }
+
   return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
 }
 
diff --git a/source/opt/struct_cfg_analysis.cpp b/source/opt/struct_cfg_analysis.cpp
index 152ded5..b16322c 100644
--- a/source/opt/struct_cfg_analysis.cpp
+++ b/source/opt/struct_cfg_analysis.cpp
@@ -45,6 +45,7 @@
   struct TraversalInfo {
     ConstructInfo cinfo;
     uint32_t merge_node;
+    uint32_t continue_node;
   };
 
   // Set up a stack to keep track of currently active constructs.
@@ -53,7 +54,9 @@
   state[0].cinfo.containing_construct = 0;
   state[0].cinfo.containing_loop = 0;
   state[0].cinfo.containing_switch = 0;
+  state[0].cinfo.in_continue = false;
   state[0].merge_node = 0;
+  state[0].continue_node = 0;
 
   for (BasicBlock* block : order) {
     if (context_->cfg()->IsPseudoEntryBlock(block) ||
@@ -65,6 +68,12 @@
       state.pop_back();
     }
 
+    // This works because the structured order is designed to keep the blocks in
+    // the continue construct between the continue header and the merge node.
+    if (block->id() == state.back().continue_node) {
+      state.back().cinfo.in_continue = true;
+    }
+
     bb_to_construct_.emplace(std::make_pair(block->id(), state.back().cinfo));
 
     if (Instruction* merge_inst = block->GetMergeInst()) {
@@ -76,8 +85,14 @@
       if (merge_inst->opcode() == SpvOpLoopMerge) {
         new_state.cinfo.containing_loop = block->id();
         new_state.cinfo.containing_switch = 0;
+        new_state.cinfo.in_continue = false;
+        new_state.continue_node =
+            merge_inst->GetSingleWordInOperand(kContinueNodeIndex);
       } else {
         new_state.cinfo.containing_loop = state.back().cinfo.containing_loop;
+        new_state.cinfo.in_continue = state.back().cinfo.in_continue;
+        new_state.continue_node = state.back().continue_node;
+
         if (merge_inst->NextNode()->opcode() == SpvOpSwitch) {
           new_state.cinfo.containing_switch = block->id();
         } else {
@@ -146,9 +161,59 @@
   return LoopContinueBlock(bb_id) == bb_id;
 }
 
+bool StructuredCFGAnalysis::IsInContainingLoopsContinueConstruct(
+    uint32_t bb_id) {
+  auto it = bb_to_construct_.find(bb_id);
+  if (it == bb_to_construct_.end()) {
+    return false;
+  }
+  return it->second.in_continue;
+}
+
+bool StructuredCFGAnalysis::IsInContinueConstruct(uint32_t bb_id) {
+  while (bb_id != 0) {
+    if (IsInContainingLoopsContinueConstruct(bb_id)) {
+      return true;
+    }
+    bb_id = ContainingLoop(bb_id);
+  }
+  return false;
+}
+
 bool StructuredCFGAnalysis::IsMergeBlock(uint32_t bb_id) {
   return merge_blocks_.Get(bb_id);
 }
 
+std::unordered_set<uint32_t>
+StructuredCFGAnalysis::FindFuncsCalledFromContinue() {
+  std::unordered_set<uint32_t> called_from_continue;
+  std::queue<uint32_t> funcs_to_process;
+
+  // First collect the functions that are called directly from a continue
+  // construct.
+  for (Function& func : *context_->module()) {
+    for (auto& bb : func) {
+      if (IsInContainingLoopsContinueConstruct(bb.id())) {
+        for (const Instruction& inst : bb) {
+          if (inst.opcode() == SpvOpFunctionCall) {
+            funcs_to_process.push(inst.GetSingleWordInOperand(0));
+          }
+        }
+      }
+    }
+  }
+
+  // Now collect all of the functions that are indirectly called as well.
+  while (!funcs_to_process.empty()) {
+    uint32_t func_id = funcs_to_process.front();
+    funcs_to_process.pop();
+    Function* func = context_->GetFunction(func_id);
+    if (called_from_continue.insert(func_id).second) {
+      context_->AddCalls(func, &funcs_to_process);
+    }
+  }
+  return called_from_continue;
+}
+
 }  // namespace opt
 }  // namespace spvtools
diff --git a/source/opt/struct_cfg_analysis.h b/source/opt/struct_cfg_analysis.h
index f25266a..dfae6d4 100644
--- a/source/opt/struct_cfg_analysis.h
+++ b/source/opt/struct_cfg_analysis.h
@@ -16,6 +16,7 @@
 #define SOURCE_OPT_STRUCT_CFG_ANALYSIS_H_
 
 #include <unordered_map>
+#include <unordered_set>
 
 #include "source/opt/function.h"
 #include "source/util/bit_vector.h"
@@ -88,21 +89,46 @@
   // if no such block exists.
   uint32_t SwitchMergeBlock(uint32_t bb_id);
 
+  // Returns true if |bb_id| is the continue block for a loop.
   bool IsContinueBlock(uint32_t bb_id);
+
+  // Returns true if |bb_id| is in the continue construct for its inner most
+  // containing loop.
+  bool IsInContainingLoopsContinueConstruct(uint32_t bb_id);
+
+  // Returns true if |bb_id| is in the continue construct for any loop in its
+  // function.
+  bool IsInContinueConstruct(uint32_t bb_id);
+
+  // Return true if |bb_id| is the merge block for a construct.
   bool IsMergeBlock(uint32_t bb_id);
 
+  // Returns the set of function ids that are called directly or indirectly from
+  // a continue construct.
+  std::unordered_set<uint32_t> FindFuncsCalledFromContinue();
+
  private:
   // Struct used to hold the information for a basic block.
   // |containing_construct| is the header for the innermost containing
   // construct, or 0 if no such construct exists.  It could be a selection
-  // construct or a loop construct. |containing_loop| is the innermost
-  // containing loop construct, or 0 if the basic bloc is not in a loop.  If the
-  // basic block is in a selection construct that is contained in a loop
-  // construct, then these two values will not be the same.
+  // construct or a loop construct.
+  //
+  // |containing_loop| is the innermost containing loop construct, or 0 if the
+  // basic bloc is not in a loop.  If the basic block is in a selection
+  // construct that is contained in a loop construct, then these two values will
+  // not be the same.
+  //
+  // |containing_switch| is the innermost contain selection construct with an
+  // |OpSwitch| for the branch, as long as there is not intervening loop.  This
+  // is used to identify the selection construct from which it can break.
+  //
+  // |in_continue| is true of the block is in the continue construct for its
+  // innermost containing loop.
   struct ConstructInfo {
     uint32_t containing_construct;
     uint32_t containing_loop;
     uint32_t containing_switch;
+    bool in_continue;
   };
 
   // Populates |bb_to_construct_| with the innermost containing merge and loop
diff --git a/source/opt/type_manager.cpp b/source/opt/type_manager.cpp
index d349481..166b828 100644
--- a/source/opt/type_manager.cpp
+++ b/source/opt/type_manager.cpp
@@ -409,6 +409,22 @@
                {static_cast<uint32_t>(
                    type->AsForwardPointer()->storage_class())}}});
       break;
+    case Type::kCooperativeMatrixNV: {
+      auto coop_mat = type->AsCooperativeMatrixNV();
+      uint32_t const component_type =
+          GetTypeInstruction(coop_mat->component_type());
+      if (component_type == 0) {
+        return 0;
+      }
+      typeInst = MakeUnique<Instruction>(
+          context(), SpvOpTypeCooperativeMatrixNV, 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()}}});
+      break;
+    }
     default:
       assert(false && "Unexpected type");
       break;
@@ -604,6 +620,14 @@
       }
       break;
     }
+    case Type::kCooperativeMatrixNV: {
+      const CooperativeMatrixNV* cm_type = type.AsCooperativeMatrixNV();
+      const Type* component_type = cm_type->component_type();
+      rebuilt_ty = MakeUnique<CooperativeMatrixNV>(
+          RebuildType(*component_type), cm_type->scope_id(), cm_type->rows_id(),
+          cm_type->columns_id());
+      break;
+    }
     default:
       assert(false && "Unhandled type");
       return nullptr;
@@ -832,6 +856,12 @@
     case SpvOpTypeAccelerationStructureNV:
       type = new AccelerationStructureNV();
       break;
+    case SpvOpTypeCooperativeMatrixNV:
+      type = new CooperativeMatrixNV(GetType(inst.GetSingleWordInOperand(0)),
+                                     inst.GetSingleWordInOperand(1),
+                                     inst.GetSingleWordInOperand(2),
+                                     inst.GetSingleWordInOperand(3));
+      break;
     default:
       SPIRV_UNIMPLEMENTED(consumer_, "unhandled type");
       break;
diff --git a/source/opt/types.cpp b/source/opt/types.cpp
index 4f7150f..17f8fe9 100644
--- a/source/opt/types.cpp
+++ b/source/opt/types.cpp
@@ -127,6 +127,7 @@
     DeclareKindCase(PipeStorage);
     DeclareKindCase(NamedBarrier);
     DeclareKindCase(AccelerationStructureNV);
+    DeclareKindCase(CooperativeMatrixNV);
 #undef DeclareKindCase
     default:
       assert(false && "Unhandled type");
@@ -171,6 +172,7 @@
     DeclareKindCase(PipeStorage);
     DeclareKindCase(NamedBarrier);
     DeclareKindCase(AccelerationStructureNV);
+    DeclareKindCase(CooperativeMatrixNV);
 #undef DeclareKindCase
     default:
       assert(false && "Unhandled type");
@@ -220,6 +222,7 @@
     DeclareKindCase(PipeStorage);
     DeclareKindCase(NamedBarrier);
     DeclareKindCase(AccelerationStructureNV);
+    DeclareKindCase(CooperativeMatrixNV);
 #undef DeclareKindCase
     default:
       assert(false && "Unhandled type");
@@ -654,6 +657,44 @@
   if (pointer_) pointer_->GetHashWords(words, seen);
 }
 
+CooperativeMatrixNV::CooperativeMatrixNV(const Type* type, const uint32_t scope,
+                                         const uint32_t rows,
+                                         const uint32_t columns)
+    : Type(kCooperativeMatrixNV),
+      component_type_(type),
+      scope_id_(scope),
+      rows_id_(rows),
+      columns_id_(columns) {
+  assert(type != nullptr);
+  assert(scope != 0);
+  assert(rows != 0);
+  assert(columns != 0);
+}
+
+std::string CooperativeMatrixNV::str() const {
+  std::ostringstream oss;
+  oss << "<" << component_type_->str() << ", " << scope_id_ << ", " << rows_id_
+      << ", " << columns_id_ << ">";
+  return oss.str();
+}
+
+void CooperativeMatrixNV::GetExtraHashWords(
+    std::vector<uint32_t>* words, std::unordered_set<const Type*>* pSet) const {
+  component_type_->GetHashWords(words, pSet);
+  words->push_back(scope_id_);
+  words->push_back(rows_id_);
+  words->push_back(columns_id_);
+}
+
+bool CooperativeMatrixNV::IsSameImpl(const Type* that,
+                                     IsSameCache* seen) const {
+  const CooperativeMatrixNV* mt = that->AsCooperativeMatrixNV();
+  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 57920df..69071ea 100644
--- a/source/opt/types.h
+++ b/source/opt/types.h
@@ -58,6 +58,7 @@
 class PipeStorage;
 class NamedBarrier;
 class AccelerationStructureNV;
+class CooperativeMatrixNV;
 
 // 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>.
@@ -93,6 +94,7 @@
     kPipeStorage,
     kNamedBarrier,
     kAccelerationStructureNV,
+    kCooperativeMatrixNV
   };
 
   Type(Kind k) : kind_(k) {}
@@ -196,6 +198,7 @@
   DeclareCastMethod(PipeStorage)
   DeclareCastMethod(NamedBarrier)
   DeclareCastMethod(AccelerationStructureNV)
+  DeclareCastMethod(CooperativeMatrixNV)
 #undef DeclareCastMethod
 
  protected:
@@ -597,6 +600,36 @@
   const Pointer* pointer_;
 };
 
+class CooperativeMatrixNV : public Type {
+ public:
+  CooperativeMatrixNV(const Type* type, const uint32_t scope,
+                      const uint32_t rows, const uint32_t columns);
+  CooperativeMatrixNV(const CooperativeMatrixNV&) = default;
+
+  std::string str() const override;
+
+  CooperativeMatrixNV* AsCooperativeMatrixNV() override { return this; }
+  const CooperativeMatrixNV* AsCooperativeMatrixNV() const override {
+    return this;
+  }
+
+  void GetExtraHashWords(std::vector<uint32_t>*,
+                         std::unordered_set<const Type*>*) 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_; }
+
+ 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_;
+};
+
 #define DefineParameterlessType(type, name)                                    \
   class type : public Type {                                                   \
    public:                                                                     \
diff --git a/source/opt/upgrade_memory_model.cpp b/source/opt/upgrade_memory_model.cpp
index f3bee9e..ab25205 100644
--- a/source/opt/upgrade_memory_model.cpp
+++ b/source/opt/upgrade_memory_model.cpp
@@ -429,7 +429,7 @@
     } else {
       assert(spvOpcodeIsComposite(element_inst->opcode()));
       element_inst = context()->get_def_use_mgr()->GetDef(
-          element_inst->GetSingleWordInOperand(1u));
+          element_inst->GetSingleWordInOperand(0u));
     }
   }
 
diff --git a/source/opt/wrap_opkill.cpp b/source/opt/wrap_opkill.cpp
index d10cdd2..ffd7a10 100644
--- a/source/opt/wrap_opkill.cpp
+++ b/source/opt/wrap_opkill.cpp
@@ -22,8 +22,11 @@
 Pass::Status WrapOpKill::Process() {
   bool modified = false;
 
-  for (auto& func : *get_module()) {
-    bool successful = func.WhileEachInst([this, &modified](Instruction* inst) {
+  auto func_to_process =
+      context()->GetStructuredCFGAnalysis()->FindFuncsCalledFromContinue();
+  for (uint32_t func_id : func_to_process) {
+    Function* func = context()->GetFunction(func_id);
+    bool successful = func->WhileEachInst([this, &modified](Instruction* inst) {
       if (inst->opcode() == SpvOpKill) {
         modified = true;
         if (!ReplaceWithFunctionCall(inst)) {
@@ -59,7 +62,24 @@
   if (ir_builder.AddFunctionCall(GetVoidTypeId(), func_id, {}) == nullptr) {
     return false;
   }
-  ir_builder.AddUnreachable();
+
+  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);
+    if (undef == nullptr) {
+      return false;
+    }
+    return_inst =
+        ir_builder.AddUnaryOp(0, SpvOpReturnValue, undef->result_id());
+  } else {
+    return_inst = ir_builder.AddNullaryOp(0, SpvOpReturn);
+  }
+
+  if (return_inst == nullptr) {
+    return false;
+  }
+
   context()->KillInst(inst);
   return true;
 }
@@ -95,9 +115,14 @@
     return 0;
   }
 
+  uint32_t void_type_id = GetVoidTypeId();
+  if (void_type_id == 0) {
+    return 0;
+  }
+
   // Generate the function start instruction
   std::unique_ptr<Instruction> func_start(new Instruction(
-      context(), SpvOpFunction, GetVoidTypeId(), opkill_func_id, {}));
+      context(), SpvOpFunction, void_type_id, opkill_func_id, {}));
   func_start->AddOperand({SPV_OPERAND_TYPE_FUNCTION_CONTROL, {0}});
   func_start->AddOperand({SPV_OPERAND_TYPE_ID, {GetVoidFunctionTypeId()}});
   opkill_function_.reset(new Function(std::move(func_start)));
@@ -142,5 +167,15 @@
   return opkill_function_->result_id();
 }
 
+uint32_t WrapOpKill::GetOwningFunctionsReturnType(Instruction* inst) {
+  BasicBlock* bb = context()->get_instr_block(inst);
+  if (bb == nullptr) {
+    return 0;
+  }
+
+  Function* func = bb->GetParent();
+  return func->type_id();
+}
+
 }  // namespace opt
 }  // namespace spvtools
diff --git a/source/opt/wrap_opkill.h b/source/opt/wrap_opkill.h
index 8b03281..87a5d69 100644
--- a/source/opt/wrap_opkill.h
+++ b/source/opt/wrap_opkill.h
@@ -56,6 +56,10 @@
   // function could not be generated.
   uint32_t GetOpKillFuncId();
 
+  // Returns the id of the return type for the function that contains |inst|.
+  // Returns 0 if |inst| is not in a function.
+  uint32_t GetOwningFunctionsReturnType(Instruction* inst);
+
   // The id of the void type.  If its value is 0, then the void type has not
   // been found or created yet.
   uint32_t void_type_id_;
diff --git a/source/print.cpp b/source/print.cpp
index f75e2d4..1d9829d 100644
--- a/source/print.cpp
+++ b/source/print.cpp
@@ -15,7 +15,7 @@
 #include "source/print.h"
 
 #if defined(SPIRV_ANDROID) || defined(SPIRV_LINUX) || defined(SPIRV_MAC) || \
-    defined(SPIRV_FREEBSD)
+    defined(SPIRV_IOS) || defined(SPIRV_FREEBSD) || defined(SPIRV_EMSCRIPTEN)
 namespace spvtools {
 
 clr::reset::operator const char*() { return "\x1b[0m"; }
diff --git a/source/reduce/CMakeLists.txt b/source/reduce/CMakeLists.txt
index 7651e86..51e9b1d 100644
--- a/source/reduce/CMakeLists.txt
+++ b/source/reduce/CMakeLists.txt
@@ -29,8 +29,6 @@
         remove_instruction_reduction_opportunity.h
         remove_function_reduction_opportunity.h
         remove_function_reduction_opportunity_finder.h
-        remove_opname_instruction_reduction_opportunity_finder.h
-        remove_relaxed_precision_decoration_opportunity_finder.h
         remove_selection_reduction_opportunity.h
         remove_selection_reduction_opportunity_finder.h
         remove_unreferenced_instruction_reduction_opportunity_finder.h
@@ -57,11 +55,9 @@
         remove_function_reduction_opportunity.cpp
         remove_function_reduction_opportunity_finder.cpp
         remove_instruction_reduction_opportunity.cpp
-        remove_relaxed_precision_decoration_opportunity_finder.cpp
         remove_selection_reduction_opportunity.cpp
         remove_selection_reduction_opportunity_finder.cpp
         remove_unreferenced_instruction_reduction_opportunity_finder.cpp
-        remove_opname_instruction_reduction_opportunity_finder.cpp
         structured_loop_to_selection_reduction_opportunity.cpp
         structured_loop_to_selection_reduction_opportunity_finder.cpp
         conditional_branch_to_simple_conditional_branch_opportunity_finder.cpp
@@ -81,8 +77,10 @@
 
 spvtools_default_compile_options(SPIRV-Tools-reduce)
 target_include_directories(SPIRV-Tools-reduce
-  PUBLIC ${spirv-tools_SOURCE_DIR}/include
-  PUBLIC ${SPIRV_HEADER_INCLUDE_DIR}
+  PUBLIC
+	$<BUILD_INTERFACE:${spirv-tools_SOURCE_DIR}/include>
+	$<BUILD_INTERFACE:${SPIRV_HEADER_INCLUDE_DIR}>
+	$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
   PRIVATE ${spirv-tools_BINARY_DIR}
 )
 # The reducer reuses a lot of functionality from the SPIRV-Tools library.
@@ -94,8 +92,16 @@
 spvtools_check_symbol_exports(SPIRV-Tools-reduce)
 
 if(ENABLE_SPIRV_TOOLS_INSTALL)
-  install(TARGETS SPIRV-Tools-reduce
+  install(TARGETS SPIRV-Tools-reduce EXPORT SPIRV-Tools-reduceTargets
     RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
     LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
     ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
+  export(EXPORT SPIRV-Tools-reduceTargets FILE SPIRV-Tools-reduceTarget.cmake)
+
+  spvtools_config_package_dir(SPIRV-Tools-reduce PACKAGE_DIR)
+  install(EXPORT SPIRV-Tools-reduceTargets FILE SPIRV-Tools-reduceTarget.cmake
+  	DESTINATION ${PACKAGE_DIR})
+
+  spvtools_generate_config_file(SPIRV-Tools-reduce)
+  install(FILES ${CMAKE_BINARY_DIR}/SPIRV-Tools-reduceConfig.cmake DESTINATION ${PACKAGE_DIR})
 endif(ENABLE_SPIRV_TOOLS_INSTALL)
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 2feca4a..0bd93b9 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
@@ -73,7 +73,7 @@
         result.push_back(
             MakeUnique<
                 ConditionalBranchToSimpleConditionalBranchReductionOpportunity>(
-                block.terminator(), redirect_to_true));
+                context, block.terminator(), redirect_to_true));
       }
     }
   }
diff --git a/source/reduce/conditional_branch_to_simple_conditional_branch_reduction_opportunity.cpp b/source/reduce/conditional_branch_to_simple_conditional_branch_reduction_opportunity.cpp
index 92c621e..d744773 100644
--- a/source/reduce/conditional_branch_to_simple_conditional_branch_reduction_opportunity.cpp
+++ b/source/reduce/conditional_branch_to_simple_conditional_branch_reduction_opportunity.cpp
@@ -19,12 +19,15 @@
 namespace spvtools {
 namespace reduce {
 
+using opt::IRContext;
 using opt::Instruction;
 
 ConditionalBranchToSimpleConditionalBranchReductionOpportunity::
     ConditionalBranchToSimpleConditionalBranchReductionOpportunity(
-        Instruction* conditional_branch_instruction, bool redirect_to_true)
-    : conditional_branch_instruction_(conditional_branch_instruction),
+        IRContext* context, Instruction* conditional_branch_instruction,
+        bool redirect_to_true)
+    : context_(context),
+      conditional_branch_instruction_(conditional_branch_instruction),
       redirect_to_true_(redirect_to_true) {}
 
 bool ConditionalBranchToSimpleConditionalBranchReductionOpportunity::
@@ -43,11 +46,24 @@
   uint32_t operand_to_copy =
       redirect_to_true_ ? kTrueBranchOperandIndex : kFalseBranchOperandIndex;
 
+  auto old_successor_block_id =
+      conditional_branch_instruction_->GetSingleWordInOperand(
+          operand_to_modify);
+
   // Do the branch redirection.
   conditional_branch_instruction_->SetInOperand(
       operand_to_modify,
       {conditional_branch_instruction_->GetSingleWordInOperand(
           operand_to_copy)});
+
+  // The old successor block may have phi instructions; these will need to
+  // respect the change in edges.
+  AdaptPhiInstructionsForRemovedEdge(
+      context_->get_instr_block(conditional_branch_instruction_)->id(),
+      context_->cfg()->block(old_successor_block_id));
+
+  // We have changed the CFG.
+  context_->InvalidateAnalysesExceptFor(IRContext::Analysis::kAnalysisNone);
 }
 
 }  // namespace reduce
diff --git a/source/reduce/conditional_branch_to_simple_conditional_branch_reduction_opportunity.h b/source/reduce/conditional_branch_to_simple_conditional_branch_reduction_opportunity.h
index 421906b..1f9cb6d 100644
--- a/source/reduce/conditional_branch_to_simple_conditional_branch_reduction_opportunity.h
+++ b/source/reduce/conditional_branch_to_simple_conditional_branch_reduction_opportunity.h
@@ -31,7 +31,8 @@
   // to the true target; otherwise, the true target will be changed to also
   // point to the false target.
   explicit ConditionalBranchToSimpleConditionalBranchReductionOpportunity(
-      opt::Instruction* conditional_branch_instruction, bool redirect_to_true);
+      opt::IRContext* context, opt::Instruction* conditional_branch_instruction,
+      bool redirect_to_true);
 
   bool PreconditionHolds() override;
 
@@ -39,6 +40,7 @@
   void Apply() override;
 
  private:
+  opt::IRContext* context_;
   opt::Instruction* conditional_branch_instruction_;
 
   // If true, the false target will be changed to point to the true target;
diff --git a/source/reduce/reducer.cpp b/source/reduce/reducer.cpp
index ebb5d47..bda41ce 100644
--- a/source/reduce/reducer.cpp
+++ b/source/reduce/reducer.cpp
@@ -24,8 +24,6 @@
 #include "source/reduce/operand_to_undef_reduction_opportunity_finder.h"
 #include "source/reduce/remove_block_reduction_opportunity_finder.h"
 #include "source/reduce/remove_function_reduction_opportunity_finder.h"
-#include "source/reduce/remove_opname_instruction_reduction_opportunity_finder.h"
-#include "source/reduce/remove_relaxed_precision_decoration_opportunity_finder.h"
 #include "source/reduce/remove_selection_reduction_opportunity_finder.h"
 #include "source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.h"
 #include "source/reduce/simple_conditional_branch_to_branch_opportunity_finder.h"
@@ -35,41 +33,32 @@
 namespace spvtools {
 namespace reduce {
 
-struct Reducer::Impl {
-  explicit Impl(spv_target_env env) : target_env(env) {}
-
-  bool ReachedStepLimit(uint32_t current_step,
-                        spv_const_reducer_options options);
-
-  const spv_target_env target_env;  // Target environment.
-  MessageConsumer consumer;         // Message consumer.
-  InterestingnessFunction interestingness_function;
-  std::vector<std::unique_ptr<ReductionPass>> passes;
-};
-
-Reducer::Reducer(spv_target_env env) : impl_(MakeUnique<Impl>(env)) {}
+Reducer::Reducer(spv_target_env target_env) : target_env_(target_env) {}
 
 Reducer::~Reducer() = default;
 
 void Reducer::SetMessageConsumer(MessageConsumer c) {
-  for (auto& pass : impl_->passes) {
+  for (auto& pass : passes_) {
     pass->SetMessageConsumer(c);
   }
-  impl_->consumer = std::move(c);
+  for (auto& pass : cleanup_passes_) {
+    pass->SetMessageConsumer(c);
+  }
+  consumer_ = std::move(c);
 }
 
 void Reducer::SetInterestingnessFunction(
     Reducer::InterestingnessFunction interestingness_function) {
-  impl_->interestingness_function = std::move(interestingness_function);
+  interestingness_function_ = std::move(interestingness_function);
 }
 
 Reducer::ReductionResultStatus Reducer::Run(
     std::vector<uint32_t>&& binary_in, std::vector<uint32_t>* binary_out,
     spv_const_reducer_options options,
-    spv_validator_options validator_options) const {
+    spv_validator_options validator_options) {
   std::vector<uint32_t> current_binary(std::move(binary_in));
 
-  spvtools::SpirvTools tools(impl_->target_env);
+  spvtools::SpirvTools tools(target_env_);
   assert(tools.IsValid() && "Failed to create SPIRV-Tools interface");
 
   // Keeps track of how many reduction attempts have been tried.  Reduction
@@ -79,105 +68,43 @@
   // Initial state should be valid.
   if (!tools.Validate(&current_binary[0], current_binary.size(),
                       validator_options)) {
-    impl_->consumer(SPV_MSG_INFO, nullptr, {},
-                    "Initial binary is invalid; stopping.");
+    consumer_(SPV_MSG_INFO, nullptr, {},
+              "Initial binary is invalid; stopping.");
     return Reducer::ReductionResultStatus::kInitialStateInvalid;
   }
 
   // Initial state should be interesting.
-  if (!impl_->interestingness_function(current_binary, reductions_applied)) {
-    impl_->consumer(SPV_MSG_INFO, nullptr, {},
-                    "Initial state was not interesting; stopping.");
+  if (!interestingness_function_(current_binary, reductions_applied)) {
+    consumer_(SPV_MSG_INFO, nullptr, {},
+              "Initial state was not interesting; stopping.");
     return Reducer::ReductionResultStatus::kInitialStateNotInteresting;
   }
 
-  // Determines whether, on completing one round of reduction passes, it is
-  // worthwhile trying a further round.
-  bool another_round_worthwhile = true;
+  Reducer::ReductionResultStatus result =
+      RunPasses(&passes_, options, validator_options, tools, &current_binary,
+                &reductions_applied);
 
-  // Apply round after round of reduction passes until we hit the reduction
-  // step limit, or deem that another round is not going to be worthwhile.
-  while (!impl_->ReachedStepLimit(reductions_applied, options) &&
-         another_round_worthwhile) {
-    // At the start of a round of reduction passes, assume another round will
-    // not be worthwhile unless we find evidence to the contrary.
-    another_round_worthwhile = false;
-
-    // Iterate through the available passes
-    for (auto& pass : impl_->passes) {
-      // If this pass hasn't reached its minimum granularity then it's
-      // worth eventually doing another round of reductions, in order to
-      // try this pass at a finer granularity.
-      another_round_worthwhile |= !pass->ReachedMinimumGranularity();
-
-      // Keep applying this pass at its current granularity until it stops
-      // working or we hit the reduction step limit.
-      impl_->consumer(SPV_MSG_INFO, nullptr, {},
-                      ("Trying pass " + pass->GetName() + ".").c_str());
-      do {
-        auto maybe_result = pass->TryApplyReduction(current_binary);
-        if (maybe_result.empty()) {
-          // For this round, the pass has no more opportunities (chunks) to
-          // apply, so move on to the next pass.
-          impl_->consumer(
-              SPV_MSG_INFO, nullptr, {},
-              ("Pass " + pass->GetName() + " did not make a reduction step.")
-                  .c_str());
-          break;
-        }
-        bool interesting = false;
-        std::stringstream stringstream;
-        reductions_applied++;
-        stringstream << "Pass " << pass->GetName() << " made reduction step "
-                     << reductions_applied << ".";
-        impl_->consumer(SPV_MSG_INFO, nullptr, {},
-                        (stringstream.str().c_str()));
-        if (!tools.Validate(&maybe_result[0], maybe_result.size(),
-                            validator_options)) {
-          // The reduction step went wrong and an invalid binary was produced.
-          // By design, this shouldn't happen; this is a safeguard to stop an
-          // invalid binary from being regarded as interesting.
-          impl_->consumer(SPV_MSG_INFO, nullptr, {},
-                          "Reduction step produced an invalid binary.");
-          if (options->fail_on_validation_error) {
-            return Reducer::ReductionResultStatus::kStateInvalid;
-          }
-        } else if (impl_->interestingness_function(maybe_result,
-                                                   reductions_applied)) {
-          // Success!  The binary produced by this reduction step is
-          // interesting, so make it the binary of interest henceforth, and
-          // note that it's worth doing another round of reduction passes.
-          impl_->consumer(SPV_MSG_INFO, nullptr, {},
-                          "Reduction step succeeded.");
-          current_binary = std::move(maybe_result);
-          interesting = true;
-          another_round_worthwhile = true;
-        }
-        // We must call this before the next call to TryApplyReduction.
-        pass->NotifyInteresting(interesting);
-        // Bail out if the reduction step limit has been reached.
-      } while (!impl_->ReachedStepLimit(reductions_applied, options));
-    }
+  if (result == Reducer::ReductionResultStatus::kComplete) {
+    // Cleanup passes.
+    result = RunPasses(&cleanup_passes_, options, validator_options, tools,
+                       &current_binary, &reductions_applied);
   }
 
+  if (result == Reducer::ReductionResultStatus::kComplete) {
+    consumer_(SPV_MSG_INFO, nullptr, {}, "No more to reduce; stopping.");
+  }
+
+  // Even if the reduction has failed by this point (e.g. due to producing an
+  // invalid binary), we still update the output binary for better debugging.
   *binary_out = std::move(current_binary);
 
-  // Report whether reduction completed, or bailed out early due to reaching
-  // the step limit.
-  if (impl_->ReachedStepLimit(reductions_applied, options)) {
-    impl_->consumer(SPV_MSG_INFO, nullptr, {},
-                    "Reached reduction step limit; stopping.");
-    return Reducer::ReductionResultStatus::kReachedStepLimit;
-  }
-  impl_->consumer(SPV_MSG_INFO, nullptr, {}, "No more to reduce; stopping.");
-  return Reducer::ReductionResultStatus::kComplete;
+  return result;
 }
 
 void Reducer::AddDefaultReductionPasses() {
-  AddReductionPass(spvtools::MakeUnique<
-                   RemoveOpNameInstructionReductionOpportunityFinder>());
-  AddReductionPass(spvtools::MakeUnique<
-                   RemoveRelaxedPrecisionDecorationOpportunityFinder>());
+  AddReductionPass(
+      spvtools::MakeUnique<
+          RemoveUnreferencedInstructionReductionOpportunityFinder>(false));
   AddReductionPass(
       spvtools::MakeUnique<OperandToUndefReductionOpportunityFinder>());
   AddReductionPass(
@@ -185,8 +112,6 @@
   AddReductionPass(
       spvtools::MakeUnique<OperandToDominatingIdReductionOpportunityFinder>());
   AddReductionPass(spvtools::MakeUnique<
-                   RemoveUnreferencedInstructionReductionOpportunityFinder>());
-  AddReductionPass(spvtools::MakeUnique<
                    StructuredLoopToSelectionReductionOpportunityFinder>());
   AddReductionPass(
       spvtools::MakeUnique<MergeBlocksReductionOpportunityFinder>());
@@ -201,18 +126,117 @@
           ConditionalBranchToSimpleConditionalBranchOpportunityFinder>());
   AddReductionPass(
       spvtools::MakeUnique<SimpleConditionalBranchToBranchOpportunityFinder>());
+
+  // Cleanup passes.
+
+  AddCleanupReductionPass(
+      spvtools::MakeUnique<
+          RemoveUnreferencedInstructionReductionOpportunityFinder>(true));
 }
 
 void Reducer::AddReductionPass(
     std::unique_ptr<ReductionOpportunityFinder>&& finder) {
-  impl_->passes.push_back(spvtools::MakeUnique<ReductionPass>(
-      impl_->target_env, std::move(finder)));
+  passes_.push_back(
+      spvtools::MakeUnique<ReductionPass>(target_env_, std::move(finder)));
 }
 
-bool Reducer::Impl::ReachedStepLimit(uint32_t current_step,
-                                     spv_const_reducer_options options) {
+void Reducer::AddCleanupReductionPass(
+    std::unique_ptr<ReductionOpportunityFinder>&& finder) {
+  cleanup_passes_.push_back(
+      spvtools::MakeUnique<ReductionPass>(target_env_, std::move(finder)));
+}
+
+bool Reducer::ReachedStepLimit(uint32_t current_step,
+                               spv_const_reducer_options options) {
   return current_step >= options->step_limit;
 }
 
+Reducer::ReductionResultStatus Reducer::RunPasses(
+    std::vector<std::unique_ptr<ReductionPass>>* passes,
+    spv_const_reducer_options options, spv_validator_options validator_options,
+    const SpirvTools& tools, std::vector<uint32_t>* current_binary,
+    uint32_t* const reductions_applied) {
+  // Determines whether, on completing one round of reduction passes, it is
+  // worthwhile trying a further round.
+  bool another_round_worthwhile = true;
+
+  // Apply round after round of reduction passes until we hit the reduction
+  // step limit, or deem that another round is not going to be worthwhile.
+  while (!ReachedStepLimit(*reductions_applied, options) &&
+         another_round_worthwhile) {
+    // At the start of a round of reduction passes, assume another round will
+    // not be worthwhile unless we find evidence to the contrary.
+    another_round_worthwhile = false;
+
+    // Iterate through the available passes.
+    for (auto& pass : *passes) {
+      // If this pass hasn't reached its minimum granularity then it's
+      // worth eventually doing another round of reductions, in order to
+      // try this pass at a finer granularity.
+      another_round_worthwhile |= !pass->ReachedMinimumGranularity();
+
+      // Keep applying this pass at its current granularity until it stops
+      // working or we hit the reduction step limit.
+      consumer_(SPV_MSG_INFO, nullptr, {},
+                ("Trying pass " + pass->GetName() + ".").c_str());
+      do {
+        auto maybe_result = pass->TryApplyReduction(*current_binary);
+        if (maybe_result.empty()) {
+          // For this round, the pass has no more opportunities (chunks) to
+          // apply, so move on to the next pass.
+          consumer_(
+              SPV_MSG_INFO, nullptr, {},
+              ("Pass " + pass->GetName() + " did not make a reduction step.")
+                  .c_str());
+          break;
+        }
+        bool interesting = false;
+        std::stringstream stringstream;
+        (*reductions_applied)++;
+        stringstream << "Pass " << pass->GetName() << " made reduction step "
+                     << *reductions_applied << ".";
+        consumer_(SPV_MSG_INFO, nullptr, {}, (stringstream.str().c_str()));
+        if (!tools.Validate(&maybe_result[0], maybe_result.size(),
+                            validator_options)) {
+          // The reduction step went wrong and an invalid binary was produced.
+          // By design, this shouldn't happen; this is a safeguard to stop an
+          // invalid binary from being regarded as interesting.
+          consumer_(SPV_MSG_INFO, nullptr, {},
+                    "Reduction step produced an invalid binary.");
+          if (options->fail_on_validation_error) {
+            // In this mode, we fail, so we update the current binary so it is
+            // output for debugging.
+            *current_binary = std::move(maybe_result);
+            return Reducer::ReductionResultStatus::kStateInvalid;
+          }
+        } else if (interestingness_function_(maybe_result,
+                                             *reductions_applied)) {
+          // Success!  The binary produced by this reduction step is
+          // interesting, so make it the binary of interest henceforth, and
+          // note that it's worth doing another round of reduction passes.
+          consumer_(SPV_MSG_INFO, nullptr, {}, "Reduction step succeeded.");
+          *current_binary = std::move(maybe_result);
+          interesting = true;
+          another_round_worthwhile = true;
+        }
+        // We must call this before the next call to TryApplyReduction.
+        pass->NotifyInteresting(interesting);
+        // Bail out if the reduction step limit has been reached.
+      } while (!ReachedStepLimit(*reductions_applied, options));
+    }
+  }
+
+  // Report whether reduction completed, or bailed out early due to reaching
+  // the step limit.
+  if (ReachedStepLimit(*reductions_applied, options)) {
+    consumer_(SPV_MSG_INFO, nullptr, {},
+              "Reached reduction step limit; stopping.");
+    return Reducer::ReductionResultStatus::kReachedStepLimit;
+  }
+
+  // The passes completed successfully, although we may still run more passes.
+  return Reducer::ReductionResultStatus::kComplete;
+}
+
 }  // namespace reduce
 }  // namespace spvtools
diff --git a/source/reduce/reducer.h b/source/reduce/reducer.h
index a9b28c3..864ce75 100644
--- a/source/reduce/reducer.h
+++ b/source/reduce/reducer.h
@@ -50,7 +50,7 @@
   using InterestingnessFunction =
       std::function<bool(const std::vector<uint32_t>&, uint32_t)>;
 
-  // Constructs an instance with the given target |env|, which is used to
+  // Constructs an instance with the given target |target_env|, which is used to
   // decode the binary to be reduced later.
   //
   // The constructed instance will have an empty message consumer, which just
@@ -59,7 +59,7 @@
   //
   // The constructed instance also needs to have an interestingness function
   // set and some reduction passes added to it in order to be useful.
-  explicit Reducer(spv_target_env env);
+  explicit Reducer(spv_target_env target_env);
 
   // Disables copy/move constructor/assignment operations.
   Reducer(const Reducer&) = delete;
@@ -86,17 +86,34 @@
   // that will be iterated over.
   void AddReductionPass(std::unique_ptr<ReductionOpportunityFinder>&& finder);
 
+  // Adds a cleanup reduction pass based on the given finder to the sequence of
+  // passes that will run after other passes.
+  void AddCleanupReductionPass(
+      std::unique_ptr<ReductionOpportunityFinder>&& finder);
+
   // Reduces the given SPIR-V module |binary_out|.
   // The reduced binary ends up in |binary_out|.
   // A status is returned.
   ReductionResultStatus Run(std::vector<uint32_t>&& binary_in,
                             std::vector<uint32_t>* binary_out,
                             spv_const_reducer_options options,
-                            spv_validator_options validator_options) const;
+                            spv_validator_options validator_options);
 
  private:
-  struct Impl;                  // Opaque struct for holding internal data.
-  std::unique_ptr<Impl> impl_;  // Unique pointer to internal data.
+  static bool ReachedStepLimit(uint32_t current_step,
+                               spv_const_reducer_options options);
+
+  ReductionResultStatus RunPasses(
+      std::vector<std::unique_ptr<ReductionPass>>* passes,
+      spv_const_reducer_options options,
+      spv_validator_options validator_options, const SpirvTools& tools,
+      std::vector<uint32_t>* current_binary, uint32_t* reductions_applied);
+
+  const spv_target_env target_env_;
+  MessageConsumer consumer_;
+  InterestingnessFunction interestingness_function_;
+  std::vector<std::unique_ptr<ReductionPass>> passes_;
+  std::vector<std::unique_ptr<ReductionPass>> cleanup_passes_;
 };
 
 }  // namespace reduce
diff --git a/source/reduce/reduction_util.cpp b/source/reduce/reduction_util.cpp
index 2b2b7e6..6f128dc 100644
--- a/source/reduce/reduction_util.cpp
+++ b/source/reduce/reduction_util.cpp
@@ -44,5 +44,22 @@
   return undef_id;
 }
 
+void AdaptPhiInstructionsForRemovedEdge(uint32_t from_id,
+                                        opt::BasicBlock* to_block) {
+  to_block->ForEachPhiInst([&from_id](Instruction* phi_inst) {
+    Instruction::OperandList new_in_operands;
+    // Go through the OpPhi's input operands in (variable, parent) pairs.
+    for (uint32_t index = 0; index < phi_inst->NumInOperands(); index += 2) {
+      // Keep all pairs where the parent is not the block from which the edge
+      // is being removed.
+      if (phi_inst->GetInOperand(index + 1).words[0] != from_id) {
+        new_in_operands.push_back(phi_inst->GetInOperand(index));
+        new_in_operands.push_back(phi_inst->GetInOperand(index + 1));
+      }
+    }
+    phi_inst->SetInOperands(std::move(new_in_operands));
+  });
+}
+
 }  // namespace reduce
 }  // namespace spvtools
diff --git a/source/reduce/reduction_util.h b/source/reduce/reduction_util.h
index b8ffb6e..7e7e153 100644
--- a/source/reduce/reduction_util.h
+++ b/source/reduce/reduction_util.h
@@ -30,6 +30,11 @@
 // adding one if it does not exist.
 uint32_t FindOrCreateGlobalUndef(opt::IRContext* context, uint32_t type_id);
 
+// Removes any components of |to_block|'s phi instructions relating to
+// |from_id|.
+void AdaptPhiInstructionsForRemovedEdge(uint32_t from_id,
+                                        opt::BasicBlock* to_block);
+
 }  // namespace reduce
 }  // namespace spvtools
 
diff --git a/source/reduce/remove_opname_instruction_reduction_opportunity_finder.cpp b/source/reduce/remove_opname_instruction_reduction_opportunity_finder.cpp
deleted file mode 100644
index f687d71..0000000
--- a/source/reduce/remove_opname_instruction_reduction_opportunity_finder.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright (c) 2018 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/reduce/remove_opname_instruction_reduction_opportunity_finder.h"
-
-#include "source/opcode.h"
-#include "source/opt/instruction.h"
-#include "source/reduce/remove_instruction_reduction_opportunity.h"
-
-namespace spvtools {
-namespace reduce {
-
-using opt::IRContext;
-
-std::vector<std::unique_ptr<ReductionOpportunity>>
-RemoveOpNameInstructionReductionOpportunityFinder::GetAvailableOpportunities(
-    IRContext* context) const {
-  std::vector<std::unique_ptr<ReductionOpportunity>> result;
-
-  for (auto& inst : context->module()->debugs2()) {
-    if (inst.opcode() == SpvOpName || inst.opcode() == SpvOpMemberName) {
-      result.push_back(
-          MakeUnique<RemoveInstructionReductionOpportunity>(&inst));
-    }
-  }
-  return result;
-}
-
-std::string RemoveOpNameInstructionReductionOpportunityFinder::GetName() const {
-  return "RemoveOpNameInstructionReductionOpportunityFinder";
-}
-
-}  // namespace reduce
-}  // namespace spvtools
diff --git a/source/reduce/remove_opname_instruction_reduction_opportunity_finder.h b/source/reduce/remove_opname_instruction_reduction_opportunity_finder.h
deleted file mode 100644
index 8b9fd6f..0000000
--- a/source/reduce/remove_opname_instruction_reduction_opportunity_finder.h
+++ /dev/null
@@ -1,45 +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.
-
-#ifndef SOURCE_REDUCE_REMOVE_OPNAME_INSTRUCTION_REDUCTION_OPPORTUNITY_FINDER_H_
-#define SOURCE_REDUCE_REMOVE_OPNAME_INSTRUCTION_REDUCTION_OPPORTUNITY_FINDER_H_
-
-#include "source/reduce/reduction_opportunity_finder.h"
-
-namespace spvtools {
-namespace reduce {
-
-// A finder for opportunities to remove OpName instructions.  As well as making
-// the module smaller, removing an OpName instruction may create opportunities
-// for subsequently removing the instructions that create the ids to which the
-// OpName applies.
-class RemoveOpNameInstructionReductionOpportunityFinder
-    : public ReductionOpportunityFinder {
- public:
-  RemoveOpNameInstructionReductionOpportunityFinder() = default;
-
-  ~RemoveOpNameInstructionReductionOpportunityFinder() override = default;
-
-  std::string GetName() const final;
-
-  std::vector<std::unique_ptr<ReductionOpportunity>> GetAvailableOpportunities(
-      opt::IRContext* context) const final;
-
- private:
-};
-
-}  // namespace reduce
-}  // namespace spvtools
-
-#endif  // SOURCE_REDUCE_REMOVE_OPNAME_INSTRUCTION_REDUCTION_OPPORTUNITY_FINDER_H_
diff --git a/source/reduce/remove_relaxed_precision_decoration_opportunity_finder.cpp b/source/reduce/remove_relaxed_precision_decoration_opportunity_finder.cpp
deleted file mode 100644
index 352cefb..0000000
--- a/source/reduce/remove_relaxed_precision_decoration_opportunity_finder.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright (c) 2018 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/reduce/remove_relaxed_precision_decoration_opportunity_finder.h"
-
-#include "source/reduce/remove_instruction_reduction_opportunity.h"
-
-namespace spvtools {
-namespace reduce {
-
-std::vector<std::unique_ptr<ReductionOpportunity>>
-RemoveRelaxedPrecisionDecorationOpportunityFinder::GetAvailableOpportunities(
-    opt::IRContext* context) const {
-  std::vector<std::unique_ptr<ReductionOpportunity>> result;
-
-  // Consider all annotation instructions
-  for (auto& inst : context->module()->annotations()) {
-    // We are interested in removing instructions of the form:
-    //   SpvOpDecorate %id RelaxedPrecision
-    // and
-    //   SpvOpMemberDecorate %id member RelaxedPrecision
-    if ((inst.opcode() == SpvOpDecorate &&
-         inst.GetSingleWordInOperand(1) == SpvDecorationRelaxedPrecision) ||
-        (inst.opcode() == SpvOpMemberDecorate &&
-         inst.GetSingleWordInOperand(2) == SpvDecorationRelaxedPrecision)) {
-      result.push_back(
-          MakeUnique<RemoveInstructionReductionOpportunity>(&inst));
-    }
-  }
-  return result;
-}
-
-std::string RemoveRelaxedPrecisionDecorationOpportunityFinder::GetName() const {
-  return "RemoveRelaxedPrecisionDecorationOpportunityFinder";
-}
-
-}  // namespace reduce
-}  // namespace spvtools
diff --git a/source/reduce/remove_relaxed_precision_decoration_opportunity_finder.h b/source/reduce/remove_relaxed_precision_decoration_opportunity_finder.h
deleted file mode 100644
index 673049c..0000000
--- a/source/reduce/remove_relaxed_precision_decoration_opportunity_finder.h
+++ /dev/null
@@ -1,36 +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.
-
-#ifndef SOURCE_REDUCE_REMOVE_RELAXED_PRECISION_OPPORTUNITY_FINDER_H_
-#define SOURCE_REDUCE_REMOVE_RELAXED_PRECISION_OPPORTUNITY_FINDER_H_
-
-#include "source/reduce/reduction_opportunity_finder.h"
-
-namespace spvtools {
-namespace reduce {
-
-// A finder for opportunities to remove relaxed precision decorations.
-class RemoveRelaxedPrecisionDecorationOpportunityFinder
-    : public ReductionOpportunityFinder {
- public:
-  std::vector<std::unique_ptr<ReductionOpportunity>> GetAvailableOpportunities(
-      opt::IRContext* context) const override;
-
-  std::string GetName() const override;
-};
-
-}  // namespace reduce
-}  // namespace spvtools
-
-#endif  // SOURCE_REDUCE_REMOVE_RELAXED_PRECISION_OPPORTUNITY_FINDER_H_
diff --git a/source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.cpp b/source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.cpp
index dabee50..da61c8d 100644
--- a/source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.cpp
+++ b/source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.cpp
@@ -21,28 +21,109 @@
 namespace spvtools {
 namespace reduce {
 
+RemoveUnreferencedInstructionReductionOpportunityFinder::
+    RemoveUnreferencedInstructionReductionOpportunityFinder(
+        bool remove_constants_and_undefs)
+    : remove_constants_and_undefs_(remove_constants_and_undefs) {}
+
 std::vector<std::unique_ptr<ReductionOpportunity>>
 RemoveUnreferencedInstructionReductionOpportunityFinder::
     GetAvailableOpportunities(opt::IRContext* context) const {
   std::vector<std::unique_ptr<ReductionOpportunity>> result;
 
+  for (auto& inst : context->module()->debugs1()) {
+    if (context->get_def_use_mgr()->NumUses(&inst) > 0) {
+      continue;
+    }
+    result.push_back(MakeUnique<RemoveInstructionReductionOpportunity>(&inst));
+  }
+
+  for (auto& inst : context->module()->debugs2()) {
+    if (context->get_def_use_mgr()->NumUses(&inst) > 0) {
+      continue;
+    }
+    result.push_back(MakeUnique<RemoveInstructionReductionOpportunity>(&inst));
+  }
+
+  for (auto& inst : context->module()->debugs3()) {
+    if (context->get_def_use_mgr()->NumUses(&inst) > 0) {
+      continue;
+    }
+    result.push_back(MakeUnique<RemoveInstructionReductionOpportunity>(&inst));
+  }
+
+  for (auto& inst : context->module()->types_values()) {
+    if (context->get_def_use_mgr()->NumUsers(&inst) > 0) {
+      continue;
+    }
+    if (!remove_constants_and_undefs_ &&
+        spvOpcodeIsConstantOrUndef(inst.opcode())) {
+      continue;
+    }
+    result.push_back(MakeUnique<RemoveInstructionReductionOpportunity>(&inst));
+  }
+
+  for (auto& inst : context->module()->annotations()) {
+    if (context->get_def_use_mgr()->NumUsers(&inst) > 0) {
+      continue;
+    }
+
+    uint32_t decoration = SpvDecorationMax;
+    switch (inst.opcode()) {
+      case SpvOpDecorate:
+      case SpvOpDecorateId:
+      case SpvOpDecorateString:
+        decoration = inst.GetSingleWordInOperand(1u);
+        break;
+      case SpvOpMemberDecorate:
+      case SpvOpMemberDecorateString:
+        decoration = inst.GetSingleWordInOperand(2u);
+        break;
+      default:
+        break;
+    }
+
+    // We conservatively only remove specific decorations that we believe will
+    // 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:
+        break;
+      default:
+        // Give up.
+        continue;
+    }
+
+    result.push_back(MakeUnique<RemoveInstructionReductionOpportunity>(&inst));
+  }
+
   for (auto& function : *context->module()) {
     for (auto& block : function) {
       for (auto& inst : block) {
         if (context->get_def_use_mgr()->NumUses(&inst) > 0) {
           continue;
         }
+        if (!remove_constants_and_undefs_ &&
+            spvOpcodeIsConstantOrUndef(inst.opcode())) {
+          continue;
+        }
         if (spvOpcodeIsBlockTerminator(inst.opcode()) ||
             inst.opcode() == SpvOpSelectionMerge ||
             inst.opcode() == SpvOpLoopMerge) {
-          // In this reduction pass we do not want to affect static control
-          // flow.
+          // In this reduction pass we do not want to affect static
+          // control flow.
           continue;
         }
-        // Given that we're in a block, we should only get here if the
-        // instruction is not directly related to control flow; i.e., it's
-        // some straightforward instruction with an unused result, like an
-        // arithmetic operation or function call.
+        // Given that we're in a block, we should only get here if
+        // the instruction is not directly related to control flow;
+        // i.e., it's some straightforward instruction with an
+        // unused result, like an arithmetic operation or function
+        // call.
         result.push_back(
             MakeUnique<RemoveInstructionReductionOpportunity>(&inst));
       }
diff --git a/source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.h b/source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.h
index 30f460b..bc4f137 100644
--- a/source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.h
+++ b/source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.h
@@ -28,7 +28,8 @@
 class RemoveUnreferencedInstructionReductionOpportunityFinder
     : public ReductionOpportunityFinder {
  public:
-  RemoveUnreferencedInstructionReductionOpportunityFinder() = default;
+  explicit RemoveUnreferencedInstructionReductionOpportunityFinder(
+      bool remove_constants_and_undefs);
 
   ~RemoveUnreferencedInstructionReductionOpportunityFinder() override = default;
 
@@ -38,6 +39,7 @@
       opt::IRContext* context) const final;
 
  private:
+  bool remove_constants_and_undefs_;
 };
 
 }  // namespace reduce
diff --git a/source/reduce/structured_loop_to_selection_reduction_opportunity.cpp b/source/reduce/structured_loop_to_selection_reduction_opportunity.cpp
index afc1298..88ea38e 100644
--- a/source/reduce/structured_loop_to_selection_reduction_opportunity.cpp
+++ b/source/reduce/structured_loop_to_selection_reduction_opportunity.cpp
@@ -168,23 +168,6 @@
 }
 
 void StructuredLoopToSelectionReductionOpportunity::
-    AdaptPhiInstructionsForRemovedEdge(uint32_t from_id, BasicBlock* to_block) {
-  to_block->ForEachPhiInst([&from_id](Instruction* phi_inst) {
-    Instruction::OperandList new_in_operands;
-    // Go through the OpPhi's input operands in (variable, parent) pairs.
-    for (uint32_t index = 0; index < phi_inst->NumInOperands(); index += 2) {
-      // Keep all pairs where the parent is not the block from which the edge
-      // is being removed.
-      if (phi_inst->GetInOperand(index + 1).words[0] != from_id) {
-        new_in_operands.push_back(phi_inst->GetInOperand(index));
-        new_in_operands.push_back(phi_inst->GetInOperand(index + 1));
-      }
-    }
-    phi_inst->SetInOperands(std::move(new_in_operands));
-  });
-}
-
-void StructuredLoopToSelectionReductionOpportunity::
     AdaptPhiInstructionsForAddedEdge(uint32_t from_id, BasicBlock* to_block) {
   to_block->ForEachPhiInst([this, &from_id](Instruction* phi_inst) {
     // Add to the phi operand an (undef, from_id) pair to reflect the added
diff --git a/source/reduce/structured_loop_to_selection_reduction_opportunity.h b/source/reduce/structured_loop_to_selection_reduction_opportunity.h
index f6c065b..564811f 100644
--- a/source/reduce/structured_loop_to_selection_reduction_opportunity.h
+++ b/source/reduce/structured_loop_to_selection_reduction_opportunity.h
@@ -62,11 +62,6 @@
   void RedirectEdge(uint32_t source_id, uint32_t original_target_id,
                     uint32_t new_target_id);
 
-  // Removes any components of |to_block|'s phi instructions relating to
-  // |from_id|.
-  void AdaptPhiInstructionsForRemovedEdge(uint32_t from_id,
-                                          opt::BasicBlock* to_block);
-
   // Adds components to |to_block|'s phi instructions to account for a new
   // incoming edge from |from_id|.
   void AdaptPhiInstructionsForAddedEdge(uint32_t from_id,
diff --git a/source/spirv_fuzzer_options.cpp b/source/spirv_fuzzer_options.cpp
index 9a2cb9f..ab8903e 100644
--- a/source/spirv_fuzzer_options.cpp
+++ b/source/spirv_fuzzer_options.cpp
@@ -22,6 +22,7 @@
 spv_fuzzer_options_t::spv_fuzzer_options_t()
     : has_random_seed(false),
       random_seed(0),
+      replay_validation_enabled(false),
       shrinker_step_limit(kDefaultStepLimit) {}
 
 SPIRV_TOOLS_EXPORT spv_fuzzer_options spvFuzzerOptionsCreate() {
@@ -32,6 +33,11 @@
   delete options;
 }
 
+SPIRV_TOOLS_EXPORT void spvFuzzerOptionsEnableReplayValidation(
+    spv_fuzzer_options options) {
+  options->replay_validation_enabled = true;
+}
+
 SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetRandomSeed(
     spv_fuzzer_options options, uint32_t seed) {
   options->has_random_seed = true;
diff --git a/source/spirv_fuzzer_options.h b/source/spirv_fuzzer_options.h
index 3b8e15c..7bb16c7 100644
--- a/source/spirv_fuzzer_options.h
+++ b/source/spirv_fuzzer_options.h
@@ -29,6 +29,9 @@
   bool has_random_seed;
   uint32_t random_seed;
 
+  // See spvFuzzerOptionsEnableReplayValidation.
+  bool replay_validation_enabled;
+
   // See spvFuzzerOptionsSetShrinkerStepLimit.
   uint32_t shrinker_step_limit;
 };
diff --git a/source/spirv_target_env.cpp b/source/spirv_target_env.cpp
index 66856a0..86f2c8d 100644
--- a/source/spirv_target_env.cpp
+++ b/source/spirv_target_env.cpp
@@ -66,6 +66,8 @@
       return "SPIR-V 1.4";
     case SPV_ENV_VULKAN_1_1_SPIRV_1_4:
       return "SPIR-V 1.4 (under Vulkan 1.1 semantics)";
+    case SPV_ENV_UNIVERSAL_1_5:
+      return "SPIR-V 1.5";
   }
   return "";
 }
@@ -99,6 +101,8 @@
     case SPV_ENV_UNIVERSAL_1_4:
     case SPV_ENV_VULKAN_1_1_SPIRV_1_4:
       return SPV_SPIRV_VERSION_WORD(1, 4);
+    case SPV_ENV_UNIVERSAL_1_5:
+      return SPV_SPIRV_VERSION_WORD(1, 5);
   }
   return SPV_SPIRV_VERSION_WORD(0, 0);
 }
@@ -112,6 +116,7 @@
     {"spv1.2", SPV_ENV_UNIVERSAL_1_2},
     {"spv1.3", SPV_ENV_UNIVERSAL_1_3},
     {"spv1.4", SPV_ENV_UNIVERSAL_1_4},
+    {"spv1.5", SPV_ENV_UNIVERSAL_1_5},
     {"opencl1.2embedded", SPV_ENV_OPENCL_EMBEDDED_1_2},
     {"opencl1.2", SPV_ENV_OPENCL_1_2},
     {"opencl2.0embedded", SPV_ENV_OPENCL_EMBEDDED_2_0},
@@ -165,6 +170,7 @@
     case SPV_ENV_UNIVERSAL_1_3:
     case SPV_ENV_WEBGPU_0:
     case SPV_ENV_UNIVERSAL_1_4:
+    case SPV_ENV_UNIVERSAL_1_5:
       return false;
     case SPV_ENV_VULKAN_1_0:
     case SPV_ENV_VULKAN_1_1:
@@ -190,6 +196,7 @@
     case SPV_ENV_WEBGPU_0:
     case SPV_ENV_UNIVERSAL_1_4:
     case SPV_ENV_VULKAN_1_1_SPIRV_1_4:
+    case SPV_ENV_UNIVERSAL_1_5:
       return false;
     case SPV_ENV_OPENCL_1_2:
     case SPV_ENV_OPENCL_EMBEDDED_1_2:
@@ -227,6 +234,7 @@
     case SPV_ENV_OPENCL_2_2:
     case SPV_ENV_UNIVERSAL_1_4:
     case SPV_ENV_VULKAN_1_1_SPIRV_1_4:
+    case SPV_ENV_UNIVERSAL_1_5:
       return false;
     case SPV_ENV_WEBGPU_0:
       return true;
@@ -253,6 +261,7 @@
     case SPV_ENV_WEBGPU_0:
     case SPV_ENV_UNIVERSAL_1_4:
     case SPV_ENV_VULKAN_1_1_SPIRV_1_4:
+    case SPV_ENV_UNIVERSAL_1_5:
       return false;
     case SPV_ENV_OPENGL_4_0:
     case SPV_ENV_OPENGL_4_1:
@@ -299,7 +308,8 @@
     case SPV_ENV_UNIVERSAL_1_1:
     case SPV_ENV_UNIVERSAL_1_2:
     case SPV_ENV_UNIVERSAL_1_3:
-    case SPV_ENV_UNIVERSAL_1_4: {
+    case SPV_ENV_UNIVERSAL_1_4:
+    case SPV_ENV_UNIVERSAL_1_5: {
       return "Universal";
     }
   }
diff --git a/source/table.cpp b/source/table.cpp
index 7890305..b7a96cc 100644
--- a/source/table.cpp
+++ b/source/table.cpp
@@ -40,6 +40,7 @@
     case SPV_ENV_VULKAN_1_1_SPIRV_1_4:
     case SPV_ENV_WEBGPU_0:
     case SPV_ENV_UNIVERSAL_1_4:
+    case SPV_ENV_UNIVERSAL_1_5:
       break;
     default:
       return nullptr;
diff --git a/source/text.cpp b/source/text.cpp
index 9eadf73..d88d4f7 100644
--- a/source/text.cpp
+++ b/source/text.cpp
@@ -546,6 +546,11 @@
            << "Expected <result-id> at the beginning of an instruction, found '"
            << firstWord << "'.";
   }
+  if (!opcodeEntry->hasResult && !result_id.empty()) {
+    return context->diagnostic()
+           << "Cannot set ID " << result_id << " because " << opcodeName
+           << " does not produce a result ID.";
+  }
   pInst->opcode = opcodeEntry->opcode;
   context->setPosition(nextPosition);
   // Reserve the first word for the instruction.
@@ -805,7 +810,8 @@
 }
 
 void spvTextDestroy(spv_text text) {
-  if (!text) return;
-  delete[] text->str;
-  delete text;
+  if (text) {
+    if (text->str) delete[] text->str;
+    delete text;
+  }
 }
diff --git a/source/util/bitutils.h b/source/util/bitutils.h
index 17d61df..9ced2f9 100644
--- a/source/util/bitutils.h
+++ b/source/util/bitutils.h
@@ -15,8 +15,10 @@
 #ifndef SOURCE_UTIL_BITUTILS_H_
 #define SOURCE_UTIL_BITUTILS_H_
 
+#include <cassert>
 #include <cstdint>
 #include <cstring>
+#include <type_traits>
 
 namespace spvtools {
 namespace utils {
@@ -31,6 +33,14 @@
   return dest;
 }
 
+// Calculates the bit width of the integer type |T|.
+template <typename T>
+struct IntegerBitWidth {
+  static_assert(std::is_integral<T>::value, "Integer type required");
+  static const size_t kBitsPerByte = 8;
+  static const size_t get = sizeof(T) * kBitsPerByte;
+};
+
 // SetBits<T, First, Num> returns an integer of type <T> with bits set
 // for position <First> through <First + Num - 1>, counting from the least
 // significant bit. In particular when Num == 0, no positions are set to 1.
@@ -38,7 +48,7 @@
 // a bit that will not fit in the underlying type is set.
 template <typename T, size_t First = 0, size_t Num = 0>
 struct SetBits {
-  static_assert(First < sizeof(T) * 8,
+  static_assert(First < IntegerBitWidth<T>::get,
                 "Tried to set a bit that is shifted too far.");
   const static T get = (T(1) << First) | SetBits<T, First + 1, Num - 1>::get;
 };
@@ -49,6 +59,11 @@
 };
 
 // This is all compile-time so we can put our tests right here.
+static_assert(IntegerBitWidth<uint32_t>::get == 32, "IntegerBitWidth mismatch");
+static_assert(IntegerBitWidth<int32_t>::get == 32, "IntegerBitWidth mismatch");
+static_assert(IntegerBitWidth<uint64_t>::get == 64, "IntegerBitWidth mismatch");
+static_assert(IntegerBitWidth<uint8_t>::get == 8, "IntegerBitWidth mismatch");
+
 static_assert(SetBits<uint32_t, 0, 0>::get == uint32_t(0x00000000),
               "SetBits failed");
 static_assert(SetBits<uint32_t, 0, 1>::get == uint32_t(0x00000001),
@@ -90,6 +105,82 @@
   return count;
 }
 
+// Checks if the bit at the |position| is set to '1'.
+// Bits zero-indexed starting at the least significant bit.
+// |position| must be within the bit width of |T|.
+template <typename T>
+bool IsBitAtPositionSet(T word, size_t position) {
+  static_assert(std::is_integral<T>::value, "Integer type required");
+  static_assert(std::is_unsigned<T>::value, "Unsigned type required");
+  assert(position < IntegerBitWidth<T>::get &&
+         "position must be less than the bit width");
+  return word & T(T(1) << position);
+}
+
+// Returns a value obtained by setting a range of adjacent bits of |word| to
+// |value|. Affected bits are within the range:
+//   [first_position, first_position + num_bits_to_mutate),
+// assuming zero-based indexing starting at the least
+// significant bit. Bits to mutate must be within the bit width of |T|.
+template <typename T>
+T MutateBits(T word, size_t first_position, size_t num_bits_to_mutate,
+             bool value) {
+  static_assert(std::is_integral<T>::value, "Integer type required");
+  static_assert(std::is_unsigned<T>::value, "Unsigned type required");
+  static const size_t word_bit_width = IntegerBitWidth<T>::get;
+  assert(first_position < word_bit_width &&
+         "Mutated bits must be within bit width");
+  assert(first_position + num_bits_to_mutate <= word_bit_width &&
+         "Mutated bits must be within bit width");
+  if (num_bits_to_mutate == 0) {
+    return word;
+  }
+
+  const T all_ones = ~T(0);
+  const size_t num_unaffected_low_bits = first_position;
+  const T unaffected_low_mask =
+      T(T(all_ones >> num_unaffected_low_bits) << num_unaffected_low_bits);
+
+  const size_t num_unaffected_high_bits =
+      word_bit_width - (first_position + num_bits_to_mutate);
+  const T unaffected_high_mask =
+      T(T(all_ones << num_unaffected_high_bits) >> num_unaffected_high_bits);
+
+  const T mutation_mask = unaffected_low_mask & unaffected_high_mask;
+  if (value) {
+    return word | mutation_mask;
+  }
+  return word & T(~mutation_mask);
+}
+
+// Returns a value obtained by setting the |num_bits_to_set| highest bits to
+// '1'. |num_bits_to_set| must be not be greater than the bit width of |T|.
+template <typename T>
+T SetHighBits(T word, size_t num_bits_to_set) {
+  if (num_bits_to_set == 0) {
+    return word;
+  }
+  const size_t word_bit_width = IntegerBitWidth<T>::get;
+  assert(num_bits_to_set <= word_bit_width &&
+         "Can't set more bits than bit width");
+  return MutateBits(word, word_bit_width - num_bits_to_set, num_bits_to_set,
+                    true);
+}
+
+// Returns a value obtained by setting the |num_bits_to_set| highest bits to
+// '0'. |num_bits_to_set| must be not be greater than the bit width of |T|.
+template <typename T>
+T ClearHighBits(T word, size_t num_bits_to_set) {
+  if (num_bits_to_set == 0) {
+    return word;
+  }
+  const size_t word_bit_width = IntegerBitWidth<T>::get;
+  assert(num_bits_to_set <= word_bit_width &&
+         "Can't clear more bits than bit width");
+  return MutateBits(word, word_bit_width - num_bits_to_set, num_bits_to_set,
+                    false);
+}
+
 }  // namespace utils
 }  // namespace spvtools
 
diff --git a/source/val/validate.cpp b/source/val/validate.cpp
index 6f1a26c..7f4b0dc 100644
--- a/source/val/validate.cpp
+++ b/source/val/validate.cpp
@@ -272,6 +272,7 @@
     return error;
   }
 
+  std::vector<Instruction*> visited_entry_points;
   for (auto& instruction : vstate->ordered_instructions()) {
     {
       // In order to do this work outside of Process Instruction we need to be
@@ -293,6 +294,24 @@
 
         vstate->RegisterEntryPoint(entry_point, execution_model,
                                    std::move(desc));
+
+        if (visited_entry_points.size() > 0) {
+          for (const Instruction* check_inst : visited_entry_points) {
+            const auto check_execution_model =
+                check_inst->GetOperandAs<SpvExecutionModel>(0);
+            const char* check_str = reinterpret_cast<const char*>(
+                check_inst->words().data() + inst->operand(2).offset);
+            const std::string check_name(check_str);
+
+            if (desc.name == check_name &&
+                execution_model == check_execution_model) {
+              return vstate->diag(SPV_ERROR_INVALID_DATA, inst)
+                     << "2 Entry points cannot share the same name and "
+                        "ExecutionMode.";
+            }
+          }
+        }
+        visited_entry_points.push_back(inst);
       }
       if (inst->opcode() == SpvOpFunctionCall) {
         if (!vstate->in_function_body()) {
@@ -324,7 +343,6 @@
     }
 
     if (auto error = CapabilityPass(*vstate, &instruction)) return error;
-    if (auto error = DataRulesPass(*vstate, &instruction)) return error;
     if (auto error = ModuleLayoutPass(*vstate, &instruction)) return error;
     if (auto error = CfgPass(*vstate, &instruction)) return error;
     if (auto error = InstructionPass(*vstate, &instruction)) return error;
@@ -333,6 +351,9 @@
     {
       Instruction* inst = const_cast<Instruction*>(&instruction);
       vstate->RegisterInstruction(inst);
+      if (inst->opcode() == SpvOpTypeForwardPointer) {
+        vstate->RegisterForwardPointer(inst->GetOperandAs<uint32_t>(0));
+      }
     }
   }
 
diff --git a/source/val/validate.h b/source/val/validate.h
index b6c4072..31a775b 100644
--- a/source/val/validate.h
+++ b/source/val/validate.h
@@ -123,11 +123,6 @@
 /// Performs Id and SSA validation of a module
 spv_result_t IdPass(ValidationState_t& _, Instruction* inst);
 
-/// Performs validation of the Data Rules subsection of 2.16.1 Universal
-/// Validation Rules.
-/// TODO(ehsann): add more comments here as more validation code is added.
-spv_result_t DataRulesPass(ValidationState_t& _, const Instruction* inst);
-
 /// Performs instruction validation.
 spv_result_t InstructionPass(ValidationState_t& _, const Instruction* inst);
 
@@ -208,7 +203,7 @@
 spv_result_t ValidateExecutionLimitations(ValidationState_t& _,
                                           const Instruction* inst);
 
-/// Validates restricted  uses of 8- and 16-bit types.
+/// Validates restricted uses of 8- and 16-bit types.
 ///
 /// Validates shaders that uses 8- or 16-bit storage capabilities, but not full
 /// capabilities only have appropriate uses of those types.
diff --git a/source/val/validate_builtins.cpp b/source/val/validate_builtins.cpp
index a9bf716..7623d49 100644
--- a/source/val/validate_builtins.cpp
+++ b/source/val/validate_builtins.cpp
@@ -2504,20 +2504,20 @@
       switch (execution_model) {
         case SpvExecutionModelGeometry:
         case SpvExecutionModelFragment:
-        case SpvExecutionModelMeshNV: {
+        case SpvExecutionModelMeshNV:
           // Ok.
           break;
-          case SpvExecutionModelVertex:
-          case SpvExecutionModelTessellationEvaluation:
-            if (!_.HasCapability(SpvCapabilityShaderViewportIndexLayerEXT)) {
-              return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
-                     << "Using BuiltIn "
-                     << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
-                                                      decoration.params()[0])
-                     << " in Vertex or Tessellation execution model requires "
-                        "the ShaderViewportIndexLayerEXT capability.";
-            }
-            break;
+        case SpvExecutionModelVertex:
+        case SpvExecutionModelTessellationEvaluation: {
+          if (!_.HasCapability(SpvCapabilityShaderViewportIndexLayerEXT)) {
+            return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
+                   << "Using BuiltIn "
+                   << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
+                                                    decoration.params()[0])
+                   << " in Vertex or Tessellation execution model requires "
+                      "the ShaderViewportIndexLayerEXT capability.";
+          }
+          break;
         }
         default: {
           return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
@@ -2901,6 +2901,26 @@
     const Decoration& decoration, const Instruction& inst) {
   const SpvBuiltIn label = SpvBuiltIn(decoration.params()[0]);
 
+  // Builtins can only be applied to variables, structures or constants.
+  auto target_opcode = inst.opcode();
+  if (target_opcode != SpvOpTypeStruct && target_opcode != SpvOpVariable &&
+      !spvOpcodeIsConstant(target_opcode)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, &inst)
+           << "BuiltIns can only target variables, structs or constants";
+  }
+
+  if (!spvIsVulkanOrWebGPUEnv(_.context()->target_env)) {
+    // Early return. All currently implemented rules are based on Vulkan or
+    // WebGPU spec.
+    //
+    // TODO: If you are adding validation rules for environments other than
+    // Vulkan or WebGPU (or general rules which are not environment
+    // independent), then you need to modify or remove this condition. Consider
+    // also adding early returns into BuiltIn-specific rules, so that the system
+    // doesn't spawn new rules which don't do anything.
+    return SPV_SUCCESS;
+  }
+
   if (spvIsWebGPUEnv(_.context()->target_env) &&
       !IsBuiltInValidForWebGPU(label)) {
     return _.diag(SPV_ERROR_INVALID_DATA, &inst)
@@ -3156,18 +3176,6 @@
 
 // Validates correctness of built-in variables.
 spv_result_t ValidateBuiltIns(ValidationState_t& _) {
-  if (!spvIsVulkanOrWebGPUEnv(_.context()->target_env)) {
-    // Early return. All currently implemented rules are based on Vulkan or
-    // WebGPU spec.
-    //
-    // TODO: If you are adding validation rules for environments other than
-    // Vulkan or WebGPU (or general rules which are not environment
-    // independent), then you need to modify or remove this condition. Consider
-    // also adding early returns into BuiltIn-specific rules, so that the system
-    // doesn't spawn new rules which don't do anything.
-    return SPV_SUCCESS;
-  }
-
   BuiltInsValidator validator(_);
   return validator.Run();
 }
diff --git a/source/val/validate_cfg.cpp b/source/val/validate_cfg.cpp
index 8d8839e..4801fc5 100644
--- a/source/val/validate_cfg.cpp
+++ b/source/val/validate_cfg.cpp
@@ -590,9 +590,68 @@
   return SPV_SUCCESS;
 }
 
+// Validates that all CFG divergences (i.e. conditional branch or switch) are
+// structured correctly. Either divergence is preceded by a merge instruction
+// or the divergence introduces at most one unseen label.
+spv_result_t ValidateStructuredSelections(
+    ValidationState_t& _, const std::vector<const BasicBlock*>& postorder) {
+  std::unordered_set<uint32_t> seen;
+  for (auto iter = postorder.rbegin(); iter != postorder.rend(); ++iter) {
+    const auto* block = *iter;
+    const auto* terminator = block->terminator();
+    if (!terminator) continue;
+    const auto index = terminator - &_.ordered_instructions()[0];
+    auto* merge = &_.ordered_instructions()[index - 1];
+    // Marks merges and continues as seen.
+    if (merge->opcode() == SpvOpSelectionMerge) {
+      seen.insert(merge->GetOperandAs<uint32_t>(0));
+    } else if (merge->opcode() == SpvOpLoopMerge) {
+      seen.insert(merge->GetOperandAs<uint32_t>(0));
+      seen.insert(merge->GetOperandAs<uint32_t>(1));
+    } else {
+      // Only track the pointer if it is a merge instruction.
+      merge = nullptr;
+    }
+
+    // Skip unreachable blocks.
+    if (!block->reachable()) continue;
+
+    if (terminator->opcode() == SpvOpBranchConditional) {
+      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
+      // was missing a merge instruction and both labels hadn't been seen
+      // previously.
+      const bool both_unseen =
+          seen.insert(true_label).second && seen.insert(false_label).second;
+      if (!merge && both_unseen) {
+        return _.diag(SPV_ERROR_INVALID_CFG, terminator)
+               << "Selection must be structured";
+      }
+    } else if (terminator->opcode() == SpvOpSwitch) {
+      uint32_t count = 0;
+      // Mark the targets as seen now, but only error out if this block was
+      // missing a merge instruction and there were multiple unseen labels.
+      for (uint32_t i = 1; i < terminator->operands().size(); i += 2) {
+        const auto target = terminator->GetOperandAs<uint32_t>(i);
+        if (seen.insert(target).second) {
+          count++;
+        }
+      }
+      if (!merge && count > 1) {
+        return _.diag(SPV_ERROR_INVALID_CFG, terminator)
+               << "Selection must be structured";
+      }
+    }
+  }
+
+  return SPV_SUCCESS;
+}
+
 spv_result_t StructuredControlFlowChecks(
     ValidationState_t& _, Function* function,
-    const std::vector<std::pair<uint32_t, uint32_t>>& back_edges) {
+    const std::vector<std::pair<uint32_t, uint32_t>>& back_edges,
+    const std::vector<const BasicBlock*>& postorder) {
   /// Check all backedges target only loop headers and have exactly one
   /// back-edge branching to it
 
@@ -709,6 +768,10 @@
     }
   }
 
+  if (auto error = ValidateStructuredSelections(_, postorder)) {
+    return error;
+  }
+
   return SPV_SUCCESS;
 }
 
@@ -931,7 +994,8 @@
 
     /// Structured control flow checks are only required for shader capabilities
     if (_.HasCapability(SpvCapabilityShader)) {
-      if (auto error = StructuredControlFlowChecks(_, &function, back_edges))
+      if (auto error =
+              StructuredControlFlowChecks(_, &function, back_edges, postorder))
         return error;
     }
   }
diff --git a/source/val/validate_composites.cpp b/source/val/validate_composites.cpp
index 9b7f592..eb8a324 100644
--- a/source/val/validate_composites.cpp
+++ b/source/val/validate_composites.cpp
@@ -30,8 +30,8 @@
 // OpCompositeInsert instruction. The function traverses the hierarchy of
 // nested data structures (structs, arrays, vectors, matrices) as directed by
 // the sequence of indices in the instruction. May return error if traversal
-// fails (encountered non-composite, out of bounds, nesting too deep).
-// Returns the type of Composite operand if the instruction has no indices.
+// fails (encountered non-composite, out of bounds, no indices, nesting too
+// deep).
 spv_result_t GetExtractInsertValueType(ValidationState_t& _,
                                        const Instruction* inst,
                                        uint32_t* member_type) {
@@ -40,10 +40,15 @@
   uint32_t word_index = opcode == SpvOpCompositeExtract ? 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;
   const uint32_t kCompositeExtractInsertMaxNumIndices = 255;
-  if (num_indices > kCompositeExtractInsertMaxNumIndices) {
+
+  if (num_indices == 0) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected at least one index to Op"
+           << spvOpcodeString(inst->opcode()) << ", zero found";
+
+  } else if (num_indices > kCompositeExtractInsertMaxNumIndices) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "The number of indexes in Op" << spvOpcodeString(opcode)
            << " may not exceed " << kCompositeExtractInsertMaxNumIndices
@@ -386,20 +391,20 @@
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "Cannot extract from a composite of 8- or 16-bit types";
   }
+
   return SPV_SUCCESS;
 }
 
 spv_result_t ValidateCompositeInsert(ValidationState_t& _,
                                      const Instruction* inst) {
-  const SpvOp opcode = inst->opcode();
   const uint32_t object_type = _.GetOperandTypeId(inst, 2);
   const uint32_t composite_type = _.GetOperandTypeId(inst, 3);
   const uint32_t result_type = inst->type_id();
   if (result_type != composite_type) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "The Result Type must be the same as Composite type in Op"
-           << spvOpcodeString(opcode) << " yielding Result Id " << result_type
-           << ".";
+           << spvOpcodeString(inst->opcode()) << " yielding Result Id "
+           << result_type << ".";
   }
 
   uint32_t member_type = 0;
@@ -421,6 +426,7 @@
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "Cannot insert into a composite of 8- or 16-bit types";
   }
+
   return SPV_SUCCESS;
 }
 
diff --git a/source/val/validate_constants.cpp b/source/val/validate_constants.cpp
index 565518b..dea95c8 100644
--- a/source/val/validate_constants.cpp
+++ b/source/val/validate_constants.cpp
@@ -304,7 +304,6 @@
     case SpvOpTypeBool:
     case SpvOpTypeInt:
     case SpvOpTypeFloat:
-    case SpvOpTypePointer:
     case SpvOpTypeEvent:
     case SpvOpTypeDeviceEvent:
     case SpvOpTypeReserveId:
@@ -325,6 +324,11 @@
       }
       return true;
     }
+    case SpvOpTypePointer:
+      if (instruction[2] == SpvStorageClassPhysicalStorageBuffer) {
+        return false;
+      }
+      return true;
     default:
       return false;
   }
@@ -342,6 +346,21 @@
   return SPV_SUCCESS;
 }
 
+// Validates that OpSpecConstant specializes to either int or float type.
+spv_result_t ValidateSpecConstant(ValidationState_t& _,
+                                  const Instruction* inst) {
+  // Operand 0 is the <id> of the type that we're specializing to.
+  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) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Specialization constant "
+                                                   "must be an integer or "
+                                                   "floating-point number.";
+  }
+  return SPV_SUCCESS;
+}
+
 spv_result_t ValidateSpecConstantOp(ValidationState_t& _,
                                     const Instruction* inst) {
   const auto op = inst->GetOperandAs<SpvOp>(2);
@@ -422,6 +441,9 @@
     case SpvOpConstantNull:
       if (auto error = ValidateConstantNull(_, inst)) return error;
       break;
+    case SpvOpSpecConstant:
+      if (auto error = ValidateSpecConstant(_, inst)) return error;
+      break;
     case SpvOpSpecConstantOp:
       if (auto error = ValidateSpecConstantOp(_, inst)) return error;
       break;
diff --git a/source/val/validate_conversion.cpp b/source/val/validate_conversion.cpp
index f7eb881..0060d0b 100644
--- a/source/val/validate_conversion.cpp
+++ b/source/val/validate_conversion.cpp
@@ -18,6 +18,7 @@
 
 #include "source/diagnostic.h"
 #include "source/opcode.h"
+#include "source/spirv_constant.h"
 #include "source/val/instruction.h"
 #include "source/val/validation_state.h"
 
@@ -467,15 +468,40 @@
                << "Expected input to be a pointer or int or float vector "
                << "or scalar: " << spvOpcodeString(opcode);
 
-      if (result_is_pointer && !input_is_pointer && !input_is_int_scalar)
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected input to be a pointer or int scalar if Result Type "
-               << "is pointer: " << spvOpcodeString(opcode);
+      if (_.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);
+        const bool input_is_int_vector = _.IsIntVectorType(input_type);
+        const bool input_has_int32 =
+            _.ContainsSizedIntOrFloatType(input_type, SpvOpTypeInt, 32);
+        if (result_is_pointer && !input_is_pointer && !input_is_int_scalar &&
+            !(input_is_int_vector && input_has_int32))
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << "Expected input to be a pointer, int scalar or 32-bit int "
+                    "vector if Result Type is pointer: "
+                 << spvOpcodeString(opcode);
 
-      if (input_is_pointer && !result_is_pointer && !result_is_int_scalar)
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Pointer can only be converted to another pointer or int "
-               << "scalar: " << spvOpcodeString(opcode);
+        if (input_is_pointer && !result_is_pointer && !result_is_int_scalar &&
+            !(result_is_int_vector && result_has_int32))
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << "Pointer can only be converted to another pointer, int "
+                    "scalar or 32-bit int vector: "
+                 << spvOpcodeString(opcode);
+      } else {
+        if (result_is_pointer && !input_is_pointer && !input_is_int_scalar)
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << "Expected input to be a pointer or int scalar if Result "
+                    "Type is pointer: "
+                 << spvOpcodeString(opcode);
+
+        if (input_is_pointer && !result_is_pointer && !result_is_int_scalar)
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << "Pointer can only be converted to another pointer or int "
+                    "scalar: "
+                 << spvOpcodeString(opcode);
+      }
 
       if (!result_is_pointer && !input_is_pointer) {
         const uint32_t result_size =
diff --git a/source/val/validate_datarules.cpp b/source/val/validate_datarules.cpp
deleted file mode 100644
index 826eb8d..0000000
--- a/source/val/validate_datarules.cpp
+++ /dev/null
@@ -1,286 +0,0 @@
-// Copyright (c) 2016 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Ensures Data Rules are followed according to the specifications.
-
-#include "source/val/validate.h"
-
-#include <cassert>
-#include <sstream>
-#include <string>
-
-#include "source/diagnostic.h"
-#include "source/opcode.h"
-#include "source/operand.h"
-#include "source/val/instruction.h"
-#include "source/val/validation_state.h"
-
-namespace spvtools {
-namespace val {
-namespace {
-
-// Validates that the number of components in the vector is valid.
-// Vector types can only be parameterized as having 2, 3, or 4 components.
-// If the Vector16 capability is added, 8 and 16 components are also allowed.
-spv_result_t ValidateVecNumComponents(ValidationState_t& _,
-                                      const Instruction* inst) {
-  // Operand 2 specifies the number of components in the vector.
-  auto num_components = inst->GetOperandAs<const uint32_t>(2);
-  if (num_components == 2 || num_components == 3 || num_components == 4) {
-    return SPV_SUCCESS;
-  }
-  if (num_components == 8 || num_components == 16) {
-    if (_.HasCapability(SpvCapabilityVector16)) {
-      return SPV_SUCCESS;
-    }
-    return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << "Having " << num_components << " components for "
-           << spvOpcodeString(inst->opcode())
-           << " requires the Vector16 capability";
-  }
-  return _.diag(SPV_ERROR_INVALID_DATA, inst)
-         << "Illegal number of components (" << num_components << ") for "
-         << spvOpcodeString(inst->opcode());
-}
-
-// Validates that the number of bits specifed for a float type is valid.
-// Scalar floating-point types can be parameterized only with 32-bits.
-// Float16 capability allows using a 16-bit OpTypeFloat.
-// Float16Buffer capability allows creation of a 16-bit OpTypeFloat.
-// Float64 capability allows using a 64-bit OpTypeFloat.
-spv_result_t ValidateFloatSize(ValidationState_t& _, const Instruction* inst) {
-  // Operand 1 is the number of bits for this float
-  auto num_bits = inst->GetOperandAs<const uint32_t>(1);
-  if (num_bits == 32) {
-    return SPV_SUCCESS;
-  }
-  if (num_bits == 16) {
-    if (_.features().declare_float16_type) {
-      return SPV_SUCCESS;
-    }
-    return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << "Using a 16-bit floating point "
-           << "type requires the Float16 or Float16Buffer capability,"
-              " or an extension that explicitly enables 16-bit floating point.";
-  }
-  if (num_bits == 64) {
-    if (_.HasCapability(SpvCapabilityFloat64)) {
-      return SPV_SUCCESS;
-    }
-    return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << "Using a 64-bit floating point "
-           << "type requires the Float64 capability.";
-  }
-  return _.diag(SPV_ERROR_INVALID_DATA, inst)
-         << "Invalid number of bits (" << num_bits << ") used for OpTypeFloat.";
-}
-
-// Validates that the number of bits specified for an Int type is valid.
-// Scalar integer types can be parameterized only with 32-bits.
-// Int8, Int16, and Int64 capabilities allow using 8-bit, 16-bit, and 64-bit
-// integers, respectively.
-spv_result_t ValidateIntSize(ValidationState_t& _, const Instruction* inst) {
-  // Operand 1 is the number of bits for this integer.
-  auto num_bits = inst->GetOperandAs<const uint32_t>(1);
-  if (num_bits == 32) {
-    return SPV_SUCCESS;
-  }
-  if (num_bits == 8) {
-    if (_.features().declare_int8_type) {
-      return SPV_SUCCESS;
-    }
-    return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << "Using an 8-bit integer type requires the Int8 capability,"
-              " or an extension that explicitly enables 8-bit integers.";
-  }
-  if (num_bits == 16) {
-    if (_.features().declare_int16_type) {
-      return SPV_SUCCESS;
-    }
-    return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << "Using a 16-bit integer type requires the Int16 capability,"
-              " or an extension that explicitly enables 16-bit integers.";
-  }
-  if (num_bits == 64) {
-    if (_.HasCapability(SpvCapabilityInt64)) {
-      return SPV_SUCCESS;
-    }
-    return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << "Using a 64-bit integer type requires the Int64 capability.";
-  }
-  return _.diag(SPV_ERROR_INVALID_DATA, inst)
-         << "Invalid number of bits (" << num_bits << ") used for OpTypeInt.";
-}
-
-// Validates that the matrix is parameterized with floating-point types.
-spv_result_t ValidateMatrixColumnType(ValidationState_t& _,
-                                      const Instruction* inst) {
-  // Find the component type of matrix columns (must be vector).
-  // Operand 1 is the <id> of the type specified for matrix columns.
-  auto type_id = inst->GetOperandAs<const uint32_t>(1);
-  auto col_type_instr = _.FindDef(type_id);
-  if (col_type_instr->opcode() != SpvOpTypeVector) {
-    return _.diag(SPV_ERROR_INVALID_ID, inst)
-           << "Columns in a matrix must be of type vector.";
-  }
-
-  // Trace back once more to find out the type of components in the vector.
-  // Operand 1 is the <id> of the type of data in the vector.
-  auto comp_type_id =
-      col_type_instr->words()[col_type_instr->operands()[1].offset];
-  auto comp_type_instruction = _.FindDef(comp_type_id);
-  if (comp_type_instruction->opcode() != SpvOpTypeFloat) {
-    return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Matrix types can only be "
-                                                   "parameterized with "
-                                                   "floating-point types.";
-  }
-  return SPV_SUCCESS;
-}
-
-// Validates that the matrix has 2,3, or 4 columns.
-spv_result_t ValidateMatrixNumCols(ValidationState_t& _,
-                                   const Instruction* inst) {
-  // Operand 2 is the number of columns in the matrix.
-  auto num_cols = inst->GetOperandAs<const uint32_t>(2);
-  if (num_cols != 2 && num_cols != 3 && num_cols != 4) {
-    return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Matrix types can only be "
-                                                   "parameterized as having "
-                                                   "only 2, 3, or 4 columns.";
-  }
-  return SPV_SUCCESS;
-}
-
-// Validates that OpSpecConstant specializes to either int or float type.
-spv_result_t ValidateSpecConstNumerical(ValidationState_t& _,
-                                        const Instruction* inst) {
-  // Operand 0 is the <id> of the type that we're specializing to.
-  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) {
-    return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Specialization constant "
-                                                   "must be an integer or "
-                                                   "floating-point number.";
-  }
-  return SPV_SUCCESS;
-}
-
-// Validates that OpSpecConstantTrue and OpSpecConstantFalse specialize to bool.
-spv_result_t ValidateSpecConstBoolean(ValidationState_t& _,
-                                      const Instruction* inst) {
-  // Find out the type that we're specializing to.
-  auto type_instruction = _.FindDef(inst->type_id());
-  if (type_instruction->opcode() != SpvOpTypeBool) {
-    return _.diag(SPV_ERROR_INVALID_ID, inst)
-           << "Specialization constant must be a boolean type.";
-  }
-  return SPV_SUCCESS;
-}
-
-// Records the <id> of the forward pointer to be used for validation.
-spv_result_t ValidateForwardPointer(ValidationState_t& _,
-                                    const Instruction* inst) {
-  // Record the <id> (which is operand 0) to ensure it's used properly.
-  // OpTypeStruct can only include undefined pointers that are
-  // previously declared as a ForwardPointer
-  return (_.RegisterForwardPointer(inst->GetOperandAs<uint32_t>(0)));
-}
-
-// Validates that any undefined component of the struct is a forward pointer.
-// It is valid to declare a forward pointer, and use its <id> as one of the
-// components of a struct.
-spv_result_t ValidateStruct(ValidationState_t& _, const Instruction* inst) {
-  // Struct components are operands 1, 2, etc.
-  for (unsigned i = 1; i < inst->operands().size(); i++) {
-    auto type_id = inst->GetOperandAs<const uint32_t>(i);
-    auto type_instruction = _.FindDef(type_id);
-    if (type_instruction == nullptr && !_.IsForwardPointer(type_id)) {
-      return _.diag(SPV_ERROR_INVALID_ID, inst)
-             << "Forward reference operands in an OpTypeStruct must first be "
-                "declared using OpTypeForwardPointer.";
-    }
-  }
-  return SPV_SUCCESS;
-}
-
-// Validates that any undefined type of the array is a forward pointer.
-// It is valid to declare a forward pointer, and use its <id> as the element
-// type of the array.
-spv_result_t ValidateArray(ValidationState_t& _, const Instruction* inst) {
-  auto element_type_id = inst->GetOperandAs<const uint32_t>(1);
-  auto element_type_instruction = _.FindDef(element_type_id);
-  if (element_type_instruction == nullptr &&
-      !_.IsForwardPointer(element_type_id)) {
-    return _.diag(SPV_ERROR_INVALID_ID, inst)
-           << "Forward reference operands in an OpTypeArray must first be "
-              "declared using OpTypeForwardPointer.";
-  }
-  return SPV_SUCCESS;
-}
-
-}  // namespace
-
-// Validates that Data Rules are followed according to the specifications.
-// (Data Rules subsection of 2.16.1 Universal Validation Rules)
-spv_result_t DataRulesPass(ValidationState_t& _, const Instruction* inst) {
-  switch (inst->opcode()) {
-    case SpvOpTypeVector: {
-      if (auto error = ValidateVecNumComponents(_, inst)) return error;
-      break;
-    }
-    case SpvOpTypeFloat: {
-      if (auto error = ValidateFloatSize(_, inst)) return error;
-      break;
-    }
-    case SpvOpTypeInt: {
-      if (auto error = ValidateIntSize(_, inst)) return error;
-      break;
-    }
-    case SpvOpTypeMatrix: {
-      if (auto error = ValidateMatrixColumnType(_, inst)) return error;
-      if (auto error = ValidateMatrixNumCols(_, inst)) return error;
-      break;
-    }
-    // TODO(ehsan): Add OpSpecConstantComposite validation code.
-    // TODO(ehsan): Add OpSpecConstantOp validation code (if any).
-    case SpvOpSpecConstant: {
-      if (auto error = ValidateSpecConstNumerical(_, inst)) return error;
-      break;
-    }
-    case SpvOpSpecConstantFalse:
-    case SpvOpSpecConstantTrue: {
-      if (auto error = ValidateSpecConstBoolean(_, inst)) return error;
-      break;
-    }
-    case SpvOpTypeForwardPointer: {
-      if (auto error = ValidateForwardPointer(_, inst)) return error;
-      break;
-    }
-    case SpvOpTypeStruct: {
-      if (auto error = ValidateStruct(_, inst)) return error;
-      break;
-    }
-    case SpvOpTypeArray: {
-      if (auto error = ValidateArray(_, inst)) return error;
-      break;
-    }
-    // TODO(ehsan): add more data rules validation here.
-    default: { break; }
-  }
-
-  return SPV_SUCCESS;
-}
-
-}  // namespace val
-}  // namespace spvtools
diff --git a/source/val/validate_decorations.cpp b/source/val/validate_decorations.cpp
index 3323961..d513a25 100644
--- a/source/val/validate_decorations.cpp
+++ b/source/val/validate_decorations.cpp
@@ -522,10 +522,13 @@
       const auto typeId = array_inst->word(2);
       const auto element_inst = vstate.FindDef(typeId);
       // Check array stride.
-      auto array_stride = 0;
+      uint32_t array_stride = 0;
       for (auto& decoration : vstate.id_decorations(array_inst->id())) {
         if (SpvDecorationArrayStride == decoration.dec_type()) {
           array_stride = decoration.params()[0];
+          if (array_stride == 0) {
+            return fail(memberIdx) << "contains an array with stride 0";
+          }
           if (!IsAlignedTo(array_stride, array_alignment))
             return fail(memberIdx)
                    << "contains an array with stride " << decoration.params()[0]
@@ -563,6 +566,14 @@
                             ? getScalarAlignment(array_inst->id(), vstate)
                             : getBaseAlignment(array_inst->id(), blockRules,
                                                constraint, constraints, vstate);
+
+      const auto element_size =
+          getSize(element_inst->id(), constraint, constraints, vstate);
+      if (element_size > array_stride) {
+        return fail(memberIdx)
+               << "contains an array with stride " << array_stride
+               << ", but with an element size of " << element_size;
+      }
     }
     nextValidOffset = offset + size;
     if (!scalar_block_layout && blockRules &&
@@ -942,6 +953,7 @@
           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;
         MemberConstraints constraints;
         ComputeMemberConstraintsForStruct(&constraints, id, LayoutConstraints(),
@@ -952,15 +964,18 @@
                     : (push_constant ? "PushConstant" : "StorageBuffer");
 
         if (spvIsVulkanEnv(vstate.context()->target_env)) {
-          if (storage_buffer &&
-              hasDecoration(id, SpvDecorationBufferBlock, vstate)) {
+          const bool block = hasDecoration(id, SpvDecorationBlock, vstate);
+          const bool buffer_block =
+              hasDecoration(id, SpvDecorationBufferBlock, vstate);
+          if (storage_buffer && buffer_block) {
             return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id))
                    << "Storage buffer id '" << var_id
                    << " In Vulkan, BufferBlock is disallowed on variables in "
                       "the StorageBuffer storage class";
           }
-          // Vulkan 14.5.1: Check Block decoration for PushConstant variables.
-          if (push_constant && !hasDecoration(id, SpvDecorationBlock, vstate)) {
+          // Vulkan 14.5.1/2: Check Block decoration for PushConstant, Uniform
+          // and StorageBuffer variables. Uniform can also use BufferBlock.
+          if (push_constant && !block) {
             return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
                    << "PushConstant id '" << id
                    << "' is missing Block decoration.\n"
@@ -968,6 +983,22 @@
                    << "Such variables must be identified with a Block "
                       "decoration";
           }
+          if (storage_buffer && !block) {
+            return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
+                   << "StorageBuffer id '" << id
+                   << "' is missing Block decoration.\n"
+                   << "From Vulkan spec, section 14.5.2:\n"
+                   << "Such variables must be identified with a Block "
+                      "decoration";
+          }
+          if (uniform && !block && !buffer_block) {
+            return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
+                   << "Uniform id '" << id
+                   << "' is missing Block or BufferBlock decoration.\n"
+                   << "From Vulkan spec, section 14.5.2:\n"
+                   << "Such variables must be identified with a Block or "
+                      "BufferBlock decoration";
+          }
           // Vulkan 14.5.2: Check DescriptorSet and Binding decoration for
           // Uniform and StorageBuffer variables.
           if (uniform || storage_buffer) {
diff --git a/source/val/validate_derivatives.cpp b/source/val/validate_derivatives.cpp
index b3f046a..067cc96 100644
--- a/source/val/validate_derivatives.cpp
+++ b/source/val/validate_derivatives.cpp
@@ -46,6 +46,10 @@
                << "Expected Result Type to be float scalar or vector type: "
                << spvOpcodeString(opcode);
       }
+      if (!_.ContainsSizedIntOrFloatType(result_type, SpvOpTypeFloat, 32)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Result type component width must be 32 bits";
+      }
 
       const uint32_t p_type = _.GetOperandTypeId(inst, 2);
       if (p_type != result_type) {
diff --git a/source/val/validate_id.cpp b/source/val/validate_id.cpp
index cb18e13..d80e1b8 100644
--- a/source/val/validate_id.cpp
+++ b/source/val/validate_id.cpp
@@ -191,7 +191,14 @@
             ret = SPV_SUCCESS;
           }
         } else if (can_have_forward_declared_ids(i)) {
-          ret = _.ForwardDeclareId(operand_word);
+          if (inst->opcode() == SpvOpTypeStruct &&
+              !_.IsForwardPointer(operand_word)) {
+            ret = _.diag(SPV_ERROR_INVALID_ID, inst)
+                  << "Operand " << _.getIdName(operand_word)
+                  << " requires a previous definition";
+          } else {
+            ret = _.ForwardDeclareId(operand_word);
+          }
         } else {
           ret = _.diag(SPV_ERROR_INVALID_ID, inst)
                 << "ID " << _.getIdName(operand_word)
diff --git a/source/val/validate_instruction.cpp b/source/val/validate_instruction.cpp
index b74b535..6478b3c 100644
--- a/source/val/validate_instruction.cpp
+++ b/source/val/validate_instruction.cpp
@@ -55,18 +55,6 @@
   return ss.str();
 }
 
-bool IsValidWebGPUStorageClass(SpvStorageClass storage_class) {
-  return storage_class == SpvStorageClassUniformConstant ||
-         storage_class == SpvStorageClassUniform ||
-         storage_class == SpvStorageClassStorageBuffer ||
-         storage_class == SpvStorageClassInput ||
-         storage_class == SpvStorageClassOutput ||
-         storage_class == SpvStorageClassImage ||
-         storage_class == SpvStorageClassWorkgroup ||
-         storage_class == SpvStorageClassPrivate ||
-         storage_class == SpvStorageClassFunction;
-}
-
 // Returns capabilities that enable an opcode.  An empty result is interpreted
 // as no prohibition of use of the opcode.  If the result is non-empty, then
 // the opcode may only be used if at least one of the capabilities is specified
@@ -205,17 +193,24 @@
           operand_desc->capabilities, operand_desc->numCapabilities);
     }
 
-    if (!state.HasAnyOfCapabilities(enabling_capabilities)) {
-      return state.diag(SPV_ERROR_INVALID_CAPABILITY, inst)
-             << "Operand " << which_operand << " of "
-             << spvOpcodeString(inst->opcode())
-             << " requires one of these capabilities: "
-             << ToString(enabling_capabilities, state.grammar());
+    // When encountering an OpCapability instruction, the instruction pass
+    // registers a capability with the module *before* checking capabilities.
+    // So in the case of an OpCapability instruction, don't bother checking
+    // enablement by another capability.
+    if (inst->opcode() != SpvOpCapability) {
+      const bool enabled_by_cap =
+          state.HasAnyOfCapabilities(enabling_capabilities);
+      if (!enabling_capabilities.IsEmpty() && !enabled_by_cap) {
+        return state.diag(SPV_ERROR_INVALID_CAPABILITY, inst)
+               << "Operand " << which_operand << " of "
+               << spvOpcodeString(inst->opcode())
+               << " requires one of these capabilities: "
+               << ToString(enabling_capabilities, state.grammar());
+      }
     }
     return OperandVersionExtensionCheck(state, inst, which_operand,
                                         *operand_desc, word);
   }
-
   return SPV_SUCCESS;
 }
 
@@ -242,23 +237,6 @@
   return SPV_SUCCESS;
 }
 
-// Returns SPV_ERROR_INVALID_BINARY and emits a diagnostic if the instruction
-// is invalid because of an execution environment constraint.
-spv_result_t EnvironmentCheck(ValidationState_t& _, const Instruction* inst) {
-  const SpvOp opcode = inst->opcode();
-  switch (opcode) {
-    case SpvOpUndef:
-      if (_.features().bans_op_undef) {
-        return _.diag(SPV_ERROR_INVALID_BINARY, inst)
-               << "OpUndef is disallowed";
-      }
-      break;
-    default:
-      break;
-  }
-  return SPV_SUCCESS;
-}
-
 // Returns SPV_ERROR_INVALID_CAPABILITY and emits a diagnostic if the
 // instruction is invalid because the required capability isn't declared
 // in the module.
@@ -492,38 +470,6 @@
     }
     _.set_addressing_model(inst->GetOperandAs<SpvAddressingModel>(0));
     _.set_memory_model(inst->GetOperandAs<SpvMemoryModel>(1));
-
-    if (_.memory_model() != SpvMemoryModelVulkanKHR &&
-        _.HasCapability(SpvCapabilityVulkanMemoryModelKHR)) {
-      return _.diag(SPV_ERROR_INVALID_DATA, inst)
-             << "VulkanMemoryModelKHR capability must only be specified if "
-                "the "
-                "VulkanKHR memory model is used.";
-    }
-
-    if (spvIsWebGPUEnv(_.context()->target_env)) {
-      if (_.addressing_model() != SpvAddressingModelLogical) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Addressing model must be Logical for WebGPU environment.";
-      }
-      if (_.memory_model() != SpvMemoryModelVulkanKHR) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Memory model must be VulkanKHR for WebGPU environment.";
-      }
-    }
-
-    if (spvIsOpenCLEnv(_.context()->target_env)) {
-      if ((_.addressing_model() != SpvAddressingModelPhysical32) &&
-          (_.addressing_model() != SpvAddressingModelPhysical64)) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Addressing model must be Physical32 or Physical64 "
-               << "in the OpenCL environment.";
-      }
-      if (_.memory_model() != SpvMemoryModelOpenCL) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Memory model must be OpenCL in the OpenCL environment.";
-      }
-    }
   } else if (opcode == SpvOpExecutionMode) {
     const uint32_t entry_point = inst->word(1);
     _.RegisterExecutionModeForEntryPoint(entry_point,
@@ -533,61 +479,9 @@
     if (auto error = LimitCheckNumVars(_, inst->id(), storage_class)) {
       return error;
     }
-
-    if (spvIsWebGPUEnv(_.context()->target_env) &&
-        !IsValidWebGPUStorageClass(storage_class)) {
-      return _.diag(SPV_ERROR_INVALID_BINARY, inst)
-             << "For WebGPU, OpVariable storage class must be one of "
-                "UniformConstant, Uniform, StorageBuffer, Input, Output, "
-                "Image, Workgroup, Private, Function for WebGPU";
-    }
-
-    if (storage_class == SpvStorageClassGeneric)
-      return _.diag(SPV_ERROR_INVALID_BINARY, inst)
-             << "OpVariable storage class cannot be Generic";
-    if (_.current_layout_section() == kLayoutFunctionDefinitions) {
-      if (storage_class != SpvStorageClassFunction) {
-        return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
-               << "Variables must have a function[7] storage class inside"
-                  " of a function";
-      }
-      if (_.current_function().IsFirstBlock(
-              _.current_function().current_block()->id()) == false) {
-        return _.diag(SPV_ERROR_INVALID_CFG, inst)
-               << "Variables can only be defined "
-                  "in the first block of a "
-                  "function";
-      }
-    } else {
-      if (storage_class == SpvStorageClassFunction) {
-        return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
-               << "Variables can not have a function[7] storage class "
-                  "outside of a function";
-      }
-    }
-  } else if (opcode == SpvOpTypePointer) {
-    const auto storage_class = inst->GetOperandAs<SpvStorageClass>(1);
-    if (spvIsWebGPUEnv(_.context()->target_env) &&
-        !IsValidWebGPUStorageClass(storage_class)) {
-      return _.diag(SPV_ERROR_INVALID_BINARY, inst)
-             << "For WebGPU, OpTypePointer storage class must be one of "
-                "UniformConstant, Uniform, StorageBuffer, Input, Output, "
-                "Image, Workgroup, Private, Function";
-    }
-  }
-
-  // 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) &&
-      inst->GetOperandAs<uint32_t>(2) != 0u) {
-    return _.diag(SPV_ERROR_INVALID_BINARY, inst)
-           << "The Signedness in OpTypeInt "
-              "must always be 0 when Kernel "
-              "capability is used.";
   }
 
   if (auto error = ReservedCheck(_, inst)) return error;
-  if (auto error = EnvironmentCheck(_, inst)) return error;
   if (auto error = CapabilityCheck(_, inst)) return error;
   if (auto error = LimitCheckIdBound(_, inst)) return error;
   if (auto error = LimitCheckStruct(_, inst)) return error;
diff --git a/source/val/validate_memory.cpp b/source/val/validate_memory.cpp
index f31c5a0..bff8b20 100644
--- a/source/val/validate_memory.cpp
+++ b/source/val/validate_memory.cpp
@@ -461,6 +461,28 @@
     }
   }
 
+  if (!_.IsValidStorageClass(storage_class)) {
+    return _.diag(SPV_ERROR_INVALID_BINARY, inst)
+           << "Invalid storage class for target environment";
+  }
+
+  if (storage_class == SpvStorageClassGeneric) {
+    return _.diag(SPV_ERROR_INVALID_BINARY, inst)
+           << "OpVariable storage class cannot be Generic";
+  }
+
+  if (inst->function() && storage_class != SpvStorageClassFunction) {
+    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) {
+    return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
+           << "Variables can not have a function[7] storage class "
+              "outside of a function";
+  }
+
   // SPIR-V 3.32.8: Check that pointer type and variable type have the same
   // storage class.
   const auto result_storage_class_index = 1;
@@ -536,6 +558,19 @@
                << "this type";
       }
     }
+
+    if (storage_class == SpvStorageClassStorageBuffer) {
+      if (!IsAllowedTypeOrArrayOfSame(_, pointee, {SpvOpTypeStruct})) {
+        return _.diag(SPV_ERROR_INVALID_ID, inst)
+               << "StorageBuffer OpVariable <id> '" << _.getIdName(inst->id())
+               << "' has illegal type.\n"
+               << "From Vulkan spec, section 14.5.2:\n"
+               << "Variables identified with the StorageBuffer storage class "
+                  "are used to access transparent buffer backed resources. "
+                  "Such variables must be typed as OpTypeStruct, or an array "
+                  "of this type";
+      }
+    }
   }
 
   // WebGPU & Vulkan Appendix A: Check that if contains initializer, then
@@ -1555,8 +1590,8 @@
            << "Operand type must be a pointer";
   }
 
+  SpvStorageClass sc = op1_type->GetOperandAs<SpvStorageClass>(1u);
   if (_.addressing_model() == SpvAddressingModelLogical) {
-    SpvStorageClass sc = op1_type->GetOperandAs<SpvStorageClass>(1u);
     if (sc != SpvStorageClassWorkgroup && sc != SpvStorageClassStorageBuffer) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
              << "Invalid pointer storage class";
@@ -1567,6 +1602,9 @@
              << "Workgroup storage class pointer requires VariablePointers "
                 "capability to be specified";
     }
+  } else if (sc == SpvStorageClassPhysicalStorageBuffer) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "Cannot use a pointer in the PhysicalStorageBuffer storage class";
   }
 
   return SPV_SUCCESS;
diff --git a/source/val/validate_misc.cpp b/source/val/validate_misc.cpp
index 28e3fc6..f0deedf 100644
--- a/source/val/validate_misc.cpp
+++ b/source/val/validate_misc.cpp
@@ -18,6 +18,7 @@
 #include "source/opcode.h"
 #include "source/spirv_target_env.h"
 #include "source/val/instruction.h"
+#include "source/val/validate_scopes.h"
 #include "source/val/validation_state.h"
 
 namespace spvtools {
@@ -32,6 +33,42 @@
            << "Cannot create undefined values with 8- or 16-bit types";
   }
 
+  if (spvIsWebGPUEnv(_.context()->target_env)) {
+    return _.diag(SPV_ERROR_INVALID_BINARY, inst) << "OpUndef is disallowed";
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateShaderClock(ValidationState_t& _,
+                                 const Instruction* inst) {
+  const uint32_t scope = inst->GetOperandAs<uint32_t>(2);
+  if (auto error = ValidateScope(_, inst, scope)) {
+    return error;
+  }
+
+  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) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Scope must be Subgroup or Device";
+  }
+
+  // Result Type must be a 64 - bit unsigned integer type or
+  // a vector of two - components of 32 -
+  // bit unsigned integer type
+  const uint32_t result_type = inst->type_id();
+  if (!(_.IsUnsignedIntScalarType(result_type) &&
+        _.GetBitWidth(result_type) == 64) &&
+      !(_.IsUnsignedIntVectorType(result_type) &&
+        _.GetDimension(result_type) == 2 && _.GetBitWidth(result_type) == 32)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected Value to be a "
+                                                   "vector of two components"
+                                                   " of unsigned integer"
+                                                   " or 64bit unsigned integer";
+  }
+
   return SPV_SUCCESS;
 }
 
@@ -110,6 +147,11 @@
                << spvOpcodeString(inst->opcode());
       break;
     }
+    case SpvOpReadClockKHR:
+      if (auto error = ValidateShaderClock(_, inst)) {
+        return error;
+      }
+      break;
     default:
       break;
   }
diff --git a/source/val/validate_mode_setting.cpp b/source/val/validate_mode_setting.cpp
index cbcf11a..e020f5a 100644
--- a/source/val/validate_mode_setting.cpp
+++ b/source/val/validate_mode_setting.cpp
@@ -485,6 +485,44 @@
   return SPV_SUCCESS;
 }
 
+spv_result_t ValidateMemoryModel(ValidationState_t& _,
+                                 const Instruction* inst) {
+  // Already produced an error if multiple memory model instructions are
+  // present.
+  if (_.memory_model() != SpvMemoryModelVulkanKHR &&
+      _.HasCapability(SpvCapabilityVulkanMemoryModelKHR)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "VulkanMemoryModelKHR capability must only be specified if "
+              "the VulkanKHR memory model is used.";
+  }
+
+  if (spvIsWebGPUEnv(_.context()->target_env)) {
+    if (_.addressing_model() != SpvAddressingModelLogical) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Addressing model must be Logical for WebGPU environment.";
+    }
+    if (_.memory_model() != SpvMemoryModelVulkanKHR) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Memory model must be VulkanKHR for WebGPU environment.";
+    }
+  }
+
+  if (spvIsOpenCLEnv(_.context()->target_env)) {
+    if ((_.addressing_model() != SpvAddressingModelPhysical32) &&
+        (_.addressing_model() != SpvAddressingModelPhysical64)) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Addressing model must be Physical32 or Physical64 "
+             << "in the OpenCL environment.";
+    }
+    if (_.memory_model() != SpvMemoryModelOpenCL) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Memory model must be OpenCL in the OpenCL environment.";
+    }
+  }
+
+  return SPV_SUCCESS;
+}
+
 }  // namespace
 
 spv_result_t ModeSettingPass(ValidationState_t& _, const Instruction* inst) {
@@ -496,6 +534,9 @@
     case SpvOpExecutionModeId:
       if (auto error = ValidateExecutionMode(_, inst)) return error;
       break;
+    case SpvOpMemoryModel:
+      if (auto error = ValidateMemoryModel(_, inst)) return error;
+      break;
     default:
       break;
   }
diff --git a/source/val/validate_scopes.cpp b/source/val/validate_scopes.cpp
index 70a65f7..1b98786 100644
--- a/source/val/validate_scopes.cpp
+++ b/source/val/validate_scopes.cpp
@@ -39,8 +39,8 @@
   return false;
 }
 
-spv_result_t ValidateExecutionScope(ValidationState_t& _,
-                                    const Instruction* inst, uint32_t scope) {
+spv_result_t ValidateScope(ValidationState_t& _, const Instruction* inst,
+                           uint32_t scope) {
   SpvOp opcode = inst->opcode();
   bool is_int32 = false, is_const_int32 = false;
   uint32_t value = 0;
@@ -48,8 +48,7 @@
 
   if (!is_int32) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << spvOpcodeString(opcode)
-           << ": expected Execution Scope to be a 32-bit int";
+           << spvOpcodeString(opcode) << ": expected scope to be a 32-bit int";
   }
 
   if (!is_const_int32) {
@@ -66,7 +65,6 @@
              << "Scope ids must be constant or specialization constant when "
              << "CooperativeMatrixNV capability is present";
     }
-    return SPV_SUCCESS;
   }
 
   if (is_const_int32 && !IsValidScope(value)) {
@@ -74,6 +72,24 @@
            << "Invalid scope value:\n " << _.Disassemble(*_.FindDef(scope));
   }
 
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateExecutionScope(ValidationState_t& _,
+                                    const Instruction* inst, uint32_t scope) {
+  SpvOp 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);
+
+  if (auto error = ValidateScope(_, inst, scope)) {
+    return error;
+  }
+
+  if (!is_const_int32) {
+    return SPV_SUCCESS;
+  }
+
   // Vulkan specific rules
   if (spvIsVulkanEnv(_.context()->target_env)) {
     // Vulkan 1.1 specific rules
@@ -152,34 +168,14 @@
   uint32_t value = 0;
   std::tie(is_int32, is_const_int32, value) = _.EvalInt32IfConst(scope);
 
-  if (!is_int32) {
-    return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << spvOpcodeString(opcode)
-           << ": expected Memory Scope to be a 32-bit int";
+  if (auto error = ValidateScope(_, inst, scope)) {
+    return error;
   }
 
   if (!is_const_int32) {
-    if (_.HasCapability(SpvCapabilityShader) &&
-        !_.HasCapability(SpvCapabilityCooperativeMatrixNV)) {
-      return _.diag(SPV_ERROR_INVALID_DATA, inst)
-             << "Scope ids must be OpConstant when Shader capability is "
-             << "present";
-    }
-    if (_.HasCapability(SpvCapabilityShader) &&
-        _.HasCapability(SpvCapabilityCooperativeMatrixNV) &&
-        !spvOpcodeIsConstant(_.GetIdOpcode(scope))) {
-      return _.diag(SPV_ERROR_INVALID_DATA, inst)
-             << "Scope ids must be constant or specialization constant when "
-             << "CooperativeMatrixNV capability is present";
-    }
     return SPV_SUCCESS;
   }
 
-  if (is_const_int32 && !IsValidScope(value)) {
-    return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << "Invalid scope value:\n " << _.Disassemble(*_.FindDef(scope));
-  }
-
   if (value == SpvScopeQueueFamilyKHR) {
     if (_.HasCapability(SpvCapabilityVulkanMemoryModelKHR)) {
       return SPV_SUCCESS;
diff --git a/source/val/validate_scopes.h b/source/val/validate_scopes.h
index 311ca7f..ba8b301 100644
--- a/source/val/validate_scopes.h
+++ b/source/val/validate_scopes.h
@@ -20,6 +20,9 @@
 namespace spvtools {
 namespace val {
 
+spv_result_t ValidateScope(ValidationState_t& _, const Instruction* inst,
+                           uint32_t scope);
+
 spv_result_t ValidateExecutionScope(ValidationState_t& _,
                                     const Instruction* inst, uint32_t scope);
 
diff --git a/source/val/validate_type.cpp b/source/val/validate_type.cpp
index afc0656..4d673b4 100644
--- a/source/val/validate_type.cpp
+++ b/source/val/validate_type.cpp
@@ -67,15 +67,89 @@
 }
 
 spv_result_t ValidateTypeInt(ValidationState_t& _, const Instruction* inst) {
+  // Validates that the number of bits specified for an Int type is valid.
+  // Scalar integer types can be parameterized only with 32-bits.
+  // Int8, Int16, and Int64 capabilities allow using 8-bit, 16-bit, and 64-bit
+  // integers, respectively.
+  auto num_bits = inst->GetOperandAs<const uint32_t>(1);
+  if (num_bits != 32) {
+    if (num_bits == 8) {
+      if (_.features().declare_int8_type) {
+        return SPV_SUCCESS;
+      }
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Using an 8-bit integer type requires the Int8 capability,"
+                " or an extension that explicitly enables 8-bit integers.";
+    } else if (num_bits == 16) {
+      if (_.features().declare_int16_type) {
+        return SPV_SUCCESS;
+      }
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "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)) {
+        return SPV_SUCCESS;
+      }
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Using a 64-bit integer type requires the Int64 capability.";
+    } else {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Invalid number of bits (" << num_bits
+             << ") used for OpTypeInt.";
+    }
+  }
+
   const auto signedness_index = 2;
   const auto signedness = inst->GetOperandAs<uint32_t>(signedness_index);
   if (signedness != 0 && signedness != 1) {
     return _.diag(SPV_ERROR_INVALID_VALUE, inst)
            << "OpTypeInt has invalid signedness:";
   }
+
+  // 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) &&
+      inst->GetOperandAs<uint32_t>(2) != 0u) {
+    return _.diag(SPV_ERROR_INVALID_BINARY, inst)
+           << "The Signedness in OpTypeInt "
+              "must always be 0 when Kernel "
+              "capability is used.";
+  }
+
   return SPV_SUCCESS;
 }
 
+spv_result_t ValidateTypeFloat(ValidationState_t& _, const Instruction* inst) {
+  // Validates that the number of bits specified for an Int type is valid.
+  // Scalar integer types can be parameterized only with 32-bits.
+  // Int8, Int16, and Int64 capabilities allow using 8-bit, 16-bit, and 64-bit
+  // integers, respectively.
+  auto num_bits = inst->GetOperandAs<const uint32_t>(1);
+  if (num_bits == 32) {
+    return SPV_SUCCESS;
+  }
+  if (num_bits == 16) {
+    if (_.features().declare_float16_type) {
+      return SPV_SUCCESS;
+    }
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Using a 16-bit floating point "
+           << "type requires the Float16 or Float16Buffer capability,"
+              " or an extension that explicitly enables 16-bit floating point.";
+  }
+  if (num_bits == 64) {
+    if (_.HasCapability(SpvCapabilityFloat64)) {
+      return SPV_SUCCESS;
+    }
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Using a 64-bit floating point "
+           << "type requires the Float64 capability.";
+  }
+  return _.diag(SPV_ERROR_INVALID_DATA, inst)
+         << "Invalid number of bits (" << num_bits << ") used for OpTypeFloat.";
+}
+
 spv_result_t ValidateTypeVector(ValidationState_t& _, const Instruction* inst) {
   const auto component_index = 1;
   const auto component_id = inst->GetOperandAs<uint32_t>(component_index);
@@ -85,6 +159,27 @@
            << "OpTypeVector Component Type <id> '" << _.getIdName(component_id)
            << "' is not a scalar type.";
   }
+
+  // Validates that the number of components in the vector is valid.
+  // Vector types can only be parameterized as having 2, 3, or 4 components.
+  // If the Vector16 capability is added, 8 and 16 components are also allowed.
+  auto num_components = inst->GetOperandAs<const uint32_t>(2);
+  if (num_components == 2 || num_components == 3 || num_components == 4) {
+    return SPV_SUCCESS;
+  } else if (num_components == 8 || num_components == 16) {
+    if (_.HasCapability(SpvCapabilityVector16)) {
+      return SPV_SUCCESS;
+    }
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Having " << num_components << " components for "
+           << spvOpcodeString(inst->opcode())
+           << " requires the Vector16 capability";
+  } else {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Illegal number of components (" << num_components << ") for "
+           << spvOpcodeString(inst->opcode());
+  }
+
   return SPV_SUCCESS;
 }
 
@@ -94,9 +189,27 @@
   const auto column_type = _.FindDef(column_type_id);
   if (!column_type || SpvOpTypeVector != column_type->opcode()) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
-           << "OpTypeMatrix Column Type <id> '" << _.getIdName(column_type_id)
-           << "' is not a vector.";
+           << "Columns in a matrix must be of type vector.";
   }
+
+  // Trace back once more to find out the type of components in the vector.
+  // 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) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Matrix types can only be "
+                                                   "parameterized with "
+                                                   "floating-point types.";
+  }
+
+  // Validates that the matrix has 2,3, or 4 columns.
+  auto num_cols = inst->GetOperandAs<const uint32_t>(2);
+  if (num_cols != 2 && num_cols != 3 && num_cols != 4) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Matrix types can only be "
+                                                   "parameterized as having "
+                                                   "only 2, 3, or 4 columns.";
+  }
+
   return SPV_SUCCESS;
 }
 
@@ -224,6 +337,11 @@
   for (size_t member_type_index = 1;
        member_type_index < inst->operands().size(); ++member_type_index) {
     auto member_type_id = inst->GetOperandAs<uint32_t>(member_type_index);
+    if (member_type_id == inst->id()) {
+      return _.diag(SPV_ERROR_INVALID_ID, inst)
+             << "Structure members may not be self references";
+    }
+
     auto member_type = _.FindDef(member_type_id);
     if (!member_type || !spvOpcodeGeneratesType(member_type->opcode())) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
@@ -245,22 +363,6 @@
              << " contains structure <id> " << _.getIdName(member_type_id)
              << ".";
     }
-    if (_.IsForwardPointer(member_type_id)) {
-      // If we're dealing with a forward pointer:
-      // Find out the type that the pointer is pointing to (must be struct)
-      // word 3 is the <id> of the type being pointed to.
-      auto type_pointing_to = _.FindDef(member_type->words()[3]);
-      if (type_pointing_to && type_pointing_to->opcode() != SpvOpTypeStruct) {
-        // Forward declared operands of a struct may only point to a struct.
-        return _.diag(SPV_ERROR_INVALID_ID, inst)
-               << "A forward reference operand in an OpTypeStruct must be an "
-                  "OpTypePointer that points to an OpTypeStruct. "
-                  "Found OpTypePointer that points to Op"
-               << spvOpcodeString(
-                      static_cast<SpvOp>(type_pointing_to->opcode()))
-               << ".";
-      }
-    }
 
     if (spvIsVulkanOrWebGPUEnv(_.context()->target_env) &&
         member_type->opcode() == SpvOpTypeRuntimeArray) {
@@ -354,9 +456,14 @@
       if (sampled == 2) _.RegisterPointerToStorageImage(inst->id());
     }
   }
+
+  if (!_.IsValidStorageClass(storage_class)) {
+    return _.diag(SPV_ERROR_INVALID_BINARY, inst)
+           << "Invalid storage class for target environment";
+  }
+
   return SPV_SUCCESS;
 }
-}  // namespace
 
 spv_result_t ValidateTypeFunction(ValidationState_t& _,
                                   const Instruction* inst) {
@@ -425,6 +532,13 @@
            << "pointer definition.";
   }
 
+  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) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "Forward pointers must point to a structure";
+  }
+
   return SPV_SUCCESS;
 }
 
@@ -474,6 +588,7 @@
 
   return SPV_SUCCESS;
 }
+}  // namespace
 
 spv_result_t TypePass(ValidationState_t& _, const Instruction* inst) {
   if (!spvOpcodeGeneratesType(inst->opcode()) &&
@@ -487,6 +602,9 @@
     case SpvOpTypeInt:
       if (auto error = ValidateTypeInt(_, inst)) return error;
       break;
+    case SpvOpTypeFloat:
+      if (auto error = ValidateTypeFloat(_, inst)) return error;
+      break;
     case SpvOpTypeVector:
       if (auto error = ValidateTypeVector(_, inst)) return error;
       break;
diff --git a/source/val/validation_state.cpp b/source/val/validation_state.cpp
index 794d0f7..20eaf88 100644
--- a/source/val/validation_state.cpp
+++ b/source/val/validation_state.cpp
@@ -212,14 +212,6 @@
     }
   }
 
-  switch (env) {
-    case SPV_ENV_WEBGPU_0:
-      features_.bans_op_undef = true;
-      break;
-    default:
-      break;
-  }
-
   // Only attempt to count if we have words, otherwise let the other validation
   // fail and generate an error.
   if (num_words > 0) {
@@ -1277,5 +1269,52 @@
   return false;
 }
 
+bool ValidationState_t::IsValidStorageClass(
+    SpvStorageClass storage_class) const {
+  if (spvIsWebGPUEnv(context()->target_env)) {
+    switch (storage_class) {
+      case SpvStorageClassUniformConstant:
+      case SpvStorageClassUniform:
+      case SpvStorageClassStorageBuffer:
+      case SpvStorageClassInput:
+      case SpvStorageClassOutput:
+      case SpvStorageClassImage:
+      case SpvStorageClassWorkgroup:
+      case SpvStorageClassPrivate:
+      case SpvStorageClassFunction:
+        return true;
+      default:
+        return false;
+    }
+  }
+
+  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 SpvStorageClassRayPayloadNV:
+      case SpvStorageClassIncomingRayPayloadNV:
+      case SpvStorageClassHitAttributeNV:
+      case SpvStorageClassCallableDataNV:
+      case SpvStorageClassIncomingCallableDataNV:
+      case SpvStorageClassShaderRecordBufferNV:
+        return true;
+      default:
+        return false;
+    }
+  }
+
+  return true;
+}
+
 }  // namespace val
 }  // namespace spvtools
diff --git a/source/val/validation_state.h b/source/val/validation_state.h
index ad16bcb..e5d31ac 100644
--- a/source/val/validation_state.h
+++ b/source/val/validation_state.h
@@ -79,9 +79,6 @@
     // Permit group oerations Reduce, InclusiveScan, ExclusiveScan
     bool group_ops_reduce_and_scans = false;
 
-    // Disallows the use of OpUndef
-    bool bans_op_undef = false;
-
     // Allow OpTypeInt with 8 bit width?
     bool declare_int8_type = false;
 
@@ -330,6 +327,12 @@
     return module_capabilities_.Contains(cap);
   }
 
+  /// Returns a reference to the set of capabilities in the module.
+  /// This is provided for debuggability.
+  const CapabilitySet& module_capabilities() const {
+    return module_capabilities_;
+  }
+
   /// Returns true if the extension is enabled in the module.
   bool HasExtension(Extension ext) const {
     return module_extensions_.Contains(ext);
@@ -380,7 +383,11 @@
 
   /// Registers the decoration for the given <id>
   void RegisterDecorationForId(uint32_t id, const Decoration& dec) {
-    id_decorations_[id].push_back(dec);
+    auto& dec_list = id_decorations_[id];
+    auto lb = std::find(dec_list.begin(), dec_list.end(), dec);
+    if (lb == dec_list.end()) {
+      dec_list.push_back(dec);
+    }
   }
 
   /// Registers the list of decorations for the given <id>
@@ -697,6 +704,9 @@
   // * OpCopyObject
   const Instruction* TracePointer(const Instruction* inst) const;
 
+  // Validates the storage class for the target environment.
+  bool IsValidStorageClass(SpvStorageClass storage_class) const;
+
  private:
   ValidationState_t(const ValidationState_t&);
 
diff --git a/test/assembly_format_test.cpp b/test/assembly_format_test.cpp
index 59e500b..718e6d3 100644
--- a/test/assembly_format_test.cpp
+++ b/test/assembly_format_test.cpp
@@ -20,7 +20,7 @@
 using spvtest::ScopedContext;
 using spvtest::TextToBinaryTest;
 
-TEST_F(TextToBinaryTest, NotPlacingResultIDAtTheBeginning) {
+TEST_F(TextToBinaryTest, InstOpcodeProducesResultIDButNoIDDefinedFails) {
   SetText("OpTypeMatrix %1 %2 1000");
   EXPECT_EQ(SPV_ERROR_INVALID_TEXT,
             spvTextToBinary(ScopedContext().context, text.str, text.length,
@@ -33,5 +33,18 @@
   EXPECT_EQ(0u, diagnostic->position.line);
 }
 
+TEST_F(TextToBinaryTest,
+       InstDefinesResultIDButOpcodeDoesNotProduceAResultFails) {
+  SetText("\n\n%foo = OpName %1 \"bar\"");
+  EXPECT_EQ(SPV_ERROR_INVALID_TEXT,
+            spvTextToBinary(ScopedContext().context, text.str, text.length,
+                            &binary, &diagnostic));
+  ASSERT_NE(nullptr, diagnostic);
+  EXPECT_STREQ(
+      "Cannot set ID %foo because OpName does not produce a result ID.",
+      diagnostic->error);
+  EXPECT_EQ(2u, diagnostic->position.line);
+}
+
 }  // namespace
 }  // namespace svptools
diff --git a/test/binary_header_get_test.cpp b/test/binary_header_get_test.cpp
index dcaf992..f8f6bdb 100644
--- a/test/binary_header_get_test.cpp
+++ b/test/binary_header_get_test.cpp
@@ -51,7 +51,8 @@
   ASSERT_EQ(SPV_SUCCESS, spvBinaryHeaderGet(&const_bin, endian, &header));
 
   ASSERT_EQ(static_cast<uint32_t>(SpvMagicNumber), header.magic);
-  ASSERT_EQ(0x00010400u, header.version);
+  // Expect SPIRV-Headers updated to SPIR-V 1.5.
+  ASSERT_EQ(0x00010500u, header.version);
   ASSERT_EQ(static_cast<uint32_t>(SPV_GENERATOR_CODEPLAY), header.generator);
   ASSERT_EQ(1u, header.bound);
   ASSERT_EQ(0u, header.schema);
diff --git a/test/c_interface_test.cpp b/test/c_interface_test.cpp
index c644fb9..841bb2c 100644
--- a/test/c_interface_test.cpp
+++ b/test/c_interface_test.cpp
@@ -107,7 +107,7 @@
 }
 
 TEST(CInterface, SpecifyConsumerNullDiagnosticForAssembling) {
-  const char input_text[] = "%1 = OpName\n";
+  const char input_text[] = "     OpName\n";
 
   auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1);
   int invocation = 0;
@@ -213,7 +213,7 @@
 // When having both a consumer and an diagnostic object, the diagnostic object
 // should take priority.
 TEST(CInterface, SpecifyConsumerSpecifyDiagnosticForAssembling) {
-  const char input_text[] = "%1 = OpName";
+  const char input_text[] = "     OpName";
 
   auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1);
   int invocation = 0;
diff --git a/test/fuzz/CMakeLists.txt b/test/fuzz/CMakeLists.txt
index 6a101dd..b38f35e 100644
--- a/test/fuzz/CMakeLists.txt
+++ b/test/fuzz/CMakeLists.txt
@@ -17,26 +17,47 @@
   set(SOURCES
           fuzz_test_util.h
 
-          fuzzer_replayer_test.cpp
-          fuzzer_shrinker_test.cpp
+          data_synonym_transformation_test.cpp
+          equivalence_relation_test.cpp
           fact_manager_test.cpp
           fuzz_test_util.cpp
           fuzzer_pass_add_useful_constructs_test.cpp
+          instruction_descriptor_test.cpp
           transformation_add_constant_boolean_test.cpp
           transformation_add_constant_scalar_test.cpp
           transformation_add_dead_break_test.cpp
           transformation_add_dead_continue_test.cpp
+          transformation_add_no_contraction_decoration_test.cpp
           transformation_add_type_boolean_test.cpp
           transformation_add_type_float_test.cpp
           transformation_add_type_int_test.cpp
           transformation_add_type_pointer_test.cpp
+          transformation_composite_construct_test.cpp
+          transformation_composite_extract_test.cpp
           transformation_copy_object_test.cpp
           transformation_move_block_down_test.cpp
           transformation_replace_boolean_constant_with_constant_binary_test.cpp
           transformation_replace_constant_with_uniform_test.cpp
+          transformation_replace_id_with_synonym_test.cpp
+          transformation_set_function_control_test.cpp
+          transformation_set_loop_control_test.cpp
+          transformation_set_memory_operands_mask_test.cpp
+          transformation_set_selection_control_test.cpp
           transformation_split_block_test.cpp
+          transformation_vector_shuffle_test.cpp
           uniform_buffer_element_descriptor_test.cpp)
 
+  if (${SPIRV_ENABLE_LONG_FUZZER_TESTS})
+    # These are long-running tests that depend on random seeds.  We do not want
+    # to run them during regular whole-project CI because they may reveal
+    # spirv-fuzz bugs in changes that are totally unrelated to spirv-fuzz,
+    # which would be counfounding.  Instead, they should be run regularly but
+    # separately.
+    set(SOURCES ${SOURCES}
+            fuzzer_replayer_test.cpp
+            fuzzer_shrinker_test.cpp)
+  endif()
+
   add_spvtools_unittest(TARGET fuzz
         SRCS ${SOURCES}
         LIBS SPIRV-Tools-fuzz
diff --git a/test/fuzz/data_synonym_transformation_test.cpp b/test/fuzz/data_synonym_transformation_test.cpp
new file mode 100644
index 0000000..21ea068
--- /dev/null
+++ b/test/fuzz/data_synonym_transformation_test.cpp
@@ -0,0 +1,1122 @@
+// 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.
+
+#include "source/fuzz/data_descriptor.h"
+#include "source/fuzz/id_use_descriptor.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_composite_extract.h"
+#include "source/fuzz/transformation_replace_id_with_synonym.h"
+#include "source/fuzz/transformation_vector_shuffle.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+// This file captures tests that check correctness of the collective use of a
+// number of transformations that relate to data synonyms.
+
+protobufs::Fact MakeSynonymFact(uint32_t first_id,
+                                std::vector<uint32_t>&& first_indices,
+                                uint32_t second_id,
+                                std::vector<uint32_t>&& second_indices) {
+  protobufs::FactDataSynonym data_synonym_fact;
+  *data_synonym_fact.mutable_data1() =
+      MakeDataDescriptor(first_id, std::move(first_indices));
+  *data_synonym_fact.mutable_data2() =
+      MakeDataDescriptor(second_id, std::move(second_indices));
+  protobufs::Fact result;
+  *result.mutable_data_synonym_fact() = data_synonym_fact;
+  return result;
+}
+
+TEST(DataSynonymTransformationTest, ArrayCompositeSynonyms) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %11 "A"
+               OpName %20 "B"
+               OpName %31 "g"
+               OpName %35 "h"
+               OpDecorate %11 RelaxedPrecision
+               OpDecorate %22 RelaxedPrecision
+               OpDecorate %27 RelaxedPrecision
+               OpDecorate %35 RelaxedPrecision
+               OpDecorate %36 RelaxedPrecision
+               OpDecorate %40 RelaxedPrecision
+               OpDecorate %41 RelaxedPrecision
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypeInt 32 0
+          %8 = OpConstant %7 3
+          %9 = OpTypeArray %6 %8
+         %10 = OpTypePointer Function %9
+         %12 = OpConstant %6 0
+         %13 = OpConstant %6 3
+         %14 = OpTypePointer Function %6
+         %16 = OpTypeFloat 32
+         %17 = OpConstant %7 4
+         %18 = OpTypeArray %16 %17
+         %19 = OpTypePointer Function %18
+         %24 = OpTypePointer Function %16
+         %28 = OpConstant %16 42
+         %30 = OpConstant %6 2
+         %34 = OpConstant %6 1
+         %38 = OpConstant %6 42
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %11 = OpVariable %10 Function
+         %20 = OpVariable %19 Function
+         %31 = OpVariable %24 Function
+         %35 = OpVariable %14 Function
+         %15 = OpAccessChain %14 %11 %12
+         %21 = OpAccessChain %14 %11 %12
+         %22 = OpLoad %6 %21
+        %100 = OpCompositeConstruct %9 %12 %13 %22
+               OpStore %15 %13
+         %23 = OpConvertSToF %16 %22
+         %25 = OpAccessChain %24 %20 %12
+               OpStore %25 %23
+         %26 = OpAccessChain %14 %11 %12
+         %27 = OpLoad %6 %26
+         %29 = OpAccessChain %24 %20 %27
+               OpStore %29 %28
+         %32 = OpLoad %16 %31
+        %101 = OpCompositeConstruct %18 %28 %23 %32 %23
+         %50 = OpCopyObject %16 %23
+         %51 = OpCopyObject %16 %23
+         %33 = OpAccessChain %24 %20 %30
+               OpStore %33 %28
+               OpStore %33 %32
+         %36 = OpLoad %6 %35
+         %37 = OpAccessChain %14 %11 %34
+               OpStore %37 %36
+         %39 = OpAccessChain %14 %11 %12
+         %40 = OpLoad %6 %39
+         %41 = OpIAdd %6 %38 %40
+         %42 = OpAccessChain %14 %11 %30
+               OpStore %42 %41
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  fact_manager.AddFact(MakeSynonymFact(12, {}, 100, {0}), context.get());
+  fact_manager.AddFact(MakeSynonymFact(13, {}, 100, {1}), context.get());
+  fact_manager.AddFact(MakeSynonymFact(22, {}, 100, {2}), context.get());
+  fact_manager.AddFact(MakeSynonymFact(28, {}, 101, {0}), context.get());
+  fact_manager.AddFact(MakeSynonymFact(23, {}, 101, {1}), context.get());
+  fact_manager.AddFact(MakeSynonymFact(32, {}, 101, {2}), context.get());
+  fact_manager.AddFact(MakeSynonymFact(23, {}, 101, {3}), context.get());
+
+  // Replace %12 with %100[0] in '%25 = OpAccessChain %24 %20 %12'
+  auto instruction_descriptor_1 =
+      MakeInstructionDescriptor(25, SpvOpAccessChain, 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});
+  ASSERT_TRUE(good_extract_1.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(bad_extract_1.IsApplicable(context.get(), fact_manager));
+  good_extract_1.Apply(context.get(), &fact_manager);
+  auto replacement_1 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(12, instruction_descriptor_1, 1), 102);
+  ASSERT_TRUE(replacement_1.IsApplicable(context.get(), fact_manager));
+  replacement_1.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Replace %13 with %100[1] in 'OpStore %15 %13'
+  auto instruction_descriptor_2 = MakeInstructionDescriptor(100, SpvOpStore, 0);
+  auto good_extract_2 =
+      TransformationCompositeExtract(instruction_descriptor_2, 103, 100, {1});
+  // No bad example provided here.
+  ASSERT_TRUE(good_extract_2.IsApplicable(context.get(), fact_manager));
+  good_extract_2.Apply(context.get(), &fact_manager);
+  auto replacement_2 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(13, instruction_descriptor_2, 1), 103);
+  ASSERT_TRUE(replacement_2.IsApplicable(context.get(), fact_manager));
+  replacement_2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Replace %22 with %100[2] in '%23 = OpConvertSToF %16 %22'
+  auto instruction_descriptor_3 =
+      MakeInstructionDescriptor(23, SpvOpConvertSToF, 0);
+  auto good_extract_3 =
+      TransformationCompositeExtract(instruction_descriptor_3, 104, 100, {2});
+  ASSERT_TRUE(good_extract_3.IsApplicable(context.get(), fact_manager));
+  good_extract_3.Apply(context.get(), &fact_manager);
+  auto replacement_3 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(22, instruction_descriptor_3, 0), 104);
+  // Bad: wrong input operand index
+  auto bad_replacement_3 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(22, instruction_descriptor_3, 1), 104);
+  ASSERT_TRUE(replacement_3.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(bad_replacement_3.IsApplicable(context.get(), fact_manager));
+  replacement_3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Replace %28 with %101[0] in 'OpStore %33 %28'
+  auto instruction_descriptor_4 = MakeInstructionDescriptor(33, SpvOpStore, 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});
+  ASSERT_TRUE(good_extract_4.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(bad_extract_4.IsApplicable(context.get(), fact_manager));
+  good_extract_4.Apply(context.get(), &fact_manager);
+  auto replacement_4 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(28, instruction_descriptor_4, 1), 105);
+  ASSERT_TRUE(replacement_4.IsApplicable(context.get(), fact_manager));
+  replacement_4.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Replace %23 with %101[1] in '%50 = OpCopyObject %16 %23'
+  auto instruction_descriptor_5 =
+      MakeInstructionDescriptor(50, SpvOpCopyObject, 0);
+  auto good_extract_5 =
+      TransformationCompositeExtract(instruction_descriptor_5, 106, 101, {1});
+  ASSERT_TRUE(good_extract_5.IsApplicable(context.get(), fact_manager));
+  good_extract_5.Apply(context.get(), &fact_manager);
+  auto replacement_5 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(23, instruction_descriptor_5, 0), 106);
+  // Bad: wrong synonym fact being used
+  auto bad_replacement_5 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(23, instruction_descriptor_5, 0), 105);
+  ASSERT_TRUE(replacement_5.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(bad_replacement_5.IsApplicable(context.get(), fact_manager));
+  replacement_5.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Replace %32 with %101[2] in 'OpStore %33 %32'
+  auto instruction_descriptor_6 = MakeInstructionDescriptor(33, SpvOpStore, 1);
+  auto good_extract_6 =
+      TransformationCompositeExtract(instruction_descriptor_6, 107, 101, {2});
+  // Bad: id 1001 does not exist
+  auto bad_extract_6 =
+      TransformationCompositeExtract(instruction_descriptor_6, 107, 1001, {2});
+  ASSERT_TRUE(good_extract_6.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(bad_extract_6.IsApplicable(context.get(), fact_manager));
+  good_extract_6.Apply(context.get(), &fact_manager);
+  auto replacement_6 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(32, instruction_descriptor_6, 1), 107);
+  ASSERT_TRUE(replacement_6.IsApplicable(context.get(), fact_manager));
+  replacement_6.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Replace %23 with %101[3] in '%51 = OpCopyObject %16 %23'
+  auto instruction_descriptor_7 =
+      MakeInstructionDescriptor(51, SpvOpCopyObject, 0);
+  auto good_extract_7 =
+      TransformationCompositeExtract(instruction_descriptor_7, 108, 101, {3});
+  ASSERT_TRUE(good_extract_7.IsApplicable(context.get(), fact_manager));
+  good_extract_7.Apply(context.get(), &fact_manager);
+  auto replacement_7 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(23, instruction_descriptor_7, 0), 108);
+  // Bad: use id 0 is invalid
+  auto bad_replacement_7 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(0, instruction_descriptor_7, 0), 108);
+  ASSERT_TRUE(replacement_7.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(bad_replacement_7.IsApplicable(context.get(), fact_manager));
+  replacement_7.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  const std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %11 "A"
+               OpName %20 "B"
+               OpName %31 "g"
+               OpName %35 "h"
+               OpDecorate %11 RelaxedPrecision
+               OpDecorate %22 RelaxedPrecision
+               OpDecorate %27 RelaxedPrecision
+               OpDecorate %35 RelaxedPrecision
+               OpDecorate %36 RelaxedPrecision
+               OpDecorate %40 RelaxedPrecision
+               OpDecorate %41 RelaxedPrecision
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypeInt 32 0
+          %8 = OpConstant %7 3
+          %9 = OpTypeArray %6 %8
+         %10 = OpTypePointer Function %9
+         %12 = OpConstant %6 0
+         %13 = OpConstant %6 3
+         %14 = OpTypePointer Function %6
+         %16 = OpTypeFloat 32
+         %17 = OpConstant %7 4
+         %18 = OpTypeArray %16 %17
+         %19 = OpTypePointer Function %18
+         %24 = OpTypePointer Function %16
+         %28 = OpConstant %16 42
+         %30 = OpConstant %6 2
+         %34 = OpConstant %6 1
+         %38 = OpConstant %6 42
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %11 = OpVariable %10 Function
+         %20 = OpVariable %19 Function
+         %31 = OpVariable %24 Function
+         %35 = OpVariable %14 Function
+         %15 = OpAccessChain %14 %11 %12
+         %21 = OpAccessChain %14 %11 %12
+         %22 = OpLoad %6 %21
+        %100 = OpCompositeConstruct %9 %12 %13 %22
+        %103 = OpCompositeExtract %6 %100 1
+               OpStore %15 %103
+        %104 = OpCompositeExtract %6 %100 2
+         %23 = OpConvertSToF %16 %104
+        %102 = OpCompositeExtract %6 %100 0
+         %25 = OpAccessChain %24 %20 %102
+               OpStore %25 %23
+         %26 = OpAccessChain %14 %11 %12
+         %27 = OpLoad %6 %26
+         %29 = OpAccessChain %24 %20 %27
+               OpStore %29 %28
+         %32 = OpLoad %16 %31
+        %101 = OpCompositeConstruct %18 %28 %23 %32 %23
+        %106 = OpCompositeExtract %16 %101 1
+         %50 = OpCopyObject %16 %106
+        %108 = OpCompositeExtract %16 %101 3
+         %51 = OpCopyObject %16 %108
+         %33 = OpAccessChain %24 %20 %30
+        %105 = OpCompositeExtract %16 %101 0
+               OpStore %33 %105
+        %107 = OpCompositeExtract %16 %101 2
+               OpStore %33 %107
+         %36 = OpLoad %6 %35
+         %37 = OpAccessChain %14 %11 %34
+               OpStore %37 %36
+         %39 = OpAccessChain %14 %11 %12
+         %40 = OpLoad %6 %39
+         %41 = OpIAdd %6 %38 %40
+         %42 = OpAccessChain %14 %11 %30
+               OpStore %42 %41
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(DataSynonymTransformationTest, MatrixCompositeSynonyms) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %10 "m"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeVector %6 4
+         %50 = OpUndef %7
+          %8 = OpTypeMatrix %7 3
+          %9 = OpTypePointer Function %8
+         %11 = OpTypeInt 32 1
+         %12 = OpConstant %11 0
+         %13 = OpConstant %6 1
+         %14 = OpConstantComposite %7 %13 %13 %13 %13
+         %15 = OpTypePointer Function %7
+         %17 = OpConstant %11 1
+         %18 = OpConstant %6 2
+         %19 = OpConstantComposite %7 %18 %18 %18 %18
+         %21 = OpConstant %11 2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %10 = OpVariable %9 Function
+         %16 = OpAccessChain %15 %10 %12
+               OpStore %16 %14
+         %20 = OpAccessChain %15 %10 %17
+               OpStore %20 %19
+         %22 = OpAccessChain %15 %10 %12
+         %23 = OpLoad %7 %22
+         %24 = OpAccessChain %15 %10 %17
+         %25 = OpLoad %7 %24
+        %100 = OpCompositeConstruct %8 %23 %25 %50
+         %26 = OpFAdd %7 %23 %25
+         %27 = OpAccessChain %15 %10 %21
+               OpStore %27 %26
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  fact_manager.AddFact(MakeSynonymFact(23, {}, 100, {0}), context.get());
+  fact_manager.AddFact(MakeSynonymFact(25, {}, 100, {1}), context.get());
+  fact_manager.AddFact(MakeSynonymFact(50, {}, 100, {2}), context.get());
+
+  // Replace %23 with %100[0] in '%26 = OpFAdd %7 %23 %25'
+  auto instruction_descriptor_1 = MakeInstructionDescriptor(26, SpvOpFAdd, 0);
+  auto extract_1 =
+      TransformationCompositeExtract(instruction_descriptor_1, 101, 100, {0});
+  ASSERT_TRUE(extract_1.IsApplicable(context.get(), fact_manager));
+  extract_1.Apply(context.get(), &fact_manager);
+  auto replacement_1 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(23, instruction_descriptor_1, 0), 101);
+  ASSERT_TRUE(replacement_1.IsApplicable(context.get(), fact_manager));
+  replacement_1.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Replace %25 with %100[1] in '%26 = OpFAdd %7 %23 %25'
+  auto instruction_descriptor_2 = MakeInstructionDescriptor(26, SpvOpFAdd, 0);
+  auto extract_2 =
+      TransformationCompositeExtract(instruction_descriptor_2, 102, 100, {1});
+  ASSERT_TRUE(extract_2.IsApplicable(context.get(), fact_manager));
+  extract_2.Apply(context.get(), &fact_manager);
+  auto replacement_2 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(25, instruction_descriptor_2, 1), 102);
+  ASSERT_TRUE(replacement_2.IsApplicable(context.get(), fact_manager));
+  replacement_2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  const std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %10 "m"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeVector %6 4
+         %50 = OpUndef %7
+          %8 = OpTypeMatrix %7 3
+          %9 = OpTypePointer Function %8
+         %11 = OpTypeInt 32 1
+         %12 = OpConstant %11 0
+         %13 = OpConstant %6 1
+         %14 = OpConstantComposite %7 %13 %13 %13 %13
+         %15 = OpTypePointer Function %7
+         %17 = OpConstant %11 1
+         %18 = OpConstant %6 2
+         %19 = OpConstantComposite %7 %18 %18 %18 %18
+         %21 = OpConstant %11 2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %10 = OpVariable %9 Function
+         %16 = OpAccessChain %15 %10 %12
+               OpStore %16 %14
+         %20 = OpAccessChain %15 %10 %17
+               OpStore %20 %19
+         %22 = OpAccessChain %15 %10 %12
+         %23 = OpLoad %7 %22
+         %24 = OpAccessChain %15 %10 %17
+         %25 = OpLoad %7 %24
+        %100 = OpCompositeConstruct %8 %23 %25 %50
+        %101 = OpCompositeExtract %7 %100 0
+        %102 = OpCompositeExtract %7 %100 1
+         %26 = OpFAdd %7 %101 %102
+         %27 = OpAccessChain %15 %10 %21
+               OpStore %27 %26
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(DataSynonymTransformationTest, StructCompositeSynonyms) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %9 "Inner"
+               OpMemberName %9 0 "a"
+               OpMemberName %9 1 "b"
+               OpName %11 "i1"
+               OpName %17 "i2"
+               OpName %31 "Point"
+               OpMemberName %31 0 "x"
+               OpMemberName %31 1 "y"
+               OpMemberName %31 2 "z"
+               OpName %32 "Outer"
+               OpMemberName %32 0 "c"
+               OpMemberName %32 1 "d"
+               OpName %34 "o1"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypeFloat 32
+          %8 = OpTypeVector %7 2
+          %9 = OpTypeStruct %6 %8
+         %10 = OpTypePointer Function %9
+         %12 = OpConstant %6 1
+         %13 = OpConstant %7 2
+         %14 = OpConstant %7 3
+         %15 = OpConstantComposite %8 %13 %14
+         %16 = OpConstantComposite %9 %12 %15
+         %18 = OpConstant %6 0
+         %19 = OpTypePointer Function %6
+         %24 = OpTypePointer Function %8
+         %27 = OpConstant %7 4
+         %31 = OpTypeStruct %7 %7 %7
+         %32 = OpTypeStruct %9 %31
+         %33 = OpTypePointer Function %32
+         %36 = OpConstant %7 10
+         %37 = OpTypeInt 32 0
+         %38 = OpConstant %37 0
+         %39 = OpTypePointer Function %7
+         %42 = OpConstant %37 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %11 = OpVariable %10 Function
+         %17 = OpVariable %10 Function
+         %34 = OpVariable %33 Function
+        %101 = OpCompositeConstruct %31 %27 %36 %27
+               OpStore %11 %16
+         %20 = OpAccessChain %19 %11 %18
+         %21 = OpLoad %6 %20
+         %22 = OpIAdd %6 %21 %12
+        %102 = OpCompositeConstruct %9 %22 %15
+         %23 = OpAccessChain %19 %17 %18
+               OpStore %23 %22
+         %25 = OpAccessChain %24 %17 %12
+         %26 = OpLoad %8 %25
+         %28 = OpCompositeConstruct %8 %27 %27
+         %29 = OpFAdd %8 %26 %28
+         %30 = OpAccessChain %24 %17 %12
+               OpStore %30 %29
+         %35 = OpLoad %9 %11
+         %40 = OpAccessChain %39 %11 %12 %38
+         %41 = OpLoad %7 %40
+         %43 = OpAccessChain %39 %11 %12 %42
+         %44 = OpLoad %7 %43
+         %45 = OpCompositeConstruct %31 %36 %41 %44
+        %100 = OpCompositeConstruct %32 %16 %45
+         %46 = OpCompositeConstruct %32 %35 %45
+               OpStore %34 %46
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  fact_manager.AddFact(MakeSynonymFact(16, {}, 100, {0}), context.get());
+  fact_manager.AddFact(MakeSynonymFact(45, {}, 100, {1}), context.get());
+  fact_manager.AddFact(MakeSynonymFact(27, {}, 101, {0}), context.get());
+  fact_manager.AddFact(MakeSynonymFact(36, {}, 101, {1}), context.get());
+  fact_manager.AddFact(MakeSynonymFact(27, {}, 101, {2}), context.get());
+  fact_manager.AddFact(MakeSynonymFact(22, {}, 102, {0}), context.get());
+  fact_manager.AddFact(MakeSynonymFact(15, {}, 102, {1}), context.get());
+
+  // Replace %45 with %100[1] in '%46 = OpCompositeConstruct %32 %35 %45'
+  auto instruction_descriptor_1 =
+      MakeInstructionDescriptor(46, SpvOpCompositeConstruct, 0);
+  auto extract_1 =
+      TransformationCompositeExtract(instruction_descriptor_1, 201, 100, {1});
+  ASSERT_TRUE(extract_1.IsApplicable(context.get(), fact_manager));
+  extract_1.Apply(context.get(), &fact_manager);
+  auto replacement_1 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(45, instruction_descriptor_1, 1), 201);
+  ASSERT_TRUE(replacement_1.IsApplicable(context.get(), fact_manager));
+  replacement_1.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Replace second occurrence of %27 with %101[0] in '%28 =
+  // OpCompositeConstruct %8 %27 %27'
+  auto instruction_descriptor_2 =
+      MakeInstructionDescriptor(28, SpvOpCompositeConstruct, 0);
+  auto extract_2 =
+      TransformationCompositeExtract(instruction_descriptor_2, 202, 101, {0});
+  ASSERT_TRUE(extract_2.IsApplicable(context.get(), fact_manager));
+  extract_2.Apply(context.get(), &fact_manager);
+  auto replacement_2 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(27, instruction_descriptor_2, 1), 202);
+  ASSERT_TRUE(replacement_2.IsApplicable(context.get(), fact_manager));
+  replacement_2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Replace %36 with %101[1] in '%45 = OpCompositeConstruct %31 %36 %41 %44'
+  auto instruction_descriptor_3 =
+      MakeInstructionDescriptor(45, SpvOpCompositeConstruct, 0);
+  auto extract_3 =
+      TransformationCompositeExtract(instruction_descriptor_3, 203, 101, {1});
+  ASSERT_TRUE(extract_3.IsApplicable(context.get(), fact_manager));
+  extract_3.Apply(context.get(), &fact_manager);
+  auto replacement_3 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(36, instruction_descriptor_3, 0), 203);
+  ASSERT_TRUE(replacement_3.IsApplicable(context.get(), fact_manager));
+  replacement_3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Replace first occurrence of %27 with %101[2] in '%28 = OpCompositeConstruct
+  // %8 %27 %27'
+  auto instruction_descriptor_4 =
+      MakeInstructionDescriptor(28, SpvOpCompositeConstruct, 0);
+  auto extract_4 =
+      TransformationCompositeExtract(instruction_descriptor_4, 204, 101, {2});
+  ASSERT_TRUE(extract_4.IsApplicable(context.get(), fact_manager));
+  extract_4.Apply(context.get(), &fact_manager);
+  auto replacement_4 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(27, instruction_descriptor_4, 0), 204);
+  ASSERT_TRUE(replacement_4.IsApplicable(context.get(), fact_manager));
+  replacement_4.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Replace %22 with %102[0] in 'OpStore %23 %22'
+  auto instruction_descriptor_5 = MakeInstructionDescriptor(23, SpvOpStore, 0);
+  auto extract_5 =
+      TransformationCompositeExtract(instruction_descriptor_5, 205, 102, {0});
+  ASSERT_TRUE(extract_5.IsApplicable(context.get(), fact_manager));
+  extract_5.Apply(context.get(), &fact_manager);
+  auto replacement_5 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(22, instruction_descriptor_5, 1), 205);
+  ASSERT_TRUE(replacement_5.IsApplicable(context.get(), fact_manager));
+  replacement_5.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  const std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %9 "Inner"
+               OpMemberName %9 0 "a"
+               OpMemberName %9 1 "b"
+               OpName %11 "i1"
+               OpName %17 "i2"
+               OpName %31 "Point"
+               OpMemberName %31 0 "x"
+               OpMemberName %31 1 "y"
+               OpMemberName %31 2 "z"
+               OpName %32 "Outer"
+               OpMemberName %32 0 "c"
+               OpMemberName %32 1 "d"
+               OpName %34 "o1"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypeFloat 32
+          %8 = OpTypeVector %7 2
+          %9 = OpTypeStruct %6 %8
+         %10 = OpTypePointer Function %9
+         %12 = OpConstant %6 1
+         %13 = OpConstant %7 2
+         %14 = OpConstant %7 3
+         %15 = OpConstantComposite %8 %13 %14
+         %16 = OpConstantComposite %9 %12 %15
+         %18 = OpConstant %6 0
+         %19 = OpTypePointer Function %6
+         %24 = OpTypePointer Function %8
+         %27 = OpConstant %7 4
+         %31 = OpTypeStruct %7 %7 %7
+         %32 = OpTypeStruct %9 %31
+         %33 = OpTypePointer Function %32
+         %36 = OpConstant %7 10
+         %37 = OpTypeInt 32 0
+         %38 = OpConstant %37 0
+         %39 = OpTypePointer Function %7
+         %42 = OpConstant %37 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %11 = OpVariable %10 Function
+         %17 = OpVariable %10 Function
+         %34 = OpVariable %33 Function
+        %101 = OpCompositeConstruct %31 %27 %36 %27
+               OpStore %11 %16
+         %20 = OpAccessChain %19 %11 %18
+         %21 = OpLoad %6 %20
+         %22 = OpIAdd %6 %21 %12
+        %102 = OpCompositeConstruct %9 %22 %15
+         %23 = OpAccessChain %19 %17 %18
+        %205 = OpCompositeExtract %6 %102 0
+               OpStore %23 %205
+         %25 = OpAccessChain %24 %17 %12
+         %26 = OpLoad %8 %25
+        %202 = OpCompositeExtract %7 %101 0
+        %204 = OpCompositeExtract %7 %101 2
+         %28 = OpCompositeConstruct %8 %204 %202
+         %29 = OpFAdd %8 %26 %28
+         %30 = OpAccessChain %24 %17 %12
+               OpStore %30 %29
+         %35 = OpLoad %9 %11
+         %40 = OpAccessChain %39 %11 %12 %38
+         %41 = OpLoad %7 %40
+         %43 = OpAccessChain %39 %11 %12 %42
+         %44 = OpLoad %7 %43
+        %203 = OpCompositeExtract %7 %101 1
+         %45 = OpCompositeConstruct %31 %203 %41 %44
+        %100 = OpCompositeConstruct %32 %16 %45
+        %201 = OpCompositeExtract %31 %100 1
+         %46 = OpCompositeConstruct %32 %35 %201
+               OpStore %34 %46
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(DataSynonymTransformationTest, VectorCompositeSynonyms) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %8 "f"
+               OpName %12 "v2"
+               OpName %18 "v3"
+               OpName %23 "v4"
+               OpName %32 "b"
+               OpName %36 "bv2"
+               OpName %41 "bv3"
+               OpName %50 "bv4"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 42
+         %10 = OpTypeVector %6 2
+         %11 = OpTypePointer Function %10
+         %16 = OpTypeVector %6 3
+         %17 = OpTypePointer Function %16
+         %21 = OpTypeVector %6 4
+         %22 = OpTypePointer Function %21
+         %30 = OpTypeBool
+         %31 = OpTypePointer Function %30
+         %33 = OpConstantFalse %30
+         %34 = OpTypeVector %30 2
+         %35 = OpTypePointer Function %34
+         %37 = OpConstantTrue %30
+         %38 = OpConstantComposite %34 %37 %37
+         %39 = OpTypeVector %30 3
+         %40 = OpTypePointer Function %39
+         %48 = OpTypeVector %30 4
+         %49 = OpTypePointer Function %48
+         %51 = OpTypeInt 32 0
+         %52 = OpConstant %51 2
+         %55 = OpConstant %6 0
+         %57 = OpConstant %51 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %12 = OpVariable %11 Function
+         %18 = OpVariable %17 Function
+         %23 = OpVariable %22 Function
+         %32 = OpVariable %31 Function
+         %36 = OpVariable %35 Function
+         %41 = OpVariable %40 Function
+         %50 = OpVariable %49 Function
+               OpStore %8 %9
+         %13 = OpLoad %6 %8
+         %14 = OpLoad %6 %8
+         %15 = OpCompositeConstruct %10 %13 %14
+               OpStore %12 %15
+         %19 = OpLoad %10 %12
+         %20 = OpVectorShuffle %16 %19 %19 0 0 1
+               OpStore %18 %20
+         %24 = OpLoad %16 %18
+         %25 = OpLoad %6 %8
+         %26 = OpCompositeExtract %6 %24 0
+         %27 = OpCompositeExtract %6 %24 1
+         %28 = OpCompositeExtract %6 %24 2
+         %29 = OpCompositeConstruct %21 %26 %27 %28 %25
+               OpStore %23 %29
+               OpStore %32 %33
+               OpStore %36 %38
+         %42 = OpLoad %30 %32
+         %43 = OpLoad %34 %36
+         %44 = OpVectorShuffle %34 %43 %43 0 0
+         %45 = OpCompositeExtract %30 %44 0
+         %46 = OpCompositeExtract %30 %44 1
+         %47 = OpCompositeConstruct %39 %42 %45 %46
+               OpStore %41 %47
+         %53 = OpAccessChain %7 %23 %52
+         %54 = OpLoad %6 %53
+
+        %100 = OpCompositeConstruct %21 %20 %54
+        %101 = OpCompositeConstruct %21 %15 %19
+        %102 = OpCompositeConstruct %16 %27 %15
+        %103 = OpCompositeConstruct %48 %33 %47
+        %104 = OpCompositeConstruct %34 %42 %45
+        %105 = OpCompositeConstruct %39 %38 %46
+
+         %86 = OpCopyObject %30 %33
+         %56 = OpFOrdNotEqual %30 %54 %55
+         %80 = OpCopyObject %16 %20
+         %58 = OpAccessChain %7 %18 %57
+         %59 = OpLoad %6 %58
+         %60 = OpFOrdNotEqual %30 %59 %55
+         %61 = OpLoad %34 %36
+         %62 = OpLogicalAnd %30 %45 %46
+         %63 = OpLogicalOr %30 %45 %46
+         %64 = OpCompositeConstruct %48 %56 %60 %62 %63
+               OpStore %12 %15
+         %81 = OpVectorShuffle %16 %19 %19 0 0 1
+         %82 = OpCompositeConstruct %21 %26 %27 %28 %25
+         %83 = OpCopyObject %10 %15
+         %84 = OpCopyObject %39 %47
+               OpStore %50 %64
+         %85 = OpCopyObject %30 %42
+               OpStore %36 %38
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  fact_manager.AddFact(MakeSynonymFact(20, {0}, 100, {0}), context.get());
+  fact_manager.AddFact(MakeSynonymFact(20, {1}, 100, {1}), context.get());
+  fact_manager.AddFact(MakeSynonymFact(20, {2}, 100, {2}), context.get());
+  fact_manager.AddFact(MakeSynonymFact(54, {}, 100, {3}), context.get());
+  fact_manager.AddFact(MakeSynonymFact(15, {0}, 101, {0}), context.get());
+  fact_manager.AddFact(MakeSynonymFact(15, {1}, 101, {1}), context.get());
+  fact_manager.AddFact(MakeSynonymFact(19, {0}, 101, {2}), context.get());
+  fact_manager.AddFact(MakeSynonymFact(19, {1}, 101, {3}), context.get());
+  fact_manager.AddFact(MakeSynonymFact(27, {}, 102, {0}), context.get());
+  fact_manager.AddFact(MakeSynonymFact(15, {0}, 102, {1}), context.get());
+  fact_manager.AddFact(MakeSynonymFact(15, {1}, 102, {2}), context.get());
+  fact_manager.AddFact(MakeSynonymFact(33, {}, 103, {0}), context.get());
+  fact_manager.AddFact(MakeSynonymFact(47, {0}, 103, {1}), context.get());
+  fact_manager.AddFact(MakeSynonymFact(47, {1}, 103, {2}), context.get());
+  fact_manager.AddFact(MakeSynonymFact(47, {2}, 103, {3}), context.get());
+  fact_manager.AddFact(MakeSynonymFact(42, {}, 104, {0}), context.get());
+  fact_manager.AddFact(MakeSynonymFact(45, {}, 104, {1}), context.get());
+  fact_manager.AddFact(MakeSynonymFact(38, {0}, 105, {0}), context.get());
+  fact_manager.AddFact(MakeSynonymFact(38, {1}, 105, {1}), context.get());
+  fact_manager.AddFact(MakeSynonymFact(46, {}, 105, {2}), context.get());
+
+  // Replace %20 with %100[0:2] in '%80 = OpCopyObject %16 %20'
+  auto instruction_descriptor_1 =
+      MakeInstructionDescriptor(80, SpvOpCopyObject, 0);
+  auto shuffle_1 = TransformationVectorShuffle(instruction_descriptor_1, 200,
+                                               100, 100, {0, 1, 2});
+  ASSERT_TRUE(shuffle_1.IsApplicable(context.get(), fact_manager));
+  shuffle_1.Apply(context.get(), &fact_manager);
+  auto replacement_1 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(20, instruction_descriptor_1, 0), 200);
+  ASSERT_TRUE(replacement_1.IsApplicable(context.get(), fact_manager));
+  replacement_1.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Replace %54 with %100[3] in '%56 = OpFOrdNotEqual %30 %54 %55'
+  auto instruction_descriptor_2 =
+      MakeInstructionDescriptor(56, SpvOpFOrdNotEqual, 0);
+  auto extract_2 =
+      TransformationCompositeExtract(instruction_descriptor_2, 201, 100, {3});
+
+  ASSERT_TRUE(extract_2.IsApplicable(context.get(), fact_manager));
+  extract_2.Apply(context.get(), &fact_manager);
+  auto replacement_2 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(54, instruction_descriptor_2, 0), 201);
+  ASSERT_TRUE(replacement_2.IsApplicable(context.get(), fact_manager));
+  replacement_2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Replace %15 with %101[0:1] in 'OpStore %12 %15'
+  auto instruction_descriptor_3 = MakeInstructionDescriptor(64, SpvOpStore, 0);
+  auto shuffle_3 = TransformationVectorShuffle(instruction_descriptor_3, 202,
+                                               101, 101, {0, 1});
+  ASSERT_TRUE(shuffle_3.IsApplicable(context.get(), fact_manager));
+  shuffle_3.Apply(context.get(), &fact_manager);
+  auto replacement_3 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(15, instruction_descriptor_3, 1), 202);
+  ASSERT_TRUE(replacement_3.IsApplicable(context.get(), fact_manager));
+  replacement_3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Replace %19 with %101[2:3] in '%81 = OpVectorShuffle %16 %19 %19 0 0 1'
+  auto instruction_descriptor_4 =
+      MakeInstructionDescriptor(81, SpvOpVectorShuffle, 0);
+  auto shuffle_4 = TransformationVectorShuffle(instruction_descriptor_4, 203,
+                                               101, 101, {2, 3});
+  ASSERT_TRUE(shuffle_4.IsApplicable(context.get(), fact_manager));
+  shuffle_4.Apply(context.get(), &fact_manager);
+  auto replacement_4 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(19, instruction_descriptor_4, 0), 203);
+  ASSERT_TRUE(replacement_4.IsApplicable(context.get(), fact_manager));
+  replacement_4.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Replace %27 with %102[0] in '%82 = OpCompositeConstruct %21 %26 %27 %28
+  // %25'
+  auto instruction_descriptor_5 =
+      MakeInstructionDescriptor(82, SpvOpCompositeConstruct, 0);
+  auto extract_5 =
+      TransformationCompositeExtract(instruction_descriptor_5, 204, 102, {0});
+
+  ASSERT_TRUE(extract_5.IsApplicable(context.get(), fact_manager));
+  extract_5.Apply(context.get(), &fact_manager);
+  auto replacement_5 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(27, instruction_descriptor_5, 1), 204);
+  ASSERT_TRUE(replacement_5.IsApplicable(context.get(), fact_manager));
+  replacement_5.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Replace %15 with %102[1:2] in '%83 = OpCopyObject %10 %15'
+  auto instruction_descriptor_6 =
+      MakeInstructionDescriptor(83, SpvOpCopyObject, 0);
+  auto shuffle_6 = TransformationVectorShuffle(instruction_descriptor_6, 205,
+                                               102, 102, {1, 2});
+  ASSERT_TRUE(shuffle_6.IsApplicable(context.get(), fact_manager));
+  shuffle_6.Apply(context.get(), &fact_manager);
+  auto replacement_6 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(15, instruction_descriptor_6, 0), 205);
+  ASSERT_TRUE(replacement_6.IsApplicable(context.get(), fact_manager));
+  replacement_6.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Replace %33 with %103[0] in '%86 = OpCopyObject %30 %33'
+  auto instruction_descriptor_7 =
+      MakeInstructionDescriptor(86, SpvOpCopyObject, 0);
+  auto extract_7 =
+      TransformationCompositeExtract(instruction_descriptor_7, 206, 103, {0});
+  ASSERT_TRUE(extract_7.IsApplicable(context.get(), fact_manager));
+  extract_7.Apply(context.get(), &fact_manager);
+  auto replacement_7 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(33, instruction_descriptor_7, 0), 206);
+  ASSERT_TRUE(replacement_7.IsApplicable(context.get(), fact_manager));
+  replacement_7.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Replace %47 with %103[1:3] in '%84 = OpCopyObject %39 %47'
+  auto instruction_descriptor_8 =
+      MakeInstructionDescriptor(84, SpvOpCopyObject, 0);
+  auto shuffle_8 = TransformationVectorShuffle(instruction_descriptor_8, 207,
+                                               103, 103, {1, 2, 3});
+  ASSERT_TRUE(shuffle_8.IsApplicable(context.get(), fact_manager));
+  shuffle_8.Apply(context.get(), &fact_manager);
+  auto replacement_8 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(47, instruction_descriptor_8, 0), 207);
+  ASSERT_TRUE(replacement_8.IsApplicable(context.get(), fact_manager));
+  replacement_8.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Replace %42 with %104[0] in '%85 = OpCopyObject %30 %42'
+  auto instruction_descriptor_9 =
+      MakeInstructionDescriptor(85, SpvOpCopyObject, 0);
+  auto extract_9 =
+      TransformationCompositeExtract(instruction_descriptor_9, 208, 104, {0});
+  ASSERT_TRUE(extract_9.IsApplicable(context.get(), fact_manager));
+  extract_9.Apply(context.get(), &fact_manager);
+  auto replacement_9 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(42, instruction_descriptor_9, 0), 208);
+  ASSERT_TRUE(replacement_9.IsApplicable(context.get(), fact_manager));
+  replacement_9.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Replace %45 with %104[1] in '%63 = OpLogicalOr %30 %45 %46'
+  auto instruction_descriptor_10 =
+      MakeInstructionDescriptor(63, SpvOpLogicalOr, 0);
+  auto extract_10 =
+      TransformationCompositeExtract(instruction_descriptor_10, 209, 104, {1});
+  ASSERT_TRUE(extract_10.IsApplicable(context.get(), fact_manager));
+  extract_10.Apply(context.get(), &fact_manager);
+  auto replacement_10 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(45, instruction_descriptor_10, 0), 209);
+  ASSERT_TRUE(replacement_10.IsApplicable(context.get(), fact_manager));
+  replacement_10.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Replace %38 with %105[0:1] in 'OpStore %36 %38'
+  auto instruction_descriptor_11 = MakeInstructionDescriptor(85, SpvOpStore, 0);
+  auto shuffle_11 = TransformationVectorShuffle(instruction_descriptor_11, 210,
+                                                105, 105, {0, 1});
+  ASSERT_TRUE(shuffle_11.IsApplicable(context.get(), fact_manager));
+  shuffle_11.Apply(context.get(), &fact_manager);
+  auto replacement_11 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(38, instruction_descriptor_11, 1), 210);
+  ASSERT_TRUE(replacement_11.IsApplicable(context.get(), fact_manager));
+  replacement_11.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Replace %46 with %105[2] in '%62 = OpLogicalAnd %30 %45 %46'
+  auto instruction_descriptor_12 =
+      MakeInstructionDescriptor(62, SpvOpLogicalAnd, 0);
+  auto extract_12 =
+      TransformationCompositeExtract(instruction_descriptor_12, 211, 105, {2});
+  ASSERT_TRUE(extract_12.IsApplicable(context.get(), fact_manager));
+  extract_12.Apply(context.get(), &fact_manager);
+  auto replacement_12 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(46, instruction_descriptor_12, 1), 211);
+  ASSERT_TRUE(replacement_12.IsApplicable(context.get(), fact_manager));
+  replacement_12.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  const std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %8 "f"
+               OpName %12 "v2"
+               OpName %18 "v3"
+               OpName %23 "v4"
+               OpName %32 "b"
+               OpName %36 "bv2"
+               OpName %41 "bv3"
+               OpName %50 "bv4"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 42
+         %10 = OpTypeVector %6 2
+         %11 = OpTypePointer Function %10
+         %16 = OpTypeVector %6 3
+         %17 = OpTypePointer Function %16
+         %21 = OpTypeVector %6 4
+         %22 = OpTypePointer Function %21
+         %30 = OpTypeBool
+         %31 = OpTypePointer Function %30
+         %33 = OpConstantFalse %30
+         %34 = OpTypeVector %30 2
+         %35 = OpTypePointer Function %34
+         %37 = OpConstantTrue %30
+         %38 = OpConstantComposite %34 %37 %37
+         %39 = OpTypeVector %30 3
+         %40 = OpTypePointer Function %39
+         %48 = OpTypeVector %30 4
+         %49 = OpTypePointer Function %48
+         %51 = OpTypeInt 32 0
+         %52 = OpConstant %51 2
+         %55 = OpConstant %6 0
+         %57 = OpConstant %51 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %12 = OpVariable %11 Function
+         %18 = OpVariable %17 Function
+         %23 = OpVariable %22 Function
+         %32 = OpVariable %31 Function
+         %36 = OpVariable %35 Function
+         %41 = OpVariable %40 Function
+         %50 = OpVariable %49 Function
+               OpStore %8 %9
+         %13 = OpLoad %6 %8
+         %14 = OpLoad %6 %8
+         %15 = OpCompositeConstruct %10 %13 %14
+               OpStore %12 %15
+         %19 = OpLoad %10 %12
+         %20 = OpVectorShuffle %16 %19 %19 0 0 1
+               OpStore %18 %20
+         %24 = OpLoad %16 %18
+         %25 = OpLoad %6 %8
+         %26 = OpCompositeExtract %6 %24 0
+         %27 = OpCompositeExtract %6 %24 1
+         %28 = OpCompositeExtract %6 %24 2
+         %29 = OpCompositeConstruct %21 %26 %27 %28 %25
+               OpStore %23 %29
+               OpStore %32 %33
+               OpStore %36 %38
+         %42 = OpLoad %30 %32
+         %43 = OpLoad %34 %36
+         %44 = OpVectorShuffle %34 %43 %43 0 0
+         %45 = OpCompositeExtract %30 %44 0
+         %46 = OpCompositeExtract %30 %44 1
+         %47 = OpCompositeConstruct %39 %42 %45 %46
+               OpStore %41 %47
+         %53 = OpAccessChain %7 %23 %52
+         %54 = OpLoad %6 %53
+
+        %100 = OpCompositeConstruct %21 %20 %54
+        %101 = OpCompositeConstruct %21 %15 %19
+        %102 = OpCompositeConstruct %16 %27 %15
+        %103 = OpCompositeConstruct %48 %33 %47
+        %104 = OpCompositeConstruct %34 %42 %45
+        %105 = OpCompositeConstruct %39 %38 %46
+
+        %206 = OpCompositeExtract %30 %103 0
+         %86 = OpCopyObject %30 %206
+        %201 = OpCompositeExtract %6 %100 3
+         %56 = OpFOrdNotEqual %30 %201 %55
+        %200 = OpVectorShuffle %16 %100 %100 0 1 2
+         %80 = OpCopyObject %16 %200
+         %58 = OpAccessChain %7 %18 %57
+         %59 = OpLoad %6 %58
+         %60 = OpFOrdNotEqual %30 %59 %55
+         %61 = OpLoad %34 %36
+        %211 = OpCompositeExtract %30 %105 2
+         %62 = OpLogicalAnd %30 %45 %211
+        %209 = OpCompositeExtract %30 %104 1
+         %63 = OpLogicalOr %30 %209 %46
+         %64 = OpCompositeConstruct %48 %56 %60 %62 %63
+        %202 = OpVectorShuffle %10 %101 %101 0 1
+               OpStore %12 %202
+        %203 = OpVectorShuffle %10 %101 %101 2 3
+         %81 = OpVectorShuffle %16 %203 %19 0 0 1
+        %204 = OpCompositeExtract %6 %102 0
+         %82 = OpCompositeConstruct %21 %26 %204 %28 %25
+        %205 = OpVectorShuffle %10 %102 %102 1 2
+         %83 = OpCopyObject %10 %205
+        %207 = OpVectorShuffle %39 %103 %103 1 2 3
+         %84 = OpCopyObject %39 %207
+               OpStore %50 %64
+        %208 = OpCompositeExtract %30 %104 0
+         %85 = OpCopyObject %30 %208
+        %210 = OpVectorShuffle %34 %105 %105 0 1
+               OpStore %36 %210
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/test/fuzz/equivalence_relation_test.cpp b/test/fuzz/equivalence_relation_test.cpp
new file mode 100644
index 0000000..3f2ea58
--- /dev/null
+++ b/test/fuzz/equivalence_relation_test.cpp
@@ -0,0 +1,145 @@
+// 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.
+
+#include <set>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "source/fuzz/equivalence_relation.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+struct UInt32Equals {
+  bool operator()(const uint32_t* first, const uint32_t* second) const {
+    return *first == *second;
+  }
+};
+
+struct UInt32Hash {
+  size_t operator()(const uint32_t* element) const {
+    return static_cast<size_t>(*element);
+  }
+};
+
+std::vector<uint32_t> ToUIntVector(
+    const std::vector<const uint32_t*>& pointers) {
+  std::vector<uint32_t> result;
+  for (auto pointer : pointers) {
+    result.push_back(*pointer);
+  }
+  return result;
+}
+
+TEST(EquivalenceRelationTest, BasicTest) {
+  EquivalenceRelation<uint32_t, UInt32Hash, UInt32Equals> relation;
+  ASSERT_TRUE(relation.GetAllKnownValues().empty());
+
+  for (uint32_t element = 2; element < 80; element += 2) {
+    relation.MakeEquivalent(0, element);
+    relation.MakeEquivalent(element - 1, element + 1);
+  }
+
+  for (uint32_t element = 82; element < 100; element += 2) {
+    relation.MakeEquivalent(80, element);
+    relation.MakeEquivalent(element - 1, element + 1);
+  }
+
+  relation.MakeEquivalent(78, 80);
+
+  std::vector<uint32_t> class1;
+  for (uint32_t element = 0; element < 98; element += 2) {
+    ASSERT_TRUE(relation.IsEquivalent(0, element));
+    ASSERT_TRUE(relation.IsEquivalent(element, element + 2));
+    class1.push_back(element);
+  }
+  class1.push_back(98);
+
+  ASSERT_THAT(ToUIntVector(relation.GetEquivalenceClass(0)),
+              testing::WhenSorted(class1));
+  ASSERT_THAT(ToUIntVector(relation.GetEquivalenceClass(4)),
+              testing::WhenSorted(class1));
+  ASSERT_THAT(ToUIntVector(relation.GetEquivalenceClass(40)),
+              testing::WhenSorted(class1));
+
+  std::vector<uint32_t> class2;
+  for (uint32_t element = 1; element < 79; element += 2) {
+    ASSERT_TRUE(relation.IsEquivalent(1, element));
+    ASSERT_TRUE(relation.IsEquivalent(element, element + 2));
+    class2.push_back(element);
+  }
+  class2.push_back(79);
+  ASSERT_THAT(ToUIntVector(relation.GetEquivalenceClass(1)),
+              testing::WhenSorted(class2));
+  ASSERT_THAT(ToUIntVector(relation.GetEquivalenceClass(11)),
+              testing::WhenSorted(class2));
+  ASSERT_THAT(ToUIntVector(relation.GetEquivalenceClass(31)),
+              testing::WhenSorted(class2));
+
+  std::vector<uint32_t> class3;
+  for (uint32_t element = 81; element < 99; element += 2) {
+    ASSERT_TRUE(relation.IsEquivalent(81, element));
+    ASSERT_TRUE(relation.IsEquivalent(element, element + 2));
+    class3.push_back(element);
+  }
+  class3.push_back(99);
+  ASSERT_THAT(ToUIntVector(relation.GetEquivalenceClass(81)),
+              testing::WhenSorted(class3));
+  ASSERT_THAT(ToUIntVector(relation.GetEquivalenceClass(91)),
+              testing::WhenSorted(class3));
+  ASSERT_THAT(ToUIntVector(relation.GetEquivalenceClass(99)),
+              testing::WhenSorted(class3));
+
+  bool first = true;
+  std::vector<const uint32_t*> previous_class;
+  for (auto representative : relation.GetEquivalenceClassRepresentatives()) {
+    std::vector<const uint32_t*> current_class =
+        relation.GetEquivalenceClass(*representative);
+    ASSERT_TRUE(std::find(current_class.begin(), current_class.end(),
+                          representative) != current_class.end());
+    if (!first) {
+      ASSERT_TRUE(std::find(previous_class.begin(), previous_class.end(),
+                            representative) == previous_class.end());
+    }
+    previous_class = current_class;
+    first = false;
+  }
+}
+
+TEST(EquivalenceRelationTest, DeterministicEquivalenceClassOrder) {
+  EquivalenceRelation<uint32_t, UInt32Hash, UInt32Equals> relation1;
+  EquivalenceRelation<uint32_t, UInt32Hash, UInt32Equals> relation2;
+
+  for (uint32_t i = 0; i < 1000; ++i) {
+    if (i >= 10) {
+      relation1.MakeEquivalent(i, i - 10);
+      relation2.MakeEquivalent(i, i - 10);
+    }
+  }
+
+  // We constructed the equivalence relations in the same way, so we would like
+  // them to have identical representatives, and identically-ordered equivalence
+  // classes per representative.
+  ASSERT_THAT(ToUIntVector(relation1.GetEquivalenceClassRepresentatives()),
+              ToUIntVector(relation2.GetEquivalenceClassRepresentatives()));
+  for (auto representative : relation1.GetEquivalenceClassRepresentatives()) {
+    ASSERT_THAT(ToUIntVector(relation1.GetEquivalenceClass(*representative)),
+                ToUIntVector(relation2.GetEquivalenceClass(*representative)));
+  }
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/test/fuzz/fact_manager_test.cpp b/test/fuzz/fact_manager_test.cpp
index 738f8c9..b3f32cd 100644
--- a/test/fuzz/fact_manager_test.cpp
+++ b/test/fuzz/fact_manager_test.cpp
@@ -738,6 +738,441 @@
                              uniform_buffer_element_descriptor));
 }
 
+TEST(FactManagerTest, DataSynonymFacts) {
+  // The SPIR-V types and constants come from the following code.  The body of
+  // the SPIR-V function then constructs a composite that is synonymous with
+  // myT.
+  //
+  // #version 310 es
+  //
+  // precision highp float;
+  //
+  // struct S {
+  //   int a;
+  //   uvec2 b;
+  // };
+  //
+  // struct T {
+  //   bool c[5];
+  //   mat4x2 d;
+  //   S e;
+  // };
+  //
+  // void main() {
+  //   T myT = T(bool[5](true, false, true, false, true),
+  //             mat4x2(vec2(1.0, 2.0), vec2(3.0, 4.0),
+  // 	           vec2(5.0, 6.0), vec2(7.0, 8.0)),
+  //             S(10, uvec2(100u, 200u)));
+  // }
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %15 "S"
+               OpMemberName %15 0 "a"
+               OpMemberName %15 1 "b"
+               OpName %16 "T"
+               OpMemberName %16 0 "c"
+               OpMemberName %16 1 "d"
+               OpMemberName %16 2 "e"
+               OpName %18 "myT"
+               OpMemberDecorate %15 0 RelaxedPrecision
+               OpMemberDecorate %15 1 RelaxedPrecision
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool
+          %7 = OpTypeInt 32 0
+          %8 = OpConstant %7 5
+          %9 = OpTypeArray %6 %8
+         %10 = OpTypeFloat 32
+         %11 = OpTypeVector %10 2
+         %12 = OpTypeMatrix %11 4
+         %13 = OpTypeInt 32 1
+         %14 = OpTypeVector %7 2
+         %15 = OpTypeStruct %13 %14
+         %16 = OpTypeStruct %9 %12 %15
+         %17 = OpTypePointer Function %16
+         %19 = OpConstantTrue %6
+         %20 = OpConstantFalse %6
+         %21 = OpConstantComposite %9 %19 %20 %19 %20 %19
+         %22 = OpConstant %10 1
+         %23 = OpConstant %10 2
+         %24 = OpConstantComposite %11 %22 %23
+         %25 = OpConstant %10 3
+         %26 = OpConstant %10 4
+         %27 = OpConstantComposite %11 %25 %26
+         %28 = OpConstant %10 5
+         %29 = OpConstant %10 6
+         %30 = OpConstantComposite %11 %28 %29
+         %31 = OpConstant %10 7
+         %32 = OpConstant %10 8
+         %33 = OpConstantComposite %11 %31 %32
+         %34 = OpConstantComposite %12 %24 %27 %30 %33
+         %35 = OpConstant %13 10
+         %36 = OpConstant %7 100
+         %37 = OpConstant %7 200
+         %38 = OpConstantComposite %14 %36 %37
+         %39 = OpConstantComposite %15 %35 %38
+         %40 = OpConstantComposite %16 %21 %34 %39
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %18 = OpVariable %17 Function
+               OpStore %18 %40
+        %100 = OpCompositeConstruct %9 %19 %20 %19 %20 %19
+        %101 = OpCompositeConstruct %11 %22 %23
+        %102 = OpCompositeConstruct %11 %25 %26
+        %103 = OpCompositeConstruct %11 %28 %29
+        %104 = OpCompositeConstruct %11 %31 %32
+        %105 = OpCompositeConstruct %12 %101 %102 %103 %104
+        %106 = OpCompositeConstruct %14 %36 %37
+        %107 = OpCompositeConstruct %15 %35 %106
+        %108 = OpCompositeConstruct %16 %100 %105 %107
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  ASSERT_FALSE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(24, {}), MakeDataDescriptor(101, {}), context.get()));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(24, {0}),
+                                         MakeDataDescriptor(101, {0}),
+                                         context.get()));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(24, {1}),
+                                         MakeDataDescriptor(101, {1}),
+                                         context.get()));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(24, {0}),
+                                         MakeDataDescriptor(101, {1}),
+                                         context.get()));
+
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(24, {}),
+                                  MakeDataDescriptor(101, {}), context.get());
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(24, {}), MakeDataDescriptor(101, {}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(24, {0}),
+                                        MakeDataDescriptor(101, {0}),
+                                        context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(24, {1}),
+                                        MakeDataDescriptor(101, {1}),
+                                        context.get()));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(24, {0}),
+                                         MakeDataDescriptor(101, {1}),
+                                         context.get()));
+
+  ASSERT_FALSE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(27, {}), MakeDataDescriptor(102, {}), context.get()));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(27, {0}),
+                                         MakeDataDescriptor(102, {0}),
+                                         context.get()));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(27, {1}),
+                                         MakeDataDescriptor(102, {1}),
+                                         context.get()));
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(27, {0}),
+                                  MakeDataDescriptor(102, {0}), context.get());
+  ASSERT_FALSE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(27, {}), MakeDataDescriptor(102, {}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(27, {0}),
+                                        MakeDataDescriptor(102, {0}),
+                                        context.get()));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(27, {1}),
+                                         MakeDataDescriptor(102, {1}),
+                                         context.get()));
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(27, {1}),
+                                  MakeDataDescriptor(102, {1}), context.get());
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(27, {}), MakeDataDescriptor(102, {}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(27, {0}),
+                                        MakeDataDescriptor(102, {0}),
+                                        context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(27, {1}),
+                                        MakeDataDescriptor(102, {1}),
+                                        context.get()));
+
+  ASSERT_FALSE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(30, {}), MakeDataDescriptor(103, {}), context.get()));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(30, {0}),
+                                         MakeDataDescriptor(103, {0}),
+                                         context.get()));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(30, {1}),
+                                         MakeDataDescriptor(103, {1}),
+                                         context.get()));
+  ASSERT_FALSE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(33, {}), MakeDataDescriptor(104, {}), context.get()));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(33, {0}),
+                                         MakeDataDescriptor(104, {0}),
+                                         context.get()));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(33, {1}),
+                                         MakeDataDescriptor(104, {1}),
+                                         context.get()));
+  ASSERT_FALSE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(34, {}), MakeDataDescriptor(105, {}), context.get()));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(34, {0}),
+                                         MakeDataDescriptor(105, {0}),
+                                         context.get()));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(34, {1}),
+                                         MakeDataDescriptor(105, {1}),
+                                         context.get()));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(34, {2}),
+                                         MakeDataDescriptor(105, {2}),
+                                         context.get()));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(34, {3}),
+                                         MakeDataDescriptor(105, {3}),
+                                         context.get()));
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(30, {}),
+                                  MakeDataDescriptor(103, {}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(33, {}),
+                                  MakeDataDescriptor(104, {}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(34, {0}),
+                                  MakeDataDescriptor(105, {0}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(34, {1}),
+                                  MakeDataDescriptor(105, {1}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(34, {2}),
+                                  MakeDataDescriptor(105, {2}), context.get());
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(30, {}), MakeDataDescriptor(103, {}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(30, {0}),
+                                        MakeDataDescriptor(103, {0}),
+                                        context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(30, {1}),
+                                        MakeDataDescriptor(103, {1}),
+                                        context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(33, {}), MakeDataDescriptor(104, {}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(33, {0}),
+                                        MakeDataDescriptor(104, {0}),
+                                        context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(33, {1}),
+                                        MakeDataDescriptor(104, {1}),
+                                        context.get()));
+  ASSERT_FALSE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(34, {}), MakeDataDescriptor(105, {}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(34, {0}),
+                                        MakeDataDescriptor(105, {0}),
+                                        context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(34, {1}),
+                                        MakeDataDescriptor(105, {1}),
+                                        context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(34, {2}),
+                                        MakeDataDescriptor(105, {2}),
+                                        context.get()));
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(34, {3}),
+                                         MakeDataDescriptor(105, {3}),
+                                         context.get()));
+
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(34, {3}),
+                                  MakeDataDescriptor(105, {3}), context.get());
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(33, {0}),
+                                        MakeDataDescriptor(104, {0}),
+                                        context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(34, {3}),
+                                        MakeDataDescriptor(105, {3}),
+                                        context.get()));
+
+  ASSERT_FALSE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(21, {}), MakeDataDescriptor(100, {}), context.get()));
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(21, {0}),
+                                  MakeDataDescriptor(100, {0}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(21, {1}),
+                                  MakeDataDescriptor(100, {1}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(21, {2}),
+                                  MakeDataDescriptor(100, {2}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(21, {3}),
+                                  MakeDataDescriptor(100, {3}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(21, {4}),
+                                  MakeDataDescriptor(100, {4}), context.get());
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(21, {}), MakeDataDescriptor(100, {}), context.get()));
+
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(39, {0}),
+                                         MakeDataDescriptor(107, {0}),
+                                         context.get()));
+  ASSERT_FALSE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(35, {}), MakeDataDescriptor(39, {0}), context.get()));
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(39, {0}),
+                                  MakeDataDescriptor(35, {}), context.get());
+  ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(39, {0}),
+                                         MakeDataDescriptor(107, {0}),
+                                         context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(35, {}), MakeDataDescriptor(39, {0}), context.get()));
+
+  ASSERT_FALSE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(38, {0}), MakeDataDescriptor(36, {}), context.get()));
+  ASSERT_FALSE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(38, {1}), MakeDataDescriptor(37, {}), context.get()));
+  ASSERT_FALSE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(106, {0}), MakeDataDescriptor(36, {}), context.get()));
+  ASSERT_FALSE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(106, {1}), MakeDataDescriptor(37, {}), context.get()));
+  ASSERT_FALSE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(38, {}), MakeDataDescriptor(106, {}), context.get()));
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(38, {0}),
+                                  MakeDataDescriptor(36, {}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(106, {0}),
+                                  MakeDataDescriptor(36, {}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(38, {1}),
+                                  MakeDataDescriptor(37, {}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(106, {1}),
+                                  MakeDataDescriptor(37, {}), context.get());
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(38, {0}), MakeDataDescriptor(36, {}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(38, {1}), MakeDataDescriptor(37, {}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(106, {0}), MakeDataDescriptor(36, {}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(106, {1}), MakeDataDescriptor(37, {}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(38, {}), MakeDataDescriptor(106, {}), context.get()));
+
+  ASSERT_FALSE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(40, {}), MakeDataDescriptor(108, {}), context.get()));
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(107, {0}),
+                                  MakeDataDescriptor(35, {}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(40, {0}),
+                                  MakeDataDescriptor(108, {0}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(40, {1}),
+                                  MakeDataDescriptor(108, {1}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(40, {2}),
+                                  MakeDataDescriptor(108, {2}), context.get());
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(40, {}), MakeDataDescriptor(108, {}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {0}),
+                                        MakeDataDescriptor(108, {0}),
+                                        context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1}),
+                                        MakeDataDescriptor(108, {1}),
+                                        context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {2}),
+                                        MakeDataDescriptor(108, {2}),
+                                        context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {0, 0}),
+                                        MakeDataDescriptor(108, {0, 0}),
+                                        context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {0, 1}),
+                                        MakeDataDescriptor(108, {0, 1}),
+                                        context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {0, 2}),
+                                        MakeDataDescriptor(108, {0, 2}),
+                                        context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {0, 3}),
+                                        MakeDataDescriptor(108, {0, 3}),
+                                        context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {0, 4}),
+                                        MakeDataDescriptor(108, {0, 4}),
+                                        context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 0}),
+                                        MakeDataDescriptor(108, {1, 0}),
+                                        context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 1}),
+                                        MakeDataDescriptor(108, {1, 1}),
+                                        context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 2}),
+                                        MakeDataDescriptor(108, {1, 2}),
+                                        context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 3}),
+                                        MakeDataDescriptor(108, {1, 3}),
+                                        context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 0, 0}),
+                                        MakeDataDescriptor(108, {1, 0, 0}),
+                                        context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 1, 0}),
+                                        MakeDataDescriptor(108, {1, 1, 0}),
+                                        context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 2, 0}),
+                                        MakeDataDescriptor(108, {1, 2, 0}),
+                                        context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 3, 0}),
+                                        MakeDataDescriptor(108, {1, 3, 0}),
+                                        context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 0, 1}),
+                                        MakeDataDescriptor(108, {1, 0, 1}),
+                                        context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 1, 1}),
+                                        MakeDataDescriptor(108, {1, 1, 1}),
+                                        context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 2, 1}),
+                                        MakeDataDescriptor(108, {1, 2, 1}),
+                                        context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {1, 3, 1}),
+                                        MakeDataDescriptor(108, {1, 3, 1}),
+                                        context.get()));
+
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {2, 0}),
+                                        MakeDataDescriptor(108, {2, 0}),
+                                        context.get()));
+
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {2, 1}),
+                                        MakeDataDescriptor(108, {2, 1}),
+                                        context.get()));
+
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {2, 1, 0}),
+                                        MakeDataDescriptor(108, {2, 1, 0}),
+                                        context.get()));
+
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {2, 1, 1}),
+                                        MakeDataDescriptor(108, {2, 1, 1}),
+                                        context.get()));
+}
+
+TEST(FactManagerTest, RecursiveAdditionOfFacts) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %12 "main"
+               OpExecutionMode %12 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeVector %6 4
+          %8 = OpTypeMatrix %7 4
+          %9 = OpConstant %6 0
+         %10 = OpConstantComposite %7 %9 %9 %9 %9
+         %11 = OpConstantComposite %8 %10 %10 %10 %10
+         %12 = OpFunction %2 None %3
+         %13 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(10, {}),
+                                  MakeDataDescriptor(11, {2}), context.get());
+
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(10, {}), MakeDataDescriptor(11, {2}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(10, {0}),
+                                        MakeDataDescriptor(11, {2, 0}),
+                                        context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(10, {1}),
+                                        MakeDataDescriptor(11, {2, 1}),
+                                        context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(10, {2}),
+                                        MakeDataDescriptor(11, {2, 2}),
+                                        context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(10, {3}),
+                                        MakeDataDescriptor(11, {2, 3}),
+                                        context.get()));
+}
+
 }  // namespace
 }  // namespace fuzz
 }  // namespace spvtools
diff --git a/test/fuzz/fuzz_test_util.cpp b/test/fuzz/fuzz_test_util.cpp
index e2b9518..bc6d4ee 100644
--- a/test/fuzz/fuzz_test_util.cpp
+++ b/test/fuzz/fuzz_test_util.cpp
@@ -16,6 +16,8 @@
 
 #include <iostream>
 
+#include "tools/io.h"
+
 namespace spvtools {
 namespace fuzz {
 
@@ -89,5 +91,19 @@
   return result;
 }
 
+void DumpShader(opt::IRContext* context, const char* filename) {
+  std::vector<uint32_t> binary;
+  context->module()->ToBinary(&binary, false);
+  DumpShader(binary, filename);
+}
+
+void DumpShader(const std::vector<uint32_t>& binary, const char* filename) {
+  auto write_file_succeeded =
+      WriteFile(filename, "wb", &binary[0], binary.size());
+  if (!write_file_succeeded) {
+    std::cerr << "Failed to dump shader" << std::endl;
+  }
+}
+
 }  // namespace fuzz
 }  // namespace spvtools
diff --git a/test/fuzz/fuzz_test_util.h b/test/fuzz/fuzz_test_util.h
index 88a0b20..93f37e5 100644
--- a/test/fuzz/fuzz_test_util.h
+++ b/test/fuzz/fuzz_test_util.h
@@ -93,6 +93,13 @@
   }
 };
 
+// Dumps the SPIRV-V module in |context| to file |filename|. Useful for
+// interactive debugging.
+void DumpShader(opt::IRContext* context, const char* filename);
+
+// Dumps |binary| to file |filename|. Useful for interactive debugging.
+void DumpShader(const std::vector<uint32_t>& binary, const char* filename);
+
 }  // namespace fuzz
 }  // namespace spvtools
 
diff --git a/test/fuzz/fuzzer_replayer_test.cpp b/test/fuzz/fuzzer_replayer_test.cpp
index 550c4fc..22a26fc 100644
--- a/test/fuzz/fuzzer_replayer_test.cpp
+++ b/test/fuzz/fuzzer_replayer_test.cpp
@@ -21,6 +21,28 @@
 namespace fuzz {
 namespace {
 
+const uint32_t kNumFuzzerRuns = 20;
+
+void AddConstantUniformFact(protobufs::FactSequence* facts,
+                            uint32_t descriptor_set, uint32_t binding,
+                            std::vector<uint32_t>&& indices, uint32_t value) {
+  protobufs::FactConstantUniform fact;
+  *fact.mutable_uniform_buffer_element_descriptor() =
+      MakeUniformBufferElementDescriptor(descriptor_set, binding,
+                                         std::move(indices));
+  *fact.mutable_constant_word()->Add() = value;
+  protobufs::Fact temp;
+  *temp.mutable_constant_uniform_fact() = fact;
+  *facts->mutable_fact()->Add() = temp;
+}
+
+// Reinterpret the bits of |value| as a 32-bit unsigned int
+uint32_t FloatBitsAsUint(float value) {
+  uint32_t result;
+  memcpy(&result, &value, sizeof(float));
+  return result;
+}
+
 // Assembles the given |shader| text, and then runs the fuzzer |num_runs|
 // times, using successive seeds starting from |initial_seed|.  Checks that
 // the binary produced after each fuzzer run is valid, and that replaying
@@ -29,7 +51,7 @@
 void RunFuzzerAndReplayer(const std::string& shader,
                           const protobufs::FactSequence& initial_facts,
                           uint32_t initial_seed, uint32_t num_runs) {
-  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto env = SPV_ENV_UNIVERSAL_1_5;
 
   std::vector<uint32_t> binary_in;
   SpirvTools t(env);
@@ -54,7 +76,7 @@
     std::vector<uint32_t> replayer_binary_out;
     protobufs::TransformationSequence replayer_transformation_sequence_out;
 
-    Replayer replayer(env);
+    Replayer replayer(env, false);
     replayer.SetMessageConsumer(kSilentConsumer);
     auto replayer_result_status = replayer.Run(
         binary_in, initial_facts, fuzzer_transformation_sequence_out,
@@ -240,9 +262,9 @@
                OpFunctionEnd
   )";
 
-  // Do 5 fuzzer runs, starting from an initial seed of 0 (seed value chosen
+  // Do some fuzzer runs, starting from an initial seed of 0 (seed value chosen
   // arbitrarily).
-  RunFuzzerAndReplayer(shader, protobufs::FactSequence(), 0, 5);
+  RunFuzzerAndReplayer(shader, protobufs::FactSequence(), 0, kNumFuzzerRuns);
 }
 
 TEST(FuzzerReplayerTest, Miscellaneous2) {
@@ -303,7 +325,7 @@
                OpCapability Shader
           %1 = OpExtInstImport "GLSL.std.450"
                OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %4 "main" %16 %139
+               OpEntryPoint Fragment %4 "main" %16 %139 %25 %68
                OpExecutionMode %4 OriginUpperLeft
                OpSource ESSL 310
                OpName %4 "main"
@@ -485,9 +507,9 @@
                OpFunctionEnd
   )";
 
-  // Do 5 fuzzer runs, starting from an initial seed of 10 (seed value chosen
+  // Do some fuzzer runs, starting from an initial seed of 10 (seed value chosen
   // arbitrarily).
-  RunFuzzerAndReplayer(shader, protobufs::FactSequence(), 10, 5);
+  RunFuzzerAndReplayer(shader, protobufs::FactSequence(), 10, kNumFuzzerRuns);
 }
 
 TEST(FuzzerReplayerTest, Miscellaneous3) {
@@ -603,7 +625,7 @@
                OpCapability Shader
           %1 = OpExtInstImport "GLSL.std.450"
                OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %4 "main" %68 %100
+               OpEntryPoint Fragment %4 "main" %68 %100 %24
                OpExecutionMode %4 OriginUpperLeft
                OpSource ESSL 310
                OpName %4 "main"
@@ -951,28 +973,657 @@
 
   // Add the facts "resolution.x == 250" and "resolution.y == 100".
   protobufs::FactSequence facts;
-  {
-    protobufs::FactConstantUniform resolution_x_eq_250;
-    *resolution_x_eq_250.mutable_uniform_buffer_element_descriptor() =
-        MakeUniformBufferElementDescriptor(0, 0, {0, 0});
-    *resolution_x_eq_250.mutable_constant_word()->Add() = 250;
-    protobufs::Fact temp;
-    *temp.mutable_constant_uniform_fact() = resolution_x_eq_250;
-    *facts.mutable_fact()->Add() = temp;
-  }
-  {
-    protobufs::FactConstantUniform resolution_y_eq_100;
-    *resolution_y_eq_100.mutable_uniform_buffer_element_descriptor() =
-        MakeUniformBufferElementDescriptor(0, 0, {0, 1});
-    *resolution_y_eq_100.mutable_constant_word()->Add() = 100;
-    protobufs::Fact temp;
-    *temp.mutable_constant_uniform_fact() = resolution_y_eq_100;
-    *facts.mutable_fact()->Add() = temp;
-  }
+  AddConstantUniformFact(&facts, 0, 0, {0, 0}, 250);
+  AddConstantUniformFact(&facts, 0, 0, {0, 1}, 100);
 
-  // Do 5 fuzzer runs, starting from an initial seed of 94 (seed value chosen
+  // Do some fuzzer runs, starting from an initial seed of 94 (seed value chosen
   // arbitrarily).
-  RunFuzzerAndReplayer(shader, facts, 94, 5);
+  RunFuzzerAndReplayer(shader, facts, 94, kNumFuzzerRuns);
+}
+
+TEST(FuzzerReplayerTest, Miscellaneous4) {
+  // The SPIR-V comes from the 'matrices_smart_loops' GLSL shader that ships
+  // with GraphicsFuzz.
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %327 %363 %65 %70 %80 %90 %99 %108 %117 %126 %135 %144 %333
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %8 "matrix_number"
+               OpName %12 "cols"
+               OpName %23 "rows"
+               OpName %31 "c"
+               OpName %41 "r"
+               OpName %65 "m22"
+               OpName %68 "buf0"
+               OpMemberName %68 0 "one"
+               OpName %70 ""
+               OpName %80 "m23"
+               OpName %90 "m24"
+               OpName %99 "m32"
+               OpName %108 "m33"
+               OpName %117 "m34"
+               OpName %126 "m42"
+               OpName %135 "m43"
+               OpName %144 "m44"
+               OpName %164 "sum_index"
+               OpName %165 "cols"
+               OpName %173 "rows"
+               OpName %184 "sums"
+               OpName %189 "c"
+               OpName %198 "r"
+               OpName %325 "region_x"
+               OpName %327 "gl_FragCoord"
+               OpName %331 "buf1"
+               OpMemberName %331 0 "resolution"
+               OpName %333 ""
+               OpName %340 "region_y"
+               OpName %348 "overall_region"
+               OpName %363 "_GLF_color"
+               OpDecorate %8 RelaxedPrecision
+               OpDecorate %12 RelaxedPrecision
+               OpDecorate %19 RelaxedPrecision
+               OpDecorate %23 RelaxedPrecision
+               OpDecorate %29 RelaxedPrecision
+               OpDecorate %31 RelaxedPrecision
+               OpDecorate %38 RelaxedPrecision
+               OpDecorate %39 RelaxedPrecision
+               OpDecorate %41 RelaxedPrecision
+               OpDecorate %47 RelaxedPrecision
+               OpDecorate %48 RelaxedPrecision
+               OpDecorate %50 RelaxedPrecision
+               OpDecorate %66 RelaxedPrecision
+               OpDecorate %67 RelaxedPrecision
+               OpMemberDecorate %68 0 Offset 0
+               OpDecorate %68 Block
+               OpDecorate %70 DescriptorSet 0
+               OpDecorate %70 Binding 0
+               OpDecorate %81 RelaxedPrecision
+               OpDecorate %82 RelaxedPrecision
+               OpDecorate %91 RelaxedPrecision
+               OpDecorate %92 RelaxedPrecision
+               OpDecorate %100 RelaxedPrecision
+               OpDecorate %101 RelaxedPrecision
+               OpDecorate %109 RelaxedPrecision
+               OpDecorate %110 RelaxedPrecision
+               OpDecorate %118 RelaxedPrecision
+               OpDecorate %119 RelaxedPrecision
+               OpDecorate %127 RelaxedPrecision
+               OpDecorate %128 RelaxedPrecision
+               OpDecorate %136 RelaxedPrecision
+               OpDecorate %137 RelaxedPrecision
+               OpDecorate %145 RelaxedPrecision
+               OpDecorate %146 RelaxedPrecision
+               OpDecorate %152 RelaxedPrecision
+               OpDecorate %154 RelaxedPrecision
+               OpDecorate %155 RelaxedPrecision
+               OpDecorate %156 RelaxedPrecision
+               OpDecorate %157 RelaxedPrecision
+               OpDecorate %159 RelaxedPrecision
+               OpDecorate %160 RelaxedPrecision
+               OpDecorate %161 RelaxedPrecision
+               OpDecorate %162 RelaxedPrecision
+               OpDecorate %163 RelaxedPrecision
+               OpDecorate %164 RelaxedPrecision
+               OpDecorate %165 RelaxedPrecision
+               OpDecorate %171 RelaxedPrecision
+               OpDecorate %173 RelaxedPrecision
+               OpDecorate %179 RelaxedPrecision
+               OpDecorate %185 RelaxedPrecision
+               OpDecorate %189 RelaxedPrecision
+               OpDecorate %195 RelaxedPrecision
+               OpDecorate %196 RelaxedPrecision
+               OpDecorate %198 RelaxedPrecision
+               OpDecorate %204 RelaxedPrecision
+               OpDecorate %205 RelaxedPrecision
+               OpDecorate %207 RelaxedPrecision
+               OpDecorate %218 RelaxedPrecision
+               OpDecorate %219 RelaxedPrecision
+               OpDecorate %220 RelaxedPrecision
+               OpDecorate %228 RelaxedPrecision
+               OpDecorate %229 RelaxedPrecision
+               OpDecorate %230 RelaxedPrecision
+               OpDecorate %238 RelaxedPrecision
+               OpDecorate %239 RelaxedPrecision
+               OpDecorate %240 RelaxedPrecision
+               OpDecorate %248 RelaxedPrecision
+               OpDecorate %249 RelaxedPrecision
+               OpDecorate %250 RelaxedPrecision
+               OpDecorate %258 RelaxedPrecision
+               OpDecorate %259 RelaxedPrecision
+               OpDecorate %260 RelaxedPrecision
+               OpDecorate %268 RelaxedPrecision
+               OpDecorate %269 RelaxedPrecision
+               OpDecorate %270 RelaxedPrecision
+               OpDecorate %278 RelaxedPrecision
+               OpDecorate %279 RelaxedPrecision
+               OpDecorate %280 RelaxedPrecision
+               OpDecorate %288 RelaxedPrecision
+               OpDecorate %289 RelaxedPrecision
+               OpDecorate %290 RelaxedPrecision
+               OpDecorate %298 RelaxedPrecision
+               OpDecorate %299 RelaxedPrecision
+               OpDecorate %300 RelaxedPrecision
+               OpDecorate %309 RelaxedPrecision
+               OpDecorate %310 RelaxedPrecision
+               OpDecorate %311 RelaxedPrecision
+               OpDecorate %312 RelaxedPrecision
+               OpDecorate %313 RelaxedPrecision
+               OpDecorate %319 RelaxedPrecision
+               OpDecorate %320 RelaxedPrecision
+               OpDecorate %321 RelaxedPrecision
+               OpDecorate %322 RelaxedPrecision
+               OpDecorate %323 RelaxedPrecision
+               OpDecorate %324 RelaxedPrecision
+               OpDecorate %325 RelaxedPrecision
+               OpDecorate %327 BuiltIn FragCoord
+               OpMemberDecorate %331 0 Offset 0
+               OpDecorate %331 Block
+               OpDecorate %333 DescriptorSet 0
+               OpDecorate %333 Binding 1
+               OpDecorate %339 RelaxedPrecision
+               OpDecorate %340 RelaxedPrecision
+               OpDecorate %347 RelaxedPrecision
+               OpDecorate %348 RelaxedPrecision
+               OpDecorate %349 RelaxedPrecision
+               OpDecorate %351 RelaxedPrecision
+               OpDecorate %352 RelaxedPrecision
+               OpDecorate %353 RelaxedPrecision
+               OpDecorate %354 RelaxedPrecision
+               OpDecorate %356 RelaxedPrecision
+               OpDecorate %363 Location 0
+               OpDecorate %364 RelaxedPrecision
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 0
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 0
+         %10 = OpTypeInt 32 1
+         %11 = OpTypePointer Function %10
+         %13 = OpConstant %10 2
+         %20 = OpConstant %10 4
+         %21 = OpTypeBool
+         %32 = OpConstant %10 0
+         %61 = OpTypeFloat 32
+         %62 = OpTypeVector %61 2
+         %63 = OpTypeMatrix %62 2
+         %64 = OpTypePointer Private %63
+         %65 = OpVariable %64 Private
+         %68 = OpTypeStruct %61
+         %69 = OpTypePointer Uniform %68
+         %70 = OpVariable %69 Uniform
+         %71 = OpTypePointer Uniform %61
+         %74 = OpTypePointer Private %61
+         %77 = OpTypeVector %61 3
+         %78 = OpTypeMatrix %77 2
+         %79 = OpTypePointer Private %78
+         %80 = OpVariable %79 Private
+         %87 = OpTypeVector %61 4
+         %88 = OpTypeMatrix %87 2
+         %89 = OpTypePointer Private %88
+         %90 = OpVariable %89 Private
+         %97 = OpTypeMatrix %62 3
+         %98 = OpTypePointer Private %97
+         %99 = OpVariable %98 Private
+        %106 = OpTypeMatrix %77 3
+        %107 = OpTypePointer Private %106
+        %108 = OpVariable %107 Private
+        %115 = OpTypeMatrix %87 3
+        %116 = OpTypePointer Private %115
+        %117 = OpVariable %116 Private
+        %124 = OpTypeMatrix %62 4
+        %125 = OpTypePointer Private %124
+        %126 = OpVariable %125 Private
+        %133 = OpTypeMatrix %77 4
+        %134 = OpTypePointer Private %133
+        %135 = OpVariable %134 Private
+        %142 = OpTypeMatrix %87 4
+        %143 = OpTypePointer Private %142
+        %144 = OpVariable %143 Private
+        %153 = OpConstant %10 1
+        %158 = OpConstant %6 1
+        %181 = OpConstant %6 9
+        %182 = OpTypeArray %61 %181
+        %183 = OpTypePointer Function %182
+        %186 = OpConstant %61 0
+        %187 = OpTypePointer Function %61
+        %314 = OpConstant %61 16
+        %326 = OpTypePointer Input %87
+        %327 = OpVariable %326 Input
+        %328 = OpTypePointer Input %61
+        %331 = OpTypeStruct %62
+        %332 = OpTypePointer Uniform %331
+        %333 = OpVariable %332 Uniform
+        %336 = OpConstant %61 3
+        %350 = OpConstant %10 3
+        %357 = OpConstant %10 9
+        %362 = OpTypePointer Output %87
+        %363 = OpVariable %362 Output
+        %368 = OpConstant %61 1
+        %374 = OpConstantComposite %87 %186 %186 %186 %368
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %12 = OpVariable %11 Function
+         %23 = OpVariable %11 Function
+         %31 = OpVariable %11 Function
+         %41 = OpVariable %11 Function
+        %164 = OpVariable %11 Function
+        %165 = OpVariable %11 Function
+        %173 = OpVariable %11 Function
+        %184 = OpVariable %183 Function
+        %189 = OpVariable %11 Function
+        %198 = OpVariable %11 Function
+        %325 = OpVariable %11 Function
+        %340 = OpVariable %11 Function
+        %348 = OpVariable %11 Function
+               OpStore %8 %9
+               OpStore %12 %13
+               OpBranch %14
+         %14 = OpLabel
+               OpLoopMerge %16 %17 None
+               OpBranch %18
+         %18 = OpLabel
+         %19 = OpLoad %10 %12
+         %22 = OpSLessThanEqual %21 %19 %20
+               OpBranchConditional %22 %15 %16
+         %15 = OpLabel
+               OpStore %23 %13
+               OpBranch %24
+         %24 = OpLabel
+               OpLoopMerge %26 %27 None
+               OpBranch %28
+         %28 = OpLabel
+         %29 = OpLoad %10 %23
+         %30 = OpSLessThanEqual %21 %29 %20
+               OpBranchConditional %30 %25 %26
+         %25 = OpLabel
+               OpStore %31 %32
+               OpBranch %33
+         %33 = OpLabel
+               OpLoopMerge %35 %36 None
+               OpBranch %37
+         %37 = OpLabel
+         %38 = OpLoad %10 %31
+         %39 = OpLoad %10 %12
+         %40 = OpSLessThan %21 %38 %39
+               OpBranchConditional %40 %34 %35
+         %34 = OpLabel
+               OpStore %41 %32
+               OpBranch %42
+         %42 = OpLabel
+               OpLoopMerge %44 %45 None
+               OpBranch %46
+         %46 = OpLabel
+         %47 = OpLoad %10 %41
+         %48 = OpLoad %10 %23
+         %49 = OpSLessThan %21 %47 %48
+               OpBranchConditional %49 %43 %44
+         %43 = OpLabel
+         %50 = OpLoad %6 %8
+               OpSelectionMerge %60 None
+               OpSwitch %50 %60 0 %51 1 %52 2 %53 3 %54 4 %55 5 %56 6 %57 7 %58 8 %59
+         %51 = OpLabel
+         %66 = OpLoad %10 %31
+         %67 = OpLoad %10 %41
+         %72 = OpAccessChain %71 %70 %32
+         %73 = OpLoad %61 %72
+         %75 = OpAccessChain %74 %65 %66 %67
+               OpStore %75 %73
+               OpBranch %60
+         %52 = OpLabel
+         %81 = OpLoad %10 %31
+         %82 = OpLoad %10 %41
+         %83 = OpAccessChain %71 %70 %32
+         %84 = OpLoad %61 %83
+         %85 = OpAccessChain %74 %80 %81 %82
+               OpStore %85 %84
+               OpBranch %60
+         %53 = OpLabel
+         %91 = OpLoad %10 %31
+         %92 = OpLoad %10 %41
+         %93 = OpAccessChain %71 %70 %32
+         %94 = OpLoad %61 %93
+         %95 = OpAccessChain %74 %90 %91 %92
+               OpStore %95 %94
+               OpBranch %60
+         %54 = OpLabel
+        %100 = OpLoad %10 %31
+        %101 = OpLoad %10 %41
+        %102 = OpAccessChain %71 %70 %32
+        %103 = OpLoad %61 %102
+        %104 = OpAccessChain %74 %99 %100 %101
+               OpStore %104 %103
+               OpBranch %60
+         %55 = OpLabel
+        %109 = OpLoad %10 %31
+        %110 = OpLoad %10 %41
+        %111 = OpAccessChain %71 %70 %32
+        %112 = OpLoad %61 %111
+        %113 = OpAccessChain %74 %108 %109 %110
+               OpStore %113 %112
+               OpBranch %60
+         %56 = OpLabel
+        %118 = OpLoad %10 %31
+        %119 = OpLoad %10 %41
+        %120 = OpAccessChain %71 %70 %32
+        %121 = OpLoad %61 %120
+        %122 = OpAccessChain %74 %117 %118 %119
+               OpStore %122 %121
+               OpBranch %60
+         %57 = OpLabel
+        %127 = OpLoad %10 %31
+        %128 = OpLoad %10 %41
+        %129 = OpAccessChain %71 %70 %32
+        %130 = OpLoad %61 %129
+        %131 = OpAccessChain %74 %126 %127 %128
+               OpStore %131 %130
+               OpBranch %60
+         %58 = OpLabel
+        %136 = OpLoad %10 %31
+        %137 = OpLoad %10 %41
+        %138 = OpAccessChain %71 %70 %32
+        %139 = OpLoad %61 %138
+        %140 = OpAccessChain %74 %135 %136 %137
+               OpStore %140 %139
+               OpBranch %60
+         %59 = OpLabel
+        %145 = OpLoad %10 %31
+        %146 = OpLoad %10 %41
+        %147 = OpAccessChain %71 %70 %32
+        %148 = OpLoad %61 %147
+        %149 = OpAccessChain %74 %144 %145 %146
+               OpStore %149 %148
+               OpBranch %60
+         %60 = OpLabel
+               OpBranch %45
+         %45 = OpLabel
+        %152 = OpLoad %10 %41
+        %154 = OpIAdd %10 %152 %153
+               OpStore %41 %154
+               OpBranch %42
+         %44 = OpLabel
+               OpBranch %36
+         %36 = OpLabel
+        %155 = OpLoad %10 %31
+        %156 = OpIAdd %10 %155 %153
+               OpStore %31 %156
+               OpBranch %33
+         %35 = OpLabel
+        %157 = OpLoad %6 %8
+        %159 = OpIAdd %6 %157 %158
+               OpStore %8 %159
+               OpBranch %27
+         %27 = OpLabel
+        %160 = OpLoad %10 %23
+        %161 = OpIAdd %10 %160 %153
+               OpStore %23 %161
+               OpBranch %24
+         %26 = OpLabel
+               OpBranch %17
+         %17 = OpLabel
+        %162 = OpLoad %10 %12
+        %163 = OpIAdd %10 %162 %153
+               OpStore %12 %163
+               OpBranch %14
+         %16 = OpLabel
+               OpStore %164 %32
+               OpStore %165 %13
+               OpBranch %166
+        %166 = OpLabel
+               OpLoopMerge %168 %169 None
+               OpBranch %170
+        %170 = OpLabel
+        %171 = OpLoad %10 %165
+        %172 = OpSLessThanEqual %21 %171 %20
+               OpBranchConditional %172 %167 %168
+        %167 = OpLabel
+               OpStore %173 %13
+               OpBranch %174
+        %174 = OpLabel
+               OpLoopMerge %176 %177 None
+               OpBranch %178
+        %178 = OpLabel
+        %179 = OpLoad %10 %173
+        %180 = OpSLessThanEqual %21 %179 %20
+               OpBranchConditional %180 %175 %176
+        %175 = OpLabel
+        %185 = OpLoad %10 %164
+        %188 = OpAccessChain %187 %184 %185
+               OpStore %188 %186
+               OpStore %189 %32
+               OpBranch %190
+        %190 = OpLabel
+               OpLoopMerge %192 %193 None
+               OpBranch %194
+        %194 = OpLabel
+        %195 = OpLoad %10 %189
+        %196 = OpLoad %10 %165
+        %197 = OpSLessThan %21 %195 %196
+               OpBranchConditional %197 %191 %192
+        %191 = OpLabel
+               OpStore %198 %32
+               OpBranch %199
+        %199 = OpLabel
+               OpLoopMerge %201 %202 None
+               OpBranch %203
+        %203 = OpLabel
+        %204 = OpLoad %10 %198
+        %205 = OpLoad %10 %173
+        %206 = OpSLessThan %21 %204 %205
+               OpBranchConditional %206 %200 %201
+        %200 = OpLabel
+        %207 = OpLoad %10 %164
+               OpSelectionMerge %217 None
+               OpSwitch %207 %217 0 %208 1 %209 2 %210 3 %211 4 %212 5 %213 6 %214 7 %215 8 %216
+        %208 = OpLabel
+        %218 = OpLoad %10 %164
+        %219 = OpLoad %10 %189
+        %220 = OpLoad %10 %198
+        %221 = OpAccessChain %74 %65 %219 %220
+        %222 = OpLoad %61 %221
+        %223 = OpAccessChain %187 %184 %218
+        %224 = OpLoad %61 %223
+        %225 = OpFAdd %61 %224 %222
+        %226 = OpAccessChain %187 %184 %218
+               OpStore %226 %225
+               OpBranch %217
+        %209 = OpLabel
+        %228 = OpLoad %10 %164
+        %229 = OpLoad %10 %189
+        %230 = OpLoad %10 %198
+        %231 = OpAccessChain %74 %80 %229 %230
+        %232 = OpLoad %61 %231
+        %233 = OpAccessChain %187 %184 %228
+        %234 = OpLoad %61 %233
+        %235 = OpFAdd %61 %234 %232
+        %236 = OpAccessChain %187 %184 %228
+               OpStore %236 %235
+               OpBranch %217
+        %210 = OpLabel
+        %238 = OpLoad %10 %164
+        %239 = OpLoad %10 %189
+        %240 = OpLoad %10 %198
+        %241 = OpAccessChain %74 %90 %239 %240
+        %242 = OpLoad %61 %241
+        %243 = OpAccessChain %187 %184 %238
+        %244 = OpLoad %61 %243
+        %245 = OpFAdd %61 %244 %242
+        %246 = OpAccessChain %187 %184 %238
+               OpStore %246 %245
+               OpBranch %217
+        %211 = OpLabel
+        %248 = OpLoad %10 %164
+        %249 = OpLoad %10 %189
+        %250 = OpLoad %10 %198
+        %251 = OpAccessChain %74 %99 %249 %250
+        %252 = OpLoad %61 %251
+        %253 = OpAccessChain %187 %184 %248
+        %254 = OpLoad %61 %253
+        %255 = OpFAdd %61 %254 %252
+        %256 = OpAccessChain %187 %184 %248
+               OpStore %256 %255
+               OpBranch %217
+        %212 = OpLabel
+        %258 = OpLoad %10 %164
+        %259 = OpLoad %10 %189
+        %260 = OpLoad %10 %198
+        %261 = OpAccessChain %74 %108 %259 %260
+        %262 = OpLoad %61 %261
+        %263 = OpAccessChain %187 %184 %258
+        %264 = OpLoad %61 %263
+        %265 = OpFAdd %61 %264 %262
+        %266 = OpAccessChain %187 %184 %258
+               OpStore %266 %265
+               OpBranch %217
+        %213 = OpLabel
+        %268 = OpLoad %10 %164
+        %269 = OpLoad %10 %189
+        %270 = OpLoad %10 %198
+        %271 = OpAccessChain %74 %117 %269 %270
+        %272 = OpLoad %61 %271
+        %273 = OpAccessChain %187 %184 %268
+        %274 = OpLoad %61 %273
+        %275 = OpFAdd %61 %274 %272
+        %276 = OpAccessChain %187 %184 %268
+               OpStore %276 %275
+               OpBranch %217
+        %214 = OpLabel
+        %278 = OpLoad %10 %164
+        %279 = OpLoad %10 %189
+        %280 = OpLoad %10 %198
+        %281 = OpAccessChain %74 %126 %279 %280
+        %282 = OpLoad %61 %281
+        %283 = OpAccessChain %187 %184 %278
+        %284 = OpLoad %61 %283
+        %285 = OpFAdd %61 %284 %282
+        %286 = OpAccessChain %187 %184 %278
+               OpStore %286 %285
+               OpBranch %217
+        %215 = OpLabel
+        %288 = OpLoad %10 %164
+        %289 = OpLoad %10 %189
+        %290 = OpLoad %10 %198
+        %291 = OpAccessChain %74 %135 %289 %290
+        %292 = OpLoad %61 %291
+        %293 = OpAccessChain %187 %184 %288
+        %294 = OpLoad %61 %293
+        %295 = OpFAdd %61 %294 %292
+        %296 = OpAccessChain %187 %184 %288
+               OpStore %296 %295
+               OpBranch %217
+        %216 = OpLabel
+        %298 = OpLoad %10 %164
+        %299 = OpLoad %10 %189
+        %300 = OpLoad %10 %198
+        %301 = OpAccessChain %74 %144 %299 %300
+        %302 = OpLoad %61 %301
+        %303 = OpAccessChain %187 %184 %298
+        %304 = OpLoad %61 %303
+        %305 = OpFAdd %61 %304 %302
+        %306 = OpAccessChain %187 %184 %298
+               OpStore %306 %305
+               OpBranch %217
+        %217 = OpLabel
+               OpBranch %202
+        %202 = OpLabel
+        %309 = OpLoad %10 %198
+        %310 = OpIAdd %10 %309 %153
+               OpStore %198 %310
+               OpBranch %199
+        %201 = OpLabel
+               OpBranch %193
+        %193 = OpLabel
+        %311 = OpLoad %10 %189
+        %312 = OpIAdd %10 %311 %153
+               OpStore %189 %312
+               OpBranch %190
+        %192 = OpLabel
+        %313 = OpLoad %10 %164
+        %315 = OpAccessChain %187 %184 %313
+        %316 = OpLoad %61 %315
+        %317 = OpFDiv %61 %316 %314
+        %318 = OpAccessChain %187 %184 %313
+               OpStore %318 %317
+        %319 = OpLoad %10 %164
+        %320 = OpIAdd %10 %319 %153
+               OpStore %164 %320
+               OpBranch %177
+        %177 = OpLabel
+        %321 = OpLoad %10 %173
+        %322 = OpIAdd %10 %321 %153
+               OpStore %173 %322
+               OpBranch %174
+        %176 = OpLabel
+               OpBranch %169
+        %169 = OpLabel
+        %323 = OpLoad %10 %165
+        %324 = OpIAdd %10 %323 %153
+               OpStore %165 %324
+               OpBranch %166
+        %168 = OpLabel
+        %329 = OpAccessChain %328 %327 %9
+        %330 = OpLoad %61 %329
+        %334 = OpAccessChain %71 %333 %32 %9
+        %335 = OpLoad %61 %334
+        %337 = OpFDiv %61 %335 %336
+        %338 = OpFDiv %61 %330 %337
+        %339 = OpConvertFToS %10 %338
+               OpStore %325 %339
+        %341 = OpAccessChain %328 %327 %158
+        %342 = OpLoad %61 %341
+        %343 = OpAccessChain %71 %333 %32 %9
+        %344 = OpLoad %61 %343
+        %345 = OpFDiv %61 %344 %336
+        %346 = OpFDiv %61 %342 %345
+        %347 = OpConvertFToS %10 %346
+               OpStore %340 %347
+        %349 = OpLoad %10 %340
+        %351 = OpIMul %10 %349 %350
+        %352 = OpLoad %10 %325
+        %353 = OpIAdd %10 %351 %352
+               OpStore %348 %353
+        %354 = OpLoad %10 %348
+        %355 = OpSGreaterThan %21 %354 %32
+        %356 = OpLoad %10 %348
+        %358 = OpSLessThan %21 %356 %357
+        %359 = OpLogicalAnd %21 %355 %358
+               OpSelectionMerge %361 None
+               OpBranchConditional %359 %360 %373
+        %360 = OpLabel
+        %364 = OpLoad %10 %348
+        %365 = OpAccessChain %187 %184 %364
+        %366 = OpLoad %61 %365
+        %367 = OpCompositeConstruct %77 %366 %366 %366
+        %369 = OpCompositeExtract %61 %367 0
+        %370 = OpCompositeExtract %61 %367 1
+        %371 = OpCompositeExtract %61 %367 2
+        %372 = OpCompositeConstruct %87 %369 %370 %371 %368
+               OpStore %363 %372
+               OpBranch %361
+        %373 = OpLabel
+               OpStore %363 %374
+               OpBranch %361
+        %361 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  // Add the facts:
+  //  - "one == 1.0"
+  //  - "resolution.y == 256.0",
+  protobufs::FactSequence facts;
+  AddConstantUniformFact(&facts, 0, 0, {0}, FloatBitsAsUint(1.0));
+  AddConstantUniformFact(&facts, 0, 1, {0, 0}, FloatBitsAsUint(256.0));
+  AddConstantUniformFact(&facts, 0, 1, {0, 1}, FloatBitsAsUint(256.0));
+
+  // Do some fuzzer runs, starting from an initial seed of 14 (seed value chosen
+  // arbitrarily).
+  RunFuzzerAndReplayer(shader, facts, 14, kNumFuzzerRuns);
 }
 
 }  // namespace
diff --git a/test/fuzz/fuzzer_shrinker_test.cpp b/test/fuzz/fuzzer_shrinker_test.cpp
index 85f41bc..6485070 100644
--- a/test/fuzz/fuzzer_shrinker_test.cpp
+++ b/test/fuzz/fuzzer_shrinker_test.cpp
@@ -125,7 +125,7 @@
     const std::vector<uint32_t>& expected_binary_out,
     uint32_t expected_transformations_out_size, uint32_t step_limit) {
   // Run the shrinker.
-  Shrinker shrinker(target_env, step_limit);
+  Shrinker shrinker(target_env, step_limit, false);
   shrinker.SetMessageConsumer(kSilentConsumer);
 
   std::vector<uint32_t> binary_out;
@@ -154,10 +154,11 @@
 void RunFuzzerAndShrinker(const std::string& shader,
                           const protobufs::FactSequence& initial_facts,
                           uint32_t seed) {
-  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto env = SPV_ENV_UNIVERSAL_1_5;
 
   std::vector<uint32_t> binary_in;
   SpirvTools t(env);
+  t.SetMessageConsumer(kConsoleMessageConsumer);
   ASSERT_TRUE(t.Assemble(shader, &binary_in, kFuzzAssembleOption));
   ASSERT_TRUE(t.Validate(binary_in));
 
@@ -433,7 +434,7 @@
                OpCapability Shader
           %1 = OpExtInstImport "GLSL.std.450"
                OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %4 "main" %16 %139
+               OpEntryPoint Fragment %4 "main" %16 %139 %25 %68
                OpExecutionMode %4 OriginUpperLeft
                OpSource ESSL 310
                OpName %4 "main"
@@ -731,7 +732,7 @@
                OpCapability Shader
           %1 = OpExtInstImport "GLSL.std.450"
                OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %4 "main" %68 %100
+               OpEntryPoint Fragment %4 "main" %68 %100 %24
                OpExecutionMode %4 OriginUpperLeft
                OpSource ESSL 310
                OpName %4 "main"
diff --git a/test/fuzz/instruction_descriptor_test.cpp b/test/fuzz/instruction_descriptor_test.cpp
new file mode 100644
index 0000000..5165cfb
--- /dev/null
+++ b/test/fuzz/instruction_descriptor_test.cpp
@@ -0,0 +1,69 @@
+// 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.
+
+#include "source/fuzz/instruction_descriptor.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(InstructionDescriptorTest, BasicTest) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 0
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 0
+         %10 = OpTypeInt 32 1
+         %11 = OpTypePointer Function %10
+         %13 = OpConstant %10 2
+         %32 = OpConstant %10 0
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+        %164 = OpVariable %11 Function
+        %165 = OpVariable %11 Function
+               OpBranch %16
+         %16 = OpLabel
+               OpStore %164 %32
+               OpStore %165 %13
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  for (auto& function : *context->module()) {
+    for (auto& block : function) {
+      for (auto inst_it = block.cbegin(); inst_it != block.cend(); ++inst_it) {
+        ASSERT_EQ(&*inst_it,
+                  FindInstruction(MakeInstructionDescriptor(block, inst_it),
+                                  context.get()));
+      }
+    }
+  }
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/test/fuzz/transformation_add_dead_break_test.cpp b/test/fuzz/transformation_add_dead_break_test.cpp
index 99bc9d9..1dd0c9d 100644
--- a/test/fuzz/transformation_add_dead_break_test.cpp
+++ b/test/fuzz/transformation_add_dead_break_test.cpp
@@ -2080,6 +2080,534 @@
   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
 }
 
+TEST(TransformationAddDeadBreakTest, RespectDominanceRules1) {
+  // Right after the loop, an OpCopyObject defined by the loop is used.  Adding
+  // a dead break would prevent that use from being dominated by its definition,
+  // so is not allowed.
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+         %10 = OpTypeBool
+         %11 = OpConstantFalse %10
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %100
+        %100 = OpLabel
+               OpLoopMerge %101 %102 None
+               OpBranch %103
+        %103 = OpLabel
+        %200 = OpCopyObject %10 %11
+               OpBranch %104
+        %104 = OpLabel
+               OpBranch %102
+        %102 = OpLabel
+               OpBranchConditional %11 %100 %101
+        %101 = OpLabel
+        %201 = OpCopyObject %10 %200
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  auto bad_transformation = TransformationAddDeadBreak(100, 101, false, {});
+  ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager));
+}
+
+TEST(TransformationAddDeadBreakTest, RespectDominanceRules2) {
+  // This example captures the following idiom:
+  //
+  //   if {
+  // L1:
+  //   }
+  //   definition;
+  // L2:
+  //   use;
+  //
+  // Adding a dead jump from L1 to L2 would lead to 'definition' no longer
+  // dominating 'use', and so is not allowed.
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+         %10 = OpTypeBool
+         %11 = OpConstantFalse %10
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %100
+        %100 = OpLabel
+               OpSelectionMerge %101 None
+               OpBranchConditional %11 %102 %103
+        %102 = OpLabel
+               OpBranch %103
+        %103 = OpLabel
+        %200 = OpCopyObject %10 %11
+               OpBranch %101
+        %101 = OpLabel
+        %201 = OpCopyObject %10 %200
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  auto bad_transformation = TransformationAddDeadBreak(102, 101, false, {});
+  ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager));
+}
+
+TEST(TransformationAddDeadBreakTest, RespectDominanceRules3) {
+  // Right after the loop, an OpCopyObject defined by the loop is used in an
+  // OpPhi. Adding a dead break is OK in this case, due to the use being in an
+  // OpPhi.
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+         %10 = OpTypeBool
+         %11 = OpConstantFalse %10
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %100
+        %100 = OpLabel
+               OpLoopMerge %101 %102 None
+               OpBranch %103
+        %103 = OpLabel
+        %200 = OpCopyObject %10 %11
+               OpBranch %104
+        %104 = OpLabel
+               OpBranch %102
+        %102 = OpLabel
+               OpBranchConditional %11 %100 %101
+        %101 = OpLabel
+        %201 = OpPhi %10 %200 %102
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  auto good_transformation = TransformationAddDeadBreak(100, 101, false, {11});
+  ASSERT_TRUE(good_transformation.IsApplicable(context.get(), fact_manager));
+
+  good_transformation.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+         %10 = OpTypeBool
+         %11 = OpConstantFalse %10
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %100
+        %100 = OpLabel
+               OpLoopMerge %101 %102 None
+               OpBranchConditional %11 %101 %103
+        %103 = OpLabel
+        %200 = OpCopyObject %10 %11
+               OpBranch %104
+        %104 = OpLabel
+               OpBranch %102
+        %102 = OpLabel
+               OpBranchConditional %11 %100 %101
+        %101 = OpLabel
+        %201 = OpPhi %10 %200 %102 %11 %100
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationAddDeadBreakTest, RespectDominanceRules4) {
+  // This example captures the following idiom:
+  //
+  //   if {
+  // L1:
+  //   }
+  //   definition;
+  // L2:
+  //   use in OpPhi;
+  //
+  // Adding a dead jump from L1 to L2 is OK, due to 'use' being in an OpPhi.
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+         %10 = OpTypeBool
+         %11 = OpConstantFalse %10
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %100
+        %100 = OpLabel
+               OpSelectionMerge %101 None
+               OpBranchConditional %11 %102 %103
+        %102 = OpLabel
+               OpBranch %103
+        %103 = OpLabel
+        %200 = OpCopyObject %10 %11
+               OpBranch %101
+        %101 = OpLabel
+        %201 = OpPhi %10 %200 %103
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  auto good_transformation = TransformationAddDeadBreak(102, 101, false, {11});
+  ASSERT_TRUE(good_transformation.IsApplicable(context.get(), fact_manager));
+
+  good_transformation.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+         %10 = OpTypeBool
+         %11 = OpConstantFalse %10
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %100
+        %100 = OpLabel
+               OpSelectionMerge %101 None
+               OpBranchConditional %11 %102 %103
+        %102 = OpLabel
+               OpBranchConditional %11 %101 %103
+        %103 = OpLabel
+        %200 = OpCopyObject %10 %11
+               OpBranch %101
+        %101 = OpLabel
+        %201 = OpPhi %10 %200 %103 %11 %102
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationAddDeadBreakTest, RespectDominanceRules5) {
+  // After, but not right after, the loop, an OpCopyObject defined by the loop
+  // is used in an OpPhi. Adding a dead break is not OK in this case.
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+         %10 = OpTypeBool
+         %11 = OpConstantFalse %10
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %100
+        %100 = OpLabel
+               OpLoopMerge %101 %102 None
+               OpBranch %103
+        %103 = OpLabel
+        %200 = OpCopyObject %10 %11
+               OpBranch %104
+        %104 = OpLabel
+               OpBranch %102
+        %102 = OpLabel
+               OpBranchConditional %11 %100 %101
+        %101 = OpLabel
+               OpBranch %105
+        %105 = OpLabel
+        %201 = OpPhi %10 %200 %101
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  auto bad_transformation = TransformationAddDeadBreak(100, 101, false, {});
+  ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager));
+}
+
+TEST(TransformationAddDeadBreakTest, RespectDominanceRules6) {
+  // This example captures the following idiom:
+  //
+  //   if {
+  // L1:
+  //   }
+  //   definition;
+  // L2:
+  //   goto L3;
+  // L3:
+  //   use in OpPhi;
+  //
+  // Adding a dead jump from L1 to L2 not OK, due to the use in an OpPhi being
+  // in L3.
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+         %10 = OpTypeBool
+         %11 = OpConstantFalse %10
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %100
+        %100 = OpLabel
+               OpSelectionMerge %101 None
+               OpBranchConditional %11 %102 %103
+        %102 = OpLabel
+               OpBranch %103
+        %103 = OpLabel
+        %200 = OpCopyObject %10 %11
+               OpBranch %101
+        %101 = OpLabel
+               OpBranch %150
+        %150 = OpLabel
+        %201 = OpPhi %10 %200 %101
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  auto bad_transformation = TransformationAddDeadBreak(102, 101, false, {});
+  ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager));
+}
+
+TEST(TransformationAddDeadBreakTest, RespectDominanceRules7) {
+  // This example - a variation on an earlier test - captures the following
+  // idiom:
+  //
+  //   loop {
+  // L1:
+  //   }
+  //   definition;
+  // L2:
+  //   use;
+  //
+  // Adding a dead jump from L1 to L2 would lead to 'definition' no longer
+  // dominating 'use', and so is not allowed.
+  //
+  // This version of the test captures the case where L1 appears after the
+  // loop merge (which SPIR-V dominance rules allow).
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+         %10 = OpTypeBool
+         %11 = OpConstantFalse %10
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %100
+        %100 = OpLabel
+               OpLoopMerge %101 %104 None
+               OpBranchConditional %11 %102 %103
+        %103 = OpLabel
+        %200 = OpCopyObject %10 %11
+               OpBranch %101
+        %101 = OpLabel
+        %201 = OpCopyObject %10 %200
+               OpReturn
+        %102 = OpLabel
+               OpBranch %103
+        %104 = OpLabel
+               OpBranch %100
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  auto bad_transformation = TransformationAddDeadBreak(102, 101, false, {});
+  ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager));
+}
+
+TEST(TransformationAddDeadBreakTest, RespectDominanceRules8) {
+  // A variation of RespectDominanceRules8 where the defining block appears
+  // in the loop, but after the definition of interest.
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+         %10 = OpTypeBool
+         %11 = OpConstantFalse %10
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %100
+        %100 = OpLabel
+               OpLoopMerge %101 %104 None
+               OpBranchConditional %11 %102 %103
+        %103 = OpLabel
+        %200 = OpCopyObject %10 %11
+               OpBranch %101
+        %102 = OpLabel
+               OpBranch %103
+        %101 = OpLabel
+        %201 = OpCopyObject %10 %200
+               OpReturn
+        %104 = OpLabel
+               OpBranch %100
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  auto bad_transformation = TransformationAddDeadBreak(102, 101, false, {});
+  ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager));
+}
+
+TEST(TransformationAddDeadBreakTest,
+     BreakWouldDisobeyDominanceBlockOrderingRules) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool
+          %9 = OpConstantTrue %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %10
+         %10 = OpLabel
+               OpLoopMerge %16 %15 None
+               OpBranch %11
+         %11 = OpLabel
+               OpSelectionMerge %14 None
+               OpBranchConditional %9 %12 %13
+         %14 = OpLabel
+               OpBranch %15
+         %12 = OpLabel
+               OpBranch %16
+         %13 = OpLabel
+               OpBranch %16
+         %15 = OpLabel
+               OpBranch %10
+         %16 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  // Bad because 14 comes before 12 in the module, and 14 has no predecessors.
+  // This means that an edge from 12 to 14 will lead to 12 dominating 14, which
+  // is illegal if 12 appears after 14.
+  auto bad_transformation = TransformationAddDeadBreak(12, 14, true, {});
+  ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager));
+}
+
 }  // namespace
 }  // namespace fuzz
 }  // namespace spvtools
diff --git a/test/fuzz/transformation_add_dead_continue_test.cpp b/test/fuzz/transformation_add_dead_continue_test.cpp
index 573db19..8173e72 100644
--- a/test/fuzz/transformation_add_dead_continue_test.cpp
+++ b/test/fuzz/transformation_add_dead_continue_test.cpp
@@ -1029,6 +1029,649 @@
   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
 }
 
+TEST(TransformationAddDeadContinueTest, RespectDominanceRules1) {
+  // Checks that a dead continue cannot be added if it would prevent a block
+  // later in the loop from dominating the loop's continue construct, in the
+  // case where said block defines and id that is used in the loop's continue
+  // construct.
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+         %10 = OpTypeBool
+         %11 = OpConstantFalse %10
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %6
+          %6 = OpLabel
+               OpLoopMerge %8 %9 None
+               OpBranch %7
+          %7 = OpLabel
+         %21 = OpCopyObject %10 %11
+               OpBranch %9
+          %9 = OpLabel
+         %20 = OpPhi %10 %21 %7
+               OpBranchConditional %11 %6 %8
+          %8 = OpLabel
+               OpBranch %12
+         %12 = OpLabel
+               OpLoopMerge %14 %15 None
+               OpBranch %13
+         %13 = OpLabel
+               OpBranch %22
+         %22 = OpLabel
+         %23 = OpCopyObject %10 %11
+               OpBranch %25
+         %25 = OpLabel
+               OpBranch %15
+         %15 = OpLabel
+         %26 = OpCopyObject %10 %23
+               OpBranchConditional %11 %12 %14
+         %14 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  // This transformation is not applicable because the dead continue from the
+  // loop body prevents the definition of %23 later in the loop body from
+  // dominating its use in the loop's continue target.
+  auto bad_transformation = TransformationAddDeadContinue(13, false, {});
+  ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager));
+
+  auto good_transformation_1 = TransformationAddDeadContinue(7, false, {});
+  ASSERT_TRUE(good_transformation_1.IsApplicable(context.get(), fact_manager));
+  good_transformation_1.Apply(context.get(), &fact_manager);
+
+  auto good_transformation_2 = TransformationAddDeadContinue(22, false, {});
+  ASSERT_TRUE(good_transformation_2.IsApplicable(context.get(), fact_manager));
+  good_transformation_2.Apply(context.get(), &fact_manager);
+
+  // This transformation is OK, because the definition of %21 in the loop body
+  // is only used in an OpPhi in the loop's continue target.
+  auto good_transformation_3 = TransformationAddDeadContinue(6, false, {11});
+  ASSERT_TRUE(good_transformation_3.IsApplicable(context.get(), fact_manager));
+  good_transformation_3.Apply(context.get(), &fact_manager);
+
+  std::string after_transformations = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+         %10 = OpTypeBool
+         %11 = OpConstantFalse %10
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %6
+          %6 = OpLabel
+               OpLoopMerge %8 %9 None
+               OpBranchConditional %11 %9 %7
+          %7 = OpLabel
+         %21 = OpCopyObject %10 %11
+               OpBranchConditional %11 %9 %9
+          %9 = OpLabel
+         %20 = OpPhi %10 %21 %7 %11 %6
+               OpBranchConditional %11 %6 %8
+          %8 = OpLabel
+               OpBranch %12
+         %12 = OpLabel
+               OpLoopMerge %14 %15 None
+               OpBranch %13
+         %13 = OpLabel
+               OpBranch %22
+         %22 = OpLabel
+         %23 = OpCopyObject %10 %11
+               OpBranchConditional %11 %15 %25
+         %25 = OpLabel
+               OpBranch %15
+         %15 = OpLabel
+         %26 = OpCopyObject %10 %23
+               OpBranchConditional %11 %12 %14
+         %14 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, after_transformations, context.get()));
+}
+
+TEST(TransformationAddDeadContinueTest, RespectDominanceRules2) {
+  // Checks that a dead continue cannot be added if it would lead to a use after
+  // the loop failing to be dominated by its definition.
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+         %10 = OpTypeBool
+         %11 = OpConstantFalse %10
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %100
+        %100 = OpLabel
+               OpLoopMerge %101 %102 None
+               OpBranch %103
+        %103 = OpLabel
+        %200 = OpCopyObject %10 %11
+               OpBranch %104
+        %104 = OpLabel
+               OpBranch %102
+        %102 = OpLabel
+               OpBranchConditional %11 %100 %101
+        %101 = OpLabel
+        %201 = OpCopyObject %10 %200
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  // This transformation would shortcut the part of the loop body that defines
+  // an id used after the loop.
+  auto bad_transformation = TransformationAddDeadContinue(100, false, {});
+  ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager));
+}
+
+TEST(TransformationAddDeadContinueTest, RespectDominanceRules3) {
+  // Checks that a dead continue cannot be added if it would lead to a dominance
+  // problem with an id used in an OpPhi after the loop.
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+         %10 = OpTypeBool
+         %11 = OpConstantFalse %10
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %100
+        %100 = OpLabel
+               OpLoopMerge %101 %102 None
+               OpBranch %103
+        %103 = OpLabel
+        %200 = OpCopyObject %10 %11
+               OpBranch %104
+        %104 = OpLabel
+               OpBranch %102
+        %102 = OpLabel
+               OpBranchConditional %11 %100 %101
+        %101 = OpLabel
+        %201 = OpPhi %10 %200 %102
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  // This transformation would shortcut the part of the loop body that defines
+  // an id used after the loop.
+  auto bad_transformation = TransformationAddDeadContinue(100, false, {});
+  ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager));
+}
+
+TEST(TransformationAddDeadContinueTest, Miscellaneous1) {
+  // A miscellaneous test that exposed a bug in spirv-fuzz.
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %586 %623
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpMemberDecorate %34 0 Offset 0
+               OpDecorate %34 Block
+               OpDecorate %36 DescriptorSet 0
+               OpDecorate %36 Binding 0
+               OpDecorate %586 BuiltIn FragCoord
+               OpMemberDecorate %591 0 Offset 0
+               OpDecorate %591 Block
+               OpDecorate %593 DescriptorSet 0
+               OpDecorate %593 Binding 1
+               OpDecorate %623 Location 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 0
+         %16 = OpConstant %6 2
+         %17 = OpTypeBool
+         %27 = OpTypeFloat 32
+         %28 = OpTypeVector %27 2
+         %29 = OpTypeMatrix %28 2
+         %30 = OpTypePointer Private %29
+         %31 = OpVariable %30 Private
+         %34 = OpTypeStruct %27
+         %35 = OpTypePointer Uniform %34
+         %36 = OpVariable %35 Uniform
+         %37 = OpTypePointer Uniform %27
+         %40 = OpTypePointer Private %27
+         %43 = OpConstant %6 1
+         %62 = OpConstant %6 3
+         %64 = OpTypeVector %27 3
+         %65 = OpTypeMatrix %64 2
+         %66 = OpTypePointer Private %65
+         %67 = OpVariable %66 Private
+         %92 = OpConstant %6 4
+         %94 = OpTypeVector %27 4
+         %95 = OpTypeMatrix %94 2
+         %96 = OpTypePointer Private %95
+         %97 = OpVariable %96 Private
+        %123 = OpTypeMatrix %28 3
+        %124 = OpTypePointer Private %123
+        %125 = OpVariable %124 Private
+        %151 = OpTypeMatrix %64 3
+        %152 = OpTypePointer Private %151
+        %153 = OpVariable %152 Private
+        %179 = OpTypeMatrix %94 3
+        %180 = OpTypePointer Private %179
+        %181 = OpVariable %180 Private
+        %207 = OpTypeMatrix %28 4
+        %208 = OpTypePointer Private %207
+        %209 = OpVariable %208 Private
+        %235 = OpTypeMatrix %64 4
+        %236 = OpTypePointer Private %235
+        %237 = OpVariable %236 Private
+        %263 = OpTypeMatrix %94 4
+        %264 = OpTypePointer Private %263
+        %265 = OpVariable %264 Private
+        %275 = OpTypeInt 32 0
+        %276 = OpConstant %275 9
+        %277 = OpTypeArray %27 %276
+        %278 = OpTypePointer Function %277
+        %280 = OpConstant %27 0
+        %281 = OpTypePointer Function %27
+        %311 = OpConstant %27 16
+        %448 = OpConstant %6 5
+        %482 = OpConstant %6 6
+        %516 = OpConstant %6 7
+        %550 = OpConstant %6 8
+        %585 = OpTypePointer Input %94
+        %586 = OpVariable %585 Input
+        %587 = OpConstant %275 0
+        %588 = OpTypePointer Input %27
+        %591 = OpTypeStruct %28
+        %592 = OpTypePointer Uniform %591
+        %593 = OpVariable %592 Uniform
+        %596 = OpConstant %27 3
+        %601 = OpConstant %275 1
+        %617 = OpConstant %6 9
+        %622 = OpTypePointer Output %94
+        %623 = OpVariable %622 Output
+        %628 = OpConstant %27 1
+        %634 = OpConstantComposite %94 %280 %280 %280 %628
+        %635 = OpUndef %6
+        %636 = OpUndef %17
+        %637 = OpUndef %27
+        %638 = OpUndef %64
+        %639 = OpUndef %94
+        %640 = OpConstantTrue %17
+        %736 = OpConstantFalse %17
+        %642 = OpVariable %37 Uniform
+        %643 = OpVariable %40 Private
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %164
+        %164 = OpLabel
+               OpLoopMerge %166 %167 None
+               OpBranch %165
+        %165 = OpLabel
+               OpBranch %172
+        %172 = OpLabel
+               OpSelectionMerge %174 None
+               OpBranchConditional %640 %174 %174
+        %174 = OpLabel
+        %785 = OpCopyObject %6 %43
+               OpBranch %167
+        %167 = OpLabel
+        %190 = OpIAdd %6 %9 %785
+               OpBranchConditional %640 %164 %166
+        %166 = OpLabel
+               OpBranch %196
+        %196 = OpLabel
+               OpBranch %194
+        %194 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  // This transformation would shortcut the part of the loop body that defines
+  // an id used in the continue target.
+  auto bad_transformation = TransformationAddDeadContinue(165, false, {});
+  ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager));
+}
+
+TEST(TransformationAddDeadContinueTest, Miscellaneous2) {
+  // A miscellaneous test that exposed a bug in spirv-fuzz.
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+         %51 = OpTypeBool
+        %395 = OpConstantTrue %51
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %389
+        %389 = OpLabel
+               OpLoopMerge %388 %391 None
+               OpBranch %339
+        %339 = OpLabel
+               OpSelectionMerge %396 None
+               OpBranchConditional %395 %388 %396
+        %396 = OpLabel
+               OpBranch %1552
+       %1552 = OpLabel
+               OpLoopMerge %1553 %1554 None
+               OpBranch %1556
+       %1556 = OpLabel
+               OpLoopMerge %1557 %1570 None
+               OpBranchConditional %395 %1562 %1557
+       %1562 = OpLabel
+               OpSelectionMerge %1570 None
+               OpBranchConditional %395 %1571 %1570
+       %1571 = OpLabel
+               OpBranch %1557
+       %1570 = OpLabel
+               OpBranch %1556
+       %1557 = OpLabel
+               OpSelectionMerge %1586 None
+               OpBranchConditional %395 %1553 %1586
+       %1586 = OpLabel
+               OpBranch %1553
+       %1554 = OpLabel
+               OpBranch %1552
+       %1553 = OpLabel
+               OpBranch %388
+        %391 = OpLabel
+               OpBranch %389
+        %388 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  // This transformation would introduce a branch from a continue target to
+  // itself.
+  auto bad_transformation = TransformationAddDeadContinue(1554, true, {});
+  ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager));
+}
+
+TEST(TransformationAddDeadContinueTest, Miscellaneous3) {
+  // A miscellaneous test that exposed a bug in spirv-fuzz.
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+         %85 = OpTypeBool
+        %434 = OpConstantFalse %85
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %234
+        %234 = OpLabel
+               OpLoopMerge %235 %236 None
+               OpBranch %259
+        %259 = OpLabel
+               OpLoopMerge %260 %274 None
+               OpBranchConditional %434 %265 %260
+        %265 = OpLabel
+               OpBranch %275
+        %275 = OpLabel
+               OpBranch %260
+        %274 = OpLabel
+               OpBranch %259
+        %260 = OpLabel
+               OpSelectionMerge %298 None
+               OpBranchConditional %434 %299 %300
+        %300 = OpLabel
+               OpBranch %235
+        %298 = OpLabel
+               OpUnreachable
+        %236 = OpLabel
+               OpBranch %234
+        %299 = OpLabel
+               OpBranch %235
+        %235 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  auto bad_transformation = TransformationAddDeadContinue(299, false, {});
+
+  // The continue edge would connect %299 to the previously-unreachable %236,
+  // making %299 dominate %236, and breaking the rule that block ordering must
+  // respect dominance.
+  ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager));
+}
+
+TEST(TransformationAddDeadContinueTest, Miscellaneous4) {
+  // A miscellaneous test that exposed a bug in spirv-fuzz.
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %8 "i"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 0
+         %16 = OpConstant %6 100
+         %17 = OpTypeBool
+        %100 = OpConstantFalse %17
+         %21 = OpConstant %6 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+               OpStore %8 %9
+               OpBranch %10
+         %13 = OpLabel
+         %20 = OpLoad %6 %8
+         %22 = OpIAdd %6 %20 %21
+               OpStore %8 %22
+               OpBranch %10
+         %10 = OpLabel
+               OpLoopMerge %12 %13 None
+               OpBranch %14
+         %14 = OpLabel
+         %15 = OpLoad %6 %8
+         %18 = OpSLessThan %17 %15 %16
+               OpBranchConditional %18 %11 %12
+         %11 = OpLabel
+               OpBranch %12
+         %12 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  auto bad_transformation = TransformationAddDeadContinue(10, false, {});
+
+  // The continue edge would connect %10 to the previously-unreachable %13,
+  // making %10 dominate %13, and breaking the rule that block ordering must
+  // respect dominance.
+  ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager));
+}
+
+TEST(TransformationAddDeadContinueTest, Miscellaneous5) {
+  // A miscellaneous test that exposed a bug in spirv-fuzz.
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool
+          %7 = OpTypePointer Function %6
+          %9 = OpConstantTrue %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %98
+         %98 = OpLabel
+               OpLoopMerge %100 %101 None
+               OpBranch %99
+         %99 = OpLabel
+               OpSelectionMerge %111 None
+               OpBranchConditional %9 %110 %111
+        %110 = OpLabel
+               OpBranch %100
+        %111 = OpLabel
+        %200 = OpCopyObject %6 %9
+               OpBranch %101
+        %101 = OpLabel
+        %201 = OpCopyObject %6 %200
+               OpBranchConditional %9 %98 %100
+        %100 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  auto bad_transformation = TransformationAddDeadContinue(110, true, {});
+
+  // The continue edge would lead to the use of %200 in block %101 no longer
+  // being dominated by its definition in block %111.
+  ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager));
+}
+
+TEST(TransformationAddDeadContinueTest, DISABLED_Miscellaneous6) {
+  // A miscellaneous test that exposing a known bug in spirv-fuzz.
+
+  // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2919): re-enable
+  //  this test as an when the known issue is fixed.
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool
+          %9 = OpConstantTrue %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %10
+         %10 = OpLabel
+               OpLoopMerge %13 %12 None
+               OpBranchConditional %9 %13 %11
+         %11 = OpLabel
+         %20 = OpCopyObject %6 %9
+               OpBranch %12
+         %12 = OpLabel
+               OpBranchConditional %9 %10 %13
+         %13 = OpLabel
+         %21 = OpCopyObject %6 %20
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  auto bad_transformation = TransformationAddDeadContinue(10, true, {});
+
+  ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager));
+}
+
 }  // namespace
 }  // namespace fuzz
 }  // namespace spvtools
diff --git a/test/fuzz/transformation_add_no_contraction_decoration_test.cpp b/test/fuzz/transformation_add_no_contraction_decoration_test.cpp
new file mode 100644
index 0000000..b1a87ea
--- /dev/null
+++ b/test/fuzz/transformation_add_no_contraction_decoration_test.cpp
@@ -0,0 +1,194 @@
+// 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.
+
+#include "source/fuzz/transformation_add_no_contraction_decoration.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationAddNoContractionDecorationTest, BasicScenarios) {
+  // This is a simple transformation and this test handles the main cases.
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %8 "x"
+               OpName %10 "y"
+               OpName %14 "i"
+               OpDecorate %32 NoContraction
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 0
+         %11 = OpConstant %6 2
+         %12 = OpTypeInt 32 1
+         %13 = OpTypePointer Function %12
+         %15 = OpConstant %12 0
+         %22 = OpConstant %12 10
+         %23 = OpTypeBool
+         %31 = OpConstant %6 3.5999999
+         %38 = OpConstant %12 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %10 = OpVariable %7 Function
+         %14 = OpVariable %13 Function
+               OpStore %8 %9
+               OpStore %10 %11
+               OpStore %14 %15
+               OpBranch %16
+         %16 = OpLabel
+               OpLoopMerge %18 %19 None
+               OpBranch %20
+         %20 = OpLabel
+         %21 = OpLoad %12 %14
+         %24 = OpSLessThan %23 %21 %22
+               OpBranchConditional %24 %17 %18
+         %17 = OpLabel
+         %25 = OpLoad %6 %10
+         %26 = OpLoad %6 %10
+         %27 = OpFMul %6 %25 %26
+         %28 = OpLoad %6 %8
+         %29 = OpFAdd %6 %28 %27
+               OpStore %8 %29
+         %30 = OpLoad %6 %10
+         %32 = OpFDiv %6 %30 %31
+               OpStore %10 %32
+         %33 = OpLoad %12 %14
+         %34 = OpConvertSToF %6 %33
+         %35 = OpLoad %6 %8
+         %36 = OpFAdd %6 %35 %34
+               OpStore %8 %36
+               OpBranch %19
+         %19 = OpLabel
+         %37 = OpLoad %12 %14
+         %39 = OpIAdd %12 %37 %38
+               OpStore %14 %39
+               OpBranch %16
+         %18 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  FactManager fact_manager;
+
+  // Invalid: 200 is not an id
+  ASSERT_FALSE(TransformationAddNoContractionDecoration(200).IsApplicable(
+      context.get(), fact_manager));
+  // Invalid: 17 is a block id
+  ASSERT_FALSE(TransformationAddNoContractionDecoration(17).IsApplicable(
+      context.get(), fact_manager));
+  // Invalid: 24 is not arithmetic
+  ASSERT_FALSE(TransformationAddNoContractionDecoration(24).IsApplicable(
+      context.get(), fact_manager));
+
+  // It is valid to add NoContraction to each of these ids (and it's fine to
+  // have duplicates of the decoration, in the case of 32).
+  for (uint32_t result_id : {32u, 32u, 27u, 29u, 39u}) {
+    TransformationAddNoContractionDecoration transformation(result_id);
+    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(IsValid(env, context.get()));
+  }
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %8 "x"
+               OpName %10 "y"
+               OpName %14 "i"
+               OpDecorate %32 NoContraction
+               OpDecorate %32 NoContraction
+               OpDecorate %32 NoContraction
+               OpDecorate %27 NoContraction
+               OpDecorate %29 NoContraction
+               OpDecorate %39 NoContraction
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 0
+         %11 = OpConstant %6 2
+         %12 = OpTypeInt 32 1
+         %13 = OpTypePointer Function %12
+         %15 = OpConstant %12 0
+         %22 = OpConstant %12 10
+         %23 = OpTypeBool
+         %31 = OpConstant %6 3.5999999
+         %38 = OpConstant %12 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %10 = OpVariable %7 Function
+         %14 = OpVariable %13 Function
+               OpStore %8 %9
+               OpStore %10 %11
+               OpStore %14 %15
+               OpBranch %16
+         %16 = OpLabel
+               OpLoopMerge %18 %19 None
+               OpBranch %20
+         %20 = OpLabel
+         %21 = OpLoad %12 %14
+         %24 = OpSLessThan %23 %21 %22
+               OpBranchConditional %24 %17 %18
+         %17 = OpLabel
+         %25 = OpLoad %6 %10
+         %26 = OpLoad %6 %10
+         %27 = OpFMul %6 %25 %26
+         %28 = OpLoad %6 %8
+         %29 = OpFAdd %6 %28 %27
+               OpStore %8 %29
+         %30 = OpLoad %6 %10
+         %32 = OpFDiv %6 %30 %31
+               OpStore %10 %32
+         %33 = OpLoad %12 %14
+         %34 = OpConvertSToF %6 %33
+         %35 = OpLoad %6 %8
+         %36 = OpFAdd %6 %35 %34
+               OpStore %8 %36
+               OpBranch %19
+         %19 = OpLabel
+         %37 = OpLoad %12 %14
+         %39 = OpIAdd %12 %37 %38
+               OpStore %14 %39
+               OpBranch %16
+         %18 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/test/fuzz/transformation_composite_construct_test.cpp b/test/fuzz/transformation_composite_construct_test.cpp
new file mode 100644
index 0000000..d303368
--- /dev/null
+++ b/test/fuzz/transformation_composite_construct_test.cpp
@@ -0,0 +1,1356 @@
+// 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.
+
+#include "source/fuzz/transformation_composite_construct.h"
+#include "source/fuzz/data_descriptor.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationCompositeConstructTest, ConstructArrays) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %11 "floats"
+               OpName %22 "x"
+               OpName %39 "vecs"
+               OpName %49 "bools"
+               OpName %60 "many_uvec3s"
+               OpDecorate %60 RelaxedPrecision
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeInt 32 0
+          %8 = OpConstant %7 2
+          %9 = OpTypeArray %6 %8
+         %10 = OpTypePointer Function %9
+         %12 = OpTypeInt 32 1
+         %13 = OpConstant %12 0
+         %14 = OpConstant %6 1
+         %15 = OpTypePointer Function %6
+         %17 = OpConstant %12 1
+         %18 = OpConstant %6 2
+         %20 = OpTypeVector %6 2
+         %21 = OpTypePointer Function %20
+         %32 = OpTypeBool
+         %36 = OpConstant %7 3
+         %37 = OpTypeArray %20 %36
+         %38 = OpTypePointer Private %37
+         %39 = OpVariable %38 Private
+         %40 = OpConstant %6 3
+         %41 = OpConstantComposite %20 %40 %40
+         %42 = OpTypePointer Private %20
+         %44 = OpConstant %12 2
+         %47 = OpTypeArray %32 %36
+         %48 = OpTypePointer Function %47
+         %50 = OpConstantTrue %32
+         %51 = OpTypePointer Function %32
+         %56 = OpTypeVector %7 3
+         %57 = OpTypeArray %56 %8
+         %58 = OpTypeArray %57 %8
+         %59 = OpTypePointer Function %58
+         %61 = OpConstant %7 4
+         %62 = OpConstantComposite %56 %61 %61 %61
+         %63 = OpTypePointer Function %56
+         %65 = OpConstant %7 5
+         %66 = OpConstantComposite %56 %65 %65 %65
+         %67 = OpConstant %7 6
+         %68 = OpConstantComposite %56 %67 %67 %67
+         %69 = OpConstantComposite %57 %66 %68
+        %100 = OpUndef %57
+         %70 = OpTypePointer Function %57
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %11 = OpVariable %10 Function
+         %22 = OpVariable %21 Function
+         %49 = OpVariable %48 Function
+         %60 = OpVariable %59 Function
+         %16 = OpAccessChain %15 %11 %13
+               OpStore %16 %14
+         %19 = OpAccessChain %15 %11 %17
+               OpStore %19 %18
+         %23 = OpAccessChain %15 %11 %13
+         %24 = OpLoad %6 %23
+         %25 = OpAccessChain %15 %11 %17
+         %26 = OpLoad %6 %25
+         %27 = OpCompositeConstruct %20 %24 %26
+               OpStore %22 %27
+         %28 = OpAccessChain %15 %11 %13
+         %29 = OpLoad %6 %28
+         %30 = OpAccessChain %15 %11 %17
+         %31 = OpLoad %6 %30
+         %33 = OpFOrdGreaterThan %32 %29 %31
+               OpSelectionMerge %35 None
+               OpBranchConditional %33 %34 %35
+         %34 = OpLabel
+         %43 = OpAccessChain %42 %39 %17
+               OpStore %43 %41
+         %45 = OpLoad %20 %22
+         %46 = OpAccessChain %42 %39 %44
+               OpStore %46 %45
+               OpBranch %35
+         %35 = OpLabel
+         %52 = OpAccessChain %51 %49 %13
+               OpStore %52 %50
+         %53 = OpAccessChain %51 %49 %13
+         %54 = OpLoad %32 %53
+         %55 = OpAccessChain %51 %49 %17
+               OpStore %55 %54
+         %64 = OpAccessChain %63 %60 %13 %13
+               OpStore %64 %62
+         %71 = OpAccessChain %70 %60 %17
+               OpStore %71 %69
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  // Make a vec2[3]
+  TransformationCompositeConstruct make_vec2_array_length_3(
+      37, {41, 45, 27}, MakeInstructionDescriptor(46, SpvOpAccessChain, 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);
+  ASSERT_TRUE(
+      make_vec2_array_length_3.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      make_vec2_array_length_3_bad.IsApplicable(context.get(), fact_manager));
+  make_vec2_array_length_3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(41, {}), MakeDataDescriptor(200, {0}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(45, {}), MakeDataDescriptor(200, {1}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(27, {}), MakeDataDescriptor(200, {2}), context.get()));
+
+  // Make a float[2]
+  TransformationCompositeConstruct make_float_array_length_2(
+      9, {24, 40}, MakeInstructionDescriptor(71, SpvOpStore, 0), 201);
+  // Bad: %41 does not have type float
+  TransformationCompositeConstruct make_float_array_length_2_bad(
+      9, {41, 40}, MakeInstructionDescriptor(71, SpvOpStore, 0), 201);
+  ASSERT_TRUE(
+      make_float_array_length_2.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      make_float_array_length_2_bad.IsApplicable(context.get(), fact_manager));
+  make_float_array_length_2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(24, {}), MakeDataDescriptor(201, {0}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(40, {}), MakeDataDescriptor(201, {1}), context.get()));
+
+  // Make a bool[3]
+  TransformationCompositeConstruct make_bool_array_length_3(
+      47, {33, 50, 50}, MakeInstructionDescriptor(33, SpvOpSelectionMerge, 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);
+  ASSERT_TRUE(
+      make_bool_array_length_3.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      make_bool_array_length_3_bad.IsApplicable(context.get(), fact_manager));
+  make_bool_array_length_3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(33, {}), MakeDataDescriptor(202, {0}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(50, {}), MakeDataDescriptor(202, {1}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(50, {}), MakeDataDescriptor(202, {2}), context.get()));
+
+  // make a uvec3[2][2]
+  TransformationCompositeConstruct make_uvec3_array_length_2_2(
+      58, {69, 100}, MakeInstructionDescriptor(64, SpvOpStore, 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);
+  ASSERT_TRUE(
+      make_uvec3_array_length_2_2.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(make_uvec3_array_length_2_2_bad.IsApplicable(context.get(),
+                                                            fact_manager));
+  make_uvec3_array_length_2_2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(69, {}), MakeDataDescriptor(203, {0}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(100, {}),
+                                        MakeDataDescriptor(203, {1}),
+                                        context.get()));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %11 "floats"
+               OpName %22 "x"
+               OpName %39 "vecs"
+               OpName %49 "bools"
+               OpName %60 "many_uvec3s"
+               OpDecorate %60 RelaxedPrecision
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeInt 32 0
+          %8 = OpConstant %7 2
+          %9 = OpTypeArray %6 %8
+         %10 = OpTypePointer Function %9
+         %12 = OpTypeInt 32 1
+         %13 = OpConstant %12 0
+         %14 = OpConstant %6 1
+         %15 = OpTypePointer Function %6
+         %17 = OpConstant %12 1
+         %18 = OpConstant %6 2
+         %20 = OpTypeVector %6 2
+         %21 = OpTypePointer Function %20
+         %32 = OpTypeBool
+         %36 = OpConstant %7 3
+         %37 = OpTypeArray %20 %36
+         %38 = OpTypePointer Private %37
+         %39 = OpVariable %38 Private
+         %40 = OpConstant %6 3
+         %41 = OpConstantComposite %20 %40 %40
+         %42 = OpTypePointer Private %20
+         %44 = OpConstant %12 2
+         %47 = OpTypeArray %32 %36
+         %48 = OpTypePointer Function %47
+         %50 = OpConstantTrue %32
+         %51 = OpTypePointer Function %32
+         %56 = OpTypeVector %7 3
+         %57 = OpTypeArray %56 %8
+         %58 = OpTypeArray %57 %8
+         %59 = OpTypePointer Function %58
+         %61 = OpConstant %7 4
+         %62 = OpConstantComposite %56 %61 %61 %61
+         %63 = OpTypePointer Function %56
+         %65 = OpConstant %7 5
+         %66 = OpConstantComposite %56 %65 %65 %65
+         %67 = OpConstant %7 6
+         %68 = OpConstantComposite %56 %67 %67 %67
+         %69 = OpConstantComposite %57 %66 %68
+        %100 = OpUndef %57
+         %70 = OpTypePointer Function %57
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %11 = OpVariable %10 Function
+         %22 = OpVariable %21 Function
+         %49 = OpVariable %48 Function
+         %60 = OpVariable %59 Function
+         %16 = OpAccessChain %15 %11 %13
+               OpStore %16 %14
+         %19 = OpAccessChain %15 %11 %17
+               OpStore %19 %18
+         %23 = OpAccessChain %15 %11 %13
+         %24 = OpLoad %6 %23
+         %25 = OpAccessChain %15 %11 %17
+         %26 = OpLoad %6 %25
+         %27 = OpCompositeConstruct %20 %24 %26
+               OpStore %22 %27
+         %28 = OpAccessChain %15 %11 %13
+         %29 = OpLoad %6 %28
+         %30 = OpAccessChain %15 %11 %17
+         %31 = OpLoad %6 %30
+         %33 = OpFOrdGreaterThan %32 %29 %31
+        %202 = OpCompositeConstruct %47 %33 %50 %50
+               OpSelectionMerge %35 None
+               OpBranchConditional %33 %34 %35
+         %34 = OpLabel
+         %43 = OpAccessChain %42 %39 %17
+               OpStore %43 %41
+         %45 = OpLoad %20 %22
+        %200 = OpCompositeConstruct %37 %41 %45 %27
+         %46 = OpAccessChain %42 %39 %44
+               OpStore %46 %45
+               OpBranch %35
+         %35 = OpLabel
+         %52 = OpAccessChain %51 %49 %13
+               OpStore %52 %50
+         %53 = OpAccessChain %51 %49 %13
+         %54 = OpLoad %32 %53
+         %55 = OpAccessChain %51 %49 %17
+               OpStore %55 %54
+         %64 = OpAccessChain %63 %60 %13 %13
+        %203 = OpCompositeConstruct %58 %69 %100
+               OpStore %64 %62
+         %71 = OpAccessChain %70 %60 %17
+        %201 = OpCompositeConstruct %9 %24 %40
+               OpStore %71 %69
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationCompositeConstructTest, ConstructMatrices) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %9 "v1"
+               OpName %12 "v2"
+               OpName %14 "v3"
+               OpName %19 "v4"
+               OpName %26 "v5"
+               OpName %29 "v6"
+               OpName %34 "m34"
+               OpName %37 "m43"
+               OpName %43 "vecs"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeVector %6 3
+          %8 = OpTypePointer Function %7
+         %10 = OpConstant %6 1
+         %11 = OpConstantComposite %7 %10 %10 %10
+         %17 = OpTypeVector %6 4
+         %18 = OpTypePointer Function %17
+         %21 = OpConstant %6 2
+         %32 = OpTypeMatrix %17 3
+         %33 = OpTypePointer Private %32
+         %34 = OpVariable %33 Private
+         %35 = OpTypeMatrix %7 4
+         %36 = OpTypePointer Private %35
+         %37 = OpVariable %36 Private
+         %38 = OpTypeVector %6 2
+         %39 = OpTypeInt 32 0
+         %40 = OpConstant %39 3
+         %41 = OpTypeArray %38 %40
+         %42 = OpTypePointer Private %41
+         %43 = OpVariable %42 Private
+        %100 = OpUndef %7
+        %101 = OpUndef %17
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %9 = OpVariable %8 Function
+         %12 = OpVariable %8 Function
+         %14 = OpVariable %8 Function
+         %19 = OpVariable %18 Function
+         %26 = OpVariable %18 Function
+         %29 = OpVariable %18 Function
+               OpStore %9 %11
+         %13 = OpLoad %7 %9
+               OpStore %12 %13
+         %15 = OpLoad %7 %12
+         %16 = OpVectorShuffle %7 %15 %15 2 1 0
+               OpStore %14 %16
+         %20 = OpLoad %7 %14
+         %22 = OpCompositeExtract %6 %20 0
+         %23 = OpCompositeExtract %6 %20 1
+         %24 = OpCompositeExtract %6 %20 2
+         %25 = OpCompositeConstruct %17 %22 %23 %24 %21
+               OpStore %19 %25
+         %27 = OpLoad %17 %19
+         %28 = OpVectorShuffle %17 %27 %27 3 2 1 0
+               OpStore %26 %28
+         %30 = OpLoad %7 %9
+         %31 = OpVectorShuffle %17 %30 %30 0 0 1 1
+               OpStore %29 %31
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  // make a mat3x4
+  TransformationCompositeConstruct make_mat34(
+      32, {25, 28, 31}, MakeInstructionDescriptor(31, SpvOpReturn, 0), 200);
+  // Bad: %35 is mat4x3, not mat3x4.
+  TransformationCompositeConstruct make_mat34_bad(
+      35, {25, 28, 31}, MakeInstructionDescriptor(31, SpvOpReturn, 0), 200);
+  ASSERT_TRUE(make_mat34.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(make_mat34_bad.IsApplicable(context.get(), fact_manager));
+  make_mat34.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(25, {}), MakeDataDescriptor(200, {0}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(28, {}), MakeDataDescriptor(200, {1}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(31, {}), MakeDataDescriptor(200, {2}), context.get()));
+
+  // make a mat4x3
+  TransformationCompositeConstruct make_mat43(
+      35, {11, 13, 16, 100}, MakeInstructionDescriptor(31, SpvOpStore, 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);
+  ASSERT_TRUE(make_mat43.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(make_mat43_bad.IsApplicable(context.get(), fact_manager));
+  make_mat43.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(11, {}), MakeDataDescriptor(201, {0}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(13, {}), MakeDataDescriptor(201, {1}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(16, {}), MakeDataDescriptor(201, {2}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(100, {}),
+                                        MakeDataDescriptor(201, {3}),
+                                        context.get()));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %9 "v1"
+               OpName %12 "v2"
+               OpName %14 "v3"
+               OpName %19 "v4"
+               OpName %26 "v5"
+               OpName %29 "v6"
+               OpName %34 "m34"
+               OpName %37 "m43"
+               OpName %43 "vecs"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeVector %6 3
+          %8 = OpTypePointer Function %7
+         %10 = OpConstant %6 1
+         %11 = OpConstantComposite %7 %10 %10 %10
+         %17 = OpTypeVector %6 4
+         %18 = OpTypePointer Function %17
+         %21 = OpConstant %6 2
+         %32 = OpTypeMatrix %17 3
+         %33 = OpTypePointer Private %32
+         %34 = OpVariable %33 Private
+         %35 = OpTypeMatrix %7 4
+         %36 = OpTypePointer Private %35
+         %37 = OpVariable %36 Private
+         %38 = OpTypeVector %6 2
+         %39 = OpTypeInt 32 0
+         %40 = OpConstant %39 3
+         %41 = OpTypeArray %38 %40
+         %42 = OpTypePointer Private %41
+         %43 = OpVariable %42 Private
+        %100 = OpUndef %7
+        %101 = OpUndef %17
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %9 = OpVariable %8 Function
+         %12 = OpVariable %8 Function
+         %14 = OpVariable %8 Function
+         %19 = OpVariable %18 Function
+         %26 = OpVariable %18 Function
+         %29 = OpVariable %18 Function
+               OpStore %9 %11
+         %13 = OpLoad %7 %9
+               OpStore %12 %13
+         %15 = OpLoad %7 %12
+         %16 = OpVectorShuffle %7 %15 %15 2 1 0
+               OpStore %14 %16
+         %20 = OpLoad %7 %14
+         %22 = OpCompositeExtract %6 %20 0
+         %23 = OpCompositeExtract %6 %20 1
+         %24 = OpCompositeExtract %6 %20 2
+         %25 = OpCompositeConstruct %17 %22 %23 %24 %21
+               OpStore %19 %25
+         %27 = OpLoad %17 %19
+         %28 = OpVectorShuffle %17 %27 %27 3 2 1 0
+               OpStore %26 %28
+         %30 = OpLoad %7 %9
+         %31 = OpVectorShuffle %17 %30 %30 0 0 1 1
+        %201 = OpCompositeConstruct %35 %11 %13 %16 %100
+               OpStore %29 %31
+        %200 = OpCompositeConstruct %32 %25 %28 %31
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationCompositeConstructTest, ConstructStructs) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %9 "Inner"
+               OpMemberName %9 0 "a"
+               OpMemberName %9 1 "b"
+               OpName %11 "i1"
+               OpName %22 "i2"
+               OpName %33 "Outer"
+               OpMemberName %33 0 "c"
+               OpMemberName %33 1 "d"
+               OpMemberName %33 2 "e"
+               OpName %35 "o"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeVector %6 2
+          %8 = OpTypeInt 32 1
+          %9 = OpTypeStruct %7 %8
+         %10 = OpTypePointer Function %9
+         %12 = OpConstant %8 0
+         %13 = OpConstant %6 2
+         %14 = OpTypeInt 32 0
+         %15 = OpConstant %14 0
+         %16 = OpTypePointer Function %6
+         %18 = OpConstant %8 1
+         %19 = OpConstant %8 3
+         %20 = OpTypePointer Function %8
+         %23 = OpTypePointer Function %7
+         %31 = OpConstant %14 2
+         %32 = OpTypeArray %9 %31
+         %33 = OpTypeStruct %32 %9 %6
+         %34 = OpTypePointer Function %33
+         %36 = OpConstant %6 1
+         %37 = OpConstantComposite %7 %36 %13
+         %38 = OpConstant %8 2
+         %39 = OpConstantComposite %9 %37 %38
+         %40 = OpConstant %6 3
+         %41 = OpConstant %6 4
+         %42 = OpConstantComposite %7 %40 %41
+         %56 = OpConstant %6 5
+        %100 = OpUndef %9
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %11 = OpVariable %10 Function
+         %22 = OpVariable %10 Function
+         %35 = OpVariable %34 Function
+         %17 = OpAccessChain %16 %11 %12 %15
+               OpStore %17 %13
+         %21 = OpAccessChain %20 %11 %18
+               OpStore %21 %19
+         %24 = OpAccessChain %23 %11 %12
+         %25 = OpLoad %7 %24
+         %26 = OpAccessChain %23 %22 %12
+               OpStore %26 %25
+         %27 = OpAccessChain %20 %11 %18
+         %28 = OpLoad %8 %27
+         %29 = OpIAdd %8 %28 %18
+         %30 = OpAccessChain %20 %22 %18
+               OpStore %30 %29
+         %43 = OpAccessChain %20 %11 %18
+         %44 = OpLoad %8 %43
+         %45 = OpCompositeConstruct %9 %42 %44
+         %46 = OpCompositeConstruct %32 %39 %45
+         %47 = OpLoad %9 %22
+         %48 = OpCompositeConstruct %33 %46 %47 %40
+               OpStore %35 %48
+         %49 = OpLoad %9 %11
+         %50 = OpAccessChain %10 %35 %12 %12
+               OpStore %50 %49
+         %51 = OpLoad %9 %22
+         %52 = OpAccessChain %10 %35 %12 %18
+               OpStore %52 %51
+         %53 = OpAccessChain %10 %35 %12 %12
+         %54 = OpLoad %9 %53
+         %55 = OpAccessChain %10 %35 %18
+               OpStore %55 %54
+         %57 = OpAccessChain %16 %35 %38
+               OpStore %57 %56
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  // make an Inner
+  TransformationCompositeConstruct make_inner(
+      9, {25, 19}, MakeInstructionDescriptor(57, SpvOpAccessChain, 0), 200);
+  // Bad: Too few fields to make the struct.
+  TransformationCompositeConstruct make_inner_bad(
+      9, {25}, MakeInstructionDescriptor(57, SpvOpAccessChain, 0), 200);
+  ASSERT_TRUE(make_inner.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(make_inner_bad.IsApplicable(context.get(), fact_manager));
+  make_inner.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(25, {}), MakeDataDescriptor(200, {0}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(19, {}), MakeDataDescriptor(200, {1}), context.get()));
+
+  // make an Outer
+  TransformationCompositeConstruct make_outer(
+      33, {46, 200, 56}, MakeInstructionDescriptor(200, SpvOpAccessChain, 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);
+  ASSERT_TRUE(make_outer.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(make_outer_bad.IsApplicable(context.get(), fact_manager));
+  make_outer.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(46, {}), MakeDataDescriptor(201, {0}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(200, {}),
+                                        MakeDataDescriptor(201, {1}),
+                                        context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(56, {}), MakeDataDescriptor(201, {2}), context.get()));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %9 "Inner"
+               OpMemberName %9 0 "a"
+               OpMemberName %9 1 "b"
+               OpName %11 "i1"
+               OpName %22 "i2"
+               OpName %33 "Outer"
+               OpMemberName %33 0 "c"
+               OpMemberName %33 1 "d"
+               OpMemberName %33 2 "e"
+               OpName %35 "o"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeVector %6 2
+          %8 = OpTypeInt 32 1
+          %9 = OpTypeStruct %7 %8
+         %10 = OpTypePointer Function %9
+         %12 = OpConstant %8 0
+         %13 = OpConstant %6 2
+         %14 = OpTypeInt 32 0
+         %15 = OpConstant %14 0
+         %16 = OpTypePointer Function %6
+         %18 = OpConstant %8 1
+         %19 = OpConstant %8 3
+         %20 = OpTypePointer Function %8
+         %23 = OpTypePointer Function %7
+         %31 = OpConstant %14 2
+         %32 = OpTypeArray %9 %31
+         %33 = OpTypeStruct %32 %9 %6
+         %34 = OpTypePointer Function %33
+         %36 = OpConstant %6 1
+         %37 = OpConstantComposite %7 %36 %13
+         %38 = OpConstant %8 2
+         %39 = OpConstantComposite %9 %37 %38
+         %40 = OpConstant %6 3
+         %41 = OpConstant %6 4
+         %42 = OpConstantComposite %7 %40 %41
+         %56 = OpConstant %6 5
+        %100 = OpUndef %9
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %11 = OpVariable %10 Function
+         %22 = OpVariable %10 Function
+         %35 = OpVariable %34 Function
+         %17 = OpAccessChain %16 %11 %12 %15
+               OpStore %17 %13
+         %21 = OpAccessChain %20 %11 %18
+               OpStore %21 %19
+         %24 = OpAccessChain %23 %11 %12
+         %25 = OpLoad %7 %24
+         %26 = OpAccessChain %23 %22 %12
+               OpStore %26 %25
+         %27 = OpAccessChain %20 %11 %18
+         %28 = OpLoad %8 %27
+         %29 = OpIAdd %8 %28 %18
+         %30 = OpAccessChain %20 %22 %18
+               OpStore %30 %29
+         %43 = OpAccessChain %20 %11 %18
+         %44 = OpLoad %8 %43
+         %45 = OpCompositeConstruct %9 %42 %44
+         %46 = OpCompositeConstruct %32 %39 %45
+         %47 = OpLoad %9 %22
+         %48 = OpCompositeConstruct %33 %46 %47 %40
+               OpStore %35 %48
+         %49 = OpLoad %9 %11
+         %50 = OpAccessChain %10 %35 %12 %12
+               OpStore %50 %49
+         %51 = OpLoad %9 %22
+         %52 = OpAccessChain %10 %35 %12 %18
+               OpStore %52 %51
+         %53 = OpAccessChain %10 %35 %12 %12
+         %54 = OpLoad %9 %53
+         %55 = OpAccessChain %10 %35 %18
+               OpStore %55 %54
+        %200 = OpCompositeConstruct %9 %25 %19
+        %201 = OpCompositeConstruct %33 %46 %200 %56
+         %57 = OpAccessChain %16 %35 %38
+               OpStore %57 %56
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationCompositeConstructTest, ConstructVectors) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %9 "v2"
+               OpName %27 "v3"
+               OpName %46 "v4"
+               OpName %53 "iv2"
+               OpName %61 "uv3"
+               OpName %72 "bv4"
+               OpName %88 "uv2"
+               OpName %95 "bv3"
+               OpName %104 "bv2"
+               OpName %116 "iv3"
+               OpName %124 "iv4"
+               OpName %133 "uv4"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeVector %6 2
+          %8 = OpTypePointer Function %7
+         %10 = OpConstant %6 1
+         %11 = OpConstant %6 2
+         %12 = OpConstantComposite %7 %10 %11
+         %13 = OpTypeInt 32 0
+         %14 = OpConstant %13 0
+         %15 = OpTypePointer Function %6
+         %18 = OpConstant %13 1
+         %21 = OpTypeBool
+         %25 = OpTypeVector %6 3
+         %26 = OpTypePointer Function %25
+         %33 = OpConstant %6 3
+         %34 = OpConstant %6 -0.756802499
+         %38 = OpConstant %13 2
+         %44 = OpTypeVector %6 4
+         %45 = OpTypePointer Function %44
+         %50 = OpTypeInt 32 1
+         %51 = OpTypeVector %50 2
+         %52 = OpTypePointer Function %51
+         %57 = OpTypePointer Function %50
+         %59 = OpTypeVector %13 3
+         %60 = OpTypePointer Function %59
+         %65 = OpConstant %13 3
+         %67 = OpTypePointer Function %13
+         %70 = OpTypeVector %21 4
+         %71 = OpTypePointer Function %70
+         %73 = OpConstantTrue %21
+         %74 = OpTypePointer Function %21
+         %86 = OpTypeVector %13 2
+         %87 = OpTypePointer Function %86
+         %93 = OpTypeVector %21 3
+         %94 = OpTypePointer Function %93
+        %102 = OpTypeVector %21 2
+        %103 = OpTypePointer Function %102
+        %111 = OpConstantFalse %21
+        %114 = OpTypeVector %50 3
+        %115 = OpTypePointer Function %114
+        %117 = OpConstant %50 3
+        %122 = OpTypeVector %50 4
+        %123 = OpTypePointer Function %122
+        %131 = OpTypeVector %13 4
+        %132 = OpTypePointer Function %131
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %9 = OpVariable %8 Function
+         %27 = OpVariable %26 Function
+         %46 = OpVariable %45 Function
+         %53 = OpVariable %52 Function
+         %61 = OpVariable %60 Function
+         %72 = OpVariable %71 Function
+         %88 = OpVariable %87 Function
+         %95 = OpVariable %94 Function
+        %104 = OpVariable %103 Function
+        %116 = OpVariable %115 Function
+        %124 = OpVariable %123 Function
+        %133 = OpVariable %132 Function
+               OpStore %9 %12
+         %16 = OpAccessChain %15 %9 %14
+         %17 = OpLoad %6 %16
+         %19 = OpAccessChain %15 %9 %18
+         %20 = OpLoad %6 %19
+         %22 = OpFOrdGreaterThan %21 %17 %20
+               OpSelectionMerge %24 None
+               OpBranchConditional %22 %23 %101
+         %23 = OpLabel
+         %28 = OpAccessChain %15 %9 %14
+         %29 = OpLoad %6 %28
+         %30 = OpAccessChain %15 %9 %18
+         %31 = OpLoad %6 %30
+         %32 = OpFAdd %6 %29 %31
+         %35 = OpCompositeConstruct %25 %32 %33 %34
+               OpStore %27 %35
+         %36 = OpAccessChain %15 %27 %14
+         %37 = OpLoad %6 %36
+         %39 = OpAccessChain %15 %27 %38
+         %40 = OpLoad %6 %39
+         %41 = OpFOrdLessThan %21 %37 %40
+               OpSelectionMerge %43 None
+               OpBranchConditional %41 %42 %69
+         %42 = OpLabel
+         %47 = OpAccessChain %15 %9 %18
+         %48 = OpLoad %6 %47
+         %49 = OpAccessChain %15 %46 %14
+               OpStore %49 %48
+         %54 = OpAccessChain %15 %27 %38
+         %55 = OpLoad %6 %54
+         %56 = OpConvertFToS %50 %55
+         %58 = OpAccessChain %57 %53 %14
+               OpStore %58 %56
+         %62 = OpAccessChain %15 %46 %14
+         %63 = OpLoad %6 %62
+         %64 = OpConvertFToU %13 %63
+         %66 = OpIAdd %13 %64 %65
+         %68 = OpAccessChain %67 %61 %14
+               OpStore %68 %66
+               OpBranch %43
+         %69 = OpLabel
+         %75 = OpAccessChain %74 %72 %14
+               OpStore %75 %73
+         %76 = OpAccessChain %74 %72 %14
+         %77 = OpLoad %21 %76
+         %78 = OpLogicalNot %21 %77
+         %79 = OpAccessChain %74 %72 %18
+               OpStore %79 %78
+         %80 = OpAccessChain %74 %72 %14
+         %81 = OpLoad %21 %80
+         %82 = OpAccessChain %74 %72 %18
+         %83 = OpLoad %21 %82
+         %84 = OpLogicalAnd %21 %81 %83
+         %85 = OpAccessChain %74 %72 %38
+               OpStore %85 %84
+         %89 = OpAccessChain %67 %88 %14
+         %90 = OpLoad %13 %89
+         %91 = OpINotEqual %21 %90 %14
+         %92 = OpAccessChain %74 %72 %65
+               OpStore %92 %91
+               OpBranch %43
+         %43 = OpLabel
+         %96 = OpLoad %70 %72
+         %97 = OpCompositeExtract %21 %96 0
+         %98 = OpCompositeExtract %21 %96 1
+         %99 = OpCompositeExtract %21 %96 2
+        %100 = OpCompositeConstruct %93 %97 %98 %99
+               OpStore %95 %100
+               OpBranch %24
+        %101 = OpLabel
+        %105 = OpAccessChain %67 %88 %14
+        %106 = OpLoad %13 %105
+        %107 = OpINotEqual %21 %106 %14
+        %108 = OpCompositeConstruct %102 %107 %107
+               OpStore %104 %108
+               OpBranch %24
+         %24 = OpLabel
+        %109 = OpAccessChain %74 %104 %18
+        %110 = OpLoad %21 %109
+        %112 = OpLogicalOr %21 %110 %111
+        %113 = OpAccessChain %74 %104 %14
+               OpStore %113 %112
+        %118 = OpAccessChain %57 %116 %14
+               OpStore %118 %117
+        %119 = OpAccessChain %57 %116 %14
+        %120 = OpLoad %50 %119
+        %121 = OpAccessChain %57 %53 %18
+               OpStore %121 %120
+        %125 = OpAccessChain %57 %116 %14
+        %126 = OpLoad %50 %125
+        %127 = OpAccessChain %57 %53 %18
+        %128 = OpLoad %50 %127
+        %129 = OpIAdd %50 %126 %128
+        %130 = OpAccessChain %57 %124 %65
+               OpStore %130 %129
+        %134 = OpAccessChain %57 %116 %14
+        %135 = OpLoad %50 %134
+        %136 = OpBitcast %13 %135
+        %137 = OpAccessChain %67 %133 %14
+               OpStore %137 %136
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  TransformationCompositeConstruct make_vec2(
+      7, {17, 11}, MakeInstructionDescriptor(100, SpvOpStore, 0), 200);
+  // Bad: not enough data for a vec2
+  TransformationCompositeConstruct make_vec2_bad(
+      7, {11}, MakeInstructionDescriptor(100, SpvOpStore, 0), 200);
+  ASSERT_TRUE(make_vec2.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(make_vec2_bad.IsApplicable(context.get(), fact_manager));
+  make_vec2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(17, {}), MakeDataDescriptor(200, {0}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(11, {}), MakeDataDescriptor(200, {1}), context.get()));
+
+  TransformationCompositeConstruct make_vec3(
+      25, {12, 32}, MakeInstructionDescriptor(35, SpvOpCompositeConstruct, 0),
+      201);
+  // Bad: too much data for a vec3
+  TransformationCompositeConstruct make_vec3_bad(
+      25, {12, 32, 32},
+      MakeInstructionDescriptor(35, SpvOpCompositeConstruct, 0), 201);
+  ASSERT_TRUE(make_vec3.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(make_vec3_bad.IsApplicable(context.get(), fact_manager));
+  make_vec3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(12, {0}),
+                                        MakeDataDescriptor(201, {0}),
+                                        context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(12, {1}),
+                                        MakeDataDescriptor(201, {1}),
+                                        context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(32, {}), MakeDataDescriptor(201, {2}), context.get()));
+
+  TransformationCompositeConstruct make_vec4(
+      44, {32, 32, 10, 11}, MakeInstructionDescriptor(75, SpvOpAccessChain, 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);
+  ASSERT_TRUE(make_vec4.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(make_vec4_bad.IsApplicable(context.get(), fact_manager));
+  make_vec4.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(32, {}), MakeDataDescriptor(202, {0}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(32, {}), MakeDataDescriptor(202, {1}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(10, {}), MakeDataDescriptor(202, {2}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(11, {}), MakeDataDescriptor(202, {3}), context.get()));
+
+  TransformationCompositeConstruct make_ivec2(
+      51, {126, 120}, MakeInstructionDescriptor(128, SpvOpLoad, 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);
+  ASSERT_TRUE(make_ivec2.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(make_ivec2_bad.IsApplicable(context.get(), fact_manager));
+  make_ivec2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(126, {}),
+                                        MakeDataDescriptor(203, {0}),
+                                        context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(120, {}),
+                                        MakeDataDescriptor(203, {1}),
+                                        context.get()));
+
+  TransformationCompositeConstruct make_ivec3(
+      114, {56, 117, 56}, MakeInstructionDescriptor(66, SpvOpAccessChain, 0),
+      204);
+  // Bad because 1300 is not an id
+  TransformationCompositeConstruct make_ivec3_bad(
+      114, {56, 117, 1300}, MakeInstructionDescriptor(66, SpvOpAccessChain, 0),
+      204);
+  ASSERT_TRUE(make_ivec3.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(make_ivec3_bad.IsApplicable(context.get(), fact_manager));
+  make_ivec3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(56, {}), MakeDataDescriptor(204, {0}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(117, {}),
+                                        MakeDataDescriptor(204, {1}),
+                                        context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(56, {}), MakeDataDescriptor(204, {2}), context.get()));
+
+  TransformationCompositeConstruct make_ivec4(
+      122, {56, 117, 117, 117}, MakeInstructionDescriptor(66, SpvOpIAdd, 0),
+      205);
+  // Bad because 86 is the wrong type.
+  TransformationCompositeConstruct make_ivec4_bad(
+      86, {56, 117, 117, 117}, MakeInstructionDescriptor(66, SpvOpIAdd, 0),
+      205);
+  ASSERT_TRUE(make_ivec4.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(make_ivec4_bad.IsApplicable(context.get(), fact_manager));
+  make_ivec4.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(56, {}), MakeDataDescriptor(205, {0}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(117, {}),
+                                        MakeDataDescriptor(205, {1}),
+                                        context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(117, {}),
+                                        MakeDataDescriptor(205, {2}),
+                                        context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(117, {}),
+                                        MakeDataDescriptor(205, {3}),
+                                        context.get()));
+
+  TransformationCompositeConstruct make_uvec2(
+      86, {18, 38}, MakeInstructionDescriptor(133, SpvOpAccessChain, 0), 206);
+  TransformationCompositeConstruct make_uvec2_bad(
+      86, {18, 38}, MakeInstructionDescriptor(133, SpvOpAccessChain, 200), 206);
+  ASSERT_TRUE(make_uvec2.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(make_uvec2_bad.IsApplicable(context.get(), fact_manager));
+  make_uvec2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(18, {}), MakeDataDescriptor(206, {0}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(38, {}), MakeDataDescriptor(206, {1}), context.get()));
+
+  TransformationCompositeConstruct make_uvec3(
+      59, {14, 18, 136}, MakeInstructionDescriptor(137, SpvOpReturn, 0), 207);
+  // Bad because 1300 is not an id
+  TransformationCompositeConstruct make_uvec3_bad(
+      59, {14, 18, 1300}, MakeInstructionDescriptor(137, SpvOpReturn, 0), 207);
+  ASSERT_TRUE(make_uvec3.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(make_uvec3_bad.IsApplicable(context.get(), fact_manager));
+  make_uvec3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(14, {}), MakeDataDescriptor(207, {0}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(18, {}), MakeDataDescriptor(207, {1}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(136, {}),
+                                        MakeDataDescriptor(207, {2}),
+                                        context.get()));
+
+  TransformationCompositeConstruct make_uvec4(
+      131, {14, 18, 136, 136},
+      MakeInstructionDescriptor(137, SpvOpAccessChain, 0), 208);
+  // Bad because 86 is the wrong type.
+  TransformationCompositeConstruct make_uvec4_bad(
+      86, {14, 18, 136, 136},
+      MakeInstructionDescriptor(137, SpvOpAccessChain, 0), 208);
+  ASSERT_TRUE(make_uvec4.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(make_uvec4_bad.IsApplicable(context.get(), fact_manager));
+  make_uvec4.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(14, {}), MakeDataDescriptor(208, {0}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(18, {}), MakeDataDescriptor(208, {1}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(136, {}),
+                                        MakeDataDescriptor(208, {2}),
+                                        context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(136, {}),
+                                        MakeDataDescriptor(208, {3}),
+                                        context.get()));
+
+  TransformationCompositeConstruct make_bvec2(
+      102,
+      {
+          111,
+          41,
+      },
+      MakeInstructionDescriptor(75, SpvOpAccessChain, 0), 209);
+  // Bad because 0 is not a valid base instruction id
+  TransformationCompositeConstruct make_bvec2_bad(
+      102,
+      {
+          111,
+          41,
+      },
+      MakeInstructionDescriptor(0, SpvOpExtInstImport, 0), 209);
+  ASSERT_TRUE(make_bvec2.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(make_bvec2_bad.IsApplicable(context.get(), fact_manager));
+  make_bvec2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(111, {}),
+                                        MakeDataDescriptor(209, {0}),
+                                        context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(41, {}), MakeDataDescriptor(209, {1}), context.get()));
+
+  TransformationCompositeConstruct make_bvec3(
+      93, {108, 73}, MakeInstructionDescriptor(108, SpvOpStore, 0), 210);
+  // Bad because there are too many components for a bvec3
+  TransformationCompositeConstruct make_bvec3_bad(
+      93, {108, 108}, MakeInstructionDescriptor(108, SpvOpStore, 0), 210);
+  ASSERT_TRUE(make_bvec3.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(make_bvec3_bad.IsApplicable(context.get(), fact_manager));
+  make_bvec3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(108, {0}),
+                                        MakeDataDescriptor(210, {0}),
+                                        context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(108, {1}),
+                                        MakeDataDescriptor(210, {1}),
+                                        context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(73, {}), MakeDataDescriptor(210, {2}), context.get()));
+
+  TransformationCompositeConstruct make_bvec4(
+      70, {108, 108}, MakeInstructionDescriptor(108, SpvOpBranch, 0), 211);
+  // Bad because 21 is a type, not a result id
+  TransformationCompositeConstruct make_bvec4_bad(
+      70, {21, 108}, MakeInstructionDescriptor(108, SpvOpBranch, 0), 211);
+  ASSERT_TRUE(make_bvec4.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(make_bvec4_bad.IsApplicable(context.get(), fact_manager));
+  make_bvec4.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(108, {0}),
+                                        MakeDataDescriptor(211, {0}),
+                                        context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(108, {1}),
+                                        MakeDataDescriptor(211, {1}),
+                                        context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(108, {0}),
+                                        MakeDataDescriptor(211, {2}),
+                                        context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(108, {1}),
+                                        MakeDataDescriptor(211, {3}),
+                                        context.get()));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %9 "v2"
+               OpName %27 "v3"
+               OpName %46 "v4"
+               OpName %53 "iv2"
+               OpName %61 "uv3"
+               OpName %72 "bv4"
+               OpName %88 "uv2"
+               OpName %95 "bv3"
+               OpName %104 "bv2"
+               OpName %116 "iv3"
+               OpName %124 "iv4"
+               OpName %133 "uv4"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeVector %6 2
+          %8 = OpTypePointer Function %7
+         %10 = OpConstant %6 1
+         %11 = OpConstant %6 2
+         %12 = OpConstantComposite %7 %10 %11
+         %13 = OpTypeInt 32 0
+         %14 = OpConstant %13 0
+         %15 = OpTypePointer Function %6
+         %18 = OpConstant %13 1
+         %21 = OpTypeBool
+         %25 = OpTypeVector %6 3
+         %26 = OpTypePointer Function %25
+         %33 = OpConstant %6 3
+         %34 = OpConstant %6 -0.756802499
+         %38 = OpConstant %13 2
+         %44 = OpTypeVector %6 4
+         %45 = OpTypePointer Function %44
+         %50 = OpTypeInt 32 1
+         %51 = OpTypeVector %50 2
+         %52 = OpTypePointer Function %51
+         %57 = OpTypePointer Function %50
+         %59 = OpTypeVector %13 3
+         %60 = OpTypePointer Function %59
+         %65 = OpConstant %13 3
+         %67 = OpTypePointer Function %13
+         %70 = OpTypeVector %21 4
+         %71 = OpTypePointer Function %70
+         %73 = OpConstantTrue %21
+         %74 = OpTypePointer Function %21
+         %86 = OpTypeVector %13 2
+         %87 = OpTypePointer Function %86
+         %93 = OpTypeVector %21 3
+         %94 = OpTypePointer Function %93
+        %102 = OpTypeVector %21 2
+        %103 = OpTypePointer Function %102
+        %111 = OpConstantFalse %21
+        %114 = OpTypeVector %50 3
+        %115 = OpTypePointer Function %114
+        %117 = OpConstant %50 3
+        %122 = OpTypeVector %50 4
+        %123 = OpTypePointer Function %122
+        %131 = OpTypeVector %13 4
+        %132 = OpTypePointer Function %131
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %9 = OpVariable %8 Function
+         %27 = OpVariable %26 Function
+         %46 = OpVariable %45 Function
+         %53 = OpVariable %52 Function
+         %61 = OpVariable %60 Function
+         %72 = OpVariable %71 Function
+         %88 = OpVariable %87 Function
+         %95 = OpVariable %94 Function
+        %104 = OpVariable %103 Function
+        %116 = OpVariable %115 Function
+        %124 = OpVariable %123 Function
+        %133 = OpVariable %132 Function
+               OpStore %9 %12
+        %206 = OpCompositeConstruct %86 %18 %38
+         %16 = OpAccessChain %15 %9 %14
+         %17 = OpLoad %6 %16
+         %19 = OpAccessChain %15 %9 %18
+         %20 = OpLoad %6 %19
+         %22 = OpFOrdGreaterThan %21 %17 %20
+               OpSelectionMerge %24 None
+               OpBranchConditional %22 %23 %101
+         %23 = OpLabel
+         %28 = OpAccessChain %15 %9 %14
+         %29 = OpLoad %6 %28
+         %30 = OpAccessChain %15 %9 %18
+         %31 = OpLoad %6 %30
+         %32 = OpFAdd %6 %29 %31
+        %201 = OpCompositeConstruct %25 %12 %32
+         %35 = OpCompositeConstruct %25 %32 %33 %34
+               OpStore %27 %35
+         %36 = OpAccessChain %15 %27 %14
+         %37 = OpLoad %6 %36
+         %39 = OpAccessChain %15 %27 %38
+         %40 = OpLoad %6 %39
+         %41 = OpFOrdLessThan %21 %37 %40
+               OpSelectionMerge %43 None
+               OpBranchConditional %41 %42 %69
+         %42 = OpLabel
+         %47 = OpAccessChain %15 %9 %18
+         %48 = OpLoad %6 %47
+         %49 = OpAccessChain %15 %46 %14
+               OpStore %49 %48
+         %54 = OpAccessChain %15 %27 %38
+         %55 = OpLoad %6 %54
+         %56 = OpConvertFToS %50 %55
+         %58 = OpAccessChain %57 %53 %14
+               OpStore %58 %56
+         %62 = OpAccessChain %15 %46 %14
+         %63 = OpLoad %6 %62
+         %64 = OpConvertFToU %13 %63
+        %205 = OpCompositeConstruct %122 %56 %117 %117 %117
+         %66 = OpIAdd %13 %64 %65
+        %204 = OpCompositeConstruct %114 %56 %117 %56
+         %68 = OpAccessChain %67 %61 %14
+               OpStore %68 %66
+               OpBranch %43
+         %69 = OpLabel
+        %202 = OpCompositeConstruct %44 %32 %32 %10 %11
+        %209 = OpCompositeConstruct %102 %111 %41
+         %75 = OpAccessChain %74 %72 %14
+               OpStore %75 %73
+         %76 = OpAccessChain %74 %72 %14
+         %77 = OpLoad %21 %76
+         %78 = OpLogicalNot %21 %77
+         %79 = OpAccessChain %74 %72 %18
+               OpStore %79 %78
+         %80 = OpAccessChain %74 %72 %14
+         %81 = OpLoad %21 %80
+         %82 = OpAccessChain %74 %72 %18
+         %83 = OpLoad %21 %82
+         %84 = OpLogicalAnd %21 %81 %83
+         %85 = OpAccessChain %74 %72 %38
+               OpStore %85 %84
+         %89 = OpAccessChain %67 %88 %14
+         %90 = OpLoad %13 %89
+         %91 = OpINotEqual %21 %90 %14
+         %92 = OpAccessChain %74 %72 %65
+               OpStore %92 %91
+               OpBranch %43
+         %43 = OpLabel
+         %96 = OpLoad %70 %72
+         %97 = OpCompositeExtract %21 %96 0
+         %98 = OpCompositeExtract %21 %96 1
+         %99 = OpCompositeExtract %21 %96 2
+        %100 = OpCompositeConstruct %93 %97 %98 %99
+        %200 = OpCompositeConstruct %7 %17 %11
+               OpStore %95 %100
+               OpBranch %24
+        %101 = OpLabel
+        %105 = OpAccessChain %67 %88 %14
+        %106 = OpLoad %13 %105
+        %107 = OpINotEqual %21 %106 %14
+        %108 = OpCompositeConstruct %102 %107 %107
+        %210 = OpCompositeConstruct %93 %108 %73
+               OpStore %104 %108
+        %211 = OpCompositeConstruct %70 %108 %108
+               OpBranch %24
+         %24 = OpLabel
+        %109 = OpAccessChain %74 %104 %18
+        %110 = OpLoad %21 %109
+        %112 = OpLogicalOr %21 %110 %111
+        %113 = OpAccessChain %74 %104 %14
+               OpStore %113 %112
+        %118 = OpAccessChain %57 %116 %14
+               OpStore %118 %117
+        %119 = OpAccessChain %57 %116 %14
+        %120 = OpLoad %50 %119
+        %121 = OpAccessChain %57 %53 %18
+               OpStore %121 %120
+        %125 = OpAccessChain %57 %116 %14
+        %126 = OpLoad %50 %125
+        %127 = OpAccessChain %57 %53 %18
+        %203 = OpCompositeConstruct %51 %126 %120
+        %128 = OpLoad %50 %127
+        %129 = OpIAdd %50 %126 %128
+        %130 = OpAccessChain %57 %124 %65
+               OpStore %130 %129
+        %134 = OpAccessChain %57 %116 %14
+        %135 = OpLoad %50 %134
+        %136 = OpBitcast %13 %135
+        %208 = OpCompositeConstruct %131 %14 %18 %136 %136
+        %137 = OpAccessChain %67 %133 %14
+               OpStore %137 %136
+        %207 = OpCompositeConstruct %59 %14 %18 %136
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/test/fuzz/transformation_composite_extract_test.cpp b/test/fuzz/transformation_composite_extract_test.cpp
new file mode 100644
index 0000000..5cc2115
--- /dev/null
+++ b/test/fuzz/transformation_composite_extract_test.cpp
@@ -0,0 +1,398 @@
+// 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.
+
+#include "source/fuzz/transformation_composite_extract.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationCompositeExtractTest, BasicTest) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %8 "a"
+               OpName %10 "b"
+               OpName %17 "FunnyPoint"
+               OpMemberName %17 0 "x"
+               OpMemberName %17 1 "y"
+               OpMemberName %17 2 "z"
+               OpName %19 "p"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+         %12 = OpTypeBool
+         %16 = OpTypeFloat 32
+         %17 = OpTypeStruct %16 %16 %6
+         %81 = OpTypeStruct %17 %16
+         %18 = OpTypePointer Function %17
+         %20 = OpConstant %6 0
+         %23 = OpTypePointer Function %16
+         %26 = OpConstant %6 1
+         %30 = OpConstant %6 2
+         %80 = OpUndef %16
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %10 = OpVariable %7 Function
+         %19 = OpVariable %18 Function
+          %9 = OpLoad %6 %8
+         %11 = OpLoad %6 %10
+        %100 = OpCompositeConstruct %17 %80 %80 %26
+        %104 = OpCompositeConstruct %81 %100 %80
+         %13 = OpIEqual %12 %9 %11
+               OpSelectionMerge %15 None
+               OpBranchConditional %13 %14 %25
+         %14 = OpLabel
+         %21 = OpLoad %6 %8
+         %22 = OpConvertSToF %16 %21
+        %101 = OpCompositeConstruct %17 %22 %80 %30
+         %24 = OpAccessChain %23 %19 %20
+               OpStore %24 %22
+               OpBranch %15
+         %25 = OpLabel
+         %27 = OpLoad %6 %10
+         %28 = OpConvertSToF %16 %27
+        %102 = OpCompositeConstruct %17 %80 %28 %27
+         %29 = OpAccessChain %23 %19 %26
+               OpStore %29 %28
+               OpBranch %15
+         %15 = OpLabel
+         %31 = OpAccessChain %23 %19 %20
+         %32 = OpLoad %16 %31
+         %33 = OpAccessChain %23 %19 %26
+         %34 = OpLoad %16 %33
+        %103 = OpCompositeConstruct %17 %34 %32 %9
+         %35 = OpFAdd %16 %32 %34
+         %36 = OpConvertFToS %6 %35
+         %37 = OpAccessChain %7 %19 %30
+               OpStore %37 %36
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  // Instruction does not exist.
+  ASSERT_FALSE(TransformationCompositeExtract(
+                   MakeInstructionDescriptor(36, SpvOpIAdd, 0), 200, 101, {0})
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Id for composite is not a composite.
+  ASSERT_FALSE(TransformationCompositeExtract(
+                   MakeInstructionDescriptor(36, SpvOpIAdd, 0), 200, 27, {})
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Composite does not dominate instruction being inserted before.
+  ASSERT_FALSE(
+      TransformationCompositeExtract(
+          MakeInstructionDescriptor(37, SpvOpAccessChain, 0), 200, 101, {0})
+          .IsApplicable(context.get(), fact_manager));
+
+  // Too many indices for extraction from struct composite.
+  ASSERT_FALSE(
+      TransformationCompositeExtract(
+          MakeInstructionDescriptor(24, SpvOpAccessChain, 0), 200, 101, {0, 0})
+          .IsApplicable(context.get(), fact_manager));
+
+  // Too many indices for extraction from struct composite.
+  ASSERT_FALSE(
+      TransformationCompositeExtract(
+          MakeInstructionDescriptor(13, SpvOpIEqual, 0), 200, 104, {0, 0, 0})
+          .IsApplicable(context.get(), fact_manager));
+
+  // Out of bounds index for extraction from struct composite.
+  ASSERT_FALSE(
+      TransformationCompositeExtract(
+          MakeInstructionDescriptor(13, SpvOpIEqual, 0), 200, 104, {0, 3})
+          .IsApplicable(context.get(), fact_manager));
+
+  // Result id already used.
+  ASSERT_FALSE(TransformationCompositeExtract(
+                   MakeInstructionDescriptor(35, SpvOpFAdd, 0), 80, 103, {0})
+                   .IsApplicable(context.get(), fact_manager));
+
+  TransformationCompositeExtract transformation_1(
+      MakeInstructionDescriptor(36, SpvOpConvertFToS, 0), 201, 100, {2});
+  ASSERT_TRUE(transformation_1.IsApplicable(context.get(), fact_manager));
+  transformation_1.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  TransformationCompositeExtract transformation_2(
+      MakeInstructionDescriptor(37, SpvOpAccessChain, 0), 202, 104, {0, 2});
+  ASSERT_TRUE(transformation_2.IsApplicable(context.get(), fact_manager));
+  transformation_2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  TransformationCompositeExtract transformation_3(
+      MakeInstructionDescriptor(29, SpvOpAccessChain, 0), 203, 104, {0});
+  ASSERT_TRUE(transformation_3.IsApplicable(context.get(), fact_manager));
+  transformation_3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  TransformationCompositeExtract transformation_4(
+      MakeInstructionDescriptor(24, SpvOpStore, 0), 204, 101, {0});
+  ASSERT_TRUE(transformation_4.IsApplicable(context.get(), fact_manager));
+  transformation_4.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  TransformationCompositeExtract transformation_5(
+      MakeInstructionDescriptor(29, SpvOpBranch, 0), 205, 102, {2});
+  ASSERT_TRUE(transformation_5.IsApplicable(context.get(), fact_manager));
+  transformation_5.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  TransformationCompositeExtract transformation_6(
+      MakeInstructionDescriptor(37, SpvOpReturn, 0), 206, 103, {1});
+  ASSERT_TRUE(transformation_6.IsApplicable(context.get(), fact_manager));
+  transformation_6.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(201, {}),
+                                        MakeDataDescriptor(100, {2}),
+                                        context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(202, {}),
+                                        MakeDataDescriptor(104, {0, 2}),
+                                        context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(203, {}),
+                                        MakeDataDescriptor(104, {0}),
+                                        context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(204, {}),
+                                        MakeDataDescriptor(101, {0}),
+                                        context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(205, {}),
+                                        MakeDataDescriptor(102, {2}),
+                                        context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(206, {}),
+                                        MakeDataDescriptor(103, {1}),
+                                        context.get()));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %8 "a"
+               OpName %10 "b"
+               OpName %17 "FunnyPoint"
+               OpMemberName %17 0 "x"
+               OpMemberName %17 1 "y"
+               OpMemberName %17 2 "z"
+               OpName %19 "p"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+         %12 = OpTypeBool
+         %16 = OpTypeFloat 32
+         %17 = OpTypeStruct %16 %16 %6
+         %81 = OpTypeStruct %17 %16
+         %18 = OpTypePointer Function %17
+         %20 = OpConstant %6 0
+         %23 = OpTypePointer Function %16
+         %26 = OpConstant %6 1
+         %30 = OpConstant %6 2
+         %80 = OpUndef %16
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %10 = OpVariable %7 Function
+         %19 = OpVariable %18 Function
+          %9 = OpLoad %6 %8
+         %11 = OpLoad %6 %10
+        %100 = OpCompositeConstruct %17 %80 %80 %26
+        %104 = OpCompositeConstruct %81 %100 %80
+         %13 = OpIEqual %12 %9 %11
+               OpSelectionMerge %15 None
+               OpBranchConditional %13 %14 %25
+         %14 = OpLabel
+         %21 = OpLoad %6 %8
+         %22 = OpConvertSToF %16 %21
+        %101 = OpCompositeConstruct %17 %22 %80 %30
+         %24 = OpAccessChain %23 %19 %20
+        %204 = OpCompositeExtract %16 %101 0
+               OpStore %24 %22
+               OpBranch %15
+         %25 = OpLabel
+         %27 = OpLoad %6 %10
+         %28 = OpConvertSToF %16 %27
+        %102 = OpCompositeConstruct %17 %80 %28 %27
+        %203 = OpCompositeExtract %17 %104 0
+         %29 = OpAccessChain %23 %19 %26
+               OpStore %29 %28
+        %205 = OpCompositeExtract %6 %102 2
+               OpBranch %15
+         %15 = OpLabel
+         %31 = OpAccessChain %23 %19 %20
+         %32 = OpLoad %16 %31
+         %33 = OpAccessChain %23 %19 %26
+         %34 = OpLoad %16 %33
+        %103 = OpCompositeConstruct %17 %34 %32 %9
+         %35 = OpFAdd %16 %32 %34
+        %201 = OpCompositeExtract %6 %100 2
+         %36 = OpConvertFToS %6 %35
+        %202 = OpCompositeExtract %6 %104 0 2
+         %37 = OpAccessChain %7 %19 %30
+               OpStore %37 %36
+        %206 = OpCompositeExtract %16 %103 1
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationCompositeExtractTest, IllegalInsertionPoints) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %51 %27
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %25 "buf"
+               OpMemberName %25 0 "value"
+               OpName %27 ""
+               OpName %51 "color"
+               OpMemberDecorate %25 0 Offset 0
+               OpDecorate %25 Block
+               OpDecorate %27 DescriptorSet 0
+               OpDecorate %27 Binding 0
+               OpDecorate %51 Location 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeVector %6 4
+         %10 = OpConstant %6 0.300000012
+         %11 = OpConstant %6 0.400000006
+         %12 = OpConstant %6 0.5
+         %13 = OpConstant %6 1
+         %14 = OpConstantComposite %7 %10 %11 %12 %13
+         %15 = OpTypeInt 32 1
+         %18 = OpConstant %15 0
+         %25 = OpTypeStruct %6
+         %26 = OpTypePointer Uniform %25
+         %27 = OpVariable %26 Uniform
+         %28 = OpTypePointer Uniform %6
+         %32 = OpTypeBool
+        %103 = OpConstantTrue %32
+         %34 = OpConstant %6 0.100000001
+         %48 = OpConstant %15 1
+         %50 = OpTypePointer Output %7
+         %51 = OpVariable %50 Output
+        %100 = OpTypePointer Function %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+        %101 = OpVariable %100 Function
+        %102 = OpVariable %100 Function
+               OpBranch %19
+         %19 = OpLabel
+         %60 = OpPhi %7 %14 %5 %58 %20
+         %59 = OpPhi %15 %18 %5 %49 %20
+         %29 = OpAccessChain %28 %27 %18
+         %30 = OpLoad %6 %29
+         %31 = OpConvertFToS %15 %30
+         %33 = OpSLessThan %32 %59 %31
+               OpLoopMerge %21 %20 None
+               OpBranchConditional %33 %20 %21
+         %20 = OpLabel
+         %39 = OpCompositeExtract %6 %60 0
+         %40 = OpFAdd %6 %39 %34
+         %55 = OpCompositeInsert %7 %40 %60 0
+         %44 = OpCompositeExtract %6 %60 1
+         %45 = OpFSub %6 %44 %34
+         %58 = OpCompositeInsert %7 %45 %55 1
+         %49 = OpIAdd %15 %59 %48
+               OpBranch %19
+         %21 = OpLabel
+               OpStore %51 %60
+               OpSelectionMerge %105 None
+               OpBranchConditional %103 %104 %105
+        %104 = OpLabel
+               OpBranch %105
+        %105 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  // Cannot insert before the OpVariables of a function.
+  ASSERT_FALSE(
+      TransformationCompositeExtract(
+          MakeInstructionDescriptor(101, SpvOpVariable, 0), 200, 14, {0})
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationCompositeExtract(
+          MakeInstructionDescriptor(101, SpvOpVariable, 1), 200, 14, {1})
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationCompositeExtract(
+          MakeInstructionDescriptor(102, SpvOpVariable, 0), 200, 14, {1})
+          .IsApplicable(context.get(), fact_manager));
+  // OK to insert right after the OpVariables.
+  ASSERT_FALSE(TransformationCompositeExtract(
+                   MakeInstructionDescriptor(102, SpvOpBranch, 1), 200, 14, {1})
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Cannot insert before the OpPhis of a block.
+  ASSERT_FALSE(TransformationCompositeExtract(
+                   MakeInstructionDescriptor(60, SpvOpPhi, 0), 200, 14, {2})
+                   .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(TransformationCompositeExtract(
+                   MakeInstructionDescriptor(59, SpvOpPhi, 0), 200, 14, {3})
+                   .IsApplicable(context.get(), fact_manager));
+  // OK to insert after the OpPhis.
+  ASSERT_TRUE(
+      TransformationCompositeExtract(
+          MakeInstructionDescriptor(59, SpvOpAccessChain, 0), 200, 14, {3})
+          .IsApplicable(context.get(), fact_manager));
+
+  // Cannot insert before OpLoopMerge
+  ASSERT_FALSE(TransformationCompositeExtract(
+                   MakeInstructionDescriptor(33, SpvOpBranchConditional, 0),
+                   200, 14, {3})
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Cannot insert before OpSelectionMerge
+  ASSERT_FALSE(TransformationCompositeExtract(
+                   MakeInstructionDescriptor(21, SpvOpBranchConditional, 0),
+                   200, 14, {2})
+                   .IsApplicable(context.get(), fact_manager));
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/test/fuzz/transformation_copy_object_test.cpp b/test/fuzz/transformation_copy_object_test.cpp
index 0c214c8..9d3fde0 100644
--- a/test/fuzz/transformation_copy_object_test.cpp
+++ b/test/fuzz/transformation_copy_object_test.cpp
@@ -12,8 +12,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "source/fuzz/transformation_copy_object.h"
+#include <algorithm>
+#include <set>
+#include <unordered_set>
+
 #include "source/fuzz/data_descriptor.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_copy_object.h"
 #include "test/fuzz/fuzz_test_util.h"
 
 namespace spvtools {
@@ -47,58 +52,77 @@
 
   FactManager fact_manager;
 
-  ASSERT_EQ(0, fact_manager.GetIdsForWhichSynonymsAreKnown().size());
+  ASSERT_EQ(0,
+            fact_manager.GetIdsForWhichSynonymsAreKnown(context.get()).size());
 
-  TransformationCopyObject copy_true(7, 5, 1, 100);
-  ASSERT_TRUE(copy_true.IsApplicable(context.get(), fact_manager));
-  copy_true.Apply(context.get(), &fact_manager);
+  {
+    TransformationCopyObject copy_true(
+        7, MakeInstructionDescriptor(5, SpvOpReturn, 0), 100);
+    ASSERT_TRUE(copy_true.IsApplicable(context.get(), fact_manager));
+    copy_true.Apply(context.get(), &fact_manager);
 
-  const std::set<uint32_t>& ids_for_which_synonyms_are_known =
-      fact_manager.GetIdsForWhichSynonymsAreKnown();
-  ASSERT_EQ(1, ids_for_which_synonyms_are_known.size());
-  ASSERT_TRUE(ids_for_which_synonyms_are_known.find(7) !=
-              ids_for_which_synonyms_are_known.end());
-  ASSERT_EQ(1, fact_manager.GetSynonymsForId(7).size());
-  protobufs::DataDescriptor descriptor_100 = MakeDataDescriptor(100, {});
-  ASSERT_TRUE(DataDescriptorEquals()(&descriptor_100,
-                                     &fact_manager.GetSynonymsForId(7)[0]));
+    std::vector<uint32_t> ids_for_which_synonyms_are_known =
+        fact_manager.GetIdsForWhichSynonymsAreKnown(context.get());
+    ASSERT_EQ(2, ids_for_which_synonyms_are_known.size());
+    ASSERT_TRUE(std::find(ids_for_which_synonyms_are_known.begin(),
+                          ids_for_which_synonyms_are_known.end(),
+                          7) != ids_for_which_synonyms_are_known.end());
+    ASSERT_EQ(2, fact_manager.GetSynonymsForId(7, context.get()).size());
+    protobufs::DataDescriptor descriptor_100 = MakeDataDescriptor(100, {});
+    ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(7, {}),
+                                          descriptor_100, context.get()));
+  }
 
-  TransformationCopyObject copy_false(8, 100, 1, 101);
-  ASSERT_TRUE(copy_false.IsApplicable(context.get(), fact_manager));
-  copy_false.Apply(context.get(), &fact_manager);
-  ASSERT_EQ(2, ids_for_which_synonyms_are_known.size());
-  ASSERT_TRUE(ids_for_which_synonyms_are_known.find(8) !=
-              ids_for_which_synonyms_are_known.end());
-  ASSERT_EQ(1, fact_manager.GetSynonymsForId(8).size());
-  protobufs::DataDescriptor descriptor_101 = MakeDataDescriptor(101, {});
-  ASSERT_TRUE(DataDescriptorEquals()(&descriptor_101,
-                                     &fact_manager.GetSynonymsForId(8)[0]));
+  {
+    TransformationCopyObject copy_false(
+        8, MakeInstructionDescriptor(100, SpvOpReturn, 0), 101);
+    ASSERT_TRUE(copy_false.IsApplicable(context.get(), fact_manager));
+    copy_false.Apply(context.get(), &fact_manager);
+    std::vector<uint32_t> ids_for_which_synonyms_are_known =
+        fact_manager.GetIdsForWhichSynonymsAreKnown(context.get());
+    ASSERT_EQ(4, ids_for_which_synonyms_are_known.size());
+    ASSERT_TRUE(std::find(ids_for_which_synonyms_are_known.begin(),
+                          ids_for_which_synonyms_are_known.end(),
+                          8) != ids_for_which_synonyms_are_known.end());
+    ASSERT_EQ(2, fact_manager.GetSynonymsForId(8, context.get()).size());
+    protobufs::DataDescriptor descriptor_101 = MakeDataDescriptor(101, {});
+    ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(8, {}),
+                                          descriptor_101, context.get()));
+  }
 
-  TransformationCopyObject copy_false_again(101, 5, 3, 102);
-  ASSERT_TRUE(copy_false_again.IsApplicable(context.get(), fact_manager));
-  copy_false_again.Apply(context.get(), &fact_manager);
-  ASSERT_EQ(3, ids_for_which_synonyms_are_known.size());
-  ASSERT_TRUE(ids_for_which_synonyms_are_known.find(101) !=
-              ids_for_which_synonyms_are_known.end());
-  ASSERT_EQ(1, fact_manager.GetSynonymsForId(101).size());
-  protobufs::DataDescriptor descriptor_102 = MakeDataDescriptor(102, {});
-  ASSERT_TRUE(DataDescriptorEquals()(&descriptor_102,
-                                     &fact_manager.GetSynonymsForId(101)[0]));
+  {
+    TransformationCopyObject copy_false_again(
+        101, MakeInstructionDescriptor(5, SpvOpReturn, 0), 102);
+    ASSERT_TRUE(copy_false_again.IsApplicable(context.get(), fact_manager));
+    copy_false_again.Apply(context.get(), &fact_manager);
+    std::vector<uint32_t> ids_for_which_synonyms_are_known =
+        fact_manager.GetIdsForWhichSynonymsAreKnown(context.get());
+    ASSERT_EQ(5, ids_for_which_synonyms_are_known.size());
+    ASSERT_TRUE(std::find(ids_for_which_synonyms_are_known.begin(),
+                          ids_for_which_synonyms_are_known.end(),
+                          101) != ids_for_which_synonyms_are_known.end());
+    ASSERT_EQ(3, fact_manager.GetSynonymsForId(101, context.get()).size());
+    protobufs::DataDescriptor descriptor_102 = MakeDataDescriptor(102, {});
+    ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(101, {}),
+                                          descriptor_102, context.get()));
+  }
 
-  TransformationCopyObject copy_true_again(7, 102, 1, 103);
-  ASSERT_TRUE(copy_true_again.IsApplicable(context.get(), fact_manager));
-  copy_true_again.Apply(context.get(), &fact_manager);
-  // This does re-uses an id for which synonyms are already known, so the count
-  // of such ids does not change.
-  ASSERT_EQ(3, ids_for_which_synonyms_are_known.size());
-  ASSERT_TRUE(ids_for_which_synonyms_are_known.find(7) !=
-              ids_for_which_synonyms_are_known.end());
-  ASSERT_EQ(2, fact_manager.GetSynonymsForId(7).size());
-  protobufs::DataDescriptor descriptor_103 = MakeDataDescriptor(103, {});
-  ASSERT_TRUE(DataDescriptorEquals()(&descriptor_103,
-                                     &fact_manager.GetSynonymsForId(7)[0]) ||
-              DataDescriptorEquals()(&descriptor_103,
-                                     &fact_manager.GetSynonymsForId(7)[1]));
+  {
+    TransformationCopyObject copy_true_again(
+        7, MakeInstructionDescriptor(102, SpvOpReturn, 0), 103);
+    ASSERT_TRUE(copy_true_again.IsApplicable(context.get(), fact_manager));
+    copy_true_again.Apply(context.get(), &fact_manager);
+    std::vector<uint32_t> ids_for_which_synonyms_are_known =
+        fact_manager.GetIdsForWhichSynonymsAreKnown(context.get());
+    ASSERT_EQ(6, ids_for_which_synonyms_are_known.size());
+    ASSERT_TRUE(std::find(ids_for_which_synonyms_are_known.begin(),
+                          ids_for_which_synonyms_are_known.end(),
+                          7) != ids_for_which_synonyms_are_known.end());
+    ASSERT_EQ(3, fact_manager.GetSynonymsForId(7, context.get()).size());
+    protobufs::DataDescriptor descriptor_103 = MakeDataDescriptor(103, {});
+    ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(7, {}),
+                                          descriptor_103, context.get()));
+  }
 
   std::string after_transformation = R"(
                OpCapability Shader
@@ -318,90 +342,113 @@
   FactManager fact_manager;
 
   // Inapplicable because %18 is decorated.
-  ASSERT_FALSE(TransformationCopyObject(18, 21, 0, 200)
+  ASSERT_FALSE(TransformationCopyObject(
+                   18, MakeInstructionDescriptor(21, SpvOpAccessChain, 0), 200)
                    .IsApplicable(context.get(), fact_manager));
 
   // Inapplicable because %77 is decorated.
-  ASSERT_FALSE(TransformationCopyObject(17, 17, 1, 200)
+  ASSERT_FALSE(TransformationCopyObject(
+                   77, MakeInstructionDescriptor(77, SpvOpBranch, 0), 200)
                    .IsApplicable(context.get(), fact_manager));
 
   // Inapplicable because %80 is decorated.
-  ASSERT_FALSE(TransformationCopyObject(80, 77, 0, 200)
+  ASSERT_FALSE(TransformationCopyObject(
+                   80, MakeInstructionDescriptor(77, SpvOpIAdd, 0), 200)
                    .IsApplicable(context.get(), fact_manager));
 
   // Inapplicable because %84 is not available at the requested point
-  ASSERT_FALSE(TransformationCopyObject(84, 32, 1, 200)
-                   .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationCopyObject(
+          84, MakeInstructionDescriptor(32, SpvOpCompositeExtract, 0), 200)
+          .IsApplicable(context.get(), fact_manager));
 
   // Fine because %84 is available at the requested point
-  ASSERT_TRUE(TransformationCopyObject(84, 32, 2, 200)
-                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(
+      TransformationCopyObject(
+          84, MakeInstructionDescriptor(32, SpvOpCompositeConstruct, 0), 200)
+          .IsApplicable(context.get(), fact_manager));
 
   // Inapplicable because id %9 is already in use
-  ASSERT_FALSE(TransformationCopyObject(84, 32, 2, 9)
-                   .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationCopyObject(
+          84, MakeInstructionDescriptor(32, SpvOpCompositeConstruct, 0), 9)
+          .IsApplicable(context.get(), fact_manager));
 
-  // Inapplicable because the requested point is not in a block
-  ASSERT_FALSE(TransformationCopyObject(84, 86, 3, 200)
+  // Inapplicable because the requested point does not exist
+  ASSERT_FALSE(TransformationCopyObject(
+                   84, MakeInstructionDescriptor(86, SpvOpReturn, 2), 200)
                    .IsApplicable(context.get(), fact_manager));
 
   // Inapplicable because %9 is not in a function
-  ASSERT_FALSE(TransformationCopyObject(9, 9, 1, 200)
-                   .IsApplicable(context.get(), fact_manager));
-
-  // Inapplicable because %9 is not in a function
-  ASSERT_FALSE(TransformationCopyObject(9, 9, 1, 200)
+  ASSERT_FALSE(TransformationCopyObject(
+                   9, MakeInstructionDescriptor(9, SpvOpTypeInt, 0), 200)
                    .IsApplicable(context.get(), fact_manager));
 
   // Inapplicable because the insert point is right before, or inside, a chunk
   // of OpPhis
-  ASSERT_FALSE(TransformationCopyObject(9, 30, 1, 200)
+  ASSERT_FALSE(TransformationCopyObject(
+                   9, MakeInstructionDescriptor(30, SpvOpPhi, 0), 200)
                    .IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(TransformationCopyObject(9, 99, 1, 200)
+  ASSERT_FALSE(TransformationCopyObject(
+                   9, MakeInstructionDescriptor(99, SpvOpPhi, 1), 200)
                    .IsApplicable(context.get(), fact_manager));
 
   // OK, because the insert point is just after a chunk of OpPhis.
-  ASSERT_TRUE(TransformationCopyObject(9, 96, 1, 200)
+  ASSERT_TRUE(TransformationCopyObject(
+                  9, MakeInstructionDescriptor(96, SpvOpAccessChain, 0), 200)
                   .IsApplicable(context.get(), fact_manager));
 
   // Inapplicable because the insert point is right after an OpSelectionMerge
-  ASSERT_FALSE(TransformationCopyObject(9, 58, 2, 200)
-                   .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationCopyObject(
+          9, MakeInstructionDescriptor(58, SpvOpBranchConditional, 0), 200)
+          .IsApplicable(context.get(), fact_manager));
 
   // OK, because the insert point is right before the OpSelectionMerge
-  ASSERT_TRUE(TransformationCopyObject(9, 58, 1, 200)
+  ASSERT_TRUE(TransformationCopyObject(
+                  9, MakeInstructionDescriptor(58, SpvOpSelectionMerge, 0), 200)
                   .IsApplicable(context.get(), fact_manager));
 
   // Inapplicable because the insert point is right after an OpSelectionMerge
-  ASSERT_FALSE(TransformationCopyObject(9, 43, 2, 200)
+  ASSERT_FALSE(TransformationCopyObject(
+                   9, MakeInstructionDescriptor(43, SpvOpSwitch, 0), 200)
                    .IsApplicable(context.get(), fact_manager));
 
   // OK, because the insert point is right before the OpSelectionMerge
-  ASSERT_TRUE(TransformationCopyObject(9, 43, 1, 200)
+  ASSERT_TRUE(TransformationCopyObject(
+                  9, MakeInstructionDescriptor(43, SpvOpSelectionMerge, 0), 200)
                   .IsApplicable(context.get(), fact_manager));
 
   // Inapplicable because the insert point is right after an OpLoopMerge
-  ASSERT_FALSE(TransformationCopyObject(9, 40, 2, 200)
-                   .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationCopyObject(
+          9, MakeInstructionDescriptor(40, SpvOpBranchConditional, 0), 200)
+          .IsApplicable(context.get(), fact_manager));
 
   // OK, because the insert point is right before the OpLoopMerge
-  ASSERT_TRUE(TransformationCopyObject(9, 40, 1, 200)
+  ASSERT_TRUE(TransformationCopyObject(
+                  9, MakeInstructionDescriptor(40, SpvOpLoopMerge, 0), 200)
                   .IsApplicable(context.get(), fact_manager));
 
   // Inapplicable because id %300 does not exist
-  ASSERT_FALSE(TransformationCopyObject(300, 40, 1, 200)
+  ASSERT_FALSE(TransformationCopyObject(
+                   300, MakeInstructionDescriptor(40, SpvOpLoopMerge, 0), 200)
                    .IsApplicable(context.get(), fact_manager));
 
   // Inapplicable because the following instruction is OpVariable
-  ASSERT_FALSE(TransformationCopyObject(9, 180, 0, 200)
+  ASSERT_FALSE(TransformationCopyObject(
+                   9, MakeInstructionDescriptor(180, SpvOpVariable, 0), 200)
                    .IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(TransformationCopyObject(9, 181, 0, 200)
+  ASSERT_FALSE(TransformationCopyObject(
+                   9, MakeInstructionDescriptor(181, SpvOpVariable, 0), 200)
                    .IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(TransformationCopyObject(9, 182, 0, 200)
+  ASSERT_FALSE(TransformationCopyObject(
+                   9, MakeInstructionDescriptor(182, SpvOpVariable, 0), 200)
                    .IsApplicable(context.get(), fact_manager));
 
   // OK, because this is just past the group of OpVariable instructions.
-  ASSERT_TRUE(TransformationCopyObject(9, 182, 1, 200)
+  ASSERT_TRUE(TransformationCopyObject(
+                  9, MakeInstructionDescriptor(182, SpvOpAccessChain, 0), 200)
                   .IsApplicable(context.get(), fact_manager));
 }
 
@@ -470,13 +517,20 @@
   FactManager fact_manager;
 
   std::vector<TransformationCopyObject> transformations = {
-      TransformationCopyObject(19, 22, 1, 100),
-      TransformationCopyObject(22, 22, 1, 101),
-      TransformationCopyObject(12, 22, 1, 102),
-      TransformationCopyObject(11, 22, 1, 103),
-      TransformationCopyObject(16, 22, 1, 104),
-      TransformationCopyObject(8, 22, 1, 105),
-      TransformationCopyObject(17, 22, 1, 106)};
+      TransformationCopyObject(19, MakeInstructionDescriptor(22, SpvOpStore, 0),
+                               100),
+      TransformationCopyObject(
+          22, MakeInstructionDescriptor(22, SpvOpCopyObject, 0), 101),
+      TransformationCopyObject(
+          12, MakeInstructionDescriptor(22, SpvOpCopyObject, 0), 102),
+      TransformationCopyObject(
+          11, MakeInstructionDescriptor(22, SpvOpCopyObject, 0), 103),
+      TransformationCopyObject(
+          16, MakeInstructionDescriptor(22, SpvOpCopyObject, 0), 104),
+      TransformationCopyObject(
+          8, MakeInstructionDescriptor(22, SpvOpCopyObject, 0), 105),
+      TransformationCopyObject(
+          17, MakeInstructionDescriptor(22, SpvOpCopyObject, 0), 106)};
 
   for (auto& transformation : transformations) {
     ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
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 b97d00f..bfc7fa7 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
@@ -16,6 +16,7 @@
 
 #include "source/fuzz/fuzzer_util.h"
 #include "source/fuzz/id_use_descriptor.h"
+#include "source/fuzz/instruction_descriptor.h"
 #include "test/fuzz/fuzz_test_util.h"
 
 namespace spvtools {
@@ -164,12 +165,14 @@
   FactManager fact_manager;
 
   std::vector<protobufs::IdUseDescriptor> uses_of_true = {
-      transformation::MakeIdUseDescriptor(41, SpvOpStore, 1, 44, 12),
-      transformation::MakeIdUseDescriptor(41, SpvOpLogicalOr, 0, 46, 0)};
+      MakeIdUseDescriptor(41, MakeInstructionDescriptor(44, SpvOpStore, 12), 1),
+      MakeIdUseDescriptor(41, MakeInstructionDescriptor(46, SpvOpLogicalOr, 0),
+                          0)};
 
   std::vector<protobufs::IdUseDescriptor> uses_of_false = {
-      transformation::MakeIdUseDescriptor(43, SpvOpStore, 1, 44, 13),
-      transformation::MakeIdUseDescriptor(43, SpvOpLogicalAnd, 1, 48, 0)};
+      MakeIdUseDescriptor(43, MakeInstructionDescriptor(44, SpvOpStore, 13), 1),
+      MakeIdUseDescriptor(43, MakeInstructionDescriptor(48, SpvOpLogicalAnd, 0),
+                          1)};
 
   const uint32_t fresh_id = 100;
 
@@ -529,10 +532,10 @@
 
   FactManager fact_manager;
 
-  auto use_of_true_in_if =
-      transformation::MakeIdUseDescriptor(13, SpvOpBranchConditional, 0, 10, 0);
-  auto use_of_false_in_while =
-      transformation::MakeIdUseDescriptor(21, SpvOpBranchConditional, 0, 16, 0);
+  auto use_of_true_in_if = MakeIdUseDescriptor(
+      13, MakeInstructionDescriptor(10, SpvOpBranchConditional, 0), 0);
+  auto use_of_false_in_while = MakeIdUseDescriptor(
+      21, MakeInstructionDescriptor(16, SpvOpBranchConditional, 0), 0);
 
   auto replacement_1 = TransformationReplaceBooleanConstantWithConstantBinary(
       use_of_true_in_if, 9, 11, SpvOpSLessThan, 100);
@@ -597,6 +600,56 @@
   ASSERT_TRUE(IsEqual(env, after, context.get()));
 }
 
+TEST(TransformationReplaceBooleanConstantWithConstantBinaryTest, OpPhi) {
+  // Hand-written SPIR-V to check applicability of the transformation on an
+  // OpPhi argument.
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool
+          %7 = OpTypePointer Function %6
+          %9 = OpConstantTrue %6
+         %16 = OpConstantFalse %6
+         %10 = OpTypeInt 32 1
+         %11 = OpTypePointer Function %10
+         %13 = OpConstant %10 0
+         %15 = OpConstant %10 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpSelectionMerge %20 None
+               OpBranchConditional %9 %21 %22
+         %21 = OpLabel
+               OpBranch %20
+         %22 = OpLabel
+               OpBranch %20
+         %20 = OpLabel
+         %23 = OpPhi %6 %9 %21 %16 %22
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  auto replacement = TransformationReplaceBooleanConstantWithConstantBinary(
+      MakeIdUseDescriptor(9, MakeInstructionDescriptor(23, SpvOpPhi, 0), 0), 13,
+      15, SpvOpSLessThan, 100);
+
+  ASSERT_FALSE(replacement.IsApplicable(context.get(), fact_manager));
+}
+
 }  // namespace
 }  // namespace fuzz
 }  // namespace spvtools
diff --git a/test/fuzz/transformation_replace_constant_with_uniform_test.cpp b/test/fuzz/transformation_replace_constant_with_uniform_test.cpp
index 06ee025..ac2e3f9 100644
--- a/test/fuzz/transformation_replace_constant_with_uniform_test.cpp
+++ b/test/fuzz/transformation_replace_constant_with_uniform_test.cpp
@@ -13,6 +13,7 @@
 // limitations under the License.
 
 #include "source/fuzz/transformation_replace_constant_with_uniform.h"
+#include "source/fuzz/instruction_descriptor.h"
 #include "source/fuzz/uniform_buffer_element_descriptor.h"
 #include "test/fuzz/fuzz_test_util.h"
 
@@ -116,11 +117,11 @@
 
   // The constant ids are 9, 11 and 14, for 1, 2 and 3 respectively.
   protobufs::IdUseDescriptor use_of_9_in_store =
-      transformation::MakeIdUseDescriptor(9, SpvOpStore, 1, 8, 0);
+      MakeIdUseDescriptor(9, MakeInstructionDescriptor(8, SpvOpStore, 0), 1);
   protobufs::IdUseDescriptor use_of_11_in_add =
-      transformation::MakeIdUseDescriptor(11, SpvOpIAdd, 1, 12, 0);
+      MakeIdUseDescriptor(11, MakeInstructionDescriptor(12, SpvOpIAdd, 0), 1);
   protobufs::IdUseDescriptor use_of_14_in_add =
-      transformation::MakeIdUseDescriptor(14, SpvOpIAdd, 0, 15, 0);
+      MakeIdUseDescriptor(14, MakeInstructionDescriptor(15, SpvOpIAdd, 0), 0);
 
   // These transformations work: they match the facts.
   auto transformation_use_of_9_in_store =
@@ -167,7 +168,7 @@
   // The following transformation does not apply because the id descriptor is
   // not sensible.
   protobufs::IdUseDescriptor nonsense_id_use_descriptor =
-      transformation::MakeIdUseDescriptor(9, SpvOpIAdd, 0, 15, 0);
+      MakeIdUseDescriptor(9, MakeInstructionDescriptor(15, SpvOpIAdd, 0), 0);
   ASSERT_FALSE(TransformationReplaceConstantWithUniform(
                    nonsense_id_use_descriptor, blockname_a, 101, 102)
                    .IsApplicable(context.get(), fact_manager));
@@ -477,13 +478,13 @@
 
   // The constant ids are 13, 15, 17 and 20, for 1, 2, 3 and 4 respectively.
   protobufs::IdUseDescriptor use_of_13_in_store =
-      transformation::MakeIdUseDescriptor(13, SpvOpStore, 1, 21, 0);
+      MakeIdUseDescriptor(13, MakeInstructionDescriptor(21, SpvOpStore, 0), 1);
   protobufs::IdUseDescriptor use_of_15_in_add =
-      transformation::MakeIdUseDescriptor(15, SpvOpIAdd, 1, 16, 0);
+      MakeIdUseDescriptor(15, MakeInstructionDescriptor(16, SpvOpIAdd, 0), 1);
   protobufs::IdUseDescriptor use_of_17_in_add =
-      transformation::MakeIdUseDescriptor(17, SpvOpIAdd, 0, 19, 0);
+      MakeIdUseDescriptor(17, MakeInstructionDescriptor(19, SpvOpIAdd, 0), 0);
   protobufs::IdUseDescriptor use_of_20_in_store =
-      transformation::MakeIdUseDescriptor(20, SpvOpStore, 1, 19, 1);
+      MakeIdUseDescriptor(20, MakeInstructionDescriptor(19, SpvOpStore, 1), 1);
 
   // These transformations work: they match the facts.
   auto transformation_use_of_13_in_store =
@@ -703,7 +704,7 @@
 
   // The constant id is 9 for 0.
   protobufs::IdUseDescriptor use_of_9_in_store =
-      transformation::MakeIdUseDescriptor(9, SpvOpStore, 1, 8, 0);
+      MakeIdUseDescriptor(9, MakeInstructionDescriptor(8, SpvOpStore, 0), 1);
 
   // This transformation is not available because no uniform pointer to integer
   // type is present:
@@ -778,7 +779,7 @@
 
   // The constant id is 9 for 9.
   protobufs::IdUseDescriptor use_of_9_in_store =
-      transformation::MakeIdUseDescriptor(9, SpvOpStore, 1, 8, 0);
+      MakeIdUseDescriptor(9, MakeInstructionDescriptor(8, SpvOpStore, 0), 1);
 
   // This transformation is not available because no constant is present for the
   // index 1 required to index into the uniform buffer:
@@ -852,7 +853,7 @@
 
   // The constant id is 9 for 3.0.
   protobufs::IdUseDescriptor use_of_9_in_store =
-      transformation::MakeIdUseDescriptor(9, SpvOpStore, 1, 8, 0);
+      MakeIdUseDescriptor(9, MakeInstructionDescriptor(8, SpvOpStore, 0), 1);
 
   // This transformation is not available because no integer type is present to
   // allow a constant index to be expressed:
@@ -937,9 +938,9 @@
 
   // The constant ids for 9 and 10 are 9 and 11 respectively
   protobufs::IdUseDescriptor use_of_9_in_store =
-      transformation::MakeIdUseDescriptor(9, SpvOpStore, 1, 10, 0);
+      MakeIdUseDescriptor(9, MakeInstructionDescriptor(10, SpvOpStore, 0), 1);
   protobufs::IdUseDescriptor use_of_11_in_store =
-      transformation::MakeIdUseDescriptor(11, SpvOpStore, 1, 10, 1);
+      MakeIdUseDescriptor(11, MakeInstructionDescriptor(10, SpvOpStore, 1), 1);
 
   // These are right:
   ASSERT_TRUE(TransformationReplaceConstantWithUniform(use_of_9_in_store,
@@ -1220,58 +1221,58 @@
   std::vector<TransformationReplaceConstantWithUniform> transformations;
 
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      transformation::MakeIdUseDescriptor(18, SpvOpStore, 1, 20, 0),
+      MakeIdUseDescriptor(18, MakeInstructionDescriptor(20, SpvOpStore, 0), 1),
       uniform_f_a_4, 200, 201));
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      transformation::MakeIdUseDescriptor(22, SpvOpStore, 1, 23, 0),
+      MakeIdUseDescriptor(22, MakeInstructionDescriptor(23, SpvOpStore, 0), 1),
       uniform_f_a_3, 202, 203));
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      transformation::MakeIdUseDescriptor(25, SpvOpStore, 1, 26, 0),
+      MakeIdUseDescriptor(25, MakeInstructionDescriptor(26, SpvOpStore, 0), 1),
       uniform_f_a_2, 204, 205));
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      transformation::MakeIdUseDescriptor(28, SpvOpStore, 1, 29, 0),
+      MakeIdUseDescriptor(28, MakeInstructionDescriptor(29, SpvOpStore, 0), 1),
       uniform_f_a_1, 206, 207));
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      transformation::MakeIdUseDescriptor(31, SpvOpStore, 1, 32, 0),
+      MakeIdUseDescriptor(31, MakeInstructionDescriptor(32, SpvOpStore, 0), 1),
       uniform_f_a_0, 208, 209));
 
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      transformation::MakeIdUseDescriptor(30, SpvOpStore, 1, 35, 0),
+      MakeIdUseDescriptor(30, MakeInstructionDescriptor(35, SpvOpStore, 0), 1),
       uniform_f_b_w, 210, 211));
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      transformation::MakeIdUseDescriptor(27, SpvOpStore, 1, 37, 0),
+      MakeIdUseDescriptor(27, MakeInstructionDescriptor(37, SpvOpStore, 0), 1),
       uniform_f_b_z, 212, 213));
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      transformation::MakeIdUseDescriptor(24, SpvOpStore, 1, 39, 0),
+      MakeIdUseDescriptor(24, MakeInstructionDescriptor(39, SpvOpStore, 0), 1),
       uniform_f_b_y, 214, 215));
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      transformation::MakeIdUseDescriptor(21, SpvOpStore, 1, 41, 0),
+      MakeIdUseDescriptor(21, MakeInstructionDescriptor(41, SpvOpStore, 0), 1),
       uniform_f_b_x, 216, 217));
 
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      transformation::MakeIdUseDescriptor(44, SpvOpStore, 1, 45, 0),
+      MakeIdUseDescriptor(44, MakeInstructionDescriptor(45, SpvOpStore, 0), 1),
       uniform_f_c_z, 220, 221));
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      transformation::MakeIdUseDescriptor(46, SpvOpStore, 1, 47, 0),
+      MakeIdUseDescriptor(46, MakeInstructionDescriptor(47, SpvOpStore, 0), 1),
       uniform_f_c_y, 222, 223));
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      transformation::MakeIdUseDescriptor(48, SpvOpStore, 1, 49, 0),
+      MakeIdUseDescriptor(48, MakeInstructionDescriptor(49, SpvOpStore, 0), 1),
       uniform_f_c_x, 224, 225));
 
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      transformation::MakeIdUseDescriptor(50, SpvOpStore, 1, 52, 0),
+      MakeIdUseDescriptor(50, MakeInstructionDescriptor(52, SpvOpStore, 0), 1),
       uniform_f_d, 226, 227));
 
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      transformation::MakeIdUseDescriptor(53, SpvOpStore, 1, 54, 0),
+      MakeIdUseDescriptor(53, MakeInstructionDescriptor(54, SpvOpStore, 0), 1),
       uniform_h_x, 228, 229));
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      transformation::MakeIdUseDescriptor(55, SpvOpStore, 1, 56, 0),
+      MakeIdUseDescriptor(55, MakeInstructionDescriptor(56, SpvOpStore, 0), 1),
       uniform_h_y, 230, 231));
 
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      transformation::MakeIdUseDescriptor(42, SpvOpStore, 1, 43, 0), uniform_g,
-      218, 219));
+      MakeIdUseDescriptor(42, MakeInstructionDescriptor(43, SpvOpStore, 0), 1),
+      uniform_g, 218, 219));
 
   for (auto& transformation : transformations) {
     ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
diff --git a/test/fuzz/transformation_replace_id_with_synonym_test.cpp b/test/fuzz/transformation_replace_id_with_synonym_test.cpp
new file mode 100644
index 0000000..75e117a
--- /dev/null
+++ b/test/fuzz/transformation_replace_id_with_synonym_test.cpp
@@ -0,0 +1,1213 @@
+// 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.
+
+#include "source/fuzz/transformation_replace_id_with_synonym.h"
+#include "source/fuzz/data_descriptor.h"
+#include "source/fuzz/id_use_descriptor.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+// The following shader was obtained from this GLSL, which was then optimized
+// with spirv-opt -O and manually edited to include some uses of OpCopyObject
+// (to introduce id synonyms).
+//
+// #version 310 es
+//
+// precision highp int;
+// precision highp float;
+//
+// layout(set = 0, binding = 0) uniform buf {
+//   int a;
+//   int b;
+//   int c;
+// };
+//
+// layout(location = 0) out vec4 color;
+//
+// void main() {
+//   int x = a;
+//   float f = 0.0;
+//   while (x < b) {
+//     switch(x % 4) {
+//       case 0:
+//         color[0] = f;
+//         break;
+//       case 1:
+//         color[1] = f;
+//         break;
+//       case 2:
+//         color[2] = f;
+//         break;
+//       case 3:
+//         color[3] = f;
+//         break;
+//       default:
+//         break;
+//     }
+//     if (x > c) {
+//       x++;
+//     } else {
+//       x += 2;
+//     }
+//   }
+//   color[0] += color[1] + float(x);
+// }
+const std::string kComplexShader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %42
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %9 "buf"
+               OpMemberName %9 0 "a"
+               OpMemberName %9 1 "b"
+               OpMemberName %9 2 "c"
+               OpName %11 ""
+               OpName %42 "color"
+               OpMemberDecorate %9 0 Offset 0
+               OpMemberDecorate %9 1 Offset 4
+               OpMemberDecorate %9 2 Offset 8
+               OpDecorate %9 Block
+               OpDecorate %11 DescriptorSet 0
+               OpDecorate %11 Binding 0
+               OpDecorate %42 Location 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %9 = OpTypeStruct %6 %6 %6
+         %10 = OpTypePointer Uniform %9
+         %11 = OpVariable %10 Uniform
+         %12 = OpConstant %6 0
+         %13 = OpTypePointer Uniform %6
+         %16 = OpTypeFloat 32
+         %19 = OpConstant %16 0
+         %26 = OpConstant %6 1
+         %29 = OpTypeBool
+         %32 = OpConstant %6 4
+         %40 = OpTypeVector %16 4
+         %41 = OpTypePointer Output %40
+         %42 = OpVariable %41 Output
+         %44 = OpTypeInt 32 0
+         %45 = OpConstant %44 0
+         %46 = OpTypePointer Output %16
+         %50 = OpConstant %44 1
+         %54 = OpConstant %44 2
+         %58 = OpConstant %44 3
+         %64 = OpConstant %6 2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+        %209 = OpCopyObject %6 %12
+         %14 = OpAccessChain %13 %11 %12
+         %15 = OpLoad %6 %14
+        %200 = OpCopyObject %6 %15
+               OpBranch %20
+         %20 = OpLabel
+         %84 = OpPhi %6 %15 %5 %86 %69
+         %27 = OpAccessChain %13 %11 %26
+         %28 = OpLoad %6 %27
+        %207 = OpCopyObject %6 %84
+        %201 = OpCopyObject %6 %15
+         %30 = OpSLessThan %29 %84 %28
+               OpLoopMerge %22 %69 None
+               OpBranchConditional %30 %21 %22
+         %21 = OpLabel
+         %33 = OpSMod %6 %84 %32
+        %208 = OpCopyObject %6 %33
+               OpSelectionMerge %39 None
+               OpSwitch %33 %38 0 %34 1 %35 2 %36 3 %37
+         %38 = OpLabel
+        %202 = OpCopyObject %6 %15
+               OpBranch %39
+         %34 = OpLabel
+        %210 = OpCopyObject %16 %19
+         %47 = OpAccessChain %46 %42 %45
+               OpStore %47 %19
+               OpBranch %39
+         %35 = OpLabel
+         %51 = OpAccessChain %46 %42 %50
+               OpStore %51 %19
+               OpBranch %39
+         %36 = OpLabel
+        %204 = OpCopyObject %44 %54
+         %55 = OpAccessChain %46 %42 %54
+        %203 = OpCopyObject %46 %55
+               OpStore %55 %19
+               OpBranch %39
+         %37 = OpLabel
+         %59 = OpAccessChain %46 %42 %58
+               OpStore %59 %19
+               OpBranch %39
+         %39 = OpLabel
+        %300 = OpIAdd %6 %15 %15
+         %65 = OpAccessChain %13 %11 %64
+         %66 = OpLoad %6 %65
+         %67 = OpSGreaterThan %29 %84 %66
+               OpSelectionMerge %69 None
+               OpBranchConditional %67 %68 %72
+         %68 = OpLabel
+         %71 = OpIAdd %6 %84 %26
+               OpBranch %69
+         %72 = OpLabel
+         %74 = OpIAdd %6 %84 %64
+        %205 = OpCopyObject %6 %74
+               OpBranch %69
+         %69 = OpLabel
+         %86 = OpPhi %6 %71 %68 %74 %72
+        %301 = OpPhi %6 %71 %68 %15 %72
+               OpBranch %20
+         %22 = OpLabel
+         %75 = OpAccessChain %46 %42 %50
+         %76 = OpLoad %16 %75
+         %78 = OpConvertSToF %16 %84
+         %80 = OpAccessChain %46 %42 %45
+        %206 = OpCopyObject %16 %78
+         %81 = OpLoad %16 %80
+         %79 = OpFAdd %16 %76 %78
+         %82 = OpFAdd %16 %81 %79
+               OpStore %80 %82
+               OpReturn
+               OpFunctionEnd
+)";
+
+protobufs::Fact MakeSynonymFact(uint32_t first, uint32_t second) {
+  protobufs::FactDataSynonym data_synonym_fact;
+  *data_synonym_fact.mutable_data1() = MakeDataDescriptor(first, {});
+  *data_synonym_fact.mutable_data2() = MakeDataDescriptor(second, {});
+  protobufs::Fact result;
+  *result.mutable_data_synonym_fact() = data_synonym_fact;
+  return result;
+}
+
+// Equips the fact manager with synonym facts for the above shader.
+void SetUpIdSynonyms(FactManager* fact_manager, opt::IRContext* context) {
+  fact_manager->AddFact(MakeSynonymFact(15, 200), context);
+  fact_manager->AddFact(MakeSynonymFact(15, 201), context);
+  fact_manager->AddFact(MakeSynonymFact(15, 202), context);
+  fact_manager->AddFact(MakeSynonymFact(55, 203), context);
+  fact_manager->AddFact(MakeSynonymFact(54, 204), context);
+  fact_manager->AddFact(MakeSynonymFact(74, 205), context);
+  fact_manager->AddFact(MakeSynonymFact(78, 206), context);
+  fact_manager->AddFact(MakeSynonymFact(84, 207), context);
+  fact_manager->AddFact(MakeSynonymFact(33, 208), context);
+  fact_manager->AddFact(MakeSynonymFact(12, 209), context);
+  fact_manager->AddFact(MakeSynonymFact(19, 210), context);
+}
+
+TEST(TransformationReplaceIdWithSynonymTest, IllegalTransformations) {
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context =
+      BuildModule(env, consumer, kComplexShader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  SetUpIdSynonyms(&fact_manager, context.get());
+
+  // %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),
+      202);
+  ASSERT_FALSE(
+      synonym_does_not_dominate_use.IsApplicable(context.get(), fact_manager));
+
+  // %202 cannot replace %15 as in-operand 2 of %301, since this is the OpPhi's
+  // 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),
+          202);
+  ASSERT_FALSE(synonym_does_not_dominate_use_op_phi.IsApplicable(context.get(),
+                                                                 fact_manager));
+
+  // %200 is not a synonym for %84
+  auto id_in_use_is_not_synonymous = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(
+          84, MakeInstructionDescriptor(67, SpvOpSGreaterThan, 0), 0),
+      200);
+  ASSERT_FALSE(
+      id_in_use_is_not_synonymous.IsApplicable(context.get(), fact_manager));
+
+  // %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),
+      74);
+  ASSERT_FALSE(id_has_no_synonyms.IsApplicable(context.get(), fact_manager));
+
+  // This would lead to %207 = 'OpCopyObject %type %207' if it were allowed
+  auto synonym_use_is_in_synonym_definition =
+      TransformationReplaceIdWithSynonym(
+          MakeIdUseDescriptor(
+              84, MakeInstructionDescriptor(207, SpvOpCopyObject, 0), 0),
+          207);
+  ASSERT_FALSE(synonym_use_is_in_synonym_definition.IsApplicable(context.get(),
+                                                                 fact_manager));
+
+  // The id use descriptor does not lead to a use (%84 is not used in the
+  // definition of %207)
+  auto bad_id_use_descriptor = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(
+          84, MakeInstructionDescriptor(200, SpvOpCopyObject, 0), 0),
+      207);
+  ASSERT_FALSE(bad_id_use_descriptor.IsApplicable(context.get(), fact_manager));
+
+  // This replacement would lead to an access chain into a struct using a
+  // non-constant index.
+  auto bad_access_chain = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(
+          12, MakeInstructionDescriptor(14, SpvOpAccessChain, 0), 1),
+      209);
+  ASSERT_FALSE(bad_access_chain.IsApplicable(context.get(), fact_manager));
+}
+
+TEST(TransformationReplaceIdWithSynonymTest, LegalTransformations) {
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context =
+      BuildModule(env, consumer, kComplexShader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  SetUpIdSynonyms(&fact_manager, context.get());
+
+  auto global_constant_synonym = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(19, MakeInstructionDescriptor(47, SpvOpStore, 0), 1),
+      210);
+  ASSERT_TRUE(
+      global_constant_synonym.IsApplicable(context.get(), fact_manager));
+  global_constant_synonym.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  auto replace_vector_access_chain_index = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(
+          54, MakeInstructionDescriptor(55, SpvOpAccessChain, 0), 1),
+      204);
+  ASSERT_TRUE(replace_vector_access_chain_index.IsApplicable(context.get(),
+                                                             fact_manager));
+  replace_vector_access_chain_index.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // This is an interesting case because it replaces something that is being
+  // copied with something that is already a synonym.
+  auto regular_replacement = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(
+          15, MakeInstructionDescriptor(202, SpvOpCopyObject, 0), 0),
+      201);
+  ASSERT_TRUE(regular_replacement.IsApplicable(context.get(), fact_manager));
+  regular_replacement.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  auto regular_replacement2 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(55, MakeInstructionDescriptor(203, SpvOpStore, 0), 0),
+      203);
+  ASSERT_TRUE(regular_replacement2.IsApplicable(context.get(), fact_manager));
+  regular_replacement2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  auto good_op_phi = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(74, MakeInstructionDescriptor(86, SpvOpPhi, 0), 2),
+      205);
+  ASSERT_TRUE(good_op_phi.IsApplicable(context.get(), fact_manager));
+  good_op_phi.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  const std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %42
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %9 "buf"
+               OpMemberName %9 0 "a"
+               OpMemberName %9 1 "b"
+               OpMemberName %9 2 "c"
+               OpName %11 ""
+               OpName %42 "color"
+               OpMemberDecorate %9 0 Offset 0
+               OpMemberDecorate %9 1 Offset 4
+               OpMemberDecorate %9 2 Offset 8
+               OpDecorate %9 Block
+               OpDecorate %11 DescriptorSet 0
+               OpDecorate %11 Binding 0
+               OpDecorate %42 Location 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %9 = OpTypeStruct %6 %6 %6
+         %10 = OpTypePointer Uniform %9
+         %11 = OpVariable %10 Uniform
+         %12 = OpConstant %6 0
+         %13 = OpTypePointer Uniform %6
+         %16 = OpTypeFloat 32
+         %19 = OpConstant %16 0
+         %26 = OpConstant %6 1
+         %29 = OpTypeBool
+         %32 = OpConstant %6 4
+         %40 = OpTypeVector %16 4
+         %41 = OpTypePointer Output %40
+         %42 = OpVariable %41 Output
+         %44 = OpTypeInt 32 0
+         %45 = OpConstant %44 0
+         %46 = OpTypePointer Output %16
+         %50 = OpConstant %44 1
+         %54 = OpConstant %44 2
+         %58 = OpConstant %44 3
+         %64 = OpConstant %6 2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+        %209 = OpCopyObject %6 %12
+         %14 = OpAccessChain %13 %11 %12
+         %15 = OpLoad %6 %14
+        %200 = OpCopyObject %6 %15
+               OpBranch %20
+         %20 = OpLabel
+         %84 = OpPhi %6 %15 %5 %86 %69
+         %27 = OpAccessChain %13 %11 %26
+         %28 = OpLoad %6 %27
+        %207 = OpCopyObject %6 %84
+        %201 = OpCopyObject %6 %15
+         %30 = OpSLessThan %29 %84 %28
+               OpLoopMerge %22 %69 None
+               OpBranchConditional %30 %21 %22
+         %21 = OpLabel
+         %33 = OpSMod %6 %84 %32
+        %208 = OpCopyObject %6 %33
+               OpSelectionMerge %39 None
+               OpSwitch %33 %38 0 %34 1 %35 2 %36 3 %37
+         %38 = OpLabel
+        %202 = OpCopyObject %6 %201
+               OpBranch %39
+         %34 = OpLabel
+        %210 = OpCopyObject %16 %19
+         %47 = OpAccessChain %46 %42 %45
+               OpStore %47 %210
+               OpBranch %39
+         %35 = OpLabel
+         %51 = OpAccessChain %46 %42 %50
+               OpStore %51 %19
+               OpBranch %39
+         %36 = OpLabel
+        %204 = OpCopyObject %44 %54
+         %55 = OpAccessChain %46 %42 %204
+        %203 = OpCopyObject %46 %55
+               OpStore %203 %19
+               OpBranch %39
+         %37 = OpLabel
+         %59 = OpAccessChain %46 %42 %58
+               OpStore %59 %19
+               OpBranch %39
+         %39 = OpLabel
+        %300 = OpIAdd %6 %15 %15
+         %65 = OpAccessChain %13 %11 %64
+         %66 = OpLoad %6 %65
+         %67 = OpSGreaterThan %29 %84 %66
+               OpSelectionMerge %69 None
+               OpBranchConditional %67 %68 %72
+         %68 = OpLabel
+         %71 = OpIAdd %6 %84 %26
+               OpBranch %69
+         %72 = OpLabel
+         %74 = OpIAdd %6 %84 %64
+        %205 = OpCopyObject %6 %74
+               OpBranch %69
+         %69 = OpLabel
+         %86 = OpPhi %6 %71 %68 %205 %72
+        %301 = OpPhi %6 %71 %68 %15 %72
+               OpBranch %20
+         %22 = OpLabel
+         %75 = OpAccessChain %46 %42 %50
+         %76 = OpLoad %16 %75
+         %78 = OpConvertSToF %16 %84
+         %80 = OpAccessChain %46 %42 %45
+        %206 = OpCopyObject %16 %78
+         %81 = OpLoad %16 %80
+         %79 = OpFAdd %16 %76 %78
+         %82 = OpFAdd %16 %81 %79
+               OpStore %80 %82
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfVariables) {
+  // The following SPIR-V comes from this GLSL, with object copies added:
+  //
+  // #version 310 es
+  //
+  // precision highp int;
+  //
+  // int g;
+  //
+  // void main() {
+  //   int l;
+  //   l = g;
+  //   g = l;
+  // }
+  const std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %8 "l"
+               OpName %10 "g"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpTypePointer Private %6
+         %10 = OpVariable %9 Private
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+        %100 = OpCopyObject %9 %10
+        %101 = OpCopyObject %7 %8
+         %11 = OpLoad %6 %10
+               OpStore %8 %11
+         %12 = OpLoad %6 %8
+               OpStore %10 %12
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  fact_manager.AddFact(MakeSynonymFact(10, 100), context.get());
+  fact_manager.AddFact(MakeSynonymFact(8, 101), context.get());
+
+  // Replace %10 with %100 in:
+  // %11 = OpLoad %6 %10
+  auto replacement1 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(10, MakeInstructionDescriptor(11, SpvOpLoad, 0), 0),
+      100);
+  ASSERT_TRUE(replacement1.IsApplicable(context.get(), fact_manager));
+  replacement1.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Replace %8 with %101 in:
+  // OpStore %8 %11
+  auto replacement2 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(8, MakeInstructionDescriptor(11, SpvOpStore, 0), 0),
+      101);
+  ASSERT_TRUE(replacement2.IsApplicable(context.get(), fact_manager));
+  replacement2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Replace %8 with %101 in:
+  // %12 = OpLoad %6 %8
+  auto replacement3 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(8, MakeInstructionDescriptor(12, SpvOpLoad, 0), 0),
+      101);
+  ASSERT_TRUE(replacement3.IsApplicable(context.get(), fact_manager));
+  replacement3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Replace %10 with %100 in:
+  // OpStore %10 %12
+  auto replacement4 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(10, MakeInstructionDescriptor(12, SpvOpStore, 0), 0),
+      100);
+  ASSERT_TRUE(replacement4.IsApplicable(context.get(), fact_manager));
+  replacement4.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  const std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %8 "l"
+               OpName %10 "g"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpTypePointer Private %6
+         %10 = OpVariable %9 Private
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+        %100 = OpCopyObject %9 %10
+        %101 = OpCopyObject %7 %8
+         %11 = OpLoad %6 %100
+               OpStore %101 %11
+         %12 = OpLoad %6 %101
+               OpStore %100 %12
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationReplaceIdWithSynonymTest,
+     SynonymOfVariableNoGoodInFunctionCall) {
+  // The following SPIR-V comes from this GLSL, with an object copy added:
+  //
+  // #version 310 es
+  //
+  // precision highp int;
+  //
+  // void foo(int x) { }
+  //
+  // void main() {
+  //   int a;
+  //   a = 2;
+  //   foo(a);
+  // }
+  const std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %10 "foo(i1;"
+               OpName %9 "x"
+               OpName %12 "a"
+               OpName %14 "param"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %8 = OpTypeFunction %2 %7
+         %13 = OpConstant %6 2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %12 = OpVariable %7 Function
+         %14 = OpVariable %7 Function
+               OpStore %12 %13
+         %15 = OpLoad %6 %12
+               OpStore %14 %15
+        %100 = OpCopyObject %7 %14
+         %16 = OpFunctionCall %2 %10 %14
+               OpReturn
+               OpFunctionEnd
+         %10 = OpFunction %2 None %8
+          %9 = OpFunctionParameter %7
+         %11 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  fact_manager.AddFact(MakeSynonymFact(14, 100), context.get());
+
+  // Replace %14 with %100 in:
+  // %16 = OpFunctionCall %2 %10 %14
+  auto replacement = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(
+          14, MakeInstructionDescriptor(16, SpvOpFunctionCall, 0), 1),
+      100);
+  ASSERT_FALSE(replacement.IsApplicable(context.get(), fact_manager));
+}
+
+TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) {
+  // The following SPIR-V comes from this GLSL, with object copies added:
+  //
+  // #version 310 es
+  //
+  // precision highp float;
+  // precision highp int;
+  //
+  // struct S {
+  //   int[3] a;
+  //   vec4 b;
+  //   bool c;
+  // } d;
+  //
+  // float[20] e;
+  //
+  // struct T {
+  //   float f;
+  //   S g;
+  // } h;
+  //
+  // T[4] i;
+  //
+  // void main() {
+  //   d.a[2] = 10;
+  //   d.b[3] = 11.0;
+  //   d.c = false;
+  //   e[17] = 12.0;
+  //   h.f = 13.0;
+  //   h.g.a[1] = 14;
+  //   h.g.b[0] = 15.0;
+  //   h.g.c = true;
+  //   i[0].f = 16.0;
+  //   i[1].g.a[0] = 17;
+  //   i[2].g.b[1] = 18.0;
+  //   i[3].g.c = true;
+  // }
+  const std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %13 "S"
+               OpMemberName %13 0 "a"
+               OpMemberName %13 1 "b"
+               OpMemberName %13 2 "c"
+               OpName %15 "d"
+               OpName %31 "e"
+               OpName %35 "T"
+               OpMemberName %35 0 "f"
+               OpMemberName %35 1 "g"
+               OpName %37 "h"
+               OpName %50 "i"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypeInt 32 0
+          %8 = OpConstant %7 3
+          %9 = OpTypeArray %6 %8
+         %10 = OpTypeFloat 32
+         %11 = OpTypeVector %10 4
+         %12 = OpTypeBool
+         %13 = OpTypeStruct %9 %11 %12
+         %14 = OpTypePointer Private %13
+         %15 = OpVariable %14 Private
+         %16 = OpConstant %6 0
+         %17 = OpConstant %6 2
+         %18 = OpConstant %6 10
+         %19 = OpTypePointer Private %6
+         %21 = OpConstant %6 1
+         %22 = OpConstant %10 11
+         %23 = OpTypePointer Private %10
+         %25 = OpConstantFalse %12
+         %26 = OpTypePointer Private %12
+         %28 = OpConstant %7 20
+         %29 = OpTypeArray %10 %28
+         %30 = OpTypePointer Private %29
+         %31 = OpVariable %30 Private
+         %32 = OpConstant %6 17
+         %33 = OpConstant %10 12
+         %35 = OpTypeStruct %10 %13
+         %36 = OpTypePointer Private %35
+         %37 = OpVariable %36 Private
+         %38 = OpConstant %10 13
+         %40 = OpConstant %6 14
+         %42 = OpConstant %10 15
+         %43 = OpConstant %7 0
+         %45 = OpConstantTrue %12
+         %47 = OpConstant %7 4
+         %48 = OpTypeArray %35 %47
+         %49 = OpTypePointer Private %48
+         %50 = OpVariable %49 Private
+         %51 = OpConstant %10 16
+         %54 = OpConstant %10 18
+         %55 = OpConstant %7 1
+         %57 = OpConstant %6 3
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+
+         %100 = OpCopyObject %6 %16 ; 0
+         %101 = OpCopyObject %6 %21 ; 1
+         %102 = OpCopyObject %6 %17 ; 2
+         %103 = OpCopyObject %6 %57 ; 3
+         %104 = OpCopyObject %6 %18 ; 10
+         %105 = OpCopyObject %6 %40 ; 14
+         %106 = OpCopyObject %6 %32 ; 17
+         %107 = OpCopyObject %7 %43 ; 0
+         %108 = OpCopyObject %7 %55 ; 1
+         %109 = OpCopyObject %7  %8 ; 3
+         %110 = OpCopyObject %7 %47 ; 4
+         %111 = OpCopyObject %7 %28 ; 20
+         %112 = OpCopyObject %12 %45 ; true
+
+         %20 = OpAccessChain %19 %15 %16 %17
+               OpStore %20 %18
+         %24 = OpAccessChain %23 %15 %21 %8
+               OpStore %24 %22
+         %27 = OpAccessChain %26 %15 %17
+               OpStore %27 %25
+         %34 = OpAccessChain %23 %31 %32
+               OpStore %34 %33
+         %39 = OpAccessChain %23 %37 %16
+               OpStore %39 %38
+         %41 = OpAccessChain %19 %37 %21 %16 %21
+               OpStore %41 %40
+         %44 = OpAccessChain %23 %37 %21 %21 %43
+               OpStore %44 %42
+         %46 = OpAccessChain %26 %37 %21 %17
+               OpStore %46 %45
+         %52 = OpAccessChain %23 %50 %16 %16
+               OpStore %52 %51
+         %53 = OpAccessChain %19 %50 %21 %21 %16 %16
+               OpStore %53 %32
+         %56 = OpAccessChain %23 %50 %17 %21 %21 %55
+               OpStore %56 %54
+         %58 = OpAccessChain %26 %50 %57 %21 %17
+               OpStore %58 %45
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  // Add synonym facts corresponding to the OpCopyObject operations that have
+  // been applied to all constants in the module.
+  fact_manager.AddFact(MakeSynonymFact(16, 100), context.get());
+  fact_manager.AddFact(MakeSynonymFact(21, 101), context.get());
+  fact_manager.AddFact(MakeSynonymFact(17, 102), context.get());
+  fact_manager.AddFact(MakeSynonymFact(57, 103), context.get());
+  fact_manager.AddFact(MakeSynonymFact(18, 104), context.get());
+  fact_manager.AddFact(MakeSynonymFact(40, 105), context.get());
+  fact_manager.AddFact(MakeSynonymFact(32, 106), context.get());
+  fact_manager.AddFact(MakeSynonymFact(43, 107), context.get());
+  fact_manager.AddFact(MakeSynonymFact(55, 108), context.get());
+  fact_manager.AddFact(MakeSynonymFact(8, 109), context.get());
+  fact_manager.AddFact(MakeSynonymFact(47, 110), context.get());
+  fact_manager.AddFact(MakeSynonymFact(28, 111), context.get());
+  fact_manager.AddFact(MakeSynonymFact(45, 112), context.get());
+
+  // Replacements of the form %16 -> %100
+
+  // %20 = OpAccessChain %19 %15 *%16* %17
+  // Corresponds to d.*a*[2]
+  // The index %16 used for a cannot be replaced
+  auto replacement1 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(
+          16, MakeInstructionDescriptor(20, SpvOpAccessChain, 0), 1),
+      100);
+  ASSERT_FALSE(replacement1.IsApplicable(context.get(), fact_manager));
+
+  // %39 = OpAccessChain %23 %37 *%16*
+  // Corresponds to h.*f*
+  // The index %16 used for f cannot be replaced
+  auto replacement2 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(
+          16, MakeInstructionDescriptor(39, SpvOpAccessChain, 0), 1),
+      100);
+  ASSERT_FALSE(replacement2.IsApplicable(context.get(), fact_manager));
+
+  // %41 = OpAccessChain %19 %37 %21 *%16* %21
+  // Corresponds to h.g.*a*[1]
+  // The index %16 used for a cannot be replaced
+  auto replacement3 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(
+          16, MakeInstructionDescriptor(41, SpvOpAccessChain, 0), 2),
+      100);
+  ASSERT_FALSE(replacement3.IsApplicable(context.get(), fact_manager));
+
+  // %52 = OpAccessChain %23 %50 *%16* %16
+  // Corresponds to i[*0*].f
+  // The index %16 used for 0 *can* be replaced
+  auto replacement4 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(
+          16, MakeInstructionDescriptor(52, SpvOpAccessChain, 0), 1),
+      100);
+  ASSERT_TRUE(replacement4.IsApplicable(context.get(), fact_manager));
+  replacement4.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // %52 = OpAccessChain %23 %50 %16 *%16*
+  // Corresponds to i[0].*f*
+  // The index %16 used for f cannot be replaced
+  auto replacement5 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(
+          16, MakeInstructionDescriptor(52, SpvOpAccessChain, 0), 2),
+      100);
+  ASSERT_FALSE(replacement5.IsApplicable(context.get(), fact_manager));
+
+  // %53 = OpAccessChain %19 %50 %21 %21 *%16* %16
+  // Corresponds to i[1].g.*a*[0]
+  // The index %16 used for a cannot be replaced
+  auto replacement6 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(
+          16, MakeInstructionDescriptor(53, SpvOpAccessChain, 0), 3),
+      100);
+  ASSERT_FALSE(replacement6.IsApplicable(context.get(), fact_manager));
+
+  // %53 = OpAccessChain %19 %50 %21 %21 %16 *%16*
+  // Corresponds to i[1].g.a[*0*]
+  // The index %16 used for 0 *can* be replaced
+  auto replacement7 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(
+          16, MakeInstructionDescriptor(53, SpvOpAccessChain, 0), 4),
+      100);
+  ASSERT_TRUE(replacement7.IsApplicable(context.get(), fact_manager));
+  replacement7.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Replacements of the form %21 -> %101
+
+  // %24 = OpAccessChain %23 %15 *%21* %8
+  // Corresponds to d.*b*[3]
+  // The index %24 used for b cannot be replaced
+  auto replacement8 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(
+          21, MakeInstructionDescriptor(24, SpvOpAccessChain, 0), 1),
+      101);
+  ASSERT_FALSE(replacement8.IsApplicable(context.get(), fact_manager));
+
+  // %41 = OpAccessChain %19 %37 *%21* %16 %21
+  // Corresponds to h.*g*.a[1]
+  // The index %24 used for g cannot be replaced
+  auto replacement9 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(
+          21, MakeInstructionDescriptor(41, SpvOpAccessChain, 0), 1),
+      101);
+  ASSERT_FALSE(replacement9.IsApplicable(context.get(), fact_manager));
+
+  // %41 = OpAccessChain %19 %37 %21 %16 *%21*
+  // Corresponds to h.g.a[*1*]
+  // The index %24 used for 1 *can* be replaced
+  auto replacement10 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(
+          21, MakeInstructionDescriptor(41, SpvOpAccessChain, 0), 3),
+      101);
+  ASSERT_TRUE(replacement10.IsApplicable(context.get(), fact_manager));
+  replacement10.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // %44 = OpAccessChain %23 %37 *%21* %21 %43
+  // Corresponds to h.*g*.b[0]
+  // The index %24 used for g cannot be replaced
+  auto replacement11 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(
+          21, MakeInstructionDescriptor(44, SpvOpAccessChain, 0), 1),
+      101);
+  ASSERT_FALSE(replacement11.IsApplicable(context.get(), fact_manager));
+
+  // %44 = OpAccessChain %23 %37 %21 *%21* %43
+  // Corresponds to h.g.*b*[0]
+  // The index %24 used for b cannot be replaced
+  auto replacement12 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(
+          21, MakeInstructionDescriptor(44, SpvOpAccessChain, 0), 2),
+      101);
+  ASSERT_FALSE(replacement12.IsApplicable(context.get(), fact_manager));
+
+  // %46 = OpAccessChain %26 %37 *%21* %17
+  // Corresponds to h.*g*.c
+  // The index %24 used for g cannot be replaced
+  auto replacement13 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(
+          21, MakeInstructionDescriptor(46, SpvOpAccessChain, 0), 1),
+      101);
+  ASSERT_FALSE(replacement13.IsApplicable(context.get(), fact_manager));
+
+  // %53 = OpAccessChain %19 %50 *%21* %21 %16 %16
+  // Corresponds to i[*1*].g.a[0]
+  // The index %24 used for 1 *can* be replaced
+  auto replacement14 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(
+          21, MakeInstructionDescriptor(53, SpvOpAccessChain, 0), 1),
+      101);
+  ASSERT_TRUE(replacement14.IsApplicable(context.get(), fact_manager));
+  replacement14.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // %53 = OpAccessChain %19 %50 %21 *%21* %16 %16
+  // Corresponds to i[1].*g*.a[0]
+  // The index %24 used for g cannot be replaced
+  auto replacement15 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(
+          21, MakeInstructionDescriptor(53, SpvOpAccessChain, 0), 2),
+      101);
+  ASSERT_FALSE(replacement15.IsApplicable(context.get(), fact_manager));
+
+  // %56 = OpAccessChain %23 %50 %17 *%21* %21 %55
+  // Corresponds to i[2].*g*.b[1]
+  // The index %24 used for g cannot be replaced
+  auto replacement16 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(
+          21, MakeInstructionDescriptor(56, SpvOpAccessChain, 0), 2),
+      101);
+  ASSERT_FALSE(replacement16.IsApplicable(context.get(), fact_manager));
+
+  // %56 = OpAccessChain %23 %50 %17 %21 *%21* %55
+  // Corresponds to i[2].g.*b*[1]
+  // The index %24 used for b cannot be replaced
+  auto replacement17 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(
+          21, MakeInstructionDescriptor(56, SpvOpAccessChain, 0), 3),
+      101);
+  ASSERT_FALSE(replacement17.IsApplicable(context.get(), fact_manager));
+
+  // %58 = OpAccessChain %26 %50 %57 *%21* %17
+  // Corresponds to i[3].*g*.c
+  // The index %24 used for g cannot be replaced
+  auto replacement18 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(
+          21, MakeInstructionDescriptor(58, SpvOpAccessChain, 0), 2),
+      101);
+  ASSERT_FALSE(replacement18.IsApplicable(context.get(), fact_manager));
+
+  // Replacements of the form %17 -> %102
+
+  // %20 = OpAccessChain %19 %15 %16 %17
+  // Corresponds to d.a[*2*]
+  // The index %17 used for 2 *can* be replaced
+  auto replacement19 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(
+          17, MakeInstructionDescriptor(20, SpvOpAccessChain, 0), 2),
+      102);
+  ASSERT_TRUE(replacement19.IsApplicable(context.get(), fact_manager));
+  replacement19.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // %27 = OpAccessChain %26 %15 %17
+  // Corresponds to d.c
+  // The index %17 used for c cannot be replaced
+  auto replacement20 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(
+          17, MakeInstructionDescriptor(27, SpvOpAccessChain, 0), 1),
+      102);
+  ASSERT_FALSE(replacement20.IsApplicable(context.get(), fact_manager));
+
+  // %46 = OpAccessChain %26 %37 %21 %17
+  // Corresponds to h.g.*c*
+  // The index %17 used for c cannot be replaced
+  auto replacement21 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(
+          17, MakeInstructionDescriptor(46, SpvOpAccessChain, 0), 2),
+      102);
+  ASSERT_FALSE(replacement21.IsApplicable(context.get(), fact_manager));
+
+  // %56 = OpAccessChain %23 %50 %17 %21 %21 %55
+  // Corresponds to i[*2*].g.b[1]
+  // The index %17 used for 2 *can* be replaced
+  auto replacement22 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(
+          17, MakeInstructionDescriptor(56, SpvOpAccessChain, 0), 1),
+      102);
+  ASSERT_TRUE(replacement22.IsApplicable(context.get(), fact_manager));
+  replacement22.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // %58 = OpAccessChain %26 %50 %57 %21 %17
+  // Corresponds to i[3].g.*c*
+  // The index %17 used for c cannot be replaced
+  auto replacement23 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(
+          17, MakeInstructionDescriptor(58, SpvOpAccessChain, 0), 3),
+      102);
+  ASSERT_FALSE(replacement23.IsApplicable(context.get(), fact_manager));
+
+  // Replacements of the form %57 -> %103
+
+  // %58 = OpAccessChain %26 %50 *%57* %21 %17
+  // Corresponds to i[*3*].g.c
+  // The index %57 used for 3 *can* be replaced
+  auto replacement24 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(
+          57, MakeInstructionDescriptor(58, SpvOpAccessChain, 0), 1),
+      103);
+  ASSERT_TRUE(replacement24.IsApplicable(context.get(), fact_manager));
+  replacement24.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Replacements of the form %32 -> %106
+
+  // %34 = OpAccessChain %23 %31 *%32*
+  // Corresponds to e[*17*]
+  // The index %32 used for 17 *can* be replaced
+  auto replacement25 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(
+          32, MakeInstructionDescriptor(34, SpvOpAccessChain, 0), 1),
+      106);
+  ASSERT_TRUE(replacement25.IsApplicable(context.get(), fact_manager));
+  replacement25.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Replacements of the form %43 -> %107
+
+  // %44 = OpAccessChain %23 %37 %21 %21 *%43*
+  // Corresponds to h.g.b[*0*]
+  // The index %43 used for 0 *can* be replaced
+  auto replacement26 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(
+          43, MakeInstructionDescriptor(44, SpvOpAccessChain, 0), 3),
+      107);
+  ASSERT_TRUE(replacement26.IsApplicable(context.get(), fact_manager));
+  replacement26.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Replacements of the form %55 -> %108
+
+  // %56 = OpAccessChain %23 %50 %17 %21 %21 *%55*
+  // Corresponds to i[2].g.b[*1*]
+  // The index %55 used for 1 *can* be replaced
+  auto replacement27 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(
+          55, MakeInstructionDescriptor(56, SpvOpAccessChain, 0), 4),
+      108);
+  ASSERT_TRUE(replacement27.IsApplicable(context.get(), fact_manager));
+  replacement27.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Replacements of the form %8 -> %109
+
+  // %24 = OpAccessChain %23 %15 %21 *%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),
+      109);
+  ASSERT_TRUE(replacement28.IsApplicable(context.get(), fact_manager));
+  replacement28.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  const std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %13 "S"
+               OpMemberName %13 0 "a"
+               OpMemberName %13 1 "b"
+               OpMemberName %13 2 "c"
+               OpName %15 "d"
+               OpName %31 "e"
+               OpName %35 "T"
+               OpMemberName %35 0 "f"
+               OpMemberName %35 1 "g"
+               OpName %37 "h"
+               OpName %50 "i"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypeInt 32 0
+          %8 = OpConstant %7 3
+          %9 = OpTypeArray %6 %8
+         %10 = OpTypeFloat 32
+         %11 = OpTypeVector %10 4
+         %12 = OpTypeBool
+         %13 = OpTypeStruct %9 %11 %12
+         %14 = OpTypePointer Private %13
+         %15 = OpVariable %14 Private
+         %16 = OpConstant %6 0
+         %17 = OpConstant %6 2
+         %18 = OpConstant %6 10
+         %19 = OpTypePointer Private %6
+         %21 = OpConstant %6 1
+         %22 = OpConstant %10 11
+         %23 = OpTypePointer Private %10
+         %25 = OpConstantFalse %12
+         %26 = OpTypePointer Private %12
+         %28 = OpConstant %7 20
+         %29 = OpTypeArray %10 %28
+         %30 = OpTypePointer Private %29
+         %31 = OpVariable %30 Private
+         %32 = OpConstant %6 17
+         %33 = OpConstant %10 12
+         %35 = OpTypeStruct %10 %13
+         %36 = OpTypePointer Private %35
+         %37 = OpVariable %36 Private
+         %38 = OpConstant %10 13
+         %40 = OpConstant %6 14
+         %42 = OpConstant %10 15
+         %43 = OpConstant %7 0
+         %45 = OpConstantTrue %12
+         %47 = OpConstant %7 4
+         %48 = OpTypeArray %35 %47
+         %49 = OpTypePointer Private %48
+         %50 = OpVariable %49 Private
+         %51 = OpConstant %10 16
+         %54 = OpConstant %10 18
+         %55 = OpConstant %7 1
+         %57 = OpConstant %6 3
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+
+         %100 = OpCopyObject %6 %16 ; 0
+         %101 = OpCopyObject %6 %21 ; 1
+         %102 = OpCopyObject %6 %17 ; 2
+         %103 = OpCopyObject %6 %57 ; 3
+         %104 = OpCopyObject %6 %18 ; 10
+         %105 = OpCopyObject %6 %40 ; 14
+         %106 = OpCopyObject %6 %32 ; 17
+         %107 = OpCopyObject %7 %43 ; 0
+         %108 = OpCopyObject %7 %55 ; 1
+         %109 = OpCopyObject %7  %8 ; 3
+         %110 = OpCopyObject %7 %47 ; 4
+         %111 = OpCopyObject %7 %28 ; 20
+         %112 = OpCopyObject %12 %45 ; true
+
+         %20 = OpAccessChain %19 %15 %16 %102
+               OpStore %20 %18
+         %24 = OpAccessChain %23 %15 %21 %109
+               OpStore %24 %22
+         %27 = OpAccessChain %26 %15 %17
+               OpStore %27 %25
+         %34 = OpAccessChain %23 %31 %106
+               OpStore %34 %33
+         %39 = OpAccessChain %23 %37 %16
+               OpStore %39 %38
+         %41 = OpAccessChain %19 %37 %21 %16 %101
+               OpStore %41 %40
+         %44 = OpAccessChain %23 %37 %21 %21 %107
+               OpStore %44 %42
+         %46 = OpAccessChain %26 %37 %21 %17
+               OpStore %46 %45
+         %52 = OpAccessChain %23 %50 %100 %16
+               OpStore %52 %51
+         %53 = OpAccessChain %19 %50 %101 %21 %16 %100
+               OpStore %53 %32
+         %56 = OpAccessChain %23 %50 %102 %21 %21 %108
+               OpStore %56 %54
+         %58 = OpAccessChain %26 %50 %103 %21 %17
+               OpStore %58 %45
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/test/fuzz/transformation_set_function_control_test.cpp b/test/fuzz/transformation_set_function_control_test.cpp
new file mode 100644
index 0000000..536e965
--- /dev/null
+++ b/test/fuzz/transformation_set_function_control_test.cpp
@@ -0,0 +1,251 @@
+// 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.
+
+#include "source/fuzz/transformation_set_function_control.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationSetFunctionControlTest, VariousScenarios) {
+  // This is a simple transformation; this test captures the important things
+  // to check for.
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %54
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %11 "foo(i1;i1;"
+               OpName %9 "a"
+               OpName %10 "b"
+               OpName %13 "bar("
+               OpName %17 "baz(i1;"
+               OpName %16 "x"
+               OpName %21 "boo(i1;i1;"
+               OpName %19 "a"
+               OpName %20 "b"
+               OpName %29 "g"
+               OpName %42 "param"
+               OpName %44 "param"
+               OpName %45 "param"
+               OpName %48 "param"
+               OpName %49 "param"
+               OpName %54 "color"
+               OpDecorate %54 Location 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %8 = OpTypeFunction %6 %7 %7
+         %15 = OpTypeFunction %6 %7
+         %28 = OpTypePointer Private %6
+         %29 = OpVariable %28 Private
+         %30 = OpConstant %6 2
+         %31 = OpConstant %6 5
+         %51 = OpTypeFloat 32
+         %52 = OpTypeVector %51 4
+         %53 = OpTypePointer Output %52
+         %54 = OpVariable %53 Output
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %42 = OpVariable %7 Function
+         %44 = OpVariable %7 Function
+         %45 = OpVariable %7 Function
+         %48 = OpVariable %7 Function
+         %49 = OpVariable %7 Function
+         %41 = OpFunctionCall %2 %13
+               OpStore %42 %30
+         %43 = OpFunctionCall %6 %17 %42
+               OpStore %44 %31
+         %46 = OpLoad %6 %29
+               OpStore %45 %46
+         %47 = OpFunctionCall %6 %21 %44 %45
+               OpStore %48 %43
+               OpStore %49 %47
+         %50 = OpFunctionCall %6 %11 %48 %49
+               OpReturn
+               OpFunctionEnd
+         %11 = OpFunction %6 Const %8
+          %9 = OpFunctionParameter %7
+         %10 = OpFunctionParameter %7
+         %12 = OpLabel
+         %23 = OpLoad %6 %9
+         %24 = OpLoad %6 %10
+         %25 = OpIAdd %6 %23 %24
+               OpReturnValue %25
+               OpFunctionEnd
+         %13 = OpFunction %2 Inline %3
+         %14 = OpLabel
+               OpStore %29 %30
+               OpReturn
+               OpFunctionEnd
+         %17 = OpFunction %6 Pure|DontInline %15
+         %16 = OpFunctionParameter %7
+         %18 = OpLabel
+         %32 = OpLoad %6 %16
+         %33 = OpIAdd %6 %31 %32
+               OpReturnValue %33
+               OpFunctionEnd
+         %21 = OpFunction %6 DontInline %8
+         %19 = OpFunctionParameter %7
+         %20 = OpFunctionParameter %7
+         %22 = OpLabel
+         %36 = OpLoad %6 %19
+         %37 = OpLoad %6 %20
+         %38 = OpIMul %6 %36 %37
+               OpReturnValue %38
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+
+  FactManager fact_manager;
+
+  // %36 is not a function
+  ASSERT_FALSE(TransformationSetFunctionControl(36, SpvFunctionControlMaskNone)
+                   .IsApplicable(context.get(), fact_manager));
+  // Cannot add the Pure function control to %4 as it did not already have it
+  ASSERT_FALSE(TransformationSetFunctionControl(4, SpvFunctionControlPureMask)
+                   .IsApplicable(context.get(), fact_manager));
+  // Cannot add the Const function control to %21 as it did not already
+  // have it
+  ASSERT_FALSE(TransformationSetFunctionControl(21, SpvFunctionControlConstMask)
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Set to None, removing Const
+  TransformationSetFunctionControl transformation1(11,
+                                                   SpvFunctionControlMaskNone);
+  ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager));
+  transformation1.Apply(context.get(), &fact_manager);
+
+  // Set to Inline; silly to do it on an entry point, but it is allowed
+  TransformationSetFunctionControl transformation2(
+      4, SpvFunctionControlInlineMask);
+  ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager));
+  transformation2.Apply(context.get(), &fact_manager);
+
+  // Set to Pure, removing DontInline
+  TransformationSetFunctionControl transformation3(17,
+                                                   SpvFunctionControlPureMask);
+  ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager));
+  transformation3.Apply(context.get(), &fact_manager);
+
+  // Change from Inline to DontInline
+  TransformationSetFunctionControl transformation4(
+      13, SpvFunctionControlDontInlineMask);
+  ASSERT_TRUE(transformation4.IsApplicable(context.get(), fact_manager));
+  transformation4.Apply(context.get(), &fact_manager);
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %54
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %11 "foo(i1;i1;"
+               OpName %9 "a"
+               OpName %10 "b"
+               OpName %13 "bar("
+               OpName %17 "baz(i1;"
+               OpName %16 "x"
+               OpName %21 "boo(i1;i1;"
+               OpName %19 "a"
+               OpName %20 "b"
+               OpName %29 "g"
+               OpName %42 "param"
+               OpName %44 "param"
+               OpName %45 "param"
+               OpName %48 "param"
+               OpName %49 "param"
+               OpName %54 "color"
+               OpDecorate %54 Location 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %8 = OpTypeFunction %6 %7 %7
+         %15 = OpTypeFunction %6 %7
+         %28 = OpTypePointer Private %6
+         %29 = OpVariable %28 Private
+         %30 = OpConstant %6 2
+         %31 = OpConstant %6 5
+         %51 = OpTypeFloat 32
+         %52 = OpTypeVector %51 4
+         %53 = OpTypePointer Output %52
+         %54 = OpVariable %53 Output
+          %4 = OpFunction %2 Inline %3
+          %5 = OpLabel
+         %42 = OpVariable %7 Function
+         %44 = OpVariable %7 Function
+         %45 = OpVariable %7 Function
+         %48 = OpVariable %7 Function
+         %49 = OpVariable %7 Function
+         %41 = OpFunctionCall %2 %13
+               OpStore %42 %30
+         %43 = OpFunctionCall %6 %17 %42
+               OpStore %44 %31
+         %46 = OpLoad %6 %29
+               OpStore %45 %46
+         %47 = OpFunctionCall %6 %21 %44 %45
+               OpStore %48 %43
+               OpStore %49 %47
+         %50 = OpFunctionCall %6 %11 %48 %49
+               OpReturn
+               OpFunctionEnd
+         %11 = OpFunction %6 None %8
+          %9 = OpFunctionParameter %7
+         %10 = OpFunctionParameter %7
+         %12 = OpLabel
+         %23 = OpLoad %6 %9
+         %24 = OpLoad %6 %10
+         %25 = OpIAdd %6 %23 %24
+               OpReturnValue %25
+               OpFunctionEnd
+         %13 = OpFunction %2 DontInline %3
+         %14 = OpLabel
+               OpStore %29 %30
+               OpReturn
+               OpFunctionEnd
+         %17 = OpFunction %6 Pure %15
+         %16 = OpFunctionParameter %7
+         %18 = OpLabel
+         %32 = OpLoad %6 %16
+         %33 = OpIAdd %6 %31 %32
+               OpReturnValue %33
+               OpFunctionEnd
+         %21 = OpFunction %6 DontInline %8
+         %19 = OpFunctionParameter %7
+         %20 = OpFunctionParameter %7
+         %22 = OpLabel
+         %36 = OpLoad %6 %19
+         %37 = OpLoad %6 %20
+         %38 = OpIMul %6 %36 %37
+               OpReturnValue %38
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/test/fuzz/transformation_set_loop_control_test.cpp b/test/fuzz/transformation_set_loop_control_test.cpp
new file mode 100644
index 0000000..83953ec
--- /dev/null
+++ b/test/fuzz/transformation_set_loop_control_test.cpp
@@ -0,0 +1,968 @@
+// 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.
+
+#include "source/fuzz/transformation_set_loop_control.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationSetLoopControlTest, VariousScenarios) {
+  // This test features loops with various different controls, and goes through
+  // a number of acceptable and unacceptable transformations to those controls.
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 0
+         %16 = OpConstant %6 100
+         %17 = OpTypeBool
+         %20 = OpConstant %6 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %22 = OpVariable %7 Function
+         %32 = OpVariable %7 Function
+         %42 = OpVariable %7 Function
+         %52 = OpVariable %7 Function
+         %62 = OpVariable %7 Function
+         %72 = OpVariable %7 Function
+         %82 = OpVariable %7 Function
+         %92 = OpVariable %7 Function
+        %102 = OpVariable %7 Function
+        %112 = OpVariable %7 Function
+        %122 = OpVariable %7 Function
+               OpStore %8 %9
+               OpBranch %10
+         %10 = OpLabel
+        %132 = OpPhi %6 %9 %5 %21 %13
+               OpLoopMerge %12 %13 None
+               OpBranch %14
+         %14 = OpLabel
+         %18 = OpSLessThan %17 %132 %16
+               OpBranchConditional %18 %11 %12
+         %11 = OpLabel
+               OpBranch %13
+         %13 = OpLabel
+         %21 = OpIAdd %6 %132 %20
+               OpStore %8 %21
+               OpBranch %10
+         %12 = OpLabel
+               OpStore %22 %9
+               OpBranch %23
+         %23 = OpLabel
+        %133 = OpPhi %6 %9 %12 %31 %26
+               OpLoopMerge %25 %26 Unroll
+               OpBranch %27
+         %27 = OpLabel
+         %29 = OpSLessThan %17 %133 %16
+               OpBranchConditional %29 %24 %25
+         %24 = OpLabel
+               OpBranch %26
+         %26 = OpLabel
+         %31 = OpIAdd %6 %133 %20
+               OpStore %22 %31
+               OpBranch %23
+         %25 = OpLabel
+               OpStore %32 %9
+               OpBranch %33
+         %33 = OpLabel
+        %134 = OpPhi %6 %9 %25 %41 %36
+               OpLoopMerge %35 %36 DontUnroll
+               OpBranch %37
+         %37 = OpLabel
+         %39 = OpSLessThan %17 %134 %16
+               OpBranchConditional %39 %34 %35
+         %34 = OpLabel
+               OpBranch %36
+         %36 = OpLabel
+         %41 = OpIAdd %6 %134 %20
+               OpStore %32 %41
+               OpBranch %33
+         %35 = OpLabel
+               OpStore %42 %9
+               OpBranch %43
+         %43 = OpLabel
+        %135 = OpPhi %6 %9 %35 %51 %46
+               OpLoopMerge %45 %46 DependencyInfinite
+               OpBranch %47
+         %47 = OpLabel
+         %49 = OpSLessThan %17 %135 %16
+               OpBranchConditional %49 %44 %45
+         %44 = OpLabel
+               OpBranch %46
+         %46 = OpLabel
+         %51 = OpIAdd %6 %135 %20
+               OpStore %42 %51
+               OpBranch %43
+         %45 = OpLabel
+               OpStore %52 %9
+               OpBranch %53
+         %53 = OpLabel
+        %136 = OpPhi %6 %9 %45 %61 %56
+               OpLoopMerge %55 %56 DependencyLength 3
+               OpBranch %57
+         %57 = OpLabel
+         %59 = OpSLessThan %17 %136 %16
+               OpBranchConditional %59 %54 %55
+         %54 = OpLabel
+               OpBranch %56
+         %56 = OpLabel
+         %61 = OpIAdd %6 %136 %20
+               OpStore %52 %61
+               OpBranch %53
+         %55 = OpLabel
+               OpStore %62 %9
+               OpBranch %63
+         %63 = OpLabel
+        %137 = OpPhi %6 %9 %55 %71 %66
+               OpLoopMerge %65 %66 MinIterations 10
+               OpBranch %67
+         %67 = OpLabel
+         %69 = OpSLessThan %17 %137 %16
+               OpBranchConditional %69 %64 %65
+         %64 = OpLabel
+               OpBranch %66
+         %66 = OpLabel
+         %71 = OpIAdd %6 %137 %20
+               OpStore %62 %71
+               OpBranch %63
+         %65 = OpLabel
+               OpStore %72 %9
+               OpBranch %73
+         %73 = OpLabel
+        %138 = OpPhi %6 %9 %65 %81 %76
+               OpLoopMerge %75 %76 MaxIterations 50
+               OpBranch %77
+         %77 = OpLabel
+         %79 = OpSLessThan %17 %138 %16
+               OpBranchConditional %79 %74 %75
+         %74 = OpLabel
+               OpBranch %76
+         %76 = OpLabel
+         %81 = OpIAdd %6 %138 %20
+               OpStore %72 %81
+               OpBranch %73
+         %75 = OpLabel
+               OpStore %82 %9
+               OpBranch %83
+         %83 = OpLabel
+        %139 = OpPhi %6 %9 %75 %91 %86
+               OpLoopMerge %85 %86 IterationMultiple 4
+               OpBranch %87
+         %87 = OpLabel
+         %89 = OpSLessThan %17 %139 %16
+               OpBranchConditional %89 %84 %85
+         %84 = OpLabel
+               OpBranch %86
+         %86 = OpLabel
+         %91 = OpIAdd %6 %139 %20
+               OpStore %82 %91
+               OpBranch %83
+         %85 = OpLabel
+               OpStore %92 %9
+               OpBranch %93
+         %93 = OpLabel
+        %140 = OpPhi %6 %9 %85 %101 %96
+               OpLoopMerge %95 %96 PeelCount 2
+               OpBranch %97
+         %97 = OpLabel
+         %99 = OpSLessThan %17 %140 %16
+               OpBranchConditional %99 %94 %95
+         %94 = OpLabel
+               OpBranch %96
+         %96 = OpLabel
+        %101 = OpIAdd %6 %140 %20
+               OpStore %92 %101
+               OpBranch %93
+         %95 = OpLabel
+               OpStore %102 %9
+               OpBranch %103
+        %103 = OpLabel
+        %141 = OpPhi %6 %9 %95 %111 %106
+               OpLoopMerge %105 %106 PartialCount 3
+               OpBranch %107
+        %107 = OpLabel
+        %109 = OpSLessThan %17 %141 %16
+               OpBranchConditional %109 %104 %105
+        %104 = OpLabel
+               OpBranch %106
+        %106 = OpLabel
+        %111 = OpIAdd %6 %141 %20
+               OpStore %102 %111
+               OpBranch %103
+        %105 = OpLabel
+               OpStore %112 %9
+               OpBranch %113
+        %113 = OpLabel
+        %142 = OpPhi %6 %9 %105 %121 %116
+               OpLoopMerge %115 %116 Unroll|PeelCount|PartialCount 3 4
+               OpBranch %117
+        %117 = OpLabel
+        %119 = OpSLessThan %17 %142 %16
+               OpBranchConditional %119 %114 %115
+        %114 = OpLabel
+               OpBranch %116
+        %116 = OpLabel
+        %121 = OpIAdd %6 %142 %20
+               OpStore %112 %121
+               OpBranch %113
+        %115 = OpLabel
+               OpStore %122 %9
+               OpBranch %123
+        %123 = OpLabel
+        %143 = OpPhi %6 %9 %115 %131 %126
+               OpLoopMerge %125 %126 DependencyLength|MinIterations|MaxIterations|IterationMultiple|PeelCount|PartialCount 2 5 90 4 7 14
+               OpBranch %127
+        %127 = OpLabel
+        %129 = OpSLessThan %17 %143 %16
+               OpBranchConditional %129 %124 %125
+        %124 = OpLabel
+               OpBranch %126
+        %126 = OpLabel
+        %131 = OpIAdd %6 %143 %20
+               OpStore %122 %131
+               OpBranch %123
+        %125 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  // These are the loop headers together with the selection controls of their
+  // merge instructions:
+  //  %10 None
+  //  %23 Unroll
+  //  %33 DontUnroll
+  //  %43 DependencyInfinite
+  //  %53 DependencyLength 3
+  //  %63 MinIterations 10
+  //  %73 MaxIterations 50
+  //  %83 IterationMultiple 4
+  //  %93 PeelCount 2
+  // %103 PartialCount 3
+  // %113 Unroll|PeelCount|PartialCount 3 4
+  // %123
+  // DependencyLength|MinIterations|MaxIterations|IterationMultiple|PeelCount|PartialCount
+  // 2 5 90 4 7 14
+
+  ASSERT_TRUE(TransformationSetLoopControl(10, SpvLoopControlMaskNone, 0, 0)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(TransformationSetLoopControl(10, SpvLoopControlUnrollMask, 0, 0)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(
+      TransformationSetLoopControl(10, SpvLoopControlDontUnrollMask, 0, 0)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(TransformationSetLoopControl(
+                   10, SpvLoopControlDependencyInfiniteMask, 0, 0)
+                   .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationSetLoopControl(10, SpvLoopControlDependencyLengthMask, 0, 0)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationSetLoopControl(10, SpvLoopControlMinIterationsMask, 0, 0)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationSetLoopControl(10, SpvLoopControlMaxIterationsMask, 0, 0)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(TransformationSetLoopControl(
+                   10, SpvLoopControlIterationMultipleMask, 0, 0)
+                   .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(
+      TransformationSetLoopControl(10, SpvLoopControlPeelCountMask, 3, 0)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationSetLoopControl(10, SpvLoopControlPeelCountMask, 3, 3)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(
+      TransformationSetLoopControl(10, SpvLoopControlPartialCountMask, 0, 3)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationSetLoopControl(10, SpvLoopControlPartialCountMask, 3, 3)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  10,
+                  SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask,
+                  3, 3)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(TransformationSetLoopControl(10,
+                                           SpvLoopControlUnrollMask |
+                                               SpvLoopControlPeelCountMask |
+                                               SpvLoopControlPartialCountMask,
+                                           3, 3)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(TransformationSetLoopControl(10,
+                                            SpvLoopControlDontUnrollMask |
+                                                SpvLoopControlPeelCountMask |
+                                                SpvLoopControlPartialCountMask,
+                                            3, 3)
+                   .IsApplicable(context.get(), fact_manager));
+
+  ASSERT_TRUE(TransformationSetLoopControl(23, SpvLoopControlMaskNone, 0, 0)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(TransformationSetLoopControl(23, SpvLoopControlUnrollMask, 0, 0)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(
+      TransformationSetLoopControl(23, SpvLoopControlDontUnrollMask, 0, 0)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  23,
+                  SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask,
+                  3, 3)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationSetLoopControl(23, SpvLoopControlMaxIterationsMask, 2, 3)
+          .IsApplicable(context.get(), fact_manager));
+
+  ASSERT_TRUE(TransformationSetLoopControl(33, SpvLoopControlMaskNone, 0, 0)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(TransformationSetLoopControl(33, SpvLoopControlUnrollMask, 0, 0)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(
+      TransformationSetLoopControl(33, SpvLoopControlDontUnrollMask, 0, 0)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationSetLoopControl(33, SpvLoopControlMinIterationsMask, 0, 0)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(
+      TransformationSetLoopControl(
+          33, SpvLoopControlUnrollMask | SpvLoopControlPeelCountMask, 5, 0)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(TransformationSetLoopControl(33,
+                                            SpvLoopControlDontUnrollMask |
+                                                SpvLoopControlPartialCountMask,
+                                            0, 10)
+                   .IsApplicable(context.get(), fact_manager));
+
+  ASSERT_TRUE(TransformationSetLoopControl(43, SpvLoopControlMaskNone, 0, 0)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(TransformationSetLoopControl(43, SpvLoopControlUnrollMask, 0, 0)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(
+      TransformationSetLoopControl(43, SpvLoopControlDontUnrollMask, 0, 0)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  43,
+                  SpvLoopControlMaskNone | SpvLoopControlDependencyInfiniteMask,
+                  0, 0)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(
+      TransformationSetLoopControl(
+          43, SpvLoopControlUnrollMask | SpvLoopControlDependencyInfiniteMask,
+          0, 0)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(
+      TransformationSetLoopControl(
+          43,
+          SpvLoopControlDontUnrollMask | SpvLoopControlDependencyInfiniteMask,
+          0, 0)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationSetLoopControl(43,
+                                   SpvLoopControlDependencyInfiniteMask |
+                                       SpvLoopControlDependencyLengthMask,
+                                   0, 0)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(
+      TransformationSetLoopControl(
+          43, SpvLoopControlUnrollMask | SpvLoopControlPeelCountMask, 5, 0)
+          .IsApplicable(context.get(), fact_manager));
+
+  ASSERT_TRUE(TransformationSetLoopControl(53, SpvLoopControlMaskNone, 0, 0)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(TransformationSetLoopControl(53, SpvLoopControlUnrollMask, 0, 0)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(
+      TransformationSetLoopControl(53, SpvLoopControlDontUnrollMask, 0, 0)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationSetLoopControl(53, SpvLoopControlMaxIterationsMask, 0, 0)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(
+      TransformationSetLoopControl(
+          53, SpvLoopControlMaskNone | SpvLoopControlDependencyLengthMask, 0, 0)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationSetLoopControl(
+          53, SpvLoopControlUnrollMask | SpvLoopControlDependencyInfiniteMask,
+          0, 0)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(
+      TransformationSetLoopControl(
+          53, SpvLoopControlDontUnrollMask | SpvLoopControlDependencyLengthMask,
+          0, 0)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationSetLoopControl(53,
+                                   SpvLoopControlDependencyInfiniteMask |
+                                       SpvLoopControlDependencyLengthMask,
+                                   0, 0)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(
+      TransformationSetLoopControl(
+          53,
+          SpvLoopControlUnrollMask | SpvLoopControlDependencyLengthMask |
+              SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask,
+          5, 3)
+          .IsApplicable(context.get(), fact_manager));
+
+  ASSERT_TRUE(TransformationSetLoopControl(63, SpvLoopControlMaskNone, 0, 0)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(TransformationSetLoopControl(63, SpvLoopControlUnrollMask, 0, 0)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(
+      TransformationSetLoopControl(63, SpvLoopControlDontUnrollMask, 0, 0)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(TransformationSetLoopControl(63,
+                                           SpvLoopControlUnrollMask |
+                                               SpvLoopControlMinIterationsMask |
+                                               SpvLoopControlPeelCountMask |
+                                               SpvLoopControlPartialCountMask,
+                                           5, 3)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(TransformationSetLoopControl(63,
+                                           SpvLoopControlUnrollMask |
+                                               SpvLoopControlMinIterationsMask |
+                                               SpvLoopControlPeelCountMask,
+                                           23, 0)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(TransformationSetLoopControl(
+                   63,
+                   SpvLoopControlUnrollMask | SpvLoopControlMinIterationsMask |
+                       SpvLoopControlPeelCountMask,
+                   2, 23)
+                   .IsApplicable(context.get(), fact_manager));
+
+  ASSERT_TRUE(TransformationSetLoopControl(73, SpvLoopControlMaskNone, 0, 0)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(TransformationSetLoopControl(73, SpvLoopControlUnrollMask, 0, 0)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(
+      TransformationSetLoopControl(73, SpvLoopControlDontUnrollMask, 0, 0)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(TransformationSetLoopControl(
+                   73,
+                   SpvLoopControlUnrollMask | SpvLoopControlMinIterationsMask |
+                       SpvLoopControlPeelCountMask |
+                       SpvLoopControlPartialCountMask,
+                   5, 3)
+                   .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(TransformationSetLoopControl(73,
+                                           SpvLoopControlUnrollMask |
+                                               SpvLoopControlMaxIterationsMask |
+                                               SpvLoopControlPeelCountMask,
+                                           23, 0)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(TransformationSetLoopControl(
+                   73,
+                   SpvLoopControlUnrollMask | SpvLoopControlMaxIterationsMask |
+                       SpvLoopControlPeelCountMask,
+                   2, 23)
+                   .IsApplicable(context.get(), fact_manager));
+
+  ASSERT_TRUE(TransformationSetLoopControl(83, SpvLoopControlMaskNone, 0, 0)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(TransformationSetLoopControl(83, SpvLoopControlUnrollMask, 0, 0)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(
+      TransformationSetLoopControl(83, SpvLoopControlDontUnrollMask, 0, 0)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(TransformationSetLoopControl(
+                   83,
+                   SpvLoopControlUnrollMask | SpvLoopControlMinIterationsMask |
+                       SpvLoopControlPeelCountMask |
+                       SpvLoopControlPartialCountMask,
+                   5, 3)
+                   .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(
+      TransformationSetLoopControl(83,
+                                   SpvLoopControlUnrollMask |
+                                       SpvLoopControlIterationMultipleMask |
+                                       SpvLoopControlPeelCountMask,
+                                   23, 0)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationSetLoopControl(83,
+                                   SpvLoopControlUnrollMask |
+                                       SpvLoopControlIterationMultipleMask |
+                                       SpvLoopControlPeelCountMask,
+                                   2, 23)
+          .IsApplicable(context.get(), fact_manager));
+
+  ASSERT_TRUE(TransformationSetLoopControl(93, SpvLoopControlMaskNone, 0, 0)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(TransformationSetLoopControl(93, SpvLoopControlUnrollMask, 0, 0)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(
+      TransformationSetLoopControl(93, SpvLoopControlDontUnrollMask, 0, 0)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(
+      TransformationSetLoopControl(93, SpvLoopControlPeelCountMask, 8, 0)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationSetLoopControl(93, SpvLoopControlPeelCountMask, 8, 8)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(
+      TransformationSetLoopControl(93, SpvLoopControlPartialCountMask, 0, 8)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  93,
+                  SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask,
+                  16, 8)
+                  .IsApplicable(context.get(), fact_manager));
+
+  ASSERT_TRUE(TransformationSetLoopControl(103, SpvLoopControlMaskNone, 0, 0)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(TransformationSetLoopControl(103, SpvLoopControlUnrollMask, 0, 0)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(
+      TransformationSetLoopControl(103, SpvLoopControlDontUnrollMask, 0, 0)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(
+      TransformationSetLoopControl(103, SpvLoopControlPartialCountMask, 0, 60)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(TransformationSetLoopControl(103,
+                                            SpvLoopControlDontUnrollMask |
+                                                SpvLoopControlPartialCountMask,
+                                            0, 60)
+                   .IsApplicable(context.get(), fact_manager));
+
+  ASSERT_TRUE(TransformationSetLoopControl(113, SpvLoopControlMaskNone, 0, 0)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(TransformationSetLoopControl(113, SpvLoopControlUnrollMask, 0, 0)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(
+      TransformationSetLoopControl(113, SpvLoopControlDontUnrollMask, 0, 0)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(
+      TransformationSetLoopControl(113, SpvLoopControlPeelCountMask, 12, 0)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationSetLoopControl(
+          113,
+          SpvLoopControlIterationMultipleMask | SpvLoopControlPeelCountMask, 12,
+          0)
+          .IsApplicable(context.get(), fact_manager));
+
+  ASSERT_TRUE(TransformationSetLoopControl(123, SpvLoopControlMaskNone, 0, 0)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(TransformationSetLoopControl(123, SpvLoopControlUnrollMask, 0, 0)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(
+      TransformationSetLoopControl(123, SpvLoopControlDontUnrollMask, 0, 0)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(
+      TransformationSetLoopControl(
+          123,
+          SpvLoopControlMinIterationsMask | SpvLoopControlMaxIterationsMask |
+              SpvLoopControlIterationMultipleMask |
+              SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask,
+          7, 8)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(TransformationSetLoopControl(123,
+                                           SpvLoopControlUnrollMask |
+                                               SpvLoopControlMinIterationsMask |
+                                               SpvLoopControlMaxIterationsMask |
+                                               SpvLoopControlPartialCountMask,
+                                           0, 9)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(TransformationSetLoopControl(
+                   123,
+                   SpvLoopControlUnrollMask | SpvLoopControlMinIterationsMask |
+                       SpvLoopControlMaxIterationsMask |
+                       SpvLoopControlPartialCountMask,
+                   7, 9)
+                   .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationSetLoopControl(
+          123,
+          SpvLoopControlDontUnrollMask | SpvLoopControlMinIterationsMask |
+              SpvLoopControlMaxIterationsMask | SpvLoopControlPartialCountMask,
+          7, 9)
+          .IsApplicable(context.get(), fact_manager));
+
+  TransformationSetLoopControl(10,
+                               SpvLoopControlUnrollMask |
+                                   SpvLoopControlPeelCountMask |
+                                   SpvLoopControlPartialCountMask,
+                               3, 3)
+      .Apply(context.get(), &fact_manager);
+  TransformationSetLoopControl(23, SpvLoopControlDontUnrollMask, 0, 0)
+      .Apply(context.get(), &fact_manager);
+  TransformationSetLoopControl(33, SpvLoopControlUnrollMask, 0, 0)
+      .Apply(context.get(), &fact_manager);
+  TransformationSetLoopControl(
+      43, SpvLoopControlDontUnrollMask | SpvLoopControlDependencyInfiniteMask,
+      0, 0)
+      .Apply(context.get(), &fact_manager);
+  TransformationSetLoopControl(53, SpvLoopControlMaskNone, 0, 0)
+      .Apply(context.get(), &fact_manager);
+  TransformationSetLoopControl(63,
+                               SpvLoopControlUnrollMask |
+                                   SpvLoopControlMinIterationsMask |
+                                   SpvLoopControlPeelCountMask,
+                               23, 0)
+      .Apply(context.get(), &fact_manager);
+  TransformationSetLoopControl(73,
+                               SpvLoopControlUnrollMask |
+                                   SpvLoopControlMaxIterationsMask |
+                                   SpvLoopControlPeelCountMask,
+                               23, 0)
+      .Apply(context.get(), &fact_manager);
+  TransformationSetLoopControl(83, SpvLoopControlDontUnrollMask, 0, 0)
+      .Apply(context.get(), &fact_manager);
+  TransformationSetLoopControl(
+      93, SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask, 16, 8)
+      .Apply(context.get(), &fact_manager);
+  TransformationSetLoopControl(103, SpvLoopControlPartialCountMask, 0, 60)
+      .Apply(context.get(), &fact_manager);
+  TransformationSetLoopControl(113, SpvLoopControlPeelCountMask, 12, 0)
+      .Apply(context.get(), &fact_manager);
+  TransformationSetLoopControl(
+      123,
+      SpvLoopControlUnrollMask | SpvLoopControlMinIterationsMask |
+          SpvLoopControlMaxIterationsMask | SpvLoopControlPartialCountMask,
+      0, 9)
+      .Apply(context.get(), &fact_manager);
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 0
+         %16 = OpConstant %6 100
+         %17 = OpTypeBool
+         %20 = OpConstant %6 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %22 = OpVariable %7 Function
+         %32 = OpVariable %7 Function
+         %42 = OpVariable %7 Function
+         %52 = OpVariable %7 Function
+         %62 = OpVariable %7 Function
+         %72 = OpVariable %7 Function
+         %82 = OpVariable %7 Function
+         %92 = OpVariable %7 Function
+        %102 = OpVariable %7 Function
+        %112 = OpVariable %7 Function
+        %122 = OpVariable %7 Function
+               OpStore %8 %9
+               OpBranch %10
+         %10 = OpLabel
+        %132 = OpPhi %6 %9 %5 %21 %13
+               OpLoopMerge %12 %13 Unroll|PeelCount|PartialCount 3 3
+               OpBranch %14
+         %14 = OpLabel
+         %18 = OpSLessThan %17 %132 %16
+               OpBranchConditional %18 %11 %12
+         %11 = OpLabel
+               OpBranch %13
+         %13 = OpLabel
+         %21 = OpIAdd %6 %132 %20
+               OpStore %8 %21
+               OpBranch %10
+         %12 = OpLabel
+               OpStore %22 %9
+               OpBranch %23
+         %23 = OpLabel
+        %133 = OpPhi %6 %9 %12 %31 %26
+               OpLoopMerge %25 %26 DontUnroll
+               OpBranch %27
+         %27 = OpLabel
+         %29 = OpSLessThan %17 %133 %16
+               OpBranchConditional %29 %24 %25
+         %24 = OpLabel
+               OpBranch %26
+         %26 = OpLabel
+         %31 = OpIAdd %6 %133 %20
+               OpStore %22 %31
+               OpBranch %23
+         %25 = OpLabel
+               OpStore %32 %9
+               OpBranch %33
+         %33 = OpLabel
+        %134 = OpPhi %6 %9 %25 %41 %36
+               OpLoopMerge %35 %36 Unroll
+               OpBranch %37
+         %37 = OpLabel
+         %39 = OpSLessThan %17 %134 %16
+               OpBranchConditional %39 %34 %35
+         %34 = OpLabel
+               OpBranch %36
+         %36 = OpLabel
+         %41 = OpIAdd %6 %134 %20
+               OpStore %32 %41
+               OpBranch %33
+         %35 = OpLabel
+               OpStore %42 %9
+               OpBranch %43
+         %43 = OpLabel
+        %135 = OpPhi %6 %9 %35 %51 %46
+               OpLoopMerge %45 %46 DontUnroll|DependencyInfinite
+               OpBranch %47
+         %47 = OpLabel
+         %49 = OpSLessThan %17 %135 %16
+               OpBranchConditional %49 %44 %45
+         %44 = OpLabel
+               OpBranch %46
+         %46 = OpLabel
+         %51 = OpIAdd %6 %135 %20
+               OpStore %42 %51
+               OpBranch %43
+         %45 = OpLabel
+               OpStore %52 %9
+               OpBranch %53
+         %53 = OpLabel
+        %136 = OpPhi %6 %9 %45 %61 %56
+               OpLoopMerge %55 %56 None
+               OpBranch %57
+         %57 = OpLabel
+         %59 = OpSLessThan %17 %136 %16
+               OpBranchConditional %59 %54 %55
+         %54 = OpLabel
+               OpBranch %56
+         %56 = OpLabel
+         %61 = OpIAdd %6 %136 %20
+               OpStore %52 %61
+               OpBranch %53
+         %55 = OpLabel
+               OpStore %62 %9
+               OpBranch %63
+         %63 = OpLabel
+        %137 = OpPhi %6 %9 %55 %71 %66
+               OpLoopMerge %65 %66 Unroll|MinIterations|PeelCount 10 23
+               OpBranch %67
+         %67 = OpLabel
+         %69 = OpSLessThan %17 %137 %16
+               OpBranchConditional %69 %64 %65
+         %64 = OpLabel
+               OpBranch %66
+         %66 = OpLabel
+         %71 = OpIAdd %6 %137 %20
+               OpStore %62 %71
+               OpBranch %63
+         %65 = OpLabel
+               OpStore %72 %9
+               OpBranch %73
+         %73 = OpLabel
+        %138 = OpPhi %6 %9 %65 %81 %76
+               OpLoopMerge %75 %76 Unroll|MaxIterations|PeelCount 50 23
+               OpBranch %77
+         %77 = OpLabel
+         %79 = OpSLessThan %17 %138 %16
+               OpBranchConditional %79 %74 %75
+         %74 = OpLabel
+               OpBranch %76
+         %76 = OpLabel
+         %81 = OpIAdd %6 %138 %20
+               OpStore %72 %81
+               OpBranch %73
+         %75 = OpLabel
+               OpStore %82 %9
+               OpBranch %83
+         %83 = OpLabel
+        %139 = OpPhi %6 %9 %75 %91 %86
+               OpLoopMerge %85 %86 DontUnroll
+               OpBranch %87
+         %87 = OpLabel
+         %89 = OpSLessThan %17 %139 %16
+               OpBranchConditional %89 %84 %85
+         %84 = OpLabel
+               OpBranch %86
+         %86 = OpLabel
+         %91 = OpIAdd %6 %139 %20
+               OpStore %82 %91
+               OpBranch %83
+         %85 = OpLabel
+               OpStore %92 %9
+               OpBranch %93
+         %93 = OpLabel
+        %140 = OpPhi %6 %9 %85 %101 %96
+               OpLoopMerge %95 %96 PeelCount|PartialCount 16 8
+               OpBranch %97
+         %97 = OpLabel
+         %99 = OpSLessThan %17 %140 %16
+               OpBranchConditional %99 %94 %95
+         %94 = OpLabel
+               OpBranch %96
+         %96 = OpLabel
+        %101 = OpIAdd %6 %140 %20
+               OpStore %92 %101
+               OpBranch %93
+         %95 = OpLabel
+               OpStore %102 %9
+               OpBranch %103
+        %103 = OpLabel
+        %141 = OpPhi %6 %9 %95 %111 %106
+               OpLoopMerge %105 %106 PartialCount 60
+               OpBranch %107
+        %107 = OpLabel
+        %109 = OpSLessThan %17 %141 %16
+               OpBranchConditional %109 %104 %105
+        %104 = OpLabel
+               OpBranch %106
+        %106 = OpLabel
+        %111 = OpIAdd %6 %141 %20
+               OpStore %102 %111
+               OpBranch %103
+        %105 = OpLabel
+               OpStore %112 %9
+               OpBranch %113
+        %113 = OpLabel
+        %142 = OpPhi %6 %9 %105 %121 %116
+               OpLoopMerge %115 %116 PeelCount 12
+               OpBranch %117
+        %117 = OpLabel
+        %119 = OpSLessThan %17 %142 %16
+               OpBranchConditional %119 %114 %115
+        %114 = OpLabel
+               OpBranch %116
+        %116 = OpLabel
+        %121 = OpIAdd %6 %142 %20
+               OpStore %112 %121
+               OpBranch %113
+        %115 = OpLabel
+               OpStore %122 %9
+               OpBranch %123
+        %123 = OpLabel
+        %143 = OpPhi %6 %9 %115 %131 %126
+               OpLoopMerge %125 %126 Unroll|MinIterations|MaxIterations|PartialCount 5 90 9
+               OpBranch %127
+        %127 = OpLabel
+        %129 = OpSLessThan %17 %143 %16
+               OpBranchConditional %129 %124 %125
+        %124 = OpLabel
+               OpBranch %126
+        %126 = OpLabel
+        %131 = OpIAdd %6 %143 %20
+               OpStore %122 %131
+               OpBranch %123
+        %125 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationSetLoopControlTest, CheckSPIRVVersionsRespected) {
+  // This test checks that we do not allow introducing PeelCount and
+  // PartialCount loop controls if the SPIR-V version being used does not
+  // support them.
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %8 "i"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 0
+         %16 = OpConstant %6 10
+         %17 = OpTypeBool
+         %20 = OpConstant %6 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+               OpStore %8 %9
+               OpBranch %10
+         %10 = OpLabel
+               OpLoopMerge %12 %13 None
+               OpBranch %14
+         %14 = OpLabel
+         %15 = OpLoad %6 %8
+         %18 = OpSLessThan %17 %15 %16
+               OpBranchConditional %18 %11 %12
+         %11 = OpLabel
+               OpBranch %13
+         %13 = OpLabel
+         %19 = OpLoad %6 %8
+         %21 = OpIAdd %6 %19 %20
+               OpStore %8 %21
+               OpBranch %10
+         %12 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto consumer = nullptr;
+  const auto context_1_0 =
+      BuildModule(SPV_ENV_UNIVERSAL_1_0, consumer, shader, kFuzzAssembleOption);
+  const auto context_1_1 =
+      BuildModule(SPV_ENV_UNIVERSAL_1_1, consumer, shader, kFuzzAssembleOption);
+  const auto context_1_2 =
+      BuildModule(SPV_ENV_UNIVERSAL_1_2, consumer, shader, kFuzzAssembleOption);
+  const auto context_1_3 =
+      BuildModule(SPV_ENV_UNIVERSAL_1_3, consumer, shader, kFuzzAssembleOption);
+  const auto context_1_4 =
+      BuildModule(SPV_ENV_UNIVERSAL_1_4, consumer, shader, kFuzzAssembleOption);
+  const auto context_1_5 =
+      BuildModule(SPV_ENV_UNIVERSAL_1_5, consumer, shader, kFuzzAssembleOption);
+
+  FactManager fact_manager;
+
+  TransformationSetLoopControl set_peel_and_partial(
+      10, SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask, 4, 4);
+
+  // PeelCount and PartialCount were introduced in SPIRV 1.4, so are not valid
+  // in the context of older versions.
+  ASSERT_FALSE(
+      set_peel_and_partial.IsApplicable(context_1_0.get(), fact_manager));
+  ASSERT_FALSE(
+      set_peel_and_partial.IsApplicable(context_1_1.get(), fact_manager));
+  ASSERT_FALSE(
+      set_peel_and_partial.IsApplicable(context_1_2.get(), fact_manager));
+  ASSERT_FALSE(
+      set_peel_and_partial.IsApplicable(context_1_3.get(), fact_manager));
+
+  ASSERT_TRUE(
+      set_peel_and_partial.IsApplicable(context_1_4.get(), fact_manager));
+  ASSERT_TRUE(
+      set_peel_and_partial.IsApplicable(context_1_5.get(), fact_manager));
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/test/fuzz/transformation_set_memory_operands_mask_test.cpp b/test/fuzz/transformation_set_memory_operands_mask_test.cpp
new file mode 100644
index 0000000..ad4dc25
--- /dev/null
+++ b/test/fuzz/transformation_set_memory_operands_mask_test.cpp
@@ -0,0 +1,432 @@
+// 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.
+
+#include "source/fuzz/transformation_set_memory_operands_mask.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationSetMemoryOperandsMaskTest, PreSpirv14) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %7 "Point3D"
+               OpMemberName %7 0 "x"
+               OpMemberName %7 1 "y"
+               OpMemberName %7 2 "z"
+               OpName %12 "global_points"
+               OpName %15 "block"
+               OpMemberName %15 0 "in_points"
+               OpMemberName %15 1 "in_point"
+               OpName %17 ""
+               OpName %133 "local_points"
+               OpMemberDecorate %7 0 Offset 0
+               OpMemberDecorate %7 1 Offset 4
+               OpMemberDecorate %7 2 Offset 8
+               OpDecorate %10 ArrayStride 16
+               OpMemberDecorate %15 0 Offset 0
+               OpMemberDecorate %15 1 Offset 192
+               OpDecorate %15 Block
+               OpDecorate %17 DescriptorSet 0
+               OpDecorate %17 Binding 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeStruct %6 %6 %6
+          %8 = OpTypeInt 32 0
+          %9 = OpConstant %8 12
+         %10 = OpTypeArray %7 %9
+         %11 = OpTypePointer Private %10
+         %12 = OpVariable %11 Private
+         %15 = OpTypeStruct %10 %7
+         %16 = OpTypePointer Uniform %15
+         %17 = OpVariable %16 Uniform
+         %18 = OpTypeInt 32 1
+         %19 = OpConstant %18 0
+         %20 = OpTypePointer Uniform %10
+         %24 = OpTypePointer Private %7
+         %27 = OpTypePointer Private %6
+         %30 = OpConstant %18 1
+        %132 = OpTypePointer Function %10
+        %135 = OpTypePointer Uniform %7
+        %145 = OpTypePointer Function %7
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+        %133 = OpVariable %132 Function
+         %21 = OpAccessChain %20 %17 %19
+               OpCopyMemory %12 %21 Aligned 16
+               OpCopyMemory %133 %12 Volatile
+        %136 = OpAccessChain %135 %17 %30
+        %138 = OpAccessChain %24 %12 %19
+               OpCopyMemory %138 %136 None
+        %146 = OpAccessChain %145 %133 %30
+        %147 = OpLoad %7 %146 Volatile|Nontemporal|Aligned 16
+        %148 = OpAccessChain %24 %12 %19
+               OpStore %148 %147 Nontemporal
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  // Not OK: the instruction is not a memory access.
+  ASSERT_FALSE(TransformationSetMemoryOperandsMask(
+                   MakeInstructionDescriptor(21, SpvOpAccessChain, 0),
+                   SpvMemoryAccessMaskNone, 0)
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Not OK to remove Aligned
+  ASSERT_FALSE(TransformationSetMemoryOperandsMask(
+                   MakeInstructionDescriptor(147, SpvOpLoad, 0),
+                   SpvMemoryAccessVolatileMask | SpvMemoryAccessNontemporalMask,
+                   0)
+                   .IsApplicable(context.get(), fact_manager));
+
+  TransformationSetMemoryOperandsMask transformation1(
+      MakeInstructionDescriptor(147, SpvOpLoad, 0),
+      SpvMemoryAccessAlignedMask | SpvMemoryAccessVolatileMask, 0);
+  ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager));
+  transformation1.Apply(context.get(), &fact_manager);
+
+  // Not OK to remove Aligned
+  ASSERT_FALSE(TransformationSetMemoryOperandsMask(
+                   MakeInstructionDescriptor(21, SpvOpCopyMemory, 0),
+                   SpvMemoryAccessMaskNone, 0)
+                   .IsApplicable(context.get(), fact_manager));
+
+  // OK: leaves the mask as is
+  ASSERT_TRUE(TransformationSetMemoryOperandsMask(
+                  MakeInstructionDescriptor(21, SpvOpCopyMemory, 0),
+                  SpvMemoryAccessAlignedMask, 0)
+                  .IsApplicable(context.get(), fact_manager));
+
+  // OK: adds Nontemporal and Volatile
+  TransformationSetMemoryOperandsMask transformation2(
+      MakeInstructionDescriptor(21, SpvOpCopyMemory, 0),
+      SpvMemoryAccessAlignedMask | SpvMemoryAccessNontemporalMask |
+          SpvMemoryAccessVolatileMask,
+      0);
+  ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager));
+  transformation2.Apply(context.get(), &fact_manager);
+
+  // Not OK to remove Volatile
+  ASSERT_FALSE(TransformationSetMemoryOperandsMask(
+                   MakeInstructionDescriptor(21, SpvOpCopyMemory, 1),
+                   SpvMemoryAccessNontemporalMask, 0)
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Not OK to add Aligned
+  ASSERT_FALSE(TransformationSetMemoryOperandsMask(
+                   MakeInstructionDescriptor(21, SpvOpCopyMemory, 1),
+                   SpvMemoryAccessAlignedMask | SpvMemoryAccessVolatileMask, 0)
+                   .IsApplicable(context.get(), fact_manager));
+
+  // OK: adds Nontemporal
+  TransformationSetMemoryOperandsMask transformation3(
+      MakeInstructionDescriptor(21, SpvOpCopyMemory, 1),
+      SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 0);
+  ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager));
+  transformation3.Apply(context.get(), &fact_manager);
+
+  // OK: adds Nontemporal and Volatile
+  TransformationSetMemoryOperandsMask transformation4(
+      MakeInstructionDescriptor(138, SpvOpCopyMemory, 0),
+      SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 0);
+  ASSERT_TRUE(transformation4.IsApplicable(context.get(), fact_manager));
+  transformation4.Apply(context.get(), &fact_manager);
+
+  // OK: removes Nontemporal, adds Volatile
+  TransformationSetMemoryOperandsMask transformation5(
+      MakeInstructionDescriptor(148, SpvOpStore, 0),
+      SpvMemoryAccessVolatileMask, 0);
+  ASSERT_TRUE(transformation5.IsApplicable(context.get(), fact_manager));
+  transformation5.Apply(context.get(), &fact_manager);
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %7 "Point3D"
+               OpMemberName %7 0 "x"
+               OpMemberName %7 1 "y"
+               OpMemberName %7 2 "z"
+               OpName %12 "global_points"
+               OpName %15 "block"
+               OpMemberName %15 0 "in_points"
+               OpMemberName %15 1 "in_point"
+               OpName %17 ""
+               OpName %133 "local_points"
+               OpMemberDecorate %7 0 Offset 0
+               OpMemberDecorate %7 1 Offset 4
+               OpMemberDecorate %7 2 Offset 8
+               OpDecorate %10 ArrayStride 16
+               OpMemberDecorate %15 0 Offset 0
+               OpMemberDecorate %15 1 Offset 192
+               OpDecorate %15 Block
+               OpDecorate %17 DescriptorSet 0
+               OpDecorate %17 Binding 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeStruct %6 %6 %6
+          %8 = OpTypeInt 32 0
+          %9 = OpConstant %8 12
+         %10 = OpTypeArray %7 %9
+         %11 = OpTypePointer Private %10
+         %12 = OpVariable %11 Private
+         %15 = OpTypeStruct %10 %7
+         %16 = OpTypePointer Uniform %15
+         %17 = OpVariable %16 Uniform
+         %18 = OpTypeInt 32 1
+         %19 = OpConstant %18 0
+         %20 = OpTypePointer Uniform %10
+         %24 = OpTypePointer Private %7
+         %27 = OpTypePointer Private %6
+         %30 = OpConstant %18 1
+        %132 = OpTypePointer Function %10
+        %135 = OpTypePointer Uniform %7
+        %145 = OpTypePointer Function %7
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+        %133 = OpVariable %132 Function
+         %21 = OpAccessChain %20 %17 %19
+               OpCopyMemory %12 %21 Aligned|Nontemporal|Volatile 16
+               OpCopyMemory %133 %12 Nontemporal|Volatile
+        %136 = OpAccessChain %135 %17 %30
+        %138 = OpAccessChain %24 %12 %19
+               OpCopyMemory %138 %136 Nontemporal|Volatile
+        %146 = OpAccessChain %145 %133 %30
+        %147 = OpLoad %7 %146 Aligned|Volatile 16
+        %148 = OpAccessChain %24 %12 %19
+               OpStore %148 %147 Volatile
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationSetMemoryOperandsMaskTest, Spirv14) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %12 %17
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %7 "Point3D"
+               OpMemberName %7 0 "x"
+               OpMemberName %7 1 "y"
+               OpMemberName %7 2 "z"
+               OpName %12 "global_points"
+               OpName %15 "block"
+               OpMemberName %15 0 "in_points"
+               OpMemberName %15 1 "in_point"
+               OpName %17 ""
+               OpName %133 "local_points"
+               OpMemberDecorate %7 0 Offset 0
+               OpMemberDecorate %7 1 Offset 4
+               OpMemberDecorate %7 2 Offset 8
+               OpDecorate %10 ArrayStride 16
+               OpMemberDecorate %15 0 Offset 0
+               OpMemberDecorate %15 1 Offset 192
+               OpDecorate %15 Block
+               OpDecorate %17 DescriptorSet 0
+               OpDecorate %17 Binding 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeStruct %6 %6 %6
+          %8 = OpTypeInt 32 0
+          %9 = OpConstant %8 12
+         %10 = OpTypeArray %7 %9
+         %11 = OpTypePointer Private %10
+         %12 = OpVariable %11 Private
+         %15 = OpTypeStruct %10 %7
+         %16 = OpTypePointer Uniform %15
+         %17 = OpVariable %16 Uniform
+         %18 = OpTypeInt 32 1
+         %19 = OpConstant %18 0
+         %20 = OpTypePointer Uniform %10
+         %24 = OpTypePointer Private %7
+         %27 = OpTypePointer Private %6
+         %30 = OpConstant %18 1
+        %132 = OpTypePointer Function %10
+        %135 = OpTypePointer Uniform %7
+        %145 = OpTypePointer Function %7
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+        %133 = OpVariable %132 Function
+         %21 = OpAccessChain %20 %17 %19
+               OpCopyMemory %12 %21 Aligned 16 Nontemporal|Aligned 16
+               OpCopyMemory %133 %12 Volatile
+        %136 = OpAccessChain %135 %17 %30
+        %138 = OpAccessChain %24 %12 %19
+               OpCopyMemory %138 %136 None Aligned 16
+               OpCopyMemory %138 %136 Aligned 16
+        %146 = OpAccessChain %145 %133 %30
+        %147 = OpLoad %7 %146 Volatile|Nontemporal|Aligned 16
+        %148 = OpAccessChain %24 %12 %19
+               OpStore %148 %147 Nontemporal
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  TransformationSetMemoryOperandsMask transformation1(
+      MakeInstructionDescriptor(21, SpvOpCopyMemory, 0),
+      SpvMemoryAccessAlignedMask | SpvMemoryAccessVolatileMask, 1);
+  // Bad: cannot remove aligned
+  ASSERT_FALSE(TransformationSetMemoryOperandsMask(
+                   MakeInstructionDescriptor(21, SpvOpCopyMemory, 0),
+                   SpvMemoryAccessVolatileMask, 1)
+                   .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager));
+  transformation1.Apply(context.get(), &fact_manager);
+
+  TransformationSetMemoryOperandsMask transformation2(
+      MakeInstructionDescriptor(21, SpvOpCopyMemory, 1),
+      SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 1);
+  // Bad: cannot remove volatile
+  ASSERT_FALSE(TransformationSetMemoryOperandsMask(
+                   MakeInstructionDescriptor(21, SpvOpCopyMemory, 1),
+                   SpvMemoryAccessNontemporalMask, 0)
+                   .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager));
+  transformation2.Apply(context.get(), &fact_manager);
+
+  TransformationSetMemoryOperandsMask transformation3(
+      MakeInstructionDescriptor(138, SpvOpCopyMemory, 0),
+      SpvMemoryAccessAlignedMask | SpvMemoryAccessNontemporalMask, 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(), fact_manager));
+  ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager));
+  transformation3.Apply(context.get(), &fact_manager);
+
+  TransformationSetMemoryOperandsMask transformation4(
+      MakeInstructionDescriptor(138, SpvOpCopyMemory, 1),
+      SpvMemoryAccessVolatileMask, 1);
+  ASSERT_TRUE(transformation4.IsApplicable(context.get(), fact_manager));
+  transformation4.Apply(context.get(), &fact_manager);
+
+  TransformationSetMemoryOperandsMask transformation5(
+      MakeInstructionDescriptor(147, SpvOpLoad, 0),
+      SpvMemoryAccessVolatileMask | SpvMemoryAccessAlignedMask, 0);
+  ASSERT_TRUE(transformation5.IsApplicable(context.get(), fact_manager));
+  transformation5.Apply(context.get(), &fact_manager);
+
+  TransformationSetMemoryOperandsMask transformation6(
+      MakeInstructionDescriptor(148, SpvOpStore, 0), SpvMemoryAccessMaskNone,
+      0);
+  ASSERT_TRUE(transformation6.IsApplicable(context.get(), fact_manager));
+  transformation6.Apply(context.get(), &fact_manager);
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %12 %17
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %7 "Point3D"
+               OpMemberName %7 0 "x"
+               OpMemberName %7 1 "y"
+               OpMemberName %7 2 "z"
+               OpName %12 "global_points"
+               OpName %15 "block"
+               OpMemberName %15 0 "in_points"
+               OpMemberName %15 1 "in_point"
+               OpName %17 ""
+               OpName %133 "local_points"
+               OpMemberDecorate %7 0 Offset 0
+               OpMemberDecorate %7 1 Offset 4
+               OpMemberDecorate %7 2 Offset 8
+               OpDecorate %10 ArrayStride 16
+               OpMemberDecorate %15 0 Offset 0
+               OpMemberDecorate %15 1 Offset 192
+               OpDecorate %15 Block
+               OpDecorate %17 DescriptorSet 0
+               OpDecorate %17 Binding 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeStruct %6 %6 %6
+          %8 = OpTypeInt 32 0
+          %9 = OpConstant %8 12
+         %10 = OpTypeArray %7 %9
+         %11 = OpTypePointer Private %10
+         %12 = OpVariable %11 Private
+         %15 = OpTypeStruct %10 %7
+         %16 = OpTypePointer Uniform %15
+         %17 = OpVariable %16 Uniform
+         %18 = OpTypeInt 32 1
+         %19 = OpConstant %18 0
+         %20 = OpTypePointer Uniform %10
+         %24 = OpTypePointer Private %7
+         %27 = OpTypePointer Private %6
+         %30 = OpConstant %18 1
+        %132 = OpTypePointer Function %10
+        %135 = OpTypePointer Uniform %7
+        %145 = OpTypePointer Function %7
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+        %133 = OpVariable %132 Function
+         %21 = OpAccessChain %20 %17 %19
+               OpCopyMemory %12 %21 Aligned 16 Aligned|Volatile 16
+               OpCopyMemory %133 %12 Volatile Nontemporal|Volatile
+        %136 = OpAccessChain %135 %17 %30
+        %138 = OpAccessChain %24 %12 %19
+               OpCopyMemory %138 %136 None Aligned|Nontemporal 16
+               OpCopyMemory %138 %136 Aligned 16 Volatile
+        %146 = OpAccessChain %145 %133 %30
+        %147 = OpLoad %7 %146 Volatile|Aligned 16
+        %148 = OpAccessChain %24 %12 %19
+               OpStore %148 %147 None
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/test/fuzz/transformation_set_selection_control_test.cpp b/test/fuzz/transformation_set_selection_control_test.cpp
new file mode 100644
index 0000000..9696417
--- /dev/null
+++ b/test/fuzz/transformation_set_selection_control_test.cpp
@@ -0,0 +1,219 @@
+// 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.
+
+#include "source/fuzz/transformation_set_selection_control.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationSetSelectionControlTest, VariousScenarios) {
+  // This is a simple transformation; this test captures the important things
+  // to check for.
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %8 "i"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 0
+         %16 = OpConstant %6 10
+         %17 = OpTypeBool
+         %20 = OpConstant %6 3
+         %25 = OpConstant %6 1
+         %28 = OpConstant %6 2
+         %38 = OpConstant %6 4
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+               OpStore %8 %9
+               OpBranch %10
+         %10 = OpLabel
+               OpLoopMerge %12 %13 None
+               OpBranch %14
+         %14 = OpLabel
+         %15 = OpLoad %6 %8
+         %18 = OpSLessThan %17 %15 %16
+               OpBranchConditional %18 %11 %12
+         %11 = OpLabel
+         %19 = OpLoad %6 %8
+         %21 = OpSGreaterThan %17 %19 %20
+               OpSelectionMerge %23 Flatten
+               OpBranchConditional %21 %22 %23
+         %22 = OpLabel
+         %24 = OpLoad %6 %8
+         %26 = OpIAdd %6 %24 %25
+               OpStore %8 %26
+               OpBranch %23
+         %23 = OpLabel
+         %27 = OpLoad %6 %8
+         %29 = OpSLessThan %17 %27 %28
+               OpSelectionMerge %31 DontFlatten
+               OpBranchConditional %29 %30 %31
+         %30 = OpLabel
+         %32 = OpLoad %6 %8
+         %33 = OpISub %6 %32 %25
+               OpStore %8 %33
+               OpBranch %31
+         %31 = OpLabel
+         %34 = OpLoad %6 %8
+               OpSelectionMerge %37 None
+               OpSwitch %34 %36 0 %35
+         %36 = OpLabel
+               OpBranch %37
+         %35 = OpLabel
+         %39 = OpLoad %6 %8
+         %40 = OpIAdd %6 %39 %38
+               OpStore %8 %40
+               OpBranch %36
+         %37 = OpLabel
+               OpBranch %13
+         %13 = OpLabel
+         %43 = OpLoad %6 %8
+         %44 = OpIAdd %6 %43 %25
+               OpStore %8 %44
+               OpBranch %10
+         %12 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+
+  FactManager fact_manager;
+
+  // %44 is not a block
+  ASSERT_FALSE(
+      TransformationSetSelectionControl(44, SpvSelectionControlFlattenMask)
+          .IsApplicable(context.get(), fact_manager));
+  // %13 does not end with OpSelectionMerge
+  ASSERT_FALSE(
+      TransformationSetSelectionControl(13, SpvSelectionControlMaskNone)
+          .IsApplicable(context.get(), fact_manager));
+  // %10 ends in OpLoopMerge, not OpSelectionMerge
+  ASSERT_FALSE(
+      TransformationSetSelectionControl(10, SpvSelectionControlMaskNone)
+          .IsApplicable(context.get(), fact_manager));
+
+  TransformationSetSelectionControl transformation1(
+      11, SpvSelectionControlDontFlattenMask);
+  ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager));
+  transformation1.Apply(context.get(), &fact_manager);
+
+  TransformationSetSelectionControl transformation2(
+      23, SpvSelectionControlFlattenMask);
+  ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager));
+  transformation2.Apply(context.get(), &fact_manager);
+
+  TransformationSetSelectionControl transformation3(
+      31, SpvSelectionControlMaskNone);
+  ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager));
+  transformation3.Apply(context.get(), &fact_manager);
+
+  TransformationSetSelectionControl transformation4(
+      31, SpvSelectionControlFlattenMask);
+  ASSERT_TRUE(transformation4.IsApplicable(context.get(), fact_manager));
+  transformation4.Apply(context.get(), &fact_manager);
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %8 "i"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 0
+         %16 = OpConstant %6 10
+         %17 = OpTypeBool
+         %20 = OpConstant %6 3
+         %25 = OpConstant %6 1
+         %28 = OpConstant %6 2
+         %38 = OpConstant %6 4
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+               OpStore %8 %9
+               OpBranch %10
+         %10 = OpLabel
+               OpLoopMerge %12 %13 None
+               OpBranch %14
+         %14 = OpLabel
+         %15 = OpLoad %6 %8
+         %18 = OpSLessThan %17 %15 %16
+               OpBranchConditional %18 %11 %12
+         %11 = OpLabel
+         %19 = OpLoad %6 %8
+         %21 = OpSGreaterThan %17 %19 %20
+               OpSelectionMerge %23 DontFlatten
+               OpBranchConditional %21 %22 %23
+         %22 = OpLabel
+         %24 = OpLoad %6 %8
+         %26 = OpIAdd %6 %24 %25
+               OpStore %8 %26
+               OpBranch %23
+         %23 = OpLabel
+         %27 = OpLoad %6 %8
+         %29 = OpSLessThan %17 %27 %28
+               OpSelectionMerge %31 Flatten
+               OpBranchConditional %29 %30 %31
+         %30 = OpLabel
+         %32 = OpLoad %6 %8
+         %33 = OpISub %6 %32 %25
+               OpStore %8 %33
+               OpBranch %31
+         %31 = OpLabel
+         %34 = OpLoad %6 %8
+               OpSelectionMerge %37 Flatten
+               OpSwitch %34 %36 0 %35
+         %36 = OpLabel
+               OpBranch %37
+         %35 = OpLabel
+         %39 = OpLoad %6 %8
+         %40 = OpIAdd %6 %39 %38
+               OpStore %8 %40
+               OpBranch %36
+         %37 = OpLabel
+               OpBranch %13
+         %13 = OpLabel
+         %43 = OpLoad %6 %8
+         %44 = OpIAdd %6 %43 %25
+               OpStore %8 %44
+               OpBranch %10
+         %12 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/test/fuzz/transformation_split_block_test.cpp b/test/fuzz/transformation_split_block_test.cpp
index d33ccba..d162e07 100644
--- a/test/fuzz/transformation_split_block_test.cpp
+++ b/test/fuzz/transformation_split_block_test.cpp
@@ -13,6 +13,7 @@
 // limitations under the License.
 
 #include "source/fuzz/transformation_split_block.h"
+#include "source/fuzz/instruction_descriptor.h"
 #include "test/fuzz/fuzz_test_util.h"
 
 namespace spvtools {
@@ -90,44 +91,55 @@
   FactManager fact_manager;
 
   // No split before OpVariable
-  ASSERT_FALSE(TransformationSplitBlock(8, 0, 100).IsApplicable(context.get(),
-                                                                fact_manager));
-  ASSERT_FALSE(TransformationSplitBlock(8, 1, 100).IsApplicable(context.get(),
-                                                                fact_manager));
+  ASSERT_FALSE(TransformationSplitBlock(
+                   MakeInstructionDescriptor(8, SpvOpVariable, 0), 100)
+                   .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(TransformationSplitBlock(
+                   MakeInstructionDescriptor(8, SpvOpVariable, 1), 100)
+                   .IsApplicable(context.get(), fact_manager));
 
   // No split before OpLabel
-  ASSERT_FALSE(TransformationSplitBlock(14, 0, 100)
+  ASSERT_FALSE(TransformationSplitBlock(
+                   MakeInstructionDescriptor(14, SpvOpLabel, 0), 100)
                    .IsApplicable(context.get(), fact_manager));
 
   // No split if base instruction is outside a function
-  ASSERT_FALSE(TransformationSplitBlock(1, 0, 100).IsApplicable(context.get(),
-                                                                fact_manager));
-  ASSERT_FALSE(TransformationSplitBlock(1, 4, 100).IsApplicable(context.get(),
-                                                                fact_manager));
-  ASSERT_FALSE(TransformationSplitBlock(1, 35, 100)
+  ASSERT_FALSE(
+      TransformationSplitBlock(MakeInstructionDescriptor(1, SpvOpLabel, 0), 100)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(TransformationSplitBlock(
+                   MakeInstructionDescriptor(1, SpvOpExecutionMode, 0), 100)
                    .IsApplicable(context.get(), fact_manager));
 
   // No split if block is loop header
-  ASSERT_FALSE(TransformationSplitBlock(27, 0, 100)
-                   .IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(TransformationSplitBlock(27, 1, 100)
-                   .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationSplitBlock(MakeInstructionDescriptor(27, SpvOpPhi, 0), 100)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationSplitBlock(MakeInstructionDescriptor(27, SpvOpPhi, 1), 100)
+          .IsApplicable(context.get(), fact_manager));
 
   // No split if base instruction does not exist
-  ASSERT_FALSE(TransformationSplitBlock(88, 0, 100)
-                   .IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(TransformationSplitBlock(88, 22, 100)
+  ASSERT_FALSE(
+      TransformationSplitBlock(MakeInstructionDescriptor(88, SpvOpIAdd, 0), 100)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(TransformationSplitBlock(
+                   MakeInstructionDescriptor(88, SpvOpIMul, 22), 100)
                    .IsApplicable(context.get(), fact_manager));
 
-  // No split if offset is too large (goes into another block)
-  ASSERT_FALSE(TransformationSplitBlock(18, 3, 100)
-                   .IsApplicable(context.get(), fact_manager));
+  // No split if too many instructions with the desired opcode are skipped
+  ASSERT_FALSE(
+      TransformationSplitBlock(
+          MakeInstructionDescriptor(18, SpvOpBranchConditional, 1), 100)
+          .IsApplicable(context.get(), fact_manager));
 
   // No split if id in use
-  ASSERT_FALSE(TransformationSplitBlock(18, 0, 27).IsApplicable(context.get(),
-                                                                fact_manager));
-  ASSERT_FALSE(TransformationSplitBlock(18, 0, 14).IsApplicable(context.get(),
-                                                                fact_manager));
+  ASSERT_FALSE(TransformationSplitBlock(
+                   MakeInstructionDescriptor(18, SpvOpSLessThan, 0), 27)
+                   .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(TransformationSplitBlock(
+                   MakeInstructionDescriptor(18, SpvOpSLessThan, 0), 14)
+                   .IsApplicable(context.get(), fact_manager));
 }
 
 TEST(TransformationSplitBlockTest, SplitBlockSeveralTimes) {
@@ -188,7 +200,8 @@
 
   FactManager fact_manager;
 
-  auto split_1 = TransformationSplitBlock(5, 3, 100);
+  auto split_1 = TransformationSplitBlock(
+      MakeInstructionDescriptor(5, SpvOpStore, 0), 100);
   ASSERT_TRUE(split_1.IsApplicable(context.get(), fact_manager));
   split_1.Apply(context.get(), &fact_manager);
   ASSERT_TRUE(IsValid(env, context.get()));
@@ -235,7 +248,8 @@
   )";
   ASSERT_TRUE(IsEqual(env, after_split_1, context.get()));
 
-  auto split_2 = TransformationSplitBlock(11, 1, 101);
+  auto split_2 = TransformationSplitBlock(
+      MakeInstructionDescriptor(11, SpvOpStore, 0), 101);
   ASSERT_TRUE(split_2.IsApplicable(context.get(), fact_manager));
   split_2.Apply(context.get(), &fact_manager);
   ASSERT_TRUE(IsValid(env, context.get()));
@@ -284,7 +298,8 @@
   )";
   ASSERT_TRUE(IsEqual(env, after_split_2, context.get()));
 
-  auto split_3 = TransformationSplitBlock(14, 0, 102);
+  auto split_3 = TransformationSplitBlock(
+      MakeInstructionDescriptor(14, SpvOpLoad, 0), 102);
   ASSERT_TRUE(split_3.IsApplicable(context.get(), fact_manager));
   split_3.Apply(context.get(), &fact_manager);
   ASSERT_TRUE(IsValid(env, context.get()));
@@ -399,12 +414,17 @@
   FactManager fact_manager;
 
   // Illegal to split between the merge and the conditional branch.
-  ASSERT_FALSE(TransformationSplitBlock(14, 2, 100)
-                   .IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(TransformationSplitBlock(12, 3, 100)
-                   .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationSplitBlock(
+          MakeInstructionDescriptor(14, SpvOpBranchConditional, 0), 100)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationSplitBlock(
+          MakeInstructionDescriptor(12, SpvOpBranchConditional, 0), 100)
+          .IsApplicable(context.get(), fact_manager));
 
-  auto split = TransformationSplitBlock(14, 1, 100);
+  auto split = TransformationSplitBlock(
+      MakeInstructionDescriptor(14, SpvOpSelectionMerge, 0), 100);
   ASSERT_TRUE(split.IsApplicable(context.get(), fact_manager));
   split.Apply(context.get(), &fact_manager);
   ASSERT_TRUE(IsValid(env, context.get()));
@@ -523,12 +543,15 @@
   FactManager fact_manager;
 
   // Illegal to split between the merge and the conditional branch.
-  ASSERT_FALSE(TransformationSplitBlock(9, 2, 100).IsApplicable(context.get(),
-                                                                fact_manager));
-  ASSERT_FALSE(TransformationSplitBlock(15, 3, 100)
+  ASSERT_FALSE(TransformationSplitBlock(
+                   MakeInstructionDescriptor(9, SpvOpSwitch, 0), 100)
+                   .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(TransformationSplitBlock(
+                   MakeInstructionDescriptor(15, SpvOpSwitch, 0), 100)
                    .IsApplicable(context.get(), fact_manager));
 
-  auto split = TransformationSplitBlock(9, 1, 100);
+  auto split = TransformationSplitBlock(
+      MakeInstructionDescriptor(9, SpvOpSelectionMerge, 0), 100);
   ASSERT_TRUE(split.IsApplicable(context.get(), fact_manager));
   split.Apply(context.get(), &fact_manager);
   ASSERT_TRUE(IsValid(env, context.get()));
@@ -654,12 +677,15 @@
 
   // We cannot split before OpPhi instructions, since the number of incoming
   // blocks may not appropriately match after splitting.
-  ASSERT_FALSE(TransformationSplitBlock(26, 0, 100)
-                   .IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(TransformationSplitBlock(27, 0, 100)
-                   .IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(TransformationSplitBlock(27, 1, 100)
-                   .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationSplitBlock(MakeInstructionDescriptor(26, SpvOpPhi, 0), 100)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationSplitBlock(MakeInstructionDescriptor(27, SpvOpPhi, 0), 100)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationSplitBlock(MakeInstructionDescriptor(27, SpvOpPhi, 1), 100)
+          .IsApplicable(context.get(), fact_manager));
 }
 
 TEST(TransformationSplitBlockTest, SplitOpPhiWithSinglePredecessor) {
@@ -687,12 +713,12 @@
          %10 = OpVariable %7 Function
                OpStore %8 %9
          %11 = OpLoad %6 %8
-	       OpBranch %20
-	 %20 = OpLabel
-	 %21 = OpPhi %6 %11 %5
-         OpStore %10 %21
-         OpReturn
-         OpFunctionEnd
+               OpBranch %20
+         %20 = OpLabel
+         %21 = OpPhi %6 %11 %5
+               OpStore %10 %21
+               OpReturn
+               OpFunctionEnd
   )";
 
   const auto env = SPV_ENV_UNIVERSAL_1_3;
@@ -701,9 +727,13 @@
 
   FactManager fact_manager;
 
-  ASSERT_TRUE(TransformationSplitBlock(21, 0, 100)
-                  .IsApplicable(context.get(), fact_manager));
-  auto split = TransformationSplitBlock(20, 1, 100);
+  ASSERT_TRUE(
+      TransformationSplitBlock(MakeInstructionDescriptor(21, SpvOpPhi, 0), 100)
+          .IsApplicable(context.get(), fact_manager));
+  // An equivalent transformation to the above, just described with respect to a
+  // different base instruction.
+  auto split =
+      TransformationSplitBlock(MakeInstructionDescriptor(20, SpvOpPhi, 0), 100);
   ASSERT_TRUE(split.IsApplicable(context.get(), fact_manager));
   split.Apply(context.get(), &fact_manager);
   ASSERT_TRUE(IsValid(env, context.get()));
@@ -732,14 +762,14 @@
          %10 = OpVariable %7 Function
                OpStore %8 %9
          %11 = OpLoad %6 %8
-	       OpBranch %20
-	 %20 = OpLabel
-         OpBranch %100
-  %100 = OpLabel
-	 %21 = OpPhi %6 %11 %20
-         OpStore %10 %21
-         OpReturn
-         OpFunctionEnd
+               OpBranch %20
+         %20 = OpLabel
+               OpBranch %100
+        %100 = OpLabel
+         %21 = OpPhi %6 %11 %20
+               OpStore %10 %21
+               OpReturn
+               OpFunctionEnd
   )";
   ASSERT_TRUE(IsEqual(env, after_split, context.get()));
 }
diff --git a/test/fuzz/transformation_vector_shuffle_test.cpp b/test/fuzz/transformation_vector_shuffle_test.cpp
new file mode 100644
index 0000000..385c38b
--- /dev/null
+++ b/test/fuzz/transformation_vector_shuffle_test.cpp
@@ -0,0 +1,532 @@
+// 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.
+
+#include "source/fuzz/transformation_vector_shuffle.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationVectorShuffle, BasicTest) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool
+          %7 = OpTypeVector %6 2
+         %10 = OpConstantTrue %6
+         %11 = OpConstantFalse %6
+         %12 = OpConstantComposite %7 %10 %11
+        %112 = OpUndef %7
+         %13 = OpTypeVector %6 3
+         %16 = OpConstantComposite %13 %10 %11 %10
+         %17 = OpTypeVector %6 4
+         %20 = OpConstantComposite %17 %10 %11 %10 %11
+         %21 = OpTypeInt 32 1
+         %22 = OpTypeVector %21 2
+         %25 = OpConstant %21 1
+         %26 = OpConstant %21 0
+         %27 = OpConstantComposite %22 %25 %26
+         %28 = OpTypeVector %21 3
+         %31 = OpConstantComposite %28 %25 %26 %25
+         %32 = OpTypeVector %21 4
+         %33 = OpTypePointer Function %32
+         %35 = OpConstantComposite %32 %25 %26 %25 %26
+         %36 = OpTypeInt 32 0
+         %37 = OpTypeVector %36 2
+         %40 = OpConstant %36 1
+         %41 = OpConstant %36 0
+         %42 = OpConstantComposite %37 %40 %41
+         %43 = OpTypeVector %36 3
+         %46 = OpConstantComposite %43 %40 %41 %40
+         %47 = OpTypeVector %36 4
+         %50 = OpConstantComposite %47 %40 %41 %40 %41
+         %51 = OpTypeFloat 32
+         %55 = OpConstant %51 1
+         %56 = OpConstant %51 0
+         %58 = OpTypeVector %51 3
+         %61 = OpConstantComposite %58 %55 %56 %55
+         %62 = OpTypeVector %51 4
+         %65 = OpConstantComposite %62 %55 %56 %55 %56
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpSelectionMerge %100 None
+               OpBranchConditional %10 %101 %102
+        %101 = OpLabel
+        %103 = OpCompositeConstruct %62 %55 %55 %55 %56
+               OpBranch %100
+        %102 = OpLabel
+               OpBranch %100
+        %100 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(10, {}),
+                                  MakeDataDescriptor(12, {0}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(11, {}),
+                                  MakeDataDescriptor(12, {1}), context.get());
+
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(10, {}),
+                                  MakeDataDescriptor(16, {0}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(11, {}),
+                                  MakeDataDescriptor(16, {1}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(10, {}),
+                                  MakeDataDescriptor(16, {2}), context.get());
+
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(10, {}),
+                                  MakeDataDescriptor(20, {0}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(11, {}),
+                                  MakeDataDescriptor(20, {1}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(10, {}),
+                                  MakeDataDescriptor(20, {2}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(11, {}),
+                                  MakeDataDescriptor(20, {3}), context.get());
+
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(25, {}),
+                                  MakeDataDescriptor(27, {0}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(26, {}),
+                                  MakeDataDescriptor(27, {1}), context.get());
+
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(25, {}),
+                                  MakeDataDescriptor(31, {0}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(26, {}),
+                                  MakeDataDescriptor(31, {1}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(25, {}),
+                                  MakeDataDescriptor(31, {2}), context.get());
+
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(25, {}),
+                                  MakeDataDescriptor(35, {0}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(26, {}),
+                                  MakeDataDescriptor(35, {1}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(25, {}),
+                                  MakeDataDescriptor(35, {2}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(26, {}),
+                                  MakeDataDescriptor(35, {3}), context.get());
+
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(40, {}),
+                                  MakeDataDescriptor(42, {0}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(41, {}),
+                                  MakeDataDescriptor(42, {1}), context.get());
+
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(40, {}),
+                                  MakeDataDescriptor(46, {0}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(41, {}),
+                                  MakeDataDescriptor(46, {1}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(40, {}),
+                                  MakeDataDescriptor(46, {2}), context.get());
+
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(40, {}),
+                                  MakeDataDescriptor(50, {0}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(41, {}),
+                                  MakeDataDescriptor(50, {1}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(40, {}),
+                                  MakeDataDescriptor(50, {2}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(41, {}),
+                                  MakeDataDescriptor(50, {3}), context.get());
+
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(55, {}),
+                                  MakeDataDescriptor(61, {0}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(56, {}),
+                                  MakeDataDescriptor(61, {1}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(55, {}),
+                                  MakeDataDescriptor(61, {2}), context.get());
+
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(55, {}),
+                                  MakeDataDescriptor(65, {0}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(56, {}),
+                                  MakeDataDescriptor(65, {1}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(55, {}),
+                                  MakeDataDescriptor(65, {2}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(56, {}),
+                                  MakeDataDescriptor(65, {3}), context.get());
+
+  // %103 does not dominate the return instruction.
+  ASSERT_FALSE(TransformationVectorShuffle(
+                   MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 103, 65,
+                   {3, 5, 7})
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Illegal to shuffle a bvec2 and a vec3
+  ASSERT_FALSE(TransformationVectorShuffle(
+                   MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 112, 61,
+                   {0, 2, 4})
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Illegal to shuffle an ivec2 and a uvec4
+  ASSERT_FALSE(TransformationVectorShuffle(
+                   MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 27, 50,
+                   {1, 3, 5})
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Vector 1 does not exist
+  ASSERT_FALSE(TransformationVectorShuffle(
+                   MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 300, 50,
+                   {1, 3, 5})
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Vector 2 does not exist
+  ASSERT_FALSE(TransformationVectorShuffle(
+                   MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 27, 300,
+                   {1, 3, 5})
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Index out of range
+  ASSERT_FALSE(
+      TransformationVectorShuffle(
+          MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 12, 112, {0, 20})
+          .IsApplicable(context.get(), fact_manager));
+
+  // Too many indices
+  ASSERT_FALSE(TransformationVectorShuffle(
+                   MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 12, 112,
+                   {0, 1, 0, 1, 0, 1, 0, 1})
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Too few indices
+  ASSERT_FALSE(
+      TransformationVectorShuffle(
+          MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 12, 112, {})
+          .IsApplicable(context.get(), fact_manager));
+
+  // Too few indices again
+  ASSERT_FALSE(
+      TransformationVectorShuffle(
+          MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 12, 112, {0})
+          .IsApplicable(context.get(), fact_manager));
+
+  // Indices define unknown type: we do not have vec2
+  ASSERT_FALSE(
+      TransformationVectorShuffle(
+          MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 65, 65, {0, 1})
+          .IsApplicable(context.get(), fact_manager));
+
+  // The instruction to insert before does not exist
+  ASSERT_FALSE(TransformationVectorShuffle(
+                   MakeInstructionDescriptor(100, SpvOpCompositeConstruct, 1),
+                   201, 20, 12, {0xFFFFFFFF, 3, 5})
+                   .IsApplicable(context.get(), fact_manager));
+
+  // The 'fresh' id is already in use
+  ASSERT_FALSE(
+      TransformationVectorShuffle(
+          MakeInstructionDescriptor(100, SpvOpReturn, 0), 12, 12, 112, {})
+          .IsApplicable(context.get(), fact_manager));
+
+  protobufs::DataDescriptor temp_dd;
+
+  TransformationVectorShuffle transformation1(
+      MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 12, 112, {1, 0});
+  ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager));
+  transformation1.Apply(context.get(), &fact_manager);
+  temp_dd = MakeDataDescriptor(200, {0});
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(11, {}), temp_dd,
+                                        context.get()));
+  temp_dd = MakeDataDescriptor(200, {1});
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(10, {}), temp_dd,
+                                        context.get()));
+
+  TransformationVectorShuffle transformation2(
+      MakeInstructionDescriptor(100, SpvOpReturn, 0), 201, 20, 12,
+      {0xFFFFFFFF, 3, 5});
+  ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager));
+  transformation2.Apply(context.get(), &fact_manager);
+  temp_dd = MakeDataDescriptor(201, {1});
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(11, {}), temp_dd,
+                                        context.get()));
+  temp_dd = MakeDataDescriptor(201, {2});
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(11, {}), temp_dd,
+                                        context.get()));
+
+  TransformationVectorShuffle transformation3(
+      MakeInstructionDescriptor(100, SpvOpReturn, 0), 202, 27, 35, {5, 4, 1});
+  ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager));
+  transformation3.Apply(context.get(), &fact_manager);
+  temp_dd = MakeDataDescriptor(202, {0});
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(26, {}), temp_dd,
+                                        context.get()));
+  temp_dd = MakeDataDescriptor(202, {1});
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(25, {}), temp_dd,
+                                        context.get()));
+  temp_dd = MakeDataDescriptor(202, {2});
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(26, {}), temp_dd,
+                                        context.get()));
+
+  TransformationVectorShuffle transformation4(
+      MakeInstructionDescriptor(100, SpvOpReturn, 0), 203, 42, 46, {0, 1});
+  ASSERT_TRUE(transformation4.IsApplicable(context.get(), fact_manager));
+  transformation4.Apply(context.get(), &fact_manager);
+  temp_dd = MakeDataDescriptor(203, {0});
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {}), temp_dd,
+                                        context.get()));
+  temp_dd = MakeDataDescriptor(203, {1});
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(41, {}), temp_dd,
+                                        context.get()));
+
+  TransformationVectorShuffle transformation5(
+      MakeInstructionDescriptor(100, SpvOpReturn, 0), 204, 42, 46, {2, 3, 4});
+  ASSERT_TRUE(transformation5.IsApplicable(context.get(), fact_manager));
+  transformation5.Apply(context.get(), &fact_manager);
+  temp_dd = MakeDataDescriptor(204, {0});
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {}), temp_dd,
+                                        context.get()));
+  temp_dd = MakeDataDescriptor(204, {1});
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(41, {}), temp_dd,
+                                        context.get()));
+  temp_dd = MakeDataDescriptor(204, {2});
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {}), temp_dd,
+                                        context.get()));
+
+  TransformationVectorShuffle transformation6(
+      MakeInstructionDescriptor(100, SpvOpReturn, 0), 205, 42, 42,
+      {0, 1, 2, 3});
+  ASSERT_TRUE(transformation6.IsApplicable(context.get(), fact_manager));
+  transformation6.Apply(context.get(), &fact_manager);
+  temp_dd = MakeDataDescriptor(205, {0});
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {}), temp_dd,
+                                        context.get()));
+  temp_dd = MakeDataDescriptor(205, {1});
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(41, {}), temp_dd,
+                                        context.get()));
+  temp_dd = MakeDataDescriptor(205, {2});
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {}), temp_dd,
+                                        context.get()));
+  temp_dd = MakeDataDescriptor(205, {3});
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(41, {}), temp_dd,
+                                        context.get()));
+
+  // swizzle vec4 from vec4 and vec4 using some undefs
+  TransformationVectorShuffle transformation7(
+      MakeInstructionDescriptor(100, SpvOpReturn, 0), 206, 65, 65,
+      {0xFFFFFFFF, 3, 6, 0xFFFFFFFF});
+  ASSERT_TRUE(transformation7.IsApplicable(context.get(), fact_manager));
+  transformation7.Apply(context.get(), &fact_manager);
+  temp_dd = MakeDataDescriptor(206, {1});
+  ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(56, {}), temp_dd,
+                                        context.get()));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool
+          %7 = OpTypeVector %6 2
+         %10 = OpConstantTrue %6
+         %11 = OpConstantFalse %6
+         %12 = OpConstantComposite %7 %10 %11
+        %112 = OpUndef %7
+         %13 = OpTypeVector %6 3
+         %16 = OpConstantComposite %13 %10 %11 %10
+         %17 = OpTypeVector %6 4
+         %20 = OpConstantComposite %17 %10 %11 %10 %11
+         %21 = OpTypeInt 32 1
+         %22 = OpTypeVector %21 2
+         %25 = OpConstant %21 1
+         %26 = OpConstant %21 0
+         %27 = OpConstantComposite %22 %25 %26
+         %28 = OpTypeVector %21 3
+         %31 = OpConstantComposite %28 %25 %26 %25
+         %32 = OpTypeVector %21 4
+         %33 = OpTypePointer Function %32
+         %35 = OpConstantComposite %32 %25 %26 %25 %26
+         %36 = OpTypeInt 32 0
+         %37 = OpTypeVector %36 2
+         %40 = OpConstant %36 1
+         %41 = OpConstant %36 0
+         %42 = OpConstantComposite %37 %40 %41
+         %43 = OpTypeVector %36 3
+         %46 = OpConstantComposite %43 %40 %41 %40
+         %47 = OpTypeVector %36 4
+         %50 = OpConstantComposite %47 %40 %41 %40 %41
+         %51 = OpTypeFloat 32
+         %55 = OpConstant %51 1
+         %56 = OpConstant %51 0
+         %58 = OpTypeVector %51 3
+         %61 = OpConstantComposite %58 %55 %56 %55
+         %62 = OpTypeVector %51 4
+         %65 = OpConstantComposite %62 %55 %56 %55 %56
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpSelectionMerge %100 None
+               OpBranchConditional %10 %101 %102
+        %101 = OpLabel
+        %103 = OpCompositeConstruct %62 %55 %55 %55 %56
+               OpBranch %100
+        %102 = OpLabel
+               OpBranch %100
+        %100 = OpLabel
+        %200 = OpVectorShuffle %7 %12 %112 1 0
+        %201 = OpVectorShuffle %13 %20 %12 0xFFFFFFFF 3 5
+        %202 = OpVectorShuffle %28 %27 %35 5 4 1
+        %203 = OpVectorShuffle %37 %42 %46 0 1
+        %204 = OpVectorShuffle %43 %42 %46 2 3 4
+        %205 = OpVectorShuffle %47 %42 %42 0 1 2 3
+        %206 = OpVectorShuffle %62 %65 %65 0xFFFFFFFF 3 6 0xFFFFFFFF
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationVectorShuffleTest, IllegalInsertionPoints) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %51 %27
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %25 "buf"
+               OpMemberName %25 0 "value"
+               OpName %27 ""
+               OpName %51 "color"
+               OpMemberDecorate %25 0 Offset 0
+               OpDecorate %25 Block
+               OpDecorate %27 DescriptorSet 0
+               OpDecorate %27 Binding 0
+               OpDecorate %51 Location 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeVector %6 4
+        %150 = OpTypeVector %6 2
+         %10 = OpConstant %6 0.300000012
+         %11 = OpConstant %6 0.400000006
+         %12 = OpConstant %6 0.5
+         %13 = OpConstant %6 1
+         %14 = OpConstantComposite %7 %10 %11 %12 %13
+         %15 = OpTypeInt 32 1
+         %18 = OpConstant %15 0
+         %25 = OpTypeStruct %6
+         %26 = OpTypePointer Uniform %25
+         %27 = OpVariable %26 Uniform
+         %28 = OpTypePointer Uniform %6
+         %32 = OpTypeBool
+        %103 = OpConstantTrue %32
+         %34 = OpConstant %6 0.100000001
+         %48 = OpConstant %15 1
+         %50 = OpTypePointer Output %7
+         %51 = OpVariable %50 Output
+        %100 = OpTypePointer Function %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+        %101 = OpVariable %100 Function
+        %102 = OpVariable %100 Function
+               OpBranch %19
+         %19 = OpLabel
+         %60 = OpPhi %7 %14 %5 %58 %20
+         %59 = OpPhi %15 %18 %5 %49 %20
+         %29 = OpAccessChain %28 %27 %18
+         %30 = OpLoad %6 %29
+         %31 = OpConvertFToS %15 %30
+         %33 = OpSLessThan %32 %59 %31
+               OpLoopMerge %21 %20 None
+               OpBranchConditional %33 %20 %21
+         %20 = OpLabel
+         %39 = OpCompositeExtract %6 %60 0
+         %40 = OpFAdd %6 %39 %34
+         %55 = OpCompositeInsert %7 %40 %60 0
+         %44 = OpCompositeExtract %6 %60 1
+         %45 = OpFSub %6 %44 %34
+         %58 = OpCompositeInsert %7 %45 %55 1
+         %49 = OpIAdd %15 %59 %48
+               OpBranch %19
+         %21 = OpLabel
+               OpStore %51 %60
+               OpSelectionMerge %105 None
+               OpBranchConditional %103 %104 %105
+        %104 = OpLabel
+               OpBranch %105
+        %105 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  // Cannot insert before the OpVariables of a function.
+  ASSERT_FALSE(
+      TransformationVectorShuffle(
+          MakeInstructionDescriptor(101, SpvOpVariable, 0), 200, 14, 14, {0, 1})
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationVectorShuffle(
+          MakeInstructionDescriptor(101, SpvOpVariable, 1), 200, 14, 14, {1, 2})
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationVectorShuffle(
+          MakeInstructionDescriptor(102, SpvOpVariable, 0), 200, 14, 14, {1, 2})
+          .IsApplicable(context.get(), fact_manager));
+  // OK to insert right after the OpVariables.
+  ASSERT_FALSE(
+      TransformationVectorShuffle(
+          MakeInstructionDescriptor(102, SpvOpBranch, 1), 200, 14, 14, {1, 1})
+          .IsApplicable(context.get(), fact_manager));
+
+  // Cannot insert before the OpPhis of a block.
+  ASSERT_FALSE(
+      TransformationVectorShuffle(MakeInstructionDescriptor(60, SpvOpPhi, 0),
+                                  200, 14, 14, {2, 0})
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationVectorShuffle(MakeInstructionDescriptor(59, SpvOpPhi, 0),
+                                  200, 14, 14, {3, 0})
+          .IsApplicable(context.get(), fact_manager));
+  // OK to insert after the OpPhis.
+  ASSERT_TRUE(TransformationVectorShuffle(
+                  MakeInstructionDescriptor(59, SpvOpAccessChain, 0), 200, 14,
+                  14, {3, 4})
+                  .IsApplicable(context.get(), fact_manager));
+
+  // Cannot insert before OpLoopMerge
+  ASSERT_FALSE(TransformationVectorShuffle(
+                   MakeInstructionDescriptor(33, SpvOpBranchConditional, 0),
+                   200, 14, 14, {3})
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Cannot insert before OpSelectionMerge
+  ASSERT_FALSE(TransformationVectorShuffle(
+                   MakeInstructionDescriptor(21, SpvOpBranchConditional, 0),
+                   200, 14, 14, {2})
+                   .IsApplicable(context.get(), fact_manager));
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/test/fuzzers/BUILD.gn b/test/fuzzers/BUILD.gn
index c565e11..be1258a 100644
--- a/test/fuzzers/BUILD.gn
+++ b/test/fuzzers/BUILD.gn
@@ -33,7 +33,9 @@
     testonly = true
 
     deps = [
+      ":spvtools_as_fuzzer",
       ":spvtools_binary_parser_fuzzer",
+      ":spvtools_dis_fuzzer",
       ":spvtools_opt_legalization_fuzzer",
       ":spvtools_opt_performance_fuzzer",
       ":spvtools_opt_size_fuzzer",
@@ -66,12 +68,24 @@
   }
 }
 
+spvtools_fuzzer("spvtools_as_fuzzer_src") {
+  sources = [
+    "spvtools_as_fuzzer.cpp",
+  ]
+}
+
 spvtools_fuzzer("spvtools_binary_parser_fuzzer_src") {
   sources = [
     "spvtools_binary_parser_fuzzer.cpp",
   ]
 }
 
+spvtools_fuzzer("spvtools_dis_fuzzer_src") {
+  sources = [
+    "spvtools_dis_fuzzer.cpp",
+  ]
+}
+
 spvtools_fuzzer("spvtools_opt_performance_fuzzer_src") {
   sources = [
     "spvtools_opt_performance_fuzzer.cpp",
@@ -116,6 +130,15 @@
 }
 
 if (!build_with_chromium || use_fuzzing_engine) {
+  fuzzer_test("spvtools_as_fuzzer") {
+    sources = []
+    deps = [
+      ":spvtools_as_fuzzer_src",
+    ]
+    # Intentionally doesn't use the seed corpus, because it consumes
+    #  part of the input as not part of the file.
+  }
+
   fuzzer_test("spvtools_binary_parser_fuzzer") {
     sources = []
     deps = [
@@ -125,6 +148,15 @@
     #  part of the input as not part of the file.
   }
 
+  fuzzer_test("spvtools_dis_fuzzer") {
+    sources = []
+    deps = [
+      ":spvtools_dis_fuzzer_src",
+    ]
+    # Intentionally doesn't use the seed corpus, because it consumes
+    #  part of the input as not part of the file.
+  }
+
   fuzzer_test("spvtools_opt_performance_fuzzer") {
     sources = []
     deps = [
diff --git a/test/fuzzers/spvtools_as_fuzzer.cpp b/test/fuzzers/spvtools_as_fuzzer.cpp
new file mode 100644
index 0000000..1b1de00
--- /dev/null
+++ b/test/fuzzers/spvtools_as_fuzzer.cpp
@@ -0,0 +1,70 @@
+// Copyright (c) 2019 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <cstdint>
+#include <cstring>  // memcpy
+#include <vector>
+
+#include "source/spirv_target_env.h"
+#include "spirv-tools/libspirv.hpp"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  if (size < sizeof(spv_target_env) + 1) return 0;
+
+  const spv_context context =
+      spvContextCreate(*reinterpret_cast<const spv_target_env*>(data));
+  if (context == nullptr) return 0;
+
+  data += sizeof(spv_target_env);
+  size -= sizeof(spv_target_env);
+
+  std::vector<uint32_t> input;
+
+  std::vector<char> input_str;
+  size_t char_count = input.size() * sizeof(uint32_t) / sizeof(char);
+  input_str.resize(char_count);
+  memcpy(input_str.data(), input.data(), input.size() * sizeof(uint32_t));
+
+  spv_binary binary;
+  spv_diagnostic diagnostic = nullptr;
+  spvTextToBinaryWithOptions(context, input_str.data(), input_str.size(),
+                             SPV_TEXT_TO_BINARY_OPTION_NONE, &binary,
+                             &diagnostic);
+  if (diagnostic) {
+    spvDiagnosticPrint(diagnostic);
+    spvDiagnosticDestroy(diagnostic);
+    diagnostic = nullptr;
+  }
+
+  if (binary) {
+    spvBinaryDestroy(binary);
+    binary = nullptr;
+  }
+
+  spvTextToBinaryWithOptions(context, input_str.data(), input_str.size(),
+                             SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS,
+                             &binary, &diagnostic);
+  if (diagnostic) {
+    spvDiagnosticPrint(diagnostic);
+    spvDiagnosticDestroy(diagnostic);
+    diagnostic = nullptr;
+  }
+
+  if (binary) {
+    spvBinaryDestroy(binary);
+    binary = nullptr;
+  }
+
+  return 0;
+}
diff --git a/test/fuzzers/spvtools_dis_fuzzer.cpp b/test/fuzzers/spvtools_dis_fuzzer.cpp
new file mode 100644
index 0000000..ca9a52d
--- /dev/null
+++ b/test/fuzzers/spvtools_dis_fuzzer.cpp
@@ -0,0 +1,71 @@
+// Copyright (c) 2019 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <cstdint>
+#include <cstring>  // memcpy
+#include <vector>
+
+#include "source/spirv_target_env.h"
+#include "spirv-tools/libspirv.hpp"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  if (size < sizeof(spv_target_env) + 1) return 0;
+
+  const spv_context context =
+      spvContextCreate(*reinterpret_cast<const spv_target_env*>(data));
+  if (context == nullptr) return 0;
+
+  data += sizeof(spv_target_env);
+  size -= sizeof(spv_target_env);
+
+  std::vector<uint32_t> input;
+  input.resize(size >> 2);
+  size_t count = 0;
+  for (size_t i = 0; (i + 3) < size; i += 4) {
+    input[count++] = data[i] | (data[i + 1] << 8) | (data[i + 2] << 16) |
+                     (data[i + 3]) << 24;
+  }
+
+  std::vector<char> input_str;
+  size_t char_count = input.size() * sizeof(uint32_t) / sizeof(char);
+  input_str.resize(char_count);
+  memcpy(input_str.data(), input.data(), input.size() * sizeof(uint32_t));
+
+  spv_text text = nullptr;
+  spv_diagnostic diagnostic = nullptr;
+
+  for (uint32_t options = SPV_BINARY_TO_TEXT_OPTION_NONE;
+       options <
+       (SPV_BINARY_TO_TEXT_OPTION_PRINT | SPV_BINARY_TO_TEXT_OPTION_COLOR |
+        SPV_BINARY_TO_TEXT_OPTION_INDENT |
+        SPV_BINARY_TO_TEXT_OPTION_SHOW_BYTE_OFFSET |
+        SPV_BINARY_TO_TEXT_OPTION_NO_HEADER |
+        SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES);
+       options++) {
+    spvBinaryToText(context, input.data(), input.size(), options, &text,
+                    &diagnostic);
+    if (diagnostic) {
+      spvDiagnosticDestroy(diagnostic);
+      diagnostic = nullptr;
+    }
+
+    if (text) {
+      spvTextDestroy(text);
+      text = nullptr;
+    }
+  }
+
+  spvContextDestroy(context);
+  return 0;
+}
diff --git a/test/fuzzers/spvtools_opt_vulkantowebgpu_fuzzer.cpp b/test/fuzzers/spvtools_opt_vulkantowebgpu_fuzzer.cpp
index 0b2ecc3..9371c0d 100644
--- a/test/fuzzers/spvtools_opt_vulkantowebgpu_fuzzer.cpp
+++ b/test/fuzzers/spvtools_opt_vulkantowebgpu_fuzzer.cpp
@@ -18,7 +18,7 @@
 #include "spirv-tools/optimizer.hpp"
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  spvtools::Optimizer optimizer(SPV_ENV_WEBGPU_0);
+  spvtools::Optimizer optimizer(SPV_ENV_VULKAN_1_1);
   optimizer.SetMessageConsumer([](spv_message_level_t, const char*,
                                   const spv_position_t&, const char*) {});
 
diff --git a/test/fuzzers/spvtools_opt_webgputovulkan_fuzzer.cpp b/test/fuzzers/spvtools_opt_webgputovulkan_fuzzer.cpp
index 1e44857..78ddbb7 100644
--- a/test/fuzzers/spvtools_opt_webgputovulkan_fuzzer.cpp
+++ b/test/fuzzers/spvtools_opt_webgputovulkan_fuzzer.cpp
@@ -18,7 +18,7 @@
 #include "spirv-tools/optimizer.hpp"
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  spvtools::Optimizer optimizer(SPV_ENV_VULKAN_1_1);
+  spvtools::Optimizer optimizer(SPV_ENV_WEBGPU_0);
   optimizer.SetMessageConsumer([](spv_message_level_t, const char*,
                                   const spv_position_t&, const char*) {});
 
diff --git a/test/operand_capabilities_test.cpp b/test/operand_capabilities_test.cpp
index 5b06527..1195597 100644
--- a/test/operand_capabilities_test.cpp
+++ b/test/operand_capabilities_test.cpp
@@ -494,8 +494,8 @@
             CASE3(BUILT_IN, BuiltInPrimitiveId, Geometry, Tessellation,
                   RayTracingNV),
             CASE2(BUILT_IN, BuiltInInvocationId, Geometry, Tessellation),
-            CASE1(BUILT_IN, BuiltInLayer, Geometry),
-            CASE1(BUILT_IN, BuiltInViewportIndex, MultiViewport),  // Bug 15234
+            CASE2(BUILT_IN, BuiltInLayer, Geometry, ShaderViewportIndexLayerEXT),
+            CASE2(BUILT_IN, BuiltInViewportIndex, MultiViewport, ShaderViewportIndexLayerEXT),  // Bug 15234
             CASE1(BUILT_IN, BuiltInTessLevelOuter, Tessellation),
             CASE1(BUILT_IN, BuiltInTessLevelInner, Tessellation),
             CASE1(BUILT_IN, BuiltInTessCoord, Tessellation),
@@ -532,6 +532,18 @@
             // clang-format on
         })));
 
+INSTANTIATE_TEST_SUITE_P(
+    BuiltInV1_5, EnumCapabilityTest,
+    Combine(
+        Values(SPV_ENV_UNIVERSAL_1_5),
+        ValuesIn(std::vector<EnumCapabilityCase>{
+            // SPIR-V 1.5 adds new capabilities to enable these two builtins.
+            CASE3(BUILT_IN, BuiltInLayer, Geometry, ShaderLayer,
+                  ShaderViewportIndexLayerEXT),
+            CASE3(BUILT_IN, BuiltInViewportIndex, MultiViewport,
+                  ShaderViewportIndex, ShaderViewportIndexLayerEXT),
+        })));
+
 // See SPIR-V Section 3.22 Selection Control
 INSTANTIATE_TEST_SUITE_P(
     SelectionControl, EnumCapabilityTest,
diff --git a/test/opt/CMakeLists.txt b/test/opt/CMakeLists.txt
index 47ce41f..327f265 100644
--- a/test/opt/CMakeLists.txt
+++ b/test/opt/CMakeLists.txt
@@ -28,6 +28,7 @@
        compact_ids_test.cpp
        constants_test.cpp
        constant_manager_test.cpp
+       convert_relaxed_to_half_test.cpp
        copy_prop_array_test.cpp
        dead_branch_elim_test.cpp
        dead_insert_elim_test.cpp
@@ -80,6 +81,7 @@
        reduce_load_size_test.cpp
        redundancy_elimination_test.cpp
        register_liveness.cpp
+       relax_float_ops_test.cpp
        replace_invalid_opc_test.cpp
        scalar_analysis.cpp
        scalar_replacement_test.cpp
diff --git a/test/opt/aggressive_dead_code_elim_test.cpp b/test/opt/aggressive_dead_code_elim_test.cpp
index 3a7fc27..9e5197d 100644
--- a/test/opt/aggressive_dead_code_elim_test.cpp
+++ b/test/opt/aggressive_dead_code_elim_test.cpp
@@ -6645,11 +6645,11 @@
 
   const std::string predefs1 =
       R"(OpCapability Shader
-OpCapability PhysicalStorageBufferAddressesEXT
+OpCapability PhysicalStorageBufferAddresses
 OpExtension "SPV_EXT_physical_storage_buffer"
 OpExtension "SPV_KHR_storage_buffer_storage_class"
 %1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
 OpEntryPoint GLCompute %main "main"
 OpExecutionMode %main LocalSize 1 1 1
 OpSource GLSL 450
@@ -6668,7 +6668,7 @@
 OpMemberDecorate %blockType 0 Offset 0
 OpMemberDecorate %blockType 1 Offset 8
 OpDecorate %blockType Block
-OpDecorate %b AliasedPointerEXT
+OpDecorate %b AliasedPointer
 OpMemberDecorate %rootBlock 0 Offset 0
 OpDecorate %rootBlock Block
 OpDecorate %r DescriptorSet 0
@@ -6695,50 +6695,50 @@
   const std::string predefs2_before =
       R"(%void = OpTypeVoid
 %3 = OpTypeFunction %void
-OpTypeForwardPointer %_ptr_PhysicalStorageBufferEXT_blockType PhysicalStorageBufferEXT
+OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_blockType PhysicalStorageBuffer
 %int = OpTypeInt 32 1
-%blockType = OpTypeStruct %int %_ptr_PhysicalStorageBufferEXT_blockType
-%_ptr_PhysicalStorageBufferEXT_blockType = OpTypePointer PhysicalStorageBufferEXT %blockType
-%_ptr_Function__ptr_PhysicalStorageBufferEXT_blockType = OpTypePointer Function %_ptr_PhysicalStorageBufferEXT_blockType
-%rootBlock = OpTypeStruct %_ptr_PhysicalStorageBufferEXT_blockType
+%blockType = OpTypeStruct %int %_ptr_PhysicalStorageBuffer_blockType
+%_ptr_PhysicalStorageBuffer_blockType = OpTypePointer PhysicalStorageBuffer %blockType
+%_ptr_Function__ptr_PhysicalStorageBuffer_blockType = OpTypePointer Function %_ptr_PhysicalStorageBuffer_blockType
+%rootBlock = OpTypeStruct %_ptr_PhysicalStorageBuffer_blockType
 %_ptr_StorageBuffer_rootBlock = OpTypePointer StorageBuffer %rootBlock
 %r = OpVariable %_ptr_StorageBuffer_rootBlock StorageBuffer
 %int_0 = OpConstant %int 0
-%_ptr_StorageBuffer__ptr_PhysicalStorageBufferEXT_blockType = OpTypePointer StorageBuffer %_ptr_PhysicalStorageBufferEXT_blockType
+%_ptr_StorageBuffer__ptr_PhysicalStorageBuffer_blockType = OpTypePointer StorageBuffer %_ptr_PhysicalStorageBuffer_blockType
 %int_1 = OpConstant %int 1
-%_ptr_PhysicalStorageBufferEXT__ptr_PhysicalStorageBufferEXT_blockType = OpTypePointer PhysicalStorageBufferEXT %_ptr_PhysicalStorageBufferEXT_blockType
+%_ptr_PhysicalStorageBuffer__ptr_PhysicalStorageBuffer_blockType = OpTypePointer PhysicalStorageBuffer %_ptr_PhysicalStorageBuffer_blockType
 %int_531 = OpConstant %int 531
-%_ptr_PhysicalStorageBufferEXT_int = OpTypePointer PhysicalStorageBufferEXT %int
+%_ptr_PhysicalStorageBuffer_int = OpTypePointer PhysicalStorageBuffer %int
 )";
 
   const std::string predefs2_after =
       R"(%void = OpTypeVoid
 %8 = OpTypeFunction %void
-OpTypeForwardPointer %_ptr_PhysicalStorageBufferEXT_blockType PhysicalStorageBufferEXT
+OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_blockType PhysicalStorageBuffer
 %int = OpTypeInt 32 1
-%blockType = OpTypeStruct %int %_ptr_PhysicalStorageBufferEXT_blockType
-%_ptr_PhysicalStorageBufferEXT_blockType = OpTypePointer PhysicalStorageBufferEXT %blockType
-%rootBlock = OpTypeStruct %_ptr_PhysicalStorageBufferEXT_blockType
+%blockType = OpTypeStruct %int %_ptr_PhysicalStorageBuffer_blockType
+%_ptr_PhysicalStorageBuffer_blockType = OpTypePointer PhysicalStorageBuffer %blockType
+%rootBlock = OpTypeStruct %_ptr_PhysicalStorageBuffer_blockType
 %_ptr_StorageBuffer_rootBlock = OpTypePointer StorageBuffer %rootBlock
 %r = OpVariable %_ptr_StorageBuffer_rootBlock StorageBuffer
 %int_0 = OpConstant %int 0
-%_ptr_StorageBuffer__ptr_PhysicalStorageBufferEXT_blockType = OpTypePointer StorageBuffer %_ptr_PhysicalStorageBufferEXT_blockType
+%_ptr_StorageBuffer__ptr_PhysicalStorageBuffer_blockType = OpTypePointer StorageBuffer %_ptr_PhysicalStorageBuffer_blockType
 %int_1 = OpConstant %int 1
-%_ptr_PhysicalStorageBufferEXT__ptr_PhysicalStorageBufferEXT_blockType = OpTypePointer PhysicalStorageBufferEXT %_ptr_PhysicalStorageBufferEXT_blockType
+%_ptr_PhysicalStorageBuffer__ptr_PhysicalStorageBuffer_blockType = OpTypePointer PhysicalStorageBuffer %_ptr_PhysicalStorageBuffer_blockType
 %int_531 = OpConstant %int 531
-%_ptr_PhysicalStorageBufferEXT_int = OpTypePointer PhysicalStorageBufferEXT %int
+%_ptr_PhysicalStorageBuffer_int = OpTypePointer PhysicalStorageBuffer %int
 )";
 
   const std::string func_before =
       R"(%main = OpFunction %void None %3
 %5 = OpLabel
-%b = OpVariable %_ptr_Function__ptr_PhysicalStorageBufferEXT_blockType Function
-%16 = OpAccessChain %_ptr_StorageBuffer__ptr_PhysicalStorageBufferEXT_blockType %r %int_0
-%17 = OpLoad %_ptr_PhysicalStorageBufferEXT_blockType %16
-%21 = OpAccessChain %_ptr_PhysicalStorageBufferEXT__ptr_PhysicalStorageBufferEXT_blockType %17 %int_1
-%22 = OpLoad %_ptr_PhysicalStorageBufferEXT_blockType %21 Aligned 8
+%b = OpVariable %_ptr_Function__ptr_PhysicalStorageBuffer_blockType Function
+%16 = OpAccessChain %_ptr_StorageBuffer__ptr_PhysicalStorageBuffer_blockType %r %int_0
+%17 = OpLoad %_ptr_PhysicalStorageBuffer_blockType %16
+%21 = OpAccessChain %_ptr_PhysicalStorageBuffer__ptr_PhysicalStorageBuffer_blockType %17 %int_1
+%22 = OpLoad %_ptr_PhysicalStorageBuffer_blockType %21 Aligned 8
 OpStore %b %22
-%26 = OpAccessChain %_ptr_PhysicalStorageBufferEXT_int %22 %int_0
+%26 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %22 %int_0
 OpStore %26 %int_531 Aligned 16
 OpReturn
 OpFunctionEnd
@@ -6747,11 +6747,11 @@
   const std::string func_after =
       R"(%main = OpFunction %void None %8
 %19 = OpLabel
-%20 = OpAccessChain %_ptr_StorageBuffer__ptr_PhysicalStorageBufferEXT_blockType %r %int_0
-%21 = OpLoad %_ptr_PhysicalStorageBufferEXT_blockType %20
-%22 = OpAccessChain %_ptr_PhysicalStorageBufferEXT__ptr_PhysicalStorageBufferEXT_blockType %21 %int_1
-%23 = OpLoad %_ptr_PhysicalStorageBufferEXT_blockType %22 Aligned 8
-%24 = OpAccessChain %_ptr_PhysicalStorageBufferEXT_int %23 %int_0
+%20 = OpAccessChain %_ptr_StorageBuffer__ptr_PhysicalStorageBuffer_blockType %r %int_0
+%21 = OpLoad %_ptr_PhysicalStorageBuffer_blockType %20
+%22 = OpAccessChain %_ptr_PhysicalStorageBuffer__ptr_PhysicalStorageBuffer_blockType %21 %int_1
+%23 = OpLoad %_ptr_PhysicalStorageBuffer_blockType %22 Aligned 8
+%24 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %23 %int_0
 OpStore %24 %int_531 Aligned 16
 OpReturn
 OpFunctionEnd
diff --git a/test/opt/amd_ext_to_khr.cpp b/test/opt/amd_ext_to_khr.cpp
index 7a6d4b4..d943d34 100644
--- a/test/opt/amd_ext_to_khr.cpp
+++ b/test/opt/amd_ext_to_khr.cpp
@@ -233,6 +233,7 @@
 
   SinglePassRunAndMatch<AmdExtensionToKhrPass>(text, true);
 }
+
 TEST_F(AmdExtToKhrTest, ReplaceWriteInvocationAMD) {
   const std::string text = R"(
 ; CHECK: OpCapability Shader
@@ -269,6 +270,585 @@
   SinglePassRunAndMatch<AmdExtensionToKhrPass>(text, true);
 }
 
+TEST_F(AmdExtToKhrTest, ReplaceFMin3AMD) {
+  const std::string text = R"(
+; CHECK: OpCapability Shader
+; CHECK-NOT: OpExtension "SPV_AMD_shader_trinary_minmax"
+; CHECK-NOT: OpExtInstImport "SPV_AMD_shader_trinary_minmax"
+; CHECK: [[ext:%\w+]] = OpExtInstImport "GLSL.std.450"
+; CHECK: [[type:%\w+]] = OpTypeFloat 32
+; CHECK: OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[x:%\w+]] = OpUndef [[type]]
+; CHECK-NEXT: [[y:%\w+]] = OpUndef [[type]]
+; CHECK-NEXT: [[z:%\w+]] = OpUndef [[type]]
+; CHECK-NEXT: [[temp:%\w+]] = OpExtInst [[type]] [[ext]] FMin [[x]] [[y]]
+; CHECK-NEXT: [[result:%\w+]] = OpExtInst [[type]] [[ext]] FMin [[temp]] [[z]]
+               OpCapability Shader
+               OpExtension "SPV_AMD_shader_trinary_minmax"
+        %ext = OpExtInstImport "SPV_AMD_shader_trinary_minmax"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %1 "func"
+               OpExecutionMode %1 OriginUpperLeft
+       %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 %float
+          %8 = OpUndef %float
+          %9 = OpUndef %float
+         %10 = OpExtInst %float %ext FMin3AMD %7 %8 %9
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<AmdExtensionToKhrPass>(text, true);
+}
+
+TEST_F(AmdExtToKhrTest, ReplaceSMin3AMD) {
+  const std::string text = R"(
+; CHECK: OpCapability Shader
+; CHECK-NOT: OpExtension "SPV_AMD_shader_trinary_minmax"
+; CHECK-NOT: OpExtInstImport "SPV_AMD_shader_trinary_minmax"
+; CHECK: [[ext:%\w+]] = OpExtInstImport "GLSL.std.450"
+; CHECK: [[type:%\w+]] = OpTypeInt 32 1
+; CHECK: OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[x:%\w+]] = OpUndef [[type]]
+; CHECK-NEXT: [[y:%\w+]] = OpUndef [[type]]
+; CHECK-NEXT: [[z:%\w+]] = OpUndef [[type]]
+; CHECK-NEXT: [[temp:%\w+]] = OpExtInst [[type]] [[ext]] SMin [[x]] [[y]]
+; CHECK-NEXT: [[result:%\w+]] = OpExtInst [[type]] [[ext]] SMin [[temp]] [[z]]
+               OpCapability Shader
+               OpExtension "SPV_AMD_shader_trinary_minmax"
+        %ext = OpExtInstImport "SPV_AMD_shader_trinary_minmax"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %1 "func"
+               OpExecutionMode %1 OriginUpperLeft
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+       %int = OpTypeInt 32 1
+      %float = OpTypeFloat 32
+     %uint_3 = OpConstant %uint 3
+          %1 = OpFunction %void None %3
+          %6 = OpLabel
+          %7 = OpUndef %int
+          %8 = OpUndef %int
+          %9 = OpUndef %int
+         %10 = OpExtInst %int %ext SMin3AMD %7 %8 %9
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<AmdExtensionToKhrPass>(text, true);
+}
+
+TEST_F(AmdExtToKhrTest, ReplaceUMin3AMD) {
+  const std::string text = R"(
+; CHECK: OpCapability Shader
+; CHECK-NOT: OpExtension "SPV_AMD_shader_trinary_minmax"
+; CHECK-NOT: OpExtInstImport "SPV_AMD_shader_trinary_minmax"
+; CHECK: [[ext:%\w+]] = OpExtInstImport "GLSL.std.450"
+; CHECK: [[type:%\w+]] = OpTypeInt 32 0
+; CHECK: OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[x:%\w+]] = OpUndef [[type]]
+; CHECK-NEXT: [[y:%\w+]] = OpUndef [[type]]
+; CHECK-NEXT: [[z:%\w+]] = OpUndef [[type]]
+; CHECK-NEXT: [[temp:%\w+]] = OpExtInst [[type]] [[ext]] UMin [[x]] [[y]]
+; CHECK-NEXT: [[result:%\w+]] = OpExtInst [[type]] [[ext]] UMin [[temp]] [[z]]
+               OpCapability Shader
+               OpExtension "SPV_AMD_shader_trinary_minmax"
+        %ext = OpExtInstImport "SPV_AMD_shader_trinary_minmax"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %1 "func"
+               OpExecutionMode %1 OriginUpperLeft
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+       %int = OpTypeInt 32 1
+      %float = OpTypeFloat 32
+     %uint_3 = OpConstant %uint 3
+          %1 = OpFunction %void None %3
+          %6 = OpLabel
+          %7 = OpUndef %uint
+          %8 = OpUndef %uint
+          %9 = OpUndef %uint
+         %10 = OpExtInst %uint %ext UMin3AMD %7 %8 %9
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<AmdExtensionToKhrPass>(text, true);
+}
+
+TEST_F(AmdExtToKhrTest, ReplaceFMax3AMD) {
+  const std::string text = R"(
+; CHECK: OpCapability Shader
+; CHECK-NOT: OpExtension "SPV_AMD_shader_trinary_minmax"
+; CHECK-NOT: OpExtInstImport "SPV_AMD_shader_trinary_minmax"
+; CHECK: [[ext:%\w+]] = OpExtInstImport "GLSL.std.450"
+; CHECK: [[type:%\w+]] = OpTypeFloat 32
+; CHECK: OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[x:%\w+]] = OpUndef [[type]]
+; CHECK-NEXT: [[y:%\w+]] = OpUndef [[type]]
+; CHECK-NEXT: [[z:%\w+]] = OpUndef [[type]]
+; CHECK-NEXT: [[temp:%\w+]] = OpExtInst [[type]] [[ext]] FMax [[x]] [[y]]
+; CHECK-NEXT: [[result:%\w+]] = OpExtInst [[type]] [[ext]] FMax [[temp]] [[z]]
+               OpCapability Shader
+               OpExtension "SPV_AMD_shader_trinary_minmax"
+        %ext = OpExtInstImport "SPV_AMD_shader_trinary_minmax"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %1 "func"
+               OpExecutionMode %1 OriginUpperLeft
+       %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 %float
+          %8 = OpUndef %float
+          %9 = OpUndef %float
+         %10 = OpExtInst %float %ext FMax3AMD %7 %8 %9
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<AmdExtensionToKhrPass>(text, true);
+}
+
+TEST_F(AmdExtToKhrTest, ReplaceSMax3AMD) {
+  const std::string text = R"(
+; CHECK: OpCapability Shader
+; CHECK-NOT: OpExtension "SPV_AMD_shader_trinary_minmax"
+; CHECK-NOT: OpExtInstImport "SPV_AMD_shader_trinary_minmax"
+; CHECK: [[ext:%\w+]] = OpExtInstImport "GLSL.std.450"
+; CHECK: [[type:%\w+]] = OpTypeInt 32 1
+; CHECK: OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[x:%\w+]] = OpUndef [[type]]
+; CHECK-NEXT: [[y:%\w+]] = OpUndef [[type]]
+; CHECK-NEXT: [[z:%\w+]] = OpUndef [[type]]
+; CHECK-NEXT: [[temp:%\w+]] = OpExtInst [[type]] [[ext]] SMax [[x]] [[y]]
+; CHECK-NEXT: [[result:%\w+]] = OpExtInst [[type]] [[ext]] SMax [[temp]] [[z]]
+               OpCapability Shader
+               OpExtension "SPV_AMD_shader_trinary_minmax"
+        %ext = OpExtInstImport "SPV_AMD_shader_trinary_minmax"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %1 "func"
+               OpExecutionMode %1 OriginUpperLeft
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+       %int = OpTypeInt 32 1
+      %float = OpTypeFloat 32
+     %uint_3 = OpConstant %uint 3
+          %1 = OpFunction %void None %3
+          %6 = OpLabel
+          %7 = OpUndef %int
+          %8 = OpUndef %int
+          %9 = OpUndef %int
+         %10 = OpExtInst %int %ext SMax3AMD %7 %8 %9
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<AmdExtensionToKhrPass>(text, true);
+}
+
+TEST_F(AmdExtToKhrTest, ReplaceUMax3AMD) {
+  const std::string text = R"(
+; CHECK: OpCapability Shader
+; CHECK-NOT: OpExtension "SPV_AMD_shader_trinary_minmax"
+; CHECK-NOT: OpExtInstImport "SPV_AMD_shader_trinary_minmax"
+; CHECK: [[ext:%\w+]] = OpExtInstImport "GLSL.std.450"
+; CHECK: [[type:%\w+]] = OpTypeInt 32 0
+; CHECK: OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[x:%\w+]] = OpUndef [[type]]
+; CHECK-NEXT: [[y:%\w+]] = OpUndef [[type]]
+; CHECK-NEXT: [[z:%\w+]] = OpUndef [[type]]
+; CHECK-NEXT: [[temp:%\w+]] = OpExtInst [[type]] [[ext]] UMax [[x]] [[y]]
+; CHECK-NEXT: [[result:%\w+]] = OpExtInst [[type]] [[ext]] UMax [[temp]] [[z]]
+               OpCapability Shader
+               OpExtension "SPV_AMD_shader_trinary_minmax"
+        %ext = OpExtInstImport "SPV_AMD_shader_trinary_minmax"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %1 "func"
+               OpExecutionMode %1 OriginUpperLeft
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+       %int = OpTypeInt 32 1
+      %float = OpTypeFloat 32
+     %uint_3 = OpConstant %uint 3
+          %1 = OpFunction %void None %3
+          %6 = OpLabel
+          %7 = OpUndef %uint
+          %8 = OpUndef %uint
+          %9 = OpUndef %uint
+         %10 = OpExtInst %uint %ext UMax3AMD %7 %8 %9
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<AmdExtensionToKhrPass>(text, true);
+}
+
+TEST_F(AmdExtToKhrTest, ReplaceVecUMax3AMD) {
+  const std::string text = R"(
+; CHECK: OpCapability Shader
+; CHECK-NOT: OpExtension "SPV_AMD_shader_trinary_minmax"
+; CHECK-NOT: OpExtInstImport "SPV_AMD_shader_trinary_minmax"
+; CHECK: [[ext:%\w+]] = OpExtInstImport "GLSL.std.450"
+; CHECK: [[type:%\w+]] = OpTypeVector
+; CHECK: OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[x:%\w+]] = OpUndef [[type]]
+; CHECK-NEXT: [[y:%\w+]] = OpUndef [[type]]
+; CHECK-NEXT: [[z:%\w+]] = OpUndef [[type]]
+; CHECK-NEXT: [[temp:%\w+]] = OpExtInst [[type]] [[ext]] UMax [[x]] [[y]]
+; CHECK-NEXT: [[result:%\w+]] = OpExtInst [[type]] [[ext]] UMax [[temp]] [[z]]
+               OpCapability Shader
+               OpExtension "SPV_AMD_shader_trinary_minmax"
+        %ext = OpExtInstImport "SPV_AMD_shader_trinary_minmax"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %1 "func"
+               OpExecutionMode %1 OriginUpperLeft
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+        %vec = OpTypeVector %uint 4
+       %int = OpTypeInt 32 1
+      %float = OpTypeFloat 32
+     %uint_3 = OpConstant %uint 3
+          %1 = OpFunction %void None %3
+          %6 = OpLabel
+          %7 = OpUndef %vec
+          %8 = OpUndef %vec
+          %9 = OpUndef %vec
+         %10 = OpExtInst %vec %ext UMax3AMD %7 %8 %9
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<AmdExtensionToKhrPass>(text, true);
+}
+
+TEST_F(AmdExtToKhrTest, ReplaceFMid3AMD) {
+  const std::string text = R"(
+; CHECK: OpCapability Shader
+; CHECK-NOT: OpExtension "SPV_AMD_shader_trinary_minmax"
+; CHECK-NOT: OpExtInstImport "SPV_AMD_shader_trinary_minmax"
+; CHECK: [[ext:%\w+]] = OpExtInstImport "GLSL.std.450"
+; CHECK: [[type:%\w+]] = OpTypeFloat 32
+; CHECK: OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[x:%\w+]] = OpUndef [[type]]
+; CHECK-NEXT: [[y:%\w+]] = OpUndef [[type]]
+; CHECK-NEXT: [[z:%\w+]] = OpUndef [[type]]
+; CHECK-NEXT: [[min:%\w+]] = OpExtInst [[type]] [[ext]] FMin [[y]] [[z]]
+; CHECK-NEXT: [[max:%\w+]] = OpExtInst [[type]] [[ext]] FMax [[y]] [[z]]
+; CHECK-NEXT: [[result:%\w+]] = OpExtInst [[type]] [[ext]] FClamp [[x]] [[min]] [[max]]
+               OpCapability Shader
+               OpExtension "SPV_AMD_shader_trinary_minmax"
+        %ext = OpExtInstImport "SPV_AMD_shader_trinary_minmax"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %1 "func"
+               OpExecutionMode %1 OriginUpperLeft
+       %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 %float
+          %8 = OpUndef %float
+          %9 = OpUndef %float
+         %10 = OpExtInst %float %ext FMid3AMD %7 %8 %9
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<AmdExtensionToKhrPass>(text, true);
+}
+
+TEST_F(AmdExtToKhrTest, ReplaceSMid3AMD) {
+  const std::string text = R"(
+; CHECK: OpCapability Shader
+; CHECK-NOT: OpExtension "SPV_AMD_shader_trinary_minmax"
+; CHECK-NOT: OpExtInstImport "SPV_AMD_shader_trinary_minmax"
+; CHECK: [[ext:%\w+]] = OpExtInstImport "GLSL.std.450"
+; CHECK: [[type:%\w+]] = OpTypeInt 32 1
+; CHECK: OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[x:%\w+]] = OpUndef [[type]]
+; CHECK-NEXT: [[y:%\w+]] = OpUndef [[type]]
+; CHECK-NEXT: [[z:%\w+]] = OpUndef [[type]]
+; CHECK-NEXT: [[min:%\w+]] = OpExtInst [[type]] [[ext]] SMin [[y]] [[z]]
+; CHECK-NEXT: [[max:%\w+]] = OpExtInst [[type]] [[ext]] SMax [[y]] [[z]]
+; CHECK-NEXT: [[result:%\w+]] = OpExtInst [[type]] [[ext]] SClamp [[x]] [[min]] [[max]]
+               OpCapability Shader
+               OpExtension "SPV_AMD_shader_trinary_minmax"
+        %ext = OpExtInstImport "SPV_AMD_shader_trinary_minmax"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %1 "func"
+               OpExecutionMode %1 OriginUpperLeft
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+       %int = OpTypeInt 32 1
+      %float = OpTypeFloat 32
+     %uint_3 = OpConstant %uint 3
+          %1 = OpFunction %void None %3
+          %6 = OpLabel
+          %7 = OpUndef %int
+          %8 = OpUndef %int
+          %9 = OpUndef %int
+         %10 = OpExtInst %int %ext SMid3AMD %7 %8 %9
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<AmdExtensionToKhrPass>(text, true);
+}
+
+TEST_F(AmdExtToKhrTest, ReplaceUMid3AMD) {
+  const std::string text = R"(
+; CHECK: OpCapability Shader
+; CHECK-NOT: OpExtension "SPV_AMD_shader_trinary_minmax"
+; CHECK-NOT: OpExtInstImport "SPV_AMD_shader_trinary_minmax"
+; CHECK: [[ext:%\w+]] = OpExtInstImport "GLSL.std.450"
+; CHECK: [[type:%\w+]] = OpTypeInt 32 0
+; CHECK: OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[x:%\w+]] = OpUndef [[type]]
+; CHECK-NEXT: [[y:%\w+]] = OpUndef [[type]]
+; CHECK-NEXT: [[z:%\w+]] = OpUndef [[type]]
+; CHECK-NEXT: [[min:%\w+]] = OpExtInst [[type]] [[ext]] UMin [[y]] [[z]]
+; CHECK-NEXT: [[max:%\w+]] = OpExtInst [[type]] [[ext]] UMax [[y]] [[z]]
+; CHECK-NEXT: [[result:%\w+]] = OpExtInst [[type]] [[ext]] UClamp [[x]] [[min]] [[max]]
+               OpCapability Shader
+               OpExtension "SPV_AMD_shader_trinary_minmax"
+        %ext = OpExtInstImport "SPV_AMD_shader_trinary_minmax"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %1 "func"
+               OpExecutionMode %1 OriginUpperLeft
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+       %int = OpTypeInt 32 1
+      %float = OpTypeFloat 32
+     %uint_3 = OpConstant %uint 3
+          %1 = OpFunction %void None %3
+          %6 = OpLabel
+          %7 = OpUndef %uint
+          %8 = OpUndef %uint
+          %9 = OpUndef %uint
+         %10 = OpExtInst %uint %ext UMid3AMD %7 %8 %9
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<AmdExtensionToKhrPass>(text, true);
+}
+
+TEST_F(AmdExtToKhrTest, ReplaceVecUMid3AMD) {
+  const std::string text = R"(
+; CHECK: OpCapability Shader
+; CHECK-NOT: OpExtension "SPV_AMD_shader_trinary_minmax"
+; CHECK-NOT: OpExtInstImport "SPV_AMD_shader_trinary_minmax"
+; CHECK: [[ext:%\w+]] = OpExtInstImport "GLSL.std.450"
+; CHECK: [[type:%\w+]] = OpTypeVector
+; CHECK: OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[x:%\w+]] = OpUndef [[type]]
+; CHECK-NEXT: [[y:%\w+]] = OpUndef [[type]]
+; CHECK-NEXT: [[z:%\w+]] = OpUndef [[type]]
+; CHECK-NEXT: [[min:%\w+]] = OpExtInst [[type]] [[ext]] UMin [[y]] [[z]]
+; CHECK-NEXT: [[max:%\w+]] = OpExtInst [[type]] [[ext]] UMax [[y]] [[z]]
+; CHECK-NEXT: [[result:%\w+]] = OpExtInst [[type]] [[ext]] UClamp [[x]] [[min]] [[max]]
+               OpCapability Shader
+               OpExtension "SPV_AMD_shader_trinary_minmax"
+        %ext = OpExtInstImport "SPV_AMD_shader_trinary_minmax"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %1 "func"
+               OpExecutionMode %1 OriginUpperLeft
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+       %vec = OpTypeVector %uint 3
+       %int = OpTypeInt 32 1
+      %float = OpTypeFloat 32
+     %uint_3 = OpConstant %uint 3
+          %1 = OpFunction %void None %3
+          %6 = OpLabel
+          %7 = OpUndef %vec
+          %8 = OpUndef %vec
+          %9 = OpUndef %vec
+         %10 = OpExtInst %vec %ext UMid3AMD %7 %8 %9
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<AmdExtensionToKhrPass>(text, true);
+}
+
+TEST_F(AmdExtToKhrTest, ReplaceCubeFaceCoordAMD) {
+  // Sorry for the Check test.  The code sequence is so long, I do not think
+  // that a match test would be anymore legible.  This tests the replacement of
+  // the CubeFaceCoordAMD instruction.
+  const std::string before = R"(
+               OpCapability Shader
+               OpExtension "SPV_KHR_storage_buffer_storage_class"
+               OpExtension "SPV_AMD_gcn_shader"
+          %1 = OpExtInstImport "SPV_AMD_gcn_shader"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %2 "main"
+               OpExecutionMode %2 LocalSize 1 1 1
+       %void = OpTypeVoid
+          %4 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v2float = OpTypeVector %float 2
+    %v3float = OpTypeVector %float 3
+          %2 = OpFunction %void None %4
+          %8 = OpLabel
+          %9 = OpUndef %v3float
+         %10 = OpExtInst %v2float %1 CubeFaceCoordAMD %9
+               OpReturn
+               OpFunctionEnd
+)";
+
+  const std::string after = R"(OpCapability Shader
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+%12 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %2 "main"
+OpExecutionMode %2 LocalSize 1 1 1
+%void = OpTypeVoid
+%4 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v2float = OpTypeVector %float 2
+%v3float = OpTypeVector %float 3
+%bool = OpTypeBool
+%float_0 = OpConstant %float 0
+%float_2 = OpConstant %float 2
+%float_0_5 = OpConstant %float 0.5
+%16 = OpConstantComposite %v2float %float_0_5 %float_0_5
+%2 = OpFunction %void None %4
+%8 = OpLabel
+%9 = OpUndef %v3float
+%17 = OpCompositeExtract %float %9 0
+%18 = OpCompositeExtract %float %9 1
+%19 = OpCompositeExtract %float %9 2
+%20 = OpFNegate %float %17
+%21 = OpFNegate %float %18
+%22 = OpFNegate %float %19
+%23 = OpExtInst %float %12 FAbs %17
+%24 = OpExtInst %float %12 FAbs %18
+%25 = OpExtInst %float %12 FAbs %19
+%26 = OpFOrdLessThan %bool %19 %float_0
+%27 = OpFOrdLessThan %bool %18 %float_0
+%28 = OpFOrdLessThan %bool %17 %float_0
+%29 = OpExtInst %float %12 FMax %23 %24
+%30 = OpExtInst %float %12 FMax %25 %29
+%31 = OpFMul %float %float_2 %30
+%32 = OpFOrdGreaterThanEqual %bool %25 %29
+%33 = OpLogicalNot %bool %32
+%34 = OpFOrdGreaterThanEqual %bool %24 %23
+%35 = OpLogicalAnd %bool %33 %34
+%36 = OpSelect %float %26 %20 %17
+%37 = OpSelect %float %28 %19 %22
+%38 = OpSelect %float %35 %17 %37
+%39 = OpSelect %float %32 %36 %38
+%40 = OpSelect %float %27 %22 %19
+%41 = OpSelect %float %35 %40 %21
+%42 = OpCompositeConstruct %v2float %39 %41
+%43 = OpCompositeConstruct %v2float %31 %31
+%44 = OpFDiv %v2float %42 %43
+%10 = OpFAdd %v2float %44 %16
+OpReturn
+OpFunctionEnd
+)";
+
+  SinglePassRunAndCheck<AmdExtensionToKhrPass>(before, after, true);
+}
+
+TEST_F(AmdExtToKhrTest, ReplaceCubeFaceIndexAMD) {
+  // Sorry for the Check test.  The code sequence is so long, I do not think
+  // that a match test would be anymore legible.  This tests the replacement of
+  // the CubeFaceIndexAMD instruction.
+  const std::string before = R"(OpCapability Shader
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+OpExtension "SPV_AMD_gcn_shader"
+%1 = OpExtInstImport "SPV_AMD_gcn_shader"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %2 "main"
+OpExecutionMode %2 LocalSize 1 1 1
+%void = OpTypeVoid
+%4 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v3float = OpTypeVector %float 3
+%2 = OpFunction %void None %4
+%7 = OpLabel
+%8 = OpUndef %v3float
+%9 = OpExtInst %float %1 CubeFaceIndexAMD %8
+OpReturn
+OpFunctionEnd
+)";
+
+  const std::string after = R"(OpCapability Shader
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+%11 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %2 "main"
+OpExecutionMode %2 LocalSize 1 1 1
+%void = OpTypeVoid
+%4 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v3float = OpTypeVector %float 3
+%bool = OpTypeBool
+%float_0 = OpConstant %float 0
+%float_1 = OpConstant %float 1
+%float_2 = OpConstant %float 2
+%float_3 = OpConstant %float 3
+%float_4 = OpConstant %float 4
+%float_5 = OpConstant %float 5
+%2 = OpFunction %void None %4
+%7 = OpLabel
+%8 = OpUndef %v3float
+%18 = OpCompositeExtract %float %8 0
+%19 = OpCompositeExtract %float %8 1
+%20 = OpCompositeExtract %float %8 2
+%21 = OpExtInst %float %11 FAbs %18
+%22 = OpExtInst %float %11 FAbs %19
+%23 = OpExtInst %float %11 FAbs %20
+%24 = OpFOrdLessThan %bool %20 %float_0
+%25 = OpFOrdLessThan %bool %19 %float_0
+%26 = OpFOrdLessThan %bool %18 %float_0
+%27 = OpExtInst %float %11 FMax %21 %22
+%28 = OpFOrdGreaterThanEqual %bool %23 %27
+%29 = OpFOrdGreaterThanEqual %bool %22 %21
+%30 = OpSelect %float %24 %float_5 %float_4
+%31 = OpSelect %float %25 %float_3 %float_2
+%32 = OpSelect %float %26 %float_1 %float_0
+%33 = OpSelect %float %29 %31 %32
+%9 = OpSelect %float %28 %30 %33
+OpReturn
+OpFunctionEnd
+)";
+
+  SinglePassRunAndCheck<AmdExtensionToKhrPass>(before, after, true);
+}
+
 TEST_F(AmdExtToKhrTest, SetVersion) {
   const std::string text = R"(
                OpCapability Shader
diff --git a/test/opt/ccp_test.cpp b/test/opt/ccp_test.cpp
index 2ee7cce..920c0f4 100644
--- a/test/opt/ccp_test.cpp
+++ b/test/opt/ccp_test.cpp
@@ -570,10 +570,10 @@
          %10 = OpSpecConstantFalse %bool
        %main = OpFunction %void None %4
          %11 = OpLabel
-         %12 = OpBranchConditional %10 %l1 %l2
-         %l1 = OpLabel
+               OpBranchConditional %10 %L1 %L2
+         %L1 = OpLabel
                OpReturn
-         %l2 = OpLabel
+         %L2 = OpLabel
                OpReturn
                OpFunctionEnd
   )";
diff --git a/test/opt/constant_manager_test.cpp b/test/opt/constant_manager_test.cpp
index 57dea65..14e14ec 100644
--- a/test/opt/constant_manager_test.cpp
+++ b/test/opt/constant_manager_test.cpp
@@ -82,6 +82,28 @@
   EXPECT_EQ(const_inst_2->result_id(), 4);
 }
 
+TEST_F(ConstantManagerTest, GetDefiningInstructionIdOverflow) {
+  const std::string text = R"(
+%1 = OpTypeInt 32 0
+%3 = OpConstant %1 1
+%4 = OpConstant %1 2
+  )";
+
+  std::unique_ptr<IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text,
+                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  ASSERT_NE(context, nullptr);
+
+  // Set the id bound to the max, so the new constant cannot be generated.
+  context->module()->SetIdBound(context->max_id_bound());
+
+  Type* int_type = context->get_type_mgr()->GetType(1);
+  IntConstant int_constant(int_type->AsInteger(), {3});
+  Instruction* inst =
+      context->get_constant_mgr()->GetDefiningInstruction(&int_constant, 1);
+  EXPECT_EQ(inst, nullptr);
+}
+
 }  // namespace
 }  // namespace analysis
 }  // namespace opt
diff --git a/test/opt/convert_relaxed_to_half_test.cpp b/test/opt/convert_relaxed_to_half_test.cpp
new file mode 100644
index 0000000..3ac8009
--- /dev/null
+++ b/test/opt/convert_relaxed_to_half_test.cpp
@@ -0,0 +1,1227 @@
+// Copyright (c) 2019 Valve Corporation
+// Copyright (c) 2019 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.
+
+// Convert Relaxed to Half tests
+
+#include <string>
+#include <vector>
+
+#include "test/opt/assembly_builder.h"
+#include "test/opt/pass_fixture.h"
+#include "test/opt/pass_utils.h"
+
+namespace spvtools {
+namespace opt {
+namespace {
+
+using ConvertToHalfTest = PassTest<::testing::Test>;
+
+TEST_F(ConvertToHalfTest, ConvertToHalfBasic) {
+  // The resulting SPIR-V was processed with --relax-float-ops.
+  //
+  // clang-format off
+  //
+  // SamplerState       g_sSamp : register(s0);
+  // uniform Texture1D <float4> g_tTex1df4 : register(t0);
+  //
+  // struct PS_INPUT
+  // {
+  //   float Tex0 : TEXCOORD0;
+  // };
+  //
+  // struct PS_OUTPUT
+  // {
+  //   float4 Color : SV_Target0;
+  // };
+  //
+  // cbuffer cbuff{
+  //   float c;
+  // }
+  //
+  // PS_OUTPUT main(PS_INPUT i)
+  // {
+  //   PS_OUTPUT psout;
+  //   psout.Color = g_tTex1df4.Sample(g_sSamp, i.Tex0) * c;
+  //   return psout;
+  // }
+  //
+  // clang-format on
+
+  const std::string defs_before =
+      R"(OpCapability Shader
+OpCapability Sampled1D
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %i_Tex0 %_entryPointOutput_Color
+OpExecutionMode %main OriginUpperLeft
+OpSource HLSL 500
+OpName %main "main"
+OpName %g_tTex1df4 "g_tTex1df4"
+OpName %g_sSamp "g_sSamp"
+OpName %cbuff "cbuff"
+OpMemberName %cbuff 0 "c"
+OpName %_ ""
+OpName %i_Tex0 "i.Tex0"
+OpName %_entryPointOutput_Color "@entryPointOutput.Color"
+OpDecorate %g_tTex1df4 DescriptorSet 0
+OpDecorate %g_tTex1df4 Binding 0
+OpDecorate %g_sSamp DescriptorSet 0
+OpDecorate %g_sSamp Binding 0
+OpMemberDecorate %cbuff 0 Offset 0
+OpDecorate %cbuff Block
+OpDecorate %_ DescriptorSet 0
+OpDecorate %_ Binding 1
+OpDecorate %i_Tex0 Location 0
+OpDecorate %_entryPointOutput_Color Location 0
+OpDecorate %48 RelaxedPrecision
+OpDecorate %63 RelaxedPrecision
+OpDecorate %65 RelaxedPrecision
+OpDecorate %66 RelaxedPrecision
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%19 = OpTypeImage %float 1D 0 0 0 1 Unknown
+%_ptr_UniformConstant_19 = OpTypePointer UniformConstant %19
+%g_tTex1df4 = OpVariable %_ptr_UniformConstant_19 UniformConstant
+%23 = OpTypeSampler
+%_ptr_UniformConstant_23 = OpTypePointer UniformConstant %23
+%g_sSamp = OpVariable %_ptr_UniformConstant_23 UniformConstant
+%27 = OpTypeSampledImage %19
+%cbuff = OpTypeStruct %float
+%_ptr_Uniform_cbuff = OpTypePointer Uniform %cbuff
+%_ = OpVariable %_ptr_Uniform_cbuff Uniform
+%_ptr_Uniform_float = OpTypePointer Uniform %float
+%_ptr_Input_float = OpTypePointer Input %float
+%i_Tex0 = OpVariable %_ptr_Input_float Input
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%_entryPointOutput_Color = OpVariable %_ptr_Output_v4float Output
+)";
+
+  const std::string defs_after =
+      R"(OpCapability Shader
+OpCapability Sampled1D
+OpCapability Float16
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %i_Tex0 %_entryPointOutput_Color
+OpExecutionMode %main OriginUpperLeft
+OpSource HLSL 500
+OpName %main "main"
+OpName %g_tTex1df4 "g_tTex1df4"
+OpName %g_sSamp "g_sSamp"
+OpName %cbuff "cbuff"
+OpMemberName %cbuff 0 "c"
+OpName %_ ""
+OpName %i_Tex0 "i.Tex0"
+OpName %_entryPointOutput_Color "@entryPointOutput.Color"
+OpDecorate %g_tTex1df4 DescriptorSet 0
+OpDecorate %g_tTex1df4 Binding 0
+OpDecorate %g_sSamp DescriptorSet 0
+OpDecorate %g_sSamp Binding 0
+OpMemberDecorate %cbuff 0 Offset 0
+OpDecorate %cbuff Block
+OpDecorate %_ DescriptorSet 0
+OpDecorate %_ Binding 1
+OpDecorate %i_Tex0 Location 0
+OpDecorate %_entryPointOutput_Color Location 0
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%19 = OpTypeImage %float 1D 0 0 0 1 Unknown
+%_ptr_UniformConstant_19 = OpTypePointer UniformConstant %19
+%g_tTex1df4 = OpVariable %_ptr_UniformConstant_19 UniformConstant
+%23 = OpTypeSampler
+%_ptr_UniformConstant_23 = OpTypePointer UniformConstant %23
+%g_sSamp = OpVariable %_ptr_UniformConstant_23 UniformConstant
+%27 = OpTypeSampledImage %19
+%cbuff = OpTypeStruct %float
+%_ptr_Uniform_cbuff = OpTypePointer Uniform %cbuff
+%_ = OpVariable %_ptr_Uniform_cbuff Uniform
+%_ptr_Uniform_float = OpTypePointer Uniform %float
+%_ptr_Input_float = OpTypePointer Input %float
+%i_Tex0 = OpVariable %_ptr_Input_float Input
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%_entryPointOutput_Color = OpVariable %_ptr_Output_v4float Output
+%half = OpTypeFloat 16
+%v4half = OpTypeVector %half 4
+)";
+
+  const std::string func_before =
+      R"(%main = OpFunction %void None %3
+%5 = OpLabel
+%48 = OpLoad %float %i_Tex0
+%58 = OpLoad %19 %g_tTex1df4
+%59 = OpLoad %23 %g_sSamp
+%60 = OpSampledImage %27 %58 %59
+%63 = OpImageSampleImplicitLod %v4float %60 %48
+%64 = OpAccessChain %_ptr_Uniform_float %_ %int_0
+%65 = OpLoad %float %64
+%66 = OpVectorTimesScalar %v4float %63 %65
+OpStore %_entryPointOutput_Color %66
+OpReturn
+OpFunctionEnd
+)";
+
+  const std::string func_after =
+      R"(%main = OpFunction %void None %3
+%5 = OpLabel
+%48 = OpLoad %float %i_Tex0
+%58 = OpLoad %19 %g_tTex1df4
+%59 = OpLoad %23 %g_sSamp
+%60 = OpSampledImage %27 %58 %59
+%63 = OpImageSampleImplicitLod %v4float %60 %48
+%64 = OpAccessChain %_ptr_Uniform_float %_ %int_0
+%65 = OpLoad %float %64
+%69 = OpFConvert %v4half %63
+%70 = OpFConvert %half %65
+%66 = OpVectorTimesScalar %v4half %69 %70
+%71 = OpFConvert %v4float %66
+OpStore %_entryPointOutput_Color %71
+OpReturn
+OpFunctionEnd
+)";
+
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndCheck<ConvertToHalfPass>(defs_before + func_before,
+                                           defs_after + func_after, true, true);
+}
+
+TEST_F(ConvertToHalfTest, ConvertToHalfWithDrefSample) {
+  // The resulting SPIR-V was processed with --relax-float-ops.
+  //
+  // clang-format off
+  //
+  // SamplerComparisonState       g_sSamp : register(s0);
+  // uniform Texture1D <float4> g_tTex1df4 : register(t0);
+  //
+  // cbuffer cbuff{
+  //   float c1;
+  // float c2;
+  // };
+  //
+  // struct PS_INPUT
+  // {
+  //   float Tex0 : TEXCOORD0;
+  //   float Tex1 : TEXCOORD1;
+  // };
+  //
+  // struct PS_OUTPUT
+  // {
+  //   float Color : SV_Target0;
+  // };
+  //
+  // PS_OUTPUT main(PS_INPUT i)
+  // {
+  //   PS_OUTPUT psout;
+  //   float txval10 = g_tTex1df4.SampleCmp(g_sSamp, i.Tex0 * 0.1, c1 + 0.1);
+  //   float txval11 = g_tTex1df4.SampleCmp(g_sSamp, i.Tex1 * 0.2, c2 + 0.2);
+  //   float t = txval10 + txval11;
+  //   float t2 = t / 2.0;
+  //   psout.Color = t2;
+  //   return psout;
+  // }
+  //
+  // clang-format on
+
+  const std::string defs_before =
+      R"(OpCapability Shader
+OpCapability Sampled1D
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %i_Tex0 %i_Tex1 %_entryPointOutput_Color
+OpExecutionMode %main OriginUpperLeft
+OpSource HLSL 500
+OpName %main "main"
+OpName %g_tTex1df4 "g_tTex1df4"
+OpName %g_sSamp "g_sSamp"
+OpName %cbuff "cbuff"
+OpMemberName %cbuff 0 "c1"
+OpMemberName %cbuff 1 "c2"
+OpName %_ ""
+OpName %i_Tex0 "i.Tex0"
+OpName %i_Tex1 "i.Tex1"
+OpName %_entryPointOutput_Color "@entryPointOutput.Color"
+OpDecorate %g_tTex1df4 DescriptorSet 0
+OpDecorate %g_tTex1df4 Binding 0
+OpDecorate %g_sSamp DescriptorSet 0
+OpDecorate %g_sSamp Binding 0
+OpMemberDecorate %cbuff 0 Offset 0
+OpMemberDecorate %cbuff 1 Offset 4
+OpDecorate %cbuff Block
+OpDecorate %_ DescriptorSet 0
+OpDecorate %_ Binding 1
+OpDecorate %i_Tex0 Location 0
+OpDecorate %i_Tex1 Location 1
+OpDecorate %_entryPointOutput_Color Location 0
+OpDecorate %100 RelaxedPrecision
+OpDecorate %76 RelaxedPrecision
+OpDecorate %79 RelaxedPrecision
+OpDecorate %98 RelaxedPrecision
+OpDecorate %101 RelaxedPrecision
+OpDecorate %110 RelaxedPrecision
+OpDecorate %102 RelaxedPrecision
+OpDecorate %112 RelaxedPrecision
+OpDecorate %104 RelaxedPrecision
+OpDecorate %113 RelaxedPrecision
+OpDecorate %114 RelaxedPrecision
+OpDecorate %116 RelaxedPrecision
+OpDecorate %119 RelaxedPrecision
+OpDecorate %121 RelaxedPrecision
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%16 = OpTypeImage %float 1D 1 0 0 1 Unknown
+%_ptr_UniformConstant_16 = OpTypePointer UniformConstant %16
+%g_tTex1df4 = OpVariable %_ptr_UniformConstant_16 UniformConstant
+%20 = OpTypeSampler
+%_ptr_UniformConstant_20 = OpTypePointer UniformConstant %20
+%g_sSamp = OpVariable %_ptr_UniformConstant_20 UniformConstant
+%24 = OpTypeSampledImage %16
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%float_0_100000001 = OpConstant %float 0.100000001
+%cbuff = OpTypeStruct %float %float
+%_ptr_Uniform_cbuff = OpTypePointer Uniform %cbuff
+%_ = OpVariable %_ptr_Uniform_cbuff Uniform
+%_ptr_Uniform_float = OpTypePointer Uniform %float
+%v2float = OpTypeVector %float 2
+%int_1 = OpConstant %int 1
+%float_0_200000003 = OpConstant %float 0.200000003
+%_ptr_Input_float = OpTypePointer Input %float
+%i_Tex0 = OpVariable %_ptr_Input_float Input
+%i_Tex1 = OpVariable %_ptr_Input_float Input
+%_ptr_Output_float = OpTypePointer Output %float
+%_entryPointOutput_Color = OpVariable %_ptr_Output_float Output
+%float_0_5 = OpConstant %float 0.5
+)";
+
+  const std::string defs_after =
+      R"(OpCapability Shader
+OpCapability Sampled1D
+OpCapability Float16
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %i_Tex0 %i_Tex1 %_entryPointOutput_Color
+OpExecutionMode %main OriginUpperLeft
+OpSource HLSL 500
+OpName %main "main"
+OpName %g_tTex1df4 "g_tTex1df4"
+OpName %g_sSamp "g_sSamp"
+OpName %cbuff "cbuff"
+OpMemberName %cbuff 0 "c1"
+OpMemberName %cbuff 1 "c2"
+OpName %_ ""
+OpName %i_Tex0 "i.Tex0"
+OpName %i_Tex1 "i.Tex1"
+OpName %_entryPointOutput_Color "@entryPointOutput.Color"
+OpDecorate %g_tTex1df4 DescriptorSet 0
+OpDecorate %g_tTex1df4 Binding 0
+OpDecorate %g_sSamp DescriptorSet 0
+OpDecorate %g_sSamp Binding 0
+OpMemberDecorate %cbuff 0 Offset 0
+OpMemberDecorate %cbuff 1 Offset 4
+OpDecorate %cbuff Block
+OpDecorate %_ DescriptorSet 0
+OpDecorate %_ Binding 1
+OpDecorate %i_Tex0 Location 0
+OpDecorate %i_Tex1 Location 1
+OpDecorate %_entryPointOutput_Color Location 0
+%void = OpTypeVoid
+%25 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%27 = OpTypeImage %float 1D 1 0 0 1 Unknown
+%_ptr_UniformConstant_27 = OpTypePointer UniformConstant %27
+%g_tTex1df4 = OpVariable %_ptr_UniformConstant_27 UniformConstant
+%29 = OpTypeSampler
+%_ptr_UniformConstant_29 = OpTypePointer UniformConstant %29
+%g_sSamp = OpVariable %_ptr_UniformConstant_29 UniformConstant
+%31 = OpTypeSampledImage %27
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%float_0_100000001 = OpConstant %float 0.100000001
+%cbuff = OpTypeStruct %float %float
+%_ptr_Uniform_cbuff = OpTypePointer Uniform %cbuff
+%_ = OpVariable %_ptr_Uniform_cbuff Uniform
+%_ptr_Uniform_float = OpTypePointer Uniform %float
+%v2float = OpTypeVector %float 2
+%int_1 = OpConstant %int 1
+%float_0_200000003 = OpConstant %float 0.200000003
+%_ptr_Input_float = OpTypePointer Input %float
+%i_Tex0 = OpVariable %_ptr_Input_float Input
+%i_Tex1 = OpVariable %_ptr_Input_float Input
+%_ptr_Output_float = OpTypePointer Output %float
+%_entryPointOutput_Color = OpVariable %_ptr_Output_float Output
+%float_0_5 = OpConstant %float 0.5
+%half = OpTypeFloat 16
+%v2half = OpTypeVector %half 2
+)";
+
+  const std::string func_before =
+      R"(%main = OpFunction %void None %3
+%5 = OpLabel
+%76 = OpLoad %float %i_Tex0
+%79 = OpLoad %float %i_Tex1
+%93 = OpLoad %16 %g_tTex1df4
+%94 = OpLoad %20 %g_sSamp
+%95 = OpSampledImage %24 %93 %94
+%98 = OpFMul %float %76 %float_0_100000001
+%99 = OpAccessChain %_ptr_Uniform_float %_ %int_0
+%100 = OpLoad %float %99
+%101 = OpFAdd %float %100 %float_0_100000001
+%102 = OpCompositeConstruct %v2float %98 %101
+%104 = OpImageSampleDrefImplicitLod %float %95 %102 %101
+%105 = OpLoad %16 %g_tTex1df4
+%106 = OpLoad %20 %g_sSamp
+%107 = OpSampledImage %24 %105 %106
+%110 = OpFMul %float %79 %float_0_200000003
+%111 = OpAccessChain %_ptr_Uniform_float %_ %int_1
+%112 = OpLoad %float %111
+%113 = OpFAdd %float %112 %float_0_200000003
+%114 = OpCompositeConstruct %v2float %110 %113
+%116 = OpImageSampleDrefImplicitLod %float %107 %114 %113
+%119 = OpFAdd %float %104 %116
+%121 = OpFMul %float %119 %float_0_5
+OpStore %_entryPointOutput_Color %121
+OpReturn
+OpFunctionEnd
+)";
+
+  const std::string func_after =
+      R"(%main = OpFunction %void None %25
+%43 = OpLabel
+%11 = OpLoad %float %i_Tex0
+%12 = OpLoad %float %i_Tex1
+%44 = OpLoad %27 %g_tTex1df4
+%45 = OpLoad %29 %g_sSamp
+%46 = OpSampledImage %31 %44 %45
+%53 = OpFConvert %half %11
+%54 = OpFConvert %half %float_0_100000001
+%13 = OpFMul %half %53 %54
+%47 = OpAccessChain %_ptr_Uniform_float %_ %int_0
+%10 = OpLoad %float %47
+%55 = OpFConvert %half %10
+%56 = OpFConvert %half %float_0_100000001
+%14 = OpFAdd %half %55 %56
+%16 = OpCompositeConstruct %v2half %13 %14
+%58 = OpFConvert %float %14
+%18 = OpImageSampleDrefImplicitLod %float %46 %16 %58
+%48 = OpLoad %27 %g_tTex1df4
+%49 = OpLoad %29 %g_sSamp
+%50 = OpSampledImage %31 %48 %49
+%59 = OpFConvert %half %12
+%60 = OpFConvert %half %float_0_200000003
+%15 = OpFMul %half %59 %60
+%51 = OpAccessChain %_ptr_Uniform_float %_ %int_1
+%17 = OpLoad %float %51
+%61 = OpFConvert %half %17
+%62 = OpFConvert %half %float_0_200000003
+%19 = OpFAdd %half %61 %62
+%20 = OpCompositeConstruct %v2half %15 %19
+%63 = OpFConvert %float %19
+%21 = OpImageSampleDrefImplicitLod %float %50 %20 %63
+%64 = OpFConvert %half %18
+%65 = OpFConvert %half %21
+%22 = OpFAdd %half %64 %65
+%66 = OpFConvert %half %float_0_5
+%23 = OpFMul %half %22 %66
+%67 = OpFConvert %float %23
+OpStore %_entryPointOutput_Color %67
+OpReturn
+OpFunctionEnd
+)";
+
+  SinglePassRunAndCheck<ConvertToHalfPass>(defs_before + func_before,
+                                           defs_after + func_after, true, true);
+}
+
+TEST_F(ConvertToHalfTest, ConvertToHalfWithVectorMatrixMult) {
+  // The resulting SPIR-V was processed with --relax-float-ops.
+  //
+  // clang-format off
+  //
+  // SamplerState       g_sSamp : register(s0);
+  // uniform Texture1D <float4> g_tTex1df4 : register(t0);
+  //
+  // struct PS_OUTPUT
+  // {
+  //   float4 Color : SV_Target0;
+  // };
+  //
+  // cbuffer cbuff{
+  //   float4x4 M;
+  // }
+  //
+  // PS_OUTPUT main()
+  // {
+  //  PS_OUTPUT psout;
+  //  float4 txval10 = g_tTex1df4.Sample(g_sSamp, 0.1);
+  //  float4 t = mul(txval10, M);
+  //  psout.Color = t;
+  //  return psout;
+  //}
+  //
+  // clang-format on
+
+  const std::string defs_before =
+      R"(OpCapability Shader
+OpCapability Sampled1D
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %_entryPointOutput_Color
+OpExecutionMode %main OriginUpperLeft
+OpSource HLSL 500
+OpName %main "main"
+OpName %g_tTex1df4 "g_tTex1df4"
+OpName %g_sSamp "g_sSamp"
+OpName %cbuff "cbuff"
+OpMemberName %cbuff 0 "M"
+OpName %_ ""
+OpName %_entryPointOutput_Color "@entryPointOutput.Color"
+OpDecorate %g_tTex1df4 DescriptorSet 0
+OpDecorate %g_tTex1df4 Binding 0
+OpDecorate %g_sSamp DescriptorSet 0
+OpDecorate %g_sSamp Binding 0
+OpMemberDecorate %cbuff 0 RowMajor
+OpMemberDecorate %cbuff 0 Offset 0
+OpMemberDecorate %cbuff 0 MatrixStride 16
+OpDecorate %cbuff Block
+OpDecorate %_ DescriptorSet 0
+OpDecorate %_ Binding 1
+OpDecorate %_entryPointOutput_Color Location 0
+OpDecorate %56 RelaxedPrecision
+OpDecorate %58 RelaxedPrecision
+OpDecorate %60 RelaxedPrecision
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%14 = OpTypeImage %float 1D 0 0 0 1 Unknown
+%_ptr_UniformConstant_14 = OpTypePointer UniformConstant %14
+%g_tTex1df4 = OpVariable %_ptr_UniformConstant_14 UniformConstant
+%18 = OpTypeSampler
+%_ptr_UniformConstant_18 = OpTypePointer UniformConstant %18
+%g_sSamp = OpVariable %_ptr_UniformConstant_18 UniformConstant
+%22 = OpTypeSampledImage %14
+%float_0_100000001 = OpConstant %float 0.100000001
+%mat4v4float = OpTypeMatrix %v4float 4
+%cbuff = OpTypeStruct %mat4v4float
+%_ptr_Uniform_cbuff = OpTypePointer Uniform %cbuff
+%_ = OpVariable %_ptr_Uniform_cbuff Uniform
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%_ptr_Uniform_mat4v4float = OpTypePointer Uniform %mat4v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%_entryPointOutput_Color = OpVariable %_ptr_Output_v4float Output
+)";
+
+  const std::string defs_after =
+      R"(OpCapability Shader
+OpCapability Sampled1D
+OpCapability Float16
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %_entryPointOutput_Color
+OpExecutionMode %main OriginUpperLeft
+OpSource HLSL 500
+OpName %main "main"
+OpName %g_tTex1df4 "g_tTex1df4"
+OpName %g_sSamp "g_sSamp"
+OpName %cbuff "cbuff"
+OpMemberName %cbuff 0 "M"
+OpName %_ ""
+OpName %_entryPointOutput_Color "@entryPointOutput.Color"
+OpDecorate %g_tTex1df4 DescriptorSet 0
+OpDecorate %g_tTex1df4 Binding 0
+OpDecorate %g_sSamp DescriptorSet 0
+OpDecorate %g_sSamp Binding 0
+OpMemberDecorate %cbuff 0 RowMajor
+OpMemberDecorate %cbuff 0 Offset 0
+OpMemberDecorate %cbuff 0 MatrixStride 16
+OpDecorate %cbuff Block
+OpDecorate %_ DescriptorSet 0
+OpDecorate %_ Binding 1
+OpDecorate %_entryPointOutput_Color Location 0
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%14 = OpTypeImage %float 1D 0 0 0 1 Unknown
+%_ptr_UniformConstant_14 = OpTypePointer UniformConstant %14
+%g_tTex1df4 = OpVariable %_ptr_UniformConstant_14 UniformConstant
+%18 = OpTypeSampler
+%_ptr_UniformConstant_18 = OpTypePointer UniformConstant %18
+%g_sSamp = OpVariable %_ptr_UniformConstant_18 UniformConstant
+%22 = OpTypeSampledImage %14
+%float_0_100000001 = OpConstant %float 0.100000001
+%mat4v4float = OpTypeMatrix %v4float 4
+%cbuff = OpTypeStruct %mat4v4float
+%_ptr_Uniform_cbuff = OpTypePointer Uniform %cbuff
+%_ = OpVariable %_ptr_Uniform_cbuff Uniform
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%_ptr_Uniform_mat4v4float = OpTypePointer Uniform %mat4v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%_entryPointOutput_Color = OpVariable %_ptr_Output_v4float Output
+%half = OpTypeFloat 16
+%v4half = OpTypeVector %half 4
+%mat4v4half = OpTypeMatrix %v4half 4
+)";
+
+  const std::string func_before =
+      R"(%main = OpFunction %void None %3
+%5 = OpLabel
+%53 = OpLoad %14 %g_tTex1df4
+%54 = OpLoad %18 %g_sSamp
+%55 = OpSampledImage %22 %53 %54
+%56 = OpImageSampleImplicitLod %v4float %55 %float_0_100000001
+%57 = OpAccessChain %_ptr_Uniform_mat4v4float %_ %int_0
+%58 = OpLoad %mat4v4float %57
+%60 = OpMatrixTimesVector %v4float %58 %56
+OpStore %_entryPointOutput_Color %60
+OpReturn
+OpFunctionEnd
+)";
+
+  const std::string func_after =
+      R"(%main = OpFunction %void None %3
+%5 = OpLabel
+%53 = OpLoad %14 %g_tTex1df4
+%54 = OpLoad %18 %g_sSamp
+%55 = OpSampledImage %22 %53 %54
+%56 = OpImageSampleImplicitLod %v4float %55 %float_0_100000001
+%57 = OpAccessChain %_ptr_Uniform_mat4v4float %_ %int_0
+%58 = OpLoad %mat4v4float %57
+%67 = OpCompositeExtract %v4float %58 0
+%68 = OpFConvert %v4half %67
+%69 = OpCompositeExtract %v4float %58 1
+%70 = OpFConvert %v4half %69
+%71 = OpCompositeExtract %v4float %58 2
+%72 = OpFConvert %v4half %71
+%73 = OpCompositeExtract %v4float %58 3
+%74 = OpFConvert %v4half %73
+%75 = OpCompositeConstruct %mat4v4half %68 %70 %72 %74
+%64 = OpCopyObject %mat4v4float %58
+%65 = OpFConvert %v4half %56
+%60 = OpMatrixTimesVector %v4half %75 %65
+%66 = OpFConvert %v4float %60
+OpStore %_entryPointOutput_Color %66
+OpReturn
+OpFunctionEnd
+)";
+
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndCheck<ConvertToHalfPass>(defs_before + func_before,
+                                           defs_after + func_after, true, true);
+}
+
+TEST_F(ConvertToHalfTest, ConvertToHalfWithPhi) {
+  // The resulting SPIR-V was processed with --relax-float-ops.
+  //
+  // clang-format off
+  //
+  // SamplerState       g_sSamp : register(s0);
+  // uniform Texture1D <float4> g_tTex1df4 : register(t0);
+  //
+  // struct PS_OUTPUT
+  // {
+  //   float4 Color : SV_Target0;
+  // };
+  //
+  // cbuffer cbuff{
+  //   bool b;
+  //   float4x4 M;
+  // }
+  //
+  // PS_OUTPUT main()
+  // {
+  //   PS_OUTPUT psout;
+  //   float4 t;
+  //
+  //   if (b)
+  //     t = g_tTex1df4.Sample(g_sSamp, 0.1);
+  //   else
+  //     t = float4(0.0, 0.0, 0.0, 0.0);
+  //
+  //   float4 t2 = t * 2.0;
+  //   psout.Color = t2;
+  //   return psout;
+  // }
+  //
+  // clang-format on
+
+  const std::string defs_before =
+      R"(OpCapability Shader
+OpCapability Sampled1D
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %_entryPointOutput_Color
+OpExecutionMode %main OriginUpperLeft
+OpSource HLSL 500
+OpName %main "main"
+OpName %cbuff "cbuff"
+OpMemberName %cbuff 0 "b"
+OpMemberName %cbuff 1 "M"
+OpName %_ ""
+OpName %g_tTex1df4 "g_tTex1df4"
+OpName %g_sSamp "g_sSamp"
+OpName %_entryPointOutput_Color "@entryPointOutput.Color"
+OpMemberDecorate %cbuff 0 Offset 0
+OpMemberDecorate %cbuff 1 RowMajor
+OpMemberDecorate %cbuff 1 Offset 16
+OpMemberDecorate %cbuff 1 MatrixStride 16
+OpDecorate %cbuff Block
+OpDecorate %_ DescriptorSet 0
+OpDecorate %_ Binding 1
+OpDecorate %g_tTex1df4 DescriptorSet 0
+OpDecorate %g_tTex1df4 Binding 0
+OpDecorate %g_sSamp DescriptorSet 0
+OpDecorate %g_sSamp Binding 0
+OpDecorate %_entryPointOutput_Color Location 0
+OpDecorate %72 RelaxedPrecision
+OpDecorate %85 RelaxedPrecision
+OpDecorate %74 RelaxedPrecision
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%uint = OpTypeInt 32 0
+%mat4v4float = OpTypeMatrix %v4float 4
+%cbuff = OpTypeStruct %uint %mat4v4float
+%_ptr_Uniform_cbuff = OpTypePointer Uniform %cbuff
+%_ = OpVariable %_ptr_Uniform_cbuff Uniform
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%_ptr_Uniform_uint = OpTypePointer Uniform %uint
+%bool = OpTypeBool
+%uint_0 = OpConstant %uint 0
+%29 = OpTypeImage %float 1D 0 0 0 1 Unknown
+%_ptr_UniformConstant_29 = OpTypePointer UniformConstant %29
+%g_tTex1df4 = OpVariable %_ptr_UniformConstant_29 UniformConstant
+%33 = OpTypeSampler
+%_ptr_UniformConstant_33 = OpTypePointer UniformConstant %33
+%g_sSamp = OpVariable %_ptr_UniformConstant_33 UniformConstant
+%37 = OpTypeSampledImage %29
+%float_0_100000001 = OpConstant %float 0.100000001
+%float_0 = OpConstant %float 0
+%43 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
+%float_2 = OpConstant %float 2
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%_entryPointOutput_Color = OpVariable %_ptr_Output_v4float Output
+)";
+
+  const std::string defs_after =
+      R"(OpCapability Shader
+OpCapability Sampled1D
+OpCapability Float16
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %_entryPointOutput_Color
+OpExecutionMode %main OriginUpperLeft
+OpSource HLSL 500
+OpName %main "main"
+OpName %cbuff "cbuff"
+OpMemberName %cbuff 0 "b"
+OpMemberName %cbuff 1 "M"
+OpName %_ ""
+OpName %g_tTex1df4 "g_tTex1df4"
+OpName %g_sSamp "g_sSamp"
+OpName %_entryPointOutput_Color "@entryPointOutput.Color"
+OpMemberDecorate %cbuff 0 Offset 0
+OpMemberDecorate %cbuff 1 RowMajor
+OpMemberDecorate %cbuff 1 Offset 16
+OpMemberDecorate %cbuff 1 MatrixStride 16
+OpDecorate %cbuff Block
+OpDecorate %_ DescriptorSet 0
+OpDecorate %_ Binding 1
+OpDecorate %g_tTex1df4 DescriptorSet 0
+OpDecorate %g_tTex1df4 Binding 0
+OpDecorate %g_sSamp DescriptorSet 0
+OpDecorate %g_sSamp Binding 0
+OpDecorate %_entryPointOutput_Color Location 0
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%uint = OpTypeInt 32 0
+%mat4v4float = OpTypeMatrix %v4float 4
+%cbuff = OpTypeStruct %uint %mat4v4float
+%_ptr_Uniform_cbuff = OpTypePointer Uniform %cbuff
+%_ = OpVariable %_ptr_Uniform_cbuff Uniform
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%_ptr_Uniform_uint = OpTypePointer Uniform %uint
+%bool = OpTypeBool
+%uint_0 = OpConstant %uint 0
+%29 = OpTypeImage %float 1D 0 0 0 1 Unknown
+%_ptr_UniformConstant_29 = OpTypePointer UniformConstant %29
+%g_tTex1df4 = OpVariable %_ptr_UniformConstant_29 UniformConstant
+%33 = OpTypeSampler
+%_ptr_UniformConstant_33 = OpTypePointer UniformConstant %33
+%g_sSamp = OpVariable %_ptr_UniformConstant_33 UniformConstant
+%37 = OpTypeSampledImage %29
+%float_0_100000001 = OpConstant %float 0.100000001
+%float_0 = OpConstant %float 0
+%43 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
+%float_2 = OpConstant %float 2
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%_entryPointOutput_Color = OpVariable %_ptr_Output_v4float Output
+%half = OpTypeFloat 16
+%v4half = OpTypeVector %half 4
+)";
+
+  const std::string func_before =
+      R"(%main = OpFunction %void None %3
+%5 = OpLabel
+%63 = OpAccessChain %_ptr_Uniform_uint %_ %int_0
+%64 = OpLoad %uint %63
+%65 = OpINotEqual %bool %64 %uint_0
+OpSelectionMerge %66 None
+OpBranchConditional %65 %67 %68
+%67 = OpLabel
+%69 = OpLoad %29 %g_tTex1df4
+%70 = OpLoad %33 %g_sSamp
+%71 = OpSampledImage %37 %69 %70
+%72 = OpImageSampleImplicitLod %v4float %71 %float_0_100000001
+OpBranch %66
+%68 = OpLabel
+OpBranch %66
+%66 = OpLabel
+%85 = OpPhi %v4float %72 %67 %43 %68
+%74 = OpVectorTimesScalar %v4float %85 %float_2
+OpStore %_entryPointOutput_Color %74
+OpReturn
+OpFunctionEnd
+)";
+
+  const std::string func_after =
+      R"(%main = OpFunction %void None %3
+%5 = OpLabel
+%63 = OpAccessChain %_ptr_Uniform_uint %_ %int_0
+%64 = OpLoad %uint %63
+%65 = OpINotEqual %bool %64 %uint_0
+OpSelectionMerge %66 None
+OpBranchConditional %65 %67 %68
+%67 = OpLabel
+%69 = OpLoad %29 %g_tTex1df4
+%70 = OpLoad %33 %g_sSamp
+%71 = OpSampledImage %37 %69 %70
+%72 = OpImageSampleImplicitLod %v4float %71 %float_0_100000001
+%88 = OpFConvert %v4half %72
+OpBranch %66
+%68 = OpLabel
+%89 = OpFConvert %v4half %43
+OpBranch %66
+%66 = OpLabel
+%85 = OpPhi %v4half %88 %67 %89 %68
+%90 = OpFConvert %half %float_2
+%74 = OpVectorTimesScalar %v4half %85 %90
+%91 = OpFConvert %v4float %74
+OpStore %_entryPointOutput_Color %91
+OpReturn
+OpFunctionEnd
+)";
+
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndCheck<ConvertToHalfPass>(defs_before + func_before,
+                                           defs_after + func_after, true, true);
+}
+
+TEST_F(ConvertToHalfTest, ConvertToHalfWithLoopAndFConvert) {
+  // The resulting SPIR-V was processed with --relax-float-ops.
+  //
+  // The loop causes an FConvert to be generated at the bottom of the loop
+  // for the Phi. The FConvert is later processed and turned into a (dead)
+  // copy.
+  //
+  // clang-format off
+  //
+  // struct PS_OUTPUT
+  // {
+  //   float4 Color : SV_Target0;
+  // };
+  //
+  // cbuffer cbuff{
+  //   float4 a[10];
+  // }
+  //
+  // PS_OUTPUT main()
+  // {
+  //   PS_OUTPUT psout;
+  //   float4 t = 0.0;;
+  //
+  //   for (int i = 0; i<10; ++i)
+  //     t = t + a[i];
+  //
+  //   float4 t2 = t / 10.0;
+  //   psout.Color = t2;
+  //   return psout;
+  // }
+  //
+  // clang-format on
+
+  const std::string defs_before =
+      R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %_entryPointOutput_Color
+OpExecutionMode %main OriginUpperLeft
+OpSource HLSL 500
+OpName %main "main"
+OpName %cbuff "cbuff"
+OpMemberName %cbuff 0 "a"
+OpName %_ ""
+OpName %_entryPointOutput_Color "@entryPointOutput.Color"
+OpDecorate %_arr_v4float_uint_10 ArrayStride 16
+OpMemberDecorate %cbuff 0 Offset 0
+OpDecorate %cbuff Block
+OpDecorate %_ DescriptorSet 0
+OpDecorate %_ Binding 0
+OpDecorate %_entryPointOutput_Color Location 0
+OpDecorate %96 RelaxedPrecision
+OpDecorate %81 RelaxedPrecision
+OpDecorate %75 RelaxedPrecision
+OpDecorate %76 RelaxedPrecision
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%float_0 = OpConstant %float 0
+%15 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%int_10 = OpConstant %int 10
+%bool = OpTypeBool
+%uint = OpTypeInt 32 0
+%uint_10 = OpConstant %uint 10
+%_arr_v4float_uint_10 = OpTypeArray %v4float %uint_10
+%cbuff = OpTypeStruct %_arr_v4float_uint_10
+%_ptr_Uniform_cbuff = OpTypePointer Uniform %cbuff
+%_ = OpVariable %_ptr_Uniform_cbuff Uniform
+%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float
+%int_1 = OpConstant %int 1
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%_entryPointOutput_Color = OpVariable %_ptr_Output_v4float Output
+%float_0_100000001 = OpConstant %float 0.100000001
+%94 = OpConstantComposite %v4float %float_0_100000001 %float_0_100000001 %float_0_100000001 %float_0_100000001
+)";
+
+  const std::string defs_after =
+      R"(OpCapability Shader
+OpCapability Float16
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %_entryPointOutput_Color
+OpExecutionMode %main OriginUpperLeft
+OpSource HLSL 500
+OpName %main "main"
+OpName %cbuff "cbuff"
+OpMemberName %cbuff 0 "a"
+OpName %_ ""
+OpName %_entryPointOutput_Color "@entryPointOutput.Color"
+OpDecorate %_arr_v4float_uint_10 ArrayStride 16
+OpMemberDecorate %cbuff 0 Offset 0
+OpDecorate %cbuff Block
+OpDecorate %_ DescriptorSet 0
+OpDecorate %_ Binding 0
+OpDecorate %_entryPointOutput_Color Location 0
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%float_0 = OpConstant %float 0
+%15 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%int_10 = OpConstant %int 10
+%bool = OpTypeBool
+%uint = OpTypeInt 32 0
+%uint_10 = OpConstant %uint 10
+%_arr_v4float_uint_10 = OpTypeArray %v4float %uint_10
+%cbuff = OpTypeStruct %_arr_v4float_uint_10
+%_ptr_Uniform_cbuff = OpTypePointer Uniform %cbuff
+%_ = OpVariable %_ptr_Uniform_cbuff Uniform
+%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float
+%int_1 = OpConstant %int 1
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%_entryPointOutput_Color = OpVariable %_ptr_Output_v4float Output
+%float_0_100000001 = OpConstant %float 0.100000001
+%94 = OpConstantComposite %v4float %float_0_100000001 %float_0_100000001 %float_0_100000001 %float_0_100000001
+%half = OpTypeFloat 16
+%v4half = OpTypeVector %half 4
+)";
+
+  const std::string func_before =
+      R"(%main = OpFunction %void None %3
+%5 = OpLabel
+OpBranch %65
+%65 = OpLabel
+%96 = OpPhi %v4float %15 %5 %76 %71
+%95 = OpPhi %int %int_0 %5 %78 %71
+%70 = OpSLessThan %bool %95 %int_10
+OpLoopMerge %66 %71 None
+OpBranchConditional %70 %71 %66
+%71 = OpLabel
+%74 = OpAccessChain %_ptr_Uniform_v4float %_ %int_0 %95
+%75 = OpLoad %v4float %74
+%76 = OpFAdd %v4float %96 %75
+%78 = OpIAdd %int %95 %int_1
+OpBranch %65
+%66 = OpLabel
+%81 = OpFMul %v4float %96 %94
+OpStore %_entryPointOutput_Color %81
+OpReturn
+OpFunctionEnd
+)";
+
+  const std::string func_after =
+      R"(%main = OpFunction %void None %3
+%5 = OpLabel
+%99 = OpFConvert %v4half %15
+OpBranch %65
+%65 = OpLabel
+%96 = OpPhi %v4half %99 %5 %76 %71
+%95 = OpPhi %int %int_0 %5 %78 %71
+%70 = OpSLessThan %bool %95 %int_10
+OpLoopMerge %66 %71 None
+OpBranchConditional %70 %71 %66
+%71 = OpLabel
+%74 = OpAccessChain %_ptr_Uniform_v4float %_ %int_0 %95
+%75 = OpLoad %v4float %74
+%103 = OpFConvert %v4half %75
+%76 = OpFAdd %v4half %96 %103
+%78 = OpIAdd %int %95 %int_1
+%100 = OpCopyObject %v4half %76
+OpBranch %65
+%66 = OpLabel
+%101 = OpFConvert %v4half %94
+%81 = OpFMul %v4half %96 %101
+%102 = OpFConvert %v4float %81
+OpStore %_entryPointOutput_Color %102
+OpReturn
+OpFunctionEnd
+)";
+
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndCheck<ConvertToHalfPass>(defs_before + func_before,
+                                           defs_after + func_after, true, true);
+}
+
+TEST_F(ConvertToHalfTest, ConvertToHalfWithExtracts) {
+  // The resulting SPIR-V was processed with --relax-float-ops.
+  //
+  // The extra converts in the func_after can be DCE'd.
+  //
+  // clang-format off
+  //
+  // SamplerState       g_sSamp : register(s0);
+  // uniform Texture1D <float4> g_tTex1df4 : register(t0);
+  //
+  // struct PS_INPUT
+  // {
+  //   float Tex0 : TEXCOORD0;
+  // };
+  //
+  // struct PS_OUTPUT
+  // {
+  //   float4 Color : SV_Target0;
+  // };
+  //
+  // cbuffer cbuff{
+  //   float c;
+  // }
+  //
+  // PS_OUTPUT main(PS_INPUT i)
+  // {
+  //   PS_OUTPUT psout;
+  //   float4 tx = g_tTex1df4.Sample(g_sSamp, i.Tex0);
+  //   float4 t = float4(tx.y, tx.z, tx.x, tx.w) * c;
+  //   psout.Color = t;
+  //   return psout;
+  // }
+  //
+  // clang-format on
+
+  const std::string defs_before =
+      R"(OpCapability Shader
+OpCapability Sampled1D
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %i_Tex0 %_entryPointOutput_Color
+OpExecutionMode %main OriginUpperLeft
+OpSource HLSL 500
+OpName %main "main"
+OpName %g_tTex1df4 "g_tTex1df4"
+OpName %g_sSamp "g_sSamp"
+OpName %cbuff "cbuff"
+OpMemberName %cbuff 0 "c"
+OpName %_ ""
+OpName %i_Tex0 "i.Tex0"
+OpName %_entryPointOutput_Color "@entryPointOutput.Color"
+OpDecorate %g_tTex1df4 DescriptorSet 0
+OpDecorate %g_tTex1df4 Binding 0
+OpDecorate %g_sSamp DescriptorSet 0
+OpDecorate %g_sSamp Binding 0
+OpMemberDecorate %cbuff 0 Offset 0
+OpDecorate %cbuff Block
+OpDecorate %_ DescriptorSet 0
+OpDecorate %_ Binding 1
+OpDecorate %i_Tex0 Location 0
+OpDecorate %_entryPointOutput_Color Location 0
+OpDecorate %65 RelaxedPrecision
+OpDecorate %82 RelaxedPrecision
+OpDecorate %84 RelaxedPrecision
+OpDecorate %86 RelaxedPrecision
+OpDecorate %88 RelaxedPrecision
+OpDecorate %90 RelaxedPrecision
+OpDecorate %91 RelaxedPrecision
+OpDecorate %93 RelaxedPrecision
+OpDecorate %94 RelaxedPrecision
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%17 = OpTypeImage %float 1D 0 0 0 1 Unknown
+%_ptr_UniformConstant_17 = OpTypePointer UniformConstant %17
+%g_tTex1df4 = OpVariable %_ptr_UniformConstant_17 UniformConstant
+%21 = OpTypeSampler
+%_ptr_UniformConstant_21 = OpTypePointer UniformConstant %21
+%g_sSamp = OpVariable %_ptr_UniformConstant_21 UniformConstant
+%25 = OpTypeSampledImage %17
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%cbuff = OpTypeStruct %float
+%_ptr_Uniform_cbuff = OpTypePointer Uniform %cbuff
+%_ = OpVariable %_ptr_Uniform_cbuff Uniform
+%_ptr_Uniform_float = OpTypePointer Uniform %float
+%_ptr_Input_float = OpTypePointer Input %float
+%i_Tex0 = OpVariable %_ptr_Input_float Input
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%_entryPointOutput_Color = OpVariable %_ptr_Output_v4float Output
+)";
+
+  const std::string defs_after =
+      R"(OpCapability Shader
+OpCapability Sampled1D
+OpCapability Float16
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %i_Tex0 %_entryPointOutput_Color
+OpExecutionMode %main OriginUpperLeft
+OpSource HLSL 500
+OpName %main "main"
+OpName %g_tTex1df4 "g_tTex1df4"
+OpName %g_sSamp "g_sSamp"
+OpName %cbuff "cbuff"
+OpMemberName %cbuff 0 "c"
+OpName %_ ""
+OpName %i_Tex0 "i.Tex0"
+OpName %_entryPointOutput_Color "@entryPointOutput.Color"
+OpDecorate %g_tTex1df4 DescriptorSet 0
+OpDecorate %g_tTex1df4 Binding 0
+OpDecorate %g_sSamp DescriptorSet 0
+OpDecorate %g_sSamp Binding 0
+OpMemberDecorate %cbuff 0 Offset 0
+OpDecorate %cbuff Block
+OpDecorate %_ DescriptorSet 0
+OpDecorate %_ Binding 1
+OpDecorate %i_Tex0 Location 0
+OpDecorate %_entryPointOutput_Color Location 0
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%17 = OpTypeImage %float 1D 0 0 0 1 Unknown
+%_ptr_UniformConstant_17 = OpTypePointer UniformConstant %17
+%g_tTex1df4 = OpVariable %_ptr_UniformConstant_17 UniformConstant
+%21 = OpTypeSampler
+%_ptr_UniformConstant_21 = OpTypePointer UniformConstant %21
+%g_sSamp = OpVariable %_ptr_UniformConstant_21 UniformConstant
+%25 = OpTypeSampledImage %17
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%cbuff = OpTypeStruct %float
+%_ptr_Uniform_cbuff = OpTypePointer Uniform %cbuff
+%_ = OpVariable %_ptr_Uniform_cbuff Uniform
+%_ptr_Uniform_float = OpTypePointer Uniform %float
+%_ptr_Input_float = OpTypePointer Input %float
+%i_Tex0 = OpVariable %_ptr_Input_float Input
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%_entryPointOutput_Color = OpVariable %_ptr_Output_v4float Output
+%half = OpTypeFloat 16
+%v4half = OpTypeVector %half 4
+)";
+
+  const std::string func_before =
+      R"(%main = OpFunction %void None %3
+%5 = OpLabel
+%65 = OpLoad %float %i_Tex0
+%77 = OpLoad %17 %g_tTex1df4
+%78 = OpLoad %21 %g_sSamp
+%79 = OpSampledImage %25 %77 %78
+%82 = OpImageSampleImplicitLod %v4float %79 %65
+%84 = OpCompositeExtract %float %82 1
+%86 = OpCompositeExtract %float %82 2
+%88 = OpCompositeExtract %float %82 0
+%90 = OpCompositeExtract %float %82 3
+%91 = OpCompositeConstruct %v4float %84 %86 %88 %90
+%92 = OpAccessChain %_ptr_Uniform_float %_ %int_0
+%93 = OpLoad %float %92
+%94 = OpVectorTimesScalar %v4float %91 %93
+OpStore %_entryPointOutput_Color %94
+OpReturn
+OpFunctionEnd
+)";
+
+  const std::string func_after =
+      R"(%main = OpFunction %void None %3
+%5 = OpLabel
+%65 = OpLoad %float %i_Tex0
+%77 = OpLoad %17 %g_tTex1df4
+%78 = OpLoad %21 %g_sSamp
+%79 = OpSampledImage %25 %77 %78
+%82 = OpImageSampleImplicitLod %v4float %79 %65
+%97 = OpFConvert %v4half %82
+%84 = OpCompositeExtract %half %97 1
+%98 = OpFConvert %v4half %82
+%86 = OpCompositeExtract %half %98 2
+%99 = OpFConvert %v4half %82
+%88 = OpCompositeExtract %half %99 0
+%100 = OpFConvert %v4half %82
+%90 = OpCompositeExtract %half %100 3
+%91 = OpCompositeConstruct %v4half %84 %86 %88 %90
+%92 = OpAccessChain %_ptr_Uniform_float %_ %int_0
+%93 = OpLoad %float %92
+%101 = OpFConvert %half %93
+%94 = OpVectorTimesScalar %v4half %91 %101
+%102 = OpFConvert %v4float %94
+OpStore %_entryPointOutput_Color %102
+OpReturn
+OpFunctionEnd
+)";
+
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndCheck<ConvertToHalfPass>(defs_before + func_before,
+                                           defs_after + func_after, true, 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 504ae67..1afee9c 100644
--- a/test/opt/copy_prop_array_test.cpp
+++ b/test/opt/copy_prop_array_test.cpp
@@ -1157,7 +1157,7 @@
 OpStore %23 %35
 %36 = OpAccessChain %_ptr_Function_v4float %23 %24
 %37 = OpLoad %v4float %36
-%39 = OpStore %36 %v4const
+      OpStore %36 %v4const
 OpStore %out_var_SV_Target %37
 OpReturn
 OpFunctionEnd
@@ -1571,6 +1571,57 @@
   SinglePassRunAndCheck<CopyPropagateArrays>(before, after, true, true);
 }
 
+TEST_F(CopyPropArrayPassTest, IndexIsNullConstnat) {
+  const std::string text = R"(
+; CHECK: [[var:%\w+]] = OpVariable {{%\w+}} Uniform
+; CHECK: [[null:%\w+]] = OpConstantNull %uint
+; CHECK: [[ac1:%\w+]] = OpAccessChain %_ptr_Uniform__arr_uint_uint_1 [[var]] %uint_0 %uint_0
+; CHECK: OpAccessChain %_ptr_Uniform_uint [[ac1]] [[null]]
+; CHECK-NEXT: OpReturn
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpSource HLSL 600
+               OpDecorate %myCBuffer DescriptorSet 0
+               OpDecorate %myCBuffer Binding 0
+               OpDecorate %_arr_v4float_uint_1 ArrayStride 16
+               OpMemberDecorate %MyConstantBuffer 0 Offset 0
+               OpMemberDecorate %type_myCBuffer 0 Offset 0
+               OpDecorate %type_myCBuffer Block
+       %uint = OpTypeInt 32 0
+      %int_0 = OpConstant %uint 0
+     %uint_1 = OpConstant %uint 1
+%_arr_v4float_uint_1 = OpTypeArray %uint %uint_1
+%MyConstantBuffer = OpTypeStruct %_arr_v4float_uint_1
+%type_myCBuffer = OpTypeStruct %MyConstantBuffer
+%_ptr_Uniform_type_myCBuffer = OpTypePointer Uniform %type_myCBuffer
+%_arr_v4float_uint_1_0 = OpTypeArray %uint %uint_1
+       %void = OpTypeVoid
+         %19 = OpTypeFunction %void
+%_ptr_Function_v4float = OpTypePointer Function %uint
+%_ptr_Uniform_MyConstantBuffer = OpTypePointer Uniform %MyConstantBuffer
+  %myCBuffer = OpVariable %_ptr_Uniform_type_myCBuffer Uniform
+%_ptr_Function__arr_v4float_uint_1_0 = OpTypePointer Function %_arr_v4float_uint_1_0
+         %23 = OpConstantNull %uint
+       %main = OpFunction %void None %19
+         %24 = OpLabel
+         %25 = OpVariable %_ptr_Function__arr_v4float_uint_1_0 Function
+         %26 = OpAccessChain %_ptr_Uniform_MyConstantBuffer %myCBuffer %int_0
+         %27 = OpLoad %MyConstantBuffer %26
+         %28 = OpCompositeExtract %_arr_v4float_uint_1 %27 0
+         %29 = OpCompositeExtract %uint %28 0
+         %30 = OpCompositeConstruct %_arr_v4float_uint_1_0 %29
+               OpStore %25 %30
+         %31 = OpAccessChain %_ptr_Function_v4float %25 %23
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndMatch<CopyPropagateArrays>(text, true);
+}
+
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools
diff --git a/test/opt/decompose_initialized_variables_test.cpp b/test/opt/decompose_initialized_variables_test.cpp
index cdebb3f..06ba59a 100644
--- a/test/opt/decompose_initialized_variables_test.cpp
+++ b/test/opt/decompose_initialized_variables_test.cpp
@@ -24,9 +24,9 @@
 using DecomposeInitializedVariablesTest = PassTest<::testing::Test>;
 
 std::string single_entry_header = R"(OpCapability Shader
-OpCapability VulkanMemoryModelKHR
+OpCapability VulkanMemoryModel
 OpExtension "SPV_KHR_vulkan_memory_model"
-OpMemoryModel Logical VulkanKHR
+OpMemoryModel Logical Vulkan
 OpEntryPoint Vertex %1 "shader"
 %uint = OpTypeInt 32 0
 %uint_1 = OpConstant %uint 1
@@ -126,9 +126,9 @@
 }
 
 std::string multiple_entry_header = R"(OpCapability Shader
-OpCapability VulkanMemoryModelKHR
+OpCapability VulkanMemoryModel
 OpExtension "SPV_KHR_vulkan_memory_model"
-OpMemoryModel Logical VulkanKHR
+OpMemoryModel Logical Vulkan
 OpEntryPoint Vertex %1 "vertex"
 OpEntryPoint Fragment %2 "fragment"
 %uint = OpTypeInt 32 0
diff --git a/test/opt/eliminate_dead_member_test.cpp b/test/opt/eliminate_dead_member_test.cpp
index 7d5db7d..b6925d7 100644
--- a/test/opt/eliminate_dead_member_test.cpp
+++ b/test/opt/eliminate_dead_member_test.cpp
@@ -561,6 +561,7 @@
                OpName %main "main"
                OpDecorate %_Globals DescriptorSet 0
                OpDecorate %_Globals Binding 0
+               OpDecorate %_runtimearr_float ArrayStride 16
                OpMemberDecorate %type__Globals 0 Offset 0
                OpMemberDecorate %type__Globals 1 Offset 4
                OpMemberDecorate %type__Globals 2 Offset 16
diff --git a/test/opt/fold_spec_const_op_composite_test.cpp b/test/opt/fold_spec_const_op_composite_test.cpp
index c96dc8c..7eddf7e 100644
--- a/test/opt/fold_spec_const_op_composite_test.cpp
+++ b/test/opt/fold_spec_const_op_composite_test.cpp
@@ -112,8 +112,12 @@
       // clang-format off
       // scalar types
       "%bool = OpTypeBool",
+      "%ushort = OpTypeInt 16 0",
+      "%short = OpTypeInt 16 1",
       "%uint = OpTypeInt 32 0",
       "%int = OpTypeInt 32 1",
+      "%ulong = OpTypeInt 64 0",
+      "%long = OpTypeInt 64 1",
       "%float = OpTypeFloat 32",
       "%double = OpTypeFloat 64",
       // vector types
@@ -122,6 +126,8 @@
       "%v2int = OpTypeVector %int 2",
       "%v3int = OpTypeVector %int 3",
       "%v4int = OpTypeVector %int 4",
+      "%v2long = OpTypeVector %long 2",
+      "%v2ulong = OpTypeVector %ulong 2",
       "%v2float = OpTypeVector %float 2",
       "%v2double = OpTypeVector %double 2",
       // variable pointer types
@@ -145,6 +151,8 @@
       "%bool_null = OpConstantNull %bool",
       "%signed_zero = OpConstant %int 0",
       "%unsigned_zero = OpConstant %uint 0",
+      "%long_zero = OpConstant %long 0",
+      "%ulong_zero = OpConstant %ulong 0",
       "%signed_one = OpConstant %int 1",
       "%unsigned_one = OpConstant %uint 1",
       "%signed_two = OpConstant %int 2",
@@ -153,6 +161,7 @@
       "%unsigned_three = OpConstant %uint 3",
       "%signed_null = OpConstantNull %int",
       "%unsigned_null = OpConstantNull %uint",
+      "%signed_minus_one = OpConstant %int -1",
       // vector constants:
       "%bool_true_vec = OpConstantComposite %v2bool %bool_true %bool_true",
       "%bool_false_vec = OpConstantComposite %v2bool %bool_false %bool_false",
@@ -167,6 +176,7 @@
       "%unsigned_three_vec = OpConstantComposite %v2uint %unsigned_three %unsigned_three",
       "%signed_null_vec = OpConstantNull %v2int",
       "%unsigned_null_vec = OpConstantNull %v2uint",
+      "%signed_minus_one_vec = OpConstantComposite %v2int %signed_minus_one %signed_minus_one",
       "%v4int_0_1_2_3 = OpConstantComposite %v4int %signed_zero %signed_one %signed_two %signed_three",
       // clang-format on
   };
@@ -345,9 +355,9 @@
 // Tests for operations that resulting in different types.
 INSTANTIATE_TEST_SUITE_P(
     Cast, FoldSpecConstantOpAndCompositePassTest,
-    ::testing::ValuesIn(
-        std::vector<FoldSpecConstantOpAndCompositePassTestCase>({
-            // clang-format off
+    ::testing::ValuesIn(std::vector<
+                        FoldSpecConstantOpAndCompositePassTestCase>({
+        // clang-format off
             // int -> bool scalar
             {
               // original
@@ -575,8 +585,108 @@
                 "%spec_uint_from_null = OpConstantComposite %v2uint %unsigned_zero %unsigned_zero",
               },
             },
-            // clang-format on
-        })));
+
+            // UConvert scalar
+            {
+              // original
+              {
+                "%spec_uint_zero = OpSpecConstantOp %uint UConvert %bool_false",
+                "%spec_uint_one = OpSpecConstantOp %uint UConvert %bool_true",
+                "%spec_ulong_zero = OpSpecConstantOp %ulong UConvert %unsigned_zero",
+                "%spec_ulong_one = OpSpecConstantOp %ulong UConvert %unsigned_one",
+                "%spec_short_zero = OpSpecConstantOp %ushort UConvert %unsigned_zero",
+                "%spec_short_one = OpSpecConstantOp %ushort UConvert %unsigned_one",
+                "%uint_max = OpConstant %uint 4294967295",
+                "%spec_ushort_max = OpSpecConstantOp %ushort UConvert %uint_max",
+                "%uint_0xDDDDDDDD = OpConstant %uint 3722304989",
+                "%spec_ushort_0xDDDD = OpSpecConstantOp %ushort UConvert %uint_0xDDDDDDDD",
+              },
+              // expected
+              {
+                "%spec_uint_zero = OpConstant %uint 0",
+                "%spec_uint_one = OpConstant %uint 1",
+                "%spec_ulong_zero = OpConstant %ulong 0",
+                "%spec_ulong_one = OpConstant %ulong 1",
+                "%spec_short_zero = OpConstant %ushort 0",
+                "%spec_short_one = OpConstant %ushort 1",
+                "%uint_max = OpConstant %uint 4294967295",
+                "%spec_ushort_max = OpConstant %ushort 65535",
+                "%uint_0xDDDDDDDD = OpConstant %uint 3722304989",
+                "%spec_ushort_0xDDDD = OpConstant %ushort 56797",
+              },
+            },
+
+            // SConvert scalar
+            {
+              // original
+              {
+                "%spec_long_zero = OpSpecConstantOp %long SConvert %signed_zero",
+                "%spec_long_one = OpSpecConstantOp %long SConvert %signed_one",
+                "%spec_long_minus_one = OpSpecConstantOp %long SConvert %signed_minus_one",
+                "%spec_short_minus_one_trunc = OpSpecConstantOp %short SConvert %signed_minus_one",
+                "%int_2_to_17_minus_one = OpConstant %int 131071",
+                "%spec_short_minus_one_trunc2 = OpSpecConstantOp %short SConvert %int_2_to_17_minus_one",
+              },
+              // expected
+              {
+                "%spec_long_zero = OpConstant %long 0",
+                "%spec_long_one = OpConstant %long 1",
+                "%spec_long_minus_one = OpConstant %long -1",
+                "%spec_short_minus_one_trunc = OpConstant %short -1",
+                "%int_2_to_17_minus_one = OpConstant %int 131071",
+                "%spec_short_minus_one_trunc2 = OpConstant %short -1",
+              },
+            },
+
+            // UConvert vector
+            {
+              // original
+              {
+                "%spec_v2uint_zero = OpSpecConstantOp %v2uint UConvert %bool_false_vec",
+                "%spec_v2uint_one = OpSpecConstantOp %v2uint UConvert %bool_true_vec",
+                "%spec_v2ulong_zero = OpSpecConstantOp %v2ulong UConvert %unsigned_zero_vec",
+                "%spec_v2ulong_one = OpSpecConstantOp %v2ulong UConvert %unsigned_one_vec",
+              },
+              // expected
+              {
+                "%uint_0 = OpConstant %uint 0",
+                "%uint_0_0 = OpConstant %uint 0",
+                "%spec_v2uint_zero = OpConstantComposite %v2uint %unsigned_zero %unsigned_zero",
+                "%uint_1 = OpConstant %uint 1",
+                "%uint_1_0 = OpConstant %uint 1",
+                "%spec_v2uint_one = OpConstantComposite %v2uint %unsigned_one %unsigned_one",
+                "%ulong_0 = OpConstant %ulong 0",
+                "%ulong_0_0 = OpConstant %ulong 0",
+                "%spec_v2ulong_zero = OpConstantComposite %v2ulong %ulong_zero %ulong_zero",
+                "%ulong_1 = OpConstant %ulong 1",
+                "%ulong_1_0 = OpConstant %ulong 1",
+                "%spec_v2ulong_one = OpConstantComposite %v2ulong %ulong_1 %ulong_1",
+              },
+            },
+
+            // SConvert vector
+            {
+              // original
+              {
+                "%spec_v2long_zero = OpSpecConstantOp %v2long SConvert %signed_zero_vec",
+                "%spec_v2long_one = OpSpecConstantOp %v2long SConvert %signed_one_vec",
+                "%spec_v2long_minus_one = OpSpecConstantOp %v2long SConvert %signed_minus_one_vec",
+              },
+              // expected
+              {
+                "%long_0 = OpConstant %long 0",
+                "%long_0_0 = OpConstant %long 0",
+                "%spec_v2long_zero = OpConstantComposite %v2long %long_zero %long_zero",
+                "%long_1 = OpConstant %long 1",
+                "%long_1_0 = OpConstant %long 1",
+                "%spec_v2long_one = OpConstantComposite %v2long %long_1 %long_1",
+                "%long_n1 = OpConstant %long -1",
+                "%long_n1_0 = OpConstant %long -1",
+                "%spec_v2long_minus_one = OpConstantComposite %v2long %long_n1 %long_n1",
+              },
+            },
+        // clang-format on
+    })));
 
 // Tests about boolean scalar logical operations and comparison operations with
 // scalar int/uint type.
@@ -851,7 +961,7 @@
               {
                 "%int_n1 = OpConstant %int -1",
                 "%int_n1_0 = OpConstant %int -1",
-                "%v2int_minus_1 = OpConstantComposite %v2int %int_n1 %int_n1",
+                "%v2int_minus_1 = OpConstantComposite %v2int %signed_minus_one %signed_minus_one",
                 "%int_n2 = OpConstant %int -2",
                 "%int_n2_0 = OpConstant %int -2",
                 "%v2int_minus_2 = OpConstantComposite %v2int %int_n2 %int_n2",
@@ -956,13 +1066,13 @@
                 "%7_srem_3 = OpConstantComposite %v2int %signed_one %signed_one",
                 "%int_n1 = OpConstant %int -1",
                 "%int_n1_0 = OpConstant %int -1",
-                "%minus_7_srem_3 = OpConstantComposite %v2int %int_n1 %int_n1",
+                "%minus_7_srem_3 = OpConstantComposite %v2int %signed_minus_one %signed_minus_one",
                 "%int_1_1 = OpConstant %int 1",
                 "%int_1_2 = OpConstant %int 1",
                 "%7_srem_minus_3 = OpConstantComposite %v2int %signed_one %signed_one",
                 "%int_n1_1 = OpConstant %int -1",
                 "%int_n1_2 = OpConstant %int -1",
-                "%minus_7_srem_minus_3 = OpConstantComposite %v2int %int_n1 %int_n1",
+                "%minus_7_srem_minus_3 = OpConstantComposite %v2int %signed_minus_one %signed_minus_one",
                 // smod
                 "%int_1_3 = OpConstant %int 1",
                 "%int_1_4 = OpConstant %int 1",
@@ -975,7 +1085,7 @@
                 "%7_smod_minus_3 = OpConstantComposite %v2int %int_n2 %int_n2",
                 "%int_n1_3 = OpConstant %int -1",
                 "%int_n1_4 = OpConstant %int -1",
-                "%minus_7_smod_minus_3 = OpConstantComposite %v2int %int_n1 %int_n1",
+                "%minus_7_smod_minus_3 = OpConstantComposite %v2int %signed_minus_one %signed_minus_one",
                 // umod
                 "%uint_1 = OpConstant %uint 1",
                 "%uint_1_0 = OpConstant %uint 1",
@@ -1018,7 +1128,7 @@
                 "%unsigned_right_shift_logical = OpConstantComposite %v2uint %unsigned_one %unsigned_one",
                 "%int_n1 = OpConstant %int -1",
                 "%int_n1_0 = OpConstant %int -1",
-                "%signed_right_shift_arithmetic = OpConstantComposite %v2int %int_n1 %int_n1",
+                "%signed_right_shift_arithmetic = OpConstantComposite %v2int %signed_minus_one %signed_minus_one",
               },
             },
             // Skip folding if any vector operands or components of the operands
diff --git a/test/opt/fold_test.cpp b/test/opt/fold_test.cpp
index b5998c7..68a7d18 100644
--- a/test/opt/fold_test.cpp
+++ b/test/opt/fold_test.cpp
@@ -222,6 +222,7 @@
 %v2float_3_2 = OpConstantComposite %v2float %float_3 %float_2
 %v2float_4_4 = OpConstantComposite %v2float %float_4 %float_4
 %v2float_2_0p5 = OpConstantComposite %v2float %float_2 %float_0p5
+%v2float_0p2_0p5 = OpConstantComposite %v2float %float_0p2 %float_0p5
 %v2float_null = OpConstantNull %v2float
 %double_n1 = OpConstant %double -1
 %105 = OpConstant %double 0 ; Need a def with an numerical id to define id maps.
@@ -231,7 +232,9 @@
 %double_2 = OpConstant %double 2
 %double_3 = OpConstant %double 3
 %double_4 = OpConstant %double 4
+%double_5 = OpConstant %double 5
 %double_0p5 = OpConstant %double 0.5
+%double_0p2 = OpConstant %double 0.2
 %v2double_0_0 = OpConstantComposite %v2double %double_0 %double_0
 %v2double_2_2 = OpConstantComposite %v2double %double_2 %double_2
 %v2double_2_3 = OpConstantComposite %v2double %double_2 %double_3
@@ -557,7 +560,155 @@
           "%2 = OpSNegate %int %int_min\n" +
           "OpReturn\n" +
           "OpFunctionEnd",
-      2, std::numeric_limits<int32_t>::min())
+      2, std::numeric_limits<int32_t>::min()),
+  // Test case 30: fold UMin 3 4
+  InstructionFoldingCase<uint32_t>(
+      Header() + "%main = OpFunction %void None %void_func\n" +
+          "%main_lab = OpLabel\n" +
+          "%2 = OpExtInst %uint %1 UMin %uint_3 %uint_4\n" +
+          "OpReturn\n" +
+          "OpFunctionEnd",
+      2, 3),
+  // Test case 31: fold UMin 4 2
+  InstructionFoldingCase<uint32_t>(
+      Header() + "%main = OpFunction %void None %void_func\n" +
+          "%main_lab = OpLabel\n" +
+          "%2 = OpExtInst %uint %1 UMin %uint_4 %uint_2\n" +
+          "OpReturn\n" +
+          "OpFunctionEnd",
+      2, 2),
+  // Test case 32: fold SMin 3 4
+  InstructionFoldingCase<uint32_t>(
+      Header() + "%main = OpFunction %void None %void_func\n" +
+          "%main_lab = OpLabel\n" +
+          "%2 = OpExtInst %int %1 UMin %int_3 %int_4\n" +
+          "OpReturn\n" +
+          "OpFunctionEnd",
+      2, 3),
+  // Test case 33: fold SMin 4 2
+  InstructionFoldingCase<uint32_t>(
+      Header() + "%main = OpFunction %void None %void_func\n" +
+          "%main_lab = OpLabel\n" +
+          "%2 = OpExtInst %int %1 SMin %int_4 %int_2\n" +
+          "OpReturn\n" +
+          "OpFunctionEnd",
+      2, 2),
+  // Test case 34: fold UMax 3 4
+  InstructionFoldingCase<uint32_t>(
+      Header() + "%main = OpFunction %void None %void_func\n" +
+          "%main_lab = OpLabel\n" +
+          "%2 = OpExtInst %uint %1 UMax %uint_3 %uint_4\n" +
+          "OpReturn\n" +
+          "OpFunctionEnd",
+      2, 4),
+  // Test case 35: fold UMax 3 2
+  InstructionFoldingCase<uint32_t>(
+      Header() + "%main = OpFunction %void None %void_func\n" +
+          "%main_lab = OpLabel\n" +
+          "%2 = OpExtInst %uint %1 UMax %uint_3 %uint_2\n" +
+          "OpReturn\n" +
+          "OpFunctionEnd",
+      2, 3),
+  // Test case 36: fold SMax 3 4
+  InstructionFoldingCase<uint32_t>(
+      Header() + "%main = OpFunction %void None %void_func\n" +
+          "%main_lab = OpLabel\n" +
+          "%2 = OpExtInst %int %1 UMax %int_3 %int_4\n" +
+          "OpReturn\n" +
+          "OpFunctionEnd",
+      2, 4),
+  // Test case 37: fold SMax 3 2
+  InstructionFoldingCase<uint32_t>(
+      Header() + "%main = OpFunction %void None %void_func\n" +
+          "%main_lab = OpLabel\n" +
+          "%2 = OpExtInst %int %1 SMax %int_3 %int_2\n" +
+          "OpReturn\n" +
+          "OpFunctionEnd",
+      2, 3),
+  // Test case 38: fold UClamp 2 3 4
+  InstructionFoldingCase<uint32_t>(
+      Header() + "%main = OpFunction %void None %void_func\n" +
+          "%main_lab = OpLabel\n" +
+          "%2 = OpExtInst %uint %1 UClamp %uint_2 %uint_3 %uint_4\n" +
+          "OpReturn\n" +
+          "OpFunctionEnd",
+      2, 3),
+  // Test case 39: fold UClamp 2 0 4
+  InstructionFoldingCase<uint32_t>(
+      Header() + "%main = OpFunction %void None %void_func\n" +
+          "%main_lab = OpLabel\n" +
+          "%2 = OpExtInst %uint %1 UClamp %uint_2 %uint_0 %uint_4\n" +
+          "OpReturn\n" +
+          "OpFunctionEnd",
+      2, 2),
+  // Test case 40: fold UClamp 2 0 1
+  InstructionFoldingCase<uint32_t>(
+      Header() + "%main = OpFunction %void None %void_func\n" +
+          "%main_lab = OpLabel\n" +
+          "%2 = OpExtInst %uint %1 UClamp %uint_2 %uint_0 %uint_1\n" +
+          "OpReturn\n" +
+          "OpFunctionEnd",
+      2, 1),
+  // Test case 41: fold SClamp 2 3 4
+  InstructionFoldingCase<uint32_t>(
+      Header() + "%main = OpFunction %void None %void_func\n" +
+          "%main_lab = OpLabel\n" +
+          "%2 = OpExtInst %int %1 SClamp %int_2 %int_3 %int_4\n" +
+          "OpReturn\n" +
+          "OpFunctionEnd",
+      2, 3),
+  // Test case 42: fold SClamp 2 0 4
+  InstructionFoldingCase<uint32_t>(
+      Header() + "%main = OpFunction %void None %void_func\n" +
+          "%main_lab = OpLabel\n" +
+          "%2 = OpExtInst %int %1 SClamp %int_2 %int_0 %int_4\n" +
+          "OpReturn\n" +
+          "OpFunctionEnd",
+      2, 2),
+  // Test case 43: fold SClamp 2 0 1
+  InstructionFoldingCase<uint32_t>(
+      Header() + "%main = OpFunction %void None %void_func\n" +
+          "%main_lab = OpLabel\n" +
+          "%2 = OpExtInst %int %1 SClamp %int_2 %int_0 %int_1\n" +
+          "OpReturn\n" +
+          "OpFunctionEnd",
+      2, 1),
+  // Test case 44: SClamp 1 2 x
+  InstructionFoldingCase<uint32_t>(
+      Header() + "%main = OpFunction %void None %void_func\n" +
+          "%main_lab = OpLabel\n" +
+          "%undef = OpUndef %int\n" +
+          "%2 = OpExtInst %int %1 SClamp %int_1 %int_2 %undef\n" +
+          "OpReturn\n" +
+          "OpFunctionEnd",
+      2, 2),
+  // Test case 45: SClamp 2 x 1
+  InstructionFoldingCase<uint32_t>(
+      Header() + "%main = OpFunction %void None %void_func\n" +
+          "%main_lab = OpLabel\n" +
+          "%undef = OpUndef %int\n" +
+          "%2 = OpExtInst %int %1 SClamp %int_2 %undef %int_1\n" +
+          "OpReturn\n" +
+          "OpFunctionEnd",
+      2, 1),
+  // Test case 44: UClamp 1 2 x
+  InstructionFoldingCase<uint32_t>(
+      Header() + "%main = OpFunction %void None %void_func\n" +
+          "%main_lab = OpLabel\n" +
+          "%undef = OpUndef %uint\n" +
+          "%2 = OpExtInst %uint %1 UClamp %uint_1 %uint_2 %undef\n" +
+          "OpReturn\n" +
+          "OpFunctionEnd",
+      2, 2),
+  // Test case 45: UClamp 2 x 1
+  InstructionFoldingCase<uint32_t>(
+      Header() + "%main = OpFunction %void None %void_func\n" +
+          "%main_lab = OpLabel\n" +
+          "%undef = OpUndef %uint\n" +
+          "%2 = OpExtInst %uint %1 UClamp %uint_2 %undef %uint_1\n" +
+          "OpReturn\n" +
+          "OpFunctionEnd",
+      2, 1)
 ));
 // clang-format on
 
@@ -643,6 +794,58 @@
 ));
 // clang-format on
 
+using FloatVectorInstructionFoldingTest =
+    ::testing::TestWithParam<InstructionFoldingCase<std::vector<float>>>;
+
+TEST_P(FloatVectorInstructionFoldingTest, 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);
+  SpvOp 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);
+    inst = def_use_mgr->GetDef(inst->GetSingleWordInOperand(0));
+    std::vector<SpvOp> opcodes = {SpvOpConstantComposite};
+    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]->GetFloat());
+      }
+    }
+  }
+}
+
+// clang-format off
+INSTANTIATE_TEST_SUITE_P(TestCase, FloatVectorInstructionFoldingTest,
+::testing::Values(
+   // Test case 0: FMix {2.0, 2.0}, {2.0, 3.0} {0.2,0.5}
+   InstructionFoldingCase<std::vector<float>>(
+       Header() + "%main = OpFunction %void None %void_func\n" +
+           "%main_lab = OpLabel\n" +
+           "%2 = OpExtInst %v2float %1 FMix %v2float_2_3 %v2float_0_0 %v2float_0p2_0p5\n" +
+           "OpReturn\n" +
+           "OpFunctionEnd",
+       2, {1.6f,1.5f})
+));
+// clang-format on
 using BooleanInstructionFoldingTest =
     ::testing::TestWithParam<InstructionFoldingCase<bool>>;
 
@@ -1473,7 +1676,81 @@
             "%2 = OpExtInst %float %1 FMix %float_1 %float_4 %float_0p2\n" +
             "OpReturn\n" +
             "OpFunctionEnd",
-        2, 1.6f)
+        2, 1.6f),
+    // Test case 21: FMin 1.0 4.0
+    InstructionFoldingCase<float>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %float %1 FMin %float_1 %float_4\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 1.0f),
+    // Test case 22: FMin 4.0 0.2
+    InstructionFoldingCase<float>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %float %1 FMin %float_4 %float_0p2\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 0.2f),
+    // Test case 21: FMax 1.0 4.0
+    InstructionFoldingCase<float>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %float %1 FMax %float_1 %float_4\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 4.0f),
+    // Test case 22: FMax 1.0 0.2
+    InstructionFoldingCase<float>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %float %1 FMax %float_1 %float_0p2\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 1.0f),
+    // Test case 23: FClamp 1.0 0.2 4.0
+    InstructionFoldingCase<float>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %float %1 FClamp %float_1 %float_0p2 %float_4\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 1.0f),
+    // Test case 24: FClamp 0.2 2.0 4.0
+    InstructionFoldingCase<float>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %float %1 FClamp %float_0p2 %float_2 %float_4\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 2.0f),
+    // Test case 25: FClamp 2049.0 2.0 4.0
+    InstructionFoldingCase<float>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %float %1 FClamp %float_2049 %float_2 %float_4\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 4.0f),
+    // Test case 26: FClamp 1.0 2.0 x
+    InstructionFoldingCase<float>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%undef = OpUndef %float\n" +
+            "%2 = OpExtInst %float %1 FClamp %float_1 %float_2 %undef\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 2.0),
+    // Test case 27: FClamp 1.0 x 0.5
+    InstructionFoldingCase<float>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%undef = OpUndef %float\n" +
+            "%2 = OpExtInst %float %1 FClamp %float_1 %undef %float_0p5\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 0.5)
 ));
 // clang-format on
 
@@ -1616,7 +1893,81 @@
                 "%2 = OpFNegate %double %double_2\n" +
                 "OpReturn\n" +
                 "OpFunctionEnd",
-            2, -2)
+            2, -2),
+        // Test case 12: FMin 1.0 4.0
+        InstructionFoldingCase<double>(
+            Header() + "%main = OpFunction %void None %void_func\n" +
+                "%main_lab = OpLabel\n" +
+                "%2 = OpExtInst %double %1 FMin %double_1 %double_4\n" +
+                "OpReturn\n" +
+                "OpFunctionEnd",
+            2, 1.0),
+        // Test case 13: FMin 4.0 0.2
+        InstructionFoldingCase<double>(
+            Header() + "%main = OpFunction %void None %void_func\n" +
+                "%main_lab = OpLabel\n" +
+                "%2 = OpExtInst %double %1 FMin %double_4 %double_0p2\n" +
+                "OpReturn\n" +
+                "OpFunctionEnd",
+            2, 0.2),
+        // Test case 14: FMax 1.0 4.0
+        InstructionFoldingCase<double>(
+            Header() + "%main = OpFunction %void None %void_func\n" +
+                "%main_lab = OpLabel\n" +
+                "%2 = OpExtInst %double %1 FMax %double_1 %double_4\n" +
+                "OpReturn\n" +
+                "OpFunctionEnd",
+            2, 4.0),
+        // Test case 15: FMax 1.0 0.2
+        InstructionFoldingCase<double>(
+            Header() + "%main = OpFunction %void None %void_func\n" +
+                "%main_lab = OpLabel\n" +
+                "%2 = OpExtInst %double %1 FMax %double_1 %double_0p2\n" +
+                "OpReturn\n" +
+                "OpFunctionEnd",
+            2, 1.0),
+        // Test case 16: FClamp 1.0 0.2 4.0
+        InstructionFoldingCase<double>(
+            Header() + "%main = OpFunction %void None %void_func\n" +
+                "%main_lab = OpLabel\n" +
+                "%2 = OpExtInst %double %1 FClamp %double_1 %double_0p2 %double_4\n" +
+                "OpReturn\n" +
+                "OpFunctionEnd",
+            2, 1.0),
+        // Test case 17: FClamp 0.2 2.0 4.0
+        InstructionFoldingCase<double>(
+            Header() + "%main = OpFunction %void None %void_func\n" +
+                "%main_lab = OpLabel\n" +
+                "%2 = OpExtInst %double %1 FClamp %double_0p2 %double_2 %double_4\n" +
+                "OpReturn\n" +
+                "OpFunctionEnd",
+            2, 2.0),
+        // Test case 18: FClamp 5.0 2.0 4.0
+        InstructionFoldingCase<double>(
+            Header() + "%main = OpFunction %void None %void_func\n" +
+                "%main_lab = OpLabel\n" +
+                "%2 = OpExtInst %double %1 FClamp %double_5 %double_2 %double_4\n" +
+                "OpReturn\n" +
+                "OpFunctionEnd",
+            2, 4.0),
+        // Test case 19: FClamp 1.0 2.0 x
+        InstructionFoldingCase<double>(
+            Header() + "%main = OpFunction %void None %void_func\n" +
+                "%main_lab = OpLabel\n" +
+                "%undef = OpUndef %double\n" +
+                "%2 = OpExtInst %double %1 FClamp %double_1 %double_2 %undef\n" +
+                "OpReturn\n" +
+                "OpFunctionEnd",
+            2, 2.0),
+        // Test case 20: FClamp 1.0 x 0.5
+        InstructionFoldingCase<double>(
+            Header() + "%main = OpFunction %void None %void_func\n" +
+                "%main_lab = OpLabel\n" +
+                "%undef = OpUndef %double\n" +
+                "%2 = OpExtInst %double %1 FClamp %double_1 %undef %double_0p5\n" +
+                "OpReturn\n" +
+                "OpFunctionEnd",
+            2, 0.5)
 ));
 // clang-format on
 
@@ -5412,6 +5763,132 @@
     4, true)
 ));
 
+INSTANTIATE_TEST_SUITE_P(MergeGenericAddSub, MatchingInstructionFoldingTest,
+::testing::Values(
+    // Test case 0: merge of add of sub
+    // (a - b) + b => a
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: %6 = OpCopyObject [[float]] %3\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var0 = OpVariable %_ptr_float Function\n" +
+      "%var1 = OpVariable %_ptr_float Function\n" +
+      "%3 = OpLoad %float %var0\n" +
+      "%4 = OpLoad %float %var1\n" +
+      "%5 = OpFSub %float %3 %4\n" +
+      "%6 = OpFAdd %float %5 %4\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      6, true),
+  // Test case 1: merge of add of sub
+  // b + (a - b) => a
+  InstructionFoldingCase<bool>(
+    Header() +
+    "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+    "; CHECK: %6 = OpCopyObject [[float]] %3\n" +
+    "%main = OpFunction %void None %void_func\n" +
+    "%main_lab = OpLabel\n" +
+    "%var0 = OpVariable %_ptr_float Function\n" +
+    "%var1 = OpVariable %_ptr_float Function\n" +
+    "%3 = OpLoad %float %var0\n" +
+    "%4 = OpLoad %float %var1\n" +
+    "%5 = OpFSub %float %3 %4\n" +
+    "%6 = OpFAdd %float %4 %5\n" +
+    "OpReturn\n" +
+    "OpFunctionEnd\n",
+    6, true)
+));
+
+INSTANTIATE_TEST_SUITE_P(FactorAddMul, MatchingInstructionFoldingTest,
+::testing::Values(
+    // Test case 0: factor of add of muls
+    // (a * b) + (a * c) => a * (b + c)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[newadd:%\\w+]] = OpFAdd [[float]] %4 %5\n" +
+      "; CHECK: %9 = OpFMul [[float]] %6 [[newadd]]\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var0 = OpVariable %_ptr_float Function\n" +
+      "%var1 = OpVariable %_ptr_float Function\n" +
+      "%var2 = OpVariable %_ptr_float Function\n" +
+      "%4 = OpLoad %float %var0\n" +
+      "%5 = OpLoad %float %var1\n" +
+      "%6 = OpLoad %float %var2\n" +
+      "%7 = OpFMul %float %6 %4\n" +
+      "%8 = OpFMul %float %6 %5\n" +
+      "%9 = OpFAdd %float %7 %8\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      9, true),
+  // Test case 1: factor of add of muls
+  // (b * a) + (a * c) => a * (b + c)
+  InstructionFoldingCase<bool>(
+    Header() +
+    "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+    "; CHECK: [[newadd:%\\w+]] = OpFAdd [[float]] %4 %5\n" +
+    "; CHECK: %9 = OpFMul [[float]] %6 [[newadd]]\n" +
+    "%main = OpFunction %void None %void_func\n" +
+    "%main_lab = OpLabel\n" +
+    "%var0 = OpVariable %_ptr_float Function\n" +
+    "%var1 = OpVariable %_ptr_float Function\n" +
+    "%var2 = OpVariable %_ptr_float Function\n" +
+    "%4 = OpLoad %float %var0\n" +
+    "%5 = OpLoad %float %var1\n" +
+    "%6 = OpLoad %float %var2\n" +
+    "%7 = OpFMul %float %4 %6\n" +
+    "%8 = OpFMul %float %6 %5\n" +
+    "%9 = OpFAdd %float %7 %8\n" +
+    "OpReturn\n" +
+    "OpFunctionEnd\n",
+    9, true),
+  // Test case 2: factor of add of muls
+  // (a * b) + (c * a) => a * (b + c)
+  InstructionFoldingCase<bool>(
+    Header() +
+    "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+    "; CHECK: [[newadd:%\\w+]] = OpFAdd [[float]] %4 %5\n" +
+    "; CHECK: %9 = OpFMul [[float]] %6 [[newadd]]\n" +
+    "%main = OpFunction %void None %void_func\n" +
+    "%main_lab = OpLabel\n" +
+    "%var0 = OpVariable %_ptr_float Function\n" +
+    "%var1 = OpVariable %_ptr_float Function\n" +
+    "%var2 = OpVariable %_ptr_float Function\n" +
+    "%4 = OpLoad %float %var0\n" +
+    "%5 = OpLoad %float %var1\n" +
+    "%6 = OpLoad %float %var2\n" +
+    "%7 = OpFMul %float %6 %4\n" +
+    "%8 = OpFMul %float %5 %6\n" +
+    "%9 = OpFAdd %float %7 %8\n" +
+    "OpReturn\n" +
+    "OpFunctionEnd\n",
+    9, true),
+  // Test case 3: factor of add of muls
+  // (b * a) + (c * a) => a * (b + c)
+  InstructionFoldingCase<bool>(
+    Header() +
+    "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+    "; CHECK: [[newadd:%\\w+]] = OpFAdd [[float]] %4 %5\n" +
+    "; CHECK: %9 = OpFMul [[float]] %6 [[newadd]]\n" +
+    "%main = OpFunction %void None %void_func\n" +
+    "%main_lab = OpLabel\n" +
+    "%var0 = OpVariable %_ptr_float Function\n" +
+    "%var1 = OpVariable %_ptr_float Function\n" +
+    "%var2 = OpVariable %_ptr_float Function\n" +
+    "%4 = OpLoad %float %var0\n" +
+    "%5 = OpLoad %float %var1\n" +
+    "%6 = OpLoad %float %var2\n" +
+    "%7 = OpFMul %float %4 %6\n" +
+    "%8 = OpFMul %float %5 %6\n" +
+    "%9 = OpFAdd %float %7 %8\n" +
+    "OpReturn\n" +
+    "OpFunctionEnd\n",
+    9, true)
+));
+
 INSTANTIATE_TEST_SUITE_P(MergeSubTest, MatchingInstructionFoldingTest,
 ::testing::Values(
   // Test case 0: merge sub of negate
@@ -6581,6 +7058,96 @@
                                  1, false)
 ));
 
+std::string ImageOperandsTestBody(const std::string& image_instruction) {
+  std::string body = R"(
+               OpCapability Shader
+               OpCapability ImageGatherExtended
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpDecorate %Texture DescriptorSet 0
+               OpDecorate %Texture Binding 0
+        %int = OpTypeInt 32 1
+     %int_n1 = OpConstant %int -1
+          %5 = OpConstant %int 0
+      %float = OpTypeFloat 32
+    %float_0 = OpConstant %float 0
+%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown
+%type_sampled_image = OpTypeSampledImage %type_2d_image
+%type_sampler = OpTypeSampler
+%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler
+%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image
+   %_ptr_int = OpTypePointer Function %int
+      %v2int = OpTypeVector %int 2
+         %10 = OpTypeVector %float 4
+       %void = OpTypeVoid
+         %22 = OpTypeFunction %void
+    %v2float = OpTypeVector %float 2
+      %v3int = OpTypeVector %int 3
+    %Texture = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
+   %gSampler = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
+        %101 = OpConstantComposite %v2int %int_n1 %int_n1
+         %20 = OpConstantComposite %v2float %float_0 %float_0
+       %main = OpFunction %void None %22
+         %23 = OpLabel
+        %var = OpVariable %_ptr_int Function
+         %88 = OpLoad %type_2d_image %Texture
+        %val = OpLoad %int %var
+    %sampler = OpLoad %type_sampler %gSampler
+         %26 = OpSampledImage %type_sampled_image %88 %sampler
+)" + image_instruction + R"(
+               OpReturn
+               OpFunctionEnd
+)";
+
+  return body;
+}
+
+INSTANTIATE_TEST_SUITE_P(ImageOperandsBitmaskFoldingTest, MatchingInstructionWithNoResultFoldingTest,
+::testing::Values(
+    // Test case 0: OpImageFetch without Offset
+    InstructionFoldingCase<bool>(ImageOperandsTestBody(
+        "%89 = OpImageFetch %10 %88 %101 Lod %5 \n")
+        , 89, false),
+    // Test case 1: OpImageFetch with non-const offset
+    InstructionFoldingCase<bool>(ImageOperandsTestBody(
+        "%89 = OpImageFetch %10 %88 %101 Lod|Offset %5 %val \n")
+        , 89, false),
+    // Test case 2: OpImageFetch with Lod and Offset
+    InstructionFoldingCase<bool>(ImageOperandsTestBody(
+      "         %89 = OpImageFetch %10 %88 %101 Lod|Offset %5 %101      \n"
+      "; CHECK: %89 = OpImageFetch %10 %88 %101 Lod|ConstOffset %5 %101 \n")
+      , 89, true),
+    // Test case 3: OpImageFetch with Bias and Offset
+    InstructionFoldingCase<bool>(ImageOperandsTestBody(
+      "         %89 = OpImageFetch %10 %88 %101 Bias|Offset %5 %101      \n"
+      "; CHECK: %89 = OpImageFetch %10 %88 %101 Bias|ConstOffset %5 %101 \n")
+      , 89, true),
+    // Test case 4: OpImageFetch with Grad and Offset.
+    // Grad adds 2 operands to the instruction.
+    InstructionFoldingCase<bool>(ImageOperandsTestBody(
+      "         %89 = OpImageFetch %10 %88 %101 Grad|Offset %5 %5 %101      \n"
+      "; CHECK: %89 = OpImageFetch %10 %88 %101 Grad|ConstOffset %5 %5 %101 \n")
+      , 89, true),
+    // Test case 5: OpImageFetch with Offset and MinLod.
+    // This is an example of a case where the bitmask bit-offset is larger than
+    // that of the Offset.
+    InstructionFoldingCase<bool>(ImageOperandsTestBody(
+      "         %89 = OpImageFetch %10 %88 %101 Offset|MinLod %101 %5      \n"
+      "; CHECK: %89 = OpImageFetch %10 %88 %101 ConstOffset|MinLod %101 %5 \n")
+      , 89, true),
+    // Test case 6: OpImageGather with constant Offset
+    InstructionFoldingCase<bool>(ImageOperandsTestBody(
+      "         %89 = OpImageGather %10 %26 %20 %5 Offset %101      \n"
+      "; CHECK: %89 = OpImageGather %10 %26 %20 %5 ConstOffset %101 \n")
+      , 89, true),
+    // Test case 7: OpImageWrite with constant Offset
+    InstructionFoldingCase<bool>(ImageOperandsTestBody(
+      "         OpImageWrite %88 %5 %101 Offset %101      \n"
+      "; CHECK: OpImageWrite %88 %5 %101 ConstOffset %101 \n")
+      , 0 /* No result-id */, true)
+));
+
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools
diff --git a/test/opt/generate_webgpu_initializers_test.cpp b/test/opt/generate_webgpu_initializers_test.cpp
index f35cf56..4aab2ce 100644
--- a/test/opt/generate_webgpu_initializers_test.cpp
+++ b/test/opt/generate_webgpu_initializers_test.cpp
@@ -46,9 +46,9 @@
   std::vector<const char*> result = {
       // clang-format off
                "OpCapability Shader",
-               "OpCapability VulkanMemoryModelKHR",
+               "OpCapability VulkanMemoryModel",
                "OpExtension \"SPV_KHR_vulkan_memory_model\"",
-               "OpMemoryModel Logical VulkanKHR",
+               "OpMemoryModel Logical Vulkan",
                "OpEntryPoint Vertex %1 \"shader\"",
        "%uint = OpTypeInt 32 0",
                 ptr_str.c_str()};
@@ -132,9 +132,9 @@
   std::vector<const char*> result = {
       // clang-format off
                "OpCapability Shader",
-               "OpCapability VulkanMemoryModelKHR",
+               "OpCapability VulkanMemoryModel",
                "OpExtension \"SPV_KHR_vulkan_memory_model\"",
-               "OpMemoryModel Logical VulkanKHR",
+               "OpMemoryModel Logical Vulkan",
                "OpEntryPoint Vertex %1 \"shader\"",
        "%uint = OpTypeInt 32 0",
                 ptr_str.c_str(),
@@ -206,9 +206,9 @@
   std::vector<const char*> spirv = {
       // clang-format off
                        "OpCapability Shader",
-                       "OpCapability VulkanMemoryModelKHR",
+                       "OpCapability VulkanMemoryModel",
                        "OpExtension \"SPV_KHR_vulkan_memory_model\"",
-                       "OpMemoryModel Logical VulkanKHR",
+                       "OpMemoryModel Logical Vulkan",
                        "OpEntryPoint Vertex %1 \"shader\"",
                "%uint = OpTypeInt 32 0",
   "%_ptr_Private_uint = OpTypePointer Private %uint",
@@ -232,9 +232,9 @@
   std::vector<const char*> input_spirv = {
       // clang-format off
                                    "OpCapability Shader",
-                                   "OpCapability VulkanMemoryModelKHR",
+                                   "OpCapability VulkanMemoryModel",
                                    "OpExtension \"SPV_KHR_vulkan_memory_model\"",
-                                   "OpMemoryModel Logical VulkanKHR",
+                                   "OpMemoryModel Logical Vulkan",
                                    "OpEntryPoint Vertex %1 \"shader\"",
                            "%uint = OpTypeInt 32 0",
                          "%uint_2 = OpConstant %uint 2",
@@ -258,9 +258,9 @@
   std::vector<const char*> expected_spirv = {
       // clang-format off
                                    "OpCapability Shader",
-                                   "OpCapability VulkanMemoryModelKHR",
+                                   "OpCapability VulkanMemoryModel",
                                    "OpExtension \"SPV_KHR_vulkan_memory_model\"",
-                                   "OpMemoryModel Logical VulkanKHR",
+                                   "OpMemoryModel Logical Vulkan",
                                    "OpEntryPoint Vertex %1 \"shader\"",
                            "%uint = OpTypeInt 32 0",
                          "%uint_2 = OpConstant %uint 2",
@@ -290,9 +290,9 @@
   std::vector<const char*> input_spirv = {
       // clang-format off
                           "OpCapability Shader",
-                          "OpCapability VulkanMemoryModelKHR",
+                          "OpCapability VulkanMemoryModel",
                           "OpExtension \"SPV_KHR_vulkan_memory_model\"",
-                          "OpMemoryModel Logical VulkanKHR",
+                          "OpMemoryModel Logical Vulkan",
                           "OpEntryPoint Vertex %1 \"shader\"",
                   "%uint = OpTypeInt 32 0",
              "%_struct_3 = OpTypeStruct %uint",
@@ -315,9 +315,9 @@
   std::vector<const char*> expected_spirv = {
       // clang-format off
                           "OpCapability Shader",
-                          "OpCapability VulkanMemoryModelKHR",
+                          "OpCapability VulkanMemoryModel",
                           "OpExtension \"SPV_KHR_vulkan_memory_model\"",
-                          "OpMemoryModel Logical VulkanKHR",
+                          "OpMemoryModel Logical Vulkan",
                           "OpEntryPoint Vertex %1 \"shader\"",
                   "%uint = OpTypeInt 32 0",
              "%_struct_3 = OpTypeStruct %uint",
diff --git a/test/opt/graphics_robust_access_test.cpp b/test/opt/graphics_robust_access_test.cpp
index 137d0e8..58bd404 100644
--- a/test/opt/graphics_robust_access_test.cpp
+++ b/test/opt/graphics_robust_access_test.cpp
@@ -90,7 +90,7 @@
 TEST_F(GraphicsRobustAccessTest,
        FailCantProcessPhysicalStorageBuffer64EXTAddressingModel) {
   const std::string text = R"(
-; CHECK: Addressing model must be Logical.  Found OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
+; CHECK: Addressing model must be Logical.  Found OpMemoryModel PhysicalStorageBuffer64 GLSL450
 OpCapability Shader
 OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
 )";
@@ -199,11 +199,11 @@
                     const std::string& original,
                     const std::string& transformed) {
   return "\n ; CHECK: %ac = " + access_chain_inst + " %ptr_ty %var" +
-         (transformed.size() ? " " : "") + transformed +
+         (transformed.empty() ? "" : " ") + transformed +
          "\n ; CHECK-NOT: " + access_chain_inst +
          "\n ; CHECK-NEXT: OpReturn"
          "\n %ac = " +
-         access_chain_inst + " %ptr_ty %var " + (original.size() ? " " : "") +
+         access_chain_inst + " %ptr_ty %var " + (original.empty() ? "" : " ") +
          original + "\n";
 }
 
@@ -211,11 +211,11 @@
                         const std::string& original,
                         const std::string& transformed) {
   return "\n ; CHECK: %ac = " + access_chain_inst + " %ptr_ty %var" +
-         (transformed.size() ? " " : "") + transformed +
+         (transformed.empty() ? "" : " ") + transformed +
          "\n ; CHECK-NOT: " + access_chain_inst +
          "\n ; CHECK-NOT: OpReturn"
          "\n %ac = " +
-         access_chain_inst + " %ptr_ty %var " + (original.size() ? " " : "") +
+         access_chain_inst + " %ptr_ty %var " + (original.empty() ? "" : " ") +
          original + "\n";
 }
 
@@ -898,7 +898,7 @@
 TEST_F(GraphicsRobustAccessTest, ACRTArrayLeastInboundClamped) {
   for (auto* ac : AccessChains()) {
     std::ostringstream shaders;
-    shaders << ShaderPreambleAC() << "OpMemberDecorate %ssbo_s 0 ArrayStride 4 "
+    shaders << ShaderPreambleAC() << "OpDecorate %rtarr ArrayStride 4 "
             << DecoSSBO() << TypesVoid() << TypesInt() << TypesFloat() << R"(
        %rtarr = OpTypeRuntimeArray %float
        %ssbo_s = OpTypeStruct %uint %uint %rtarr
@@ -924,9 +924,8 @@
   for (auto* ac : AccessChains()) {
     std::ostringstream shaders;
     shaders << "OpCapability Int16\n"
-            << ShaderPreambleAC({"i"})
-            << "OpMemberDecorate %ssbo_s 0 ArrayStride 4 " << DecoSSBO()
-            << TypesVoid() << TypesShort() << TypesFloat() << R"(
+            << ShaderPreambleAC({"i"}) << "OpDecorate %rtarr ArrayStride 4 "
+            << DecoSSBO() << TypesVoid() << TypesShort() << TypesFloat() << R"(
        %rtarr = OpTypeRuntimeArray %float
        %ssbo_s = OpTypeStruct %short %short %rtarr
        %var_ty = OpTypePointer Uniform %ssbo_s
@@ -954,9 +953,8 @@
   for (auto* ac : AccessChains()) {
     std::ostringstream shaders;
     shaders << "OpCapability Int16\n"
-            << ShaderPreambleAC({"i"})
-            << "OpMemberDecorate %ssbo_s 0 ArrayStride 4 " << DecoSSBO()
-            << TypesVoid() << TypesShort() << TypesFloat() << R"(
+            << ShaderPreambleAC({"i"}) << "OpDecorate %rtarr ArrayStride 4 "
+            << DecoSSBO() << TypesVoid() << TypesShort() << TypesFloat() << R"(
        %rtarr = OpTypeRuntimeArray %float
        %ssbo_s = OpTypeStruct %short %short %rtarr
        %var_ty = OpTypePointer Uniform %ssbo_s
@@ -983,9 +981,8 @@
 TEST_F(GraphicsRobustAccessTest, ACRTArrayGeneralIntIndexClamped) {
   for (auto* ac : AccessChains()) {
     std::ostringstream shaders;
-    shaders << ShaderPreambleAC({"i"})
-            << "OpMemberDecorate %ssbo_s 0 ArrayStride 4 " << DecoSSBO()
-            << TypesVoid() << TypesInt() << TypesFloat() << R"(
+    shaders << ShaderPreambleAC({"i"}) << "OpDecorate %rtarr ArrayStride 4 "
+            << DecoSSBO() << TypesVoid() << TypesInt() << TypesFloat() << R"(
        %rtarr = OpTypeRuntimeArray %float
        %ssbo_s = OpTypeStruct %int %int %rtarr
        %var_ty = OpTypePointer Uniform %ssbo_s
@@ -1000,8 +997,9 @@
        ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2
        ; CHECK: %[[max:\w+]] = OpISub %int %[[arrlen]] %int_1
        ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %i %int_0 %[[max]]
-       )" << MainPrefix()
-            << ACCheck(ac, "%int_2 %i", "%int_2 %[[clamp]]") << MainSuffix();
+       )"
+            << MainPrefix() << ACCheck(ac, "%int_2 %i", "%int_2 %[[clamp]]")
+            << MainSuffix();
     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
   }
 }
@@ -1009,9 +1007,8 @@
 TEST_F(GraphicsRobustAccessTest, ACRTArrayGeneralUIntIndexClamped) {
   for (auto* ac : AccessChains()) {
     std::ostringstream shaders;
-    shaders << ShaderPreambleAC({"i"})
-            << "OpMemberDecorate %ssbo_s 0 ArrayStride 4 " << DecoSSBO()
-            << TypesVoid() << TypesInt() << TypesFloat() << R"(
+    shaders << ShaderPreambleAC({"i"}) << "OpDecorate %rtarr ArrayStride 4 "
+            << DecoSSBO() << TypesVoid() << TypesInt() << TypesFloat() << R"(
        %rtarr = OpTypeRuntimeArray %float
        %ssbo_s = OpTypeStruct %int %int %rtarr
        %var_ty = OpTypePointer Uniform %ssbo_s
@@ -1026,8 +1023,9 @@
        ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2
        ; CHECK: %[[max:\w+]] = OpISub %uint %[[arrlen]] %uint_1
        ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UClamp %i %uint_0 %[[max]]
-       )" << MainPrefix()
-            << ACCheck(ac, "%int_2 %i", "%int_2 %[[clamp]]") << MainSuffix();
+       )"
+            << MainPrefix() << ACCheck(ac, "%int_2 %i", "%int_2 %[[clamp]]")
+            << MainSuffix();
     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
   }
 }
@@ -1036,8 +1034,8 @@
   for (auto* ac : AccessChains()) {
     std::ostringstream shaders;
     shaders << "OpCapability Int64" << ShaderPreambleAC({"i"})
-            << "OpMemberDecorate %ssbo_s 0 ArrayStride 4 " << DecoSSBO()
-            << TypesVoid() << TypesInt() << TypesLong() << TypesFloat() << R"(
+            << "OpDecorate %rtarr ArrayStride 4 " << DecoSSBO() << TypesVoid()
+            << TypesInt() << TypesLong() << TypesFloat() << R"(
        %rtarr = OpTypeRuntimeArray %float
        %ssbo_s = OpTypeStruct %int %int %rtarr
        %var_ty = OpTypePointer Uniform %ssbo_s
@@ -1053,9 +1051,8 @@
        ; CHECK: %[[arrlen_ext:\w+]] = OpUConvert %ulong %[[arrlen]]
        ; CHECK: %[[max:\w+]] = OpISub %long %[[arrlen_ext]] %long_1
        ; CHECK: %[[clamp:\w+]] = OpExtInst %long %[[GLSLSTD450]] UClamp %i %long_0 %[[max]]
-       )"
-            << MainPrefix() << ACCheck(ac, "%int_2 %i", "%int_2 %[[clamp]]")
-            << MainSuffix();
+       )" << MainPrefix()
+            << ACCheck(ac, "%int_2 %i", "%int_2 %[[clamp]]") << MainSuffix();
     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
   }
 }
@@ -1064,8 +1061,8 @@
   for (auto* ac : AccessChains()) {
     std::ostringstream shaders;
     shaders << "OpCapability Int64" << ShaderPreambleAC({"i"})
-            << "OpMemberDecorate %ssbo_s 0 ArrayStride 4 " << DecoSSBO()
-            << TypesVoid() << TypesInt() << TypesLong() << TypesFloat() << R"(
+            << "OpDecorate %rtarr ArrayStride 4 " << DecoSSBO() << TypesVoid()
+            << TypesInt() << TypesLong() << TypesFloat() << R"(
        %rtarr = OpTypeRuntimeArray %float
        %ssbo_s = OpTypeStruct %int %int %rtarr
        %var_ty = OpTypePointer Uniform %ssbo_s
@@ -1081,9 +1078,8 @@
        ; CHECK: %[[arrlen_ext:\w+]] = OpUConvert %ulong %[[arrlen]]
        ; CHECK: %[[max:\w+]] = OpISub %ulong %[[arrlen_ext]] %ulong_1
        ; CHECK: %[[clamp:\w+]] = OpExtInst %ulong %[[GLSLSTD450]] UClamp %i %ulong_0 %[[max]]
-       )"
-            << MainPrefix() << ACCheck(ac, "%int_2 %i", "%int_2 %[[clamp]]")
-            << MainSuffix();
+       )" << MainPrefix()
+            << ACCheck(ac, "%int_2 %i", "%int_2 %[[clamp]]") << MainSuffix();
     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
   }
 }
@@ -1095,7 +1091,7 @@
   for (auto* ac : AccessChains()) {
     std::ostringstream shaders;
     shaders << ShaderPreambleAC({"i", "j"})
-            << "OpMemberDecorate %ssbo_s 0 ArrayStride 32\n"
+            << "OpDecorate %rtarr ArrayStride 32\n"
             << DecoSSBO() << "OpMemberDecorate %rtelem 0 Offset 0\n"
             << "OpMemberDecorate %rtelem 1 Offset 16\n"
             << TypesVoid() << TypesInt() << TypesFloat() << R"(
@@ -1131,7 +1127,7 @@
   for (auto* ac : AccessChains()) {
     std::ostringstream shaders;
     shaders << ShaderPreambleAC({"i", "ssbo_s"})
-            << "OpMemberDecorate %ssbo_s 0 ArrayStride 32\n"
+            << "OpDecorate %rtarr ArrayStride 32\n"
             << DecoSSBO() << "OpMemberDecorate %rtelem 0 Offset 0\n"
             << "OpMemberDecorate %rtelem 1 Offset 16\n"
             << TypesVoid() << TypesInt() << TypesFloat() << R"(
@@ -1175,7 +1171,7 @@
     std::ostringstream shaders;
     shaders << ShaderPreambleAC({"i", "j", "k", "ssbo_s", "ssbo_pty",
                                  "rtarr_pty", "ac_ssbo", "ac_rtarr"})
-            << "OpMemberDecorate %ssbo_s 0 ArrayStride 32\n"
+            << "OpDecorate %rtarr ArrayStride 32\n"
             << DecoSSBO() << "OpMemberDecorate %rtelem 0 Offset 0\n"
             << "OpMemberDecorate %rtelem 1 Offset 16\n"
             << TypesVoid() << TypesInt() << TypesFloat() << R"(
@@ -1238,7 +1234,7 @@
     shaders << ShaderPreambleAC({"i", "j", "k", "bb1", "bb2", "ssbo_s",
                                  "ssbo_pty", "rtarr_pty", "ac_ssbo",
                                  "ac_rtarr"})
-            << "OpMemberDecorate %ssbo_s 0 ArrayStride 32\n"
+            << "OpDecorate %rtarr ArrayStride 32\n"
             << DecoSSBO() << "OpMemberDecorate %rtelem 0 Offset 0\n"
             << "OpMemberDecorate %rtelem 1 Offset 16\n"
             << TypesVoid() << TypesInt() << TypesFloat() << R"(
diff --git a/test/opt/if_conversion_test.cpp b/test/opt/if_conversion_test.cpp
index 03932a9..aa5adea 100644
--- a/test/opt/if_conversion_test.cpp
+++ b/test/opt/if_conversion_test.cpp
@@ -375,14 +375,12 @@
 OpSelectionMerge %12 None
 OpBranchConditional %true %13 %12
 %13 = OpLabel
-OpBranchConditional %true %14 %15
+OpBranchConditional %true %14 %12
 %14 = OpLabel
 OpBranch %12
-%15 = OpLabel
-OpBranch %12
 %12 = OpLabel
-%16 = OpPhi %uint %uint_0 %11 %uint_0 %14 %uint_1 %15
-OpStore %2 %16
+%15 = OpPhi %uint %uint_0 %11 %uint_0 %13 %uint_1 %14
+OpStore %2 %15
 OpReturn
 OpFunctionEnd
 )";
diff --git a/test/opt/inline_test.cpp b/test/opt/inline_test.cpp
index 7170812..fac49ca 100644
--- a/test/opt/inline_test.cpp
+++ b/test/opt/inline_test.cpp
@@ -2792,182 +2792,6 @@
   }
 }
 
-TEST_F(InlineTest, OpKill) {
-  const std::string text = R"(
-; CHECK: OpFunction
-; CHECK-NEXT: OpLabel
-; CHECK-NEXT: OpKill
-; CHECK-NEXT: OpLabel
-; CHECK-NEXT: OpReturn
-; CHECK-NEXT: OpFunctionEnd
-OpCapability Shader
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main"
-OpExecutionMode %main OriginUpperLeft
-%void = OpTypeVoid
-%voidfuncty = OpTypeFunction %void
-%main = OpFunction %void None %voidfuncty
-%1 = OpLabel
-%2 = OpFunctionCall %void %func
-OpReturn
-OpFunctionEnd
-%func = OpFunction %void None %voidfuncty
-%3 = OpLabel
-OpKill
-OpFunctionEnd
-)";
-
-  SinglePassRunAndMatch<InlineExhaustivePass>(text, true);
-}
-
-TEST_F(InlineTest, OpKillWithTrailingInstructions) {
-  const std::string text = R"(
-; CHECK: OpFunction
-; CHECK-NEXT: OpLabel
-; CHECK-NEXT: [[var:%\w+]] = OpVariable
-; CHECK-NEXT: OpKill
-; CHECK-NEXT: OpLabel
-; CHECK-NEXT: OpStore [[var]]
-; CHECK-NEXT: OpReturn
-; CHECK-NEXT: OpFunctionEnd
-OpCapability Shader
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main"
-OpExecutionMode %main OriginUpperLeft
-%void = OpTypeVoid
-%bool = OpTypeBool
-%true = OpConstantTrue %bool
-%bool_func_ptr = OpTypePointer Function %bool
-%voidfuncty = OpTypeFunction %void
-%main = OpFunction %void None %voidfuncty
-%1 = OpLabel
-%2 = OpVariable %bool_func_ptr Function
-%3 = OpFunctionCall %void %func
-OpStore %2 %true
-OpReturn
-OpFunctionEnd
-%func = OpFunction %void None %voidfuncty
-%4 = OpLabel
-OpKill
-OpFunctionEnd
-)";
-
-  SinglePassRunAndMatch<InlineExhaustivePass>(text, true);
-}
-
-TEST_F(InlineTest, OpKillInIf) {
-  const std::string text = R"(
-; CHECK: OpFunction
-; CHECK: OpLabel
-; CHECK: [[var:%\w+]] = OpVariable
-; CHECK-NEXT: [[ld:%\w+]] = OpLoad {{%\w+}} [[var]]
-; CHECK-NEXT: OpBranch [[label:%\w+]]
-; CHECK-NEXT: [[label]] = OpLabel
-; CHECK-NEXT: OpLoopMerge [[loop_merge:%\w+]] [[continue:%\w+]] None
-; CHECK-NEXT: OpBranch [[label:%\w+]]
-; CHECK-NEXT: [[label]] = OpLabel
-; CHECK-NEXT: OpSelectionMerge [[sel_merge:%\w+]] None
-; CHECK-NEXT: OpBranchConditional {{%\w+}} [[kill_label:%\w+]] [[label:%\w+]]
-; CHECK-NEXT: [[kill_label]] = OpLabel
-; CHECK-NEXT: OpKill
-; CHECK-NEXT: [[label]] = OpLabel
-; CHECK-NEXT: OpBranch [[loop_merge]]
-; CHECK-NEXT: [[sel_merge]] = OpLabel
-; CHECK-NEXT: OpBranch [[loop_merge]]
-; CHECK-NEXT: [[continue]] = OpLabel
-; CHECK-NEXT: OpBranchConditional
-; CHECK-NEXT: [[loop_merge]] = OpLabel
-; CHECK-NEXT: OpStore [[var]] [[ld]]
-; CHECK-NEXT: OpReturn
-; CHECK-NEXT: OpFunctionEnd
-OpCapability Shader
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main"
-OpExecutionMode %main OriginUpperLeft
-%void = OpTypeVoid
-%bool = OpTypeBool
-%true = OpConstantTrue %bool
-%bool_func_ptr = OpTypePointer Function %bool
-%voidfuncty = OpTypeFunction %void
-%main = OpFunction %void None %voidfuncty
-%1 = OpLabel
-%2 = OpVariable %bool_func_ptr Function
-%3 = OpLoad %bool %2
-%4 = OpFunctionCall %void %func
-OpStore %2 %3
-OpReturn
-OpFunctionEnd
-%func = OpFunction %void None %voidfuncty
-%5 = OpLabel
-OpSelectionMerge %6 None
-OpBranchConditional %true %7 %8
-%7 = OpLabel
-OpKill
-%8 = OpLabel
-OpReturn
-%6 = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
-  SinglePassRunAndMatch<InlineExhaustivePass>(text, true);
-}
-
-TEST_F(InlineTest, OpKillInLoop) {
-  const std::string text = R"(
-; CHECK: OpFunction
-; CHECK: OpLabel
-; CHECK: [[var:%\w+]] = OpVariable
-; CHECK-NEXT: [[ld:%\w+]] = OpLoad {{%\w+}} [[var]]
-; CHECK-NEXT: OpBranch [[loop:%\w+]]
-; CHECK-NEXT: [[loop]] = OpLabel
-; CHECK-NEXT: OpLoopMerge [[loop_merge:%\w+]] [[continue:%\w+]] None
-; CHECK-NEXT: OpBranch [[label:%\w+]]
-; CHECK-NEXT: [[label]] = OpLabel
-; CHECK-NEXT: OpKill
-; CHECK-NEXT: [[loop_merge]] = OpLabel
-; CHECK-NEXT: OpBranch [[label:%\w+]]
-; CHECK-NEXT: [[continue]] = OpLabel
-; CHECK-NEXT: OpBranch [[loop]]
-; CHECK-NEXT: [[label]] = OpLabel
-; CHECK-NEXT: OpStore [[var]] [[ld]]
-; CHECK-NEXT: OpReturn
-; CHECK-NEXT: OpFunctionEnd
-OpCapability Shader
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main"
-OpExecutionMode %main OriginUpperLeft
-%void = OpTypeVoid
-%bool = OpTypeBool
-%true = OpConstantTrue %bool
-%voidfuncty = OpTypeFunction %void
-%bool_func_ptr = OpTypePointer Function %bool
-%main = OpFunction %void None %voidfuncty
-%1 = OpLabel
-%2 = OpVariable %bool_func_ptr Function
-%3 = OpLoad %bool %2
-%4 = OpFunctionCall %void %func
-OpStore %2 %3
-OpReturn
-OpFunctionEnd
-%func = OpFunction %void None %voidfuncty
-%5 = OpLabel
-OpBranch %10
-%10 = OpLabel
-OpLoopMerge %6 %7 None
-OpBranch %8
-%8 = OpLabel
-OpKill
-%6 = OpLabel
-OpReturn
-%7 = OpLabel
-OpBranch %10
-OpFunctionEnd
-)";
-
-  SinglePassRunAndMatch<InlineExhaustivePass>(text, true);
-}
-
 TEST_F(InlineTest, OpVariableWithInit) {
   // Check that there is a store that corresponds to the initializer.  This
   // test makes sure that is a store to the variable in the loop and before any
@@ -3112,6 +2936,100 @@
   SinglePassRunAndCheck<InlineExhaustivePass>(test, test, false, true);
 }
 
+TEST_F(InlineTest, DontInlineFuncWithOpKillInContinue) {
+  const std::string test =
+      R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 330
+OpName %main "main"
+OpName %kill_ "kill("
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%bool = OpTypeBool
+%true = OpConstantTrue %bool
+%main = OpFunction %void None %3
+%5 = OpLabel
+OpBranch %9
+%9 = OpLabel
+OpLoopMerge %11 %12 None
+OpBranch %13
+%13 = OpLabel
+OpBranchConditional %true %10 %11
+%10 = OpLabel
+OpBranch %12
+%12 = OpLabel
+%16 = OpFunctionCall %void %kill_
+OpBranch %9
+%11 = OpLabel
+OpReturn
+OpFunctionEnd
+%kill_ = OpFunction %void None %3
+%7 = OpLabel
+OpKill
+OpFunctionEnd
+)";
+
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndCheck<InlineExhaustivePass>(test, test, false, true);
+}
+
+TEST_F(InlineTest, InlineFuncWithOpKillNotInContinue) {
+  const std::string before =
+      R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 330
+OpName %main "main"
+OpName %kill_ "kill("
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%bool = OpTypeBool
+%true = OpConstantTrue %bool
+%main = OpFunction %void None %3
+%5 = OpLabel
+%16 = OpFunctionCall %void %kill_
+OpReturn
+OpFunctionEnd
+%kill_ = OpFunction %void None %3
+%7 = OpLabel
+OpKill
+OpFunctionEnd
+)";
+
+  const std::string after =
+      R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 330
+OpName %main "main"
+OpName %kill_ "kill("
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%bool = OpTypeBool
+%true = OpConstantTrue %bool
+%main = OpFunction %void None %3
+%5 = OpLabel
+OpKill
+%17 = OpLabel
+OpReturn
+OpFunctionEnd
+%kill_ = OpFunction %void None %3
+%7 = OpLabel
+OpKill
+OpFunctionEnd
+)";
+
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndCheck<InlineExhaustivePass>(before, after, false, true);
+}
+
 // TODO(greg-lunarg): Add tests to verify handling of these cases:
 //
 //    Empty modules
diff --git a/test/opt/inst_bindless_check_test.cpp b/test/opt/inst_bindless_check_test.cpp
index 6e1adaa..b4db2d5 100644
--- a/test/opt/inst_bindless_check_test.cpp
+++ b/test/opt/inst_bindless_check_test.cpp
@@ -1915,7 +1915,7 @@
 
   const std::string defs_before =
       R"(OpCapability Shader
-OpCapability RuntimeDescriptorArrayEXT
+OpCapability RuntimeDescriptorArray
 OpExtension "SPV_EXT_descriptor_indexing"
 %1 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel Logical GLSL450
@@ -1968,7 +1968,7 @@
 
   const std::string defs_after =
       R"(OpCapability Shader
-OpCapability RuntimeDescriptorArrayEXT
+OpCapability RuntimeDescriptorArray
 OpExtension "SPV_EXT_descriptor_indexing"
 OpExtension "SPV_KHR_storage_buffer_storage_class"
 %1 = OpExtInstImport "GLSL.std.450"
@@ -2647,9 +2647,9 @@
 
   const std::string defs_before =
       R"(OpCapability Shader
-OpCapability ShaderNonUniformEXT
-OpCapability RuntimeDescriptorArrayEXT
-OpCapability UniformBufferArrayNonUniformIndexingEXT
+OpCapability ShaderNonUniform
+OpCapability RuntimeDescriptorArray
+OpCapability UniformBufferArrayNonUniformIndexing
 OpExtension "SPV_EXT_descriptor_indexing"
 %1 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel Logical GLSL450
@@ -2670,9 +2670,9 @@
 OpDecorate %uniformBuffer Binding 3
 OpDecorate %nu_ii Flat
 OpDecorate %nu_ii Location 0
-OpDecorate %nu_ii NonUniformEXT
-OpDecorate %16 NonUniformEXT
-OpDecorate %20 NonUniformEXT
+OpDecorate %nu_ii NonUniform
+OpDecorate %16 NonUniform
+OpDecorate %20 NonUniform
 %void = OpTypeVoid
 %3 = OpTypeFunction %void
 %float = OpTypeFloat 32
@@ -2691,9 +2691,9 @@
 
   const std::string defs_after =
       R"(OpCapability Shader
-OpCapability ShaderNonUniformEXT
-OpCapability RuntimeDescriptorArrayEXT
-OpCapability UniformBufferArrayNonUniformIndexingEXT
+OpCapability ShaderNonUniform
+OpCapability RuntimeDescriptorArray
+OpCapability UniformBufferArrayNonUniformIndexing
 OpExtension "SPV_EXT_descriptor_indexing"
 OpExtension "SPV_KHR_storage_buffer_storage_class"
 %1 = OpExtInstImport "GLSL.std.450"
@@ -2715,22 +2715,22 @@
 OpDecorate %uniformBuffer Binding 3
 OpDecorate %nu_ii Flat
 OpDecorate %nu_ii Location 0
-OpDecorate %nu_ii NonUniformEXT
-OpDecorate %7 NonUniformEXT
-OpDecorate %102 NonUniformEXT
+OpDecorate %nu_ii NonUniform
+OpDecorate %7 NonUniform
+OpDecorate %102 NonUniform
 OpDecorate %_runtimearr_uint ArrayStride 4
 OpDecorate %_struct_31 Block
 OpMemberDecorate %_struct_31 0 Offset 0
 OpDecorate %33 DescriptorSet 7
 OpDecorate %33 Binding 1
-OpDecorate %130 NonUniformEXT
+OpDecorate %130 NonUniform
 OpDecorate %_struct_55 Block
 OpMemberDecorate %_struct_55 0 Offset 0
 OpMemberDecorate %_struct_55 1 Offset 4
 OpDecorate %57 DescriptorSet 7
 OpDecorate %57 Binding 0
 OpDecorate %gl_FragCoord BuiltIn FragCoord
-OpDecorate %127 NonUniformEXT
+OpDecorate %127 NonUniform
 %void = OpTypeVoid
 %10 = OpTypeFunction %void
 %float = OpTypeFloat 32
@@ -2928,9 +2928,9 @@
 
   const std::string defs_before =
       R"(OpCapability Shader
-OpCapability ShaderNonUniformEXT
-OpCapability RuntimeDescriptorArrayEXT
-OpCapability UniformBufferArrayNonUniformIndexingEXT
+OpCapability ShaderNonUniform
+OpCapability RuntimeDescriptorArray
+OpCapability UniformBufferArrayNonUniformIndexing
 OpExtension "SPV_EXT_descriptor_indexing"
 %1 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel Logical GLSL450
@@ -2951,9 +2951,9 @@
 OpDecorate %storageBuffer Binding 3
 OpDecorate %nu_ii Flat
 OpDecorate %nu_ii Location 0
-OpDecorate %nu_ii NonUniformEXT
-OpDecorate %16 NonUniformEXT
-OpDecorate %20 NonUniformEXT
+OpDecorate %nu_ii NonUniform
+OpDecorate %16 NonUniform
+OpDecorate %20 NonUniform
 %void = OpTypeVoid
 %3 = OpTypeFunction %void
 %float = OpTypeFloat 32
@@ -2972,9 +2972,9 @@
 
   const std::string defs_after =
       R"(OpCapability Shader
-OpCapability ShaderNonUniformEXT
-OpCapability RuntimeDescriptorArrayEXT
-OpCapability UniformBufferArrayNonUniformIndexingEXT
+OpCapability ShaderNonUniform
+OpCapability RuntimeDescriptorArray
+OpCapability UniformBufferArrayNonUniformIndexing
 OpExtension "SPV_EXT_descriptor_indexing"
 OpExtension "SPV_KHR_storage_buffer_storage_class"
 %1 = OpExtInstImport "GLSL.std.450"
@@ -2996,22 +2996,22 @@
 OpDecorate %storageBuffer Binding 3
 OpDecorate %nu_ii Flat
 OpDecorate %nu_ii Location 0
-OpDecorate %nu_ii NonUniformEXT
-OpDecorate %7 NonUniformEXT
-OpDecorate %102 NonUniformEXT
+OpDecorate %nu_ii NonUniform
+OpDecorate %7 NonUniform
+OpDecorate %102 NonUniform
 OpDecorate %_runtimearr_uint ArrayStride 4
 OpDecorate %_struct_31 Block
 OpMemberDecorate %_struct_31 0 Offset 0
 OpDecorate %33 DescriptorSet 7
 OpDecorate %33 Binding 1
-OpDecorate %130 NonUniformEXT
+OpDecorate %130 NonUniform
 OpDecorate %_struct_55 Block
 OpMemberDecorate %_struct_55 0 Offset 0
 OpMemberDecorate %_struct_55 1 Offset 4
 OpDecorate %57 DescriptorSet 7
 OpDecorate %57 Binding 0
 OpDecorate %gl_FragCoord BuiltIn FragCoord
-OpDecorate %127 NonUniformEXT
+OpDecorate %127 NonUniform
 %void = OpTypeVoid
 %10 = OpTypeFunction %void
 %float = OpTypeFloat 32
@@ -3198,9 +3198,9 @@
 
   const std::string defs_before =
       R"(OpCapability Shader
-OpCapability ShaderNonUniformEXT
-OpCapability RuntimeDescriptorArrayEXT
-OpCapability StorageBufferArrayNonUniformIndexingEXT
+OpCapability ShaderNonUniform
+OpCapability RuntimeDescriptorArray
+OpCapability StorageBufferArrayNonUniformIndexing
 OpExtension "SPV_EXT_descriptor_indexing"
 %1 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel Logical GLSL450
@@ -3221,9 +3221,9 @@
 OpDecorate %storageBuffer Binding 3
 OpDecorate %nu_ii Flat
 OpDecorate %nu_ii Location 0
-OpDecorate %nu_ii NonUniformEXT
-OpDecorate %16 NonUniformEXT
-OpDecorate %20 NonUniformEXT
+OpDecorate %nu_ii NonUniform
+OpDecorate %16 NonUniform
+OpDecorate %20 NonUniform
 %void = OpTypeVoid
 %3 = OpTypeFunction %void
 %float = OpTypeFloat 32
@@ -3242,9 +3242,9 @@
 
   const std::string defs_after =
       R"(OpCapability Shader
-OpCapability ShaderNonUniformEXT
-OpCapability RuntimeDescriptorArrayEXT
-OpCapability StorageBufferArrayNonUniformIndexingEXT
+OpCapability ShaderNonUniform
+OpCapability RuntimeDescriptorArray
+OpCapability StorageBufferArrayNonUniformIndexing
 OpExtension "SPV_EXT_descriptor_indexing"
 OpExtension "SPV_KHR_storage_buffer_storage_class"
 %1 = OpExtInstImport "GLSL.std.450"
@@ -3266,22 +3266,22 @@
 OpDecorate %storageBuffer Binding 3
 OpDecorate %nu_ii Flat
 OpDecorate %nu_ii Location 0
-OpDecorate %nu_ii NonUniformEXT
-OpDecorate %7 NonUniformEXT
-OpDecorate %102 NonUniformEXT
+OpDecorate %nu_ii NonUniform
+OpDecorate %7 NonUniform
+OpDecorate %102 NonUniform
 OpDecorate %_runtimearr_uint ArrayStride 4
 OpDecorate %_struct_31 Block
 OpMemberDecorate %_struct_31 0 Offset 0
 OpDecorate %33 DescriptorSet 7
 OpDecorate %33 Binding 1
-OpDecorate %130 NonUniformEXT
+OpDecorate %130 NonUniform
 OpDecorate %_struct_55 Block
 OpMemberDecorate %_struct_55 0 Offset 0
 OpMemberDecorate %_struct_55 1 Offset 4
 OpDecorate %57 DescriptorSet 7
 OpDecorate %57 Binding 0
 OpDecorate %gl_FragCoord BuiltIn FragCoord
-OpDecorate %127 NonUniformEXT
+OpDecorate %127 NonUniform
 %void = OpTypeVoid
 %10 = OpTypeFunction %void
 %float = OpTypeFloat 32
@@ -3704,9 +3704,9 @@
 
   const std::string defs_before =
       R"(OpCapability Shader
-OpCapability ShaderNonUniformEXT
-OpCapability RuntimeDescriptorArrayEXT
-OpCapability StorageBufferArrayNonUniformIndexingEXT
+OpCapability ShaderNonUniform
+OpCapability RuntimeDescriptorArray
+OpCapability StorageBufferArrayNonUniformIndexing
 OpExtension "SPV_EXT_descriptor_indexing"
 %1 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel Logical GLSL450
@@ -3726,8 +3726,8 @@
 OpDecorate %storageBuffer Binding 4
 OpDecorate %nu_ii Flat
 OpDecorate %nu_ii Location 0
-OpDecorate %nu_ii NonUniformEXT
-OpDecorate %14 NonUniformEXT
+OpDecorate %nu_ii NonUniform
+OpDecorate %14 NonUniform
 OpDecorate %b Location 1
 %void = OpTypeVoid
 %3 = OpTypeFunction %void
@@ -3747,9 +3747,9 @@
 
   const std::string defs_after =
       R"(OpCapability Shader
-OpCapability ShaderNonUniformEXT
-OpCapability RuntimeDescriptorArrayEXT
-OpCapability StorageBufferArrayNonUniformIndexingEXT
+OpCapability ShaderNonUniform
+OpCapability RuntimeDescriptorArray
+OpCapability StorageBufferArrayNonUniformIndexing
 OpExtension "SPV_EXT_descriptor_indexing"
 OpExtension "SPV_KHR_storage_buffer_storage_class"
 %1 = OpExtInstImport "GLSL.std.450"
@@ -3770,8 +3770,8 @@
 OpDecorate %storageBuffer Binding 4
 OpDecorate %nu_ii Flat
 OpDecorate %nu_ii Location 0
-OpDecorate %nu_ii NonUniformEXT
-OpDecorate %7 NonUniformEXT
+OpDecorate %nu_ii NonUniform
+OpDecorate %7 NonUniform
 OpDecorate %b Location 1
 OpDecorate %_runtimearr_uint ArrayStride 4
 OpDecorate %_struct_31 Block
@@ -3978,8 +3978,8 @@
 
   const std::string defs_before =
       R"(OpCapability Shader
-OpCapability ShaderNonUniformEXT
-OpCapability UniformBufferArrayNonUniformIndexingEXT
+OpCapability ShaderNonUniform
+OpCapability UniformBufferArrayNonUniformIndexing
 OpExtension "SPV_EXT_descriptor_indexing"
 %1 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel Logical GLSL450
@@ -4000,9 +4000,9 @@
 OpDecorate %uniformBuffer Binding 3
 OpDecorate %nu_ii Flat
 OpDecorate %nu_ii Location 0
-OpDecorate %nu_ii NonUniformEXT
-OpDecorate %18 NonUniformEXT
-OpDecorate %22 NonUniformEXT
+OpDecorate %nu_ii NonUniform
+OpDecorate %18 NonUniform
+OpDecorate %22 NonUniform
 %void = OpTypeVoid
 %3 = OpTypeFunction %void
 %float = OpTypeFloat 32
@@ -4023,8 +4023,8 @@
 
   const std::string defs_after =
       R"(OpCapability Shader
-OpCapability ShaderNonUniformEXT
-OpCapability UniformBufferArrayNonUniformIndexingEXT
+OpCapability ShaderNonUniform
+OpCapability UniformBufferArrayNonUniformIndexing
 OpExtension "SPV_EXT_descriptor_indexing"
 OpExtension "SPV_KHR_storage_buffer_storage_class"
 %1 = OpExtInstImport "GLSL.std.450"
@@ -4046,10 +4046,10 @@
 OpDecorate %uniformBuffer Binding 3
 OpDecorate %nu_ii Flat
 OpDecorate %nu_ii Location 0
-OpDecorate %nu_ii NonUniformEXT
-OpDecorate %7 NonUniformEXT
-OpDecorate %89 NonUniformEXT
-OpDecorate %120 NonUniformEXT
+OpDecorate %nu_ii NonUniform
+OpDecorate %7 NonUniform
+OpDecorate %89 NonUniform
+OpDecorate %120 NonUniform
 OpDecorate %_runtimearr_uint ArrayStride 4
 OpDecorate %_struct_39 Block
 OpMemberDecorate %_struct_39 0 Offset 0
@@ -4061,7 +4061,7 @@
 OpMemberDecorate %_struct_98 0 Offset 0
 OpDecorate %100 DescriptorSet 7
 OpDecorate %100 Binding 1
-OpDecorate %117 NonUniformEXT
+OpDecorate %117 NonUniform
 %void = OpTypeVoid
 %10 = OpTypeFunction %void
 %float = OpTypeFloat 32
@@ -6313,7 +6313,7 @@
 
   const std::string defs_before =
       R"(OpCapability Shader
-OpCapability RuntimeDescriptorArrayEXT
+OpCapability RuntimeDescriptorArray
 OpExtension "SPV_EXT_descriptor_indexing"
 %1 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel Logical GLSL450
@@ -6366,7 +6366,7 @@
 
   const std::string defs_after =
       R"(OpCapability Shader
-OpCapability RuntimeDescriptorArrayEXT
+OpCapability RuntimeDescriptorArray
 OpExtension "SPV_EXT_descriptor_indexing"
 OpExtension "SPV_KHR_storage_buffer_storage_class"
 %1 = OpExtInstImport "GLSL.std.450"
@@ -6970,9 +6970,9 @@
 
   const std::string defs_before =
       R"(OpCapability Shader
-OpCapability ShaderNonUniformEXT
-OpCapability RuntimeDescriptorArrayEXT
-OpCapability UniformBufferArrayNonUniformIndexingEXT
+OpCapability ShaderNonUniform
+OpCapability RuntimeDescriptorArray
+OpCapability UniformBufferArrayNonUniformIndexing
 OpExtension "SPV_EXT_descriptor_indexing"
 %1 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel Logical GLSL450
@@ -6993,9 +6993,9 @@
 OpDecorate %uniformBuffer Binding 3
 OpDecorate %nu_ii Flat
 OpDecorate %nu_ii Location 0
-OpDecorate %nu_ii NonUniformEXT
-OpDecorate %16 NonUniformEXT
-OpDecorate %20 NonUniformEXT
+OpDecorate %nu_ii NonUniform
+OpDecorate %16 NonUniform
+OpDecorate %20 NonUniform
 %void = OpTypeVoid
 %3 = OpTypeFunction %void
 %float = OpTypeFloat 32
@@ -7014,9 +7014,9 @@
 
   const std::string defs_after =
       R"(OpCapability Shader
-OpCapability ShaderNonUniformEXT
-OpCapability RuntimeDescriptorArrayEXT
-OpCapability UniformBufferArrayNonUniformIndexingEXT
+OpCapability ShaderNonUniform
+OpCapability RuntimeDescriptorArray
+OpCapability UniformBufferArrayNonUniformIndexing
 OpExtension "SPV_EXT_descriptor_indexing"
 OpExtension "SPV_KHR_storage_buffer_storage_class"
 %1 = OpExtInstImport "GLSL.std.450"
@@ -7038,22 +7038,22 @@
 OpDecorate %uniformBuffer Binding 3
 OpDecorate %nu_ii Flat
 OpDecorate %nu_ii Location 0
-OpDecorate %nu_ii NonUniformEXT
-OpDecorate %7 NonUniformEXT
-OpDecorate %102 NonUniformEXT
+OpDecorate %nu_ii NonUniform
+OpDecorate %7 NonUniform
+OpDecorate %102 NonUniform
 OpDecorate %_runtimearr_uint ArrayStride 4
 OpDecorate %_struct_31 Block
 OpMemberDecorate %_struct_31 0 Offset 0
 OpDecorate %33 DescriptorSet 7
 OpDecorate %33 Binding 1
-OpDecorate %130 NonUniformEXT
+OpDecorate %130 NonUniform
 OpDecorate %_struct_55 Block
 OpMemberDecorate %_struct_55 0 Offset 0
 OpMemberDecorate %_struct_55 1 Offset 4
 OpDecorate %57 DescriptorSet 7
 OpDecorate %57 Binding 0
 OpDecorate %gl_FragCoord BuiltIn FragCoord
-OpDecorate %127 NonUniformEXT
+OpDecorate %127 NonUniform
 %void = OpTypeVoid
 %10 = OpTypeFunction %void
 %float = OpTypeFloat 32
@@ -7251,9 +7251,9 @@
 
   const std::string defs_before =
       R"(OpCapability Shader
-OpCapability ShaderNonUniformEXT
-OpCapability RuntimeDescriptorArrayEXT
-OpCapability StorageBufferArrayNonUniformIndexingEXT
+OpCapability ShaderNonUniform
+OpCapability RuntimeDescriptorArray
+OpCapability StorageBufferArrayNonUniformIndexing
 OpExtension "SPV_EXT_descriptor_indexing"
 %1 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel Logical GLSL450
@@ -7274,9 +7274,9 @@
 OpDecorate %storageBuffer Binding 3
 OpDecorate %nu_ii Flat
 OpDecorate %nu_ii Location 0
-OpDecorate %nu_ii NonUniformEXT
-OpDecorate %16 NonUniformEXT
-OpDecorate %20 NonUniformEXT
+OpDecorate %nu_ii NonUniform
+OpDecorate %16 NonUniform
+OpDecorate %20 NonUniform
 %void = OpTypeVoid
 %3 = OpTypeFunction %void
 %float = OpTypeFloat 32
@@ -7295,9 +7295,9 @@
 
   const std::string defs_after =
       R"(OpCapability Shader
-OpCapability ShaderNonUniformEXT
-OpCapability RuntimeDescriptorArrayEXT
-OpCapability StorageBufferArrayNonUniformIndexingEXT
+OpCapability ShaderNonUniform
+OpCapability RuntimeDescriptorArray
+OpCapability StorageBufferArrayNonUniformIndexing
 OpExtension "SPV_EXT_descriptor_indexing"
 OpExtension "SPV_KHR_storage_buffer_storage_class"
 %1 = OpExtInstImport "GLSL.std.450"
@@ -7319,22 +7319,22 @@
 OpDecorate %storageBuffer Binding 3
 OpDecorate %nu_ii Flat
 OpDecorate %nu_ii Location 0
-OpDecorate %nu_ii NonUniformEXT
-OpDecorate %7 NonUniformEXT
-OpDecorate %102 NonUniformEXT
+OpDecorate %nu_ii NonUniform
+OpDecorate %7 NonUniform
+OpDecorate %102 NonUniform
 OpDecorate %_runtimearr_uint ArrayStride 4
 OpDecorate %_struct_31 Block
 OpMemberDecorate %_struct_31 0 Offset 0
 OpDecorate %33 DescriptorSet 7
 OpDecorate %33 Binding 1
-OpDecorate %130 NonUniformEXT
+OpDecorate %130 NonUniform
 OpDecorate %_struct_55 Block
 OpMemberDecorate %_struct_55 0 Offset 0
 OpMemberDecorate %_struct_55 1 Offset 4
 OpDecorate %57 DescriptorSet 7
 OpDecorate %57 Binding 0
 OpDecorate %gl_FragCoord BuiltIn FragCoord
-OpDecorate %127 NonUniformEXT
+OpDecorate %127 NonUniform
 %void = OpTypeVoid
 %10 = OpTypeFunction %void
 %float = OpTypeFloat 32
@@ -7521,9 +7521,9 @@
 
   const std::string defs_before =
       R"(OpCapability Shader
-OpCapability ShaderNonUniformEXT
-OpCapability RuntimeDescriptorArrayEXT
-OpCapability StorageBufferArrayNonUniformIndexingEXT
+OpCapability ShaderNonUniform
+OpCapability RuntimeDescriptorArray
+OpCapability StorageBufferArrayNonUniformIndexing
 OpExtension "SPV_EXT_descriptor_indexing"
 %1 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel Logical GLSL450
@@ -7544,9 +7544,9 @@
 OpDecorate %storageBuffer Binding 3
 OpDecorate %nu_ii Flat
 OpDecorate %nu_ii Location 0
-OpDecorate %nu_ii NonUniformEXT
-OpDecorate %16 NonUniformEXT
-OpDecorate %20 NonUniformEXT
+OpDecorate %nu_ii NonUniform
+OpDecorate %16 NonUniform
+OpDecorate %20 NonUniform
 %void = OpTypeVoid
 %3 = OpTypeFunction %void
 %float = OpTypeFloat 32
@@ -7565,9 +7565,9 @@
 
   const std::string defs_after =
       R"(OpCapability Shader
-OpCapability ShaderNonUniformEXT
-OpCapability RuntimeDescriptorArrayEXT
-OpCapability StorageBufferArrayNonUniformIndexingEXT
+OpCapability ShaderNonUniform
+OpCapability RuntimeDescriptorArray
+OpCapability StorageBufferArrayNonUniformIndexing
 OpExtension "SPV_EXT_descriptor_indexing"
 OpExtension "SPV_KHR_storage_buffer_storage_class"
 %1 = OpExtInstImport "GLSL.std.450"
@@ -7589,22 +7589,22 @@
 OpDecorate %storageBuffer Binding 3
 OpDecorate %nu_ii Flat
 OpDecorate %nu_ii Location 0
-OpDecorate %nu_ii NonUniformEXT
-OpDecorate %7 NonUniformEXT
-OpDecorate %102 NonUniformEXT
+OpDecorate %nu_ii NonUniform
+OpDecorate %7 NonUniform
+OpDecorate %102 NonUniform
 OpDecorate %_runtimearr_uint ArrayStride 4
 OpDecorate %_struct_31 Block
 OpMemberDecorate %_struct_31 0 Offset 0
 OpDecorate %33 DescriptorSet 7
 OpDecorate %33 Binding 1
-OpDecorate %130 NonUniformEXT
+OpDecorate %130 NonUniform
 OpDecorate %_struct_55 Block
 OpMemberDecorate %_struct_55 0 Offset 0
 OpMemberDecorate %_struct_55 1 Offset 4
 OpDecorate %57 DescriptorSet 7
 OpDecorate %57 Binding 0
 OpDecorate %gl_FragCoord BuiltIn FragCoord
-OpDecorate %127 NonUniformEXT
+OpDecorate %127 NonUniform
 %void = OpTypeVoid
 %10 = OpTypeFunction %void
 %float = OpTypeFloat 32
@@ -8027,9 +8027,9 @@
 
   const std::string defs_before =
       R"(OpCapability Shader
-OpCapability ShaderNonUniformEXT
-OpCapability RuntimeDescriptorArrayEXT
-OpCapability StorageBufferArrayNonUniformIndexingEXT
+OpCapability ShaderNonUniform
+OpCapability RuntimeDescriptorArray
+OpCapability StorageBufferArrayNonUniformIndexing
 OpExtension "SPV_EXT_descriptor_indexing"
 %1 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel Logical GLSL450
@@ -8049,8 +8049,8 @@
 OpDecorate %storageBuffer Binding 4
 OpDecorate %nu_ii Flat
 OpDecorate %nu_ii Location 0
-OpDecorate %nu_ii NonUniformEXT
-OpDecorate %14 NonUniformEXT
+OpDecorate %nu_ii NonUniform
+OpDecorate %14 NonUniform
 OpDecorate %b Location 1
 %void = OpTypeVoid
 %3 = OpTypeFunction %void
@@ -8070,9 +8070,9 @@
 
   const std::string defs_after =
       R"(OpCapability Shader
-OpCapability ShaderNonUniformEXT
-OpCapability RuntimeDescriptorArrayEXT
-OpCapability StorageBufferArrayNonUniformIndexingEXT
+OpCapability ShaderNonUniform
+OpCapability RuntimeDescriptorArray
+OpCapability StorageBufferArrayNonUniformIndexing
 OpExtension "SPV_EXT_descriptor_indexing"
 OpExtension "SPV_KHR_storage_buffer_storage_class"
 %1 = OpExtInstImport "GLSL.std.450"
@@ -8093,8 +8093,8 @@
 OpDecorate %storageBuffer Binding 4
 OpDecorate %nu_ii Flat
 OpDecorate %nu_ii Location 0
-OpDecorate %nu_ii NonUniformEXT
-OpDecorate %7 NonUniformEXT
+OpDecorate %nu_ii NonUniform
+OpDecorate %7 NonUniform
 OpDecorate %b Location 1
 OpDecorate %_runtimearr_uint ArrayStride 4
 OpDecorate %_struct_31 Block
@@ -8301,8 +8301,8 @@
 
   const std::string defs_before =
       R"(OpCapability Shader
-OpCapability ShaderNonUniformEXT
-OpCapability UniformBufferArrayNonUniformIndexingEXT
+OpCapability ShaderNonUniform
+OpCapability UniformBufferArrayNonUniformIndexing
 OpExtension "SPV_EXT_descriptor_indexing"
 %1 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel Logical GLSL450
@@ -8323,9 +8323,9 @@
 OpDecorate %uniformBuffer Binding 3
 OpDecorate %nu_ii Flat
 OpDecorate %nu_ii Location 0
-OpDecorate %nu_ii NonUniformEXT
-OpDecorate %18 NonUniformEXT
-OpDecorate %22 NonUniformEXT
+OpDecorate %nu_ii NonUniform
+OpDecorate %18 NonUniform
+OpDecorate %22 NonUniform
 %void = OpTypeVoid
 %3 = OpTypeFunction %void
 %float = OpTypeFloat 32
@@ -8346,8 +8346,8 @@
 
   const std::string defs_after =
       R"(OpCapability Shader
-OpCapability ShaderNonUniformEXT
-OpCapability UniformBufferArrayNonUniformIndexingEXT
+OpCapability ShaderNonUniform
+OpCapability UniformBufferArrayNonUniformIndexing
 OpExtension "SPV_EXT_descriptor_indexing"
 OpExtension "SPV_KHR_storage_buffer_storage_class"
 %1 = OpExtInstImport "GLSL.std.450"
@@ -8369,10 +8369,10 @@
 OpDecorate %uniformBuffer Binding 3
 OpDecorate %nu_ii Flat
 OpDecorate %nu_ii Location 0
-OpDecorate %nu_ii NonUniformEXT
-OpDecorate %7 NonUniformEXT
-OpDecorate %89 NonUniformEXT
-OpDecorate %120 NonUniformEXT
+OpDecorate %nu_ii NonUniform
+OpDecorate %7 NonUniform
+OpDecorate %89 NonUniform
+OpDecorate %120 NonUniform
 OpDecorate %_runtimearr_uint ArrayStride 4
 OpDecorate %_struct_39 Block
 OpMemberDecorate %_struct_39 0 Offset 0
@@ -8384,7 +8384,7 @@
 OpMemberDecorate %_struct_98 0 Offset 0
 OpDecorate %100 DescriptorSet 7
 OpDecorate %100 Binding 1
-OpDecorate %117 NonUniformEXT
+OpDecorate %117 NonUniform
 %void = OpTypeVoid
 %10 = OpTypeFunction %void
 %float = OpTypeFloat 32
@@ -8575,7 +8575,7 @@
 
   const std::string defs_before =
       R"(OpCapability Shader
-OpCapability RuntimeDescriptorArrayEXT
+OpCapability RuntimeDescriptorArray
 OpExtension "SPV_EXT_descriptor_indexing"
 %1 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel Logical GLSL450
@@ -8622,7 +8622,7 @@
 
   const std::string defs_after =
       R"(OpCapability Shader
-OpCapability RuntimeDescriptorArrayEXT
+OpCapability RuntimeDescriptorArray
 OpExtension "SPV_EXT_descriptor_indexing"
 OpExtension "SPV_KHR_storage_buffer_storage_class"
 %1 = OpExtInstImport "GLSL.std.450"
@@ -8895,7 +8895,7 @@
   // }
 
   const std::string defs_before =
-      R"(OpCapability RuntimeDescriptorArrayEXT
+      R"(OpCapability RuntimeDescriptorArray
 OpCapability RayTracingNV
 OpExtension "SPV_EXT_descriptor_indexing"
 OpExtension "SPV_NV_ray_tracing"
@@ -8923,7 +8923,7 @@
 )";
 
   const std::string defs_after =
-      R"(OpCapability RuntimeDescriptorArrayEXT
+      R"(OpCapability RuntimeDescriptorArray
 OpCapability RayTracingNV
 OpExtension "SPV_EXT_descriptor_indexing"
 OpExtension "SPV_NV_ray_tracing"
@@ -9219,7 +9219,7 @@
   // }
 
   const std::string defs_before =
-      R"(OpCapability RuntimeDescriptorArrayEXT
+      R"(OpCapability RuntimeDescriptorArray
 OpCapability RayTracingNV
 OpExtension "SPV_EXT_descriptor_indexing"
 OpExtension "SPV_NV_ray_tracing"
@@ -9247,7 +9247,7 @@
 )";
 
   const std::string defs_after =
-      R"(OpCapability RuntimeDescriptorArrayEXT
+      R"(OpCapability RuntimeDescriptorArray
 OpCapability RayTracingNV
 OpExtension "SPV_EXT_descriptor_indexing"
 OpExtension "SPV_NV_ray_tracing"
@@ -9543,7 +9543,7 @@
   // }
 
   const std::string defs_before =
-      R"(OpCapability RuntimeDescriptorArrayEXT
+      R"(OpCapability RuntimeDescriptorArray
 OpCapability RayTracingNV
 OpExtension "SPV_EXT_descriptor_indexing"
 OpExtension "SPV_NV_ray_tracing"
@@ -9571,7 +9571,7 @@
 )";
 
   const std::string defs_after =
-      R"(OpCapability RuntimeDescriptorArrayEXT
+      R"(OpCapability RuntimeDescriptorArray
 OpCapability RayTracingNV
 OpExtension "SPV_EXT_descriptor_indexing"
 OpExtension "SPV_NV_ray_tracing"
@@ -9867,7 +9867,7 @@
   // }
 
   const std::string defs_before =
-      R"(OpCapability RuntimeDescriptorArrayEXT
+      R"(OpCapability RuntimeDescriptorArray
 OpCapability RayTracingNV
 OpExtension "SPV_EXT_descriptor_indexing"
 OpExtension "SPV_NV_ray_tracing"
@@ -9895,7 +9895,7 @@
 )";
 
   const std::string defs_after =
-      R"(OpCapability RuntimeDescriptorArrayEXT
+      R"(OpCapability RuntimeDescriptorArray
 OpCapability RayTracingNV
 OpExtension "SPV_EXT_descriptor_indexing"
 OpExtension "SPV_NV_ray_tracing"
@@ -10191,7 +10191,7 @@
   // }
 
   const std::string defs_before =
-      R"(OpCapability RuntimeDescriptorArrayEXT
+      R"(OpCapability RuntimeDescriptorArray
 OpCapability RayTracingNV
 OpExtension "SPV_EXT_descriptor_indexing"
 OpExtension "SPV_NV_ray_tracing"
@@ -10219,7 +10219,7 @@
 )";
 
   const std::string defs_after =
-      R"(OpCapability RuntimeDescriptorArrayEXT
+      R"(OpCapability RuntimeDescriptorArray
 OpCapability RayTracingNV
 OpExtension "SPV_EXT_descriptor_indexing"
 OpExtension "SPV_NV_ray_tracing"
@@ -10515,7 +10515,7 @@
   // }
 
   const std::string defs_before =
-      R"(OpCapability RuntimeDescriptorArrayEXT
+      R"(OpCapability RuntimeDescriptorArray
 OpCapability RayTracingNV
 OpExtension "SPV_EXT_descriptor_indexing"
 OpExtension "SPV_NV_ray_tracing"
@@ -10543,7 +10543,7 @@
 )";
 
   const std::string defs_after =
-      R"(OpCapability RuntimeDescriptorArrayEXT
+      R"(OpCapability RuntimeDescriptorArray
 OpCapability RayTracingNV
 OpExtension "SPV_EXT_descriptor_indexing"
 OpExtension "SPV_NV_ray_tracing"
@@ -10820,6 +10820,415 @@
       true, 7u, 23u, true, true, 2u);
 }
 
+TEST_F(InstBindlessTest, InstBoundsInitSameBlockOpReplication) {
+  // Test that same block ops like OpSampledImage are replicated properly
+  // where needed.
+  //
+  // clang-format off
+  //
+  // #version 450 core
+  // #extension GL_EXT_nonuniform_qualifier : enable
+  //
+  // layout(location = 0) in vec2 inTexcoord;
+  // layout(location = 0) out vec4 outColor;
+  //
+  // layout(set = 0, 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];
+  //
+  // void main() {
+  //   int index = 0;
+  //   float x = texture(sampler2D(uniformTexArr[nonuniformEXT(index)], uniformSampler), inTexcoord.xy).x;
+  //   float y = texture(sampler2D(uniformTex, uniformSampler), inTexcoord.xy * uniforms.var0.xy).x;
+  //   outColor = vec4(x, y, 0.0, 0.0);
+  // }
+  //
+  // clang-format on
+
+  const std::string defs_before =
+      R"(OpCapability Shader
+OpCapability ShaderNonUniformEXT
+OpCapability SampledImageArrayNonUniformIndexingEXT
+OpExtension "SPV_EXT_descriptor_indexing"
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %inTexcoord %outColor
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 450
+OpSourceExtension "GL_EXT_nonuniform_qualifier"
+OpName %main "main"
+OpName %index "index"
+OpName %x "x"
+OpName %uniformTexArr "uniformTexArr"
+OpName %uniformSampler "uniformSampler"
+OpName %inTexcoord "inTexcoord"
+OpName %y "y"
+OpName %uniformTex "uniformTex"
+OpName %Uniforms "Uniforms"
+OpMemberName %Uniforms 0 "var0"
+OpName %uniforms "uniforms"
+OpName %outColor "outColor"
+OpDecorate %uniformTexArr DescriptorSet 0
+OpDecorate %uniformTexArr Binding 3
+OpDecorate %19 NonUniformEXT
+OpDecorate %22 NonUniformEXT
+OpDecorate %uniformSampler DescriptorSet 0
+OpDecorate %uniformSampler Binding 1
+OpDecorate %inTexcoord Location 0
+OpDecorate %uniformTex DescriptorSet 0
+OpDecorate %uniformTex Binding 2
+OpMemberDecorate %Uniforms 0 Offset 0
+OpDecorate %Uniforms Block
+OpDecorate %uniforms DescriptorSet 0
+OpDecorate %uniforms Binding 0
+OpDecorate %outColor Location 0
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%int = OpTypeInt 32 1
+%_ptr_Function_int = OpTypePointer Function %int
+%int_0 = OpConstant %int 0
+%float = OpTypeFloat 32
+%_ptr_Function_float = OpTypePointer Function %float
+%13 = OpTypeImage %float 2D 0 0 0 1 Unknown
+%uint = OpTypeInt 32 0
+%uint_8 = OpConstant %uint 8
+%_arr_13_uint_8 = OpTypeArray %13 %uint_8
+%_ptr_UniformConstant__arr_13_uint_8 = OpTypePointer UniformConstant %_arr_13_uint_8
+%uniformTexArr = OpVariable %_ptr_UniformConstant__arr_13_uint_8 UniformConstant
+%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13
+%23 = OpTypeSampler
+%_ptr_UniformConstant_23 = OpTypePointer UniformConstant %23
+%uniformSampler = OpVariable %_ptr_UniformConstant_23 UniformConstant
+%27 = OpTypeSampledImage %13
+%v2float = OpTypeVector %float 2
+%_ptr_Input_v2float = OpTypePointer Input %v2float
+%inTexcoord = OpVariable %_ptr_Input_v2float Input
+%v4float = OpTypeVector %float 4
+%uint_0 = OpConstant %uint 0
+%uniformTex = OpVariable %_ptr_UniformConstant_13 UniformConstant
+%Uniforms = OpTypeStruct %v2float
+%_ptr_Uniform_Uniforms = OpTypePointer Uniform %Uniforms
+%uniforms = OpVariable %_ptr_Uniform_Uniforms Uniform
+%_ptr_Uniform_v2float = OpTypePointer Uniform %v2float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%outColor = OpVariable %_ptr_Output_v4float Output
+%float_0 = OpConstant %float 0
+)";
+
+  const std::string defs_after =
+      R"(OpCapability Shader
+OpCapability ShaderNonUniform
+OpCapability SampledImageArrayNonUniformIndexing
+OpExtension "SPV_EXT_descriptor_indexing"
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %inTexcoord %outColor %gl_FragCoord
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 450
+OpSourceExtension "GL_EXT_nonuniform_qualifier"
+OpName %main "main"
+OpName %index "index"
+OpName %x "x"
+OpName %uniformTexArr "uniformTexArr"
+OpName %uniformSampler "uniformSampler"
+OpName %inTexcoord "inTexcoord"
+OpName %y "y"
+OpName %uniformTex "uniformTex"
+OpName %Uniforms "Uniforms"
+OpMemberName %Uniforms 0 "var0"
+OpName %uniforms "uniforms"
+OpName %outColor "outColor"
+OpDecorate %uniformTexArr DescriptorSet 0
+OpDecorate %uniformTexArr Binding 3
+OpDecorate %19 NonUniform
+OpDecorate %22 NonUniform
+OpDecorate %uniformSampler DescriptorSet 0
+OpDecorate %uniformSampler Binding 1
+OpDecorate %inTexcoord Location 0
+OpDecorate %uniformTex DescriptorSet 0
+OpDecorate %uniformTex Binding 2
+OpMemberDecorate %Uniforms 0 Offset 0
+OpDecorate %Uniforms Block
+OpDecorate %uniforms DescriptorSet 0
+OpDecorate %uniforms Binding 0
+OpDecorate %outColor Location 0
+OpDecorate %63 NonUniform
+OpDecorate %_runtimearr_uint ArrayStride 4
+OpDecorate %_struct_75 Block
+OpMemberDecorate %_struct_75 0 Offset 0
+OpMemberDecorate %_struct_75 1 Offset 4
+OpDecorate %77 DescriptorSet 7
+OpDecorate %77 Binding 0
+OpDecorate %gl_FragCoord BuiltIn FragCoord
+OpDecorate %_struct_132 Block
+OpMemberDecorate %_struct_132 0 Offset 0
+OpDecorate %134 DescriptorSet 7
+OpDecorate %134 Binding 1
+OpDecorate %151 NonUniform
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%int = OpTypeInt 32 1
+%_ptr_Function_int = OpTypePointer Function %int
+%int_0 = OpConstant %int 0
+%float = OpTypeFloat 32
+%_ptr_Function_float = OpTypePointer Function %float
+%13 = OpTypeImage %float 2D 0 0 0 1 Unknown
+%uint = OpTypeInt 32 0
+%uint_8 = OpConstant %uint 8
+%_arr_13_uint_8 = OpTypeArray %13 %uint_8
+%_ptr_UniformConstant__arr_13_uint_8 = OpTypePointer UniformConstant %_arr_13_uint_8
+%uniformTexArr = OpVariable %_ptr_UniformConstant__arr_13_uint_8 UniformConstant
+%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13
+%23 = OpTypeSampler
+%_ptr_UniformConstant_23 = OpTypePointer UniformConstant %23
+%uniformSampler = OpVariable %_ptr_UniformConstant_23 UniformConstant
+%27 = OpTypeSampledImage %13
+%v2float = OpTypeVector %float 2
+%_ptr_Input_v2float = OpTypePointer Input %v2float
+%inTexcoord = OpVariable %_ptr_Input_v2float Input
+%v4float = OpTypeVector %float 4
+%uint_0 = OpConstant %uint 0
+%uniformTex = OpVariable %_ptr_UniformConstant_13 UniformConstant
+%Uniforms = OpTypeStruct %v2float
+%_ptr_Uniform_Uniforms = OpTypePointer Uniform %Uniforms
+%uniforms = OpVariable %_ptr_Uniform_Uniforms Uniform
+%_ptr_Uniform_v2float = OpTypePointer Uniform %v2float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%outColor = OpVariable %_ptr_Output_v4float Output
+%float_0 = OpConstant %float 0
+%bool = OpTypeBool
+%68 = OpTypeFunction %void %uint %uint %uint %uint
+%_runtimearr_uint = OpTypeRuntimeArray %uint
+%_struct_75 = OpTypeStruct %uint %_runtimearr_uint
+%_ptr_StorageBuffer__struct_75 = OpTypePointer StorageBuffer %_struct_75
+%77 = OpVariable %_ptr_StorageBuffer__struct_75 StorageBuffer
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%uint_10 = OpConstant %uint 10
+%uint_4 = OpConstant %uint 4
+%uint_1 = OpConstant %uint 1
+%uint_23 = OpConstant %uint 23
+%uint_2 = OpConstant %uint 2
+%uint_3 = OpConstant %uint 3
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%gl_FragCoord = OpVariable %_ptr_Input_v4float Input
+%v4uint = OpTypeVector %uint 4
+%uint_5 = OpConstant %uint 5
+%uint_7 = OpConstant %uint 7
+%uint_9 = OpConstant %uint 9
+%uint_79 = OpConstant %uint 79
+%122 = OpConstantNull %v4float
+%126 = OpTypeFunction %uint %uint %uint %uint %uint
+%_struct_132 = OpTypeStruct %_runtimearr_uint
+%_ptr_StorageBuffer__struct_132 = OpTypePointer StorageBuffer %_struct_132
+%134 = OpVariable %_ptr_StorageBuffer__struct_132 StorageBuffer
+%uint_87 = OpConstant %uint 87
+%165 = OpConstantNull %v2float
+%uint_89 = OpConstant %uint 89
+)";
+
+  const std::string func_before =
+      R"(%main = OpFunction %void None %3
+%5 = OpLabel
+%index = OpVariable %_ptr_Function_int Function
+%x = OpVariable %_ptr_Function_float Function
+%y = OpVariable %_ptr_Function_float Function
+OpStore %index %int_0
+%19 = OpLoad %int %index
+%21 = OpAccessChain %_ptr_UniformConstant_13 %uniformTexArr %19
+%22 = OpLoad %13 %21
+%26 = OpLoad %23 %uniformSampler
+%28 = OpSampledImage %27 %22 %26
+%32 = OpLoad %v2float %inTexcoord
+%34 = OpImageSampleImplicitLod %v4float %28 %32
+%36 = OpCompositeExtract %float %34 0
+OpStore %x %36
+%39 = OpLoad %13 %uniformTex
+%40 = OpLoad %23 %uniformSampler
+%41 = OpSampledImage %27 %39 %40
+%42 = OpLoad %v2float %inTexcoord
+%47 = OpAccessChain %_ptr_Uniform_v2float %uniforms %int_0
+%48 = OpLoad %v2float %47
+%49 = OpFMul %v2float %42 %48
+%50 = OpImageSampleImplicitLod %v4float %41 %49
+%51 = OpCompositeExtract %float %50 0
+OpStore %y %51
+%54 = OpLoad %float %x
+%55 = OpLoad %float %y
+%57 = OpCompositeConstruct %v4float %54 %55 %float_0 %float_0
+OpStore %outColor %57
+OpReturn
+OpFunctionEnd
+)";
+
+  const std::string func_after =
+      R"(%main = OpFunction %void None %3
+%5 = OpLabel
+%index = OpVariable %_ptr_Function_int Function
+%x = OpVariable %_ptr_Function_float Function
+%y = OpVariable %_ptr_Function_float Function
+OpStore %index %int_0
+%19 = OpLoad %int %index
+%21 = OpAccessChain %_ptr_UniformConstant_13 %uniformTexArr %19
+%22 = OpLoad %13 %21
+%26 = OpLoad %23 %uniformSampler
+%28 = OpSampledImage %27 %22 %26
+%32 = OpLoad %v2float %inTexcoord
+%59 = OpULessThan %bool %19 %uint_8
+OpSelectionMerge %60 None
+OpBranchConditional %59 %61 %62
+%61 = OpLabel
+%63 = OpLoad %13 %21
+%64 = OpSampledImage %27 %63 %26
+%124 = OpBitcast %uint %19
+%146 = OpFunctionCall %uint %125 %uint_0 %uint_0 %uint_3 %124
+%147 = OpINotEqual %bool %146 %uint_0
+OpSelectionMerge %148 None
+OpBranchConditional %147 %149 %150
+%149 = OpLabel
+%151 = OpLoad %13 %21
+%152 = OpSampledImage %27 %151 %26
+%153 = OpImageSampleImplicitLod %v4float %152 %32
+OpBranch %148
+%150 = OpLabel
+%154 = OpBitcast %uint %19
+%155 = OpFunctionCall %void %67 %uint_79 %uint_1 %154 %uint_0
+OpBranch %148
+%148 = OpLabel
+%156 = OpPhi %v4float %153 %149 %122 %150
+OpBranch %60
+%62 = OpLabel
+%66 = OpBitcast %uint %19
+%121 = OpFunctionCall %void %67 %uint_79 %uint_0 %66 %uint_8
+OpBranch %60
+%60 = OpLabel
+%123 = OpPhi %v4float %156 %148 %122 %62
+%36 = OpCompositeExtract %float %123 0
+OpStore %x %36
+%39 = OpLoad %13 %uniformTex
+%40 = OpLoad %23 %uniformSampler
+%41 = OpSampledImage %27 %39 %40
+%42 = OpLoad %v2float %inTexcoord
+%47 = OpAccessChain %_ptr_Uniform_v2float %uniforms %int_0
+%157 = OpFunctionCall %uint %125 %uint_0 %uint_0 %uint_0 %uint_0
+%158 = OpINotEqual %bool %157 %uint_0
+OpSelectionMerge %159 None
+OpBranchConditional %158 %160 %161
+%160 = OpLabel
+%162 = OpLoad %v2float %47
+OpBranch %159
+%161 = OpLabel
+%164 = OpFunctionCall %void %67 %uint_87 %uint_1 %uint_0 %uint_0
+OpBranch %159
+%159 = OpLabel
+%166 = OpPhi %v2float %162 %160 %165 %161
+%49 = OpFMul %v2float %42 %166
+%167 = OpSampledImage %27 %39 %40
+%168 = OpFunctionCall %uint %125 %uint_0 %uint_0 %uint_2 %uint_0
+%169 = OpINotEqual %bool %168 %uint_0
+OpSelectionMerge %170 None
+OpBranchConditional %169 %171 %172
+%171 = OpLabel
+%173 = OpLoad %13 %uniformTex
+%174 = OpSampledImage %27 %173 %40
+%175 = OpImageSampleImplicitLod %v4float %174 %49
+OpBranch %170
+%172 = OpLabel
+%177 = OpFunctionCall %void %67 %uint_89 %uint_1 %uint_0 %uint_0
+OpBranch %170
+%170 = OpLabel
+%178 = OpPhi %v4float %175 %171 %122 %172
+%51 = OpCompositeExtract %float %178 0
+OpStore %y %51
+%54 = OpLoad %float %x
+%55 = OpLoad %float %y
+%57 = OpCompositeConstruct %v4float %54 %55 %float_0 %float_0
+OpStore %outColor %57
+OpReturn
+OpFunctionEnd
+)";
+
+  const std::string new_funcs =
+      R"(%67 = OpFunction %void None %68
+%69 = OpFunctionParameter %uint
+%70 = OpFunctionParameter %uint
+%71 = OpFunctionParameter %uint
+%72 = OpFunctionParameter %uint
+%73 = OpLabel
+%79 = OpAccessChain %_ptr_StorageBuffer_uint %77 %uint_0
+%82 = OpAtomicIAdd %uint %79 %uint_4 %uint_0 %uint_10
+%83 = OpIAdd %uint %82 %uint_10
+%84 = OpArrayLength %uint %77 1
+%85 = OpULessThanEqual %bool %83 %84
+OpSelectionMerge %86 None
+OpBranchConditional %85 %87 %86
+%87 = OpLabel
+%88 = OpIAdd %uint %82 %uint_0
+%90 = OpAccessChain %_ptr_StorageBuffer_uint %77 %uint_1 %88
+OpStore %90 %uint_10
+%92 = OpIAdd %uint %82 %uint_1
+%93 = OpAccessChain %_ptr_StorageBuffer_uint %77 %uint_1 %92
+OpStore %93 %uint_23
+%95 = OpIAdd %uint %82 %uint_2
+%96 = OpAccessChain %_ptr_StorageBuffer_uint %77 %uint_1 %95
+OpStore %96 %69
+%98 = OpIAdd %uint %82 %uint_3
+%99 = OpAccessChain %_ptr_StorageBuffer_uint %77 %uint_1 %98
+OpStore %99 %uint_4
+%102 = OpLoad %v4float %gl_FragCoord
+%104 = OpBitcast %v4uint %102
+%105 = OpCompositeExtract %uint %104 0
+%106 = OpIAdd %uint %82 %uint_4
+%107 = OpAccessChain %_ptr_StorageBuffer_uint %77 %uint_1 %106
+OpStore %107 %105
+%108 = OpCompositeExtract %uint %104 1
+%110 = OpIAdd %uint %82 %uint_5
+%111 = OpAccessChain %_ptr_StorageBuffer_uint %77 %uint_1 %110
+OpStore %111 %108
+%113 = OpIAdd %uint %82 %uint_7
+%114 = OpAccessChain %_ptr_StorageBuffer_uint %77 %uint_1 %113
+OpStore %114 %70
+%115 = OpIAdd %uint %82 %uint_8
+%116 = OpAccessChain %_ptr_StorageBuffer_uint %77 %uint_1 %115
+OpStore %116 %71
+%118 = OpIAdd %uint %82 %uint_9
+%119 = OpAccessChain %_ptr_StorageBuffer_uint %77 %uint_1 %118
+OpStore %119 %72
+OpBranch %86
+%86 = OpLabel
+OpReturn
+OpFunctionEnd
+%125 = OpFunction %uint None %126
+%127 = OpFunctionParameter %uint
+%128 = OpFunctionParameter %uint
+%129 = OpFunctionParameter %uint
+%130 = OpFunctionParameter %uint
+%131 = OpLabel
+%135 = OpAccessChain %_ptr_StorageBuffer_uint %134 %uint_0 %127
+%136 = OpLoad %uint %135
+%137 = OpIAdd %uint %136 %128
+%138 = OpAccessChain %_ptr_StorageBuffer_uint %134 %uint_0 %137
+%139 = OpLoad %uint %138
+%140 = OpIAdd %uint %139 %129
+%141 = OpAccessChain %_ptr_StorageBuffer_uint %134 %uint_0 %140
+%142 = OpLoad %uint %141
+%143 = OpIAdd %uint %142 %130
+%144 = OpAccessChain %_ptr_StorageBuffer_uint %134 %uint_0 %143
+%145 = OpLoad %uint %144
+OpReturnValue %145
+OpFunctionEnd
+)";
+
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndCheck<InstBindlessCheckPass>(
+      defs_before + func_before, defs_after + func_after + new_funcs, true,
+      true, 7u, 23u, true, true, 2u);
+}
+
 // TODO(greg-lunarg): Add tests to verify handling of these cases:
 //
 //   Compute shader
diff --git a/test/opt/inst_buff_addr_check_test.cpp b/test/opt/inst_buff_addr_check_test.cpp
index c31266e..f8448a9 100644
--- a/test/opt/inst_buff_addr_check_test.cpp
+++ b/test/opt/inst_buff_addr_check_test.cpp
@@ -51,10 +51,10 @@
 
   const std::string defs_before =
       R"(OpCapability Shader
-OpCapability PhysicalStorageBufferAddressesEXT
+OpCapability PhysicalStorageBufferAddresses
 OpExtension "SPV_EXT_physical_storage_buffer"
 %1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
 OpEntryPoint GLCompute %main "main"
 OpExecutionMode %main LocalSize 1 1 1
 OpSource GLSL 450
@@ -78,31 +78,31 @@
 OpDecorate %u_info Binding 0
 %void = OpTypeVoid
 %3 = OpTypeFunction %void
-OpTypeForwardPointer %_ptr_PhysicalStorageBufferEXT_bufStruct PhysicalStorageBufferEXT
+OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_bufStruct PhysicalStorageBuffer
 %uint = OpTypeInt 32 0
-%ufoo = OpTypeStruct %_ptr_PhysicalStorageBufferEXT_bufStruct %uint
+%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_PhysicalStorageBufferEXT_bufStruct = OpTypePointer PhysicalStorageBufferEXT %bufStruct
+%_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_PhysicalStorageBufferEXT_bufStruct = OpTypePointer Uniform %_ptr_PhysicalStorageBufferEXT_bufStruct
+%_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct = OpTypePointer Uniform %_ptr_PhysicalStorageBuffer_bufStruct
 %int_1 = OpConstant %int 1
 %int_3239 = OpConstant %int 3239
-%_ptr_PhysicalStorageBufferEXT_int = OpTypePointer PhysicalStorageBufferEXT %int
+%_ptr_PhysicalStorageBuffer_int = OpTypePointer PhysicalStorageBuffer %int
 )";
 
   const std::string defs_after =
       R"(OpCapability Shader
-OpCapability PhysicalStorageBufferAddressesEXT
+OpCapability PhysicalStorageBufferAddresses
 OpCapability Int64
 OpExtension "SPV_EXT_physical_storage_buffer"
 OpExtension "SPV_KHR_storage_buffer_storage_class"
 %1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
 OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID
 OpExecutionMode %main LocalSize 1 1 1
 OpSource GLSL 450
@@ -138,21 +138,21 @@
 OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId
 %void = OpTypeVoid
 %8 = OpTypeFunction %void
-OpTypeForwardPointer %_ptr_PhysicalStorageBufferEXT_bufStruct PhysicalStorageBufferEXT
+OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_bufStruct PhysicalStorageBuffer
 %uint = OpTypeInt 32 0
-%ufoo = OpTypeStruct %_ptr_PhysicalStorageBufferEXT_bufStruct %uint
+%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_PhysicalStorageBufferEXT_bufStruct = OpTypePointer PhysicalStorageBufferEXT %bufStruct
+%_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_PhysicalStorageBufferEXT_bufStruct = OpTypePointer Uniform %_ptr_PhysicalStorageBufferEXT_bufStruct
+%_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct = OpTypePointer Uniform %_ptr_PhysicalStorageBuffer_bufStruct
 %int_1 = OpConstant %int 1
 %int_3239 = OpConstant %int 3239
-%_ptr_PhysicalStorageBufferEXT_int = OpTypePointer PhysicalStorageBufferEXT %int
+%_ptr_PhysicalStorageBuffer_int = OpTypePointer PhysicalStorageBuffer %int
 %ulong = OpTypeInt 64 0
 %uint_4 = OpConstant %uint 4
 %bool = OpTypeBool
@@ -188,9 +188,9 @@
   const std::string func_before =
       R"(%main = OpFunction %void None %3
 %5 = OpLabel
-%17 = OpAccessChain %_ptr_Uniform__ptr_PhysicalStorageBufferEXT_bufStruct %u_info %int_0
-%18 = OpLoad %_ptr_PhysicalStorageBufferEXT_bufStruct %17
-%22 = OpAccessChain %_ptr_PhysicalStorageBufferEXT_int %18 %int_1
+%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
 OpStore %22 %int_3239 Aligned 16
 OpReturn
 OpFunctionEnd
@@ -199,9 +199,9 @@
   const std::string func_after =
       R"(%main = OpFunction %void None %8
 %19 = OpLabel
-%20 = OpAccessChain %_ptr_Uniform__ptr_PhysicalStorageBufferEXT_bufStruct %u_info %int_0
-%21 = OpLoad %_ptr_PhysicalStorageBufferEXT_bufStruct %20
-%22 = OpAccessChain %_ptr_PhysicalStorageBufferEXT_int %21 %int_1
+%20 = OpAccessChain %_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct %u_info %int_0
+%21 = OpLoad %_ptr_PhysicalStorageBuffer_bufStruct %20
+%22 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %21 %int_1
 %24 = OpConvertPtrToU %ulong %22
 %61 = OpFunctionCall %bool %26 %24 %uint_4
 OpSelectionMerge %62 None
@@ -339,11 +339,11 @@
 
   const std::string defs_before =
       R"(OpCapability Shader
-OpCapability PhysicalStorageBufferAddressesEXT
+OpCapability PhysicalStorageBufferAddresses
 OpExtension "SPV_EXT_physical_storage_buffer"
 OpExtension "SPV_KHR_storage_buffer_storage_class"
 %1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
 OpEntryPoint GLCompute %main "main"
 OpExecutionMode %main LocalSize 1 1 1
 OpSource GLSL 450
@@ -364,29 +364,29 @@
 OpDecorate %r Binding 0
 %void = OpTypeVoid
 %3 = OpTypeFunction %void
-OpTypeForwardPointer %_ptr_PhysicalStorageBufferEXT_blockType PhysicalStorageBufferEXT
+OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_blockType PhysicalStorageBuffer
 %int = OpTypeInt 32 1
-%blockType = OpTypeStruct %int %_ptr_PhysicalStorageBufferEXT_blockType
-%_ptr_PhysicalStorageBufferEXT_blockType = OpTypePointer PhysicalStorageBufferEXT %blockType
-%rootBlock = OpTypeStruct %_ptr_PhysicalStorageBufferEXT_blockType
+%blockType = OpTypeStruct %int %_ptr_PhysicalStorageBuffer_blockType
+%_ptr_PhysicalStorageBuffer_blockType = OpTypePointer PhysicalStorageBuffer %blockType
+%rootBlock = OpTypeStruct %_ptr_PhysicalStorageBuffer_blockType
 %_ptr_StorageBuffer_rootBlock = OpTypePointer StorageBuffer %rootBlock
 %r = OpVariable %_ptr_StorageBuffer_rootBlock StorageBuffer
 %int_0 = OpConstant %int 0
-%_ptr_StorageBuffer__ptr_PhysicalStorageBufferEXT_blockType = OpTypePointer StorageBuffer %_ptr_PhysicalStorageBufferEXT_blockType
+%_ptr_StorageBuffer__ptr_PhysicalStorageBuffer_blockType = OpTypePointer StorageBuffer %_ptr_PhysicalStorageBuffer_blockType
 %int_1 = OpConstant %int 1
-%_ptr_PhysicalStorageBufferEXT__ptr_PhysicalStorageBufferEXT_blockType = OpTypePointer PhysicalStorageBufferEXT %_ptr_PhysicalStorageBufferEXT_blockType
+%_ptr_PhysicalStorageBuffer__ptr_PhysicalStorageBuffer_blockType = OpTypePointer PhysicalStorageBuffer %_ptr_PhysicalStorageBuffer_blockType
 %int_531 = OpConstant %int 531
-%_ptr_PhysicalStorageBufferEXT_int = OpTypePointer PhysicalStorageBufferEXT %int
+%_ptr_PhysicalStorageBuffer_int = OpTypePointer PhysicalStorageBuffer %int
 )";
 
   const std::string defs_after =
       R"(OpCapability Shader
-OpCapability PhysicalStorageBufferAddressesEXT
+OpCapability PhysicalStorageBufferAddresses
 OpCapability Int64
 OpExtension "SPV_EXT_physical_storage_buffer"
 OpExtension "SPV_KHR_storage_buffer_storage_class"
 %1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
 OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID
 OpExecutionMode %main LocalSize 1 1 1
 OpSource GLSL 450
@@ -419,19 +419,19 @@
 OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId
 %void = OpTypeVoid
 %3 = OpTypeFunction %void
-OpTypeForwardPointer %_ptr_PhysicalStorageBufferEXT_blockType PhysicalStorageBufferEXT
+OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_blockType PhysicalStorageBuffer
 %int = OpTypeInt 32 1
-%blockType = OpTypeStruct %int %_ptr_PhysicalStorageBufferEXT_blockType
-%_ptr_PhysicalStorageBufferEXT_blockType = OpTypePointer PhysicalStorageBufferEXT %blockType
-%rootBlock = OpTypeStruct %_ptr_PhysicalStorageBufferEXT_blockType
+%blockType = OpTypeStruct %int %_ptr_PhysicalStorageBuffer_blockType
+%_ptr_PhysicalStorageBuffer_blockType = OpTypePointer PhysicalStorageBuffer %blockType
+%rootBlock = OpTypeStruct %_ptr_PhysicalStorageBuffer_blockType
 %_ptr_StorageBuffer_rootBlock = OpTypePointer StorageBuffer %rootBlock
 %r = OpVariable %_ptr_StorageBuffer_rootBlock StorageBuffer
 %int_0 = OpConstant %int 0
-%_ptr_StorageBuffer__ptr_PhysicalStorageBufferEXT_blockType = OpTypePointer StorageBuffer %_ptr_PhysicalStorageBufferEXT_blockType
+%_ptr_StorageBuffer__ptr_PhysicalStorageBuffer_blockType = OpTypePointer StorageBuffer %_ptr_PhysicalStorageBuffer_blockType
 %int_1 = OpConstant %int 1
-%_ptr_PhysicalStorageBufferEXT__ptr_PhysicalStorageBufferEXT_blockType = OpTypePointer PhysicalStorageBufferEXT %_ptr_PhysicalStorageBufferEXT_blockType
+%_ptr_PhysicalStorageBuffer__ptr_PhysicalStorageBuffer_blockType = OpTypePointer PhysicalStorageBuffer %_ptr_PhysicalStorageBuffer_blockType
 %int_531 = OpConstant %int 531
-%_ptr_PhysicalStorageBufferEXT_int = OpTypePointer PhysicalStorageBufferEXT %int
+%_ptr_PhysicalStorageBuffer_int = OpTypePointer PhysicalStorageBuffer %int
 %uint = OpTypeInt 32 0
 %uint_2 = OpConstant %uint 2
 %ulong = OpTypeInt 64 0
@@ -464,18 +464,18 @@
 %uint_7 = OpConstant %uint 7
 %uint_9 = OpConstant %uint 9
 %uint_44 = OpConstant %uint 44
-%132 = OpConstantNull %_ptr_PhysicalStorageBufferEXT_blockType
+%132 = OpConstantNull %ulong
 %uint_46 = OpConstant %uint 46
 )";
 
   const std::string func_before =
       R"(%main = OpFunction %void None %3
 %5 = OpLabel
-%16 = OpAccessChain %_ptr_StorageBuffer__ptr_PhysicalStorageBufferEXT_blockType %r %int_0
-%17 = OpLoad %_ptr_PhysicalStorageBufferEXT_blockType %16
-%21 = OpAccessChain %_ptr_PhysicalStorageBufferEXT__ptr_PhysicalStorageBufferEXT_blockType %17 %int_1
-%22 = OpLoad %_ptr_PhysicalStorageBufferEXT_blockType %21 Aligned 8
-%26 = OpAccessChain %_ptr_PhysicalStorageBufferEXT_int %22 %int_0
+%16 = OpAccessChain %_ptr_StorageBuffer__ptr_PhysicalStorageBuffer_blockType %r %int_0
+%17 = OpLoad %_ptr_PhysicalStorageBuffer_blockType %16
+%21 = OpAccessChain %_ptr_PhysicalStorageBuffer__ptr_PhysicalStorageBuffer_blockType %17 %int_1
+%22 = OpLoad %_ptr_PhysicalStorageBuffer_blockType %21 Aligned 8
+%26 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %22 %int_0
 OpStore %26 %int_531 Aligned 16
 OpReturn
 OpFunctionEnd
@@ -484,39 +484,40 @@
   const std::string func_after =
       R"(%main = OpFunction %void None %3
 %5 = OpLabel
-%16 = OpAccessChain %_ptr_StorageBuffer__ptr_PhysicalStorageBufferEXT_blockType %r %int_0
-%17 = OpLoad %_ptr_PhysicalStorageBufferEXT_blockType %16
-%21 = OpAccessChain %_ptr_PhysicalStorageBufferEXT__ptr_PhysicalStorageBufferEXT_blockType %17 %int_1
+%16 = OpAccessChain %_ptr_StorageBuffer__ptr_PhysicalStorageBuffer_blockType %r %int_0
+%17 = OpLoad %_ptr_PhysicalStorageBuffer_blockType %16
+%21 = OpAccessChain %_ptr_PhysicalStorageBuffer__ptr_PhysicalStorageBuffer_blockType %17 %int_1
 %30 = OpConvertPtrToU %ulong %21
 %67 = OpFunctionCall %bool %32 %30 %uint_8
 OpSelectionMerge %68 None
 OpBranchConditional %67 %69 %70
 %69 = OpLabel
-%71 = OpLoad %_ptr_PhysicalStorageBufferEXT_blockType %21 Aligned 8
+%71 = OpLoad %_ptr_PhysicalStorageBuffer_blockType %21 Aligned 8
 OpBranch %68
 %70 = OpLabel
 %72 = OpUConvert %uint %30
 %74 = OpShiftRightLogical %ulong %30 %uint_32
 %75 = OpUConvert %uint %74
 %131 = OpFunctionCall %void %76 %uint_44 %uint_2 %72 %75
+%133 = OpConvertUToPtr %_ptr_PhysicalStorageBuffer_blockType %132
 OpBranch %68
 %68 = OpLabel
-%133 = OpPhi %_ptr_PhysicalStorageBufferEXT_blockType %71 %69 %132 %70
-%26 = OpAccessChain %_ptr_PhysicalStorageBufferEXT_int %133 %int_0
-%134 = OpConvertPtrToU %ulong %26
-%135 = OpFunctionCall %bool %32 %134 %uint_4
-OpSelectionMerge %136 None
-OpBranchConditional %135 %137 %138
-%137 = OpLabel
-OpStore %26 %int_531 Aligned 16
-OpBranch %136
+%134 = OpPhi %_ptr_PhysicalStorageBuffer_blockType %71 %69 %133 %70
+%26 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %134 %int_0
+%135 = OpConvertPtrToU %ulong %26
+%136 = OpFunctionCall %bool %32 %135 %uint_4
+OpSelectionMerge %137 None
+OpBranchConditional %136 %138 %139
 %138 = OpLabel
-%139 = OpUConvert %uint %134
-%140 = OpShiftRightLogical %ulong %134 %uint_32
-%141 = OpUConvert %uint %140
-%143 = OpFunctionCall %void %76 %uint_46 %uint_2 %139 %141
-OpBranch %136
-%136 = OpLabel
+OpStore %26 %int_531 Aligned 16
+OpBranch %137
+%139 = OpLabel
+%140 = OpUConvert %uint %135
+%141 = OpShiftRightLogical %ulong %135 %uint_32
+%142 = OpUConvert %uint %141
+%144 = OpFunctionCall %void %76 %uint_46 %uint_2 %140 %142
+OpBranch %137
+%137 = OpLabel
 OpReturn
 OpFunctionEnd
 )";
diff --git a/test/opt/ir_context_test.cpp b/test/opt/ir_context_test.cpp
index 4e2f5b2..d5710fc 100644
--- a/test/opt/ir_context_test.cpp
+++ b/test/opt/ir_context_test.cpp
@@ -12,6 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "source/opt/ir_context.h"
+
 #include <algorithm>
 #include <memory>
 #include <string>
@@ -19,7 +21,6 @@
 
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
-#include "source/opt/ir_context.h"
 #include "source/opt/pass.h"
 #include "test/opt/pass_fixture.h"
 #include "test/opt/pass_utils.h"
@@ -664,6 +665,90 @@
   EXPECT_EQ(next_id_bound, 0);
   EXPECT_EQ(current_bound, context->module()->id_bound());
 }
+
+TEST_F(IRContextTest, CfgAndDomAnalysis) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%1 = OpTypeVoid
+%2 = OpTypeFunction %1
+%3 = OpFunction %1 None %2
+%4 = OpLabel
+OpReturn
+OpFunctionEnd)";
+
+  std::unique_ptr<IRContext> ctx =
+      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
+                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+  // Building the dominator analysis should build the CFG.
+  ASSERT_TRUE(ctx->module()->begin() != ctx->module()->end());
+  ctx->GetDominatorAnalysis(&*ctx->module()->begin());
+
+  EXPECT_TRUE(ctx->AreAnalysesValid(IRContext::kAnalysisCFG));
+  EXPECT_TRUE(ctx->AreAnalysesValid(IRContext::kAnalysisDominatorAnalysis));
+
+  // Invalidating the CFG analysis should invalidate the dominator analysis.
+  ctx->InvalidateAnalyses(IRContext::kAnalysisCFG);
+  EXPECT_FALSE(ctx->AreAnalysesValid(IRContext::kAnalysisCFG));
+  EXPECT_FALSE(ctx->AreAnalysesValid(IRContext::kAnalysisDominatorAnalysis));
+}
+
+TEST_F(IRContextTest, AsanErrorTest) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %8 "x"
+               OpName %10 "y"
+               OpDecorate %8 RelaxedPrecision
+               OpDecorate %10 RelaxedPrecision
+               OpDecorate %11 RelaxedPrecision
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %10 = OpVariable %7 Function
+               OpStore %8 %9
+         %11 = OpLoad %6 %8
+	       OpBranch %20
+	 %20 = OpLabel
+	 %21 = OpPhi %6 %11 %5
+         OpStore %10 %21
+         OpReturn
+         OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(
+      env, consumer, shader, SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+  opt::Function* fun =
+      context->cfg()->block(5)->GetParent();  // Computes the CFG analysis
+  opt::DominatorAnalysis* dom = nullptr;
+  dom = context->GetDominatorAnalysis(fun);  // Computes the dominator analysis,
+                                             // which depends on the CFG
+                                             // analysis
+  context->InvalidateAnalysesExceptFor(
+      opt::IRContext::Analysis::kAnalysisDominatorAnalysis);  // Invalidates the
+                                                              // CFG analysis
+  dom = context->GetDominatorAnalysis(
+      fun);  // Recompute the CFG analysis because the Dominator tree uses it.
+  auto bb = dom->ImmediateDominator(5);
+  std::cout
+      << bb->id();  // Make sure asan does not complain about use after free.
+}
+
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools
diff --git a/test/opt/legalize_vector_shuffle_test.cpp b/test/opt/legalize_vector_shuffle_test.cpp
index 8b9695b..07d96eb 100644
--- a/test/opt/legalize_vector_shuffle_test.cpp
+++ b/test/opt/legalize_vector_shuffle_test.cpp
@@ -34,9 +34,9 @@
 
 std::vector<const char*> header = {
     "OpCapability Shader",
-    "OpCapability VulkanMemoryModelKHR",
+    "OpCapability VulkanMemoryModel",
     "OpExtension \"SPV_KHR_vulkan_memory_model\"",
-    "OpMemoryModel Logical VulkanKHR",
+    "OpMemoryModel Logical Vulkan",
     "OpEntryPoint Vertex %1 \"shader\"",
     "%uint = OpTypeInt 32 0",
     "%v3uint = OpTypeVector %uint 3"};
diff --git a/test/opt/local_access_chain_convert_test.cpp b/test/opt/local_access_chain_convert_test.cpp
index 1b75b36..39899e3 100644
--- a/test/opt/local_access_chain_convert_test.cpp
+++ b/test/opt/local_access_chain_convert_test.cpp
@@ -820,6 +820,113 @@
   SinglePassRunAndCheck<LocalAccessChainConvertPass>(test, test, false, true);
 }
 
+TEST_F(LocalAccessChainConvertTest, IdOverflowReplacingLoad) {
+  const std::string text =
+      R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "PSMain"
+               OpExecutionMode %4 OriginUpperLeft
+               OpDecorate %10 Location 47360
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+  %_struct_8 = OpTypeStruct %v4float
+%_ptr_Function__struct_8 = OpTypePointer Function %_struct_8
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+          %4 = OpFunction %void None %3
+          %5 = OpLabel
+         %10 = OpVariable %_ptr_Function__struct_8 Function
+    %4194301 = OpAccessChain %_ptr_Function_v4float %10 %int_0
+    %4194302 = OpLoad %v4float %4194301
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+  std::vector<Message> messages = {
+      {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}};
+  SetMessageConsumer(GetTestMessageConsumer(messages));
+  auto result = SinglePassRunToBinary<LocalAccessChainConvertPass>(text, true);
+  EXPECT_EQ(Pass::Status::Failure, std::get<1>(result));
+}
+
+TEST_F(LocalAccessChainConvertTest, IdOverflowReplacingStore1) {
+  const std::string text =
+      R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "PSMain"
+               OpExecutionMode %4 OriginUpperLeft
+               OpDecorate %10 Location 47360
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+  %_struct_7 = OpTypeStruct %v4float
+%_ptr_Function__struct_7 = OpTypePointer Function %_struct_7
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+         %13 = OpConstantNull %v4float
+          %4 = OpFunction %void None %3
+          %5 = OpLabel
+         %10 = OpVariable %_ptr_Function__struct_7 Function
+    %4194302 = OpAccessChain %_ptr_Function_v4float %10 %int_0
+               OpStore %4194302 %13
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+  std::vector<Message> messages = {
+      {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}};
+  SetMessageConsumer(GetTestMessageConsumer(messages));
+  auto result = SinglePassRunToBinary<LocalAccessChainConvertPass>(text, true);
+  EXPECT_EQ(Pass::Status::Failure, std::get<1>(result));
+}
+
+TEST_F(LocalAccessChainConvertTest, IdOverflowReplacingStore2) {
+  const std::string text =
+      R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "PSMain"
+               OpExecutionMode %4 OriginUpperLeft
+               OpDecorate %10 Location 47360
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+  %_struct_7 = OpTypeStruct %v4float
+%_ptr_Function__struct_7 = OpTypePointer Function %_struct_7
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+         %13 = OpConstantNull %v4float
+          %4 = OpFunction %void None %3
+          %5 = OpLabel
+         %10 = OpVariable %_ptr_Function__struct_7 Function
+    %4194301 = OpAccessChain %_ptr_Function_v4float %10 %int_0
+               OpStore %4194301 %13
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+  std::vector<Message> messages = {
+      {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}};
+  SetMessageConsumer(GetTestMessageConsumer(messages));
+  auto result = SinglePassRunToBinary<LocalAccessChainConvertPass>(text, true);
+  EXPECT_EQ(Pass::Status::Failure, std::get<1>(result));
+}
+
 // TODO(greg-lunarg): Add tests to verify handling of these cases:
 //
 //    Assorted vector and matrix types
diff --git a/test/opt/local_ssa_elim_test.cpp b/test/opt/local_ssa_elim_test.cpp
index a490d52..f10d118 100644
--- a/test/opt/local_ssa_elim_test.cpp
+++ b/test/opt/local_ssa_elim_test.cpp
@@ -138,8 +138,8 @@
 OpFunctionEnd
 )";
 
-  SinglePassRunAndCheck<LocalMultiStoreElimPass>(predefs + before,
-                                                 predefs + after, true, true);
+  SinglePassRunAndCheck<SSARewritePass>(predefs + before, predefs + after, true,
+                                        true);
 }
 
 TEST_F(LocalSSAElimTest, NestedForLoop) {
@@ -280,8 +280,8 @@
 OpFunctionEnd
 )";
 
-  SinglePassRunAndCheck<LocalMultiStoreElimPass>(predefs + before,
-                                                 predefs + after, true, true);
+  SinglePassRunAndCheck<SSARewritePass>(predefs + before, predefs + after, true,
+                                        true);
 }
 
 TEST_F(LocalSSAElimTest, ForLoopWithContinue) {
@@ -426,9 +426,9 @@
 OpFunctionEnd
 )";
 
-  SinglePassRunAndCheck<LocalMultiStoreElimPass>(
-      predefs + names + predefs2 + before, predefs + names + predefs2 + after,
-      true, true);
+  SinglePassRunAndCheck<SSARewritePass>(predefs + names + predefs2 + before,
+                                        predefs + names + predefs2 + after,
+                                        true, true);
 }
 
 TEST_F(LocalSSAElimTest, ForLoopWithBreak) {
@@ -567,8 +567,8 @@
 OpFunctionEnd
 )";
 
-  SinglePassRunAndCheck<LocalMultiStoreElimPass>(predefs + before,
-                                                 predefs + after, true, true);
+  SinglePassRunAndCheck<SSARewritePass>(predefs + before, predefs + after, true,
+                                        true);
 }
 
 TEST_F(LocalSSAElimTest, SwapProblem) {
@@ -704,8 +704,8 @@
 OpFunctionEnd
 )";
 
-  SinglePassRunAndCheck<LocalMultiStoreElimPass>(predefs + before,
-                                                 predefs + after, true, true);
+  SinglePassRunAndCheck<SSARewritePass>(predefs + before, predefs + after, true,
+                                        true);
 }
 
 TEST_F(LocalSSAElimTest, LostCopyProblem) {
@@ -848,8 +848,8 @@
 OpFunctionEnd
 )";
 
-  SinglePassRunAndCheck<LocalMultiStoreElimPass>(predefs + before,
-                                                 predefs + after, true, true);
+  SinglePassRunAndCheck<SSARewritePass>(predefs + before, predefs + after, true,
+                                        true);
 }
 
 TEST_F(LocalSSAElimTest, IfThenElse) {
@@ -948,8 +948,8 @@
 OpFunctionEnd
 )";
 
-  SinglePassRunAndCheck<LocalMultiStoreElimPass>(predefs + before,
-                                                 predefs + after, true, true);
+  SinglePassRunAndCheck<SSARewritePass>(predefs + before, predefs + after, true,
+                                        true);
 }
 
 TEST_F(LocalSSAElimTest, IfThen) {
@@ -1037,8 +1037,8 @@
 OpFunctionEnd
 )";
 
-  SinglePassRunAndCheck<LocalMultiStoreElimPass>(predefs + before,
-                                                 predefs + after, true, true);
+  SinglePassRunAndCheck<SSARewritePass>(predefs + before, predefs + after, true,
+                                        true);
 }
 
 TEST_F(LocalSSAElimTest, Switch) {
@@ -1168,8 +1168,8 @@
 OpFunctionEnd
 )";
 
-  SinglePassRunAndCheck<LocalMultiStoreElimPass>(predefs + before,
-                                                 predefs + after, true, true);
+  SinglePassRunAndCheck<SSARewritePass>(predefs + before, predefs + after, true,
+                                        true);
 }
 
 TEST_F(LocalSSAElimTest, SwitchWithFallThrough) {
@@ -1300,8 +1300,8 @@
 OpFunctionEnd
 )";
 
-  SinglePassRunAndCheck<LocalMultiStoreElimPass>(predefs + before,
-                                                 predefs + after, true, true);
+  SinglePassRunAndCheck<SSARewritePass>(predefs + before, predefs + after, true,
+                                        true);
 }
 
 TEST_F(LocalSSAElimTest, DontPatchPhiInLoopHeaderThatIsNotAVar) {
@@ -1331,7 +1331,7 @@
 OpFunctionEnd
 )";
 
-  SinglePassRunAndCheck<LocalMultiStoreElimPass>(before, before, true, true);
+  SinglePassRunAndCheck<SSARewritePass>(before, before, true, true);
 }
 
 TEST_F(LocalSSAElimTest, OptInitializedVariableLikeStore) {
@@ -1428,8 +1428,8 @@
 OpFunctionEnd
 )";
 
-  SinglePassRunAndCheck<LocalMultiStoreElimPass>(
-      predefs + func_before, predefs + func_after, true, true);
+  SinglePassRunAndCheck<SSARewritePass>(predefs + func_before,
+                                        predefs + func_after, true, true);
 }
 
 TEST_F(LocalSSAElimTest, PointerVariable) {
@@ -1531,7 +1531,7 @@
   // Relax logical pointers to allow pointer allocations.
   SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   ValidatorOptions()->relax_logical_pointer = true;
-  SinglePassRunAndCheck<LocalMultiStoreElimPass>(before, after, true, true);
+  SinglePassRunAndCheck<SSARewritePass>(before, after, true, true);
 }
 
 TEST_F(LocalSSAElimTest, VerifyInstToBlockMap) {
@@ -1620,7 +1620,7 @@
   // Force the instruction to block mapping to get built.
   context->get_instr_block(27u);
 
-  auto pass = MakeUnique<LocalMultiStoreElimPass>();
+  auto pass = MakeUnique<SSARewritePass>();
   pass->SetMessageConsumer(nullptr);
   const auto status = pass->Run(context.get());
   EXPECT_TRUE(status == Pass::Status::SuccessWithChange);
@@ -1927,7 +1927,7 @@
                OpReturn
                OpFunctionEnd
   )";
-  SinglePassRunAndMatch<LocalMultiStoreElimPass>(text, false);
+  SinglePassRunAndMatch<SSARewritePass>(text, false);
 }
 
 TEST_F(LocalSSAElimTest, ChainedTrivialPhis) {
@@ -1987,6 +1987,41 @@
   SinglePassRunAndMatch<SSARewritePass>(text, false);
 }
 
+TEST_F(LocalSSAElimTest, Overflowtest1) {
+  // Check that the copy object get the undef value implicitly assigned in the
+  // entry block.
+  const std::string text = R"(
+OpCapability Geometry
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %4 "P2Mai" %12 %17
+OpExecutionMode %4 OriginUpperLeft
+%2 = OpTypeVoid
+%3 = OpTypeFunction %2
+%6 = OpTypeFloat 32
+%7 = OpTypeVector %6 4
+%11 = OpTypePointer Input %7
+%16 = OpTypePointer Output %7
+%23 = OpTypePointer Function %7
+%12 = OpVariable %11 Input
+%17 = OpVariable %16 Output
+%4 = OpFunction %2 None %3
+%2177 = OpLabel
+%4194302 = OpVariable %23 Function
+%4194301 = OpLoad %7 %4194302
+OpStore %17 %4194301
+OpReturn
+OpFunctionEnd
+  )";
+
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+  std::vector<Message> messages = {
+      {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}};
+  SetMessageConsumer(GetTestMessageConsumer(messages));
+  auto result = SinglePassRunToBinary<SSARewritePass>(text, true);
+  EXPECT_EQ(Pass::Status::Failure, std::get<1>(result));
+}
+
 // TODO(greg-lunarg): Add tests to verify handling of these cases:
 //
 //    No optimization in the presence of
diff --git a/test/opt/module_test.cpp b/test/opt/module_test.cpp
index 569cf9b..406da09 100644
--- a/test/opt/module_test.cpp
+++ b/test/opt/module_test.cpp
@@ -21,6 +21,7 @@
 #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"
 
@@ -228,6 +229,72 @@
   EXPECT_EQ(next_id_bound, 0);
   EXPECT_EQ(current_bound, context->module()->id_bound());
 }
+
+// Tests that "text" does not change when it is assembled, converted into a
+// module, converted back to a binary, and then disassembled.
+void AssembleAndDisassemble(const std::string& text) {
+  std::unique_ptr<IRContext> context = BuildModule(text);
+  std::vector<uint32_t> binary;
+
+  context->module()->ToBinary(&binary, false);
+
+  SpirvTools tools(SPV_ENV_UNIVERSAL_1_1);
+  std::string s;
+  tools.Disassemble(binary, &s);
+  EXPECT_EQ(s, text);
+}
+
+TEST(ModuleTest, TrailingOpLine) {
+  const std::string text = R"(OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%5 = OpString "file.ext"
+%void = OpTypeVoid
+%2 = OpTypeFunction %void
+%3 = OpFunction %void None %2
+%4 = OpLabel
+OpReturn
+OpFunctionEnd
+OpLine %5 1 0
+)";
+
+  AssembleAndDisassemble(text);
+}
+
+TEST(ModuleTest, TrailingOpNoLine) {
+  const std::string text = R"(OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%void = OpTypeVoid
+%2 = OpTypeFunction %void
+%3 = OpFunction %void None %2
+%4 = OpLabel
+OpReturn
+OpFunctionEnd
+OpNoLine
+)";
+
+  AssembleAndDisassemble(text);
+}
+
+TEST(ModuleTest, MulitpleTrailingOpLine) {
+  const std::string text = R"(OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%5 = OpString "file.ext"
+%void = OpTypeVoid
+%2 = OpTypeFunction %void
+%3 = OpFunction %void None %2
+%4 = OpLabel
+OpReturn
+OpFunctionEnd
+OpLine %5 1 0
+OpNoLine
+OpLine %5 1 1
+)";
+
+  AssembleAndDisassemble(text);
+}
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools
diff --git a/test/opt/optimizer_test.cpp b/test/opt/optimizer_test.cpp
index ee6e949..5c0707d 100644
--- a/test/opt/optimizer_test.cpp
+++ b/test/opt/optimizer_test.cpp
@@ -222,7 +222,7 @@
 }
 
 TEST(Optimizer, VulkanToWebGPUSetsCorrectPasses) {
-  Optimizer opt(SPV_ENV_WEBGPU_0);
+  Optimizer opt(SPV_ENV_VULKAN_1_1);
   opt.RegisterVulkanToWebGPUPasses();
   std::vector<const char*> pass_names = opt.GetPassNames();
 
@@ -267,13 +267,14 @@
     tools.Assemble(GetParam().input, &binary);
   }
 
-  Optimizer opt(SPV_ENV_WEBGPU_0);
+  Optimizer opt(SPV_ENV_VULKAN_1_1);
   opt.RegisterVulkanToWebGPUPasses();
 
   std::vector<uint32_t> optimized;
   class ValidatorOptions validator_options;
   ASSERT_TRUE(opt.Run(binary.data(), binary.size(), &optimized,
-                      validator_options, true));
+                      validator_options, true))
+      << GetParam().input << "\n";
   std::string disassembly;
   {
     SpirvTools tools(SPV_ENV_WEBGPU_0);
@@ -290,9 +291,9 @@
         // FlattenDecorations
         {// input
          "OpCapability Shader\n"
-         "OpCapability VulkanMemoryModelKHR\n"
+         "OpCapability VulkanMemoryModel\n"
          "OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
-         "OpMemoryModel Logical VulkanKHR\n"
+         "OpMemoryModel Logical Vulkan\n"
          "OpEntryPoint Fragment %main \"main\" %hue %saturation %value\n"
          "OpExecutionMode %main OriginUpperLeft\n"
          "OpDecorate %group Flat\n"
@@ -311,9 +312,9 @@
          "OpFunctionEnd\n",
          // expected
          "OpCapability Shader\n"
-         "OpCapability VulkanMemoryModelKHR\n"
+         "OpCapability VulkanMemoryModel\n"
          "OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
-         "OpMemoryModel Logical VulkanKHR\n"
+         "OpMemoryModel Logical Vulkan\n"
          "OpEntryPoint Fragment %1 \"main\" %2 %3 %4\n"
          "OpExecutionMode %1 OriginUpperLeft\n"
          "%void = OpTypeVoid\n"
@@ -332,9 +333,9 @@
         // Strip Debug
         {// input
          "OpCapability Shader\n"
-         "OpCapability VulkanMemoryModelKHR\n"
+         "OpCapability VulkanMemoryModel\n"
          "OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
-         "OpMemoryModel Logical VulkanKHR\n"
+         "OpMemoryModel Logical Vulkan\n"
          "OpEntryPoint Vertex %func \"shader\"\n"
          "OpName %main \"main\"\n"
          "OpName %void_fn \"void_fn\"\n"
@@ -346,9 +347,9 @@
          "OpFunctionEnd\n",
          // expected
          "OpCapability Shader\n"
-         "OpCapability VulkanMemoryModelKHR\n"
+         "OpCapability VulkanMemoryModel\n"
          "OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
-         "OpMemoryModel Logical VulkanKHR\n"
+         "OpMemoryModel Logical Vulkan\n"
          "OpEntryPoint Vertex %1 \"shader\"\n"
          "%void = OpTypeVoid\n"
          "%3 = OpTypeFunction %void\n"
@@ -361,9 +362,9 @@
         // Eliminate Dead Constants
         {// input
          "OpCapability Shader\n"
-         "OpCapability VulkanMemoryModelKHR\n"
+         "OpCapability VulkanMemoryModel\n"
          "OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
-         "OpMemoryModel Logical VulkanKHR\n"
+         "OpMemoryModel Logical Vulkan\n"
          "OpEntryPoint Vertex %func \"shader\"\n"
          "%u32 = OpTypeInt 32 0\n"
          "%u32_ptr = OpTypePointer Workgroup %u32\n"
@@ -380,9 +381,9 @@
          "OpFunctionEnd\n",
          // expected
          "OpCapability Shader\n"
-         "OpCapability VulkanMemoryModelKHR\n"
+         "OpCapability VulkanMemoryModel\n"
          "OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
-         "OpMemoryModel Logical VulkanKHR\n"
+         "OpMemoryModel Logical Vulkan\n"
          "OpEntryPoint Vertex %1 \"shader\"\n"
          "%uint = OpTypeInt 32 0\n"
          "%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint\n"
@@ -397,9 +398,9 @@
         // Strip Atomic Counter Memory
         {// input
          "OpCapability Shader\n"
-         "OpCapability VulkanMemoryModelKHR\n"
+         "OpCapability VulkanMemoryModel\n"
          "OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
-         "OpMemoryModel Logical VulkanKHR\n"
+         "OpMemoryModel Logical Vulkan\n"
          "OpEntryPoint Vertex %func \"shader\"\n"
          "%u32 = OpTypeInt 32 0\n"
          "%u32_ptr = OpTypePointer Workgroup %u32\n"
@@ -412,7 +413,7 @@
          "%void_f = OpTypeFunction %void\n"
          "%func = OpFunction %void None %void_f\n"
          "%label = OpLabel\n"
-         "%val0 = OpAtomicStore %u32_var %cross_device "
+         "        OpAtomicStore %u32_var %cross_device "
          "%acquire_release_atomic_counter_workgroup %u32_1\n"
          "%val1 = OpAtomicIIncrement %u32 %u32_var %cross_device "
          "%acquire_release_atomic_counter_workgroup\n"
@@ -423,9 +424,9 @@
          "OpFunctionEnd\n",
          // expected
          "OpCapability Shader\n"
-         "OpCapability VulkanMemoryModelKHR\n"
+         "OpCapability VulkanMemoryModel\n"
          "OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
-         "OpMemoryModel Logical VulkanKHR\n"
+         "OpMemoryModel Logical Vulkan\n"
          "OpEntryPoint Vertex %1 \"shader\"\n"
          "%uint = OpTypeInt 32 0\n"
          "%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint\n"
@@ -449,9 +450,9 @@
         // Generate WebGPU Initializers
         {// input
          "OpCapability Shader\n"
-         "OpCapability VulkanMemoryModelKHR\n"
+         "OpCapability VulkanMemoryModel\n"
          "OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
-         "OpMemoryModel Logical VulkanKHR\n"
+         "OpMemoryModel Logical Vulkan\n"
          "OpEntryPoint Vertex %func \"shader\"\n"
          "%u32 = OpTypeInt 32 0\n"
          "%u32_ptr = OpTypePointer Private %u32\n"
@@ -466,9 +467,9 @@
          "OpFunctionEnd\n",
          // expected
          "OpCapability Shader\n"
-         "OpCapability VulkanMemoryModelKHR\n"
+         "OpCapability VulkanMemoryModel\n"
          "OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
-         "OpMemoryModel Logical VulkanKHR\n"
+         "OpMemoryModel Logical Vulkan\n"
          "OpEntryPoint Vertex %1 \"shader\"\n"
          "%uint = OpTypeInt 32 0\n"
          "%_ptr_Private_uint = OpTypePointer Private %uint\n"
@@ -487,9 +488,9 @@
         // Legalize Vector Shuffle
         {// input
          "OpCapability Shader\n"
-         "OpCapability VulkanMemoryModelKHR\n"
+         "OpCapability VulkanMemoryModel\n"
          "OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
-         "OpMemoryModel Logical VulkanKHR\n"
+         "OpMemoryModel Logical Vulkan\n"
          "OpEntryPoint Vertex %1 \"shader\"\n"
          "%uint = OpTypeInt 32 0\n"
          "%v3uint = OpTypeVector %uint 3\n"
@@ -506,9 +507,9 @@
          "OpFunctionEnd\n",
          // expected
          "OpCapability Shader\n"
-         "OpCapability VulkanMemoryModelKHR\n"
+         "OpCapability VulkanMemoryModel\n"
          "OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
-         "OpMemoryModel Logical VulkanKHR\n"
+         "OpMemoryModel Logical Vulkan\n"
          "OpEntryPoint Vertex %1 \"shader\"\n"
          "%uint = OpTypeInt 32 0\n"
          "%v3uint = OpTypeVector %uint 3\n"
@@ -529,9 +530,9 @@
         // Split Invalid Unreachable
         {// input
          "OpCapability Shader\n"
-         "OpCapability VulkanMemoryModelKHR\n"
+         "OpCapability VulkanMemoryModel\n"
          "OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
-         "OpMemoryModel Logical VulkanKHR\n"
+         "OpMemoryModel Logical Vulkan\n"
          "OpEntryPoint Vertex %1 \"shader\"\n"
          "%uint = OpTypeInt 32 0\n"
          "%uint_1 = OpConstant %uint 1\n"
@@ -560,9 +561,9 @@
          "OpFunctionEnd\n",
          // expected
          "OpCapability Shader\n"
-         "OpCapability VulkanMemoryModelKHR\n"
+         "OpCapability VulkanMemoryModel\n"
          "OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
-         "OpMemoryModel Logical VulkanKHR\n"
+         "OpMemoryModel Logical Vulkan\n"
          "OpEntryPoint Vertex %1 \"shader\"\n"
          "%uint = OpTypeInt 32 0\n"
          "%uint_1 = OpConstant %uint 1\n"
@@ -596,9 +597,9 @@
         // Compact IDs
         {// input
          "OpCapability Shader\n"
-         "OpCapability VulkanMemoryModelKHR\n"
+         "OpCapability VulkanMemoryModel\n"
          "OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
-         "OpMemoryModel Logical VulkanKHR\n"
+         "OpMemoryModel Logical Vulkan\n"
          "OpEntryPoint Vertex %1000 \"shader\"\n"
          "%10 = OpTypeVoid\n"
          "%100 = OpTypeFunction %10\n"
@@ -608,9 +609,9 @@
          "OpFunctionEnd\n",
          // expected
          "OpCapability Shader\n"
-         "OpCapability VulkanMemoryModelKHR\n"
+         "OpCapability VulkanMemoryModel\n"
          "OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
-         "OpMemoryModel Logical VulkanKHR\n"
+         "OpMemoryModel Logical Vulkan\n"
          "OpEntryPoint Vertex %1 \"shader\"\n"
          "%void = OpTypeVoid\n"
          "%3 = OpTypeFunction %void\n"
@@ -622,7 +623,7 @@
          "compact-ids"}}));
 
 TEST(Optimizer, WebGPUToVulkanSetsCorrectPasses) {
-  Optimizer opt(SPV_ENV_VULKAN_1_1);
+  Optimizer opt(SPV_ENV_WEBGPU_0);
   opt.RegisterWebGPUToVulkanPasses();
   std::vector<const char*> pass_names = opt.GetPassNames();
 
@@ -659,7 +660,7 @@
     tools.Assemble(GetParam().input, &binary);
   }
 
-  Optimizer opt(SPV_ENV_VULKAN_1_1);
+  Optimizer opt(SPV_ENV_WEBGPU_0);
   opt.RegisterWebGPUToVulkanPasses();
 
   std::vector<uint32_t> optimized;
@@ -682,9 +683,9 @@
         // Decompose Initialized Variables
         {// input
          "OpCapability Shader\n"
-         "OpCapability VulkanMemoryModelKHR\n"
+         "OpCapability VulkanMemoryModel\n"
          "OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
-         "OpMemoryModel Logical VulkanKHR\n"
+         "OpMemoryModel Logical Vulkan\n"
          "OpEntryPoint Vertex %1 \"shader\"\n"
          "%uint = OpTypeInt 32 0\n"
          "%_ptr_Function_uint = OpTypePointer Function %uint\n"
@@ -698,9 +699,9 @@
          "OpFunctionEnd\n",
          // expected
          "OpCapability Shader\n"
-         "OpCapability VulkanMemoryModelKHR\n"
+         "OpCapability VulkanMemoryModel\n"
          "OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
-         "OpMemoryModel Logical VulkanKHR\n"
+         "OpMemoryModel Logical Vulkan\n"
          "OpEntryPoint Vertex %1 \"shader\"\n"
          "%uint = OpTypeInt 32 0\n"
          "%_ptr_Function_uint = OpTypePointer Function %uint\n"
@@ -718,9 +719,9 @@
         // Compact IDs
         {// input
          "OpCapability Shader\n"
-         "OpCapability VulkanMemoryModelKHR\n"
+         "OpCapability VulkanMemoryModel\n"
          "OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
-         "OpMemoryModel Logical VulkanKHR\n"
+         "OpMemoryModel Logical Vulkan\n"
          "OpEntryPoint Vertex %1000 \"shader\"\n"
          "%10 = OpTypeVoid\n"
          "%100 = OpTypeFunction %10\n"
@@ -730,9 +731,9 @@
          "OpFunctionEnd\n",
          // expected
          "OpCapability Shader\n"
-         "OpCapability VulkanMemoryModelKHR\n"
+         "OpCapability VulkanMemoryModel\n"
          "OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
-         "OpMemoryModel Logical VulkanKHR\n"
+         "OpMemoryModel Logical Vulkan\n"
          "OpEntryPoint Vertex %1 \"shader\"\n"
          "%void = OpTypeVoid\n"
          "%3 = OpTypeFunction %void\n"
@@ -743,6 +744,54 @@
          // pass
          "compact-ids"}}));
 
+TEST(Optimizer, RemoveNop) {
+  // Test that OpNops are removed even if no optimizations are run.
+  const std::string before = R"(OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%void = OpTypeVoid
+%2 = OpTypeFunction %void
+%3 = OpFunction %void None %2
+%4 = OpLabel
+OpNop
+OpReturn
+OpFunctionEnd
+)";
+
+  const std::string after = R"(OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%void = OpTypeVoid
+%2 = OpTypeFunction %void
+%3 = OpFunction %void None %2
+%4 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  std::vector<uint32_t> binary;
+  {
+    SpirvTools tools(SPV_ENV_VULKAN_1_1);
+    tools.Assemble(before, &binary);
+  }
+
+  Optimizer opt(SPV_ENV_VULKAN_1_1);
+
+  std::vector<uint32_t> optimized;
+  class ValidatorOptions validator_options;
+  ASSERT_TRUE(opt.Run(binary.data(), binary.size(), &optimized,
+                      validator_options, true))
+      << before << "\n";
+  std::string disassembly;
+  {
+    SpirvTools tools(SPV_ENV_WEBGPU_0);
+    tools.Disassemble(optimized.data(), optimized.size(), &disassembly);
+  }
+
+  EXPECT_EQ(after, disassembly)
+      << "Was expecting the OpNop to have been removed.";
+}
+
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools
diff --git a/test/opt/reduce_load_size_test.cpp b/test/opt/reduce_load_size_test.cpp
index 7b850e3..50dc501 100644
--- a/test/opt/reduce_load_size_test.cpp
+++ b/test/opt/reduce_load_size_test.cpp
@@ -321,34 +321,6 @@
   SinglePassRunAndCheck<ReduceLoadSize>(test, test, true, false);
 }
 
-TEST_F(ReduceLoadSizeTest, extract_with_no_index) {
-  const std::string test =
-      R"(
-               OpCapability ImageGatherExtended
-               OpExtension ""
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %4 "P�Ma'" %12 %17
-               OpExecutionMode %4 OriginUpperLeft
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-  %_struct_7 = OpTypeStruct %float %float
-%_ptr_Input__struct_7 = OpTypePointer Input %_struct_7
-%_ptr_Output__struct_7 = OpTypePointer Output %_struct_7
-         %12 = OpVariable %_ptr_Input__struct_7 Input
-         %17 = OpVariable %_ptr_Output__struct_7 Output
-          %4 = OpFunction %void DontInline|Pure|Const %3
-        %245 = OpLabel
-         %13 = OpLoad %_struct_7 %12
-         %33 = OpCompositeExtract %_struct_7 %13
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  auto result = SinglePassRunAndDisassemble<ReduceLoadSize>(test, true, true);
-  EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result));
-}
-
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools
diff --git a/test/opt/relax_float_ops_test.cpp b/test/opt/relax_float_ops_test.cpp
new file mode 100644
index 0000000..14cde0b
--- /dev/null
+++ b/test/opt/relax_float_ops_test.cpp
@@ -0,0 +1,142 @@
+// Copyright (c) 2019 Valve Corporation
+// Copyright (c) 2019 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.
+
+// Relax float ops tests
+
+#include <string>
+#include <vector>
+
+#include "test/opt/assembly_builder.h"
+#include "test/opt/pass_fixture.h"
+#include "test/opt/pass_utils.h"
+
+namespace spvtools {
+namespace opt {
+namespace {
+
+using RelaxFloatOpsTest = PassTest<::testing::Test>;
+
+TEST_F(RelaxFloatOpsTest, RelaxFloatOpsBasic) {
+  // All float result instructions in functions should be relaxed
+  // clang-format off
+  //
+  // SamplerState       g_sSamp : register(s0);
+  // uniform Texture1D <float4> g_tTex1df4 : register(t0);
+  //
+  // struct PS_INPUT
+  // {
+  //   float Tex0 : TEXCOORD0;
+  //   float Tex1 : TEXCOORD1;
+  // };
+  //
+  // struct PS_OUTPUT
+  // {
+  //   float4 Color : SV_Target0;
+  // };
+  //
+  // PS_OUTPUT main(PS_INPUT i)
+  // {
+  //   PS_OUTPUT psout;
+  //   float4 txval10 = g_tTex1df4.Sample(g_sSamp, i.Tex0);
+  //   float4 txval11 = g_tTex1df4.Sample(g_sSamp, i.Tex1);
+  //   float4 t = txval10 + txval11;
+  //   float4 t2 = t / 2.0;
+  //   psout.Color = t2;
+  //   return psout;
+  // }
+  // clang-format on
+
+  const std::string defs0 =
+      R"(OpCapability Shader
+OpCapability Sampled1D
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %i_Tex0 %i_Tex1 %_entryPointOutput_Color
+OpExecutionMode %main OriginUpperLeft
+OpSource HLSL 500
+OpName %main "main"
+OpName %g_tTex1df4 "g_tTex1df4"
+OpName %g_sSamp "g_sSamp"
+OpName %i_Tex0 "i.Tex0"
+OpName %i_Tex1 "i.Tex1"
+OpName %_entryPointOutput_Color "@entryPointOutput.Color"
+OpDecorate %g_tTex1df4 DescriptorSet 0
+OpDecorate %g_tTex1df4 Binding 0
+OpDecorate %g_sSamp DescriptorSet 0
+OpDecorate %g_sSamp Binding 0
+OpDecorate %i_Tex0 Location 0
+OpDecorate %i_Tex1 Location 1
+OpDecorate %_entryPointOutput_Color Location 0
+)";
+
+  const std::string defs1 =
+      R"(%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%17 = OpTypeImage %float 1D 0 0 0 1 Unknown
+%_ptr_UniformConstant_17 = OpTypePointer UniformConstant %17
+%g_tTex1df4 = OpVariable %_ptr_UniformConstant_17 UniformConstant
+%21 = OpTypeSampler
+%_ptr_UniformConstant_21 = OpTypePointer UniformConstant %21
+%g_sSamp = OpVariable %_ptr_UniformConstant_21 UniformConstant
+%25 = OpTypeSampledImage %17
+%_ptr_Input_float = OpTypePointer Input %float
+%i_Tex0 = OpVariable %_ptr_Input_float Input
+%i_Tex1 = OpVariable %_ptr_Input_float Input
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%_entryPointOutput_Color = OpVariable %_ptr_Output_v4float Output
+%float_0_5 = OpConstant %float 0.5
+%116 = OpConstantComposite %v4float %float_0_5 %float_0_5 %float_0_5 %float_0_5
+)";
+
+  const std::string relax_decos =
+      R"(OpDecorate %60 RelaxedPrecision
+OpDecorate %63 RelaxedPrecision
+OpDecorate %82 RelaxedPrecision
+OpDecorate %88 RelaxedPrecision
+OpDecorate %91 RelaxedPrecision
+OpDecorate %94 RelaxedPrecision
+)";
+
+  const std::string func_orig =
+      R"(%main = OpFunction %void None %3
+%5 = OpLabel
+%60 = OpLoad %float %i_Tex0
+%63 = OpLoad %float %i_Tex1
+%77 = OpLoad %17 %g_tTex1df4
+%78 = OpLoad %21 %g_sSamp
+%79 = OpSampledImage %25 %77 %78
+%82 = OpImageSampleImplicitLod %v4float %79 %60
+%83 = OpLoad %17 %g_tTex1df4
+%84 = OpLoad %21 %g_sSamp
+%85 = OpSampledImage %25 %83 %84
+%88 = OpImageSampleImplicitLod %v4float %85 %63
+%91 = OpFAdd %v4float %82 %88
+%94 = OpFMul %v4float %91 %116
+OpStore %_entryPointOutput_Color %94
+OpReturn
+OpFunctionEnd
+)";
+
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndCheck<RelaxFloatOpsPass>(
+      defs0 + defs1 + func_orig, defs0 + relax_decos + defs1 + func_orig, true,
+      true);
+}
+
+}  // namespace
+}  // namespace opt
+}  // namespace spvtools
diff --git a/test/opt/simplification_test.cpp b/test/opt/simplification_test.cpp
index 1420498..7a9696e 100644
--- a/test/opt/simplification_test.cpp
+++ b/test/opt/simplification_test.cpp
@@ -71,6 +71,41 @@
   SinglePassRunAndMatch<SimplificationPass>(text, false);
 }
 
+TEST_F(SimplificationTest, NewInstructionTest) {
+  // Testing that new instructions are simplified. Specifically,
+  // that the new add instruction generated by FactorAddMul is
+  // further simplified by MergeGenericAddSub.
+  const std::string text = R"(OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpSource GLSL 430
+               OpName %main "main"
+       %void = OpTypeVoid
+          %4 = OpTypeFunction %void
+        %int = OpTypeInt 32 1
+   %_ptr_int = OpTypePointer Function %int
+; CHECK: [[mul:%[a-zA-Z_\d]+]] = OpIMul %int %13 %11
+       %main = OpFunction %void None %4
+          %7 = OpLabel
+          %8 = OpVariable %_ptr_int Function
+          %9 = OpVariable %_ptr_int Function
+         %10 = OpVariable %_ptr_int Function
+         %11 = OpLoad %int %8
+         %12 = OpLoad %int %9
+         %13 = OpLoad %int %10
+         %14 = OpISub %int %11 %12
+         %15 = OpIMul %int %13 %11
+         %16 = OpIMul %int %13 %12
+         %17 = OpIAdd %int %14 %15
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<SimplificationPass>(text, false);
+}
+
 TEST_F(SimplificationTest, AcrossBasicBlocks) {
   // Testing that folding rules are combined across basic blocks.
   const std::string text = R"(OpCapability Shader
@@ -206,7 +241,7 @@
   // Don't simplify OpCopyObject if the result id has a decoration that the
   // operand does not.
   const std::string text = R"(OpCapability Shader
-OpCapability ShaderNonUniformEXT
+OpCapability ShaderNonUniform
 %1 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel Logical GLSL450
 OpEntryPoint Fragment %2 "main"
@@ -214,7 +249,7 @@
 OpSource GLSL 430
 OpSourceExtension "GL_GOOGLE_cpp_style_line_directive"
 OpSourceExtension "GL_GOOGLE_include_directive"
-OpDecorate %3 NonUniformEXT
+OpDecorate %3 NonUniform
 %void = OpTypeVoid
 %5 = OpTypeFunction %void
 %int = OpTypeInt 32 1
@@ -234,7 +269,7 @@
   // Simplify OpCopyObject if the result id is a subset of the decorations of
   // the operand.
   const std::string before = R"(OpCapability Shader
-OpCapability ShaderNonUniformEXT
+OpCapability ShaderNonUniform
 %1 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel Logical GLSL450
 OpEntryPoint Fragment %2 "main"
@@ -242,7 +277,7 @@
 OpSource GLSL 430
 OpSourceExtension "GL_GOOGLE_cpp_style_line_directive"
 OpSourceExtension "GL_GOOGLE_include_directive"
-OpDecorate %3 NonUniformEXT
+OpDecorate %3 NonUniform
 %void = OpTypeVoid
 %5 = OpTypeFunction %void
 %int = OpTypeInt 32 1
@@ -256,7 +291,7 @@
 )";
 
   const std::string after = R"(OpCapability Shader
-OpCapability ShaderNonUniformEXT
+OpCapability ShaderNonUniform
 %1 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel Logical GLSL450
 OpEntryPoint Fragment %2 "main"
@@ -264,7 +299,7 @@
 OpSource GLSL 430
 OpSourceExtension "GL_GOOGLE_cpp_style_line_directive"
 OpSourceExtension "GL_GOOGLE_include_directive"
-OpDecorate %3 NonUniformEXT
+OpDecorate %3 NonUniform
 %void = OpTypeVoid
 %5 = OpTypeFunction %void
 %int = OpTypeInt 32 1
diff --git a/test/opt/split_invalid_unreachable_test.cpp b/test/opt/split_invalid_unreachable_test.cpp
index 868c7b5..520af01 100644
--- a/test/opt/split_invalid_unreachable_test.cpp
+++ b/test/opt/split_invalid_unreachable_test.cpp
@@ -24,9 +24,9 @@
 using SplitInvalidUnreachableTest = PassTest<::testing::Test>;
 
 std::string spirv_header = R"(OpCapability Shader
-OpCapability VulkanMemoryModelKHR
+OpCapability VulkanMemoryModel
 OpExtension "SPV_KHR_vulkan_memory_model"
-OpMemoryModel Logical VulkanKHR
+OpMemoryModel Logical Vulkan
 OpEntryPoint Vertex %1 "shader"
 %uint = OpTypeInt 32 0
 %uint_1 = OpConstant %uint 1
diff --git a/test/opt/strip_atomic_counter_memory_test.cpp b/test/opt/strip_atomic_counter_memory_test.cpp
index 6287a13..90daa59 100644
--- a/test/opt/strip_atomic_counter_memory_test.cpp
+++ b/test/opt/strip_atomic_counter_memory_test.cpp
@@ -45,9 +45,9 @@
   std::vector<const char*> result = {
       // clang-format off
               "OpCapability Shader",
-              "OpCapability VulkanMemoryModelKHR",
+              "OpCapability VulkanMemoryModel",
               "OpExtension \"SPV_KHR_vulkan_memory_model\"",
-              "OpMemoryModel Logical VulkanKHR",
+              "OpMemoryModel Logical Vulkan",
               "OpEntryPoint Vertex %1 \"shader\"",
       "%uint = OpTypeInt 32 0",
 "%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint",
@@ -76,9 +76,9 @@
   std::vector<const char*> result = {
       // clang-format off
               "OpCapability Shader",
-              "OpCapability VulkanMemoryModelKHR",
+              "OpCapability VulkanMemoryModel",
               "OpExtension \"SPV_KHR_vulkan_memory_model\"",
-              "OpMemoryModel Logical VulkanKHR",
+              "OpMemoryModel Logical Vulkan",
               "OpEntryPoint Vertex %1 \"shader\"",
       "%uint = OpTypeInt 32 0",
 "%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint",
diff --git a/test/opt/strip_debug_info_test.cpp b/test/opt/strip_debug_info_test.cpp
index 25cf7d8..2f2ff46 100644
--- a/test/opt/strip_debug_info_test.cpp
+++ b/test/opt/strip_debug_info_test.cpp
@@ -51,6 +51,8 @@
                "OpLine %3 4 4",
                "OpNoLine",
                "OpFunctionEnd",
+               "OpNoLine",
+               "OpLine %3 4 5"
       // clang-format on
   };
   SinglePassRunAndCheck<StripDebugInfoPass>(JoinAllInsts(text),
@@ -74,6 +76,82 @@
                                             /* skip_nop = */ false);
 }
 
+using StripDebugStringTest = PassTest<::testing::Test>;
+
+TEST_F(StripDebugStringTest, OpDecorateRemoved) {
+  std::vector<const char*> input{
+      // clang-format off
+                     "OpCapability Shader",
+                "%1 = OpExtInstImport \"GLSL.std.450\"",
+                     "OpMemoryModel Logical GLSL450",
+                     "OpEntryPoint Vertex %2 \"main\"",
+                "%3 = OpString \"minimal.vert\"",
+                     "OpDecorate %3 Location 1337",
+             "%void = OpTypeVoid",
+                "%5 = OpTypeFunction %void",
+                "%2 = OpFunction %void None %5",
+                "%6 = OpLabel",
+                     "OpReturn",
+                     "OpFunctionEnd",
+      // clang-format on
+  };
+  std::vector<const char*> output{
+      // clang-format off
+                     "OpCapability Shader",
+                "%1 = OpExtInstImport \"GLSL.std.450\"",
+                     "OpMemoryModel Logical GLSL450",
+                     "OpEntryPoint Vertex %2 \"main\"",
+             "%void = OpTypeVoid",
+                "%5 = OpTypeFunction %void",
+                "%2 = OpFunction %void None %5",
+                "%6 = OpLabel",
+                     "OpReturn",
+                     "OpFunctionEnd",
+      // clang-format on
+  };
+  SinglePassRunAndCheck<StripDebugInfoPass>(JoinAllInsts(input),
+                                            JoinAllInsts(output),
+                                            /* skip_nop = */ false,
+                                            /* do_validation */ true);
+}
+
+TEST_F(StripDebugStringTest, OpNameRemoved) {
+  std::vector<const char*> input{
+      // clang-format off
+                     "OpCapability Shader",
+                "%1 = OpExtInstImport \"GLSL.std.450\"",
+                     "OpMemoryModel Logical GLSL450",
+                     "OpEntryPoint Vertex %2 \"main\"",
+                "%3 = OpString \"minimal.vert\"",
+                     "OpName %3 \"bob\"",
+             "%void = OpTypeVoid",
+                "%5 = OpTypeFunction %void",
+                "%2 = OpFunction %void None %5",
+                "%6 = OpLabel",
+                     "OpReturn",
+                     "OpFunctionEnd",
+      // clang-format on
+  };
+  std::vector<const char*> output{
+      // clang-format off
+                     "OpCapability Shader",
+                "%1 = OpExtInstImport \"GLSL.std.450\"",
+                     "OpMemoryModel Logical GLSL450",
+                     "OpEntryPoint Vertex %2 \"main\"",
+             "%void = OpTypeVoid",
+                "%5 = OpTypeFunction %void",
+                "%2 = OpFunction %void None %5",
+                "%6 = OpLabel",
+                     "OpReturn",
+                     "OpFunctionEnd",
+      // clang-format on
+  };
+  SinglePassRunAndCheck<StripDebugInfoPass>(JoinAllInsts(input),
+                                            JoinAllInsts(output),
+                                            /* skip_nop = */ false,
+                                            /* do_validation */ true);
+}
+
 using StripDebugInfoTest = PassTest<::testing::TestWithParam<const char*>>;
 
 TEST_P(StripDebugInfoTest, Kind) {
diff --git a/test/opt/struct_cfg_analysis_test.cpp b/test/opt/struct_cfg_analysis_test.cpp
index 7fb3784..0451a8b 100644
--- a/test/opt/struct_cfg_analysis_test.cpp
+++ b/test/opt/struct_cfg_analysis_test.cpp
@@ -26,6 +26,7 @@
 namespace {
 
 using StructCFGAnalysisTest = PassTest<::testing::Test>;
+using ::testing::UnorderedElementsAre;
 
 TEST_F(StructCFGAnalysisTest, BBInSelection) {
   const std::string text = R"(
@@ -62,6 +63,10 @@
   EXPECT_EQ(analysis.LoopMergeBlock(1), 0);
   EXPECT_EQ(analysis.ContainingSwitch(1), 0);
   EXPECT_EQ(analysis.SwitchMergeBlock(1), 0);
+  EXPECT_FALSE(analysis.IsContinueBlock(1));
+  EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1));
+  EXPECT_FALSE(analysis.IsInContinueConstruct(1));
+  EXPECT_FALSE(analysis.IsMergeBlock(1));
 
   // BB2 is in the construct.
   EXPECT_EQ(analysis.ContainingConstruct(2), 1);
@@ -70,6 +75,10 @@
   EXPECT_EQ(analysis.LoopMergeBlock(2), 0);
   EXPECT_EQ(analysis.ContainingSwitch(2), 0);
   EXPECT_EQ(analysis.SwitchMergeBlock(2), 0);
+  EXPECT_FALSE(analysis.IsContinueBlock(2));
+  EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2));
+  EXPECT_FALSE(analysis.IsInContinueConstruct(2));
+  EXPECT_FALSE(analysis.IsMergeBlock(2));
 
   // The merge node is not in the construct.
   EXPECT_EQ(analysis.ContainingConstruct(3), 0);
@@ -78,6 +87,10 @@
   EXPECT_EQ(analysis.LoopMergeBlock(3), 0);
   EXPECT_EQ(analysis.ContainingSwitch(3), 0);
   EXPECT_EQ(analysis.SwitchMergeBlock(3), 0);
+  EXPECT_FALSE(analysis.IsContinueBlock(3));
+  EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3));
+  EXPECT_FALSE(analysis.IsInContinueConstruct(3));
+  EXPECT_TRUE(analysis.IsMergeBlock(3));
 }
 
 TEST_F(StructCFGAnalysisTest, BBInLoop) {
@@ -119,6 +132,10 @@
   EXPECT_EQ(analysis.LoopMergeBlock(1), 0);
   EXPECT_EQ(analysis.ContainingSwitch(1), 0);
   EXPECT_EQ(analysis.SwitchMergeBlock(1), 0);
+  EXPECT_FALSE(analysis.IsContinueBlock(1));
+  EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1));
+  EXPECT_FALSE(analysis.IsInContinueConstruct(1));
+  EXPECT_FALSE(analysis.IsMergeBlock(1));
 
   // BB2 is in the construct.
   EXPECT_EQ(analysis.ContainingConstruct(2), 1);
@@ -127,6 +144,10 @@
   EXPECT_EQ(analysis.LoopMergeBlock(2), 3);
   EXPECT_EQ(analysis.ContainingSwitch(2), 0);
   EXPECT_EQ(analysis.SwitchMergeBlock(2), 0);
+  EXPECT_FALSE(analysis.IsContinueBlock(2));
+  EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2));
+  EXPECT_FALSE(analysis.IsInContinueConstruct(2));
+  EXPECT_FALSE(analysis.IsMergeBlock(2));
 
   // The merge node is not in the construct.
   EXPECT_EQ(analysis.ContainingConstruct(3), 0);
@@ -135,6 +156,10 @@
   EXPECT_EQ(analysis.LoopMergeBlock(3), 0);
   EXPECT_EQ(analysis.ContainingSwitch(3), 0);
   EXPECT_EQ(analysis.SwitchMergeBlock(3), 0);
+  EXPECT_FALSE(analysis.IsContinueBlock(3));
+  EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3));
+  EXPECT_FALSE(analysis.IsInContinueConstruct(3));
+  EXPECT_TRUE(analysis.IsMergeBlock(3));
 
   // The continue block is in the construct.
   EXPECT_EQ(analysis.ContainingConstruct(4), 1);
@@ -143,6 +168,10 @@
   EXPECT_EQ(analysis.LoopMergeBlock(4), 3);
   EXPECT_EQ(analysis.ContainingSwitch(4), 0);
   EXPECT_EQ(analysis.SwitchMergeBlock(4), 0);
+  EXPECT_TRUE(analysis.IsContinueBlock(4));
+  EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(4));
+  EXPECT_TRUE(analysis.IsInContinueConstruct(4));
+  EXPECT_FALSE(analysis.IsMergeBlock(4));
 }
 
 TEST_F(StructCFGAnalysisTest, SelectionInLoop) {
@@ -189,6 +218,10 @@
   EXPECT_EQ(analysis.LoopMergeBlock(1), 0);
   EXPECT_EQ(analysis.ContainingSwitch(1), 0);
   EXPECT_EQ(analysis.SwitchMergeBlock(1), 0);
+  EXPECT_FALSE(analysis.IsContinueBlock(1));
+  EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1));
+  EXPECT_FALSE(analysis.IsInContinueConstruct(1));
+  EXPECT_FALSE(analysis.IsMergeBlock(1));
 
   // Selection header is in the loop only.
   EXPECT_EQ(analysis.ContainingConstruct(2), 1);
@@ -197,6 +230,10 @@
   EXPECT_EQ(analysis.LoopMergeBlock(2), 3);
   EXPECT_EQ(analysis.ContainingSwitch(2), 0);
   EXPECT_EQ(analysis.SwitchMergeBlock(2), 0);
+  EXPECT_FALSE(analysis.IsContinueBlock(2));
+  EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2));
+  EXPECT_FALSE(analysis.IsInContinueConstruct(2));
+  EXPECT_FALSE(analysis.IsMergeBlock(2));
 
   // The loop merge node is not in either construct.
   EXPECT_EQ(analysis.ContainingConstruct(3), 0);
@@ -205,6 +242,10 @@
   EXPECT_EQ(analysis.LoopMergeBlock(3), 0);
   EXPECT_EQ(analysis.ContainingSwitch(3), 0);
   EXPECT_EQ(analysis.SwitchMergeBlock(3), 0);
+  EXPECT_FALSE(analysis.IsContinueBlock(3));
+  EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3));
+  EXPECT_FALSE(analysis.IsInContinueConstruct(3));
+  EXPECT_TRUE(analysis.IsMergeBlock(3));
 
   // The continue block is in the loop only.
   EXPECT_EQ(analysis.ContainingConstruct(4), 1);
@@ -213,14 +254,22 @@
   EXPECT_EQ(analysis.LoopMergeBlock(4), 3);
   EXPECT_EQ(analysis.ContainingSwitch(4), 0);
   EXPECT_EQ(analysis.SwitchMergeBlock(4), 0);
+  EXPECT_TRUE(analysis.IsContinueBlock(4));
+  EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(4));
+  EXPECT_TRUE(analysis.IsInContinueConstruct(4));
+  EXPECT_FALSE(analysis.IsMergeBlock(4));
 
-  // BB5 is in the selection fist and the loop.
+  // BB5 is in the selection and the loop.
   EXPECT_EQ(analysis.ContainingConstruct(5), 2);
   EXPECT_EQ(analysis.ContainingLoop(5), 1);
   EXPECT_EQ(analysis.MergeBlock(5), 6);
   EXPECT_EQ(analysis.LoopMergeBlock(5), 3);
   EXPECT_EQ(analysis.ContainingSwitch(5), 0);
   EXPECT_EQ(analysis.SwitchMergeBlock(5), 0);
+  EXPECT_FALSE(analysis.IsContinueBlock(5));
+  EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(5));
+  EXPECT_FALSE(analysis.IsInContinueConstruct(5));
+  EXPECT_FALSE(analysis.IsMergeBlock(5));
 
   // The selection merge is in the loop only.
   EXPECT_EQ(analysis.ContainingConstruct(6), 1);
@@ -229,6 +278,10 @@
   EXPECT_EQ(analysis.LoopMergeBlock(6), 3);
   EXPECT_EQ(analysis.ContainingSwitch(6), 0);
   EXPECT_EQ(analysis.SwitchMergeBlock(6), 0);
+  EXPECT_FALSE(analysis.IsContinueBlock(6));
+  EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(6));
+  EXPECT_FALSE(analysis.IsInContinueConstruct(6));
+  EXPECT_TRUE(analysis.IsMergeBlock(6));
 }
 
 TEST_F(StructCFGAnalysisTest, LoopInSelection) {
@@ -275,6 +328,10 @@
   EXPECT_EQ(analysis.LoopMergeBlock(1), 0);
   EXPECT_EQ(analysis.ContainingSwitch(1), 0);
   EXPECT_EQ(analysis.SwitchMergeBlock(1), 0);
+  EXPECT_FALSE(analysis.IsContinueBlock(1));
+  EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1));
+  EXPECT_FALSE(analysis.IsInContinueConstruct(1));
+  EXPECT_FALSE(analysis.IsMergeBlock(1));
 
   // Loop header is in the selection only.
   EXPECT_EQ(analysis.ContainingConstruct(2), 1);
@@ -283,6 +340,10 @@
   EXPECT_EQ(analysis.LoopMergeBlock(2), 0);
   EXPECT_EQ(analysis.ContainingSwitch(2), 0);
   EXPECT_EQ(analysis.SwitchMergeBlock(2), 0);
+  EXPECT_FALSE(analysis.IsContinueBlock(2));
+  EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2));
+  EXPECT_FALSE(analysis.IsInContinueConstruct(2));
+  EXPECT_FALSE(analysis.IsMergeBlock(2));
 
   // The selection merge node is not in either construct.
   EXPECT_EQ(analysis.ContainingConstruct(3), 0);
@@ -291,6 +352,10 @@
   EXPECT_EQ(analysis.LoopMergeBlock(3), 0);
   EXPECT_EQ(analysis.ContainingSwitch(3), 0);
   EXPECT_EQ(analysis.SwitchMergeBlock(3), 0);
+  EXPECT_FALSE(analysis.IsContinueBlock(3));
+  EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3));
+  EXPECT_FALSE(analysis.IsInContinueConstruct(3));
+  EXPECT_TRUE(analysis.IsMergeBlock(3));
 
   // The loop merge is in the selection only.
   EXPECT_EQ(analysis.ContainingConstruct(4), 1);
@@ -299,6 +364,10 @@
   EXPECT_EQ(analysis.LoopMergeBlock(4), 0);
   EXPECT_EQ(analysis.ContainingSwitch(4), 0);
   EXPECT_EQ(analysis.SwitchMergeBlock(4), 0);
+  EXPECT_FALSE(analysis.IsContinueBlock(4));
+  EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(4));
+  EXPECT_FALSE(analysis.IsInContinueConstruct(4));
+  EXPECT_TRUE(analysis.IsMergeBlock(4));
 
   // The loop continue target is in the loop.
   EXPECT_EQ(analysis.ContainingConstruct(5), 2);
@@ -307,6 +376,10 @@
   EXPECT_EQ(analysis.LoopMergeBlock(5), 4);
   EXPECT_EQ(analysis.ContainingSwitch(5), 0);
   EXPECT_EQ(analysis.SwitchMergeBlock(5), 0);
+  EXPECT_TRUE(analysis.IsContinueBlock(5));
+  EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(5));
+  EXPECT_TRUE(analysis.IsInContinueConstruct(5));
+  EXPECT_FALSE(analysis.IsMergeBlock(5));
 
   // BB6 is in the loop.
   EXPECT_EQ(analysis.ContainingConstruct(6), 2);
@@ -315,6 +388,10 @@
   EXPECT_EQ(analysis.LoopMergeBlock(6), 4);
   EXPECT_EQ(analysis.ContainingSwitch(6), 0);
   EXPECT_EQ(analysis.SwitchMergeBlock(6), 0);
+  EXPECT_FALSE(analysis.IsContinueBlock(6));
+  EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(6));
+  EXPECT_FALSE(analysis.IsInContinueConstruct(6));
+  EXPECT_FALSE(analysis.IsMergeBlock(6));
 }
 
 TEST_F(StructCFGAnalysisTest, SelectionInSelection) {
@@ -359,6 +436,10 @@
   EXPECT_EQ(analysis.LoopMergeBlock(1), 0);
   EXPECT_EQ(analysis.ContainingSwitch(1), 0);
   EXPECT_EQ(analysis.SwitchMergeBlock(1), 0);
+  EXPECT_FALSE(analysis.IsContinueBlock(1));
+  EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1));
+  EXPECT_FALSE(analysis.IsInContinueConstruct(1));
+  EXPECT_FALSE(analysis.IsMergeBlock(1));
 
   // The inner header is in the outer selection.
   EXPECT_EQ(analysis.ContainingConstruct(2), 1);
@@ -367,6 +448,10 @@
   EXPECT_EQ(analysis.LoopMergeBlock(2), 0);
   EXPECT_EQ(analysis.ContainingSwitch(2), 0);
   EXPECT_EQ(analysis.SwitchMergeBlock(2), 0);
+  EXPECT_FALSE(analysis.IsContinueBlock(2));
+  EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2));
+  EXPECT_FALSE(analysis.IsInContinueConstruct(2));
+  EXPECT_FALSE(analysis.IsMergeBlock(2));
 
   // The outer merge node is not in either construct.
   EXPECT_EQ(analysis.ContainingConstruct(3), 0);
@@ -375,6 +460,10 @@
   EXPECT_EQ(analysis.LoopMergeBlock(3), 0);
   EXPECT_EQ(analysis.ContainingSwitch(3), 0);
   EXPECT_EQ(analysis.SwitchMergeBlock(3), 0);
+  EXPECT_FALSE(analysis.IsContinueBlock(3));
+  EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3));
+  EXPECT_FALSE(analysis.IsInContinueConstruct(3));
+  EXPECT_TRUE(analysis.IsMergeBlock(3));
 
   // The inner merge is in the outer selection.
   EXPECT_EQ(analysis.ContainingConstruct(4), 1);
@@ -383,6 +472,10 @@
   EXPECT_EQ(analysis.LoopMergeBlock(4), 0);
   EXPECT_EQ(analysis.ContainingSwitch(4), 0);
   EXPECT_EQ(analysis.SwitchMergeBlock(4), 0);
+  EXPECT_FALSE(analysis.IsContinueBlock(4));
+  EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(4));
+  EXPECT_FALSE(analysis.IsInContinueConstruct(4));
+  EXPECT_TRUE(analysis.IsMergeBlock(4));
 
   // BB5 is in the inner selection.
   EXPECT_EQ(analysis.ContainingConstruct(5), 2);
@@ -391,6 +484,10 @@
   EXPECT_EQ(analysis.LoopMergeBlock(5), 0);
   EXPECT_EQ(analysis.ContainingSwitch(5), 0);
   EXPECT_EQ(analysis.SwitchMergeBlock(5), 0);
+  EXPECT_FALSE(analysis.IsContinueBlock(5));
+  EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(5));
+  EXPECT_FALSE(analysis.IsInContinueConstruct(5));
+  EXPECT_FALSE(analysis.IsMergeBlock(5));
 }
 
 TEST_F(StructCFGAnalysisTest, LoopInLoop) {
@@ -439,6 +536,10 @@
   EXPECT_EQ(analysis.LoopMergeBlock(1), 0);
   EXPECT_EQ(analysis.ContainingSwitch(1), 0);
   EXPECT_EQ(analysis.SwitchMergeBlock(1), 0);
+  EXPECT_FALSE(analysis.IsContinueBlock(1));
+  EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1));
+  EXPECT_FALSE(analysis.IsInContinueConstruct(1));
+  EXPECT_FALSE(analysis.IsMergeBlock(1));
 
   // The inner loop header is in the outer loop.
   EXPECT_EQ(analysis.ContainingConstruct(2), 1);
@@ -447,6 +548,10 @@
   EXPECT_EQ(analysis.LoopMergeBlock(2), 3);
   EXPECT_EQ(analysis.ContainingSwitch(2), 0);
   EXPECT_EQ(analysis.SwitchMergeBlock(2), 0);
+  EXPECT_FALSE(analysis.IsContinueBlock(2));
+  EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2));
+  EXPECT_FALSE(analysis.IsInContinueConstruct(2));
+  EXPECT_FALSE(analysis.IsMergeBlock(2));
 
   // The outer merge node is not in either construct.
   EXPECT_EQ(analysis.ContainingConstruct(3), 0);
@@ -455,6 +560,10 @@
   EXPECT_EQ(analysis.LoopMergeBlock(3), 0);
   EXPECT_EQ(analysis.ContainingSwitch(3), 0);
   EXPECT_EQ(analysis.SwitchMergeBlock(3), 0);
+  EXPECT_FALSE(analysis.IsContinueBlock(3));
+  EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3));
+  EXPECT_FALSE(analysis.IsInContinueConstruct(3));
+  EXPECT_TRUE(analysis.IsMergeBlock(3));
 
   // The inner merge is in the outer loop.
   EXPECT_EQ(analysis.ContainingConstruct(4), 1);
@@ -463,6 +572,10 @@
   EXPECT_EQ(analysis.LoopMergeBlock(4), 3);
   EXPECT_EQ(analysis.ContainingSwitch(4), 0);
   EXPECT_EQ(analysis.SwitchMergeBlock(4), 0);
+  EXPECT_FALSE(analysis.IsContinueBlock(4));
+  EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(4));
+  EXPECT_FALSE(analysis.IsInContinueConstruct(4));
+  EXPECT_TRUE(analysis.IsMergeBlock(4));
 
   // The inner continue target is in the inner loop.
   EXPECT_EQ(analysis.ContainingConstruct(5), 2);
@@ -471,6 +584,10 @@
   EXPECT_EQ(analysis.LoopMergeBlock(5), 4);
   EXPECT_EQ(analysis.ContainingSwitch(5), 0);
   EXPECT_EQ(analysis.SwitchMergeBlock(5), 0);
+  EXPECT_TRUE(analysis.IsContinueBlock(5));
+  EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(5));
+  EXPECT_TRUE(analysis.IsInContinueConstruct(5));
+  EXPECT_FALSE(analysis.IsMergeBlock(5));
 
   // BB6 is in the loop.
   EXPECT_EQ(analysis.ContainingConstruct(6), 2);
@@ -479,6 +596,10 @@
   EXPECT_EQ(analysis.LoopMergeBlock(6), 4);
   EXPECT_EQ(analysis.ContainingSwitch(6), 0);
   EXPECT_EQ(analysis.SwitchMergeBlock(6), 0);
+  EXPECT_FALSE(analysis.IsContinueBlock(6));
+  EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(6));
+  EXPECT_FALSE(analysis.IsInContinueConstruct(6));
+  EXPECT_FALSE(analysis.IsMergeBlock(6));
 
   // The outer continue target is in the outer loop.
   EXPECT_EQ(analysis.ContainingConstruct(7), 1);
@@ -487,6 +608,10 @@
   EXPECT_EQ(analysis.LoopMergeBlock(7), 3);
   EXPECT_EQ(analysis.ContainingSwitch(7), 0);
   EXPECT_EQ(analysis.SwitchMergeBlock(7), 0);
+  EXPECT_TRUE(analysis.IsContinueBlock(7));
+  EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(7));
+  EXPECT_TRUE(analysis.IsInContinueConstruct(7));
+  EXPECT_FALSE(analysis.IsMergeBlock(7));
 }
 
 TEST_F(StructCFGAnalysisTest, KernelTest) {
@@ -523,6 +648,10 @@
     EXPECT_EQ(analysis.LoopMergeBlock(i), 0);
     EXPECT_EQ(analysis.ContainingSwitch(i), 0);
     EXPECT_EQ(analysis.SwitchMergeBlock(i), 0);
+    EXPECT_FALSE(analysis.IsContinueBlock(i));
+    EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(i));
+    EXPECT_FALSE(analysis.IsInContinueConstruct(i));
+    EXPECT_FALSE(analysis.IsMergeBlock(i));
   }
 }
 
@@ -581,6 +710,10 @@
   EXPECT_EQ(analysis.LoopMergeBlock(1), 0);
   EXPECT_EQ(analysis.ContainingSwitch(1), 0);
   EXPECT_EQ(analysis.SwitchMergeBlock(1), 0);
+  EXPECT_FALSE(analysis.IsContinueBlock(1));
+  EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1));
+  EXPECT_FALSE(analysis.IsInContinueConstruct(1));
+  EXPECT_FALSE(analysis.IsMergeBlock(1));
 
   // BB2 is in the construct.
   EXPECT_EQ(analysis.ContainingConstruct(2), 1);
@@ -589,6 +722,10 @@
   EXPECT_EQ(analysis.LoopMergeBlock(2), 0);
   EXPECT_EQ(analysis.ContainingSwitch(2), 1);
   EXPECT_EQ(analysis.SwitchMergeBlock(2), 3);
+  EXPECT_FALSE(analysis.IsContinueBlock(2));
+  EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2));
+  EXPECT_FALSE(analysis.IsInContinueConstruct(2));
+  EXPECT_FALSE(analysis.IsMergeBlock(2));
 
   // The merge node is not in the construct.
   EXPECT_EQ(analysis.ContainingConstruct(3), 0);
@@ -597,6 +734,10 @@
   EXPECT_EQ(analysis.LoopMergeBlock(3), 0);
   EXPECT_EQ(analysis.ContainingSwitch(3), 0);
   EXPECT_EQ(analysis.SwitchMergeBlock(3), 0);
+  EXPECT_FALSE(analysis.IsContinueBlock(3));
+  EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3));
+  EXPECT_FALSE(analysis.IsInContinueConstruct(3));
+  EXPECT_TRUE(analysis.IsMergeBlock(3));
 }
 
 TEST_F(StructCFGAnalysisTest, LoopInSwitch) {
@@ -643,6 +784,10 @@
   EXPECT_EQ(analysis.LoopMergeBlock(1), 0);
   EXPECT_EQ(analysis.ContainingSwitch(1), 0);
   EXPECT_EQ(analysis.SwitchMergeBlock(1), 0);
+  EXPECT_FALSE(analysis.IsContinueBlock(1));
+  EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1));
+  EXPECT_FALSE(analysis.IsInContinueConstruct(1));
+  EXPECT_FALSE(analysis.IsMergeBlock(1));
 
   // Loop header is in the selection only.
   EXPECT_EQ(analysis.ContainingConstruct(2), 1);
@@ -651,6 +796,10 @@
   EXPECT_EQ(analysis.LoopMergeBlock(2), 0);
   EXPECT_EQ(analysis.ContainingSwitch(2), 1);
   EXPECT_EQ(analysis.SwitchMergeBlock(2), 3);
+  EXPECT_FALSE(analysis.IsContinueBlock(2));
+  EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2));
+  EXPECT_FALSE(analysis.IsInContinueConstruct(2));
+  EXPECT_FALSE(analysis.IsMergeBlock(2));
 
   // The selection merge node is not in either construct.
   EXPECT_EQ(analysis.ContainingConstruct(3), 0);
@@ -659,6 +808,10 @@
   EXPECT_EQ(analysis.LoopMergeBlock(3), 0);
   EXPECT_EQ(analysis.ContainingSwitch(3), 0);
   EXPECT_EQ(analysis.SwitchMergeBlock(3), 0);
+  EXPECT_FALSE(analysis.IsContinueBlock(3));
+  EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3));
+  EXPECT_FALSE(analysis.IsInContinueConstruct(3));
+  EXPECT_TRUE(analysis.IsMergeBlock(3));
 
   // The loop merge is in the selection only.
   EXPECT_EQ(analysis.ContainingConstruct(4), 1);
@@ -667,6 +820,10 @@
   EXPECT_EQ(analysis.LoopMergeBlock(4), 0);
   EXPECT_EQ(analysis.ContainingSwitch(4), 1);
   EXPECT_EQ(analysis.SwitchMergeBlock(4), 3);
+  EXPECT_FALSE(analysis.IsContinueBlock(4));
+  EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(4));
+  EXPECT_FALSE(analysis.IsInContinueConstruct(4));
+  EXPECT_TRUE(analysis.IsMergeBlock(4));
 
   // The loop continue target is in the loop.
   EXPECT_EQ(analysis.ContainingConstruct(5), 2);
@@ -675,6 +832,10 @@
   EXPECT_EQ(analysis.LoopMergeBlock(5), 4);
   EXPECT_EQ(analysis.ContainingSwitch(5), 0);
   EXPECT_EQ(analysis.SwitchMergeBlock(5), 0);
+  EXPECT_TRUE(analysis.IsContinueBlock(5));
+  EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(5));
+  EXPECT_TRUE(analysis.IsInContinueConstruct(5));
+  EXPECT_FALSE(analysis.IsMergeBlock(5));
 
   // BB6 is in the loop.
   EXPECT_EQ(analysis.ContainingConstruct(6), 2);
@@ -683,6 +844,10 @@
   EXPECT_EQ(analysis.LoopMergeBlock(6), 4);
   EXPECT_EQ(analysis.ContainingSwitch(6), 0);
   EXPECT_EQ(analysis.SwitchMergeBlock(6), 0);
+  EXPECT_FALSE(analysis.IsContinueBlock(6));
+  EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(6));
+  EXPECT_FALSE(analysis.IsInContinueConstruct(6));
+  EXPECT_FALSE(analysis.IsMergeBlock(6));
 }
 
 TEST_F(StructCFGAnalysisTest, SelectionInSwitch) {
@@ -727,6 +892,10 @@
   EXPECT_EQ(analysis.LoopMergeBlock(1), 0);
   EXPECT_EQ(analysis.ContainingSwitch(1), 0);
   EXPECT_EQ(analysis.SwitchMergeBlock(1), 0);
+  EXPECT_FALSE(analysis.IsContinueBlock(1));
+  EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1));
+  EXPECT_FALSE(analysis.IsInContinueConstruct(1));
+  EXPECT_FALSE(analysis.IsMergeBlock(1));
 
   // The inner header is in the outer selection.
   EXPECT_EQ(analysis.ContainingConstruct(2), 1);
@@ -735,6 +904,10 @@
   EXPECT_EQ(analysis.LoopMergeBlock(2), 0);
   EXPECT_EQ(analysis.ContainingSwitch(2), 1);
   EXPECT_EQ(analysis.SwitchMergeBlock(2), 3);
+  EXPECT_FALSE(analysis.IsContinueBlock(2));
+  EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2));
+  EXPECT_FALSE(analysis.IsInContinueConstruct(2));
+  EXPECT_FALSE(analysis.IsMergeBlock(2));
 
   // The outer merge node is not in either construct.
   EXPECT_EQ(analysis.ContainingConstruct(3), 0);
@@ -743,6 +916,10 @@
   EXPECT_EQ(analysis.LoopMergeBlock(3), 0);
   EXPECT_EQ(analysis.ContainingSwitch(3), 0);
   EXPECT_EQ(analysis.SwitchMergeBlock(3), 0);
+  EXPECT_FALSE(analysis.IsContinueBlock(3));
+  EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3));
+  EXPECT_FALSE(analysis.IsInContinueConstruct(3));
+  EXPECT_TRUE(analysis.IsMergeBlock(3));
 
   // The inner merge is in the outer selection.
   EXPECT_EQ(analysis.ContainingConstruct(4), 1);
@@ -751,6 +928,10 @@
   EXPECT_EQ(analysis.LoopMergeBlock(4), 0);
   EXPECT_EQ(analysis.ContainingSwitch(4), 1);
   EXPECT_EQ(analysis.SwitchMergeBlock(4), 3);
+  EXPECT_FALSE(analysis.IsContinueBlock(4));
+  EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(4));
+  EXPECT_FALSE(analysis.IsInContinueConstruct(4));
+  EXPECT_TRUE(analysis.IsMergeBlock(4));
 
   // BB5 is in the inner selection.
   EXPECT_EQ(analysis.ContainingConstruct(5), 2);
@@ -759,6 +940,10 @@
   EXPECT_EQ(analysis.LoopMergeBlock(5), 0);
   EXPECT_EQ(analysis.ContainingSwitch(5), 1);
   EXPECT_EQ(analysis.SwitchMergeBlock(5), 3);
+  EXPECT_FALSE(analysis.IsContinueBlock(5));
+  EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(5));
+  EXPECT_FALSE(analysis.IsInContinueConstruct(5));
+  EXPECT_FALSE(analysis.IsMergeBlock(5));
 }
 
 TEST_F(StructCFGAnalysisTest, SwitchInSelection) {
@@ -803,6 +988,10 @@
   EXPECT_EQ(analysis.LoopMergeBlock(1), 0);
   EXPECT_EQ(analysis.ContainingSwitch(1), 0);
   EXPECT_EQ(analysis.SwitchMergeBlock(1), 0);
+  EXPECT_FALSE(analysis.IsContinueBlock(1));
+  EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1));
+  EXPECT_FALSE(analysis.IsInContinueConstruct(1));
+  EXPECT_FALSE(analysis.IsMergeBlock(1));
 
   // The inner header is in the outer selection.
   EXPECT_EQ(analysis.ContainingConstruct(2), 1);
@@ -811,6 +1000,10 @@
   EXPECT_EQ(analysis.LoopMergeBlock(2), 0);
   EXPECT_EQ(analysis.ContainingSwitch(2), 0);
   EXPECT_EQ(analysis.SwitchMergeBlock(2), 0);
+  EXPECT_FALSE(analysis.IsContinueBlock(2));
+  EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2));
+  EXPECT_FALSE(analysis.IsInContinueConstruct(2));
+  EXPECT_FALSE(analysis.IsMergeBlock(2));
 
   // The outer merge node is not in either construct.
   EXPECT_EQ(analysis.ContainingConstruct(3), 0);
@@ -819,6 +1012,10 @@
   EXPECT_EQ(analysis.LoopMergeBlock(3), 0);
   EXPECT_EQ(analysis.ContainingSwitch(3), 0);
   EXPECT_EQ(analysis.SwitchMergeBlock(3), 0);
+  EXPECT_FALSE(analysis.IsContinueBlock(3));
+  EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3));
+  EXPECT_FALSE(analysis.IsInContinueConstruct(3));
+  EXPECT_TRUE(analysis.IsMergeBlock(3));
 
   // The inner merge is in the outer selection.
   EXPECT_EQ(analysis.ContainingConstruct(4), 1);
@@ -827,6 +1024,10 @@
   EXPECT_EQ(analysis.LoopMergeBlock(4), 0);
   EXPECT_EQ(analysis.ContainingSwitch(4), 0);
   EXPECT_EQ(analysis.SwitchMergeBlock(4), 0);
+  EXPECT_FALSE(analysis.IsContinueBlock(4));
+  EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(4));
+  EXPECT_FALSE(analysis.IsInContinueConstruct(4));
+  EXPECT_TRUE(analysis.IsMergeBlock(4));
 
   // BB5 is in the inner selection.
   EXPECT_EQ(analysis.ContainingConstruct(5), 2);
@@ -835,8 +1036,339 @@
   EXPECT_EQ(analysis.LoopMergeBlock(5), 0);
   EXPECT_EQ(analysis.ContainingSwitch(5), 2);
   EXPECT_EQ(analysis.SwitchMergeBlock(5), 4);
+  EXPECT_FALSE(analysis.IsContinueBlock(5));
+  EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(5));
+  EXPECT_FALSE(analysis.IsInContinueConstruct(5));
+  EXPECT_FALSE(analysis.IsMergeBlock(5));
 }
 
+TEST_F(StructCFGAnalysisTest, SelectionInContinue) {
+  const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+%void = OpTypeVoid
+%bool = OpTypeBool
+%bool_undef = OpUndef %bool
+%uint = OpTypeInt 32 0
+%uint_undef = OpUndef %uint
+%void_func = OpTypeFunction %void
+%main = OpFunction %void None %void_func
+%entry_lab = OpLabel
+OpBranch %1
+%1 = OpLabel
+OpLoopMerge %3 %4 None
+OpBranchConditional %undef_bool %2 %3
+%2 = OpLabel
+OpBranch %3
+%4 = OpLabel
+OpSelectionMerge %6 None
+OpBranchConditional %undef_bool %5 %6
+%5 = OpLabel
+OpBranch %6
+%6 = OpLabel
+OpBranch %1
+%3 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  std::unique_ptr<IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
+                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+  StructuredCFGAnalysis analysis(context.get());
+
+  // The loop header is not in either construct.
+  EXPECT_EQ(analysis.ContainingConstruct(1), 0);
+  EXPECT_EQ(analysis.ContainingLoop(1), 0);
+  EXPECT_EQ(analysis.MergeBlock(1), 0);
+  EXPECT_EQ(analysis.LoopMergeBlock(1), 0);
+  EXPECT_EQ(analysis.ContainingSwitch(1), 0);
+  EXPECT_EQ(analysis.SwitchMergeBlock(1), 0);
+  EXPECT_FALSE(analysis.IsContinueBlock(1));
+  EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1));
+  EXPECT_FALSE(analysis.IsInContinueConstruct(1));
+  EXPECT_FALSE(analysis.IsMergeBlock(1));
+
+  // Selection header is in the loop only.
+  EXPECT_EQ(analysis.ContainingConstruct(2), 1);
+  EXPECT_EQ(analysis.ContainingLoop(2), 1);
+  EXPECT_EQ(analysis.MergeBlock(2), 3);
+  EXPECT_EQ(analysis.LoopMergeBlock(2), 3);
+  EXPECT_EQ(analysis.ContainingSwitch(2), 0);
+  EXPECT_EQ(analysis.SwitchMergeBlock(2), 0);
+  EXPECT_FALSE(analysis.IsContinueBlock(2));
+  EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2));
+  EXPECT_FALSE(analysis.IsInContinueConstruct(2));
+  EXPECT_FALSE(analysis.IsMergeBlock(2));
+
+  // The loop merge node is not in either construct.
+  EXPECT_EQ(analysis.ContainingConstruct(3), 0);
+  EXPECT_EQ(analysis.ContainingLoop(3), 0);
+  EXPECT_EQ(analysis.MergeBlock(3), 0);
+  EXPECT_EQ(analysis.LoopMergeBlock(3), 0);
+  EXPECT_EQ(analysis.ContainingSwitch(3), 0);
+  EXPECT_EQ(analysis.SwitchMergeBlock(3), 0);
+  EXPECT_FALSE(analysis.IsContinueBlock(3));
+  EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3));
+  EXPECT_FALSE(analysis.IsInContinueConstruct(3));
+  EXPECT_TRUE(analysis.IsMergeBlock(3));
+
+  // The continue block is in the loop only.
+  EXPECT_EQ(analysis.ContainingConstruct(4), 1);
+  EXPECT_EQ(analysis.ContainingLoop(4), 1);
+  EXPECT_EQ(analysis.MergeBlock(4), 3);
+  EXPECT_EQ(analysis.LoopMergeBlock(4), 3);
+  EXPECT_EQ(analysis.ContainingSwitch(4), 0);
+  EXPECT_EQ(analysis.SwitchMergeBlock(4), 0);
+  EXPECT_TRUE(analysis.IsContinueBlock(4));
+  EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(4));
+  EXPECT_TRUE(analysis.IsInContinueConstruct(4));
+  EXPECT_FALSE(analysis.IsMergeBlock(4));
+
+  // BB5 is in the selection and the continue for the loop.
+  EXPECT_EQ(analysis.ContainingConstruct(5), 4);
+  EXPECT_EQ(analysis.ContainingLoop(5), 1);
+  EXPECT_EQ(analysis.MergeBlock(5), 6);
+  EXPECT_EQ(analysis.LoopMergeBlock(5), 3);
+  EXPECT_EQ(analysis.ContainingSwitch(5), 0);
+  EXPECT_EQ(analysis.SwitchMergeBlock(5), 0);
+  EXPECT_FALSE(analysis.IsContinueBlock(5));
+  EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(5));
+  EXPECT_TRUE(analysis.IsInContinueConstruct(5));
+  EXPECT_FALSE(analysis.IsMergeBlock(5));
+
+  // BB5 is in the continue for the loop.
+  EXPECT_EQ(analysis.ContainingConstruct(6), 1);
+  EXPECT_EQ(analysis.ContainingLoop(6), 1);
+  EXPECT_EQ(analysis.MergeBlock(6), 3);
+  EXPECT_EQ(analysis.LoopMergeBlock(6), 3);
+  EXPECT_EQ(analysis.ContainingSwitch(6), 0);
+  EXPECT_EQ(analysis.SwitchMergeBlock(6), 0);
+  EXPECT_FALSE(analysis.IsContinueBlock(6));
+  EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(6));
+  EXPECT_TRUE(analysis.IsInContinueConstruct(6));
+  EXPECT_TRUE(analysis.IsMergeBlock(6));
+}
+
+TEST_F(StructCFGAnalysisTest, LoopInContinue) {
+  const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+%void = OpTypeVoid
+%bool = OpTypeBool
+%bool_undef = OpUndef %bool
+%uint = OpTypeInt 32 0
+%uint_undef = OpUndef %uint
+%void_func = OpTypeFunction %void
+%main = OpFunction %void None %void_func
+%entry_lab = OpLabel
+OpBranch %1
+%1 = OpLabel
+OpLoopMerge %3 %7 None
+OpBranchConditional %undef_bool %2 %3
+%2 = OpLabel
+OpBranchConditional %undef_bool %3 %7
+%7 = OpLabel
+OpLoopMerge %4 %5 None
+OpBranchConditional %undef_bool %4 %6
+%5 = OpLabel
+OpBranch %7
+%6 = OpLabel
+OpBranch %4
+%4 = OpLabel
+OpBranch %1
+%3 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  std::unique_ptr<IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
+                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+  StructuredCFGAnalysis analysis(context.get());
+
+  // The outer loop header is not in either construct.
+  EXPECT_EQ(analysis.ContainingConstruct(1), 0);
+  EXPECT_EQ(analysis.ContainingLoop(1), 0);
+  EXPECT_EQ(analysis.MergeBlock(1), 0);
+  EXPECT_EQ(analysis.LoopMergeBlock(1), 0);
+  EXPECT_EQ(analysis.ContainingSwitch(1), 0);
+  EXPECT_EQ(analysis.SwitchMergeBlock(1), 0);
+  EXPECT_FALSE(analysis.IsContinueBlock(1));
+  EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1));
+  EXPECT_FALSE(analysis.IsInContinueConstruct(1));
+  EXPECT_FALSE(analysis.IsMergeBlock(1));
+
+  // BB2 is a regular block in the inner loop.
+  EXPECT_EQ(analysis.ContainingConstruct(2), 1);
+  EXPECT_EQ(analysis.ContainingLoop(2), 1);
+  EXPECT_EQ(analysis.MergeBlock(2), 3);
+  EXPECT_EQ(analysis.LoopMergeBlock(2), 3);
+  EXPECT_EQ(analysis.ContainingSwitch(2), 0);
+  EXPECT_EQ(analysis.SwitchMergeBlock(2), 0);
+  EXPECT_FALSE(analysis.IsContinueBlock(2));
+  EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2));
+  EXPECT_FALSE(analysis.IsInContinueConstruct(2));
+  EXPECT_FALSE(analysis.IsMergeBlock(2));
+
+  // The outer merge node is not in either construct.
+  EXPECT_EQ(analysis.ContainingConstruct(3), 0);
+  EXPECT_EQ(analysis.ContainingLoop(3), 0);
+  EXPECT_EQ(analysis.MergeBlock(3), 0);
+  EXPECT_EQ(analysis.LoopMergeBlock(3), 0);
+  EXPECT_EQ(analysis.ContainingSwitch(3), 0);
+  EXPECT_EQ(analysis.SwitchMergeBlock(3), 0);
+  EXPECT_FALSE(analysis.IsContinueBlock(3));
+  EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3));
+  EXPECT_FALSE(analysis.IsInContinueConstruct(3));
+  EXPECT_TRUE(analysis.IsMergeBlock(3));
+
+  // The inner merge is in the continue of the outer loop.
+  EXPECT_EQ(analysis.ContainingConstruct(4), 1);
+  EXPECT_EQ(analysis.ContainingLoop(4), 1);
+  EXPECT_EQ(analysis.MergeBlock(4), 3);
+  EXPECT_EQ(analysis.LoopMergeBlock(4), 3);
+  EXPECT_EQ(analysis.ContainingSwitch(4), 0);
+  EXPECT_EQ(analysis.SwitchMergeBlock(4), 0);
+  EXPECT_FALSE(analysis.IsContinueBlock(4));
+  EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(4));
+  EXPECT_TRUE(analysis.IsInContinueConstruct(4));
+  EXPECT_TRUE(analysis.IsMergeBlock(4));
+
+  // The inner continue target is in the inner loop.
+  EXPECT_EQ(analysis.ContainingConstruct(5), 7);
+  EXPECT_EQ(analysis.ContainingLoop(5), 7);
+  EXPECT_EQ(analysis.MergeBlock(5), 4);
+  EXPECT_EQ(analysis.LoopMergeBlock(5), 4);
+  EXPECT_EQ(analysis.ContainingSwitch(5), 0);
+  EXPECT_EQ(analysis.SwitchMergeBlock(5), 0);
+  EXPECT_TRUE(analysis.IsContinueBlock(5));
+  EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(5));
+  EXPECT_TRUE(analysis.IsInContinueConstruct(5));
+  EXPECT_FALSE(analysis.IsMergeBlock(5));
+
+  // BB6 is a regular block in the inner loop.
+  EXPECT_EQ(analysis.ContainingConstruct(6), 7);
+  EXPECT_EQ(analysis.ContainingLoop(6), 7);
+  EXPECT_EQ(analysis.MergeBlock(6), 4);
+  EXPECT_EQ(analysis.LoopMergeBlock(6), 4);
+  EXPECT_EQ(analysis.ContainingSwitch(6), 0);
+  EXPECT_EQ(analysis.SwitchMergeBlock(6), 0);
+  EXPECT_FALSE(analysis.IsContinueBlock(6));
+  EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(6));
+  EXPECT_TRUE(analysis.IsInContinueConstruct(6));
+  EXPECT_FALSE(analysis.IsMergeBlock(6));
+
+  // The outer continue target is in the outer loop.
+  EXPECT_EQ(analysis.ContainingConstruct(7), 1);
+  EXPECT_EQ(analysis.ContainingLoop(7), 1);
+  EXPECT_EQ(analysis.MergeBlock(7), 3);
+  EXPECT_EQ(analysis.LoopMergeBlock(7), 3);
+  EXPECT_EQ(analysis.ContainingSwitch(7), 0);
+  EXPECT_EQ(analysis.SwitchMergeBlock(7), 0);
+  EXPECT_TRUE(analysis.IsContinueBlock(7));
+  EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(7));
+  EXPECT_TRUE(analysis.IsInContinueConstruct(7));
+  EXPECT_FALSE(analysis.IsMergeBlock(7));
+}
+
+TEST_F(StructCFGAnalysisTest, FuncCallInContinueDirect) {
+  const std::string text = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %1 "main"
+       %void = OpTypeVoid
+       %bool = OpTypeBool
+          %4 = OpUndef %bool
+       %uint = OpTypeInt 32 0
+          %6 = OpUndef %uint
+          %7 = OpTypeFunction %void
+          %1 = OpFunction %void None %7
+          %8 = OpLabel
+               OpBranch %9
+          %9 = OpLabel
+               OpLoopMerge %10 %11 None
+               OpBranchConditional %12 %10 %11
+         %11 = OpLabel
+         %13 = OpFunctionCall %void %14
+               OpBranch %9
+         %10 = OpLabel
+         %15 = OpFunctionCall %void %16
+               OpReturn
+               OpFunctionEnd
+         %14 = OpFunction %void None %7
+         %17 = OpLabel
+               OpReturn
+               OpFunctionEnd
+         %16 = OpFunction %void None %7
+         %18 = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+
+  std::unique_ptr<IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
+                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+  StructuredCFGAnalysis analysis(context.get());
+
+  auto c = analysis.FindFuncsCalledFromContinue();
+  EXPECT_THAT(c, UnorderedElementsAre(14u));
+}
+
+TEST_F(StructCFGAnalysisTest, FuncCallInContinueIndirect) {
+  const std::string text = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %1 "main"
+       %void = OpTypeVoid
+       %bool = OpTypeBool
+          %4 = OpUndef %bool
+       %uint = OpTypeInt 32 0
+          %6 = OpUndef %uint
+          %7 = OpTypeFunction %void
+          %1 = OpFunction %void None %7
+          %8 = OpLabel
+               OpBranch %9
+          %9 = OpLabel
+               OpLoopMerge %10 %11 None
+               OpBranchConditional %12 %10 %11
+         %11 = OpLabel
+         %13 = OpFunctionCall %void %14
+               OpBranch %9
+         %10 = OpLabel
+         %15 = OpFunctionCall %void %16
+               OpReturn
+               OpFunctionEnd
+         %14 = OpFunction %void None %7
+         %17 = OpLabel
+         %19 = OpFunctionCall %void %16
+               OpReturn
+               OpFunctionEnd
+         %16 = OpFunction %void None %7
+         %18 = OpLabel
+         %20 = OpFunctionCall %void %21
+               OpReturn
+               OpFunctionEnd
+         %21 = OpFunction %void None %7
+         %22 = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+
+  std::unique_ptr<IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
+                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+  StructuredCFGAnalysis analysis(context.get());
+
+  auto c = analysis.FindFuncsCalledFromContinue();
+  EXPECT_THAT(c, UnorderedElementsAre(14u, 16u, 21u));
+}
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools
diff --git a/test/opt/type_manager_test.cpp b/test/opt/type_manager_test.cpp
index 267d98c..743d0b6 100644
--- a/test/opt/type_manager_test.cpp
+++ b/test/opt/type_manager_test.cpp
@@ -156,7 +156,8 @@
   types.emplace_back(new ReserveId());
   types.emplace_back(new Queue());
 
-  // Pipe, Forward Pointer, PipeStorage, NamedBarrier, AccelerationStructureNV
+  // 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));
@@ -165,6 +166,7 @@
   types.emplace_back(new PipeStorage());
   types.emplace_back(new NamedBarrier());
   types.emplace_back(new AccelerationStructureNV());
+  types.emplace_back(new CooperativeMatrixNV(f32, 24, 24, 24));
 
   return types;
 }
@@ -214,6 +216,7 @@
     %arr_spec_const_with_id = OpTypeArray %s32 %spec_const_with_id
     %arr_long_constant = OpTypeArray %s32 %long_constant
     %arr_spec_const_op = OpTypeArray %s32 %spec_const_op
+    %cm   = OpTypeCooperativeMatrixNV %f64 %id4 %id4 %id4
   )";
 
   std::vector<std::pair<uint32_t, std::string>> type_id_strs = {
@@ -251,6 +254,7 @@
       {36, "[sint32, id(1), words(1,99,42)]"},
       {37, "[sint32, id(33), words(0,705032704,1)]"},
       {38, "[sint32, id(34), words(2,34)]"},
+      {39, "<float64, 6, 6, 6>"},
   };
 
   std::unique_ptr<IRContext> context =
@@ -1060,6 +1064,7 @@
 ; CHECK: OpTypePipeStorage
 ; CHECK: OpTypeNamedBarrier
 ; CHECK: OpTypeAccelerationStructureNV
+; CHECK: OpTypeCooperativeMatrixNV [[f32]] [[uint24]] [[uint24]] [[uint24]]
 OpCapability Shader
 OpCapability Int64
 OpCapability Linkage
diff --git a/test/opt/upgrade_memory_model_test.cpp b/test/opt/upgrade_memory_model_test.cpp
index b012383..7f64ffd 100644
--- a/test/opt/upgrade_memory_model_test.cpp
+++ b/test/opt/upgrade_memory_model_test.cpp
@@ -34,14 +34,14 @@
   SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
 }
 
-TEST_F(UpgradeMemoryModelTest, InvalidMemoryModelVulkanKHR) {
+TEST_F(UpgradeMemoryModelTest, InvalidMemoryModelVulkan) {
   const std::string text = R"(
-; CHECK: OpMemoryModel Logical VulkanKHR
+; CHECK: OpMemoryModel Logical Vulkan
 OpCapability Shader
 OpCapability Linkage
-OpCapability VulkanMemoryModelKHR
+OpCapability VulkanMemoryModel
 OpExtension "SPV_KHR_vulkan_memory_model"
-OpMemoryModel Logical VulkanKHR
+OpMemoryModel Logical Vulkan
 )";
 
   SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
@@ -49,9 +49,9 @@
 
 TEST_F(UpgradeMemoryModelTest, JustMemoryModel) {
   const std::string text = R"(
-; CHECK: OpCapability VulkanMemoryModelKHR
+; CHECK: OpCapability VulkanMemoryModel
 ; CHECK: OpExtension "SPV_KHR_vulkan_memory_model"
-; CHECK: OpMemoryModel Logical VulkanKHR
+; CHECK: OpMemoryModel Logical Vulkan
 OpCapability Shader
 OpCapability Linkage
 OpMemoryModel Logical GLSL450
@@ -79,8 +79,8 @@
 TEST_F(UpgradeMemoryModelTest, WorkgroupVariable) {
   const std::string text = R"(
 ; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 2
-; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
-; CHECK: OpStore {{%\w+}} {{%\w+}} MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]]
+; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerVisible|NonPrivatePointer [[scope]]
+; CHECK: OpStore {{%\w+}} {{%\w+}} MakePointerAvailable|NonPrivatePointer [[scope]]
 OpCapability Shader
 OpCapability Linkage
 OpMemoryModel Logical GLSL450
@@ -92,7 +92,7 @@
 %func = OpFunction %void None %func_ty
 %1 = OpLabel
 %ld = OpLoad %int %var
-%st = OpStore %var %ld
+OpStore %var %ld
 OpReturn
 OpFunctionEnd
 )";
@@ -103,8 +103,8 @@
 TEST_F(UpgradeMemoryModelTest, WorkgroupFunctionParameter) {
   const std::string text = R"(
 ; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 2
-; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
-; CHECK: OpStore {{%\w+}} {{%\w+}} MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]]
+; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerVisible|NonPrivatePointer [[scope]]
+; CHECK: OpStore {{%\w+}} {{%\w+}} MakePointerAvailable|NonPrivatePointer [[scope]]
 OpCapability Shader
 OpCapability Linkage
 OpMemoryModel Logical GLSL450
@@ -116,7 +116,7 @@
 %param = OpFunctionParameter %ptr_int_Workgroup
 %1 = OpLabel
 %ld = OpLoad %int %param
-%st = OpStore %param %ld
+OpStore %param %ld
 OpReturn
 OpFunctionEnd
 )";
@@ -128,8 +128,8 @@
   const std::string text = R"(
 ; CHECK-NOT: OpDecorate
 ; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
-; CHECK: OpLoad {{%\w+}} {{%\w+}} Volatile|MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
-; CHECK: OpStore {{%\w+}} {{%\w+}} Volatile|MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]]
+; CHECK: OpLoad {{%\w+}} {{%\w+}} Volatile|MakePointerVisible|NonPrivatePointer [[scope]]
+; CHECK: OpStore {{%\w+}} {{%\w+}} Volatile|MakePointerAvailable|NonPrivatePointer [[scope]]
 OpCapability Shader
 OpCapability Linkage
 OpMemoryModel Logical GLSL450
@@ -155,8 +155,8 @@
   const std::string text = R"(
 ; CHECK-NOT: OpDecorate
 ; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
-; CHECK: OpLoad {{%\w+}} {{%\w+}} Volatile|MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
-; CHECK: OpStore {{%\w+}} {{%\w+}} Volatile|MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]]
+; CHECK: OpLoad {{%\w+}} {{%\w+}} Volatile|MakePointerVisible|NonPrivatePointer [[scope]]
+; CHECK: OpStore {{%\w+}} {{%\w+}} Volatile|MakePointerAvailable|NonPrivatePointer [[scope]]
 OpCapability Shader
 OpCapability Linkage
 OpMemoryModel Logical GLSL450
@@ -208,8 +208,8 @@
   const std::string text = R"(
 ; CHECK-NOT: OpDecorate
 ; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
-; CHECK: OpLoad {{%\w+}} {{%\w+}} Volatile|MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
-; CHECK: OpStore {{%\w+}} {{%\w+}} Volatile|MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]]
+; CHECK: OpLoad {{%\w+}} {{%\w+}} Volatile|MakePointerVisible|NonPrivatePointer [[scope]]
+; CHECK: OpStore {{%\w+}} {{%\w+}} Volatile|MakePointerAvailable|NonPrivatePointer [[scope]]
 OpCapability Shader
 OpCapability Linkage
 OpMemoryModel Logical GLSL450
@@ -236,8 +236,8 @@
   const std::string text = R"(
 ; CHECK-NOT: OpDecorate
 ; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
-; CHECK: OpLoad {{%\w+}} {{%\w+}} Volatile|MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
-; CHECK: OpStore {{%\w+}} {{%\w+}} Volatile|MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]]
+; CHECK: OpLoad {{%\w+}} {{%\w+}} Volatile|MakePointerVisible|NonPrivatePointer [[scope]]
+; CHECK: OpStore {{%\w+}} {{%\w+}} Volatile|MakePointerAvailable|NonPrivatePointer [[scope]]
 OpCapability Shader
 OpCapability Linkage
 OpMemoryModel Logical GLSL450
@@ -265,8 +265,8 @@
   const std::string text = R"(
 ; CHECK-NOT: OpDecorate
 ; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
-; CHECK: OpLoad {{%\w+}} {{%\w+}} Volatile|MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
-; CHECK: OpStore {{%\w+}} {{%\w+}} Volatile|MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]]
+; CHECK: OpLoad {{%\w+}} {{%\w+}} Volatile|MakePointerVisible|NonPrivatePointer [[scope]]
+; CHECK: OpStore {{%\w+}} {{%\w+}} Volatile|MakePointerAvailable|NonPrivatePointer [[scope]]
 OpCapability Shader
 OpCapability Linkage
 OpMemoryModel Logical GLSL450
@@ -297,8 +297,8 @@
   const std::string text = R"(
 ; CHECK-NOT: OpDecorate
 ; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
-; CHECK: OpLoad {{%\w+}} {{%\w+}} Volatile|MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
-; CHECK: OpStore {{%\w+}} {{%\w+}} Volatile|MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]]
+; CHECK: OpLoad {{%\w+}} {{%\w+}} Volatile|MakePointerVisible|NonPrivatePointer [[scope]]
+; CHECK: OpStore {{%\w+}} {{%\w+}} Volatile|MakePointerAvailable|NonPrivatePointer [[scope]]
 OpCapability Shader
 OpCapability Linkage
 OpMemoryModel Logical GLSL450
@@ -330,8 +330,8 @@
   const std::string text = R"(
 ; CHECK-NOT: OpDecorate
 ; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
-; CHECK: OpLoad {{%\w+}} {{%\w+}} Volatile|MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
-; CHECK: OpStore {{%\w+}} {{%\w+}} Volatile|MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]]
+; CHECK: OpLoad {{%\w+}} {{%\w+}} Volatile|MakePointerVisible|NonPrivatePointer [[scope]]
+; CHECK: OpStore {{%\w+}} {{%\w+}} Volatile|MakePointerAvailable|NonPrivatePointer [[scope]]
 OpCapability Shader
 OpCapability Linkage
 OpCapability VariablePointers
@@ -363,8 +363,8 @@
   const std::string text = R"(
 ; CHECK-NOT: OpDecorate
 ; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
-; CHECK: OpLoad {{%\w+}} {{%\w+}} Volatile|MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
-; CHECK: OpStore {{%\w+}} {{%\w+}} Volatile|MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]]
+; CHECK: OpLoad {{%\w+}} {{%\w+}} Volatile|MakePointerVisible|NonPrivatePointer [[scope]]
+; CHECK: OpStore {{%\w+}} {{%\w+}} Volatile|MakePointerAvailable|NonPrivatePointer [[scope]]
 OpCapability Shader
 OpCapability Linkage
 OpCapability VariablePointers
@@ -396,8 +396,8 @@
   const std::string text = R"(
 ; CHECK-NOT: OpDecorate {{%\w+}} Coherent
 ; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
-; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
-; CHECK: OpStore {{%\w+}} {{%\w+}} MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]]
+; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerVisible|NonPrivatePointer [[scope]]
+; CHECK: OpStore {{%\w+}} {{%\w+}} MakePointerAvailable|NonPrivatePointer [[scope]]
 OpCapability Shader
 OpCapability Linkage
 OpCapability VariablePointers
@@ -439,8 +439,8 @@
   const std::string text = R"(
 ; CHECK-NOT: OpMemberDecorate
 ; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
-; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
-; CHECK: OpStore {{%\w+}} {{%\w+}} MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]]
+; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerVisible|NonPrivatePointer [[scope]]
+; CHECK: OpStore {{%\w+}} {{%\w+}} MakePointerAvailable|NonPrivatePointer [[scope]]
 OpCapability Shader
 OpCapability Linkage
 OpExtension "SPV_KHR_storage_buffer_storage_class"
@@ -470,8 +470,8 @@
   const std::string text = R"(
 ; CHECK-NOT: OpMemberDecorate
 ; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
-; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
-; CHECK: OpStore {{%\w+}} {{%\w+}} MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]]
+; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerVisible|NonPrivatePointer [[scope]]
+; CHECK: OpStore {{%\w+}} {{%\w+}} MakePointerAvailable|NonPrivatePointer [[scope]]
 OpCapability Shader
 OpCapability Linkage
 OpExtension "SPV_KHR_storage_buffer_storage_class"
@@ -497,9 +497,9 @@
 TEST_F(UpgradeMemoryModelTest, CoherentElementNotAccessed) {
   const std::string text = R"(
 ; CHECK-NOT: OpMemberDecorate
-; CHECK-NOT: MakePointerAvailableKHR
-; CHECK-NOT: NonPrivatePointerKHR
-; CHECK-NOT: MakePointerVisibleKHR
+; CHECK-NOT: MakePointerAvailable
+; CHECK-NOT: NonPrivatePointer
+; CHECK-NOT: MakePointerVisible
 OpCapability Shader
 OpCapability Linkage
 OpExtension "SPV_KHR_storage_buffer_storage_class"
@@ -529,8 +529,8 @@
   const std::string text = R"(
 ; CHECK-NOT: OpMemberDecorate
 ; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
-; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
-; CHECK: OpStore {{%\w+}} {{%\w+}} MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]]
+; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerVisible|NonPrivatePointer [[scope]]
+; CHECK: OpStore {{%\w+}} {{%\w+}} MakePointerAvailable|NonPrivatePointer [[scope]]
 OpCapability Shader
 OpCapability Linkage
 OpExtension "SPV_KHR_storage_buffer_storage_class"
@@ -563,9 +563,9 @@
 TEST_F(UpgradeMemoryModelTest, MultiIndexAccessNonCoherent) {
   const std::string text = R"(
 ; CHECK-NOT: OpMemberDecorate
-; CHECK-NOT: MakePointerAvailableKHR
-; CHECK-NOT: NonPrivatePointerKHR
-; CHECK-NOT: MakePointerVisibleKHR
+; CHECK-NOT: MakePointerAvailable
+; CHECK-NOT: NonPrivatePointer
+; CHECK-NOT: MakePointerVisible
 OpCapability Shader
 OpCapability Linkage
 OpExtension "SPV_KHR_storage_buffer_storage_class"
@@ -599,8 +599,8 @@
   const std::string text = R"(
 ; CHECK-NOT: OpMemberDecorate
 ; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
-; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
-; CHECK: OpStore {{%\w+}} {{%\w+}} MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]]
+; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerVisible|NonPrivatePointer [[scope]]
+; CHECK: OpStore {{%\w+}} {{%\w+}} MakePointerAvailable|NonPrivatePointer [[scope]]
 OpCapability Shader
 OpCapability Linkage
 OpExtension "SPV_KHR_storage_buffer_storage_class"
@@ -639,9 +639,9 @@
 TEST_F(UpgradeMemoryModelTest, ConsecutiveAccessChainNonCoherent) {
   const std::string text = R"(
 ; CHECK-NOT: OpMemberDecorate
-; CHECK-NOT: MakePointerAvailableKHR
-; CHECK-NOT: NonPrivatePointerKHR
-; CHECK-NOT: MakePointerVisibleKHR
+; CHECK-NOT: MakePointerAvailable
+; CHECK-NOT: NonPrivatePointer
+; CHECK-NOT: MakePointerVisible
 OpCapability Shader
 OpCapability Linkage
 OpExtension "SPV_KHR_storage_buffer_storage_class"
@@ -681,8 +681,8 @@
   const std::string text = R"(
 ; CHECK-NOT: OpMemberDecorate
 ; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
-; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
-; CHECK: OpStore {{%\w+}} {{%\w+}} MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]]
+; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerVisible|NonPrivatePointer [[scope]]
+; CHECK: OpStore {{%\w+}} {{%\w+}} MakePointerAvailable|NonPrivatePointer [[scope]]
 OpCapability Shader
 OpCapability Linkage
 OpExtension "SPV_KHR_storage_buffer_storage_class"
@@ -722,8 +722,8 @@
   const std::string text = R"(
 ; CHECK-NOT: OpMemberDecorate
 ; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
-; CHECK-NOT: MakePointerVisibleKHR
-; CHECK: OpStore {{%\w+}} {{%\w+}} MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]]
+; CHECK-NOT: MakePointerVisible
+; CHECK: OpStore {{%\w+}} {{%\w+}} MakePointerAvailable|NonPrivatePointer [[scope]]
 OpCapability Shader
 OpCapability Linkage
 OpExtension "SPV_KHR_storage_buffer_storage_class"
@@ -763,7 +763,7 @@
   const std::string text = R"(
 ; CHECK-NOT: OpDecorate
 ; CHECK: [[queuefamily:%\w+]] = OpConstant {{%\w+}} 5
-; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} Volatile|MakePointerVisibleKHR|NonPrivatePointerKHR [[queuefamily]]
+; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} Volatile|MakePointerVisible|NonPrivatePointer [[queuefamily]]
 ; CHECK-NOT: [[queuefamily]]
 OpCapability Shader
 OpCapability Linkage
@@ -791,7 +791,7 @@
   const std::string text = R"(
 ; CHECK-NOT: OpDecorate
 ; CHECK: [[queuefamily:%\w+]] = OpConstant {{%\w+}} 5
-; CHECK: OpCopyMemorySized {{%\w+}} {{%\w+}} {{%\w+}} Volatile|MakePointerAvailableKHR|NonPrivatePointerKHR [[queuefamily]]
+; CHECK: OpCopyMemorySized {{%\w+}} {{%\w+}} {{%\w+}} Volatile|MakePointerAvailable|NonPrivatePointer [[queuefamily]]
 ; CHECK-NOT: [[queuefamily]]
 OpCapability Shader
 OpCapability Linkage
@@ -822,7 +822,7 @@
 ; CHECK-NOT: OpDecorate
 ; CHECK-DAG: [[queuefamily:%\w+]] = OpConstant {{%\w+}} 5
 ; CHECK-DAG: [[workgroup:%\w+]] = OpConstant {{%\w+}} 2
-; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} MakePointerAvailableKHR|MakePointerVisibleKHR|NonPrivatePointerKHR [[workgroup]] [[queuefamily]]
+; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} MakePointerAvailable|MakePointerVisible|NonPrivatePointer [[workgroup]] [[queuefamily]]
 OpCapability Shader
 OpCapability Linkage
 OpExtension "SPV_KHR_storage_buffer_storage_class"
@@ -850,7 +850,7 @@
   const std::string text = R"(
 ; CHECK-NOT: OpDecorate
 ; CHECK: OpLoad {{%\w+}} {{%\w+}} Volatile
-; CHECK: OpImageRead {{%\w+}} {{%\w+}} {{%\w+}} VolatileTexelKHR
+; CHECK: OpImageRead {{%\w+}} {{%\w+}} {{%\w+}} VolatileTexel
 OpCapability Shader
 OpCapability Linkage
 OpCapability StorageImageReadWithoutFormat
@@ -882,8 +882,8 @@
   const std::string text = R"(
 ; CHECK-NOT: OpDecorate
 ; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
-; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
-; CHECK: OpImageRead {{%\w+}} {{%\w+}} {{%\w+}} MakeTexelVisibleKHR|NonPrivateTexelKHR [[scope]] 
+; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerVisible|NonPrivatePointer [[scope]]
+; CHECK: OpImageRead {{%\w+}} {{%\w+}} {{%\w+}} MakeTexelVisible|NonPrivateTexel [[scope]]
 OpCapability Shader
 OpCapability Linkage
 OpCapability StorageImageReadWithoutFormat
@@ -916,9 +916,9 @@
 ; CHECK-NOT: OpDecorate
 ; CHECK: [[image:%\w+]] = OpTypeImage
 ; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
-; CHECK: OpLoad [[image]] {{%\w+}} MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
-; CHECK-NOT: NonPrivatePointerKHR
-; CHECK: OpImageRead {{%\w+}} {{%\w+}} {{%\w+}} MakeTexelVisibleKHR|NonPrivateTexelKHR [[scope]]
+; CHECK: OpLoad [[image]] {{%\w+}} MakePointerVisible|NonPrivatePointer [[scope]]
+; CHECK-NOT: NonPrivatePointer
+; CHECK: OpImageRead {{%\w+}} {{%\w+}} {{%\w+}} MakeTexelVisible|NonPrivateTexel [[scope]]
 OpCapability Shader
 OpCapability Linkage
 OpCapability StorageImageReadWithoutFormat
@@ -957,7 +957,7 @@
   const std::string text = R"(
 ; CHECK-NOT: OpDecorate
 ; CHECK: OpLoad {{%\w+}} {{%\w+}} Volatile
-; CHECK: OpImageWrite {{%\w+}} {{%\w+}} {{%\w+}} VolatileTexelKHR
+; CHECK: OpImageWrite {{%\w+}} {{%\w+}} {{%\w+}} VolatileTexel
 OpCapability Shader
 OpCapability Linkage
 OpCapability StorageImageWriteWithoutFormat
@@ -989,8 +989,8 @@
   const std::string text = R"(
 ; CHECK-NOT: OpDecorate
 ; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
-; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerVisibleKHR|NonPrivatePointerKHR
-; CHECK: OpImageWrite {{%\w+}} {{%\w+}} {{%\w+}} MakeTexelAvailableKHR|NonPrivateTexelKHR [[scope]]
+; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerVisible|NonPrivatePointer
+; CHECK: OpImageWrite {{%\w+}} {{%\w+}} {{%\w+}} MakeTexelAvailable|NonPrivateTexel [[scope]]
 OpCapability Shader
 OpCapability Linkage
 OpCapability StorageImageWriteWithoutFormat
@@ -1022,9 +1022,9 @@
   const std::string text = R"(
 ; CHECK-NOT: OpDecorate
 ; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
-; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerVisibleKHR|NonPrivatePointerKHR
-; CHECK-NOT: NonPrivatePointerKHR
-; CHECK: OpImageWrite {{%\w+}} {{%\w+}} {{%\w+}} MakeTexelAvailableKHR|NonPrivateTexelKHR [[scope]]
+; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerVisible|NonPrivatePointer
+; CHECK-NOT: NonPrivatePointer
+; CHECK: OpImageWrite {{%\w+}} {{%\w+}} {{%\w+}} MakeTexelAvailable|NonPrivateTexel [[scope]]
 OpCapability Shader
 OpCapability Linkage
 OpCapability StorageImageWriteWithoutFormat
@@ -1063,7 +1063,7 @@
   const std::string text = R"(
 ; CHECK-NOT: OpDecorate
 ; CHECK: OpLoad {{%\w+}} {{%\w+}} Volatile
-; CHECK: OpImageSparseRead {{%\w+}} {{%\w+}} {{%\w+}} VolatileTexelKHR
+; CHECK: OpImageSparseRead {{%\w+}} {{%\w+}} {{%\w+}} VolatileTexel
 OpCapability Shader
 OpCapability Linkage
 OpCapability StorageImageReadWithoutFormat
@@ -1097,8 +1097,8 @@
   const std::string text = R"(
 ; CHECK-NOT: OpDecorate
 ; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
-; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
-; CHECK: OpImageSparseRead {{%\w+}} {{%\w+}} {{%\w+}} MakeTexelVisibleKHR|NonPrivateTexelKHR [[scope]] 
+; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerVisible|NonPrivatePointer [[scope]]
+; CHECK: OpImageSparseRead {{%\w+}} {{%\w+}} {{%\w+}} MakeTexelVisible|NonPrivateTexel [[scope]]
 OpCapability Shader
 OpCapability Linkage
 OpCapability StorageImageReadWithoutFormat
@@ -1134,9 +1134,9 @@
 ; CHECK-NOT: OpDecorate
 ; CHECK: [[image:%\w+]] = OpTypeImage
 ; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
-; CHECK: OpLoad [[image]] {{%\w+}} MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
-; CHECK-NOT: NonPrivatePointerKHR
-; CHECK: OpImageSparseRead {{%\w+}} {{%\w+}} {{%\w+}} MakeTexelVisibleKHR|NonPrivateTexelKHR [[scope]]
+; CHECK: OpLoad [[image]] {{%\w+}} MakePointerVisible|NonPrivatePointer [[scope]]
+; CHECK-NOT: NonPrivatePointer
+; CHECK: OpImageSparseRead {{%\w+}} {{%\w+}} {{%\w+}} MakeTexelVisible|NonPrivateTexel [[scope]]
 OpCapability Shader
 OpCapability Linkage
 OpCapability StorageImageReadWithoutFormat
@@ -1440,7 +1440,7 @@
 ; CHECK: [[ex0:%\w+]] = OpCompositeExtract [[float]] [[modfstruct]] 0
 ; CHECK: [[ex1:%\w+]] = OpCompositeExtract [[float]] [[modfstruct]] 1
 ; CHECK: OpStore [[var]] [[ex1]]
-; CHECK-NOT: NonPrivatePointerKHR
+; CHECK-NOT: NonPrivatePointer
 ; CHECK: OpFAdd [[float]] [[float_0]] [[ex0]]
 OpCapability Shader
 OpMemoryModel Logical GLSL450
@@ -1474,7 +1474,7 @@
 ; CHECK: [[modfstruct:%\w+]] = OpExtInst [[struct]] {{%\w+}} ModfStruct [[float_0]]
 ; CHECK: [[ex0:%\w+]] = OpCompositeExtract [[float]] [[modfstruct]] 0
 ; CHECK: [[ex1:%\w+]] = OpCompositeExtract [[float]] [[modfstruct]] 1
-; CHECK: OpStore [[var]] [[ex1]] MakePointerAvailableKHR|NonPrivatePointerKHR [[wg_scope]]
+; CHECK: OpStore [[var]] [[ex1]] MakePointerAvailable|NonPrivatePointer [[wg_scope]]
 ; CHECK: OpFAdd [[float]] [[float_0]] [[ex0]]
 OpCapability Shader
 OpMemoryModel Logical GLSL450
@@ -1509,7 +1509,7 @@
 ; CHECK: [[modfstruct:%\w+]] = OpExtInst [[struct]] {{%\w+}} ModfStruct [[float_0]]
 ; CHECK: [[ex0:%\w+]] = OpCompositeExtract [[float]] [[modfstruct]] 0
 ; CHECK: [[ex1:%\w+]] = OpCompositeExtract [[float]] [[modfstruct]] 1
-; CHECK: OpStore [[var]] [[ex1]] MakePointerAvailableKHR|NonPrivatePointerKHR [[qf_scope]]
+; CHECK: OpStore [[var]] [[ex1]] MakePointerAvailable|NonPrivatePointer [[qf_scope]]
 ; CHECK: OpFAdd [[float]] [[float_0]] [[ex0]]
 OpCapability Shader
 OpMemoryModel Logical GLSL450
@@ -1579,7 +1579,7 @@
 ; CHECK: [[ex0:%\w+]] = OpCompositeExtract [[float]] [[modfstruct]] 0
 ; CHECK: [[ex1:%\w+]] = OpCompositeExtract [[int]] [[modfstruct]] 1
 ; CHECK: OpStore [[var]] [[ex1]]
-; CHECK-NOT: NonPrivatePointerKHR
+; CHECK-NOT: NonPrivatePointer
 ; CHECK: OpFAdd [[float]] [[float_0]] [[ex0]]
 OpCapability Shader
 OpMemoryModel Logical GLSL450
@@ -1615,7 +1615,7 @@
 ; CHECK: [[modfstruct:%\w+]] = OpExtInst [[struct]] {{%\w+}} FrexpStruct [[float_0]]
 ; CHECK: [[ex0:%\w+]] = OpCompositeExtract [[float]] [[modfstruct]] 0
 ; CHECK: [[ex1:%\w+]] = OpCompositeExtract [[int]] [[modfstruct]] 1
-; CHECK: OpStore [[var]] [[ex1]] MakePointerAvailableKHR|NonPrivatePointerKHR [[wg_scope]]
+; CHECK: OpStore [[var]] [[ex1]] MakePointerAvailable|NonPrivatePointer [[wg_scope]]
 ; CHECK: OpFAdd [[float]] [[float_0]] [[ex0]]
 OpCapability Shader
 OpMemoryModel Logical GLSL450
@@ -1652,7 +1652,7 @@
 ; CHECK: [[modfstruct:%\w+]] = OpExtInst [[struct]] {{%\w+}} FrexpStruct [[float_0]]
 ; CHECK: [[ex0:%\w+]] = OpCompositeExtract [[float]] [[modfstruct]] 0
 ; CHECK: [[ex1:%\w+]] = OpCompositeExtract [[int]] [[modfstruct]] 1
-; CHECK: OpStore [[var]] [[ex1]] MakePointerAvailableKHR|NonPrivatePointerKHR [[qf_scope]]
+; CHECK: OpStore [[var]] [[ex1]] MakePointerAvailable|NonPrivatePointer [[qf_scope]]
 ; CHECK: OpFAdd [[float]] [[float_0]] [[ex0]]
 OpCapability Shader
 OpMemoryModel Logical GLSL450
@@ -1785,7 +1785,7 @@
 TEST_F(UpgradeMemoryModelTest, SPV14CopyMemoryDstCoherent) {
   const std::string text = R"(
 ; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
-; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]] None
+; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} MakePointerAvailable|NonPrivatePointer [[scope]] None
 OpCapability Shader
 OpMemoryModel Logical GLSL450
 OpEntryPoint GLCompute %func "func" %src %dst
@@ -1810,7 +1810,7 @@
 TEST_F(UpgradeMemoryModelTest, SPV14CopyMemoryDstCoherentPreviousArgs) {
   const std::string text = R"(
 ; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
-; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} Aligned|MakePointerAvailableKHR|NonPrivatePointerKHR 4 [[scope]] Aligned 4
+; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} Aligned|MakePointerAvailable|NonPrivatePointer 4 [[scope]] Aligned 4
 OpCapability Shader
 OpMemoryModel Logical GLSL450
 OpEntryPoint GLCompute %func "func" %src %dst
@@ -1835,7 +1835,7 @@
 TEST_F(UpgradeMemoryModelTest, SPV14CopyMemorySrcCoherent) {
   const std::string text = R"(
 ; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
-; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} None MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
+; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} None MakePointerVisible|NonPrivatePointer [[scope]]
 OpCapability Shader
 OpMemoryModel Logical GLSL450
 OpEntryPoint GLCompute %func "func" %src %dst
@@ -1860,7 +1860,7 @@
 TEST_F(UpgradeMemoryModelTest, SPV14CopyMemorySrcCoherentPreviousArgs) {
   const std::string text = R"(
 ; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
-; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} Aligned 4 Aligned|MakePointerVisibleKHR|NonPrivatePointerKHR 4 [[scope]]
+; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} Aligned 4 Aligned|MakePointerVisible|NonPrivatePointer 4 [[scope]]
 OpCapability Shader
 OpMemoryModel Logical GLSL450
 OpEntryPoint GLCompute %func "func" %src %dst
@@ -1886,7 +1886,7 @@
   const std::string text = R"(
 ; CHECK-DAG: [[queue:%\w+]] = OpConstant {{%\w+}} 5
 ; CHECK-DAG: [[wg:%\w+]] = OpConstant {{%\w+}} 2
-; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} MakePointerAvailableKHR|NonPrivatePointerKHR [[wg]] MakePointerVisibleKHR|NonPrivatePointerKHR [[queue]]
+; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} MakePointerAvailable|NonPrivatePointer [[wg]] MakePointerVisible|NonPrivatePointer [[queue]]
 OpCapability Shader
 OpMemoryModel Logical GLSL450
 OpEntryPoint GLCompute %func "func" %src %dst
@@ -1913,7 +1913,7 @@
   const std::string text = R"(
 ; CHECK-DAG: [[queue:%\w+]] = OpConstant {{%\w+}} 5
 ; CHECK-DAG: [[wg:%\w+]] = OpConstant {{%\w+}} 2
-; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} Aligned|MakePointerAvailableKHR|NonPrivatePointerKHR 4 [[queue]] Aligned|MakePointerVisibleKHR|NonPrivatePointerKHR 4 [[wg]]
+; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} Aligned|MakePointerAvailable|NonPrivatePointer 4 [[queue]] Aligned|MakePointerVisible|NonPrivatePointer 4 [[wg]]
 OpCapability Shader
 OpMemoryModel Logical GLSL450
 OpEntryPoint GLCompute %func "func" %src %dst
@@ -1989,7 +1989,7 @@
 TEST_F(UpgradeMemoryModelTest, SPV14CopyMemoryDstCoherentTwoOperands) {
   const std::string text = R"(
 ; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
-; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]] None
+; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} MakePointerAvailable|NonPrivatePointer [[scope]] None
 OpCapability Shader
 OpMemoryModel Logical GLSL450
 OpEntryPoint GLCompute %func "func" %src %dst
@@ -2015,7 +2015,7 @@
        SPV14CopyMemoryDstCoherentPreviousArgsTwoOperands) {
   const std::string text = R"(
 ; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
-; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} Aligned|MakePointerAvailableKHR|NonPrivatePointerKHR 4 [[scope]] Aligned 8
+; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} Aligned|MakePointerAvailable|NonPrivatePointer 4 [[scope]] Aligned 8
 OpCapability Shader
 OpMemoryModel Logical GLSL450
 OpEntryPoint GLCompute %func "func" %src %dst
@@ -2236,4 +2236,37 @@
   SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
 }
 
+TEST_F(UpgradeMemoryModelTest, CoherentStructMemberInArray) {
+  const std::string text = R"(
+; CHECK-NOT: OpMemberDecorate
+; CHECK: [[int:%[a-zA-Z0-9_]+]] = OpTypeInt 32 0
+; CHECK: [[device:%[a-zA-Z0-9_]+]] = OpConstant [[int]] 1
+; CHECK: OpLoad [[int]] {{.*}} MakePointerVisible|NonPrivatePointer
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpMemberDecorate %inner 1 Coherent
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%int_4 = OpConstant %int 4
+%inner = OpTypeStruct %int %int
+%array = OpTypeArray %inner %int_4
+%struct = OpTypeStruct %array
+%ptr_ssbo_struct = OpTypePointer StorageBuffer %struct
+%ptr_ssbo_int = OpTypePointer StorageBuffer %int
+%ssbo_var = OpVariable %ptr_ssbo_struct StorageBuffer
+%void_fn = OpTypeFunction %void
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+%gep = OpAccessChain %ptr_ssbo_int %ssbo_var %int_0 %int_0 %int_1
+%ld = OpLoad %int %gep
+OpReturn
+OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
+}
+
 }  // namespace
diff --git a/test/opt/vector_dce_test.cpp b/test/opt/vector_dce_test.cpp
index a978a07..594995c 100644
--- a/test/opt/vector_dce_test.cpp
+++ b/test/opt/vector_dce_test.cpp
@@ -1158,74 +1158,6 @@
   SinglePassRunAndCheck<VectorDCE>(text, text, true, true);
 }
 
-TEST_F(VectorDCETest, InsertWithNoIndices) {
-  const std::string text = R"(
-; CHECK: OpEntryPoint Fragment {{%\w+}} "PSMain" [[in1:%\w+]] [[in2:%\w+]] [[out:%\w+]]
-; CHECK: OpFunction
-; CHECK: [[ld:%\w+]] = OpLoad %v4float [[in2]]
-; CHECK: OpStore [[out]] [[ld]]
-               OpCapability Shader
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %1 "PSMain" %2 %14 %3
-               OpExecutionMode %1 OriginUpperLeft
-       %void = OpTypeVoid
-          %5 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v4float = OpTypeVector %float 4
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-          %2 = OpVariable %_ptr_Input_v4float Input
-          %14 = OpVariable %_ptr_Input_v4float Input
-          %3 = OpVariable %_ptr_Output_v4float Output
-          %1 = OpFunction %void None %5
-         %10 = OpLabel
-         %13 = OpLoad %v4float %14
-         %11 = OpLoad %v4float %2
-         %12 = OpCompositeInsert %v4float %13 %11
-               OpStore %3 %12
-               OpReturn
-               OpFunctionEnd
-)";
-
-  SinglePassRunAndMatch<VectorDCE>(text, true);
-}
-
-TEST_F(VectorDCETest, ExtractWithNoIndices) {
-  const std::string text = R"(
-; CHECK: OpLoad %float
-; CHECK: [[ld:%\w+]] = OpLoad %v4float
-; CHECK: [[ex1:%\w+]] = OpCompositeExtract %v4float [[ld]]
-; CHECK: [[ex2:%\w+]] = OpCompositeExtract %float [[ex1]] 1
-; CHECK: OpStore {{%\w+}} [[ex2]]
-               OpCapability Shader
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %1 "PSMain" %2 %14 %3
-               OpExecutionMode %1 OriginUpperLeft
-       %void = OpTypeVoid
-          %5 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v4float = OpTypeVector %float 4
-%_ptr_Input_float = OpTypePointer Input %float
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%_ptr_Output_float = OpTypePointer Output %float
-          %2 = OpVariable %_ptr_Input_v4float Input
-          %14 = OpVariable %_ptr_Input_float Input
-          %3 = OpVariable %_ptr_Output_float Output
-          %1 = OpFunction %void None %5
-         %10 = OpLabel
-         %13 = OpLoad %float %14
-         %11 = OpLoad %v4float %2
-         %12 = OpCompositeInsert %v4float %13 %11 0
-         %20 = OpCompositeExtract %v4float %12
-         %21 = OpCompositeExtract %float %20 1
-               OpStore %3 %21
-               OpReturn
-               OpFunctionEnd
-)";
-
-  SinglePassRunAndMatch<VectorDCE>(text, true);
-}
-
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools
diff --git a/test/opt/wrap_opkill_test.cpp b/test/opt/wrap_opkill_test.cpp
index df1b865..d50af28 100644
--- a/test/opt/wrap_opkill_test.cpp
+++ b/test/opt/wrap_opkill_test.cpp
@@ -31,7 +31,7 @@
 ; CHECK: [[orig_kill]] = OpFunction
 ; CHECK-NEXT: OpLabel
 ; CHECK-NEXT: OpFunctionCall %void [[new_kill:%\w+]]
-; CHECK-NEXT: OpUnreachable
+; CHECK-NEXT: OpReturn
 ; CHECK: [[new_kill]] = OpFunction
 ; CHECK-NEXT: OpLabel
 ; CHECK-NEXT: OpKill
@@ -83,10 +83,10 @@
 ; CHECK-NEXT: OpBranchConditional
 ; CHECK-NEXT: OpLabel
 ; CHECK-NEXT: OpFunctionCall %void [[new_kill:%\w+]]
-; CHECK-NEXT: OpUnreachable
+; CHECK-NEXT: OpReturn
 ; CHECK-NEXT: OpLabel
 ; CHECK-NEXT: OpFunctionCall %void [[new_kill]]
-; CHECK-NEXT: OpUnreachable
+; CHECK-NEXT: OpReturn
 ; CHECK: [[new_kill]] = OpFunction
 ; CHECK-NEXT: OpLabel
 ; CHECK-NEXT: OpKill
@@ -143,11 +143,11 @@
 ; CHECK: [[orig_kill1]] = OpFunction
 ; CHECK-NEXT: OpLabel
 ; CHECK-NEXT: OpFunctionCall %void [[new_kill:%\w+]]
-; CHECK-NEXT: OpUnreachable
+; CHECK-NEXT: OpReturn
 ; CHECK: [[orig_kill2]] = OpFunction
 ; CHECK-NEXT: OpLabel
 ; CHECK-NEXT: OpFunctionCall %void [[new_kill]]
-; CHECK-NEXT: OpUnreachable
+; CHECK-NEXT: OpReturn
 ; CHECK: [[new_kill]] = OpFunction
 ; CHECK-NEXT: OpLabel
 ; CHECK-NEXT: OpKill
@@ -193,19 +193,89 @@
   SinglePassRunAndMatch<WrapOpKill>(text, true);
 }
 
+TEST_F(WrapOpKillTest, FuncWithReturnValue) {
+  const std::string text = R"(
+; CHECK: OpEntryPoint Fragment [[main:%\w+]]
+; CHECK: [[main]] = OpFunction
+; CHECK: OpFunctionCall %int [[orig_kill:%\w+]]
+; CHECK: [[orig_kill]] = OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpFunctionCall %void [[new_kill:%\w+]]
+; CHECK-NEXT: [[undef:%\w+]] = OpUndef %int
+; CHECK-NEXT: OpReturnValue [[undef]]
+; CHECK: [[new_kill]] = OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpKill
+; CHECK-NEXT: OpFunctionEnd
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpSource GLSL 330
+               OpName %main "main"
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+        %int = OpTypeInt 32 1
+  %func_type = OpTypeFunction %int
+       %bool = OpTypeBool
+       %true = OpConstantTrue %bool
+       %main = OpFunction %void None %5
+          %8 = OpLabel
+               OpBranch %9
+          %9 = OpLabel
+               OpLoopMerge %10 %11 None
+               OpBranch %12
+         %12 = OpLabel
+               OpBranchConditional %true %13 %10
+         %13 = OpLabel
+               OpBranch %11
+         %11 = OpLabel
+         %14 = OpFunctionCall %int %kill_
+               OpBranch %9
+         %10 = OpLabel
+               OpReturn
+               OpFunctionEnd
+      %kill_ = OpFunction %int None %func_type
+         %15 = OpLabel
+               OpKill
+               OpFunctionEnd
+  )";
+
+  SinglePassRunAndMatch<WrapOpKill>(text, true);
+}
+
 TEST_F(WrapOpKillTest, IdBoundOverflow1) {
   const std::string text = R"(
 OpCapability GeometryStreams
 OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %4 "main"
-OpExecutionMode %4 OriginUpperLeft
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
 %2 = OpTypeVoid
 %3 = OpTypeFunction %2
-%4 = OpFunction %2 Pure|Const %3
+%bool = OpTypeBool
+%true = OpConstantTrue %bool
+%main = OpFunction %2 None %3
+%8 = OpLabel
+OpBranch %9
+%9 = OpLabel
+OpLoopMerge %10 %11 None
+OpBranch %12
+%12 = OpLabel
+OpBranchConditional %true %13 %10
+%13 = OpLabel
+OpBranch %11
+%11 = OpLabel
+%14 = OpFunctionCall %void %kill_
+OpBranch %9
+%10 = OpLabel
+OpReturn
+OpFunctionEnd
+%kill_ = OpFunction %2 Pure|Const %3
 %4194302 = OpLabel
 OpKill
 OpFunctionEnd
-  )";
+)";
 
   SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
 
@@ -220,15 +290,33 @@
   const std::string text = R"(
 OpCapability GeometryStreams
 OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %4 "main"
-OpExecutionMode %4 OriginUpperLeft
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
 %2 = OpTypeVoid
 %3 = OpTypeFunction %2
-%4 = OpFunction %2 Pure|Const %3
+%bool = OpTypeBool
+%true = OpConstantTrue %bool
+%main = OpFunction %2 None %3
+%8 = OpLabel
+OpBranch %9
+%9 = OpLabel
+OpLoopMerge %10 %11 None
+OpBranch %12
+%12 = OpLabel
+OpBranchConditional %true %13 %10
+%13 = OpLabel
+OpBranch %11
+%11 = OpLabel
+%14 = OpFunctionCall %void %kill_
+OpBranch %9
+%10 = OpLabel
+OpReturn
+OpFunctionEnd
+%kill_ = OpFunction %2 Pure|Const %3
 %4194301 = OpLabel
 OpKill
 OpFunctionEnd
-  )";
+)";
 
   SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
 
@@ -243,15 +331,33 @@
   const std::string text = R"(
 OpCapability GeometryStreams
 OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %4 "main"
-OpExecutionMode %4 OriginUpperLeft
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
 %2 = OpTypeVoid
 %3 = OpTypeFunction %2
-%4 = OpFunction %2 Pure|Const %3
+%bool = OpTypeBool
+%true = OpConstantTrue %bool
+%main = OpFunction %2 None %3
+%8 = OpLabel
+OpBranch %9
+%9 = OpLabel
+OpLoopMerge %10 %11 None
+OpBranch %12
+%12 = OpLabel
+OpBranchConditional %true %13 %10
+%13 = OpLabel
+OpBranch %11
+%11 = OpLabel
+%14 = OpFunctionCall %void %kill_
+OpBranch %9
+%10 = OpLabel
+OpReturn
+OpFunctionEnd
+%kill_ = OpFunction %2 Pure|Const %3
 %4194300 = OpLabel
 OpKill
 OpFunctionEnd
-  )";
+)";
 
   SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
 
@@ -262,6 +368,151 @@
   EXPECT_EQ(Pass::Status::Failure, std::get<1>(result));
 }
 
+TEST_F(WrapOpKillTest, IdBoundOverflow4) {
+  const std::string text = R"(
+OpCapability DerivativeControl
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %2 Location 539091968
+%2 = OpTypeVoid
+%3 = OpTypeFunction %2
+%bool = OpTypeBool
+%true = OpConstantTrue %bool
+%main = OpFunction %2 None %3
+%8 = OpLabel
+OpBranch %9
+%9 = OpLabel
+OpLoopMerge %10 %11 None
+OpBranch %12
+%12 = OpLabel
+OpBranchConditional %true %13 %10
+%13 = OpLabel
+OpBranch %11
+%11 = OpLabel
+%14 = OpFunctionCall %void %kill_
+OpBranch %9
+%10 = OpLabel
+OpReturn
+OpFunctionEnd
+%kill_ = OpFunction %2 Inline|Pure|Const %3
+%4194302 = OpLabel
+OpKill
+OpFunctionEnd
+)";
+
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+  std::vector<Message> messages = {
+      {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}};
+  SetMessageConsumer(GetTestMessageConsumer(messages));
+  auto result = SinglePassRunToBinary<WrapOpKill>(text, true);
+  EXPECT_EQ(Pass::Status::Failure, std::get<1>(result));
+}
+
+TEST_F(WrapOpKillTest, IdBoundOverflow5) {
+  const std::string text = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %1 "main"
+               OpExecutionMode %1 OriginUpperLeft
+               OpDecorate %void Location 539091968
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+  %_struct_5 = OpTypeStruct %float %float
+  %_struct_6 = OpTypeStruct %_struct_5
+%_ptr_Function__struct_6 = OpTypePointer Function %_struct_6
+%_ptr_Output_float = OpTypePointer Output %float
+          %9 = OpTypeFunction %_struct_5 %_ptr_Function__struct_6
+       %bool = OpTypeBool
+       %true = OpConstantTrue %bool
+          %1 = OpFunction %void None %3
+         %12 = OpLabel
+         %13 = OpVariable %_ptr_Function__struct_6 Function
+               OpBranch %14
+         %14 = OpLabel
+               OpLoopMerge %15 %16 None
+               OpBranch %17
+         %17 = OpLabel
+               OpBranchConditional %true %18 %15
+         %18 = OpLabel
+               OpBranch %16
+         %16 = OpLabel
+         %19 = OpFunctionCall %void %20
+         %21 = OpFunctionCall %_struct_5 %22 %13
+               OpBranch %14
+         %15 = OpLabel
+               OpReturn
+               OpFunctionEnd
+         %20 = OpFunction %void Inline|Pure|Const %3
+         %23 = OpLabel
+         %24 = OpVariable %_ptr_Function__struct_6 Function
+         %25 = OpFunctionCall %_struct_5 %26 %24
+               OpKill
+               OpFunctionEnd
+         %26 = OpFunction %_struct_5 None %9
+         %27 = OpLabel
+               OpUnreachable
+               OpFunctionEnd
+         %22 = OpFunction %_struct_5 Inline %9
+    %4194295 = OpLabel
+               OpKill
+               OpFunctionEnd
+)";
+
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+  std::vector<Message> messages = {
+      {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}};
+  SetMessageConsumer(GetTestMessageConsumer(messages));
+  auto result = SinglePassRunToBinary<WrapOpKill>(text, true);
+  EXPECT_EQ(Pass::Status::Failure, std::get<1>(result));
+}
+
+TEST_F(WrapOpKillTest, SkipEntryPoint) {
+  const std::string text = R"(
+OpCapability GeometryStreams
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %4 "main"
+OpExecutionMode %4 OriginUpperLeft
+%2 = OpTypeVoid
+%3 = OpTypeFunction %2
+%4 = OpFunction %2 Pure|Const %3
+%5 = OpLabel
+OpKill
+OpFunctionEnd
+)";
+
+  auto result = SinglePassRunToBinary<WrapOpKill>(text, true);
+  EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result));
+}
+
+TEST_F(WrapOpKillTest, SkipFunctionNotInContinue) {
+  const std::string text = R"(
+OpCapability GeometryStreams
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+%2 = OpTypeVoid
+%3 = OpTypeFunction %2
+%bool = OpTypeBool
+%true = OpConstantTrue %bool
+%main = OpFunction %2 None %3
+%6 = OpLabel
+%7 = OpFunctionCall %void %4
+OpReturn
+OpFunctionEnd
+%4 = OpFunction %2 Pure|Const %3
+%5 = OpLabel
+OpKill
+OpFunctionEnd
+)";
+
+  auto result = SinglePassRunToBinary<WrapOpKill>(text, true);
+  EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result));
+}
+
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools
diff --git a/test/reduce/CMakeLists.txt b/test/reduce/CMakeLists.txt
index 2d3b378..b19bba4 100644
--- a/test/reduce/CMakeLists.txt
+++ b/test/reduce/CMakeLists.txt
@@ -23,8 +23,6 @@
         reducer_test.cpp
         remove_block_test.cpp
         remove_function_test.cpp
-        remove_opname_instruction_test.cpp
-        remove_relaxed_precision_decoration_test.cpp
         remove_selection_test.cpp
         remove_unreferenced_instruction_test.cpp
         structured_loop_to_selection_test.cpp
diff --git a/test/reduce/reducer_test.cpp b/test/reduce/reducer_test.cpp
index 8787733..a650d3b 100644
--- a/test/reduce/reducer_test.cpp
+++ b/test/reduce/reducer_test.cpp
@@ -14,8 +14,8 @@
 
 #include "source/reduce/reducer.h"
 
+#include "source/opt/build_module.h"
 #include "source/reduce/operand_to_const_reduction_opportunity_finder.h"
-#include "source/reduce/remove_opname_instruction_reduction_opportunity_finder.h"
 #include "source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.h"
 #include "test/reduce/reduce_test_util.h"
 
@@ -23,6 +23,12 @@
 namespace reduce {
 namespace {
 
+using opt::BasicBlock;
+using opt::IRContext;
+
+const spv_target_env kEnv = SPV_ENV_UNIVERSAL_1_3;
+const MessageConsumer kMessageConsumer = CLIMessageConsumer;
+
 // This changes its mind each time IsInteresting is invoked as to whether the
 // binary is interesting, until some limit is reached after which the binary is
 // always deemed interesting.  This is useful to test that reduction passes
@@ -55,6 +61,8 @@
 };
 
 TEST(ReducerTest, ExprToConstantAndRemoveUnreferenced) {
+  // Check that ExprToConstant and RemoveUnreferenced work together; once some
+  // ID uses have been changed to constants, those IDs can be removed.
   std::string original = R"(
                OpCapability Shader
           %1 = OpExtInstImport "GLSL.std.450"
@@ -149,15 +157,6 @@
                OpMemoryModel Logical GLSL450
                OpEntryPoint Fragment %4 "main" %60
                OpExecutionMode %4 OriginUpperLeft
-               OpSource ESSL 310
-               OpName %4 "main"
-               OpName %16 "buf2"
-               OpMemberName %16 0 "i"
-               OpName %18 ""
-               OpName %25 "buf1"
-               OpMemberName %25 0 "f"
-               OpName %27 ""
-               OpName %60 "_GLF_color"
                OpMemberDecorate %16 0 Offset 0
                OpDecorate %16 Block
                OpDecorate %18 DescriptorSet 0
@@ -174,14 +173,12 @@
          %16 = OpTypeStruct %6
          %17 = OpTypePointer Uniform %16
          %18 = OpVariable %17 Uniform
-         %19 = OpTypePointer Uniform %6
          %22 = OpTypeBool
         %100 = OpConstantTrue %22
          %24 = OpTypeFloat 32
          %25 = OpTypeStruct %24
          %26 = OpTypePointer Uniform %25
          %27 = OpVariable %26 Uniform
-         %28 = OpTypePointer Uniform %24
          %31 = OpConstant %24 2
          %56 = OpConstant %6 1
          %58 = OpTypeVector %24 4
@@ -209,8 +206,7 @@
                OpFunctionEnd
   )";
 
-  spv_target_env env = SPV_ENV_UNIVERSAL_1_3;
-  Reducer reducer(env);
+  Reducer reducer(kEnv);
   PingPongInteresting ping_pong_interesting(10);
   reducer.SetMessageConsumer(NopDiagnostic);
   reducer.SetInterestingnessFunction(
@@ -218,12 +214,13 @@
         return ping_pong_interesting.IsInteresting(binary);
       });
   reducer.AddReductionPass(
-      MakeUnique<OperandToConstReductionOpportunityFinder>());
+      MakeUnique<RemoveUnreferencedInstructionReductionOpportunityFinder>(
+          false));
   reducer.AddReductionPass(
-      MakeUnique<RemoveUnreferencedInstructionReductionOpportunityFinder>());
+      MakeUnique<OperandToConstReductionOpportunityFinder>());
 
   std::vector<uint32_t> binary_in;
-  SpirvTools t(env);
+  SpirvTools t(kEnv);
 
   ASSERT_TRUE(t.Assemble(original, &binary_in, kReduceAssembleOption));
   std::vector<uint32_t> binary_out;
@@ -237,71 +234,169 @@
 
   ASSERT_EQ(status, Reducer::ReductionResultStatus::kComplete);
 
-  CheckEqual(env, expected, binary_out);
+  CheckEqual(kEnv, expected, binary_out);
 }
 
-TEST(ReducerTest, RemoveOpnameAndRemoveUnreferenced) {
-  const std::string original = R"(
+bool InterestingWhileOpcodeExists(const std::vector<uint32_t>& binary,
+                                  uint32_t opcode, uint32_t count, bool dump) {
+  if (dump) {
+    std::stringstream ss;
+    ss << "temp_" << count << ".spv";
+    DumpShader(binary, ss.str().c_str());
+  }
+
+  std::unique_ptr<IRContext> context =
+      BuildModule(kEnv, kMessageConsumer, binary.data(), binary.size());
+  assert(context);
+  bool interesting = false;
+  for (auto& function : *context->module()) {
+    context->cfg()->ForEachBlockInPostOrder(
+        &*function.begin(), [opcode, &interesting](BasicBlock* block) -> void {
+          for (auto& inst : *block) {
+            if (inst.opcode() == opcode) {
+              interesting = true;
+              break;
+            }
+          }
+        });
+    if (interesting) {
+      break;
+    }
+  }
+  return interesting;
+}
+
+bool InterestingWhileIMulReachable(const std::vector<uint32_t>& binary,
+                                   uint32_t count) {
+  return InterestingWhileOpcodeExists(binary, SpvOpIMul, count, false);
+}
+
+bool InterestingWhileSDivReachable(const std::vector<uint32_t>& binary,
+                                   uint32_t count) {
+  return InterestingWhileOpcodeExists(binary, SpvOpSDiv, count, false);
+}
+
+// The shader below was derived from the following GLSL, and optimized.
+// #version 310 es
+// precision highp float;
+// layout(location = 0) out vec4 _GLF_color;
+// int foo() {
+//    int x = 1;
+//    int y;
+//    x = y / x;   // SDiv
+//    return x;
+// }
+// void main() {
+//    int c;
+//    while (bool(c)) {
+//        do {
+//            if (bool(c)) {
+//                if (bool(c)) {
+//                    ++c;
+//                } else {
+//                    _GLF_color.x = float(c*c);  // IMul
+//                }
+//                return;
+//            }
+//        } while(bool(foo()));
+//        return;
+//    }
+// }
+const std::string kShaderWithLoopsDivAndMul = R"(
                OpCapability Shader
           %1 = OpExtInstImport "GLSL.std.450"
                OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %2 "main"
-               OpExecutionMode %2 OriginUpperLeft
+               OpEntryPoint Fragment %4 "main" %49
+               OpExecutionMode %4 OriginUpperLeft
                OpSource ESSL 310
-               OpName %2 "main"
-               OpName %3 "a"
-               OpName %4 "this-name-counts-as-usage-for-load-instruction"
-          %5 = OpTypeVoid
-          %6 = OpTypeFunction %5
-          %7 = OpTypeFloat 32
-          %8 = OpTypePointer Function %7
-          %9 = OpConstant %7 1
-          %2 = OpFunction %5 None %6
-         %10 = OpLabel
-          %3 = OpVariable %8 Function
-          %4 = OpLoad %7 %3
-               OpStore %3 %9
+               OpName %4 "main"
+               OpName %49 "_GLF_color"
+               OpDecorate %49 Location 0
+               OpDecorate %52 RelaxedPrecision
+               OpDecorate %77 RelaxedPrecision
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+         %12 = OpConstant %6 1
+         %27 = OpTypeBool
+         %28 = OpTypeInt 32 0
+         %29 = OpConstant %28 0
+         %46 = OpTypeFloat 32
+         %47 = OpTypeVector %46 4
+         %48 = OpTypePointer Output %47
+         %49 = OpVariable %48 Output
+         %54 = OpTypePointer Output %46
+         %64 = OpConstantFalse %27
+         %67 = OpConstantTrue %27
+         %81 = OpUndef %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %61
+         %61 = OpLabel
+               OpLoopMerge %60 %63 None
+               OpBranch %20
+         %20 = OpLabel
+         %30 = OpINotEqual %27 %81 %29
+               OpLoopMerge %22 %23 None
+               OpBranchConditional %30 %21 %22
+         %21 = OpLabel
+               OpBranch %31
+         %31 = OpLabel
+               OpLoopMerge %33 %38 None
+               OpBranch %32
+         %32 = OpLabel
+               OpSelectionMerge %38 None
+               OpBranchConditional %30 %37 %38
+         %37 = OpLabel
+               OpSelectionMerge %42 None
+               OpBranchConditional %30 %41 %45
+         %41 = OpLabel
+               OpBranch %42
+         %45 = OpLabel
+         %52 = OpIMul %6 %81 %81
+         %53 = OpConvertSToF %46 %52
+         %55 = OpAccessChain %54 %49 %29
+               OpStore %55 %53
+               OpBranch %42
+         %42 = OpLabel
+               OpBranch %33
+         %38 = OpLabel
+         %77 = OpSDiv %6 %81 %12
+         %58 = OpINotEqual %27 %77 %29
+               OpBranchConditional %58 %31 %33
+         %33 = OpLabel
+         %86 = OpPhi %27 %67 %42 %64 %38
+               OpSelectionMerge %68 None
+               OpBranchConditional %86 %22 %68
+         %68 = OpLabel
+               OpBranch %22
+         %23 = OpLabel
+               OpBranch %20
+         %22 = OpLabel
+         %90 = OpPhi %27 %64 %20 %86 %33 %67 %68
+               OpSelectionMerge %70 None
+               OpBranchConditional %90 %60 %70
+         %70 = OpLabel
+               OpBranch %60
+         %63 = OpLabel
+               OpBranch %61
+         %60 = OpLabel
                OpReturn
                OpFunctionEnd
   )";
 
-  const std::string expected = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %2 "main"
-               OpExecutionMode %2 OriginUpperLeft
-               OpSource ESSL 310
-          %5 = OpTypeVoid
-          %6 = OpTypeFunction %5
-          %7 = OpTypeFloat 32
-          %8 = OpTypePointer Function %7
-          %9 = OpConstant %7 1
-          %2 = OpFunction %5 None %6
-         %10 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
+TEST(ReducerTest, ShaderReduceWhileMulReachable) {
+  Reducer reducer(kEnv);
 
-  spv_target_env env = SPV_ENV_UNIVERSAL_1_3;
-  Reducer reducer(env);
-  // Make ping-pong interesting very quickly, as there are not many
-  // opportunities.
-  PingPongInteresting ping_pong_interesting(1);
-  reducer.SetMessageConsumer(NopDiagnostic);
-  reducer.SetInterestingnessFunction(
-      [&](const std::vector<uint32_t>& binary, uint32_t) -> bool {
-        return ping_pong_interesting.IsInteresting(binary);
-      });
-  reducer.AddReductionPass(
-      MakeUnique<RemoveOpNameInstructionReductionOpportunityFinder>());
-  reducer.AddReductionPass(
-      MakeUnique<RemoveUnreferencedInstructionReductionOpportunityFinder>());
+  reducer.SetInterestingnessFunction(InterestingWhileIMulReachable);
+  reducer.AddDefaultReductionPasses();
+  reducer.SetMessageConsumer(kMessageConsumer);
 
   std::vector<uint32_t> binary_in;
-  SpirvTools t(env);
+  SpirvTools t(kEnv);
 
-  ASSERT_TRUE(t.Assemble(original, &binary_in, kReduceAssembleOption));
+  ASSERT_TRUE(
+      t.Assemble(kShaderWithLoopsDivAndMul, &binary_in, kReduceAssembleOption));
   std::vector<uint32_t> binary_out;
   spvtools::ReducerOptions reducer_options;
   reducer_options.set_step_limit(500);
@@ -312,8 +407,30 @@
       std::move(binary_in), &binary_out, reducer_options, validator_options);
 
   ASSERT_EQ(status, Reducer::ReductionResultStatus::kComplete);
+}
 
-  CheckEqual(env, expected, binary_out);
+TEST(ReducerTest, ShaderReduceWhileDivReachable) {
+  Reducer reducer(kEnv);
+
+  reducer.SetInterestingnessFunction(InterestingWhileSDivReachable);
+  reducer.AddDefaultReductionPasses();
+  reducer.SetMessageConsumer(kMessageConsumer);
+
+  std::vector<uint32_t> binary_in;
+  SpirvTools t(kEnv);
+
+  ASSERT_TRUE(
+      t.Assemble(kShaderWithLoopsDivAndMul, &binary_in, kReduceAssembleOption));
+  std::vector<uint32_t> binary_out;
+  spvtools::ReducerOptions reducer_options;
+  reducer_options.set_step_limit(500);
+  reducer_options.set_fail_on_validation_error(true);
+  spvtools::ValidatorOptions validator_options;
+
+  Reducer::ReductionResultStatus status = reducer.Run(
+      std::move(binary_in), &binary_out, reducer_options, validator_options);
+
+  ASSERT_EQ(status, Reducer::ReductionResultStatus::kComplete);
 }
 
 }  // namespace
diff --git a/test/reduce/remove_opname_instruction_test.cpp b/test/reduce/remove_opname_instruction_test.cpp
deleted file mode 100644
index 9d40cfc..0000000
--- a/test/reduce/remove_opname_instruction_test.cpp
+++ /dev/null
@@ -1,225 +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.
-
-#include "source/reduce/remove_opname_instruction_reduction_opportunity_finder.h"
-
-#include "source/opt/build_module.h"
-#include "source/reduce/reduction_opportunity.h"
-#include "source/reduce/reduction_pass.h"
-#include "test/reduce/reduce_test_util.h"
-
-namespace spvtools {
-namespace reduce {
-namespace {
-
-TEST(RemoveOpnameInstructionReductionPassTest, NothingToRemove) {
-  const std::string source = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %4 "main"
-               OpExecutionMode %4 OriginUpperLeft
-               OpSource ESSL 310
-          %2 = OpTypeVoid
-          %3 = OpTypeFunction %2
-          %4 = OpFunction %2 None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  const auto env = SPV_ENV_UNIVERSAL_1_3;
-  const auto consumer = nullptr;
-  const auto context =
-      BuildModule(env, consumer, source, kReduceAssembleOption);
-  const auto ops = RemoveOpNameInstructionReductionOpportunityFinder()
-                       .GetAvailableOpportunities(context.get());
-  ASSERT_EQ(0, ops.size());
-}
-
-TEST(RemoveOpnameInstructionReductionPassTest, RemoveSingleOpName) {
-  const std::string prologue = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %4 "main"
-               OpExecutionMode %4 OriginUpperLeft
-               OpSource ESSL 310
-  )";
-
-  const std::string epilogue = R"(
-          %2 = OpTypeVoid
-          %3 = OpTypeFunction %2
-          %4 = OpFunction %2 None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  const std::string original = prologue + R"(
-               OpName %4 "main"
-  )" + epilogue;
-
-  const std::string expected = prologue + epilogue;
-
-  const auto env = SPV_ENV_UNIVERSAL_1_3;
-  const auto consumer = nullptr;
-  const auto context =
-      BuildModule(env, consumer, original, kReduceAssembleOption);
-  const auto ops = RemoveOpNameInstructionReductionOpportunityFinder()
-                       .GetAvailableOpportunities(context.get());
-  ASSERT_EQ(1, ops.size());
-  ASSERT_TRUE(ops[0]->PreconditionHolds());
-  ops[0]->TryToApply();
-
-  CheckEqual(env, expected, context.get());
-}
-
-TEST(RemoveOpnameInstructionReductionPassTest, TryApplyRemovesAllOpName) {
-  const std::string prologue = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %4 "main"
-               OpExecutionMode %4 OriginUpperLeft
-               OpSource ESSL 310
-  )";
-
-  const std::string epilogue = R"(
-          %2 = OpTypeVoid
-          %3 = OpTypeFunction %2
-          %6 = OpTypeFloat 32
-          %7 = OpTypePointer Function %6
-          %9 = OpConstant %6 1
-          %4 = OpFunction %2 None %3
-          %5 = OpLabel
-          %8 = OpVariable %7 Function
-         %10 = OpVariable %7 Function
-         %11 = OpVariable %7 Function
-         %12 = OpVariable %7 Function
-               OpStore %8 %9
-               OpStore %10 %9
-               OpStore %11 %9
-               OpStore %12 %9
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  const std::string original = prologue + R"(
-               OpName %4 "main"
-               OpName %8 "a"
-               OpName %10 "b"
-               OpName %11 "c"
-               OpName %12 "d"
-  )" + epilogue;
-
-  const std::string expected = prologue + epilogue;
-
-  const auto env = SPV_ENV_UNIVERSAL_1_3;
-
-  {
-    // Check the right number of opportunities is detected
-    const auto consumer = nullptr;
-    const auto context =
-        BuildModule(env, consumer, original, kReduceAssembleOption);
-    const auto ops = RemoveOpNameInstructionReductionOpportunityFinder()
-                         .GetAvailableOpportunities(context.get());
-    ASSERT_EQ(5, ops.size());
-  }
-
-  {
-    // The reduction should remove all OpName
-    std::vector<uint32_t> binary;
-    SpirvTools t(env);
-    ASSERT_TRUE(t.Assemble(original, &binary, kReduceAssembleOption));
-    auto reduced_binary =
-        ReductionPass(env,
-                      spvtools::MakeUnique<
-                          RemoveOpNameInstructionReductionOpportunityFinder>())
-            .TryApplyReduction(binary);
-    CheckEqual(env, expected, reduced_binary);
-  }
-}
-
-TEST(RemoveOpnameInstructionReductionPassTest,
-     TryApplyRemovesAllOpNameAndOpMemberName) {
-  const std::string prologue = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %4 "main"
-               OpExecutionMode %4 OriginUpperLeft
-               OpSource ESSL 310
-  )";
-
-  const std::string epilogue = R"(
-          %2 = OpTypeVoid
-          %3 = OpTypeFunction %2
-          %6 = OpTypeFloat 32
-          %7 = OpTypeInt 32 1
-          %8 = OpTypeVector %6 3
-          %9 = OpTypeStruct %6 %7 %8
-         %10 = OpTypePointer Function %9
-         %12 = OpConstant %7 0
-         %13 = OpConstant %6 1
-         %14 = OpTypePointer Function %6
-          %4 = OpFunction %2 None %3
-          %5 = OpLabel
-         %11 = OpVariable %10 Function
-         %15 = OpAccessChain %14 %11 %12
-               OpStore %15 %13
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  const std::string original = prologue + R"(
-               OpName %4 "main"
-               OpName %9 "S"
-               OpMemberName %9 0 "f"
-               OpMemberName %9 1 "i"
-               OpMemberName %9 2 "v"
-               OpName %11 "s"
-  )" + epilogue;
-
-  const std::string expected = prologue + epilogue;
-
-  const auto env = SPV_ENV_UNIVERSAL_1_3;
-
-  {
-    // Check the right number of opportunities is detected
-    const auto consumer = nullptr;
-    const auto context =
-        BuildModule(env, consumer, original, kReduceAssembleOption);
-    const auto ops = RemoveOpNameInstructionReductionOpportunityFinder()
-                         .GetAvailableOpportunities(context.get());
-    ASSERT_EQ(6, ops.size());
-  }
-
-  {
-    // The reduction should remove all OpName
-    std::vector<uint32_t> binary;
-    SpirvTools t(env);
-    ASSERT_TRUE(t.Assemble(original, &binary, kReduceAssembleOption));
-    auto reduced_binary =
-        ReductionPass(env,
-                      spvtools::MakeUnique<
-                          RemoveOpNameInstructionReductionOpportunityFinder>())
-            .TryApplyReduction(binary);
-    CheckEqual(env, expected, reduced_binary);
-  }
-}
-
-}  // namespace
-}  // namespace reduce
-}  // namespace spvtools
diff --git a/test/reduce/remove_relaxed_precision_decoration_test.cpp b/test/reduce/remove_relaxed_precision_decoration_test.cpp
deleted file mode 100644
index f9ff081..0000000
--- a/test/reduce/remove_relaxed_precision_decoration_test.cpp
+++ /dev/null
@@ -1,177 +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.
-
-#include "source/reduce/remove_relaxed_precision_decoration_opportunity_finder.h"
-
-#include "source/opt/build_module.h"
-#include "source/reduce/reduction_opportunity.h"
-#include "source/reduce/reduction_pass.h"
-#include "test/reduce/reduce_test_util.h"
-
-namespace spvtools {
-namespace reduce {
-namespace {
-
-TEST(RemoveRelaxedPrecisionDecorationTest, NothingToRemove) {
-  const std::string source = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %4 "main"
-               OpExecutionMode %4 OriginUpperLeft
-               OpSource ESSL 310
-          %2 = OpTypeVoid
-          %3 = OpTypeFunction %2
-          %4 = OpFunction %2 None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  const auto env = SPV_ENV_UNIVERSAL_1_3;
-  const auto consumer = nullptr;
-  const auto context =
-      BuildModule(env, consumer, source, kReduceAssembleOption);
-  const auto ops = RemoveRelaxedPrecisionDecorationOpportunityFinder()
-                       .GetAvailableOpportunities(context.get());
-  ASSERT_EQ(0, ops.size());
-}
-
-TEST(RemoveRelaxedPrecisionDecorationTest, RemoveDecorations) {
-  const std::string source = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %4 "main"
-               OpExecutionMode %4 OriginUpperLeft
-               OpSource ESSL 310
-               OpName %4 "main"
-               OpName %8 "f"
-               OpName %12 "i"
-               OpName %16 "v"
-               OpName %19 "S"
-               OpMemberName %19 0 "a"
-               OpMemberName %19 1 "b"
-               OpMemberName %19 2 "c"
-               OpName %21 "s"
-               OpDecorate %8 RelaxedPrecision
-               OpDecorate %12 RelaxedPrecision
-               OpDecorate %16 RelaxedPrecision
-               OpDecorate %17 RelaxedPrecision
-               OpDecorate %18 RelaxedPrecision
-               OpMemberDecorate %19 0 RelaxedPrecision
-               OpMemberDecorate %19 1 RelaxedPrecision
-               OpMemberDecorate %19 2 RelaxedPrecision
-               OpDecorate %22 RelaxedPrecision
-               OpDecorate %23 RelaxedPrecision
-               OpDecorate %24 RelaxedPrecision
-          %2 = OpTypeVoid
-          %3 = OpTypeFunction %2
-          %6 = OpTypeFloat 32
-          %7 = OpTypePointer Function %6
-          %9 = OpConstant %6 2
-         %10 = OpTypeInt 32 1
-         %11 = OpTypePointer Function %10
-         %13 = OpConstant %10 22
-         %14 = OpTypeVector %6 2
-         %15 = OpTypePointer Function %14
-         %19 = OpTypeStruct %10 %6 %14
-         %20 = OpTypePointer Function %19
-          %4 = OpFunction %2 None %3
-          %5 = OpLabel
-          %8 = OpVariable %7 Function
-         %12 = OpVariable %11 Function
-         %16 = OpVariable %15 Function
-         %21 = OpVariable %20 Function
-               OpStore %8 %9
-               OpStore %12 %13
-         %17 = OpLoad %6 %8
-         %18 = OpCompositeConstruct %14 %17 %17
-               OpStore %16 %18
-         %22 = OpLoad %10 %12
-         %23 = OpLoad %6 %8
-         %24 = OpLoad %14 %16
-         %25 = OpCompositeConstruct %19 %22 %23 %24
-               OpStore %21 %25
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  const auto env = SPV_ENV_UNIVERSAL_1_3;
-  const auto consumer = nullptr;
-  const auto context =
-      BuildModule(env, consumer, source, kReduceAssembleOption);
-  const auto ops = RemoveRelaxedPrecisionDecorationOpportunityFinder()
-                       .GetAvailableOpportunities(context.get());
-  ASSERT_EQ(11, ops.size());
-
-  for (auto& op : ops) {
-    ASSERT_TRUE(op->PreconditionHolds());
-    op->TryToApply();
-  }
-
-  const std::string expected = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %4 "main"
-               OpExecutionMode %4 OriginUpperLeft
-               OpSource ESSL 310
-               OpName %4 "main"
-               OpName %8 "f"
-               OpName %12 "i"
-               OpName %16 "v"
-               OpName %19 "S"
-               OpMemberName %19 0 "a"
-               OpMemberName %19 1 "b"
-               OpMemberName %19 2 "c"
-               OpName %21 "s"
-          %2 = OpTypeVoid
-          %3 = OpTypeFunction %2
-          %6 = OpTypeFloat 32
-          %7 = OpTypePointer Function %6
-          %9 = OpConstant %6 2
-         %10 = OpTypeInt 32 1
-         %11 = OpTypePointer Function %10
-         %13 = OpConstant %10 22
-         %14 = OpTypeVector %6 2
-         %15 = OpTypePointer Function %14
-         %19 = OpTypeStruct %10 %6 %14
-         %20 = OpTypePointer Function %19
-          %4 = OpFunction %2 None %3
-          %5 = OpLabel
-          %8 = OpVariable %7 Function
-         %12 = OpVariable %11 Function
-         %16 = OpVariable %15 Function
-         %21 = OpVariable %20 Function
-               OpStore %8 %9
-               OpStore %12 %13
-         %17 = OpLoad %6 %8
-         %18 = OpCompositeConstruct %14 %17 %17
-               OpStore %16 %18
-         %22 = OpLoad %10 %12
-         %23 = OpLoad %6 %8
-         %24 = OpLoad %14 %16
-         %25 = OpCompositeConstruct %19 %22 %23 %24
-               OpStore %21 %25
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CheckEqual(env, expected, context.get());
-}
-
-}  // namespace
-}  // namespace reduce
-}  // namespace spvtools
diff --git a/test/reduce/remove_unreferenced_instruction_test.cpp b/test/reduce/remove_unreferenced_instruction_test.cpp
index 0babf78..3caf88c 100644
--- a/test/reduce/remove_unreferenced_instruction_test.cpp
+++ b/test/reduce/remove_unreferenced_instruction_test.cpp
@@ -23,19 +23,26 @@
 namespace reduce {
 namespace {
 
+const spv_target_env kEnv = SPV_ENV_UNIVERSAL_1_3;
+
 TEST(RemoveUnreferencedInstructionReductionPassTest, RemoveStores) {
-  const std::string prologue = R"(
+  // A module with some unused instructions, including some unused OpStore
+  // instructions.
+
+  RemoveUnreferencedInstructionReductionOpportunityFinder finder(true);
+
+  const std::string original = R"(
                OpCapability Shader
           %1 = OpExtInstImport "GLSL.std.450"
                OpMemoryModel Logical GLSL450
                OpEntryPoint Fragment %4 "main"
                OpExecutionMode %4 OriginUpperLeft
-               OpSource ESSL 310
-               OpName %4 "main"
-               OpName %8 "a"
-               OpName %10 "b"
-               OpName %12 "c"
-               OpName %14 "d"
+               OpSource ESSL 310  ; 0
+               OpName %4 "main"   ; 1
+               OpName %8 "a"      ; 2
+               OpName %10 "b"     ; 3
+               OpName %12 "c"     ; 4
+               OpName %14 "d"     ; 5
           %2 = OpTypeVoid
           %3 = OpTypeFunction %2
           %6 = OpTypeInt 32 1
@@ -49,51 +56,323 @@
          %10 = OpVariable %7 Function
          %12 = OpVariable %7 Function
          %14 = OpVariable %7 Function
+               OpStore %8 %9           ; 6
+               OpStore %10 %11         ; 7
+               OpStore %12 %13         ; 8
+         %15 = OpLoad %6 %8
+               OpStore %14 %15         ; 9
+               OpReturn
+               OpFunctionEnd
+
   )";
 
-  const std::string epilogue = R"(
+  const MessageConsumer consumer = nullptr;
+  const auto context =
+      BuildModule(kEnv, consumer, original, kReduceAssembleOption);
+
+  CheckValid(kEnv, context.get());
+
+  auto ops = finder.GetAvailableOpportunities(context.get());
+
+  ASSERT_EQ(10, ops.size());
+
+  for (auto& op : ops) {
+    ASSERT_TRUE(op->PreconditionHolds());
+    op->TryToApply();
+    CheckValid(kEnv, context.get());
+  }
+
+  const std::string step_2 = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 10       ; 0
+         %11 = OpConstant %6 20       ; 1
+         %13 = OpConstant %6 30       ; 2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %10 = OpVariable %7 Function ; 3
+         %12 = OpVariable %7 Function ; 4
+         %14 = OpVariable %7 Function ; 5
+         %15 = OpLoad %6 %8           ; 6
                OpReturn
                OpFunctionEnd
   )";
 
-  const std::string original = prologue + R"(
-               OpStore %8 %9
-               OpStore %10 %11
-               OpStore %12 %13
-         %15 = OpLoad %6 %8
-               OpStore %14 %15
-  )" + epilogue;
+  CheckEqual(kEnv, step_2, context.get());
 
-  const std::string expected_after_2 = prologue + R"(
-               OpStore %12 %13
-         %15 = OpLoad %6 %8
-               OpStore %14 %15
-  )" + epilogue;
+  ops = finder.GetAvailableOpportunities(context.get());
 
-  const std::string expected_after_4 = prologue + R"(
-         %15 = OpLoad %6 %8
-  )" + epilogue;
+  ASSERT_EQ(7, ops.size());
 
-  const auto env = SPV_ENV_UNIVERSAL_1_3;
-  const auto consumer = nullptr;
-  const auto context =
-      BuildModule(env, consumer, original, kReduceAssembleOption);
-  const auto ops = RemoveUnreferencedInstructionReductionOpportunityFinder()
-                       .GetAvailableOpportunities(context.get());
-  ASSERT_EQ(4, ops.size());
-  ASSERT_TRUE(ops[0]->PreconditionHolds());
-  ops[0]->TryToApply();
-  ASSERT_TRUE(ops[1]->PreconditionHolds());
-  ops[1]->TryToApply();
+  for (auto& op : ops) {
+    ASSERT_TRUE(op->PreconditionHolds());
+    op->TryToApply();
+    CheckValid(kEnv, context.get());
+  }
 
-  CheckEqual(env, expected_after_2, context.get());
+  const std::string step_3 = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function   ; 0
+               OpReturn
+               OpFunctionEnd
+  )";
 
-  ASSERT_TRUE(ops[2]->PreconditionHolds());
-  ops[2]->TryToApply();
-  ASSERT_TRUE(ops[3]->PreconditionHolds());
-  ops[3]->TryToApply();
+  CheckEqual(kEnv, step_3, context.get());
 
-  CheckEqual(env, expected_after_4, context.get());
+  ops = finder.GetAvailableOpportunities(context.get());
+
+  ASSERT_EQ(1, ops.size());
+
+  for (auto& op : ops) {
+    ASSERT_TRUE(op->PreconditionHolds());
+    op->TryToApply();
+    CheckValid(kEnv, context.get());
+  }
+
+  const std::string step_4 = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6  ; 0
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CheckEqual(kEnv, step_4, context.get());
+
+  ops = finder.GetAvailableOpportunities(context.get());
+
+  ASSERT_EQ(1, ops.size());
+
+  for (auto& op : ops) {
+    ASSERT_TRUE(op->PreconditionHolds());
+    op->TryToApply();
+    CheckValid(kEnv, context.get());
+  }
+
+  const std::string step_5 = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1        ; 0
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CheckEqual(kEnv, step_5, context.get());
+
+  ops = finder.GetAvailableOpportunities(context.get());
+
+  ASSERT_EQ(1, ops.size());
+
+  for (auto& op : ops) {
+    ASSERT_TRUE(op->PreconditionHolds());
+    op->TryToApply();
+    CheckValid(kEnv, context.get());
+  }
+
+  const std::string step_6 = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CheckEqual(kEnv, step_6, context.get());
+
+  ops = finder.GetAvailableOpportunities(context.get());
+
+  ASSERT_EQ(0, ops.size());
+}
+
+TEST(RemoveUnreferencedInstructionReductionPassTest, Referenced) {
+  // A module with some unused global variables, constants, and types. Some will
+  // not be removed initially because of the OpDecorate instructions.
+
+  RemoveUnreferencedInstructionReductionOpportunityFinder finder(true);
+
+  const std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310                 ; 1
+               OpName %4 "main"                  ; 2
+               OpName %12 "a"                    ; 3
+               OpDecorate %12 RelaxedPrecision   ; 4
+               OpDecorate %13 RelaxedPrecision   ; 5
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool
+          %7 = OpConstantTrue %6                 ; 6
+         %10 = OpTypeInt 32 1
+         %11 = OpTypePointer Private %10
+         %12 = OpVariable %11 Private
+         %13 = OpConstant %10 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  auto context = BuildModule(kEnv, nullptr, shader, kReduceAssembleOption);
+
+  CheckValid(kEnv, context.get());
+
+  auto ops = finder.GetAvailableOpportunities(context.get());
+
+  ASSERT_EQ(6, ops.size());
+
+  for (auto& op : ops) {
+    ASSERT_TRUE(op->PreconditionHolds());
+    op->TryToApply();
+    CheckValid(kEnv, context.get());
+  }
+
+  std::string after = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool                 ; 1
+         %10 = OpTypeInt 32 1
+         %11 = OpTypePointer Private %10
+         %12 = OpVariable %11 Private     ; 2
+         %13 = OpConstant %10 1           ; 3
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+    )";
+
+  CheckEqual(kEnv, after, context.get());
+
+  ops = finder.GetAvailableOpportunities(context.get());
+
+  ASSERT_EQ(3, ops.size());
+
+  for (auto& op : ops) {
+    ASSERT_TRUE(op->PreconditionHolds());
+    op->TryToApply();
+    CheckValid(kEnv, context.get());
+  }
+
+  std::string after_2 = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+         %10 = OpTypeInt 32 1
+         %11 = OpTypePointer Private %10   ; 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+    )";
+
+  CheckEqual(kEnv, after_2, context.get());
+
+  ops = finder.GetAvailableOpportunities(context.get());
+
+  ASSERT_EQ(1, ops.size());
+
+  for (auto& op : ops) {
+    ASSERT_TRUE(op->PreconditionHolds());
+    op->TryToApply();
+    CheckValid(kEnv, context.get());
+  }
+
+  std::string after_3 = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+         %10 = OpTypeInt 32 1          ; 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+    )";
+
+  CheckEqual(kEnv, after_3, context.get());
+
+  ops = finder.GetAvailableOpportunities(context.get());
+
+  ASSERT_EQ(1, ops.size());
+
+  for (auto& op : ops) {
+    ASSERT_TRUE(op->PreconditionHolds());
+    op->TryToApply();
+    CheckValid(kEnv, context.get());
+  }
+
+  std::string after_4 = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+    )";
+
+  CheckEqual(kEnv, after_4, context.get());
+
+  ops = finder.GetAvailableOpportunities(context.get());
+
+  ASSERT_EQ(0, ops.size());
 }
 
 }  // namespace
diff --git a/test/text_to_binary.constant_test.cpp b/test/text_to_binary.constant_test.cpp
index 6624d41..679bee4 100644
--- a/test/text_to_binary.constant_test.cpp
+++ b/test/text_to_binary.constant_test.cpp
@@ -328,7 +328,6 @@
 
 using OpConstantInvalidTypeTest =
     spvtest::TextToBinaryTestBase<::testing::TestWithParam<std::string>>;
-
 TEST_P(OpConstantInvalidTypeTest, InvalidTypes) {
   const std::string input = "%1 = " + GetParam() +
                             "\n"
@@ -360,8 +359,11 @@
        "OpTypeReserveId",
        "OpTypeQueue",
        "OpTypePipe ReadOnly",
-       "OpTypeForwardPointer %a UniformConstant",
-        // At least one thing that isn't a type at all
+
+       // Skip OpTypeForwardPointer doesn't even produce a result ID.
+       // The assembler errors out if we try to check it in this scenario.
+
+       // Try at least one thing that isn't a type at all
        "OpNot %a %b"
       },
     }));
@@ -470,8 +472,10 @@
        "OpTypeReserveId",
        "OpTypeQueue",
        "OpTypePipe ReadOnly",
-       "OpTypeForwardPointer %a UniformConstant",
-        // At least one thing that isn't a type at all
+
+       // Skip testing OpTypeForwardPointer because it doesn't even produce a result ID.
+
+       // Try at least one thing that isn't a type at all
        "OpNot %a %b"
       },
     }));
diff --git a/test/text_to_binary.control_flow_test.cpp b/test/text_to_binary.control_flow_test.cpp
index 01cc8e6..ae51f55 100644
--- a/test/text_to_binary.control_flow_test.cpp
+++ b/test/text_to_binary.control_flow_test.cpp
@@ -342,7 +342,7 @@
       "%1 = " + GetParam() +
       "\n"
       "%3 = OpCopyObject %1 %2\n"  // We only care the type of the expression
-      "%4 = OpSwitch %3 %default 32 %c\n";
+      "     OpSwitch %3 %default 32 %c\n";
   EXPECT_THAT(CompileFailure(input),
               Eq("The selector operand for OpSwitch must be the result of an "
                  "instruction that generates an integer scalar"));
@@ -371,8 +371,11 @@
        "OpTypeReserveId",
        "OpTypeQueue",
        "OpTypePipe ReadOnly",
-       "OpTypeForwardPointer %a UniformConstant",
-           // At least one thing that isn't a type at all
+
+       // Skip OpTypeForwardPointer becasuse it doesn't even produce a result
+       // ID.
+
+       // At least one thing that isn't a type at all
        "OpNot %a %b"
       },
     }));
diff --git a/test/text_to_binary.extension_test.cpp b/test/text_to_binary.extension_test.cpp
index 9408e9a..023763b 100644
--- a/test/text_to_binary.extension_test.cpp
+++ b/test/text_to_binary.extension_test.cpp
@@ -527,54 +527,54 @@
         Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1,
                SPV_ENV_UNIVERSAL_1_3, SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1),
         ValuesIn(std::vector<AssemblyCase>{
-            {"OpCapability VulkanMemoryModelKHR\n",
+            {"OpCapability VulkanMemoryModel\n",
              MakeInstruction(SpvOpCapability,
                              {SpvCapabilityVulkanMemoryModelKHR})},
-            {"OpCapability VulkanMemoryModelDeviceScopeKHR\n",
+            {"OpCapability VulkanMemoryModelDeviceScope\n",
              MakeInstruction(SpvOpCapability,
                              {SpvCapabilityVulkanMemoryModelDeviceScopeKHR})},
-            {"OpMemoryModel Logical VulkanKHR\n",
+            {"OpMemoryModel Logical Vulkan\n",
              MakeInstruction(SpvOpMemoryModel, {SpvAddressingModelLogical,
                                                 SpvMemoryModelVulkanKHR})},
-            {"OpStore %1 %2 MakePointerAvailableKHR %3\n",
+            {"OpStore %1 %2 MakePointerAvailable %3\n",
              MakeInstruction(SpvOpStore,
                              {1, 2, SpvMemoryAccessMakePointerAvailableKHRMask,
                               3})},
-            {"OpStore %1 %2 Volatile|MakePointerAvailableKHR %3\n",
+            {"OpStore %1 %2 Volatile|MakePointerAvailable %3\n",
              MakeInstruction(SpvOpStore,
                              {1, 2,
                               int(SpvMemoryAccessMakePointerAvailableKHRMask) |
                                   int(SpvMemoryAccessVolatileMask),
                               3})},
-            {"OpStore %1 %2 Aligned|MakePointerAvailableKHR 4 %3\n",
+            {"OpStore %1 %2 Aligned|MakePointerAvailable 4 %3\n",
              MakeInstruction(SpvOpStore,
                              {1, 2,
                               int(SpvMemoryAccessMakePointerAvailableKHRMask) |
                                   int(SpvMemoryAccessAlignedMask),
                               4, 3})},
-            {"OpStore %1 %2 MakePointerAvailableKHR|NonPrivatePointerKHR %3\n",
+            {"OpStore %1 %2 MakePointerAvailable|NonPrivatePointer %3\n",
              MakeInstruction(SpvOpStore,
                              {1, 2,
                               int(SpvMemoryAccessMakePointerAvailableKHRMask) |
                                   int(SpvMemoryAccessNonPrivatePointerKHRMask),
                               3})},
-            {"%2 = OpLoad %1 %3 MakePointerVisibleKHR %4\n",
+            {"%2 = OpLoad %1 %3 MakePointerVisible %4\n",
              MakeInstruction(SpvOpLoad,
                              {1, 2, 3, SpvMemoryAccessMakePointerVisibleKHRMask,
                               4})},
-            {"%2 = OpLoad %1 %3 Volatile|MakePointerVisibleKHR %4\n",
+            {"%2 = OpLoad %1 %3 Volatile|MakePointerVisible %4\n",
              MakeInstruction(SpvOpLoad,
                              {1, 2, 3,
                               int(SpvMemoryAccessMakePointerVisibleKHRMask) |
                                   int(SpvMemoryAccessVolatileMask),
                               4})},
-            {"%2 = OpLoad %1 %3 Aligned|MakePointerVisibleKHR 8 %4\n",
+            {"%2 = OpLoad %1 %3 Aligned|MakePointerVisible 8 %4\n",
              MakeInstruction(SpvOpLoad,
                              {1, 2, 3,
                               int(SpvMemoryAccessMakePointerVisibleKHRMask) |
                                   int(SpvMemoryAccessAlignedMask),
                               8, 4})},
-            {"%2 = OpLoad %1 %3 MakePointerVisibleKHR|NonPrivatePointerKHR "
+            {"%2 = OpLoad %1 %3 MakePointerVisible|NonPrivatePointer "
              "%4\n",
              MakeInstruction(SpvOpLoad,
                              {1, 2, 3,
@@ -582,9 +582,9 @@
                                   int(SpvMemoryAccessNonPrivatePointerKHRMask),
                               4})},
             {"OpCopyMemory %1 %2 "
-             "MakePointerAvailableKHR|"
-             "MakePointerVisibleKHR|"
-             "NonPrivatePointerKHR "
+             "MakePointerAvailable|"
+             "MakePointerVisible|"
+             "NonPrivatePointer "
              "%3 %4\n",
              MakeInstruction(SpvOpCopyMemory,
                              {1, 2,
@@ -593,9 +593,9 @@
                                int(SpvMemoryAccessNonPrivatePointerKHRMask)),
                               3, 4})},
             {"OpCopyMemorySized %1 %2 %3 "
-             "MakePointerAvailableKHR|"
-             "MakePointerVisibleKHR|"
-             "NonPrivatePointerKHR "
+             "MakePointerAvailable|"
+             "MakePointerVisible|"
+             "NonPrivatePointer "
              "%4 %5\n",
              MakeInstruction(SpvOpCopyMemorySized,
                              {1, 2, 3,
@@ -604,12 +604,12 @@
                                int(SpvMemoryAccessNonPrivatePointerKHRMask)),
                               4, 5})},
             // Image operands
-            {"OpImageWrite %1 %2 %3 MakeTexelAvailableKHR "
+            {"OpImageWrite %1 %2 %3 MakeTexelAvailable "
              "%4\n",
              MakeInstruction(
                  SpvOpImageWrite,
                  {1, 2, 3, int(SpvImageOperandsMakeTexelAvailableKHRMask), 4})},
-            {"OpImageWrite %1 %2 %3 MakeTexelAvailableKHR|NonPrivateTexelKHR "
+            {"OpImageWrite %1 %2 %3 MakeTexelAvailable|NonPrivateTexel "
              "%4\n",
              MakeInstruction(SpvOpImageWrite,
                              {1, 2, 3,
@@ -617,7 +617,7 @@
                                   int(SpvImageOperandsNonPrivateTexelKHRMask),
                               4})},
             {"OpImageWrite %1 %2 %3 "
-             "MakeTexelAvailableKHR|NonPrivateTexelKHR|VolatileTexelKHR "
+             "MakeTexelAvailable|NonPrivateTexel|VolatileTexel "
              "%4\n",
              MakeInstruction(SpvOpImageWrite,
                              {1, 2, 3,
@@ -625,14 +625,14 @@
                                   int(SpvImageOperandsNonPrivateTexelKHRMask) |
                                   int(SpvImageOperandsVolatileTexelKHRMask),
                               4})},
-            {"%2 = OpImageRead %1 %3 %4 MakeTexelVisibleKHR "
+            {"%2 = OpImageRead %1 %3 %4 MakeTexelVisible "
              "%5\n",
              MakeInstruction(SpvOpImageRead,
                              {1, 2, 3, 4,
                               int(SpvImageOperandsMakeTexelVisibleKHRMask),
                               5})},
             {"%2 = OpImageRead %1 %3 %4 "
-             "MakeTexelVisibleKHR|NonPrivateTexelKHR "
+             "MakeTexelVisible|NonPrivateTexel "
              "%5\n",
              MakeInstruction(SpvOpImageRead,
                              {1, 2, 3, 4,
@@ -640,7 +640,7 @@
                                   int(SpvImageOperandsNonPrivateTexelKHRMask),
                               5})},
             {"%2 = OpImageRead %1 %3 %4 "
-             "MakeTexelVisibleKHR|NonPrivateTexelKHR|VolatileTexelKHR "
+             "MakeTexelVisible|NonPrivateTexel|VolatileTexel "
              "%5\n",
              MakeInstruction(SpvOpImageRead,
                              {1, 2, 3, 4,
@@ -826,82 +826,82 @@
              MakeInstruction(SpvOpExtension,
                              MakeVector("SPV_EXT_descriptor_indexing"))},
             // Check capabilities, by name
-            {"OpCapability ShaderNonUniformEXT\n",
+            {"OpCapability ShaderNonUniform\n",
              MakeInstruction(SpvOpCapability,
                              {SpvCapabilityShaderNonUniformEXT})},
-            {"OpCapability RuntimeDescriptorArrayEXT\n",
+            {"OpCapability RuntimeDescriptorArray\n",
              MakeInstruction(SpvOpCapability,
                              {SpvCapabilityRuntimeDescriptorArrayEXT})},
-            {"OpCapability InputAttachmentArrayDynamicIndexingEXT\n",
+            {"OpCapability InputAttachmentArrayDynamicIndexing\n",
              MakeInstruction(
                  SpvOpCapability,
                  {SpvCapabilityInputAttachmentArrayDynamicIndexingEXT})},
-            {"OpCapability UniformTexelBufferArrayDynamicIndexingEXT\n",
+            {"OpCapability UniformTexelBufferArrayDynamicIndexing\n",
              MakeInstruction(
                  SpvOpCapability,
                  {SpvCapabilityUniformTexelBufferArrayDynamicIndexingEXT})},
-            {"OpCapability StorageTexelBufferArrayDynamicIndexingEXT\n",
+            {"OpCapability StorageTexelBufferArrayDynamicIndexing\n",
              MakeInstruction(
                  SpvOpCapability,
                  {SpvCapabilityStorageTexelBufferArrayDynamicIndexingEXT})},
-            {"OpCapability UniformBufferArrayNonUniformIndexingEXT\n",
+            {"OpCapability UniformBufferArrayNonUniformIndexing\n",
              MakeInstruction(
                  SpvOpCapability,
                  {SpvCapabilityUniformBufferArrayNonUniformIndexingEXT})},
-            {"OpCapability SampledImageArrayNonUniformIndexingEXT\n",
+            {"OpCapability SampledImageArrayNonUniformIndexing\n",
              MakeInstruction(
                  SpvOpCapability,
                  {SpvCapabilitySampledImageArrayNonUniformIndexingEXT})},
-            {"OpCapability StorageBufferArrayNonUniformIndexingEXT\n",
+            {"OpCapability StorageBufferArrayNonUniformIndexing\n",
              MakeInstruction(
                  SpvOpCapability,
                  {SpvCapabilityStorageBufferArrayNonUniformIndexingEXT})},
-            {"OpCapability StorageImageArrayNonUniformIndexingEXT\n",
+            {"OpCapability StorageImageArrayNonUniformIndexing\n",
              MakeInstruction(
                  SpvOpCapability,
                  {SpvCapabilityStorageImageArrayNonUniformIndexingEXT})},
-            {"OpCapability InputAttachmentArrayNonUniformIndexingEXT\n",
+            {"OpCapability InputAttachmentArrayNonUniformIndexing\n",
              MakeInstruction(
                  SpvOpCapability,
                  {SpvCapabilityInputAttachmentArrayNonUniformIndexingEXT})},
-            {"OpCapability UniformTexelBufferArrayNonUniformIndexingEXT\n",
+            {"OpCapability UniformTexelBufferArrayNonUniformIndexing\n",
              MakeInstruction(
                  SpvOpCapability,
                  {SpvCapabilityUniformTexelBufferArrayNonUniformIndexingEXT})},
-            {"OpCapability StorageTexelBufferArrayNonUniformIndexingEXT\n",
+            {"OpCapability StorageTexelBufferArrayNonUniformIndexing\n",
              MakeInstruction(
                  SpvOpCapability,
                  {SpvCapabilityStorageTexelBufferArrayNonUniformIndexingEXT})},
             // Check capabilities, by number
-            {"OpCapability ShaderNonUniformEXT\n",
+            {"OpCapability ShaderNonUniform\n",
              MakeInstruction(SpvOpCapability, {5301})},
-            {"OpCapability RuntimeDescriptorArrayEXT\n",
+            {"OpCapability RuntimeDescriptorArray\n",
              MakeInstruction(SpvOpCapability, {5302})},
-            {"OpCapability InputAttachmentArrayDynamicIndexingEXT\n",
+            {"OpCapability InputAttachmentArrayDynamicIndexing\n",
              MakeInstruction(SpvOpCapability, {5303})},
-            {"OpCapability UniformTexelBufferArrayDynamicIndexingEXT\n",
+            {"OpCapability UniformTexelBufferArrayDynamicIndexing\n",
              MakeInstruction(SpvOpCapability, {5304})},
-            {"OpCapability StorageTexelBufferArrayDynamicIndexingEXT\n",
+            {"OpCapability StorageTexelBufferArrayDynamicIndexing\n",
              MakeInstruction(SpvOpCapability, {5305})},
-            {"OpCapability UniformBufferArrayNonUniformIndexingEXT\n",
+            {"OpCapability UniformBufferArrayNonUniformIndexing\n",
              MakeInstruction(SpvOpCapability, {5306})},
-            {"OpCapability SampledImageArrayNonUniformIndexingEXT\n",
+            {"OpCapability SampledImageArrayNonUniformIndexing\n",
              MakeInstruction(SpvOpCapability, {5307})},
-            {"OpCapability StorageBufferArrayNonUniformIndexingEXT\n",
+            {"OpCapability StorageBufferArrayNonUniformIndexing\n",
              MakeInstruction(SpvOpCapability, {5308})},
-            {"OpCapability StorageImageArrayNonUniformIndexingEXT\n",
+            {"OpCapability StorageImageArrayNonUniformIndexing\n",
              MakeInstruction(SpvOpCapability, {5309})},
-            {"OpCapability InputAttachmentArrayNonUniformIndexingEXT\n",
+            {"OpCapability InputAttachmentArrayNonUniformIndexing\n",
              MakeInstruction(SpvOpCapability, {5310})},
-            {"OpCapability UniformTexelBufferArrayNonUniformIndexingEXT\n",
+            {"OpCapability UniformTexelBufferArrayNonUniformIndexing\n",
              MakeInstruction(SpvOpCapability, {5311})},
-            {"OpCapability StorageTexelBufferArrayNonUniformIndexingEXT\n",
+            {"OpCapability StorageTexelBufferArrayNonUniformIndexing\n",
              MakeInstruction(SpvOpCapability, {5312})},
 
             // Check the decoration token
-            {"OpDecorate %1 NonUniformEXT\n",
+            {"OpDecorate %1 NonUniform\n",
              MakeInstruction(SpvOpDecorate, {1, SpvDecorationNonUniformEXT})},
-            {"OpDecorate %1 NonUniformEXT\n",
+            {"OpDecorate %1 NonUniform\n",
              MakeInstruction(SpvOpDecorate, {1, 5300})},
         })));
 
diff --git a/test/text_to_binary.memory_test.cpp b/test/text_to_binary.memory_test.cpp
index c83c847..7b09ed5 100644
--- a/test/text_to_binary.memory_test.cpp
+++ b/test/text_to_binary.memory_test.cpp
@@ -212,7 +212,7 @@
 }
 
 TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessAvGood) {
-  std::string spirv = "OpCopyMemory %1 %2 MakePointerAvailableKHR %3\n";
+  std::string spirv = "OpCopyMemory %1 %2 MakePointerAvailable %3\n";
   EXPECT_THAT(CompiledInstructions(spirv),
               Eq(MakeInstruction(SpvOpCopyMemory, {1, 2, 8, 3})));
   std::string disassembly =
@@ -221,7 +221,7 @@
 }
 
 TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessVisGood) {
-  std::string spirv = "OpCopyMemory %1 %2 MakePointerVisibleKHR %3\n";
+  std::string spirv = "OpCopyMemory %1 %2 MakePointerVisible %3\n";
   EXPECT_THAT(CompiledInstructions(spirv),
               Eq(MakeInstruction(SpvOpCopyMemory, {1, 2, 16, 3})));
   std::string disassembly =
@@ -230,7 +230,7 @@
 }
 
 TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessNonPrivateGood) {
-  std::string spirv = "OpCopyMemory %1 %2 NonPrivatePointerKHR\n";
+  std::string spirv = "OpCopyMemory %1 %2 NonPrivatePointer\n";
   EXPECT_THAT(CompiledInstructions(spirv),
               Eq(MakeInstruction(SpvOpCopyMemory, {1, 2, 32})));
   std::string disassembly =
@@ -241,8 +241,8 @@
 TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessMixedGood) {
   std::string spirv =
       "OpCopyMemory %1 %2 "
-      "Volatile|Aligned|Nontemporal|MakePointerAvailableKHR|"
-      "MakePointerVisibleKHR|NonPrivatePointerKHR 16 %3 %4\n";
+      "Volatile|Aligned|Nontemporal|MakePointerAvailable|"
+      "MakePointerVisible|NonPrivatePointer 16 %3 %4\n";
   EXPECT_THAT(CompiledInstructions(spirv),
               Eq(MakeInstruction(SpvOpCopyMemory, {1, 2, 63, 16, 3, 4})));
   std::string disassembly =
@@ -272,8 +272,8 @@
 TEST_F(MemoryRoundTripTest, OpCopyMemoryTwoAccessMixedV14Good) {
   std::string spirv =
       "OpCopyMemory %1 %2 Volatile|Nontemporal|"
-      "MakePointerVisibleKHR %3 "
-      "Aligned|MakePointerAvailableKHR|NonPrivatePointerKHR 16 %4\n";
+      "MakePointerVisible %3 "
+      "Aligned|MakePointerAvailable|NonPrivatePointer 16 %4\n";
   EXPECT_THAT(CompiledInstructions(spirv),
               Eq(MakeInstruction(SpvOpCopyMemory, {1, 2, 21, 3, 42, 16, 4})));
   std::string disassembly =
@@ -341,7 +341,7 @@
 }
 
 TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessAvGood) {
-  std::string spirv = "OpCopyMemorySized %1 %2 %3 MakePointerAvailableKHR %4\n";
+  std::string spirv = "OpCopyMemorySized %1 %2 %3 MakePointerAvailable %4\n";
   EXPECT_THAT(CompiledInstructions(spirv),
               Eq(MakeInstruction(SpvOpCopyMemorySized, {1, 2, 3, 8, 4})));
   std::string disassembly =
@@ -350,7 +350,7 @@
 }
 
 TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessVisGood) {
-  std::string spirv = "OpCopyMemorySized %1 %2 %3 MakePointerVisibleKHR %4\n";
+  std::string spirv = "OpCopyMemorySized %1 %2 %3 MakePointerVisible %4\n";
   EXPECT_THAT(CompiledInstructions(spirv),
               Eq(MakeInstruction(SpvOpCopyMemorySized, {1, 2, 3, 16, 4})));
   std::string disassembly =
@@ -359,7 +359,7 @@
 }
 
 TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessNonPrivateGood) {
-  std::string spirv = "OpCopyMemorySized %1 %2 %3 NonPrivatePointerKHR\n";
+  std::string spirv = "OpCopyMemorySized %1 %2 %3 NonPrivatePointer\n";
   EXPECT_THAT(CompiledInstructions(spirv),
               Eq(MakeInstruction(SpvOpCopyMemorySized, {1, 2, 3, 32})));
   std::string disassembly =
@@ -370,8 +370,8 @@
 TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessMixedGood) {
   std::string spirv =
       "OpCopyMemorySized %1 %2 %3 "
-      "Volatile|Aligned|Nontemporal|MakePointerAvailableKHR|"
-      "MakePointerVisibleKHR|NonPrivatePointerKHR 16 %4 %5\n";
+      "Volatile|Aligned|Nontemporal|MakePointerAvailable|"
+      "MakePointerVisible|NonPrivatePointer 16 %4 %5\n";
   EXPECT_THAT(
       CompiledInstructions(spirv),
       Eq(MakeInstruction(SpvOpCopyMemorySized, {1, 2, 3, 63, 16, 4, 5})));
@@ -402,8 +402,8 @@
 TEST_F(MemoryRoundTripTest, OpCopyMemorySizedTwoAccessMixedV14Good) {
   std::string spirv =
       "OpCopyMemorySized %1 %2 %3 Volatile|Nontemporal|"
-      "MakePointerVisibleKHR %4 "
-      "Aligned|MakePointerAvailableKHR|NonPrivatePointerKHR 16 %5\n";
+      "MakePointerVisible %4 "
+      "Aligned|MakePointerAvailable|NonPrivatePointer 16 %5\n";
   EXPECT_THAT(
       CompiledInstructions(spirv),
       Eq(MakeInstruction(SpvOpCopyMemorySized, {1, 2, 3, 21, 4, 42, 16, 5})));
diff --git a/test/tools/expect.py b/test/tools/expect.py
index e21a0c4..52999ce 100755
--- a/test/tools/expect.py
+++ b/test/tools/expect.py
@@ -270,8 +270,8 @@
     return True, ''
 
 
-class ValidObjectFile1_4(ReturnCodeIsZero, CorrectObjectFilePreamble):
-  """Mixin class for checking that every input file generates a valid SPIR-V 1.4
+class ValidObjectFile1_5(ReturnCodeIsZero, CorrectObjectFilePreamble):
+  """Mixin class for checking that every input file generates a valid SPIR-V 1.5
     object file following the object file naming rule, and there is no output on
     stdout/stderr."""
 
@@ -279,7 +279,7 @@
     for input_filename in status.input_filenames:
       object_filename = get_object_filename(input_filename)
       success, message = self.verify_object_file_preamble(
-          os.path.join(status.directory, object_filename), 0x10400)
+          os.path.join(status.directory, object_filename), 0x10500)
       if not success:
         return False, message
     return True, ''
diff --git a/test/tools/opt/flags.py b/test/tools/opt/flags.py
index 49e2cab..b34a168 100644
--- a/test/tools/opt/flags.py
+++ b/test/tools/opt/flags.py
@@ -34,7 +34,7 @@
 
 
 @inside_spirv_testsuite('SpirvOptBase')
-class TestAssemblyFileAsOnlyParameter(expect.ValidObjectFile1_4):
+class TestAssemblyFileAsOnlyParameter(expect.ValidObjectFile1_5):
   """Tests that spirv-opt accepts a SPIR-V object file."""
 
   shader = placeholder.FileSPIRVShader(empty_main_assembly(), '.spvasm')
@@ -52,7 +52,7 @@
 
 
 @inside_spirv_testsuite('SpirvOptFlags')
-class TestValidPassFlags(expect.ValidObjectFile1_4,
+class TestValidPassFlags(expect.ValidObjectFile1_5,
                          expect.ExecutedListOfPasses):
   """Tests that spirv-opt accepts all valid optimization flags."""
 
@@ -91,7 +91,7 @@
       'eliminate-dead-variables',
       # --eliminate-insert-extract runs the simplify-instructions pass.
       'simplify-instructions',
-      'eliminate-local-multi-store',
+      'ssa-rewrite',
       'eliminate-local-single-block',
       'eliminate-local-single-store',
       'flatten-decorations',
@@ -129,7 +129,7 @@
 
 
 @inside_spirv_testsuite('SpirvOptFlags')
-class TestPerformanceOptimizationPasses(expect.ValidObjectFile1_4,
+class TestPerformanceOptimizationPasses(expect.ValidObjectFile1_5,
                                         expect.ExecutedListOfPasses):
   """Tests that spirv-opt schedules all the passes triggered by -O."""
 
@@ -149,7 +149,7 @@
       'eliminate-local-single-block',
       'eliminate-local-single-store',
       'eliminate-dead-code-aggressive',
-      'eliminate-local-multi-store',
+      'ssa-rewrite',
       'eliminate-dead-code-aggressive',
       'ccp',
       'eliminate-dead-code-aggressive',
@@ -177,7 +177,7 @@
 
 
 @inside_spirv_testsuite('SpirvOptFlags')
-class TestSizeOptimizationPasses(expect.ValidObjectFile1_4,
+class TestSizeOptimizationPasses(expect.ValidObjectFile1_5,
                                  expect.ExecutedListOfPasses):
   """Tests that spirv-opt schedules all the passes triggered by -Os."""
 
@@ -196,7 +196,7 @@
       'eliminate-dead-code-aggressive',
       'simplify-instructions',
       'eliminate-dead-inserts',
-      'eliminate-local-multi-store',
+      'ssa-rewrite',
       'eliminate-dead-code-aggressive',
       'ccp',
       'eliminate-dead-code-aggressive',
@@ -217,7 +217,7 @@
 
 
 @inside_spirv_testsuite('SpirvOptFlags')
-class TestLegalizationPasses(expect.ValidObjectFile1_4,
+class TestLegalizationPasses(expect.ValidObjectFile1_5,
                              expect.ExecutedListOfPasses):
   """Tests that spirv-opt schedules all the passes triggered by --legalize-hlsl.
   """
@@ -238,7 +238,7 @@
       'eliminate-local-single-block',
       'eliminate-local-single-store',
       'eliminate-dead-code-aggressive',
-      'eliminate-local-multi-store',
+      'ssa-rewrite',
       'eliminate-dead-code-aggressive',
       'ccp',
       'loop-unroll',
diff --git a/test/util/CMakeLists.txt b/test/util/CMakeLists.txt
index 8cdb35f..6679dba 100644
--- a/test/util/CMakeLists.txt
+++ b/test/util/CMakeLists.txt
@@ -15,6 +15,7 @@
 add_spvtools_unittest(TARGET utils
   SRCS ilist_test.cpp
        bit_vector_test.cpp
+       bitutils_test.cpp
        small_vector_test.cpp
   LIBS SPIRV-Tools-opt
 )
diff --git a/test/util/bitutils_test.cpp b/test/util/bitutils_test.cpp
new file mode 100644
index 0000000..3be7ed2
--- /dev/null
+++ b/test/util/bitutils_test.cpp
@@ -0,0 +1,193 @@
+// Copyright (c) 2019 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/util/bitutils.h"
+
+#include "gmock/gmock.h"
+
+namespace spvtools {
+namespace utils {
+namespace {
+
+using BitUtilsTest = ::testing::Test;
+
+TEST(BitUtilsTest, MutateBitsWholeWord) {
+  const uint32_t zero_u32 = 0;
+  const uint32_t max_u32 = ~0;
+
+  EXPECT_EQ(MutateBits(zero_u32, 0, 0, false), zero_u32);
+  EXPECT_EQ(MutateBits(max_u32, 0, 0, false), max_u32);
+  EXPECT_EQ(MutateBits(zero_u32, 0, 32, false), zero_u32);
+  EXPECT_EQ(MutateBits(zero_u32, 0, 32, true), max_u32);
+  EXPECT_EQ(MutateBits(max_u32, 0, 32, true), max_u32);
+  EXPECT_EQ(MutateBits(max_u32, 0, 32, false), zero_u32);
+}
+
+TEST(BitUtilsTest, MutateBitsLow) {
+  const uint32_t zero_u32 = 0;
+  const uint32_t one_u32 = 1;
+  const uint32_t max_u32 = ~0;
+
+  EXPECT_EQ(MutateBits(zero_u32, 0, 1, false), zero_u32);
+  EXPECT_EQ(MutateBits(zero_u32, 0, 1, true), one_u32);
+  EXPECT_EQ(MutateBits(max_u32, 0, 1, true), max_u32);
+  EXPECT_EQ(MutateBits(one_u32, 0, 32, false), zero_u32);
+  EXPECT_EQ(MutateBits(one_u32, 0, 1, true), one_u32);
+  EXPECT_EQ(MutateBits(one_u32, 0, 1, false), zero_u32);
+  EXPECT_EQ(MutateBits(zero_u32, 0, 3, true), uint32_t(7));
+  EXPECT_EQ(MutateBits(uint32_t(7), 0, 2, false), uint32_t(4));
+}
+
+TEST(BitUtilsTest, MutateBitsHigh) {
+  const uint8_t zero_u8 = 0;
+  const uint8_t one_u8 = 1;
+  const uint8_t max_u8 = 255;
+
+  EXPECT_EQ(MutateBits(zero_u8, 7, 0, true), zero_u8);
+  EXPECT_EQ(MutateBits(zero_u8, 7, 1, true), uint8_t(128));
+  EXPECT_EQ(MutateBits(one_u8, 7, 1, true), uint8_t(129));
+  EXPECT_EQ(MutateBits(max_u8, 7, 1, true), max_u8);
+  EXPECT_EQ(MutateBits(max_u8, 7, 1, false), uint8_t(127));
+  EXPECT_EQ(MutateBits(max_u8, 6, 2, true), max_u8);
+  EXPECT_EQ(MutateBits(max_u8, 6, 2, false), uint8_t(63));
+}
+
+TEST(BitUtilsTest, MutateBitsUint8Mid) {
+  const uint8_t zero_u8 = 0;
+  const uint8_t max_u8 = 255;
+
+  EXPECT_EQ(MutateBits(zero_u8, 1, 2, true), uint8_t(6));
+  EXPECT_EQ(MutateBits(max_u8, 1, 2, true), max_u8);
+  EXPECT_EQ(MutateBits(max_u8, 1, 2, false), uint8_t(0xF9));
+  EXPECT_EQ(MutateBits(zero_u8, 2, 3, true), uint8_t(0x1C));
+}
+
+TEST(BitUtilsTest, MutateBitsUint64Mid) {
+  const uint64_t zero_u64 = 0;
+  const uint64_t max_u64 = ~zero_u64;
+
+  EXPECT_EQ(MutateBits(zero_u64, 1, 2, true), uint64_t(6));
+  EXPECT_EQ(MutateBits(max_u64, 1, 2, true), max_u64);
+  EXPECT_EQ(MutateBits(max_u64, 1, 2, false), uint64_t(0xFFFFFFFFFFFFFFF9));
+  EXPECT_EQ(MutateBits(zero_u64, 2, 3, true), uint64_t(0x000000000000001C));
+  EXPECT_EQ(MutateBits(zero_u64, 2, 35, true), uint64_t(0x0000001FFFFFFFFC));
+  EXPECT_EQ(MutateBits(zero_u64, 36, 4, true), uint64_t(0x000000F000000000));
+  EXPECT_EQ(MutateBits(max_u64, 36, 4, false), uint64_t(0xFFFFFF0FFFFFFFFF));
+}
+
+TEST(BitUtilsTest, SetHighBitsUint32) {
+  const uint32_t zero_u32 = 0;
+  const uint32_t one_u32 = 1;
+  const uint32_t max_u32 = ~zero_u32;
+
+  EXPECT_EQ(SetHighBits(zero_u32, 0), zero_u32);
+  EXPECT_EQ(SetHighBits(zero_u32, 1), 0x80000000);
+  EXPECT_EQ(SetHighBits(one_u32, 1), 0x80000001);
+  EXPECT_EQ(SetHighBits(one_u32, 2), 0xC0000001);
+  EXPECT_EQ(SetHighBits(zero_u32, 31), 0xFFFFFFFE);
+  EXPECT_EQ(SetHighBits(zero_u32, 32), max_u32);
+  EXPECT_EQ(SetHighBits(max_u32, 32), max_u32);
+}
+
+TEST(BitUtilsTest, ClearHighBitsUint32) {
+  const uint32_t zero_u32 = 0;
+  const uint32_t one_u32 = 1;
+  const uint32_t max_u32 = ~zero_u32;
+
+  EXPECT_EQ(ClearHighBits(zero_u32, 0), zero_u32);
+  EXPECT_EQ(ClearHighBits(zero_u32, 1), zero_u32);
+  EXPECT_EQ(ClearHighBits(one_u32, 1), one_u32);
+  EXPECT_EQ(ClearHighBits(one_u32, 31), one_u32);
+  EXPECT_EQ(ClearHighBits(one_u32, 32), zero_u32);
+  EXPECT_EQ(ClearHighBits(max_u32, 0), max_u32);
+  EXPECT_EQ(ClearHighBits(max_u32, 1), 0x7FFFFFFF);
+  EXPECT_EQ(ClearHighBits(max_u32, 2), 0x3FFFFFFF);
+  EXPECT_EQ(ClearHighBits(max_u32, 31), one_u32);
+  EXPECT_EQ(ClearHighBits(max_u32, 32), zero_u32);
+}
+
+TEST(BitUtilsTest, IsBitSetAtPositionZero) {
+  const uint32_t zero_u32 = 0;
+  for (size_t i = 0; i != 32; ++i) {
+    EXPECT_FALSE(IsBitAtPositionSet(zero_u32, i));
+  }
+
+  const uint8_t zero_u8 = 0;
+  for (size_t i = 0; i != 8; ++i) {
+    EXPECT_FALSE(IsBitAtPositionSet(zero_u8, i));
+  }
+
+  const uint64_t zero_u64 = 0;
+  for (size_t i = 0; i != 64; ++i) {
+    EXPECT_FALSE(IsBitAtPositionSet(zero_u64, i));
+  }
+}
+
+TEST(BitUtilsTest, IsBitSetAtPositionOne) {
+  const uint32_t one_u32 = 1;
+  for (size_t i = 0; i != 32; ++i) {
+    if (i == 0) {
+      EXPECT_TRUE(IsBitAtPositionSet(one_u32, i));
+    } else {
+      EXPECT_FALSE(IsBitAtPositionSet(one_u32, i));
+    }
+  }
+
+  const uint32_t two_to_17_u32 = 1 << 17;
+  for (size_t i = 0; i != 32; ++i) {
+    if (i == 17) {
+      EXPECT_TRUE(IsBitAtPositionSet(two_to_17_u32, i));
+    } else {
+      EXPECT_FALSE(IsBitAtPositionSet(two_to_17_u32, i));
+    }
+  }
+
+  const uint8_t two_to_4_u8 = 1 << 4;
+  for (size_t i = 0; i != 8; ++i) {
+    if (i == 4) {
+      EXPECT_TRUE(IsBitAtPositionSet(two_to_4_u8, i));
+    } else {
+      EXPECT_FALSE(IsBitAtPositionSet(two_to_4_u8, i));
+    }
+  }
+
+  const uint64_t two_to_55_u64 = uint64_t(1) << 55;
+  for (size_t i = 0; i != 64; ++i) {
+    if (i == 55) {
+      EXPECT_TRUE(IsBitAtPositionSet(two_to_55_u64, i));
+    } else {
+      EXPECT_FALSE(IsBitAtPositionSet(two_to_55_u64, i));
+    }
+  }
+}
+
+TEST(BitUtilsTest, IsBitSetAtPositionAll) {
+  const uint32_t max_u32 = ~0;
+  for (size_t i = 0; i != 32; ++i) {
+    EXPECT_TRUE(IsBitAtPositionSet(max_u32, i));
+  }
+
+  const uint32_t max_u8 = ~uint8_t(0);
+  for (size_t i = 0; i != 8; ++i) {
+    EXPECT_TRUE(IsBitAtPositionSet(max_u8, i));
+  }
+
+  const uint64_t max_u64 = ~uint64_t(0);
+  for (size_t i = 0; i != 64; ++i) {
+    EXPECT_TRUE(IsBitAtPositionSet(max_u64, i));
+  }
+}
+}  // namespace
+}  // namespace utils
+}  // namespace spvtools
diff --git a/test/val/CMakeLists.txt b/test/val/CMakeLists.txt
index b52c764..d4bfe1d 100644
--- a/test/val/CMakeLists.txt
+++ b/test/val/CMakeLists.txt
@@ -28,7 +28,6 @@
        val_barriers_test.cpp
        val_bitwise_test.cpp
        val_builtins_test.cpp
-       val_capability_test.cpp
        val_cfg_test.cpp
        val_composites_test.cpp
        val_constants_test.cpp
@@ -36,6 +35,7 @@
        val_data_test.cpp
        val_decoration_test.cpp
        val_derivatives_test.cpp
+       val_entry_point.cpp
        val_explicit_reserved_test.cpp
        val_extensions_test.cpp
        val_ext_inst_test.cpp
@@ -44,6 +44,13 @@
   PCH_FILE pch_test_val
 )
 
+add_spvtools_unittest(TARGET val_capability
+  SRCS
+       val_capability_test.cpp
+  LIBS ${SPIRV_TOOLS}
+  PCH_FILE pch_test_val
+)
+
 add_spvtools_unittest(TARGET val_limits
   SRCS val_limits_test.cpp
        ${VAL_TEST_COMMON_SRCS}
diff --git a/test/val/val_adjacency_test.cpp b/test/val/val_adjacency_test.cpp
index 5c1124a..e61c03d 100644
--- a/test/val/val_adjacency_test.cpp
+++ b/test/val/val_adjacency_test.cpp
@@ -144,7 +144,7 @@
 %false_label = OpLabel
 OpBranch %end_label
 %end_label = OpLabel
-%line = OpLine %string 0 0
+OpLine %string 0 0
 %result = OpPhi %bool %true %true_label %false %false_label
 )";
 
@@ -178,7 +178,7 @@
 %false_label = OpLabel
 OpBranch %end_label
 %end_label = OpLabel
-%line = OpLine %string 0 0
+OpLine %string 0 0
 %result = OpPhi %bool %true %true_label %false %false_label
 )";
 
diff --git a/test/val/val_atomics_test.cpp b/test/val/val_atomics_test.cpp
index 57a1187..cd723b2 100644
--- a/test/val/val_atomics_test.cpp
+++ b/test/val/val_atomics_test.cpp
@@ -499,9 +499,8 @@
 
   CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr("AtomicLoad: expected Memory Scope to be a 32-bit int"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("AtomicLoad: expected scope to be a 32-bit int"));
 }
 
 TEST_F(ValidateAtomics, AtomicLoadWrongMemorySemanticsType) {
@@ -674,10 +673,9 @@
 
   CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr("AtomicStore: expected Memory Scope to be a 32-bit int\n  "
-                "OpAtomicStore %28 %float_1 %uint_0_1 %float_1\n"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("AtomicStore: expected scope to be a 32-bit int\n  "
+                        "OpAtomicStore %28 %float_1 %uint_0_1 %float_1\n"));
 }
 
 TEST_F(ValidateAtomics, AtomicStoreWrongMemorySemanticsType) {
@@ -708,7 +706,7 @@
 
 TEST_F(ValidateAtomics, AtomicExchangeShaderSuccess) {
   const std::string body = R"(
-%val1 = OpAtomicStore %u32_var %device %relaxed %u32_1
+OpAtomicStore %u32_var %device %relaxed %u32_1
 %val2 = OpAtomicExchange %u32 %u32_var %device %relaxed %u32_0
 )";
 
@@ -720,7 +718,7 @@
   const std::string body = R"(
 OpAtomicStore %f32_var %device %relaxed %f32_1
 %val2 = OpAtomicExchange %f32 %f32_var %device %relaxed %f32_0
-%val3 = OpAtomicStore %u32_var %device %relaxed %u32_1
+OpAtomicStore %u32_var %device %relaxed %u32_1
 %val4 = OpAtomicExchange %u32 %u32_var %device %relaxed %u32_0
 )";
 
@@ -743,7 +741,7 @@
 
 TEST_F(ValidateAtomics, AtomicExchangeWrongResultType) {
   const std::string body = R"(
-%val1 = OpStore %f32vec4_var %f32vec4_0000
+OpStore %f32vec4_var %f32vec4_0000
 %val2 = OpAtomicExchange %f32vec4 %f32vec4_var %device %relaxed %f32vec4_0000
 )";
 
@@ -768,7 +766,7 @@
 
 TEST_F(ValidateAtomics, AtomicExchangeWrongPointerDataType) {
   const std::string body = R"(
-%val1 = OpStore %f32vec4_var %f32vec4_0000
+OpStore %f32vec4_var %f32vec4_0000
 %val2 = OpAtomicExchange %f32 %f32vec4_var %device %relaxed %f32vec4_0000
 )";
 
@@ -788,9 +786,8 @@
 
   CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr("AtomicExchange: expected Memory Scope to be a 32-bit int"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("AtomicExchange: expected scope to be a 32-bit int"));
 }
 
 TEST_F(ValidateAtomics, AtomicExchangeWrongMemorySemanticsType) {
@@ -822,7 +819,7 @@
 
 TEST_F(ValidateAtomics, AtomicCompareExchangeShaderSuccess) {
   const std::string body = R"(
-%val1 = OpAtomicStore %u32_var %device %relaxed %u32_1
+OpAtomicStore %u32_var %device %relaxed %u32_1
 %val2 = OpAtomicCompareExchange %u32 %u32_var %device %relaxed %relaxed %u32_0 %u32_0
 )";
 
@@ -834,7 +831,7 @@
   const std::string body = R"(
 OpAtomicStore %f32_var %device %relaxed %f32_1
 %val2 = OpAtomicCompareExchange %f32 %f32_var %device %relaxed %relaxed %f32_0 %f32_1
-%val3 = OpAtomicStore %u32_var %device %relaxed %u32_1
+OpAtomicStore %u32_var %device %relaxed %u32_1
 %val4 = OpAtomicCompareExchange %u32 %u32_var %device %relaxed %relaxed %u32_0 %u32_0
 )";
 
@@ -857,7 +854,7 @@
 
 TEST_F(ValidateAtomics, AtomicCompareExchangeWrongResultType) {
   const std::string body = R"(
-%val1 = OpStore %f32vec4_var %f32vec4_0000
+OpStore %f32vec4_var %f32vec4_0000
 %val2 = OpAtomicCompareExchange %f32vec4 %f32vec4_var %device %relaxed %relaxed %f32vec4_0000 %f32vec4_0000
 )";
 
@@ -882,7 +879,7 @@
 
 TEST_F(ValidateAtomics, AtomicCompareExchangeWrongPointerDataType) {
   const std::string body = R"(
-%val1 = OpStore %f32vec4_var %f32vec4_0000
+OpStore %f32vec4_var %f32vec4_0000
 %val2 = OpAtomicCompareExchange %f32 %f32vec4_var %device %relaxed %relaxed %f32_0 %f32_1
 )";
 
@@ -902,10 +899,9 @@
 
   CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr("AtomicCompareExchange: expected Memory Scope to be a 32-bit "
-                "int"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("AtomicCompareExchange: expected scope to be a 32-bit "
+                        "int"));
 }
 
 TEST_F(ValidateAtomics, AtomicCompareExchangeWrongMemorySemanticsType1) {
@@ -975,7 +971,7 @@
 
 TEST_F(ValidateAtomics, AtomicCompareExchangeWeakSuccess) {
   const std::string body = R"(
-%val3 = OpAtomicStore %u32_var %device %relaxed %u32_1
+OpAtomicStore %u32_var %device %relaxed %u32_1
 %val4 = OpAtomicCompareExchangeWeak %u32 %u32_var %device %relaxed %relaxed %u32_0 %u32_0
 )";
 
@@ -1085,8 +1081,7 @@
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(
       getDiagnosticString(),
-      HasSubstr(
-          "AtomicFlagTestAndSet: expected Memory Scope to be a 32-bit int"));
+      HasSubstr("AtomicFlagTestAndSet: expected scope to be a 32-bit int"));
 }
 
 TEST_F(ValidateAtomics, AtomicFlagTestAndSetWrongMemorySemanticsType) {
@@ -1159,7 +1154,7 @@
   CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("AtomicFlagClear: expected Memory Scope to be a 32-bit "
+              HasSubstr("AtomicFlagClear: expected scope to be a 32-bit "
                         "int\n  OpAtomicFlagClear %30 %ulong_1 %uint_0_1\n"));
 }
 
diff --git a/test/val/val_barriers_test.cpp b/test/val/val_barriers_test.cpp
index 18f57f8..ae166d9 100644
--- a/test/val/val_barriers_test.cpp
+++ b/test/val/val_barriers_test.cpp
@@ -341,9 +341,8 @@
 
   CompileSuccessfully(GenerateShaderCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr("ControlBarrier: expected Execution Scope to be a 32-bit int"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("ControlBarrier: expected scope to be a 32-bit int"));
 }
 
 TEST_F(ValidateBarriers, OpControlBarrierU64ExecutionScope) {
@@ -353,9 +352,8 @@
 
   CompileSuccessfully(GenerateShaderCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr("ControlBarrier: expected Execution Scope to be a 32-bit int"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("ControlBarrier: expected scope to be a 32-bit int"));
 }
 
 TEST_F(ValidateBarriers, OpControlBarrierFloatMemoryScope) {
@@ -365,9 +363,8 @@
 
   CompileSuccessfully(GenerateShaderCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr("ControlBarrier: expected Memory Scope to be a 32-bit int"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("ControlBarrier: expected scope to be a 32-bit int"));
 }
 
 TEST_F(ValidateBarriers, OpControlBarrierU64MemoryScope) {
@@ -377,9 +374,8 @@
 
   CompileSuccessfully(GenerateShaderCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr("ControlBarrier: expected Memory Scope to be a 32-bit int"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("ControlBarrier: expected scope to be a 32-bit int"));
 }
 
 TEST_F(ValidateBarriers, OpControlBarrierFloatMemorySemantics) {
@@ -797,9 +793,8 @@
 
   CompileSuccessfully(GenerateShaderCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr("MemoryBarrier: expected Memory Scope to be a 32-bit int"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("MemoryBarrier: expected scope to be a 32-bit int"));
 }
 
 TEST_F(ValidateBarriers, OpMemoryBarrierU64MemoryScope) {
@@ -809,9 +804,8 @@
 
   CompileSuccessfully(GenerateShaderCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr("MemoryBarrier: expected Memory Scope to be a 32-bit int"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("MemoryBarrier: expected scope to be a 32-bit int"));
 }
 
 TEST_F(ValidateBarriers, OpMemoryBarrierFloatMemorySemantics) {
@@ -993,8 +987,7 @@
             ValidateInstructions(SPV_ENV_UNIVERSAL_1_1));
   EXPECT_THAT(
       getDiagnosticString(),
-      HasSubstr(
-          "MemoryNamedBarrier: expected Memory Scope to be a 32-bit int"));
+      HasSubstr("MemoryNamedBarrier: expected scope to be a 32-bit int"));
 }
 
 TEST_F(ValidateBarriers, OpMemoryNamedBarrierFloatMemorySemantics) {
diff --git a/test/val/val_builtins_test.cpp b/test/val/val_builtins_test.cpp
index e3eca09..58593dc 100644
--- a/test/val/val_builtins_test.cpp
+++ b/test/val/val_builtins_test.cpp
@@ -2330,9 +2330,9 @@
 
   CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0);
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Vulkan spec requires BuiltIn WorkgroupSize to be a "
-                        "constant. ID <2> (OpCopyObject) is not a constant"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("BuiltIns can only target variables, structs or constants"));
 }
 
 CodeGenerator GetWorkgroupSizeNotVectorGenerator(spv_target_env env) {
@@ -3302,6 +3302,78 @@
       HasSubstr("BuiltIn SubgroupId cannot be used as a member decoration"));
 }
 
+TEST_F(ValidateBuiltIns, TargetIsType) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpDecorate %void BuiltIn Position
+%void = OpTypeVoid
+)";
+
+  CompileSuccessfully(text);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("BuiltIns can only target variables, structs or constants"));
+}
+
+TEST_F(ValidateBuiltIns, TargetIsVariable) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpDecorate %wg_var BuiltIn Position
+%int = OpTypeInt 32 0
+%int_wg_ptr = OpTypePointer Workgroup %int
+%wg_var = OpVariable %int_wg_ptr Workgroup
+)";
+
+  CompileSuccessfully(text);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateBuiltIns, TargetIsStruct) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpDecorate %struct BuiltIn Position
+%struct = OpTypeStruct
+)";
+
+  CompileSuccessfully(text);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateBuiltIns, TargetIsConstant) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpDecorate %int0 BuiltIn Position
+%int = OpTypeInt 32 0
+%int0 = OpConstant %int 0
+)";
+
+  CompileSuccessfully(text);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateBuiltIns, TargetIsSpecConstant) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpDecorate %int0 BuiltIn Position
+%int = OpTypeInt 32 0
+%int0 = OpSpecConstant %int 0
+)";
+
+  CompileSuccessfully(text);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
 }  // namespace
 }  // namespace val
 }  // namespace spvtools
diff --git a/test/val/val_capability_test.cpp b/test/val/val_capability_test.cpp
index 5a6e751..ae1d62b 100644
--- a/test/val/val_capability_test.cpp
+++ b/test/val/val_capability_test.cpp
@@ -23,6 +23,7 @@
 #include "gmock/gmock.h"
 #include "source/assembly_grammar.h"
 #include "source/spirv_target_env.h"
+#include "spirv-tools/libspirv.h"
 #include "test/test_fixture.h"
 #include "test/unit_spirv.h"
 #include "test/val/val_fixtures.h"
@@ -33,6 +34,7 @@
 
 using spvtest::ScopedContext;
 using testing::Combine;
+using testing::Eq;
 using testing::HasSubstr;
 using testing::Values;
 using testing::ValuesIn;
@@ -1286,221 +1288,264 @@
                             Values(
 std::make_pair(std::string(kOpenCLMemoryModel) +
           "OpEntryPoint Kernel %func \"compute\" \n" +
-          "OpDecorate %intt BuiltIn Position\n"
-          "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+          "OpDecorate %int0 BuiltIn Position\n"
+          "%intt = OpTypeInt 32 0\n"
+          "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
           ShaderDependencies()),
 // Just mentioning PointSize, ClipDistance, or CullDistance as a BuiltIn does
 // not trigger the requirement for the associated capability.
 // See https://github.com/KhronosGroup/SPIRV-Tools/issues/365
 std::make_pair(std::string(kOpenCLMemoryModel) +
           "OpEntryPoint Kernel %func \"compute\" \n" +
-          "OpDecorate %intt BuiltIn PointSize\n"
-          "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+          "OpDecorate %int0 BuiltIn PointSize\n"
+          "%intt = OpTypeInt 32 0\n"
+          "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
           AllCapabilities()),
 std::make_pair(std::string(kOpenCLMemoryModel) +
           "OpEntryPoint Kernel %func \"compute\" \n" +
-          "OpDecorate %intt BuiltIn ClipDistance\n"
-          "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+          "OpDecorate %int0 BuiltIn ClipDistance\n"
+          "%intt = OpTypeInt 32 0\n"
+          "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
           AllCapabilities()),
 std::make_pair(std::string(kOpenCLMemoryModel) +
           "OpEntryPoint Kernel %func \"compute\" \n" +
-          "OpDecorate %intt BuiltIn CullDistance\n"
-          "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+          "OpDecorate %int0 BuiltIn CullDistance\n"
+          "%intt = OpTypeInt 32 0\n"
+          "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
           AllCapabilities()),
 std::make_pair(std::string(kOpenCLMemoryModel) +
           "OpEntryPoint Kernel %func \"compute\" \n" +
-          "OpDecorate %intt BuiltIn VertexId\n"
-          "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+          "OpDecorate %int0 BuiltIn VertexId\n"
+          "%intt = OpTypeInt 32 0\n"
+          "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
           ShaderDependencies()),
 std::make_pair(std::string(kOpenCLMemoryModel) +
           "OpEntryPoint Kernel %func \"compute\" \n" +
-          "OpDecorate %intt BuiltIn InstanceId\n"
-          "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+          "OpDecorate %int0 BuiltIn InstanceId\n"
+          "%intt = OpTypeInt 32 0\n"
+          "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
           ShaderDependencies()),
 std::make_pair(std::string(kOpenCLMemoryModel) +
           "OpEntryPoint Kernel %func \"compute\" \n" +
-          "OpDecorate %intt BuiltIn PrimitiveId\n"
-          "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+          "OpDecorate %int0 BuiltIn PrimitiveId\n"
+          "%intt = OpTypeInt 32 0\n"
+          "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
           GeometryTessellationDependencies()),
 std::make_pair(std::string(kOpenCLMemoryModel) +
           "OpEntryPoint Kernel %func \"compute\" \n" +
-          "OpDecorate %intt BuiltIn InvocationId\n"
-          "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+          "OpDecorate %int0 BuiltIn InvocationId\n"
+          "%intt = OpTypeInt 32 0\n"
+          "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
           GeometryTessellationDependencies()),
 std::make_pair(std::string(kOpenCLMemoryModel) +
           "OpEntryPoint Kernel %func \"compute\" \n" +
-          "OpDecorate %intt BuiltIn Layer\n"
-          "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+          "OpDecorate %int0 BuiltIn Layer\n"
+          "%intt = OpTypeInt 32 0\n"
+          "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
           GeometryDependencies()),
 std::make_pair(std::string(kOpenCLMemoryModel) +
           "OpEntryPoint Kernel %func \"compute\" \n" +
-          "OpDecorate %intt BuiltIn ViewportIndex\n"
-          "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+          "OpDecorate %int0 BuiltIn ViewportIndex\n"
+          "%intt = OpTypeInt 32 0\n"
+          "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
           std::vector<std::string>{"MultiViewport"}),
 std::make_pair(std::string(kOpenCLMemoryModel) +
           "OpEntryPoint Kernel %func \"compute\" \n" +
-          "OpDecorate %intt BuiltIn TessLevelOuter\n"
-          "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+          "OpDecorate %int0 BuiltIn TessLevelOuter\n"
+          "%intt = OpTypeInt 32 0\n"
+          "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
           TessellationDependencies()),
 std::make_pair(std::string(kOpenCLMemoryModel) +
           "OpEntryPoint Kernel %func \"compute\" \n" +
-          "OpDecorate %intt BuiltIn TessLevelInner\n"
-          "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+          "OpDecorate %int0 BuiltIn TessLevelInner\n"
+          "%intt = OpTypeInt 32 0\n"
+          "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
           TessellationDependencies()),
 std::make_pair(std::string(kOpenCLMemoryModel) +
           "OpEntryPoint Kernel %func \"compute\" \n" +
-          "OpDecorate %intt BuiltIn TessCoord\n"
-          "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+          "OpDecorate %int0 BuiltIn TessCoord\n"
+          "%intt = OpTypeInt 32 0\n"
+          "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
           TessellationDependencies()),
 std::make_pair(std::string(kOpenCLMemoryModel) +
           "OpEntryPoint Kernel %func \"compute\" \n" +
-          "OpDecorate %intt BuiltIn PatchVertices\n"
-          "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+          "OpDecorate %int0 BuiltIn PatchVertices\n"
+          "%intt = OpTypeInt 32 0\n"
+          "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
           TessellationDependencies()),
 std::make_pair(std::string(kOpenCLMemoryModel) +
           "OpEntryPoint Kernel %func \"compute\" \n" +
-          "OpDecorate %intt BuiltIn FragCoord\n"
-          "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+          "OpDecorate %int0 BuiltIn FragCoord\n"
+          "%intt = OpTypeInt 32 0\n"
+          "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
           ShaderDependencies()),
 std::make_pair(std::string(kOpenCLMemoryModel) +
           "OpEntryPoint Kernel %func \"compute\" \n" +
-          "OpDecorate %intt BuiltIn PointCoord\n"
-          "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+          "OpDecorate %int0 BuiltIn PointCoord\n"
+          "%intt = OpTypeInt 32 0\n"
+          "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
           ShaderDependencies()),
 std::make_pair(std::string(kOpenCLMemoryModel) +
           "OpEntryPoint Kernel %func \"compute\" \n" +
-          "OpDecorate %intt BuiltIn FrontFacing\n"
-          "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+          "OpDecorate %int0 BuiltIn FrontFacing\n"
+          "%intt = OpTypeInt 32 0\n"
+          "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
           ShaderDependencies()),
 std::make_pair(std::string(kOpenCLMemoryModel) +
           "OpEntryPoint Kernel %func \"compute\" \n" +
-          "OpDecorate %intt BuiltIn SampleId\n"
-          "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+          "OpDecorate %int0 BuiltIn SampleId\n"
+          "%intt = OpTypeInt 32 0\n"
+          "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
           std::vector<std::string>{"SampleRateShading"}),
 std::make_pair(std::string(kOpenCLMemoryModel) +
           "OpEntryPoint Kernel %func \"compute\" \n" +
-          "OpDecorate %intt BuiltIn SamplePosition\n"
-          "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+          "OpDecorate %int0 BuiltIn SamplePosition\n"
+          "%intt = OpTypeInt 32 0\n"
+          "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
           std::vector<std::string>{"SampleRateShading"}),
 std::make_pair(std::string(kOpenCLMemoryModel) +
           "OpEntryPoint Kernel %func \"compute\" \n" +
-          "OpDecorate %intt BuiltIn SampleMask\n"
-          "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+          "OpDecorate %int0 BuiltIn SampleMask\n"
+          "%intt = OpTypeInt 32 0\n"
+          "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
           ShaderDependencies()),
 std::make_pair(std::string(kOpenCLMemoryModel) +
           "OpEntryPoint Kernel %func \"compute\" \n" +
-          "OpDecorate %intt BuiltIn FragDepth\n"
-          "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+          "OpDecorate %int0 BuiltIn FragDepth\n"
+          "%intt = OpTypeInt 32 0\n"
+          "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
           ShaderDependencies()),
 std::make_pair(std::string(kOpenCLMemoryModel) +
           "OpEntryPoint Kernel %func \"compute\" \n" +
-          "OpDecorate %intt BuiltIn HelperInvocation\n"
-          "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+          "OpDecorate %int0 BuiltIn HelperInvocation\n"
+          "%intt = OpTypeInt 32 0\n"
+          "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
           ShaderDependencies()),
 std::make_pair(std::string(kOpenCLMemoryModel) +
           "OpEntryPoint Kernel %func \"compute\" \n" +
-          "OpDecorate %intt BuiltIn VertexIndex\n"
-          "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+          "OpDecorate %int0 BuiltIn VertexIndex\n"
+          "%intt = OpTypeInt 32 0\n"
+          "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
           ShaderDependencies()),
 std::make_pair(std::string(kOpenCLMemoryModel) +
           "OpEntryPoint Kernel %func \"compute\" \n" +
-          "OpDecorate %intt BuiltIn InstanceIndex\n"
-          "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+          "OpDecorate %int0 BuiltIn InstanceIndex\n"
+          "%intt = OpTypeInt 32 0\n"
+          "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
           ShaderDependencies()),
 std::make_pair(std::string(kOpenCLMemoryModel) +
           "OpEntryPoint Kernel %func \"compute\" \n" +
-          "OpDecorate %intt BuiltIn NumWorkgroups\n"
-          "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+          "OpDecorate %int0 BuiltIn NumWorkgroups\n"
+          "%intt = OpTypeInt 32 0\n"
+          "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
           AllCapabilities()),
 std::make_pair(std::string(kOpenCLMemoryModel) +
           "OpEntryPoint Kernel %func \"compute\" \n" +
-          "OpDecorate %intt BuiltIn WorkgroupSize\n"
-          "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+          "OpDecorate %int0 BuiltIn WorkgroupSize\n"
+          "%intt = OpTypeInt 32 0\n"
+          "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
           AllCapabilities()),
 std::make_pair(std::string(kOpenCLMemoryModel) +
           "OpEntryPoint Kernel %func \"compute\" \n" +
-          "OpDecorate %intt BuiltIn WorkgroupId\n"
-          "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+          "OpDecorate %int0 BuiltIn WorkgroupId\n"
+          "%intt = OpTypeInt 32 0\n"
+          "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
           AllCapabilities()),
 std::make_pair(std::string(kOpenCLMemoryModel) +
           "OpEntryPoint Kernel %func \"compute\" \n" +
-          "OpDecorate %intt BuiltIn LocalInvocationId\n"
-          "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+          "OpDecorate %int0 BuiltIn LocalInvocationId\n"
+          "%intt = OpTypeInt 32 0\n"
+          "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
           AllCapabilities()),
 std::make_pair(std::string(kOpenCLMemoryModel) +
           "OpEntryPoint Kernel %func \"compute\" \n" +
-          "OpDecorate %intt BuiltIn GlobalInvocationId\n"
-          "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+          "OpDecorate %int0 BuiltIn GlobalInvocationId\n"
+          "%intt = OpTypeInt 32 0\n"
+          "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
           AllCapabilities()),
 std::make_pair(std::string(kOpenCLMemoryModel) +
           "OpEntryPoint Kernel %func \"compute\" \n" +
-          "OpDecorate %intt BuiltIn LocalInvocationIndex\n"
-          "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+          "OpDecorate %int0 BuiltIn LocalInvocationIndex\n"
+          "%intt = OpTypeInt 32 0\n"
+          "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
           AllCapabilities()),
 std::make_pair(std::string(kGLSL450MemoryModel) +
           "OpEntryPoint Vertex %func \"shader\" \n" +
-          "OpDecorate %intt BuiltIn WorkDim\n"
-          "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+          "OpDecorate %int0 BuiltIn WorkDim\n"
+          "%intt = OpTypeInt 32 0\n"
+          "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
           KernelDependencies()),
 std::make_pair(std::string(kGLSL450MemoryModel) +
           "OpEntryPoint Vertex %func \"shader\" \n" +
-          "OpDecorate %intt BuiltIn GlobalSize\n"
-          "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+          "OpDecorate %int0 BuiltIn GlobalSize\n"
+          "%intt = OpTypeInt 32 0\n"
+          "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
           KernelDependencies()),
 std::make_pair(std::string(kGLSL450MemoryModel) +
           "OpEntryPoint Vertex %func \"shader\" \n" +
-          "OpDecorate %intt BuiltIn EnqueuedWorkgroupSize\n"
-          "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+          "OpDecorate %int0 BuiltIn EnqueuedWorkgroupSize\n"
+          "%intt = OpTypeInt 32 0\n"
+          "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
           KernelDependencies()),
 std::make_pair(std::string(kGLSL450MemoryModel) +
           "OpEntryPoint Vertex %func \"shader\" \n" +
-          "OpDecorate %intt BuiltIn GlobalOffset\n"
-          "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+          "OpDecorate %int0 BuiltIn GlobalOffset\n"
+          "%intt = OpTypeInt 32 0\n"
+          "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
           KernelDependencies()),
 std::make_pair(std::string(kGLSL450MemoryModel) +
           "OpEntryPoint Vertex %func \"shader\" \n" +
-          "OpDecorate %intt BuiltIn GlobalLinearId\n"
-          "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+          "OpDecorate %int0 BuiltIn GlobalLinearId\n"
+          "%intt = OpTypeInt 32 0\n"
+          "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
           KernelDependencies()),
 std::make_pair(std::string(kGLSL450MemoryModel) +
           "OpEntryPoint Vertex %func \"shader\" \n" +
-          "OpDecorate %intt BuiltIn SubgroupSize\n"
-          "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+          "OpDecorate %int0 BuiltIn SubgroupSize\n"
+          "%intt = OpTypeInt 32 0\n"
+          "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
           KernelAndGroupNonUniformDependencies()),
 std::make_pair(std::string(kGLSL450MemoryModel) +
           "OpEntryPoint Vertex %func \"shader\" \n" +
-          "OpDecorate %intt BuiltIn SubgroupMaxSize\n"
-          "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+          "OpDecorate %int0 BuiltIn SubgroupMaxSize\n"
+          "%intt = OpTypeInt 32 0\n"
+          "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
           KernelDependencies()),
 std::make_pair(std::string(kGLSL450MemoryModel) +
           "OpEntryPoint Vertex %func \"shader\" \n" +
-          "OpDecorate %intt BuiltIn NumSubgroups\n"
-          "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+          "OpDecorate %int0 BuiltIn NumSubgroups\n"
+          "%intt = OpTypeInt 32 0\n"
+          "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
           KernelAndGroupNonUniformDependencies()),
 std::make_pair(std::string(kGLSL450MemoryModel) +
           "OpEntryPoint Vertex %func \"shader\" \n" +
-          "OpDecorate %intt BuiltIn NumEnqueuedSubgroups\n"
-          "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+          "OpDecorate %int0 BuiltIn NumEnqueuedSubgroups\n"
+          "%intt = OpTypeInt 32 0\n"
+          "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
           KernelDependencies()),
 std::make_pair(std::string(kGLSL450MemoryModel) +
           "OpEntryPoint Vertex %func \"shader\" \n" +
-          "OpDecorate %intt BuiltIn SubgroupId\n"
-          "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+          "OpDecorate %int0 BuiltIn SubgroupId\n"
+          "%intt = OpTypeInt 32 0\n"
+          "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
           KernelAndGroupNonUniformDependencies()),
 std::make_pair(std::string(kGLSL450MemoryModel) +
           "OpEntryPoint Vertex %func \"shader\" \n" +
-          "OpDecorate %intt BuiltIn SubgroupLocalInvocationId\n"
-          "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+          "OpDecorate %int0 BuiltIn SubgroupLocalInvocationId\n"
+          "%intt = OpTypeInt 32 0\n"
+          "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
           KernelAndGroupNonUniformDependencies()),
 std::make_pair(std::string(kOpenCLMemoryModel) +
           "OpEntryPoint Kernel %func \"compute\" \n" +
-          "OpDecorate %intt BuiltIn VertexIndex\n"
-          "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+          "OpDecorate %int0 BuiltIn VertexIndex\n"
+          "%intt = OpTypeInt 32 0\n"
+          "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
           ShaderDependencies()),
 std::make_pair(std::string(kOpenCLMemoryModel) +
           "OpEntryPoint Kernel %func \"compute\" \n" +
-          "OpDecorate %intt BuiltIn InstanceIndex\n"
-          "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+          "OpDecorate %int0 BuiltIn InstanceIndex\n"
+          "%intt = OpTypeInt 32 0\n"
+          "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
           ShaderDependencies())
 )));
 
@@ -1548,18 +1593,21 @@
                             Values(
 std::make_pair(std::string(kGLSL450MemoryModel) +
           "OpEntryPoint Vertex %func \"shader\" \n" +
-          "OpDecorate %intt BuiltIn PointSize\n"
-          "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+          "OpDecorate %int0 BuiltIn PointSize\n"
+          "%intt = OpTypeInt 32 0\n"
+          "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
           AllSpirV10Capabilities()),
 std::make_pair(std::string(kGLSL450MemoryModel) +
           "OpEntryPoint Vertex %func \"shader\" \n" +
-          "OpDecorate %intt BuiltIn ClipDistance\n"
-          "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+          "OpDecorate %int0 BuiltIn ClipDistance\n"
+          "%intt = OpTypeInt 32 0\n"
+          "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
           AllSpirV10Capabilities()),
 std::make_pair(std::string(kGLSL450MemoryModel) +
           "OpEntryPoint Vertex %func \"shader\" \n" +
-          "OpDecorate %intt BuiltIn CullDistance\n"
-          "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+          "OpDecorate %int0 BuiltIn CullDistance\n"
+          "%intt = OpTypeInt 32 0\n"
+          "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
           AllSpirV10Capabilities())
 )));
 
@@ -1580,13 +1628,15 @@
                             Values(
 std::make_pair(std::string(kGLSL450MemoryModel) +
           "OpEntryPoint Vertex %func \"shader\" \n" +
-          "OpDecorate %intt BuiltIn PointSize\n"
-          "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+          "OpDecorate %int0 BuiltIn PointSize\n"
+          "%intt = OpTypeInt 32 0\n"
+          "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
           AllVulkan11Capabilities()),
 std::make_pair(std::string(kGLSL450MemoryModel) +
           "OpEntryPoint Vertex %func \"shader\" \n" +
-          "OpDecorate %intt BuiltIn CullDistance\n"
-          "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+          "OpDecorate %int0 BuiltIn CullDistance\n"
+          "%intt = OpTypeInt 32 0\n"
+          "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
           AllVulkan11Capabilities())
 )));
 
@@ -2436,6 +2486,178 @@
       << getDiagnosticString();
 }
 
+// Test that extensions incorporated into SPIR-V 1.5 no longer require
+// the associated OpExtension instruction.  Test one capability per extension.
+
+struct CapabilityExtensionVersionCase {
+  std::string capability;
+  std::string capability_new_name;
+  std::string extension;
+  spv_target_env last_version_requiring_extension;
+  spv_target_env first_version_in_core;
+};
+
+using ValidateCapabilityExtensionVersionTest =
+    spvtest::ValidateBase<CapabilityExtensionVersionCase>;
+
+// Returns a minimal shader module with the given capability instruction.
+std::string MinimalShaderModuleWithCapability(std::string cap) {
+  std::string mem_model =
+      (cap.find("VulkanMemory") == 0) ? "VulkanKHR" : "GLSL450";
+  std::string extra_cap = (cap.find("VulkanMemoryModelDeviceScope") == 0)
+                              ? "\nOpCapability VulkanMemoryModelKHR\n"
+                              : "";
+  return std::string("OpCapability ") + cap + extra_cap + R"(
+OpCapability Shader
+OpMemoryModel Logical )" + mem_model + R"(
+OpEntryPoint Vertex %main "main"
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+}
+
+TEST_P(ValidateCapabilityExtensionVersionTest, FailsInOlderSpirvVersion) {
+  const auto spirv = MinimalShaderModuleWithCapability(GetParam().capability);
+  CompileSuccessfully(spirv, GetParam().last_version_requiring_extension);
+  EXPECT_EQ(SPV_ERROR_MISSING_EXTENSION,
+            ValidateInstructions(GetParam().last_version_requiring_extension));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr(std::string("1st operand of Capability: operand ") +
+                        GetParam().capability_new_name))
+      << spirv << "\n";
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr(std::string("requires one of these extensions: ") +
+                        GetParam().extension));
+}
+
+TEST_P(ValidateCapabilityExtensionVersionTest,
+       SucceedsInNewerSpirvVersionWithOldName) {
+  const auto spirv = MinimalShaderModuleWithCapability(GetParam().capability);
+  CompileSuccessfully(spirv, GetParam().first_version_in_core);
+  EXPECT_EQ(SPV_SUCCESS,
+            ValidateInstructions(GetParam().first_version_in_core));
+  EXPECT_THAT(getDiagnosticString(), Eq("")) << spirv << "\n";
+}
+
+TEST_P(ValidateCapabilityExtensionVersionTest,
+       SucceedsInNewerSpirvVersionWithNewName) {
+  const auto spirv =
+      MinimalShaderModuleWithCapability(GetParam().capability_new_name);
+  CompileSuccessfully(spirv, GetParam().first_version_in_core);
+  EXPECT_EQ(SPV_SUCCESS,
+            ValidateInstructions(GetParam().first_version_in_core));
+  EXPECT_THAT(getDiagnosticString(), Eq("")) << spirv << "\n";
+}
+
+std::vector<CapabilityExtensionVersionCase> CapVersionCases1_5() {
+#define IN15NOSUFFIX(C, E) \
+  { C, C, E, SPV_ENV_UNIVERSAL_1_4, SPV_ENV_UNIVERSAL_1_5 }
+#define IN15(C, C_WITHOUT_SUFFIX, E) \
+  { C, C_WITHOUT_SUFFIX, E, SPV_ENV_UNIVERSAL_1_4, SPV_ENV_UNIVERSAL_1_5 }
+  return std::vector<CapabilityExtensionVersionCase>{
+      // SPV_KHR_8bit_storage
+      IN15NOSUFFIX("StorageBuffer8BitAccess", "SPV_KHR_8bit_storage"),
+      IN15NOSUFFIX("UniformAndStorageBuffer8BitAccess", "SPV_KHR_8bit_storage"),
+      IN15NOSUFFIX("StoragePushConstant8", "SPV_KHR_8bit_storage"),
+      // SPV_EXT_descriptor_indexing
+      IN15("ShaderNonUniformEXT", "ShaderNonUniform",
+           "SPV_EXT_descriptor_indexing"),
+      IN15("RuntimeDescriptorArrayEXT", "RuntimeDescriptorArray",
+           "SPV_EXT_descriptor_indexing"),
+      IN15("InputAttachmentArrayDynamicIndexingEXT",
+           "InputAttachmentArrayDynamicIndexing",
+           "SPV_EXT_descriptor_indexing"),
+      IN15("UniformTexelBufferArrayDynamicIndexingEXT",
+           "UniformTexelBufferArrayDynamicIndexing",
+           "SPV_EXT_descriptor_indexing"),
+      IN15("StorageTexelBufferArrayDynamicIndexingEXT",
+           "StorageTexelBufferArrayDynamicIndexing",
+           "SPV_EXT_descriptor_indexing"),
+      IN15("UniformBufferArrayNonUniformIndexingEXT",
+           "UniformBufferArrayNonUniformIndexing",
+           "SPV_EXT_descriptor_indexing"),
+      IN15("SampledImageArrayNonUniformIndexingEXT",
+           "SampledImageArrayNonUniformIndexing",
+           "SPV_EXT_descriptor_indexing"),
+      IN15("StorageBufferArrayNonUniformIndexingEXT",
+           "StorageBufferArrayNonUniformIndexing",
+           "SPV_EXT_descriptor_indexing"),
+      IN15("StorageImageArrayNonUniformIndexingEXT",
+           "StorageImageArrayNonUniformIndexing",
+           "SPV_EXT_descriptor_indexing"),
+      IN15("InputAttachmentArrayNonUniformIndexingEXT",
+           "InputAttachmentArrayNonUniformIndexing",
+           "SPV_EXT_descriptor_indexing"),
+      IN15("UniformTexelBufferArrayNonUniformIndexingEXT",
+           "UniformTexelBufferArrayNonUniformIndexing",
+           "SPV_EXT_descriptor_indexing"),
+      IN15("StorageTexelBufferArrayNonUniformIndexingEXT",
+           "StorageTexelBufferArrayNonUniformIndexing",
+           "SPV_EXT_descriptor_indexing"),
+      // SPV_EXT_physical_storage_buffer
+      IN15("PhysicalStorageBufferAddressesEXT",
+           "PhysicalStorageBufferAddresses", "SPV_EXT_physical_storage_buffer"),
+      // SPV_KHR_vulkan_memory_model
+      IN15("VulkanMemoryModelKHR", "VulkanMemoryModel",
+           "SPV_KHR_vulkan_memory_model"),
+      IN15("VulkanMemoryModelDeviceScopeKHR", "VulkanMemoryModelDeviceScope",
+           "SPV_KHR_vulkan_memory_model"),
+  };
+#undef IN15
+}
+
+INSTANTIATE_TEST_SUITE_P(NewInSpirv1_5, ValidateCapabilityExtensionVersionTest,
+                         ValuesIn(CapVersionCases1_5()));
+
+TEST_P(ValidateCapability,
+       CapShaderViewportIndexLayerFailsInOlderSpirvVersion) {
+  const auto spirv =
+      MinimalShaderModuleWithCapability("ShaderViewportIndexLayerEXT");
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
+  EXPECT_EQ(SPV_ERROR_MISSING_EXTENSION,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "1st operand of Capability: operand ShaderViewportIndexLayerEXT"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("requires one of these extensions: "
+                        "SPV_EXT_shader_viewport_index_layer"));
+}
+
+TEST_P(ValidateCapability, CapShaderViewportIndexLayerFailsInNewSpirvVersion) {
+  const auto spirv =
+      MinimalShaderModuleWithCapability("ShaderViewportIndexLayerEXT");
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_5);
+  EXPECT_EQ(SPV_ERROR_MISSING_EXTENSION,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_5));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "1st operand of Capability: operand ShaderViewportIndexLayerEXT"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("requires one of these extensions: "
+                        "SPV_EXT_shader_viewport_index_layer"));
+}
+
+TEST_F(ValidateCapability, CapShaderViewportIndexSucceedsInNewSpirvVersion) {
+  const auto spirv = MinimalShaderModuleWithCapability("ShaderViewportIndex");
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_5);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5));
+  EXPECT_THAT(getDiagnosticString(), Eq(""));
+}
+
+TEST_F(ValidateCapability, CapShaderLayerSucceedsInNewSpirvVersion) {
+  const auto spirv = MinimalShaderModuleWithCapability("ShaderLayer");
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_5);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5));
+  EXPECT_THAT(getDiagnosticString(), Eq(""));
+}
+
 }  // namespace
 }  // namespace val
 }  // namespace spvtools
diff --git a/test/val/val_cfg_test.cpp b/test/val/val_cfg_test.cpp
index b22db06..f06f36c 100644
--- a/test/val/val_cfg_test.cpp
+++ b/test/val/val_cfg_test.cpp
@@ -342,11 +342,10 @@
   str += "OpFunctionEnd\n";
 
   CompileSuccessfully(str);
-  ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr(
-          "Variables can only be defined in the first block of a function"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("All OpVariable instructions in a function must be the "
+                        "first instructions in the first block"));
 }
 
 TEST_P(ValidateCFG, BlockSelfLoopIsOk) {
@@ -3027,6 +3026,7 @@
 %undef = OpUndef %bool
 %func = OpFunction %void None %void_fn
 %entry = OpLabel
+OpSelectionMerge %block None
 OpBranchConditional %undef %block %unreachable
 %block = OpLabel
 OpReturn
@@ -3050,6 +3050,7 @@
 %undef = OpUndef %int
 %func = OpFunction %void None %void_fn
 %entry = OpLabel
+OpSelectionMerge %block1 None
 OpSwitch %undef %block1 0 %unreachable 1 %block2
 %block1 = OpLabel
 OpReturn
@@ -3749,7 +3750,355 @@
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
-/// TODO(umar): Nested CFG constructs
+TEST_F(ValidateCFG, MissingMergeConditionalBranchBad) {
+  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
+OpBranchConditional %undef %then %else
+%then = OpLabel
+OpReturn
+%else = 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
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%int = OpTypeInt 32 0
+%undef = OpUndef %int
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+OpSwitch %undef %then 0 %else
+%then = OpLabel
+OpReturn
+%else = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text);
+  EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(), HasSubstr("Selection must be structured"));
+}
+
+TEST_F(ValidateCFG, MissingMergeSwitchBad2) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%int = OpTypeInt 32 0
+%undef = OpUndef %int
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+OpSwitch %undef %then 0 %then 1 %then 2 %else
+%then = OpLabel
+OpReturn
+%else = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text);
+  EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(), HasSubstr("Selection must be structured"));
+}
+
+TEST_F(ValidateCFG, MissingMergeOneBranchToMergeGood) {
+  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
+OpSelectionMerge %b3 None
+OpBranchConditional %undef %b1 %b2
+%b1 = OpLabel
+OpBranchConditional %undef %b2 %b3
+%b2 = OpLabel
+OpBranch %b3
+%b3 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateCFG, MissingMergeSameTargetConditionalBranchGood) {
+  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
+OpBranchConditional %undef %then %then
+%then = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateCFG, MissingMergeOneTargetSwitchGood) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%int = OpTypeInt 32 0
+%undef = OpUndef %int
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+OpSwitch %undef %then 0 %then 1 %then
+%then = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateCFG, MissingMergeOneUnseenTargetSwitchGood) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%int = OpTypeInt 32 0
+%undef_int = OpUndef %int
+%bool = OpTypeBool
+%undef_bool = OpUndef %bool
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+OpSelectionMerge %merge None
+OpBranchConditional %undef_bool %merge %b1
+%b1 = OpLabel
+OpSwitch %undef_int %b2 0 %b2 1 %merge 2 %b2
+%b2 = OpLabel
+OpBranch %merge
+%merge = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateCFG, MissingMergeLoopBreakGood) {
+  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
+OpBranch %body
+%body = OpLabel
+OpBranchConditional %undef %body2 %exit
+%body2 = OpLabel
+OpBranch %continue
+%continue = OpLabel
+OpBranch %loop
+%exit = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateCFG, MissingMergeLoopContinueGood) {
+  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
+OpBranch %body
+%body = OpLabel
+OpBranchConditional %undef %body2 %continue
+%body2 = OpLabel
+OpBranch %continue
+%continue = OpLabel
+OpBranch %loop
+%exit = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateCFG, MissingMergeSwitchBreakGood) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%bool = OpTypeBool
+%undef = OpUndef %bool
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+OpSelectionMerge %merge None
+OpSwitch %int_0 %merge 1 %b1
+%b1 = OpLabel
+OpBranchConditional %undef %merge %b2
+%b2 = OpLabel
+OpBranch %merge
+%merge = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateCFG, MissingMergeSwitchFallThroughGood) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%bool = OpTypeBool
+%undef = OpUndef %bool
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+OpSelectionMerge %merge None
+OpSwitch %int_0 %b1 1 %b2
+%b1 = OpLabel
+OpBranchConditional %undef %b3 %b2
+%b2 = OpLabel
+OpBranch %merge
+%b3 = OpLabel
+OpBranch %merge
+%merge = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateCFG, MissingMergeInALoopBad) {
+  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
+OpBranch %body
+%body = OpLabel
+OpBranchConditional %undef %b1 %b2
+%b1 = OpLabel
+OpBranch %exit
+%b2 = OpLabel
+OpBranch %continue
+%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, MissingMergeCrissCrossBad) {
+  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
+OpSelectionMerge %merge None
+OpBranchConditional %undef %b1 %b2
+%b1 = OpLabel
+OpBranchConditional %undef %b3 %b4
+%b2 = OpLabel
+OpBranchConditional %undef %b3 %b4
+%b3 = OpLabel
+OpBranch %merge
+%b4 = OpLabel
+OpBranch %merge
+%merge = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text);
+  EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(), HasSubstr("Selection must be structured"));
+}
 
 }  // namespace
 }  // namespace val
diff --git a/test/val/val_composites_test.cpp b/test/val/val_composites_test.cpp
index 74b6f20..e970562 100644
--- a/test/val/val_composites_test.cpp
+++ b/test/val/val_composites_test.cpp
@@ -648,7 +648,6 @@
 %val16 = OpCompositeExtract %f32 %struct 4 1000 1
 %val17 = OpCompositeExtract %f32 %struct 5 0
 %val18 = OpCompositeExtract %u32 %struct 5 1
-%val19 = OpCompositeExtract %big_struct %struct
 )";
 
   CompileSuccessfully(GenerateShaderCode(body));
@@ -767,6 +766,18 @@
                         "indexes still remain to be traversed."));
 }
 
+TEST_F(ValidateComposites, CompositeExtractNoIndices) {
+  const std::string body = R"(
+%struct = OpLoad %big_struct %var_big_struct
+%val1 = OpCompositeExtract %big_struct %struct
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Expected at least one index to OpCompositeExtract"));
+}
+
 TEST_F(ValidateComposites, CompositeExtractWrongType1) {
   const std::string body = R"(
 %struct = OpLoad %big_struct %var_big_struct
@@ -861,7 +872,6 @@
 %val16 = OpCompositeInsert %big_struct %f32_3 %struct 4 1000 1
 %val17 = OpCompositeInsert %big_struct %f32_3 %struct 5 0
 %val18 = OpCompositeInsert %big_struct %u32_3 %struct 5 1
-%val19 = OpCompositeInsert %big_struct %struct %struct
 )";
 
   CompileSuccessfully(GenerateShaderCode(body));
@@ -1157,9 +1167,8 @@
               HasSubstr("The Result Type must be the same as Composite type"));
 }
 
-// Valid: No Indexes were passed to OpCompositeExtract, and the Result Type is
-// the same as the Base Composite type.
-TEST_F(ValidateComposites, CompositeExtractNoIndexesGood) {
+// Invalid: No Indexes were passed to OpCompositeExtract.
+TEST_F(ValidateComposites, CompositeExtractNoIndices2) {
   std::ostringstream spirv;
   spirv << GetHeaderForTestsFromValId() << std::endl;
   spirv << "%matrix = OpLoad %mat4x3 %my_matrix" << std::endl;
@@ -1167,29 +1176,32 @@
   spirv << R"(OpReturn
               OpFunctionEnd)";
   CompileSuccessfully(spirv.str());
-  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Expected at least one index to OpCompositeExtract, zero found"));
 }
 
-// Invalid: No Indexes were passed to OpCompositeExtract, but the Result Type is
-// different from the Base Composite type.
-TEST_F(ValidateComposites, CompositeExtractNoIndexesBad) {
+// Invalid: No Indexes were passed to OpCompositeExtract.
+TEST_F(ValidateComposites, CompositeExtractNoIndicesWrongResultType) {
   std::ostringstream spirv;
   spirv << GetHeaderForTestsFromValId() << std::endl;
   spirv << "%matrix = OpLoad %mat4x3 %my_matrix" << std::endl;
-  spirv << "%float_entry = OpCompositeExtract  %float %matrix" << std::endl;
+  spirv << "%float_entry = OpCompositeExtract %float %matrix" << std::endl;
   spirv << R"(OpReturn
               OpFunctionEnd)";
   CompileSuccessfully(spirv.str());
   EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Result type (OpTypeFloat) does not match the type "
-                        "that results from indexing into the composite "
-                        "(OpTypeMatrix)."));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Expected at least one index to OpCompositeExtract, zero found"));
 }
 
-// Valid: No Indexes were passed to OpCompositeInsert, and the type of the
+// Invalid: No Indices were passed to OpCompositeInsert, and the type of the
 // Object<id> argument matches the Composite type.
-TEST_F(ValidateComposites, CompositeInsertMissingIndexesGood) {
+TEST_F(ValidateComposites, CompositeInsertMissingIndices) {
   std::ostringstream spirv;
   spirv << GetHeaderForTestsFromValId() << std::endl;
   spirv << "%matrix   = OpLoad %mat4x3 %my_matrix" << std::endl;
@@ -1199,12 +1211,16 @@
               OpReturn
               OpFunctionEnd)";
   CompileSuccessfully(spirv.str());
-  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Expected at least one index to OpCompositeInsert, zero found"));
 }
 
-// Invalid: No Indexes were passed to OpCompositeInsert, but the type of the
+// Invalid: No Indices were passed to OpCompositeInsert, but the type of the
 // Object<id> argument does not match the Composite type.
-TEST_F(ValidateComposites, CompositeInsertMissingIndexesBad) {
+TEST_F(ValidateComposites, CompositeInsertMissingIndices2) {
   std::ostringstream spirv;
   spirv << GetHeaderForTestsFromValId() << std::endl;
   spirv << "%matrix = OpLoad %mat4x3 %my_matrix" << std::endl;
@@ -1214,10 +1230,10 @@
               OpFunctionEnd)";
   CompileSuccessfully(spirv.str());
   EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("The Object type (OpTypeInt) does not match the type "
-                        "that results from indexing into the Composite "
-                        "(OpTypeMatrix)."));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Expected at least one index to OpCompositeInsert, zero found"));
 }
 
 // Valid: Tests that we can index into Struct, Array, Matrix, and Vector!
diff --git a/test/val/val_constants_test.cpp b/test/val/val_constants_test.cpp
index 2499f5c..301539d 100644
--- a/test/val/val_constants_test.cpp
+++ b/test/val/val_constants_test.cpp
@@ -458,6 +458,26 @@
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
+TEST_F(ValidateConstant, NullPhysicalStorageBuffer) {
+  std::string spirv = R"(
+OpCapability Shader
+OpCapability PhysicalStorageBufferAddresses
+OpCapability Linkage
+OpExtension "SPV_KHR_physical_storage_buffer"
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
+OpName %ptr "ptr"
+%int = OpTypeInt 32 0
+%ptr = OpTypePointer PhysicalStorageBuffer %int
+%null = OpConstantNull %ptr
+)";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpConstantNull Result Type <id> '1[%ptr]' cannot have "
+                        "a null value"));
+}
+
 }  // namespace
 }  // namespace val
 }  // namespace spvtools
diff --git a/test/val/val_conversion_test.cpp b/test/val/val_conversion_test.cpp
index 8851af3..47e6793 100644
--- a/test/val/val_conversion_test.cpp
+++ b/test/val/val_conversion_test.cpp
@@ -248,6 +248,8 @@
 %f64vec4_0123 = OpConstantComposite %f64vec4 %f64_0 %f64_1 %f64_2 %f64_3
 %f64vec4_1234 = OpConstantComposite %f64vec4 %f64_1 %f64_2 %f64_3 %f64_4
 
+%u64vec2_01 = OpConstantComposite %u64vec2 %u64_0 %u64_1
+
 %true = OpConstantTrue %bool
 %false = OpConstantFalse %bool
 
@@ -1206,6 +1208,49 @@
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
+TEST_F(ValidateConversion, BitcastSuccessSPV1p5) {
+  const std::string body = R"(
+%ptr = OpVariable %f32ptr_func Function
+%val1 = OpBitcast %u32 %ptr
+%val2 = OpBitcast %u64 %ptr
+%val3 = OpBitcast %f32ptr_func %u32_1
+%val4 = OpBitcast %f32ptr_wg %u64_1
+%val5 = OpBitcast %f32 %u32_1
+%val6 = OpBitcast %f32vec2 %u32vec2_12
+%val7 = OpBitcast %f32vec2 %u64_1
+%val8 = OpBitcast %f64 %u32vec2_12
+%val9 = OpBitcast %f32vec4 %f64vec2_12
+%val10 = OpBitcast %u32ptr_func %u32vec2_01
+%val11 = OpBitcast %u32vec2 %ptr
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body).c_str(), SPV_ENV_UNIVERSAL_1_5);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5));
+}
+
+TEST_F(ValidateConversion, BitcastSuccessPhysicalStorageBufferKHR) {
+  const std::string body = R"(
+%ptr = OpVariable %f32ptr_func Function
+%val1 = OpBitcast %u32 %ptr
+%val2 = OpBitcast %u64 %ptr
+%val3 = OpBitcast %f32ptr_func %u32_1
+%val4 = OpBitcast %f32ptr_wg %u64_1
+%val5 = OpBitcast %f32 %u32_1
+%val6 = OpBitcast %f32vec2 %u32vec2_12
+%val7 = OpBitcast %f32vec2 %u64_1
+%val8 = OpBitcast %f64 %u32vec2_12
+%val9 = OpBitcast %f32vec4 %f64vec2_12
+%val10 = OpBitcast %u32ptr_func %u32vec2_01
+%val11 = OpBitcast %u32vec2 %ptr
+)";
+
+  CompileSuccessfully(
+      GenerateKernelCode(body,
+                         "\nOpExtension \"SPV_KHR_physical_storage_buffer\"")
+          .c_str());
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
 TEST_F(ValidateConversion, BitcastInputHasNoType) {
   const std::string body = R"(
 %val = OpBitcast %u32 %f32
@@ -1249,10 +1294,66 @@
 
   CompileSuccessfully(GenerateKernelCode(body).c_str());
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr("Expected input to be a pointer or int scalar if Result Type "
-                "is pointer: Bitcast"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Expected input to be a pointer or int scalar if "
+                        "Result Type is pointer: Bitcast"));
+}
+
+TEST_F(ValidateConversion, BitcastPtrWrongInputTypeSPV1p5) {
+  const std::string body = R"(
+%val = OpBitcast %u32ptr_func %f32_1
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body).c_str(), SPV_ENV_UNIVERSAL_1_5);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_5));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Expected input to be a pointer, int scalar or 32-bit "
+                        "int vector if Result Type is pointer: Bitcast"));
+}
+
+TEST_F(ValidateConversion, BitcastPtrWrongInputTypePhysicalStorageBufferKHR) {
+  const std::string body = R"(
+%val = OpBitcast %u32ptr_func %f32_1
+)";
+
+  CompileSuccessfully(
+      GenerateKernelCode(body,
+                         "\nOpExtension \"SPV_KHR_physical_storage_buffer\"")
+          .c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Expected input to be a pointer, int scalar or 32-bit "
+                        "int vector if Result Type is pointer: Bitcast"));
+}
+
+TEST_F(ValidateConversion, BitcastPtrWrongInputTypeIntVectorSPV1p5) {
+  const std::string body = R"(
+%val = OpBitcast %u32ptr_func %u64vec2_01
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body).c_str(), SPV_ENV_UNIVERSAL_1_5);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_5));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Expected input to be a pointer, int scalar or 32-bit "
+                        "int vector if Result Type is pointer: Bitcast"));
+}
+
+TEST_F(ValidateConversion,
+       BitcastPtrWrongInputTypeIntVectorPhysicalStorageBufferKHR) {
+  const std::string body = R"(
+%val = OpBitcast %u32ptr_func %u64vec2_01
+)";
+
+  CompileSuccessfully(
+      GenerateKernelCode(body,
+                         "\nOpExtension \"SPV_KHR_physical_storage_buffer\"")
+          .c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Expected input to be a pointer, int scalar or 32-bit "
+                        "int vector if Result Type is pointer: Bitcast"));
 }
 
 TEST_F(ValidateConversion, BitcastPtrWrongResultType) {
@@ -1262,11 +1363,66 @@
 
   CompileSuccessfully(GenerateKernelCode(body).c_str());
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr(
-          "Pointer can only be converted to another pointer or int scalar: "
-          "Bitcast"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Pointer can only be converted to another pointer or "
+                        "int scalar: Bitcast"));
+}
+
+TEST_F(ValidateConversion, BitcastPtrWrongResultTypeSPV1p5) {
+  const std::string body = R"(
+%val = OpBitcast %f32 %f32inp
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body).c_str(), SPV_ENV_UNIVERSAL_1_5);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_5));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Pointer can only be converted to another pointer, int "
+                        "scalar or 32-bit int vector: Bitcast"));
+}
+
+TEST_F(ValidateConversion, BitcastPtrWrongResultTypePhysicalStorageBufferKHR) {
+  const std::string body = R"(
+%val = OpBitcast %f32 %f32inp
+)";
+
+  CompileSuccessfully(
+      GenerateKernelCode(body,
+                         "\nOpExtension \"SPV_KHR_physical_storage_buffer\"")
+          .c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Pointer can only be converted to another pointer, int "
+                        "scalar or 32-bit int vector: Bitcast"));
+}
+
+TEST_F(ValidateConversion, BitcastPtrWrongResultTypeIntVectorSPV1p5) {
+  const std::string body = R"(
+%val = OpBitcast %u64vec2 %f32inp
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body).c_str(), SPV_ENV_UNIVERSAL_1_5);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_5));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Pointer can only be converted to another pointer, int "
+                        "scalar or 32-bit int vector: Bitcast"));
+}
+
+TEST_F(ValidateConversion,
+       BitcastPtrWrongResultTypeIntVectorPhysicalStorageBufferKHR) {
+  const std::string body = R"(
+%val = OpBitcast %u64vec2 %f32inp
+)";
+
+  CompileSuccessfully(
+      GenerateKernelCode(body,
+                         "\nOpExtension \"SPV_KHR_physical_storage_buffer\"")
+          .c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Pointer can only be converted to another pointer, int "
+                        "scalar or 32-bit int vector: Bitcast"));
 }
 
 TEST_F(ValidateConversion, BitcastDifferentTotalBitWidth) {
diff --git a/test/val/val_data_test.cpp b/test/val/val_data_test.cpp
index 4690f97..30afd03 100644
--- a/test/val/val_data_test.cpp
+++ b/test/val/val_data_test.cpp
@@ -629,15 +629,26 @@
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
-TEST_F(ValidateData, specialize_boolean_to_int) {
+TEST_F(ValidateData, specialize_boolean_true_to_int) {
   std::string str = header + R"(
 %2 = OpTypeInt 32 1
-%3 = OpSpecConstantTrue %2
+%3 = OpSpecConstantTrue %2)";
+  CompileSuccessfully(str.c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpSpecConstantTrue Result Type <id> '1[%int]' is not "
+                        "a boolean type"));
+}
+
+TEST_F(ValidateData, specialize_boolean_false_to_int) {
+  std::string str = header + R"(
+%2 = OpTypeInt 32 1
 %4 = OpSpecConstantFalse %2)";
   CompileSuccessfully(str.c_str());
   ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Specialization constant must be a boolean"));
+              HasSubstr("OpSpecConstantFalse Result Type <id> '1[%int]' is not "
+                        "a boolean type"));
 }
 
 TEST_F(ValidateData, missing_forward_pointer_decl) {
@@ -648,7 +659,7 @@
   CompileSuccessfully(str.c_str());
   ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("must first be declared using OpTypeForwardPointer"));
+              HasSubstr("Operand 3[%3] requires a previous definition"));
 }
 
 TEST_F(ValidateData, missing_forward_pointer_decl_self_reference) {
@@ -658,8 +669,9 @@
 )";
   CompileSuccessfully(str.c_str());
   ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("must first be declared using OpTypeForwardPointer"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Operand 2[%_struct_2] requires a previous definition"));
 }
 
 TEST_F(ValidateData, forward_pointer_missing_definition) {
@@ -698,9 +710,7 @@
   CompileSuccessfully(str.c_str());
   ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("A forward reference operand in an OpTypeStruct must "
-                        "be an OpTypePointer that points to an OpTypeStruct. "
-                        "Found OpTypePointer that points to OpTypeInt."));
+              HasSubstr("Forward pointers must point to a structure"));
 }
 
 TEST_F(ValidateData, struct_forward_pointer_good) {
@@ -934,23 +944,6 @@
                         "OpTypeStruct %_runtimearr_uint %uint\n"));
 }
 
-TEST_F(ValidateData, invalid_forward_reference_in_array) {
-  std::string str = R"(
-               OpCapability Shader
-               OpCapability Linkage
-               OpMemoryModel Logical GLSL450
-       %uint = OpTypeInt 32 0
-%uint_1 = OpConstant %uint 1
-%_arr_3_uint_1 = OpTypeArray %_arr_3_uint_1 %uint_1
-)";
-
-  CompileSuccessfully(str.c_str(), SPV_ENV_UNIVERSAL_1_3);
-  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Forward reference operands in an OpTypeArray must "
-                        "first be declared using OpTypeForwardPointer."));
-}
-
 }  // namespace
 }  // namespace val
 }  // namespace spvtools
diff --git a/test/val/val_decoration_test.cpp b/test/val/val_decoration_test.cpp
index c267dbf..256e115 100644
--- a/test/val/val_decoration_test.cpp
+++ b/test/val/val_decoration_test.cpp
@@ -4785,7 +4785,8 @@
             ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("requires SPIR-V version 1.4 or later\n"
-                        "  OpDecorateId %int0 UniformId %subgroupscope"));
+                        "  OpDecorateId %int0 UniformId %subgroupscope"))
+      << spirv;
 }
 
 TEST_F(ValidateDecorations, UniformIdDecorationWithScopeIdV13BadTargetV14) {
@@ -4860,9 +4861,8 @@
   CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
   EXPECT_EQ(SPV_ERROR_INVALID_DATA,
             ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr("ConstantNull: expected Execution Scope to be a 32-bit int"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("ConstantNull: expected scope to be a 32-bit int"));
 }
 
 TEST_F(ValidateDecorations,
@@ -6690,6 +6690,311 @@
   EXPECT_THAT(getDiagnosticString(), Eq(""));
 }
 
+TEST_F(ValidateDecorations, VulkanStorageBufferBlock) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %struct Block
+OpMemberDecorate %struct 0 Offset 0
+%void = OpTypeVoid
+%uint = OpTypeInt 32 0
+%struct = OpTypeStruct %uint
+%ptr_ssbo = OpTypePointer StorageBuffer %struct
+%var = OpVariable %ptr_ssbo StorageBuffer
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateDecorations, VulkanStorageBufferMissingBlock) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%uint = OpTypeInt 32 0
+%struct = OpTypeStruct %uint
+%ptr_ssbo = OpTypePointer StorageBuffer %struct
+%var = OpVariable %ptr_ssbo StorageBuffer
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("From Vulkan spec, section 14.5.2:\nSuch variables "
+                        "must be identified with a Block decoration"));
+}
+
+TEST_F(ValidateDecorations, VulkanStorageBufferArrayMissingBlock) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%uint = OpTypeInt 32 0
+%uint_4 = OpConstant %uint 4
+%struct = OpTypeStruct %uint
+%array = OpTypeArray %struct %uint_4
+%ptr_ssbo = OpTypePointer StorageBuffer %array
+%var = OpVariable %ptr_ssbo StorageBuffer
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("From Vulkan spec, section 14.5.2:\nSuch variables "
+                        "must be identified with a Block decoration"));
+}
+
+TEST_F(ValidateDecorations, VulkanStorageBufferRuntimeArrayMissingBlock) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability RuntimeDescriptorArrayEXT
+OpExtension "SPV_EXT_descriptor_indexing"
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%uint = OpTypeInt 32 0
+%struct = OpTypeStruct %uint
+%array = OpTypeRuntimeArray %struct
+%ptr_ssbo = OpTypePointer StorageBuffer %array
+%var = OpVariable %ptr_ssbo StorageBuffer
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("From Vulkan spec, section 14.5.2:\nSuch variables "
+                        "must be identified with a Block decoration"));
+}
+
+TEST_F(ValidateDecorations, VulkanUniformBlock) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %struct Block
+OpMemberDecorate %struct 0 Offset 0
+%void = OpTypeVoid
+%uint = OpTypeInt 32 0
+%struct = OpTypeStruct %uint
+%ptr_ubo = OpTypePointer Uniform %struct
+%var = OpVariable %ptr_ubo Uniform
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateDecorations, VulkanUniformBufferBlock) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %struct BufferBlock
+OpMemberDecorate %struct 0 Offset 0
+%void = OpTypeVoid
+%uint = OpTypeInt 32 0
+%struct = OpTypeStruct %uint
+%ptr_ubo = OpTypePointer Uniform %struct
+%var = OpVariable %ptr_ubo Uniform
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateDecorations, VulkanUniformMissingBlock) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%uint = OpTypeInt 32 0
+%struct = OpTypeStruct %uint
+%ptr_ubo = OpTypePointer Uniform %struct
+%var = OpVariable %ptr_ubo Uniform
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("From Vulkan spec, section 14.5.2:\nSuch variables must be "
+                "identified with a Block or BufferBlock decoration"));
+}
+
+TEST_F(ValidateDecorations, VulkanUniformArrayMissingBlock) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%uint = OpTypeInt 32 0
+%uint_4 = OpConstant %uint 4
+%struct = OpTypeStruct %uint
+%array = OpTypeArray %struct %uint_4
+%ptr_ubo = OpTypePointer Uniform %array
+%var = OpVariable %ptr_ubo Uniform
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("From Vulkan spec, section 14.5.2:\nSuch variables must be "
+                "identified with a Block or BufferBlock decoration"));
+}
+
+TEST_F(ValidateDecorations, VulkanUniformRuntimeArrayMissingBlock) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability RuntimeDescriptorArrayEXT
+OpExtension "SPV_EXT_descriptor_indexing"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%uint = OpTypeInt 32 0
+%struct = OpTypeStruct %uint
+%array = OpTypeRuntimeArray %struct
+%ptr_ubo = OpTypePointer Uniform %array
+%var = OpVariable %ptr_ubo Uniform
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("From Vulkan spec, section 14.5.2:\nSuch variables must be "
+                "identified with a Block or BufferBlock decoration"));
+}
+
+TEST_F(ValidateDecorations, VulkanArrayStrideZero) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+OpDecorate %struct Block
+OpMemberDecorate %struct 0 Offset 0
+OpDecorate %array ArrayStride 0
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_4 = OpConstant %int 4
+%array = OpTypeArray %int %int_4
+%struct = OpTypeStruct %array
+%ptr_ssbo_struct = OpTypePointer StorageBuffer %struct
+%var = OpVariable %ptr_ssbo_struct StorageBuffer
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("contains an array with stride 0"));
+}
+
+TEST_F(ValidateDecorations, VulkanArrayStrideTooSmall) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+OpDecorate %struct Block
+OpMemberDecorate %struct 0 Offset 0
+OpDecorate %inner ArrayStride 4
+OpDecorate %outer ArrayStride 4
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_4 = OpConstant %int 4
+%inner = OpTypeArray %int %int_4
+%outer = OpTypeArray %inner %int_4
+%struct = OpTypeStruct %outer
+%ptr_ssbo_struct = OpTypePointer StorageBuffer %struct
+%var = OpVariable %ptr_ssbo_struct StorageBuffer
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "contains an array with stride 4, but with an element size of 16"));
+}
+
 }  // namespace
 }  // namespace val
 }  // namespace spvtools
diff --git a/test/val/val_derivatives_test.cpp b/test/val/val_derivatives_test.cpp
index dac95f6..606abb9 100644
--- a/test/val/val_derivatives_test.cpp
+++ b/test/val/val_derivatives_test.cpp
@@ -62,7 +62,17 @@
 
 %f32vec4_ptr_input = OpTypePointer Input %f32vec4
 %f32vec4_var_input = OpVariable %f32vec4_ptr_input Input
+)";
 
+  if (capabilities_and_extensions.find("OpCapability Float16") !=
+      std::string::npos) {
+    ss << "%f16 = OpTypeFloat 16\n"
+       << "%f16vec4 = OpTypeVector %f16 4\n"
+       << "%f16_0 = OpConstantNull %f16\n"
+       << "%f16vec4_0 = OpConstantNull %f16vec4\n";
+  }
+
+  ss << R"(
 %main = OpFunction %void None %func
 %main_entry = OpLabel
 )";
@@ -150,6 +160,36 @@
                         "execution model: DPdx"));
 }
 
+using ValidateHalfDerivatives = spvtest::ValidateBase<std::string>;
+
+TEST_P(ValidateHalfDerivatives, ScalarFailure) {
+  const std::string op = GetParam();
+  const std::string body = "%val = " + op + " %f16 %f16_0\n";
+
+  CompileSuccessfully(
+      GenerateShaderCode(body, "OpCapability Float16\n").c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Result type component width must be 32 bits"));
+}
+
+TEST_P(ValidateHalfDerivatives, VectorFailure) {
+  const std::string op = GetParam();
+  const std::string body = "%val = " + op + " %f16vec4 %f16vec4_0\n";
+
+  CompileSuccessfully(
+      GenerateShaderCode(body, "OpCapability Float16\n").c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Result type component width must be 32 bits"));
+}
+
+INSTANTIATE_TEST_SUITE_P(HalfDerivatives, ValidateHalfDerivatives,
+                         ::testing::Values("OpDPdx", "OpDPdy", "OpFwidth",
+                                           "OpDPdxFine", "OpDPdyFine",
+                                           "OpFwidthFine", "OpDPdxCoarse",
+                                           "OpDPdyCoarse", "OpFwidthCoarse"));
+
 }  // namespace
 }  // namespace val
 }  // namespace spvtools
diff --git a/test/val/val_entry_point.cpp b/test/val/val_entry_point.cpp
new file mode 100644
index 0000000..f28cf5d
--- /dev/null
+++ b/test/val/val_entry_point.cpp
@@ -0,0 +1,76 @@
+// Copyright (c) 2019 Samsung Inc
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <string>
+
+#include "gmock/gmock.h"
+#include "test/unit_spirv.h"
+#include "test/val/val_fixtures.h"
+
+namespace spvtools {
+namespace {
+
+using ::testing::Eq;
+using ::testing::HasSubstr;
+
+using ValidateEntryPoints = spvtest::ValidateBase<bool>;
+
+TEST_F(ValidateEntryPoints, DuplicateEntryPoints) {
+  const std::string body = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %3 "foo"
+OpEntryPoint GLCompute %4 "foo"
+%1 = OpTypeVoid
+%2 = OpTypeFunction %1
+%3 = OpFunction %1 None %2
+%20 = OpLabel
+OpReturn
+OpFunctionEnd
+%4 = OpFunction %1 None %2
+%21 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(body);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Entry points cannot share the same name"));
+}
+
+TEST_F(ValidateEntryPoints, UniqueEntryPoints) {
+  const std::string body = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %3 "foo"
+OpEntryPoint GLCompute %4 "foo2"
+%1 = OpTypeVoid
+%2 = OpTypeFunction %1
+%3 = OpFunction %1 None %2
+%20 = OpLabel
+OpReturn
+OpFunctionEnd
+%4 = OpFunction %1 None %2
+%21 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(body);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+}  // namespace
+}  // namespace spvtools
diff --git a/test/val/val_id_test.cpp b/test/val/val_id_test.cpp
index ec5715c..327aef1 100644
--- a/test/val/val_id_test.cpp
+++ b/test/val/val_id_test.cpp
@@ -1476,7 +1476,8 @@
   CompileSuccessfully(spirv.c_str());
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Specialization constant must be a boolean type."));
+              HasSubstr("OpSpecConstantTrue Result Type <id> '1[%void]' is not "
+                        "a boolean type"));
 }
 
 TEST_F(ValidateIdWithMessage, OpSpecConstantFalseGood) {
@@ -1492,8 +1493,10 @@
 %2 = OpSpecConstantFalse %1)";
   CompileSuccessfully(spirv.c_str());
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Specialization constant must be a boolean type."));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpSpecConstantFalse Result Type <id> '1[%void]' is not "
+                "a boolean type"));
 }
 
 TEST_F(ValidateIdWithMessage, OpSpecConstantGood) {
@@ -2317,8 +2320,8 @@
  %6 = OpFunction %1 None %4
  %7 = OpLabel
  %8 = OpLoad %2 %5
- %9 = OpReturn
-%10 = OpFunctionEnd
+      OpReturn
+      OpFunctionEnd
 )";
   CompileSuccessfully(spirv.c_str());
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
@@ -5176,8 +5179,7 @@
   CompileSuccessfully(spirv.c_str());
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Forward reference operands in an OpTypeStruct must "
-                        "first be declared using OpTypeForwardPointer."));
+              HasSubstr("Operand 2[%2] requires a previous definition"));
 }
 
 TEST_F(ValidateIdWithMessage, UndefinedIdScope) {
@@ -6462,6 +6464,26 @@
                 "pointer definition.\n  OpTypeForwardPointer "
                 "%_ptr_Function_int CrossWorkgroup"));
 }
+
+TEST_F(ValidateIdWithMessage, MissingForwardPointer) {
+  const std::string spirv = R"(
+               OpCapability Linkage
+               OpCapability Shader
+               OpMemoryModel Logical Simple
+      %float = OpTypeFloat 32
+  %_struct_9 = OpTypeStruct %float %_ptr_Uniform__struct_9
+%_ptr_Uniform__struct_9 = OpTypePointer Uniform %_struct_9
+       %1278 = OpVariable %_ptr_Uniform__struct_9 Uniform
+)";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Operand 3[%_ptr_Uniform__struct_2] requires a previous definition"));
+}
+
 }  // namespace
 }  // namespace val
 }  // namespace spvtools
diff --git a/test/val/val_image_test.cpp b/test/val/val_image_test.cpp
index fd3d886..433c9fa 100644
--- a/test/val/val_image_test.cpp
+++ b/test/val/val_image_test.cpp
@@ -2938,7 +2938,7 @@
 TEST_F(ValidateImage, WriteSuccess1) {
   const std::string body = R"(
 %img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000
-%res1 = OpImageWrite %img %u32vec2_01 %u32vec4_0123
+OpImageWrite %img %u32vec2_01 %u32vec4_0123
 )";
 
   const std::string extra = "\nOpCapability StorageImageWriteWithoutFormat\n";
@@ -2949,7 +2949,7 @@
 TEST_F(ValidateImage, WriteSuccess2) {
   const std::string body = R"(
 %img = OpLoad %type_image_f32_1d_0002_rgba32f %uniform_image_f32_1d_0002_rgba32f
-%res1 = OpImageWrite %img %u32_1 %f32vec4_0000
+OpImageWrite %img %u32_1 %f32vec4_0000
 )";
 
   const std::string extra = "\nOpCapability Image1D\n";
@@ -2960,7 +2960,7 @@
 TEST_F(ValidateImage, WriteSuccess3) {
   const std::string body = R"(
 %img = OpLoad %type_image_f32_cube_0102_rgba32f %uniform_image_f32_cube_0102_rgba32f
-%res1 = OpImageWrite %img %u32vec3_012 %f32vec4_0000
+OpImageWrite %img %u32vec3_012 %f32vec4_0000
 )";
 
   const std::string extra = "\nOpCapability ImageCubeArray\n";
@@ -2972,8 +2972,8 @@
   const std::string body = R"(
 %img = OpLoad %type_image_f32_2d_0010 %uniform_image_f32_2d_0010
 ;TODO(atgoo@github.com) Is it legal to write to MS image without sample index?
-%res1 = OpImageWrite %img %u32vec2_01 %f32vec4_0000
-%res2 = OpImageWrite %img %u32vec2_01 %f32vec4_0000 Sample %u32_1
+OpImageWrite %img %u32vec2_01 %f32vec4_0000
+OpImageWrite %img %u32vec2_01 %f32vec4_0000 Sample %u32_1
 )";
 
   const std::string extra = "\nOpCapability StorageImageWriteWithoutFormat\n";
@@ -2984,7 +2984,7 @@
 TEST_F(ValidateImage, WriteSubpassData) {
   const std::string body = R"(
 %img = OpLoad %type_image_f32_spd_0002 %uniform_image_f32_spd_0002
-%res1 = OpImageWrite %img %u32vec2_01 %f32vec4_0000
+OpImageWrite %img %u32vec2_01 %f32vec4_0000
 )";
 
   CompileSuccessfully(GenerateShaderCode(body).c_str());
@@ -2996,7 +2996,7 @@
 TEST_F(ValidateImage, WriteNeedCapabilityStorageImageWriteWithoutFormat) {
   const std::string body = R"(
 %img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000
-%res1 = OpImageWrite %img %u32vec2_01 %u32vec4_0123
+OpImageWrite %img %u32vec2_01 %u32vec4_0123
 )";
 
   CompileSuccessfully(GenerateShaderCode(body).c_str());
@@ -3006,7 +3006,7 @@
 TEST_F(ValidateImage, WriteNeedCapabilityStorageImageWriteWithoutFormatVulkan) {
   const std::string body = R"(
 %img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000
-%res1 = OpImageWrite %img %u32vec2_01 %u32vec4_0123
+OpImageWrite %img %u32vec2_01 %u32vec4_0123
 )";
 
   spv_target_env env = SPV_ENV_VULKAN_1_0;
@@ -3023,7 +3023,7 @@
 TEST_F(ValidateImage, WriteNeedCapabilityImage1D) {
   const std::string body = R"(
 %img = OpLoad %type_image_f32_1d_0002_rgba32f %uniform_image_f32_1d_0002_rgba32f
-%res1 = OpImageWrite %img %u32vec2_01 %f32vec4_0000
+OpImageWrite %img %u32vec2_01 %f32vec4_0000
 )";
 
   CompileSuccessfully(GenerateShaderCode(body).c_str());
@@ -3036,7 +3036,7 @@
 TEST_F(ValidateImage, WriteNeedCapabilityImageCubeArray) {
   const std::string body = R"(
 %img = OpLoad %type_image_f32_cube_0102_rgba32f %uniform_image_f32_cube_0102_rgba32f
-%res1 = OpImageWrite %img %u32vec3_012 %f32vec4_0000
+OpImageWrite %img %u32vec3_012 %f32vec4_0000
 )";
 
   CompileSuccessfully(GenerateShaderCode(body).c_str());
@@ -3050,7 +3050,7 @@
 TEST_F(ValidateImage, WriteNotImage) {
   const std::string body = R"(
 %sampler = OpLoad %type_sampler %uniform_sampler
-%res1 = OpImageWrite %sampler %u32vec2_01 %f32vec4_0000
+OpImageWrite %sampler %u32vec2_01 %f32vec4_0000
 )";
 
   CompileSuccessfully(GenerateShaderCode(body).c_str());
@@ -3062,7 +3062,7 @@
 TEST_F(ValidateImage, WriteImageSampled) {
   const std::string body = R"(
 %img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001
-%res1 = OpImageWrite %img %u32vec2_01 %f32vec4_0000
+OpImageWrite %img %u32vec2_01 %f32vec4_0000
 )";
 
   const std::string extra = "\nOpCapability StorageImageWriteWithoutFormat\n";
@@ -3075,7 +3075,7 @@
 TEST_F(ValidateImage, WriteWrongCoordinateType) {
   const std::string body = R"(
 %img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000
-%res1 = OpImageWrite %img %f32vec2_00 %u32vec4_0123
+OpImageWrite %img %f32vec2_00 %u32vec4_0123
 )";
 
   const std::string extra = "\nOpCapability StorageImageWriteWithoutFormat\n";
@@ -3088,7 +3088,7 @@
 TEST_F(ValidateImage, WriteCoordinateSizeTooSmall) {
   const std::string body = R"(
 %img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000
-%res1 = OpImageWrite %img %u32_1 %u32vec4_0123
+OpImageWrite %img %u32_1 %u32vec4_0123
 )";
 
   const std::string extra = "\nOpCapability StorageImageWriteWithoutFormat\n";
@@ -3102,7 +3102,7 @@
 TEST_F(ValidateImage, WriteTexelWrongType) {
   const std::string body = R"(
 %img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000
-%res1 = OpImageWrite %img %u32vec2_01 %img
+OpImageWrite %img %u32vec2_01 %img
 )";
 
   const std::string extra = "\nOpCapability StorageImageWriteWithoutFormat\n";
@@ -3115,7 +3115,7 @@
 TEST_F(ValidateImage, DISABLED_WriteTexelNotVector4) {
   const std::string body = R"(
 %img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000
-%res1 = OpImageWrite %img %u32vec2_01 %u32vec3_012
+OpImageWrite %img %u32vec2_01 %u32vec3_012
 )";
 
   const std::string extra = "\nOpCapability StorageImageWriteWithoutFormat\n";
@@ -3128,7 +3128,7 @@
 TEST_F(ValidateImage, WriteTexelWrongComponentType) {
   const std::string body = R"(
 %img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000
-%res1 = OpImageWrite %img %u32vec2_01 %f32vec4_0000
+OpImageWrite %img %u32vec2_01 %f32vec4_0000
 )";
 
   const std::string extra = "\nOpCapability StorageImageWriteWithoutFormat\n";
@@ -3143,7 +3143,7 @@
 TEST_F(ValidateImage, WriteSampleNotInteger) {
   const std::string body = R"(
 %img = OpLoad %type_image_f32_2d_0010 %uniform_image_f32_2d_0010
-%res1 = OpImageWrite %img %u32vec2_01 %f32vec4_0000 Sample %f32_1
+OpImageWrite %img %u32vec2_01 %f32vec4_0000 Sample %f32_1
 )";
 
   const std::string extra = "\nOpCapability StorageImageWriteWithoutFormat\n";
@@ -3156,7 +3156,7 @@
 TEST_F(ValidateImage, SampleNotMultisampled) {
   const std::string body = R"(
 %img = OpLoad %type_image_f32_2d_0002 %uniform_image_f32_2d_0002
-%res2 = OpImageWrite %img %u32vec2_01 %f32vec4_0000 Sample %u32_1
+OpImageWrite %img %u32vec2_01 %f32vec4_0000 Sample %u32_1
 )";
 
   const std::string extra = "\nOpCapability StorageImageWriteWithoutFormat\n";
@@ -4527,7 +4527,7 @@
 TEST_F(ValidateImage, MakeTexelAvailableKHRSuccessImageWrite) {
   const std::string body = R"(
 %img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000
-%res1 = OpImageWrite %img %u32vec2_01 %u32vec4_0123 MakeTexelAvailableKHR|NonPrivateTexelKHR %u32_2
+OpImageWrite %img %u32vec2_01 %u32vec4_0123 MakeTexelAvailableKHR|NonPrivateTexelKHR %u32_2
 )";
 
   const std::string extra = R"(
@@ -4567,7 +4567,7 @@
 TEST_F(ValidateImage, MakeTexelAvailableKHRFailureMissingNonPrivate) {
   const std::string body = R"(
 %img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000
-%res1 = OpImageWrite %img %u32vec2_01 %u32vec4_0123 MakeTexelAvailableKHR %u32_1
+OpImageWrite %img %u32vec2_01 %u32vec4_0123 MakeTexelAvailableKHR %u32_1
 )";
 
   const std::string extra = R"(
@@ -4588,7 +4588,7 @@
 TEST_F(ValidateImage, VulkanMemoryModelDeviceScopeImageWriteBad) {
   const std::string body = R"(
 %img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000
-%res1 = OpImageWrite %img %u32vec2_01 %u32vec4_0123 MakeTexelAvailableKHR|NonPrivateTexelKHR %u32_1
+OpImageWrite %img %u32vec2_01 %u32vec4_0123 MakeTexelAvailableKHR|NonPrivateTexelKHR %u32_1
 )";
 
   const std::string extra = R"(
@@ -4610,7 +4610,7 @@
 TEST_F(ValidateImage, VulkanMemoryModelDeviceScopeImageWriteGood) {
   const std::string body = R"(
 %img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000
-%res1 = OpImageWrite %img %u32vec2_01 %u32vec4_0123 MakeTexelAvailableKHR|NonPrivateTexelKHR %u32_1
+OpImageWrite %img %u32vec2_01 %u32vec4_0123 MakeTexelAvailableKHR|NonPrivateTexelKHR %u32_1
 )";
 
   const std::string extra = R"(
diff --git a/test/val/val_limits_test.cpp b/test/val/val_limits_test.cpp
index becf7be..0ef61e2 100644
--- a/test/val/val_limits_test.cpp
+++ b/test/val/val_limits_test.cpp
@@ -212,7 +212,7 @@
 %5 = OpFunction %1 None %2
 %7 = OpLabel
 %8 = OpIAdd %3 %4 %4
-%9 = OpSwitch %4 %10)";
+     OpSwitch %4 %10)";
 
   // Now add the (literal, label) pairs
   for (int i = 0; i < 16383; ++i) {
@@ -240,7 +240,7 @@
 %5 = OpFunction %1 None %2
 %7 = OpLabel
 %8 = OpIAdd %3 %4 %4
-%9 = OpSwitch %4 %10)";
+     OpSwitch %4 %10)";
 
   // Now add the (literal, label) pairs
   for (int i = 0; i < 16384; ++i) {
@@ -271,7 +271,7 @@
 %5 = OpFunction %1 None %2
 %7 = OpLabel
 %8 = OpIAdd %3 %4 %4
-%9 = OpSwitch %4 %10)";
+     OpSwitch %4 %10)";
 
   // Now add the (literal, label) pairs
   for (int i = 0; i < 10; ++i) {
@@ -301,7 +301,7 @@
 %5 = OpFunction %1 None %2
 %7 = OpLabel
 %8 = OpIAdd %3 %4 %4
-%9 = OpSwitch %4 %10)";
+     OpSwitch %4 %10)";
 
   // Now add the (literal, label) pairs
   for (int i = 0; i < 11; ++i) {
diff --git a/test/val/val_memory_test.cpp b/test/val/val_memory_test.cpp
index 54009df..22761cc 100644
--- a/test/val/val_memory_test.cpp
+++ b/test/val/val_memory_test.cpp
@@ -1492,8 +1492,8 @@
       getDiagnosticString(),
       HasSubstr(
           "Source memory access must not include MakePointerAvailableKHR\n"
-          "  OpCopyMemory %5 %6 MakePointerAvailableKHR|NonPrivatePointerKHR"
-          " %uint_1 MakePointerAvailableKHR|NonPrivatePointerKHR %uint_1"));
+          "  OpCopyMemory %5 %6 MakePointerAvailable|NonPrivatePointer"
+          " %uint_1 MakePointerAvailable|NonPrivatePointer %uint_1"));
 }
 
 TEST_F(ValidateMemory, VulkanMemoryModelCopyMemoryTwoAccessSecondWithVisBad) {
@@ -1525,10 +1525,9 @@
             ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
   EXPECT_THAT(
       getDiagnosticString(),
-      HasSubstr(
-          "Target memory access must not include MakePointerVisibleKHR\n"
-          "  OpCopyMemory %5 %6 MakePointerVisibleKHR|NonPrivatePointerKHR"
-          " %uint_1 MakePointerVisibleKHR|NonPrivatePointerKHR %uint_1"));
+      HasSubstr("Target memory access must not include MakePointerVisibleKHR\n"
+                "  OpCopyMemory %5 %6 MakePointerVisible|NonPrivatePointer"
+                " %uint_1 MakePointerVisible|NonPrivatePointer %uint_1"));
 }
 
 TEST_F(ValidateMemory, VulkanMemoryModelDeviceScopeCopyMemorySizedBad1) {
@@ -2319,9 +2318,14 @@
 OpMemoryModel Logical GLSL450
 OpEntryPoint Fragment %func "func"
 OpExecutionMode %func OriginUpperLeft
+OpDecorate %struct Block
+OpMemberDecorate %struct 0 Offset 0
 %sampler_t = OpTypeSampler
+%uint = OpTypeInt 32 0
 %array_t = OpTypeRuntimeArray %sampler_t
-%array_sb_ptr = OpTypePointer StorageBuffer %array_t
+%struct = OpTypeStruct %uint
+%sb_array_t = OpTypeRuntimeArray %struct
+%array_sb_ptr = OpTypePointer StorageBuffer %sb_array_t
 %2 = OpVariable %array_sb_ptr StorageBuffer
 %array_uc_ptr = OpTypePointer UniformConstant %array_t
 %3 = OpVariable %array_uc_ptr UniformConstant
@@ -2992,6 +2996,7 @@
 OpMemoryModel Logical GLSL450
 OpEntryPoint Fragment %func "func"
 OpExecutionMode %func OriginUpperLeft
+OpDecorate %inner_array_t ArrayStride 4
 OpDecorate %array_t ArrayStride 4
 OpMemberDecorate %struct_t 0 Offset 0
 OpDecorate %struct_t Block
@@ -3021,6 +3026,7 @@
 OpMemoryModel Logical GLSL450
 OpEntryPoint Fragment %func "func"
 OpExecutionMode %func OriginUpperLeft
+OpDecorate %inner_array_t ArrayStride 4
 OpDecorate %array_t ArrayStride 4
 OpMemberDecorate %struct_t 0 Offset 0
 OpDecorate %struct_t Block
@@ -4293,6 +4299,185 @@
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
 }
 
+TEST_F(ValidateMemory, VulkanStorageBufferNotAStruct) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%uint = OpTypeInt 32 0
+%ptr_ssbo = OpTypePointer StorageBuffer %uint
+%var = OpVariable %ptr_ssbo StorageBuffer
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("From Vulkan spec, section 14.5.2:\nVariables identified with "
+                "the StorageBuffer storage class are used to access "
+                "transparent buffer backed resources. Such variables must be "
+                "typed as OpTypeStruct, or an array of this type"));
+}
+
+TEST_F(ValidateMemory, VulkanStorageBufferRuntimeArrayNotAStruct) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability RuntimeDescriptorArrayEXT
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+OpExtension "SPV_EXT_descriptor_indexing"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%uint = OpTypeInt 32 0
+%array = OpTypeRuntimeArray %uint
+%ptr_ssbo = OpTypePointer StorageBuffer %array
+%var = OpVariable %ptr_ssbo StorageBuffer
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("From Vulkan spec, section 14.5.2:\nVariables identified with "
+                "the StorageBuffer storage class are used to access "
+                "transparent buffer backed resources. Such variables must be "
+                "typed as OpTypeStruct, or an array of this type"));
+}
+
+TEST_F(ValidateMemory, VulkanStorageBufferArrayNotAStruct) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%uint = OpTypeInt 32 0
+%uint_4 = OpConstant %uint 4
+%array = OpTypeArray %uint %uint_4
+%ptr_ssbo = OpTypePointer StorageBuffer %array
+%var = OpVariable %ptr_ssbo StorageBuffer
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("From Vulkan spec, section 14.5.2:\nVariables identified with "
+                "the StorageBuffer storage class are used to access "
+                "transparent buffer backed resources. Such variables must be "
+                "typed as OpTypeStruct, or an array of this type"));
+}
+
+TEST_F(ValidateMemory, PhysicalStorageBufferPtrEqual) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability Int64
+OpCapability PhysicalStorageBufferAddresses
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%bool = OpTypeBool
+%long = OpTypeInt 64 0
+%long_0 = OpConstant %long 0
+%ptr_pssbo_long = OpTypePointer PhysicalStorageBuffer %long
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%conv = OpConvertUToPtr %ptr_pssbo_long %long_0
+%eq = OpPtrEqual %bool %conv %conv
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_5);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Cannot use a pointer in the PhysicalStorageBuffer storage class"));
+}
+
+TEST_F(ValidateMemory, PhysicalStorageBufferPtrNotEqual) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability Int64
+OpCapability PhysicalStorageBufferAddresses
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%bool = OpTypeBool
+%long = OpTypeInt 64 0
+%long_0 = OpConstant %long 0
+%ptr_pssbo_long = OpTypePointer PhysicalStorageBuffer %long
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%conv = OpConvertUToPtr %ptr_pssbo_long %long_0
+%neq = OpPtrNotEqual %bool %conv %conv
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_5);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Cannot use a pointer in the PhysicalStorageBuffer storage class"));
+}
+
+TEST_F(ValidateMemory, PhysicalStorageBufferPtrDiff) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability Int64
+OpCapability PhysicalStorageBufferAddresses
+OpCapability VariablePointers
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%long = OpTypeInt 64 0
+%long_0 = OpConstant %long 0
+%ptr_pssbo_long = OpTypePointer PhysicalStorageBuffer %long
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%conv = OpConvertUToPtr %ptr_pssbo_long %long_0
+%diff = OpPtrDiff %long %conv %conv
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_5);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Cannot use a pointer in the PhysicalStorageBuffer storage class"));
+}
+
 }  // namespace
 }  // namespace val
 }  // namespace spvtools
diff --git a/test/val/val_misc_test.cpp b/test/val/val_misc_test.cpp
index 350f561..9395484 100644
--- a/test/val/val_misc_test.cpp
+++ b/test/val/val_misc_test.cpp
@@ -83,6 +83,149 @@
       getDiagnosticString(),
       HasSubstr("Cannot create undefined values with 8- or 16-bit types"));
 }
+
+const std::string ShaderClockSpriv = R"(
+OpCapability Shader
+OpCapability Int64
+OpCapability ShaderClockKHR
+OpExtension "SPV_KHR_shader_clock"
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 450
+OpSourceExtension "GL_ARB_gpu_shader_int64"
+OpSourceExtension "GL_ARB_shader_clock"
+OpSourceExtension "GL_EXT_shader_realtime_clock"
+OpName %main "main"
+OpName %time1 "time1"
+%void = OpTypeVoid
+)";
+
+TEST_F(ValidateMisc, ShaderClockInt64) {
+  const std::string spirv = ShaderClockSpriv + R"(
+%3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%_ptr_Function_uint = OpTypePointer Function %uint
+%uint_3 = OpConstant %uint 3
+%uint_1 = OpConstant %uint 1
+%main = OpFunction %void None %3
+%5 = OpLabel
+%time1 = OpVariable %_ptr_Function_uint Function
+%11 = OpReadClockKHR %uint %uint_3
+OpStore %time1 %11
+OpReturn
+OpFunctionEnd)";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(), HasSubstr("or 64bit unsigned integer"));
+}
+
+TEST_F(ValidateMisc, ShaderClockVec2) {
+  const std::string spirv = ShaderClockSpriv + R"(
+%3 = OpTypeFunction %void
+%ulong = OpTypeInt 64 0
+%_ptr_Function_ulong = OpTypePointer Function %ulong
+%uint = OpTypeInt 32 0
+%uint_3 = OpConstant %uint 3
+%v2uint = OpTypeVector %ulong 2
+%_ptr_Function_v2uint = OpTypePointer Function %v2uint
+%main = OpFunction %void None %3
+%5 = OpLabel
+%time1 = OpVariable %_ptr_Function_v2uint Function
+%15 = OpReadClockKHR %v2uint %uint_3
+OpStore %time1 %15
+OpReturn
+OpFunctionEnd)";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(), HasSubstr("vector of two components"));
+}
+
+TEST_F(ValidateMisc, ShaderClockInvalidScopeValue) {
+  const std::string spirv = ShaderClockSpriv + R"(
+%3 = OpTypeFunction %void
+%ulong = OpTypeInt 64 0
+%uint = OpTypeInt 32 0
+%_ptr_Function_ulong = OpTypePointer Function %ulong
+%uint_10 = OpConstant %uint 10
+%uint_1 = OpConstant %uint 1
+%main = OpFunction %void None %3
+%5 = OpLabel
+%time1 = OpVariable %_ptr_Function_ulong Function
+%11 = OpReadClockKHR %ulong %uint_10
+OpStore %time1 %11
+OpReturn
+OpFunctionEnd)";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(), HasSubstr("Invalid scope value"));
+}
+
+TEST_F(ValidateMisc, ShaderClockSubgroupScope) {
+  const std::string spirv = ShaderClockSpriv + R"(
+%3 = OpTypeFunction %void
+%ulong = OpTypeInt 64 0
+%uint = OpTypeInt 32 0
+%_ptr_Function_ulong = OpTypePointer Function %ulong
+%subgroup = OpConstant %uint 3
+%uint_1 = OpConstant %uint 1
+%main = OpFunction %void None %3
+%5 = OpLabel
+%time1 = OpVariable %_ptr_Function_ulong Function
+%11 = OpReadClockKHR %ulong %subgroup
+OpStore %time1 %11
+OpReturn
+OpFunctionEnd)";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateMisc, ShaderClockDeviceScope) {
+  const std::string spirv = ShaderClockSpriv + R"(
+%3 = OpTypeFunction %void
+%ulong = OpTypeInt 64 0
+%uint = OpTypeInt 32 0
+%_ptr_Function_ulong = OpTypePointer Function %ulong
+%device = OpConstant %uint 1
+%uint_1 = OpConstant %uint 1
+%main = OpFunction %void None %3
+%5 = OpLabel
+%time1 = OpVariable %_ptr_Function_ulong Function
+%11 = OpReadClockKHR %ulong %device
+OpStore %time1 %11
+OpReturn
+OpFunctionEnd)";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateMisc, ShaderClockWorkgroupScope) {
+  const std::string spirv = ShaderClockSpriv + R"(
+%3 = OpTypeFunction %void
+%ulong = OpTypeInt 64 0
+%uint = OpTypeInt 32 0
+%_ptr_Function_ulong = OpTypePointer Function %ulong
+%workgroup = OpConstant %uint 2
+%uint_1 = OpConstant %uint 1
+%main = OpFunction %void None %3
+%5 = OpLabel
+%time1 = OpVariable %_ptr_Function_ulong Function
+%11 = OpReadClockKHR %ulong %workgroup
+OpStore %time1 %11
+OpReturn
+OpFunctionEnd)";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Scope must be Subgroup or Device"));
+}
 }  // namespace
 }  // namespace val
 }  // namespace spvtools
diff --git a/test/val/val_opencl_test.cpp b/test/val/val_opencl_test.cpp
index 18b2f71..1064158 100644
--- a/test/val/val_opencl_test.cpp
+++ b/test/val/val_opencl_test.cpp
@@ -45,15 +45,18 @@
 TEST_F(ValidateOpenCL, NonOpenCLMemoryModelBad) {
   std::string spirv = R"(
      OpCapability Kernel
-     OpMemoryModel Physical32 GLSL450
+     OpCapability Addresses
+     OpCapability VulkanMemoryModelKHR
+     OpExtension "SPV_KHR_vulkan_memory_model"
+     OpMemoryModel Physical32 VulkanKHR
 )";
 
   CompileSuccessfully(spirv);
 
   EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_OPENCL_1_2));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Memory model must be OpenCL in the OpenCL environment."
-                        "\n  OpMemoryModel Physical32 GLSL450\n"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Memory model must be OpenCL in the OpenCL environment."));
 }
 
 TEST_F(ValidateOpenCL, NonVoidSampledTypeImageBad) {
diff --git a/test/val/val_ssa_test.cpp b/test/val/val_ssa_test.cpp
index e10abd7..035c710 100644
--- a/test/val/val_ssa_test.cpp
+++ b/test/val/val_ssa_test.cpp
@@ -1415,7 +1415,8 @@
                OpName %intptrt "intptrt"
                OpTypeForwardPointer %intptrt UniformConstant
        %uint = OpTypeInt 32 0
-    %intptrt = OpTypePointer UniformConstant %uint
+     %struct = OpTypeStruct %uint
+    %intptrt = OpTypePointer UniformConstant %struct
 )";
 
   CompileSuccessfully(str);
diff --git a/test/val/val_storage_test.cpp b/test/val/val_storage_test.cpp
index f54b425..fe37a93 100644
--- a/test/val/val_storage_test.cpp
+++ b/test/val/val_storage_test.cpp
@@ -157,6 +157,7 @@
   const auto str = R"(
           OpCapability Kernel
           OpCapability Linkage
+          OpCapability GenericPointer
           OpMemoryModel Logical OpenCL
 %intt   = OpTypeInt 32 0
 %ptrt   = OpTypePointer Function %intt
@@ -172,6 +173,7 @@
   const auto str = R"(
           OpCapability Shader
           OpCapability Linkage
+          OpCapability GenericPointer
           OpMemoryModel Logical GLSL450
 %intt   = OpTypeInt 32 1
 %voidt  = OpTypeVoid
@@ -184,7 +186,7 @@
           OpFunctionEnd
 )";
   CompileSuccessfully(str);
-  ASSERT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions());
+  EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("OpVariable storage class cannot be Generic"));
 }
@@ -307,12 +309,10 @@
            std::make_tuple("Workgroup", false, true, ""),
            std::make_tuple("Private", false, true, ""),
            std::make_tuple("Function", true, true, ""),
-           std::make_tuple(
-               "CrossWorkgroup", false, false,
-               "For WebGPU, OpTypePointer storage class must be one of"),
-           std::make_tuple(
-               "PushConstant", false, false,
-               "For WebGPU, OpTypePointer storage class must be one of")));
+           std::make_tuple("CrossWorkgroup", false, false,
+                           "Invalid storage class for target environment"),
+           std::make_tuple("PushConstant", false, false,
+                           "Invalid storage class for target environment")));
 
 }  // namespace
 }  // namespace val
diff --git a/test/val/val_type_unique_test.cpp b/test/val/val_type_unique_test.cpp
index 67ceadd..45a4d50 100644
--- a/test/val/val_type_unique_test.cpp
+++ b/test/val/val_type_unique_test.cpp
@@ -210,9 +210,11 @@
 OpTypeForwardPointer %ptr Generic
 OpTypeForwardPointer %ptr2 Generic
 %intt = OpTypeInt 32 0
+%int_struct = OpTypeStruct %intt
 %floatt = OpTypeFloat 32
-%ptr = OpTypePointer Generic %intt
-%ptr2 = OpTypePointer Generic %floatt
+%ptr = OpTypePointer Generic %int_struct
+%float_struct = OpTypeStruct %floatt
+%ptr2 = OpTypePointer Generic %float_struct
 )";
   CompileSuccessfully(str.c_str());
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
diff --git a/test/val/val_webgpu_test.cpp b/test/val/val_webgpu_test.cpp
index c55f927..8f62555 100644
--- a/test/val/val_webgpu_test.cpp
+++ b/test/val/val_webgpu_test.cpp
@@ -187,23 +187,6 @@
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
 }
 
-TEST_F(ValidateWebGPU, NonLogicalAddressingModelBad) {
-  std::string spirv = R"(
-     OpCapability Shader
-     OpCapability VulkanMemoryModelKHR
-     OpExtension "SPV_KHR_vulkan_memory_model"
-     OpMemoryModel Physical32 VulkanKHR
-)";
-
-  CompileSuccessfully(spirv);
-
-  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Addressing model must be Logical for WebGPU "
-                        "environment.\n  OpMemoryModel Physical32 "
-                        "VulkanKHR\n"));
-}
-
 TEST_F(ValidateWebGPU, NonVulkanKHRMemoryModelBad) {
   std::string spirv = R"(
      OpCapability Shader
diff --git a/tools/as/as.cpp b/tools/as/as.cpp
index acef5b4..f6e9629 100644
--- a/tools/as/as.cpp
+++ b/tools/as/as.cpp
@@ -48,7 +48,7 @@
       argv0, argv0, target_env_list.c_str());
 }
 
-static const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_4;
+static const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_5;
 
 int main(int argc, char** argv) {
   const char* inFile = nullptr;
diff --git a/tools/cfg/cfg.cpp b/tools/cfg/cfg.cpp
index 411ef88..ce7f1c2 100644
--- a/tools/cfg/cfg.cpp
+++ b/tools/cfg/cfg.cpp
@@ -44,7 +44,7 @@
       argv0, argv0);
 }
 
-static const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_4;
+static const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_5;
 
 int main(int argc, char** argv) {
   const char* inFile = nullptr;
diff --git a/tools/dis/dis.cpp b/tools/dis/dis.cpp
index 2a6f411..c0ce267 100644
--- a/tools/dis/dis.cpp
+++ b/tools/dis/dis.cpp
@@ -60,7 +60,7 @@
       argv0, argv0);
 }
 
-static const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_4;
+static const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_5;
 
 int main(int argc, char** argv) {
   const char* inFile = nullptr;
diff --git a/tools/fuzz/fuzz.cpp b/tools/fuzz/fuzz.cpp
index 24e4ac6..5d582f5 100644
--- a/tools/fuzz/fuzz.cpp
+++ b/tools/fuzz/fuzz.cpp
@@ -20,6 +20,7 @@
 #include <sstream>
 #include <string>
 
+#include "source/fuzz/force_render_red.h"
 #include "source/fuzz/fuzzer.h"
 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
 #include "source/fuzz/replayer.h"
@@ -54,6 +55,8 @@
 
 // Status and actions to perform after parsing command-line arguments.
 enum class FuzzActions {
+  FORCE_RENDER_RED,  // Turn the shader into a form such that it is guaranteed
+                     // to render a red image.
   FUZZ,    // Run the fuzzer to apply transformations in a randomized fashion.
   REPLAY,  // Replay an existing sequence of transformations.
   SHRINK,  // Shrink an existing sequence of transformations with respect to an
@@ -72,43 +75,66 @@
       R"(%s - Fuzzes an equivalent SPIR-V binary based on a given binary.
 
 USAGE: %s [options] <input.spv> -o <output.spv>
+USAGE: %s [options] <input.spv> -o <output.spv> \
+  --shrink=<input.transformations> -- <interestingness_test> [args...]
 
-The SPIR-V binary is read from <input.spv>, which must have extension .spv.  If
-<input.facts> is also present, facts about the SPIR-V binary are read from this
-file.
+The SPIR-V binary is read from <input.spv>.  If <input.facts> is also present,
+facts about the SPIR-V binary are read from this file.
 
 The transformed SPIR-V binary is written to <output.spv>.  Human-readable and
 binary representations of the transformations that were applied are written to
 <output.transformations_json> and <output.transformations>, respectively.
 
+When passing --shrink=<input.transformations> an <interestingness_test>
+must also be provided; this is the path to a script that returns 0 if and only
+if a given SPIR-V binary is interesting.  The SPIR-V binary will be passed to
+the script as an argument after any other provided arguments [args...].  The
+"--" characters are optional but denote that all arguments that follow are
+positional arguments and thus will be forwarded to the interestingness script,
+and not parsed by %s.
+
 NOTE: The fuzzer is a work in progress.
 
 Options (in lexicographical order):
 
   -h, --help
                Print this help.
+  --force-render-red
+               Transforms the input shader into a shader that writes red to the
+               output buffer, and then captures the original shader as the body
+               of a conditional with a dynamically false guard.  Exploits input
+               facts to make the guard non-obviously false.  This option is a
+               helper for massaging crash-inducing tests into a runnable
+               format; it does not perform any fuzzing.
   --replay
                File from which to read a sequence of transformations to replay
                (instead of fuzzing)
-  --seed
+  --seed=
                Unsigned 32-bit integer seed to control random number
                generation.
-  --shrink
+  --shrink=
                File from which to read a sequence of transformations to shrink
                (instead of fuzzing)
-  --shrinker-step-limit
+  --shrinker-step-limit=
                Unsigned 32-bit integer specifying maximum number of steps the
                shrinker will take before giving up.  Ignored unless --shrink
                is used.
-  --interestingness
-               Path to an interestingness function to guide shrinking: a script
-               that returns 0 if and only if a given binary is interesting.
-               Required if --shrink is provided; disallowed otherwise.
+  --shrinker-temp-file-prefix=
+               Specifies a temporary file prefix that will be used to output
+               temporary shader files during shrinking.  A number and .spv
+               extension will be added.  The default is "temp_", which will
+               cause files like "temp_0001.spv" to be output to the current
+               directory.  Ignored unless --shrink is used.
+  --replay-validation
+               Run the validator after applying each transformation during
+               replay (including the replay that occurs during shrinking).
+               Aborts if an invalid binary is created.  Useful for debugging
+               spirv-fuzz.
   --version
                Display fuzzer version information.
 
 )",
-      program, program);
+      program, program, program, program);
 }
 
 // Message consumer for this tool.  Used to emit diagnostics during
@@ -122,24 +148,20 @@
   fprintf(stderr, "%s\n", message);
 }
 
-bool EndsWithSpv(const std::string& filename) {
-  std::string dot_spv = ".spv";
-  return filename.length() >= dot_spv.length() &&
-         0 == filename.compare(filename.length() - dot_spv.length(),
-                               filename.length(), dot_spv);
-}
-
 FuzzStatus ParseFlags(int argc, const char** argv, std::string* in_binary_file,
                       std::string* out_binary_file,
                       std::string* replay_transformations_file,
-                      std::string* interestingness_function_file,
+                      std::vector<std::string>* interestingness_test,
                       std::string* shrink_transformations_file,
+                      std::string* shrink_temp_file_prefix,
                       spvtools::FuzzerOptions* fuzzer_options) {
   uint32_t positional_arg_index = 0;
+  bool only_positional_arguments_remain = false;
+  bool force_render_red = false;
 
   for (int argi = 1; argi < argc; ++argi) {
     const char* cur_arg = argv[argi];
-    if ('-' == cur_arg[0]) {
+    if ('-' == cur_arg[0] && !only_positional_arguments_remain) {
       if (0 == strcmp(cur_arg, "--version")) {
         spvtools::Logf(FuzzDiagnostic, SPV_MSG_INFO, nullptr, {}, "%s\n",
                        spvSoftwareVersionDetailsString());
@@ -154,13 +176,15 @@
           PrintUsage(argv[0]);
           return {FuzzActions::STOP, 1};
         }
+      } else if (0 == strncmp(cur_arg, "--force-render-red",
+                              sizeof("--force-render-red") - 1)) {
+        force_render_red = true;
       } else if (0 == strncmp(cur_arg, "--replay=", sizeof("--replay=") - 1)) {
         const auto split_flag = spvtools::utils::SplitFlagArgs(cur_arg);
         *replay_transformations_file = std::string(split_flag.second);
-      } else if (0 == strncmp(cur_arg, "--interestingness=",
-                              sizeof("--interestingness=") - 1)) {
-        const auto split_flag = spvtools::utils::SplitFlagArgs(cur_arg);
-        *interestingness_function_file = std::string(split_flag.second);
+      } else if (0 == strncmp(cur_arg, "--replay-validation",
+                              sizeof("--replay-validation") - 1)) {
+        fuzzer_options->enable_replay_validation();
       } else if (0 == strncmp(cur_arg, "--shrink=", sizeof("--shrink=") - 1)) {
         const auto split_flag = spvtools::utils::SplitFlagArgs(cur_arg);
         *shrink_transformations_file = std::string(split_flag.second);
@@ -181,11 +205,18 @@
             static_cast<uint32_t>(strtol(split_flag.second.c_str(), &end, 10));
         assert(end != split_flag.second.c_str() && errno == 0);
         fuzzer_options->set_shrinker_step_limit(step_limit);
-      } else if ('\0' == cur_arg[1]) {
-        // We do not support fuzzing from standard input.  We could support
-        // this if there was a compelling use case.
+      } else if (0 == strncmp(cur_arg, "--shrinker-temp-file-prefix=",
+                              sizeof("--shrinker-temp-file-prefix=") - 1)) {
+        const auto split_flag = spvtools::utils::SplitFlagArgs(cur_arg);
+        *shrink_temp_file_prefix = std::string(split_flag.second);
+      } else if (0 == strcmp(cur_arg, "--")) {
+        only_positional_arguments_remain = true;
+      } else {
+        std::stringstream ss;
+        ss << "Unrecognized argument: " << cur_arg << std::endl;
+        spvtools::Error(FuzzDiagnostic, nullptr, {}, ss.str().c_str());
         PrintUsage(argv[0]);
-        return {FuzzActions::STOP, 0};
+        return {FuzzActions::STOP, 1};
       }
     } else if (positional_arg_index == 0) {
       // Binary input file name
@@ -193,9 +224,7 @@
       *in_binary_file = std::string(cur_arg);
       positional_arg_index++;
     } else {
-      spvtools::Error(FuzzDiagnostic, nullptr, {},
-                      "Too many positional arguments specified");
-      return {FuzzActions::STOP, 1};
+      interestingness_test->push_back(std::string(cur_arg));
     }
   }
 
@@ -204,20 +233,47 @@
     return {FuzzActions::STOP, 1};
   }
 
-  if (!EndsWithSpv(*in_binary_file)) {
-    spvtools::Error(FuzzDiagnostic, nullptr, {},
-                    "Input filename must have extension .spv");
-    return {FuzzActions::STOP, 1};
-  }
-
   if (out_binary_file->empty()) {
     spvtools::Error(FuzzDiagnostic, nullptr, {}, "-o required");
     return {FuzzActions::STOP, 1};
   }
 
-  if (!EndsWithSpv(*out_binary_file)) {
+  auto const_fuzzer_options =
+      static_cast<spv_const_fuzzer_options>(*fuzzer_options);
+  if (force_render_red) {
+    if (!replay_transformations_file->empty() ||
+        !shrink_transformations_file->empty() ||
+        const_fuzzer_options->replay_validation_enabled) {
+      spvtools::Error(FuzzDiagnostic, nullptr, {},
+                      "The --force-render-red argument cannot be used with any "
+                      "other arguments except -o.");
+      return {FuzzActions::STOP, 1};
+    }
+    return {FuzzActions::FORCE_RENDER_RED, 0};
+  }
+
+  if (replay_transformations_file->empty() &&
+      shrink_transformations_file->empty() &&
+      static_cast<spv_const_fuzzer_options>(*fuzzer_options)
+          ->replay_validation_enabled) {
     spvtools::Error(FuzzDiagnostic, nullptr, {},
-                    "Output filename must have extension .spv");
+                    "The --replay-validation argument can only be used with "
+                    "one of the --replay or --shrink arguments.");
+    return {FuzzActions::STOP, 1};
+  }
+
+  if (shrink_transformations_file->empty() && !interestingness_test->empty()) {
+    spvtools::Error(FuzzDiagnostic, nullptr, {},
+                    "Too many positional arguments specified; extra positional "
+                    "arguments are used as the interestingness function, which "
+                    "are only valid with the --shrink option.");
+    return {FuzzActions::STOP, 1};
+  }
+
+  if (!shrink_transformations_file->empty() && interestingness_test->empty()) {
+    spvtools::Error(
+        FuzzDiagnostic, nullptr, {},
+        "The --shrink option requires an interestingness function.");
     return {FuzzActions::STOP, 1};
   }
 
@@ -230,28 +286,14 @@
           "The --replay and --shrink arguments are mutually exclusive.");
       return {FuzzActions::STOP, 1};
     }
-    if (!interestingness_function_file->empty()) {
-      spvtools::Error(FuzzDiagnostic, nullptr, {},
-                      "The --replay and --interestingness arguments are "
-                      "mutually exclusive.");
-      return {FuzzActions::STOP, 1};
-    }
     return {FuzzActions::REPLAY, 0};
   }
 
-  if (!shrink_transformations_file->empty() ^
-      !interestingness_function_file->empty()) {
-    spvtools::Error(FuzzDiagnostic, nullptr, {},
-                    "Both or neither of the --shrink and --interestingness "
-                    "arguments must be provided.");
-    return {FuzzActions::STOP, 1};
-  }
-
   if (!shrink_transformations_file->empty()) {
     // The tool is being invoked in shrink mode.
-    assert(!interestingness_function_file->empty() &&
-           "An error should have been raised if --shrink but not --interesting "
-           "was provided.");
+    assert(!interestingness_test->empty() &&
+           "An error should have been raised if --shrink was provided without "
+           "an interestingness test.");
     return {FuzzActions::SHRINK, 0};
   }
 
@@ -278,6 +320,7 @@
 }
 
 bool Replay(const spv_target_env& target_env,
+            spv_const_fuzzer_options fuzzer_options,
             const std::vector<uint32_t>& binary_in,
             const spvtools::fuzz::protobufs::FactSequence& initial_facts,
             const std::string& replay_transformations_file,
@@ -289,7 +332,8 @@
                             &transformation_sequence)) {
     return false;
   }
-  spvtools::fuzz::Replayer replayer(target_env);
+  spvtools::fuzz::Replayer replayer(target_env,
+                                    fuzzer_options->replay_validation_enabled);
   replayer.SetMessageConsumer(spvtools::utils::CLIMessageConsumer);
   auto replay_result_status =
       replayer.Run(binary_in, initial_facts, transformation_sequence,
@@ -303,7 +347,8 @@
             const std::vector<uint32_t>& binary_in,
             const spvtools::fuzz::protobufs::FactSequence& initial_facts,
             const std::string& shrink_transformations_file,
-            const std::string& interestingness_function_file,
+            const std::string& shrink_temp_file_prefix,
+            const std::vector<std::string>& interestingness_command,
             std::vector<uint32_t>* binary_out,
             spvtools::fuzz::protobufs::TransformationSequence*
                 transformations_applied) {
@@ -313,18 +358,28 @@
     return false;
   }
   spvtools::fuzz::Shrinker shrinker(target_env,
-                                    fuzzer_options->shrinker_step_limit);
+                                    fuzzer_options->shrinker_step_limit,
+                                    fuzzer_options->replay_validation_enabled);
   shrinker.SetMessageConsumer(spvtools::utils::CLIMessageConsumer);
 
+  assert(!interestingness_command.empty() &&
+         "An error should have been raised because the interestingness_command "
+         "is empty.");
+  std::stringstream joined;
+  joined << interestingness_command[0];
+  for (size_t i = 1, size = interestingness_command.size(); i < size; ++i) {
+    joined << " " << interestingness_command[i];
+  }
+  std::string interestingness_command_joined = joined.str();
+
   spvtools::fuzz::Shrinker::InterestingnessFunction interestingness_function =
-      [interestingness_function_file](std::vector<uint32_t> binary,
-                                      uint32_t reductions_applied) -> bool {
+      [interestingness_command_joined, shrink_temp_file_prefix](
+          std::vector<uint32_t> binary, uint32_t reductions_applied) -> bool {
     std::stringstream ss;
-    ss << "temp_" << std::setw(4) << std::setfill('0') << reductions_applied
-       << ".spv";
+    ss << shrink_temp_file_prefix << std::setw(4) << std::setfill('0')
+       << reductions_applied << ".spv";
     const auto spv_file = ss.str();
-    const std::string command =
-        std::string(interestingness_function_file) + " " + spv_file;
+    const std::string command = interestingness_command_joined + " " + spv_file;
     auto write_file_succeeded =
         WriteFile(spv_file.c_str(), "wb", &binary[0], binary.size());
     (void)(write_file_succeeded);
@@ -362,21 +417,39 @@
 
 }  // namespace
 
+// Dumps |binary| to file |filename|. Useful for interactive debugging.
+void DumpShader(const std::vector<uint32_t>& binary, const char* filename) {
+  auto write_file_succeeded =
+      WriteFile(filename, "wb", &binary[0], binary.size());
+  if (!write_file_succeeded) {
+    std::cerr << "Failed to dump shader" << std::endl;
+  }
+}
+
+// Dumps the SPIRV-V module in |context| to file |filename|. Useful for
+// interactive debugging.
+void DumpShader(spvtools::opt::IRContext* context, const char* filename) {
+  std::vector<uint32_t> binary;
+  context->module()->ToBinary(&binary, false);
+  DumpShader(binary, filename);
+}
+
 const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_3;
 
 int main(int argc, const char** argv) {
   std::string in_binary_file;
   std::string out_binary_file;
   std::string replay_transformations_file;
-  std::string interestingness_function_file;
+  std::vector<std::string> interestingness_test;
   std::string shrink_transformations_file;
+  std::string shrink_temp_file_prefix = "temp_";
 
   spvtools::FuzzerOptions fuzzer_options;
 
-  FuzzStatus status =
-      ParseFlags(argc, argv, &in_binary_file, &out_binary_file,
-                 &replay_transformations_file, &interestingness_function_file,
-                 &shrink_transformations_file, &fuzzer_options);
+  FuzzStatus status = ParseFlags(
+      argc, argv, &in_binary_file, &out_binary_file,
+      &replay_transformations_file, &interestingness_test,
+      &shrink_transformations_file, &shrink_temp_file_prefix, &fuzzer_options);
 
   if (status.action == FuzzActions::STOP) {
     return status.code;
@@ -388,10 +461,12 @@
   }
 
   spvtools::fuzz::protobufs::FactSequence initial_facts;
-  const std::string dot_spv(".spv");
-  std::string in_facts_file =
-      in_binary_file.substr(0, in_binary_file.length() - dot_spv.length()) +
-      ".facts";
+
+  // If not found, dot_pos will be std::string::npos, which can be used in
+  // substr to mean "the end of the string"; there is no need to check the
+  // result.
+  size_t dot_pos = in_binary_file.rfind('.');
+  std::string in_facts_file = in_binary_file.substr(0, dot_pos) + ".facts";
   std::ifstream facts_input(in_facts_file);
   if (facts_input) {
     std::string facts_json_string((std::istreambuf_iterator<char>(facts_input)),
@@ -411,6 +486,12 @@
   spv_target_env target_env = kDefaultEnvironment;
 
   switch (status.action) {
+    case FuzzActions::FORCE_RENDER_RED:
+      if (!spvtools::fuzz::ForceRenderRed(target_env, binary_in, initial_facts,
+                                          &binary_out)) {
+        return 1;
+      }
+      break;
     case FuzzActions::FUZZ:
       if (!Fuzz(target_env, fuzzer_options, binary_in, initial_facts,
                 &binary_out, &transformations_applied)) {
@@ -418,7 +499,7 @@
       }
       break;
     case FuzzActions::REPLAY:
-      if (!Replay(target_env, binary_in, initial_facts,
+      if (!Replay(target_env, fuzzer_options, binary_in, initial_facts,
                   replay_transformations_file, &binary_out,
                   &transformations_applied)) {
         return 1;
@@ -431,8 +512,9 @@
         return 1;
       }
       if (!Shrink(target_env, fuzzer_options, binary_in, initial_facts,
-                  shrink_transformations_file, interestingness_function_file,
-                  &binary_out, &transformations_applied)) {
+                  shrink_transformations_file, shrink_temp_file_prefix,
+                  interestingness_test, &binary_out,
+                  &transformations_applied)) {
         return 1;
       }
     } break;
@@ -447,35 +529,40 @@
     return 1;
   }
 
-  std::string output_file_prefix =
-      out_binary_file.substr(0, out_binary_file.length() - dot_spv.length());
-  std::ofstream transformations_file;
-  transformations_file.open(output_file_prefix + ".transformations",
-                            std::ios::out | std::ios::binary);
-  bool success =
-      transformations_applied.SerializeToOstream(&transformations_file);
-  transformations_file.close();
-  if (!success) {
-    spvtools::Error(FuzzDiagnostic, nullptr, {},
-                    "Error writing out transformations binary");
-    return 1;
-  }
+  if (status.action != FuzzActions::FORCE_RENDER_RED) {
+    // If not found, dot_pos will be std::string::npos, which can be used in
+    // substr to mean "the end of the string"; there is no need to check the
+    // result.
+    dot_pos = out_binary_file.rfind('.');
+    std::string output_file_prefix = out_binary_file.substr(0, dot_pos);
+    std::ofstream transformations_file;
+    transformations_file.open(output_file_prefix + ".transformations",
+                              std::ios::out | std::ios::binary);
+    bool success =
+        transformations_applied.SerializeToOstream(&transformations_file);
+    transformations_file.close();
+    if (!success) {
+      spvtools::Error(FuzzDiagnostic, nullptr, {},
+                      "Error writing out transformations binary");
+      return 1;
+    }
 
-  std::string json_string;
-  auto json_options = google::protobuf::util::JsonOptions();
-  json_options.add_whitespace = true;
-  auto json_generation_status = google::protobuf::util::MessageToJsonString(
-      transformations_applied, &json_string, json_options);
-  if (json_generation_status != google::protobuf::util::Status::OK) {
-    spvtools::Error(FuzzDiagnostic, nullptr, {},
-                    "Error writing out transformations in JSON format");
-    return 1;
-  }
+    std::string json_string;
+    auto json_options = google::protobuf::util::JsonOptions();
+    json_options.add_whitespace = true;
+    auto json_generation_status = google::protobuf::util::MessageToJsonString(
+        transformations_applied, &json_string, json_options);
+    if (json_generation_status != google::protobuf::util::Status::OK) {
+      spvtools::Error(FuzzDiagnostic, nullptr, {},
+                      "Error writing out transformations in JSON format");
+      return 1;
+    }
 
-  std::ofstream transformations_json_file(output_file_prefix +
-                                          ".transformations_json");
-  transformations_json_file << json_string;
-  transformations_json_file.close();
+    std::ofstream transformations_json_file(output_file_prefix +
+                                            ".transformations_json");
+    transformations_json_file << json_string;
+    transformations_json_file.close();
+  }
 
   return 0;
 }
diff --git a/tools/opt/opt.cpp b/tools/opt/opt.cpp
index b229c84..4364e3e 100644
--- a/tools/opt/opt.cpp
+++ b/tools/opt/opt.cpp
@@ -59,7 +59,7 @@
   return ss.str();
 }
 
-const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_4;
+const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_5;
 
 std::string GetLegalizationPasses() {
   spvtools::Optimizer optimizer(kDefaultEnvironment);
@@ -80,13 +80,13 @@
 }
 
 std::string GetVulkanToWebGPUPasses() {
-  spvtools::Optimizer optimizer(SPV_ENV_WEBGPU_0);
+  spvtools::Optimizer optimizer(SPV_ENV_VULKAN_1_1);
   optimizer.RegisterVulkanToWebGPUPasses();
   return GetListOfPassesAsString(optimizer);
 }
 
 std::string GetWebGPUToVulkanPasses() {
-  spvtools::Optimizer optimizer(SPV_ENV_VULKAN_1_1);
+  spvtools::Optimizer optimizer(SPV_ENV_WEBGPU_0);
   optimizer.RegisterWebGPUToVulkanPasses();
   return GetListOfPassesAsString(optimizer);
 }
@@ -141,6 +141,16 @@
                and constant index access chains in entry point call tree
                functions.)");
   printf(R"(
+  --convert-relaxed-to-half
+               Convert all RelaxedPrecision arithmetic operations to half
+               precision, inserting conversion operations where needed.
+               Run after function scope variable load and store elimination
+               for better results. Simplify-instructions, redundancy-elimination
+               and DCE should be run after this pass to eliminate excess
+               conversions. This conversion is useful when the target platform
+               does not support RelaxedPrecision or ignores it. This pass also
+               removes all RelaxedPrecision decorations.)");
+  printf(R"(
   --copy-propagate-arrays
                Does propagation of memory references when an array is a copy of
                another.  It will only propagate an array if the source is never
@@ -393,6 +403,10 @@
                Looks for instructions in the same function that compute the
                same value, and deletes the redundant ones.)");
   printf(R"(
+  --relax-float-ops
+               Decorate all float operations with RelaxedPrecision if not already
+               so decorated. This does not decorate types or variables.)");
+  printf(R"(
   --relax-struct-store
                Allow store from one struct type to a different type with
                compatible layout and members. This option is forwarded to the
@@ -512,6 +526,13 @@
                avoid triggering those bugs.
                Current workarounds: Avoid OpUnreachable in loops.)");
   printf(R"(
+  --wrap-opkill
+               Replaces all OpKill instructions in functions that can be called
+               from a continue construct with a function call to a function
+               whose only instruction is an OpKill.  This is done to enable
+               inlining on these functions.
+               )");
+  printf(R"(
   --unify-const
                Remove the duplicated constants.)");
   printf(R"(
@@ -778,7 +799,7 @@
           return {OPT_STOP, 1};
         }
 
-        optimizer->SetTargetEnv(SPV_ENV_WEBGPU_0);
+        optimizer->SetTargetEnv(SPV_ENV_VULKAN_1_1);
         optimizer->RegisterVulkanToWebGPUPasses();
       } else if (0 == strcmp(cur_arg, "--webgpu-to-vulkan")) {
         webgpu_to_vulkan_set = true;
@@ -796,7 +817,7 @@
           return {OPT_STOP, 1};
         }
 
-        optimizer->SetTargetEnv(SPV_ENV_VULKAN_1_1);
+        optimizer->SetTargetEnv(SPV_ENV_WEBGPU_0);
         optimizer->RegisterWebGPUToVulkanPasses();
       } else if (0 == strcmp(cur_arg, "--validate-after-all")) {
         optimizer->SetValidateAfterAll(true);
diff --git a/tools/reduce/reduce.cpp b/tools/reduce/reduce.cpp
index be07b20..0bdeb82 100644
--- a/tools/reduce/reduce.cpp
+++ b/tools/reduce/reduce.cpp
@@ -58,20 +58,24 @@
   // NOTE: Please maintain flags in lexicographical order.
   printf(
       R"(%s - Reduce a SPIR-V binary file with respect to a user-provided
-              interestingness test.
+interestingness test.
 
-USAGE: %s [options] <input> <interestingness-test>
+USAGE: %s [options] <input.spv> -o <output.spv> -- <interestingness_test> [args...]
 
-The SPIR-V binary is read from <input>.
+The SPIR-V binary is read from <input.spv>. The reduced SPIR-V binary is
+written to <output.spv>.
 
-Whether a binary is interesting is determined by <interestingness-test>, which
-should be the path to a script.
+Whether a binary is interesting is determined by <interestingness_test>, which
+should be the path to a script. The "--" characters are optional but denote
+that all arguments that follow are positional arguments and thus will be
+forwarded to the interestingness test, and not parsed by %s.
 
  * The script must be executable.
 
- * The script should take the path to a SPIR-V binary file (.spv) as its single
+ * The script should take the path to a SPIR-V binary file (.spv) as an
    argument, and exit with code 0 if and only if the binary file is
-   interesting.
+   interesting.  The binary will be passed to the script as an argument after
+   any other provided arguments [args...].
 
  * Example: an interestingness test for reducing a SPIR-V binary file that
    causes tool "foo" to exit with error code 1 and print "Fatal error: bar" to
@@ -95,9 +99,15 @@
                SPIR-V module that fails to validate.
   -h, --help
                Print this help.
-  --step-limit
+  --step-limit=
                32-bit unsigned integer specifying maximum number of steps the
                reducer will take before giving up.
+  --temp-file-prefix=
+               Specifies a temporary file prefix that will be used to output
+               temporary shader files during reduction.  A number and .spv
+               extension will be added.  The default is "temp_", which will
+               cause files like "temp_0001.spv" to be output to the current
+               directory.
   --version
                Display reducer version information.
 
@@ -109,7 +119,7 @@
   --scalar-block-layout
   --skip-block-layout
 )",
-      program, program);
+      program, program, program);
 }
 
 // Message consumer for this tool.  Used to emit diagnostics during
@@ -123,15 +133,19 @@
   fprintf(stderr, "%s\n", message);
 }
 
-ReduceStatus ParseFlags(int argc, const char** argv, const char** in_file,
-                        const char** interestingness_test,
+ReduceStatus ParseFlags(int argc, const char** argv,
+                        std::string* in_binary_file,
+                        std::string* out_binary_file,
+                        std::vector<std::string>* interestingness_test,
+                        std::string* temp_file_prefix,
                         spvtools::ReducerOptions* reducer_options,
                         spvtools::ValidatorOptions* validator_options) {
   uint32_t positional_arg_index = 0;
+  bool only_positional_arguments_remain = false;
 
   for (int argi = 1; argi < argc; ++argi) {
     const char* cur_arg = argv[argi];
-    if ('-' == cur_arg[0]) {
+    if ('-' == cur_arg[0] && !only_positional_arguments_remain) {
       if (0 == strcmp(cur_arg, "--version")) {
         spvtools::Logf(ReduceDiagnostic, SPV_MSG_INFO, nullptr, {}, "%s\n",
                        spvSoftwareVersionDetailsString());
@@ -139,11 +153,13 @@
       } else if (0 == strcmp(cur_arg, "--help") || 0 == strcmp(cur_arg, "-h")) {
         PrintUsage(argv[0]);
         return {REDUCE_STOP, 0};
-      } else if ('\0' == cur_arg[1]) {
-        // We do not support reduction from standard input.  We could support
-        // this if there was a compelling use case.
-        PrintUsage(argv[0]);
-        return {REDUCE_STOP, 0};
+      } else if (0 == strcmp(cur_arg, "-o")) {
+        if (out_binary_file->empty() && argi + 1 < argc) {
+          *out_binary_file = std::string(argv[++argi]);
+        } else {
+          PrintUsage(argv[0]);
+          return {REDUCE_STOP, 1};
+        }
       } else if (0 == strncmp(cur_arg,
                               "--step-limit=", sizeof("--step-limit=") - 1)) {
         const auto split_flag = spvtools::utils::SplitFlagArgs(cur_arg);
@@ -153,43 +169,54 @@
             static_cast<uint32_t>(strtol(split_flag.second.c_str(), &end, 10));
         assert(end != split_flag.second.c_str() && errno == 0);
         reducer_options->set_step_limit(step_limit);
+      } else if (0 == strcmp(cur_arg, "--fail-on-validation-error")) {
+        reducer_options->set_fail_on_validation_error(true);
+      } else if (0 == strcmp(cur_arg, "--before-hlsl-legalization")) {
+        validator_options->SetBeforeHlslLegalization(true);
+      } else if (0 == strcmp(cur_arg, "--relax-logical-pointer")) {
+        validator_options->SetRelaxLogicalPointer(true);
+      } else if (0 == strcmp(cur_arg, "--relax-block-layout")) {
+        validator_options->SetRelaxBlockLayout(true);
+      } else if (0 == strcmp(cur_arg, "--scalar-block-layout")) {
+        validator_options->SetScalarBlockLayout(true);
+      } else if (0 == strcmp(cur_arg, "--skip-block-layout")) {
+        validator_options->SetSkipBlockLayout(true);
+      } else if (0 == strcmp(cur_arg, "--relax-struct-store")) {
+        validator_options->SetRelaxStructStore(true);
+      } else if (0 == strncmp(cur_arg, "--temp-file-prefix=",
+                              sizeof("--temp-file-prefix=") - 1)) {
+        const auto split_flag = spvtools::utils::SplitFlagArgs(cur_arg);
+        *temp_file_prefix = std::string(split_flag.second);
+      } else if (0 == strcmp(cur_arg, "--")) {
+        only_positional_arguments_remain = true;
+      } else {
+        std::stringstream ss;
+        ss << "Unrecognized argument: " << cur_arg << std::endl;
+        spvtools::Error(ReduceDiagnostic, nullptr, {}, ss.str().c_str());
+        PrintUsage(argv[0]);
+        return {REDUCE_STOP, 1};
       }
     } else if (positional_arg_index == 0) {
-      // Input file name
-      assert(!*in_file);
-      *in_file = cur_arg;
+      // Binary input file name
+      assert(in_binary_file->empty());
+      *in_binary_file = std::string(cur_arg);
       positional_arg_index++;
-    } else if (positional_arg_index == 1) {
-      assert(!*interestingness_test);
-      *interestingness_test = cur_arg;
-      positional_arg_index++;
-    } else if (0 == strcmp(cur_arg, "--fail-on-validation-error")) {
-      reducer_options->set_fail_on_validation_error(true);
-    } else if (0 == strcmp(cur_arg, "--before-hlsl-legalization")) {
-      validator_options->SetBeforeHlslLegalization(true);
-    } else if (0 == strcmp(cur_arg, "--relax-logical-pointer")) {
-      validator_options->SetRelaxLogicalPointer(true);
-    } else if (0 == strcmp(cur_arg, "--relax-block-layout")) {
-      validator_options->SetRelaxBlockLayout(true);
-    } else if (0 == strcmp(cur_arg, "--scalar-block-layout")) {
-      validator_options->SetScalarBlockLayout(true);
-    } else if (0 == strcmp(cur_arg, "--skip-block-layout")) {
-      validator_options->SetSkipBlockLayout(true);
-    } else if (0 == strcmp(cur_arg, "--relax-struct-store")) {
-      validator_options->SetRelaxStructStore(true);
     } else {
-      spvtools::Error(ReduceDiagnostic, nullptr, {},
-                      "Too many positional arguments specified");
-      return {REDUCE_STOP, 1};
+      interestingness_test->push_back(std::string(cur_arg));
     }
   }
 
-  if (!*in_file) {
+  if (in_binary_file->empty()) {
     spvtools::Error(ReduceDiagnostic, nullptr, {}, "No input file specified");
     return {REDUCE_STOP, 1};
   }
 
-  if (!*interestingness_test) {
+  if (out_binary_file->empty()) {
+    spvtools::Error(ReduceDiagnostic, nullptr, {}, "-o required");
+    return {REDUCE_STOP, 1};
+  }
+
+  if (interestingness_test->empty()) {
     spvtools::Error(ReduceDiagnostic, nullptr, {},
                     "No interestingness test specified");
     return {REDUCE_STOP, 1};
@@ -217,18 +244,21 @@
   DumpShader(binary, filename);
 }
 
-const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_4;
+const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_5;
 
 int main(int argc, const char** argv) {
-  const char* in_file = nullptr;
-  const char* interestingness_test = nullptr;
+  std::string in_binary_file;
+  std::string out_binary_file;
+  std::vector<std::string> interestingness_test;
+  std::string temp_file_prefix = "temp_";
 
   spv_target_env target_env = kDefaultEnvironment;
   spvtools::ReducerOptions reducer_options;
   spvtools::ValidatorOptions validator_options;
 
-  ReduceStatus status = ParseFlags(argc, argv, &in_file, &interestingness_test,
-                                   &reducer_options, &validator_options);
+  ReduceStatus status = ParseFlags(
+      argc, argv, &in_binary_file, &out_binary_file, &interestingness_test,
+      &temp_file_prefix, &reducer_options, &validator_options);
 
   if (status.action == REDUCE_STOP) {
     return status.code;
@@ -242,15 +272,22 @@
 
   spvtools::reduce::Reducer reducer(target_env);
 
+  std::stringstream joined;
+  joined << interestingness_test[0];
+  for (size_t i = 1, size = interestingness_test.size(); i < size; ++i) {
+    joined << " " << interestingness_test[i];
+  }
+  std::string interestingness_command_joined = joined.str();
+
   reducer.SetInterestingnessFunction(
-      [interestingness_test](std::vector<uint32_t> binary,
-                             uint32_t reductions_applied) -> bool {
+      [interestingness_command_joined, temp_file_prefix](
+          std::vector<uint32_t> binary, uint32_t reductions_applied) -> bool {
         std::stringstream ss;
-        ss << "temp_" << std::setw(4) << std::setfill('0') << reductions_applied
-           << ".spv";
+        ss << temp_file_prefix << std::setw(4) << std::setfill('0')
+           << reductions_applied << ".spv";
         const auto spv_file = ss.str();
         const std::string command =
-            std::string(interestingness_test) + " " + spv_file;
+            interestingness_command_joined + " " + spv_file;
         auto write_file_succeeded =
             WriteFile(spv_file.c_str(), "wb", &binary[0], binary.size());
         (void)(write_file_succeeded);
@@ -263,7 +300,7 @@
   reducer.SetMessageConsumer(spvtools::utils::CLIMessageConsumer);
 
   std::vector<uint32_t> binary_in;
-  if (!ReadFile<uint32_t>(in_file, "rb", &binary_in)) {
+  if (!ReadFile<uint32_t>(in_binary_file.c_str(), "rb", &binary_in)) {
     return 1;
   }
 
@@ -271,12 +308,20 @@
   const auto reduction_status = reducer.Run(std::move(binary_in), &binary_out,
                                             reducer_options, validator_options);
 
-  if (reduction_status == spvtools::reduce::Reducer::ReductionResultStatus::
-                              kInitialStateNotInteresting ||
-      !WriteFile<uint32_t>("_reduced_final.spv", "wb", binary_out.data(),
+  // Always try to write the output file, even if the reduction failed.
+  if (!WriteFile<uint32_t>(out_binary_file.c_str(), "wb", binary_out.data(),
                            binary_out.size())) {
     return 1;
   }
 
-  return 0;
+  // These are the only successful statuses.
+  switch (reduction_status) {
+    case spvtools::reduce::Reducer::ReductionResultStatus::kComplete:
+    case spvtools::reduce::Reducer::ReductionResultStatus::kReachedStepLimit:
+      return 0;
+    default:
+      break;
+  }
+
+  return 1;
 }
diff --git a/tools/sva/.eslintrc.json b/tools/sva/.eslintrc.json
new file mode 100644
index 0000000..2f07726
--- /dev/null
+++ b/tools/sva/.eslintrc.json
@@ -0,0 +1,25 @@
+{
+    "env": {
+        "browser": true,
+        "es6": true,
+        "node": true,
+        "mocha": true
+    },
+    "extends": "eslint:recommended",
+    "parserOptions": {
+        "ecmaVersion": 2018,
+        "sourceType": "module"
+    },
+    "rules": {
+        "block-scoped-var": "error",
+        "consistent-return": "error",
+        "eqeqeq": ["error", "always"],
+        "indent": [ "error", 2 ],
+        "linebreak-style": [ "error", "unix" ],
+        "no-eval": "error",
+        "no-shadow": "error",
+        "no-shadow-restricted-names": "error",
+        "quotes": [ "error", "double" ],
+        "semi": [ "error", "always" ]
+    }
+}
diff --git a/tools/sva/.gitignore b/tools/sva/.gitignore
new file mode 100644
index 0000000..88e64c3
--- /dev/null
+++ b/tools/sva/.gitignore
@@ -0,0 +1,6 @@
+.DS_Store
+node_modules
+third_party/spirv-headers
+o.sva
+build
+yarn-error.log
diff --git a/tools/sva/README.md b/tools/sva/README.md
new file mode 100644
index 0000000..d80b4d2
--- /dev/null
+++ b/tools/sva/README.md
@@ -0,0 +1,41 @@
+# SVA
+
+SPIR-V Assember for WebGPU. The SPIR-V Assembler is a JavaScript library to
+convert SPIR-V assembly (as produced by spirv-dis in SPIR-V Tools) into a
+SPIR-V binary. The assembler assumes it is generating WebGPU SPIR-V and thus has
+the following limitations.
+
+ * Only 32 bit integers and floats supported
+ * Only GLSL accepted as an extended instruction set
+ * Doesn't support ! syntax for integers
+ * Doesn't support hex encoding for float
+
+```shell
+yarn install
+yarn test
+```
+
+You can also use `yarn watch` to watch all of the files and re-run tests as
+needed.
+
+## Webserver
+Using `yarn serve` will start a webserver on localhost:5000. If you load the
+`tests/index.html` file this will load the SVA files into browser.
+
+## Command Line
+There is a simple assembler binary with can be executed from the command line.
+
+```shell
+yarn sva tests/simple.spv_asm
+```
+
+The above will generate a `o.sva` file in the current directory.
+
+## Update spirv.data.json
+
+If there is a new spirv-headers release update the externals folder checkout
+and then:
+
+```shell
+./tools/process_grammar.rb > src/spirv.data.json
+```
diff --git a/tools/sva/bin/sva.js b/tools/sva/bin/sva.js
new file mode 100755
index 0000000..e2448d6
--- /dev/null
+++ b/tools/sva/bin/sva.js
@@ -0,0 +1,32 @@
+#!/usr/bin/env node
+//
+// Copyright 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+"use strict";
+
+const fs = require("fs");
+
+import SVA from "../src/sva.js";
+
+let input = fs.readFileSync(process.argv[2], "utf-8");
+let u = SVA.assemble(input);
+
+if (typeof u === "string") {
+  console.log(u);
+} else {
+  fs.writeFileSync("o.sva", new Buffer(u.buffer), (err) => {
+    console.log(["ERROR", err]);
+  });
+}
diff --git a/tools/sva/mocha.opts b/tools/sva/mocha.opts
new file mode 100644
index 0000000..4a52320
--- /dev/null
+++ b/tools/sva/mocha.opts
@@ -0,0 +1 @@
+--recursive
diff --git a/tools/sva/package.json b/tools/sva/package.json
new file mode 100644
index 0000000..3072d4c
--- /dev/null
+++ b/tools/sva/package.json
@@ -0,0 +1,25 @@
+{
+  "name": "sva",
+  "version": "0.1.0",
+  "description": "SPIR-V Assembler",
+  "main": "index.js",
+  "author": "dan sinclair <dsinclair@google.com>",
+  "license": "Apache-2.0",
+  "private": true,
+  "scripts": {
+    "sva": "node -r esm bin/sva.js",
+    "lint": "eslint --fix --ext .js .",
+    "test": "mocha --require esm src/**/*_test.js",
+    "watch": "mocha --require esm --watch --watch-extension js \"src/**/*_test.js\"",
+    "serve": "serve",
+    "bundle": "rollup -c"
+  },
+  "devDependencies": {
+    "chai": "^4.2.0",
+    "eslint": "^6.3.0",
+    "esm": "^3.2.25",
+    "mocha": "^6.2.0",
+    "rollup": "^1.21.4",
+    "serve": "^11.1.0"
+  }
+}
diff --git a/tools/sva/rollup.config.js b/tools/sva/rollup.config.js
new file mode 100644
index 0000000..2056e16
--- /dev/null
+++ b/tools/sva/rollup.config.js
@@ -0,0 +1,7 @@
+export default {
+  input: 'src/sva.js',
+  output: {
+    file: 'build/sva.js',
+    format: 'esm',
+  }
+}
diff --git a/tools/sva/src/assembler.js b/tools/sva/src/assembler.js
new file mode 100644
index 0000000..7bc208e
--- /dev/null
+++ b/tools/sva/src/assembler.js
@@ -0,0 +1,98 @@
+// Copyright 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+export default class Assembler {
+  static get GENERATOR_ID() { return 0; }
+
+  /**
+   * @param {AST} the AST to build the SPIR-V from
+   */
+  constructor(ast) {
+    this.ast_ = ast;
+  }
+
+  /**
+   * Assembles the AST into binary SPIR-V.
+   * @return {Uint32Array} The SPIR-V binary data.
+   */
+  assemble() {
+    let total_size = 5;
+    for (const inst of this.ast_.instructions()) {
+      total_size += 1;
+      for (const op of inst.operands()) {
+        total_size += op.length();
+      }
+    }
+
+    let u = new Uint32Array(total_size);
+    u[0] = 0x07230203;  // Magic
+    u[1] = 0x00010500;  // Version 1.5
+    u[2] = Assembler.GENERATOR_ID;  // Generator magic number
+    u[3] = this.ast_.getIdBounds();  // ID bounds
+    u[4] = 0;  // Reserved
+
+    let idx = 5;
+    for (const inst of this.ast_.instructions()) {
+      let op_size = 1;
+      for (const op of inst.operands()) {
+        op_size += op.length();
+      }
+
+      u[idx++] = op_size << 16 | inst.opcode();
+      for (const op of inst.operands()) {
+        idx = this.processOp(u, idx, op);
+      }
+    }
+
+    return u;
+  }
+
+  processOp(u, idx, op) {
+    if (op.type() === "string") {
+      let len = 0;
+      let v = 0;
+      for (const ch of op.value()) {
+        v = v | (ch.charCodeAt(0) << (len * 8));
+        len += 1;
+
+        if (len === 4) {
+          u[idx++] = v;
+          len = 0;
+          v = 0;
+        }
+      }
+      // Make sure either the terminating 0 byte is written or the last
+      // partial word is written.
+      u[idx++] = v;
+
+    } else if (op.type() === "float") {
+      // TODO(dsinclair): Handle 64 bit floats ...
+      let b = new ArrayBuffer(4);
+      let f = new Float32Array(b);
+      f[0] = op.value();
+
+      let u2 = new Uint32Array(b);
+
+      u[idx++] = u2[0];
+    } else {
+      u[idx++] = op.value();
+    }
+
+    for (const param of op.params()) {
+      idx = this.processOp(u, idx, param);
+    }
+
+    return idx;
+  }
+}
diff --git a/tools/sva/src/assembler_test.js b/tools/sva/src/assembler_test.js
new file mode 100644
index 0000000..a23d211
--- /dev/null
+++ b/tools/sva/src/assembler_test.js
@@ -0,0 +1,165 @@
+// Copyright 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import { assert } from "chai";
+import Lexer from "./lexer";
+import Parser from "./parser";
+import grammar from "./spirv.data.js";
+import Assembler from "./assembler";
+
+describe("assembler", () => {
+  it("generates SPIR-V magic number", () => {
+    let input = `; SPIR-V
+; Version: 1.0
+; Generator: Khronos Glslang Reference Front End; 7
+; Bound: 6
+; Schema: 0
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpSource GLSL 440
+               OpName %main "main"
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd`;
+    let l = new Lexer(input);
+    let p = new Parser(grammar, l);
+
+    let ast = p.parse();
+    assert.exists(ast, p.error);
+
+    let a = new Assembler(ast);
+    let res = a.assemble();
+    assert.equal(res[0], 0x07230203);
+  });
+
+  it("assembles enumerant params", () => {
+    let input = "OpExecutionMode %main LocalSize 2 3 4";
+
+    let l = new Lexer(input);
+    let p = new Parser(grammar, l);
+
+    let ast = p.parse();
+    assert.exists(ast, p.error);
+
+    let a = new Assembler(ast);
+    let res = a.assemble();
+
+    assert.lengthOf(res, 11);
+    assert.equal(res[5], (6 /* word count */ << 16) | 16 /* opcode */);
+    assert.equal(res[6], 1 /* %main */);
+    assert.equal(res[7], 17 /* LocalSize */);
+    assert.equal(res[8], 2);
+    assert.equal(res[9], 3);
+    assert.equal(res[10], 4);
+  });
+
+  it("assembles float 32 values", () => {
+    let input = `%float = OpTypeFloat 32
+                 %float1 = OpConstant %float 0.400000006`;
+    let l = new Lexer(input);
+    let p = new Parser(grammar, l);
+
+    let ast = p.parse();
+    assert.exists(ast, p.error);
+
+    let a = new Assembler(ast);
+    let res = a.assemble();
+
+    assert.lengthOf(res, 12);
+    assert.equal(res[8], (4 /* word count */ << 16) | 43 /* opcode */);
+    assert.equal(res[9], 1 /* %float */);
+    assert.equal(res[10], 2 /* %float */);
+    assert.equal(res[11], 0x3ecccccd /* 0.400000006 */);
+  });
+
+  describe("strings", () => {
+    it("assembles 'abcd'", () => {
+      let input = `OpName %mains "abcd"`;
+      let l = new Lexer(input);
+      let p = new Parser(grammar, l);
+
+      let ast = p.parse();
+      assert.exists(ast, p.error);
+
+      let a = new Assembler(ast);
+      let res = a.assemble();
+
+      assert.lengthOf(res, 9);
+      assert.equal(res[5], (4 /* word count */ << 16) | 5 /* opcode */);
+      assert.equal(res[6], 1 /* %mains */);
+      assert.equal(res[7], 0x64636261 /* food */);
+      assert.equal(res[8], 0x00000000 /* null byte */);
+    });
+
+    it("assembles 'abcde'", () => {
+      let input = `OpName %mains "abcde"`;
+      let l = new Lexer(input);
+      let p = new Parser(grammar, l);
+
+      let ast = p.parse();
+      assert.exists(ast, p.error);
+
+      let a = new Assembler(ast);
+      let res = a.assemble();
+
+      assert.lengthOf(res, 9);
+      assert.equal(res[5], (4 /* word count */ << 16) | 5 /* opcode */);
+      assert.equal(res[6], 1 /* %mains */);
+      assert.equal(res[7], 0x64636261 /* abcd */);
+      assert.equal(res[8], 0x00000065 /* e */);
+    });
+
+    it("assembles 'abcdef'", () => {
+      let input = `OpName %mains "abcdef"`;
+      let l = new Lexer(input);
+      let p = new Parser(grammar, l);
+
+      let ast = p.parse();
+      assert.exists(ast, p.error);
+
+      let a = new Assembler(ast);
+      let res = a.assemble();
+
+      assert.lengthOf(res, 9);
+      assert.equal(res[5], (4 /* word count */ << 16) | 5 /* opcode */);
+      assert.equal(res[6], 1 /* %mains */);
+      assert.equal(res[7], 0x64636261 /* abcd */);
+      assert.equal(res[8], 0x00006665 /* ef */);
+    });
+
+    it("assembles 'abcdefg'", () => {
+      let input = `OpName %mains "abcdefg"`;
+      let l = new Lexer(input);
+      let p = new Parser(grammar, l);
+
+      let ast = p.parse();
+      assert.exists(ast, p.error);
+
+      let a = new Assembler(ast);
+      let res = a.assemble();
+
+      assert.lengthOf(res, 9);
+      assert.equal(res[5], (4 /* word count */ << 16) | 5 /* opcode */);
+      assert.equal(res[6], 1 /* %mains */);
+      assert.equal(res[7], 0x64636261 /* abcd */);
+      assert.equal(res[8], 0x00676665 /* efg */);
+    });
+  });
+});
diff --git a/tools/sva/src/ast.js b/tools/sva/src/ast.js
new file mode 100644
index 0000000..d396d2f
--- /dev/null
+++ b/tools/sva/src/ast.js
@@ -0,0 +1,141 @@
+// Copyright 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+class Module {
+  constructor() {
+    this.instructions_ = [];
+    this.next_id_ = 1;
+
+    /**
+     * Maps {string, hash} where the string is the type name and the hash is:
+     *   type- 'float' or 'int'
+     *   width- number of bits needed to store number
+     *   signed- the sign of the number
+     */
+    this.types_ = {};
+
+    /**
+     * Maps {string, number} where the string is the type name and the number is
+     * the id value.
+     */
+    this.assigned_ids_ = {};
+  }
+
+  instructions() { return this.instructions_; }
+
+  instruction(val) { return this.instructions_[val]; }
+
+  addInstruction(inst) {
+    this.instructions_.push(inst);
+
+    // Record type information
+    if (inst.name() === "OpTypeInt" || inst.name() === "OpTypeFloat") {
+      let is_int = inst.name() === "OpTypeInt";
+
+      this.types_[inst.operand(0).name()] = {
+        type: is_int ? "int" : "float",
+        width: inst.operand(1).value(),
+        signed: is_int ? inst.operand(2).value() : 1
+      };
+    }
+
+    // Record operand result id's
+    inst.operands().forEach((op) => {
+      if (op.rawValue() !== undefined && op.type() === "result_id") {
+        this.next_id_ = Math.max(this.next_id_, op.rawValue() + 1);
+      }
+    });
+  }
+
+  getType(name) { return this.types_[name]; }
+
+  getId(name) {
+    if (this.assigned_ids_[name] !== undefined) {
+      return this.assigned_ids_[name];
+    }
+
+    let next = this.next_id_;
+    this.assigned_ids_[name] = next;
+
+    this.next_id_ += 1;
+    return next;
+  }
+
+  getIdBounds() { return this.next_id_; }
+}
+
+class Instruction {
+  constructor(name, opcode, operands) {
+    this.name_ = name;
+    this.opcode_ = opcode;
+    this.operands_ = operands;
+  }
+
+  name() { return this.name_; }
+
+  opcode() { return this.opcode_; }
+
+  operands() { return this.operands_; }
+
+  operand(val) { return this.operands_[val]; }
+}
+
+class Operand {
+  constructor(mod, name, type, value, params) {
+    this.module_ = mod;
+    this.name_ = name;
+    this.type_ = type;
+    this.value_ = value;
+    this.params_ = params;
+  }
+
+  name() { return this.name_; }
+
+  length() {
+    // Get the value just to force it to be filled.
+    this.value();
+
+    if (this.type_ === "string") {
+      return Math.ceil((this.value_.length + 1) / 4);
+    }
+
+    let size = 1;
+    for (const param of this.params_) {
+      size += param.length();
+    }
+    return size;
+  }
+
+  type() { return this.type_; }
+
+  rawValue() { return this.value_; }
+
+  // This method should only be called on ResultId's after the full parse is
+  // complete. This is because the AST will only have the maximum seen numeric
+  // ResultId when the parse is done.
+  value() {
+    if (this.value_ === undefined) {
+      this.value_ = this.module_.getId(this.name_);
+    }
+    return this.value_;
+  }
+
+  params() { return this.params_; }
+}
+
+export {
+  Module,
+  Instruction,
+  Operand
+};
diff --git a/tools/sva/src/lexer.js b/tools/sva/src/lexer.js
new file mode 100644
index 0000000..b39f93a
--- /dev/null
+++ b/tools/sva/src/lexer.js
@@ -0,0 +1,363 @@
+// Copyright 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import { Token, TokenType } from "./token.js";
+
+export default class Lexer {
+  /**
+   * @param {String} input The input string to tokenize.
+   */
+  constructor(input) {
+    this.input_ = input;
+    this.len_ = input.length;
+    this.cur_pos_ = 0;
+    this.cur_line_ = 1;
+
+    this.num_regex_ = /^[0-9]+$/;
+    this.alpha_regex_ = /^[a-zA-Z_]+$/;
+    this.op_regex_ = /^Op[A-Z][^\s]*$/;
+    this.hex_regex_ = /^[0-9a-fA-F]$/;
+  }
+
+  /**
+   * Parses the next token from the input stream.
+   * @return {Token} the next token.
+   */
+  next() {
+    this.skipWhitespace();
+    this.skipComments();
+
+    if (this.cur_pos_ >= this.len_)
+      return new Token(TokenType.kEOF, this.cur_line_);
+
+    let n = this.tryHexInteger();
+    if (n !== undefined)
+      return n;
+
+    n = this.tryFloat();
+    if (n !== undefined)
+      return n;
+
+    n = this.tryInteger();
+    if (n !== undefined)
+      return n;
+
+    n = this.tryString();
+    if (n !== undefined)
+      return n;
+
+    n = this.tryOp();
+    if (n !== undefined)
+      return n;
+
+    n = this.tryPunctuation();
+    if (n !== undefined)
+      return n;
+
+    n = this.tryResultId();
+    if (n !== undefined)
+      return n;
+
+    n = this.tryIdent();
+    if (n !== undefined)
+      return n;
+
+    return new Token(TokenType.kError, this.cur_line_, "Failed to match token");
+  }
+
+  is(str) {
+    if (this.len_ <= this.cur_pos_ + (str.length - 1))
+      return false;
+
+    for (let i = 0; i < str.length; ++i) {
+      if (this.input_[this.cur_pos_ + i] !== str[i])
+        return false;
+    }
+
+    return true;
+  }
+
+  isNum(ch) {
+    return ch.match(this.num_regex_);
+  }
+
+  isAlpha(ch) {
+    return ch.match(this.alpha_regex_);
+  }
+
+  isAlphaNum(ch) {
+    return this.isNum(ch) || this.isAlpha(ch);
+  }
+
+  isHex(char) {
+    return char.match(this.hex_regex_);
+  }
+
+  isCurWhitespace() {
+    return this.is(" ") || this.is("\t") || this.is("\r") || this.is("\n");
+  }
+
+  skipWhitespace() {
+    for(;;) {
+      let cur_pos = this.cur_pos_;
+      while (this.cur_pos_ < this.len_ &&
+          this.isCurWhitespace()) {
+        if (this.is("\n"))
+          this.cur_line_ += 1;
+
+        this.cur_pos_ += 1;
+      }
+
+      this.skipComments();
+
+      // Cursor didn't move so no whitespace matched.
+      if (cur_pos === this.cur_pos_)
+        break;
+    }
+  }
+
+  skipComments() {
+    if (!this.is(";"))
+      return;
+
+    while (this.cur_pos_ < this.len_ && !this.is("\n"))
+      this.cur_pos_ += 1;
+  }
+
+  /**
+   * Attempt to parse the next part of the input as a float.
+   * @return {Token|undefined} returns a Token if a float is matched,
+   *                           undefined otherwise.
+   */
+  tryFloat() {
+    let start = this.cur_pos_;
+    let end = start;
+
+    if (this.cur_pos_ >= this.len_)
+      return undefined;
+    if (this.input_[end] === "-")
+      end += 1;
+
+    while (end < this.len_ && this.isNum(this.input_[end]))
+      end += 1;
+
+    // Must have a "." in a float
+    if (end >= this.len_ || this.input_[end] !== ".")
+      return undefined;
+
+    end += 1;
+    while (end < this.len_ && this.isNum(this.input_[end]))
+      end += 1;
+
+    let substr = this.input_.substr(start, end - start);
+    if (substr === "." || substr === "-.")
+      return undefined;
+
+    this.cur_pos_ = end;
+
+    return new Token(TokenType.kFloatLiteral, this.cur_line_, parseFloat(substr));
+  }
+
+  /**
+   * Attempt to parse a hex encoded integer.
+   * @return {Token|undefined} returns a Token if a Hex number is matched,
+   *                           undefined otherwise.
+   */
+  tryHexInteger() {
+    let start = this.cur_pos_;
+    let end = start;
+
+    if (this.cur_pos_ >= this.len_)
+      return undefined;
+    if (end + 2 >= this.len_ || this.input_[end] !== "0" ||
+        this.input_[end + 1] !== "x") {
+      return undefined;
+    }
+
+    end += 2;
+
+    while (end < this.len_ && this.isHex(this.input_[end]))
+      end += 1;
+
+    this.cur_pos_ = end;
+
+    let val = parseInt(this.input_.substr(start, end - start), 16);
+    return new Token(TokenType.kIntegerLiteral, this.cur_line_, val);
+  }
+
+  /**
+   * Attempt to parse an encoded integer.
+   * @return {Token|undefined} returns a Token if a number is matched,
+   *                           undefined otherwise.
+   */
+  tryInteger() {
+    let start = this.cur_pos_;
+    let end = start;
+
+    if (this.cur_pos_ >= this.len_)
+      return undefined;
+    if (this.input_[end] === "-")
+      end += 1;
+
+    if (end >= this.len_ || !this.isNum(this.input_[end]))
+      return undefined;
+
+    while (end < this.len_ && this.isNum(this.input_[end]))
+      end += 1;
+
+    this.cur_pos_ = end;
+
+    let val = parseInt(this.input_.substr(start, end - start), 10);
+    return new Token(TokenType.kIntegerLiteral, this.cur_line_, val);
+  }
+
+  /**
+   * Attempt to parse a result id.
+   * @return {Token|undefined} returns a Token if a result id is matched,
+   *                           undefined otherwise.
+   */
+  tryResultId() {
+    let start = this.cur_pos_;
+    if (start >= this.len_)
+      return undefined;
+    if (!this.is("%"))
+      return undefined;
+
+    start += 1;
+    this.cur_pos_ += 1;
+    while (this.cur_pos_ < this.len_ &&
+        (this.isAlphaNum(this.input_[this.cur_pos_]) || this.is("_"))) {
+      this.cur_pos_ += 1;
+    }
+
+    let ident = this.input_.substr(start, this.cur_pos_ - start);
+    let value = undefined;
+    if (ident.match(this.num_regex_))
+      value = parseInt(ident, 10);
+
+    return new Token(TokenType.kResultId, this.cur_line_, {
+      name: ident,
+      val: value
+    });
+  }
+
+  /**
+   * Attempt to parse an identifier.
+   * @return {Token|undefined} returns a Token if an identifier is matched,
+   *                           undefined otherwise.
+   */
+  tryIdent() {
+    let start = this.cur_pos_;
+    if (start >= this.len_)
+      return undefined;
+
+    while (this.cur_pos_ < this.len_ &&
+        (this.isAlphaNum(this.input_[this.cur_pos_]) || this.is("_"))) {
+      this.cur_pos_ += 1;
+    }
+
+    let ident = this.input_.substr(start, this.cur_pos_ - start);
+    return new Token(TokenType.kIdentifier, this.cur_line_, ident);
+  }
+
+  /**
+   * Attempt to parse an Op command.
+   * @return {Token|undefined} returns a Token if an Op command is matched,
+   *                           undefined otherwise.
+   */
+  tryOp() {
+    let start = this.cur_pos_;
+    if (this.cur_pos_ >= this.len_ || (this.cur_pos_ + 1 >= this.len_))
+      return undefined;
+
+    if (this.input_[this.cur_pos_] !== "O" ||
+        this.input_[this.cur_pos_ + 1] !== "p") {
+      return undefined;
+    }
+
+    while (this.cur_pos_ < this.len_ &&
+        !this.isCurWhitespace()) {
+      this.cur_pos_ += 1;
+    }
+
+    return new Token(TokenType.kOp, this.cur_line_, {
+      name: this.input_.substr(start, this.cur_pos_ - start)
+    });
+  }
+
+  /**
+   * Attempts to match punctuation strings against the input
+   * @return {Token|undefined} Returns the Token for the punctuation or
+   *                           undefined if no matches found.
+   */
+  tryPunctuation() {
+    let type = undefined;
+    if (this.is("="))
+      type = TokenType.kEqual;
+    else if (this.is("|"))
+      type = TokenType.kPipe;
+
+    if (type === undefined)
+      return undefined;
+
+    this.cur_pos_ += type.length;
+    return new Token(type, this.cur_line_, type);
+  }
+
+  /**
+   * Attempts to match strings against the input
+   * @return {Token|undefined} Returns the Token for the string or undefined
+   *                           if no match found.
+   */
+  tryString() {
+    let start = this.cur_pos_;
+
+    // Must have at least 2 chars for a string.
+    if (this.cur_pos_ >= this.len_ || (this.cur_pos_ + 1 >= this.len_))
+      return undefined;
+    if (!this.is("\""))
+      return undefined;
+
+    this.cur_pos_ += 1;
+    let str = "";
+    while (this.cur_pos_ <= this.len_) {
+      if (this.is("\""))
+        break;
+
+      if (this.is("\\")) {
+        this.cur_pos_ += 1;
+        if (this.cur_pos_ >= this.len_)
+          return undefined;
+
+        if (this.is("\\")) {
+          str += "\\";
+        } else if (this.is("\"")) {
+          str += '"';
+        } else {
+          str += this.input_[this.cur_pos_];
+        }
+      } else {
+        str += this.input_[this.cur_pos_];
+      }
+      this.cur_pos_ += 1;
+    }
+
+    if (this.cur_pos_ >= this.len_)
+      return undefined;
+
+    this.cur_pos_ += 1;
+
+    return new Token(TokenType.kStringLiteral, this.cur_line_, str);
+  }
+}
diff --git a/tools/sva/src/lexer_test.js b/tools/sva/src/lexer_test.js
new file mode 100644
index 0000000..32b24c7
--- /dev/null
+++ b/tools/sva/src/lexer_test.js
@@ -0,0 +1,191 @@
+// Copyright 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import { assert } from "chai";
+import Lexer from "./lexer";
+import { TokenType } from "./token";
+
+describe("lexer", () => {
+  describe("skipped content", () => {
+    it("skips whitespace", () => {
+      let input = " \t\r\n\t  \tOpKill\t\n\t  \r  ";
+      let l = new Lexer(input);
+
+      let t = l.next();
+      assert.equal(t.type, TokenType.kOp);
+      assert.equal(t.line, 2);
+      assert.equal(t.data.name, "OpKill");
+
+      t = l.next();
+      assert.equal(t.type, TokenType.kEOF);
+      assert.equal(t.line, 3);
+    });
+
+    it("skips ; comments", () => {
+      let input = `; start with comment
+OpKill ; end of line comment
+; another comment
+%1`;
+
+      let l = new Lexer(input);
+      let t = l.next();
+      assert.equal(t.type, TokenType.kOp);
+      assert.equal(t.data.name, "OpKill");
+      assert.equal(t.line, 2);
+
+      t = l.next();
+      assert.equal(t.type, TokenType.kResultId);
+      assert.equal(t.data.name, "1");
+      assert.equal(t.data.val, 1);
+      assert.equal(t.line, 4);
+    });
+  });
+
+  describe("numerics", () => {
+    it("parses floats", () => {
+      let input = ["0.0", "0.", ".0", "5.7", "5.", ".7", "-0.0", "-.0",
+        "-0.", "-5.7", "-5.", "-.7"];
+
+      let results = [0.0, 0.0, 0.0, 5.7, 5.0, 0.7, 0.0, 0.0, 0.0, -5.7, -5.0,
+        -0.7];
+      input.forEach((val, idx) => {
+        let l = new Lexer(val);
+        let t = l.next();
+
+        assert.equal(t.type, TokenType.kFloatLiteral,
+          `expected ${val} to be a float got ${t.type}`);
+        assert.equal(t.data, results[idx],
+          `expected ${results[idx]} === ${t.data}`);
+
+        t = l.next();
+        assert.equal(t.type, TokenType.kEOF);
+        assert.equal(t.data, undefined);
+      });
+    });
+
+    it("handles invalid floats", () => {
+      let input = [".", "-."];
+      input.forEach((val) => {
+        let l = new Lexer(val);
+        let t = l.next();
+
+        assert.notEqual(t.type, TokenType.kFloatLiteral,
+          `expect ${val} to not match type float`);
+      });
+    });
+
+    it("parses integers", () => {
+      let input = ["0", "-0", "123", "-123", "2147483647", "-2147483648",
+        "4294967295", "0x00", "0x24"];
+      let results = [0, 0, 123, -123,2147483647, -2147483648, 4294967295,
+        0x0, 0x24];
+
+      input.forEach((val, idx) => {
+        let l = new Lexer(val);
+        let t = l.next();
+
+        assert.equal(t.type, TokenType.kIntegerLiteral,
+          `expected ${val} to be an integer got ${t.type}`);
+        assert.equal(t.data, results[idx],
+          `expected ${results[idx]} === ${t.data}`);
+
+        t = l.next();
+        assert.equal(t.type, TokenType.kEOF);
+        assert.equal(t.data, undefined);
+      });
+    });
+  });
+
+  it("matches result_ids", () => {
+    let input = `%123
+%001
+%main
+%_a_b_c`;
+
+    let result = [
+      {name: "123", val: 123},
+      {name: "001", val: 1},
+      {name: "main", val: undefined},
+      {name: "_a_b_c", val: undefined}
+    ];
+
+    let l = new Lexer(input);
+    for (let i = 0; i < result.length; ++i) {
+      let t = l.next();
+      assert.equal(t.type, TokenType.kResultId);
+      assert.equal(t.data.name, result[i].name);
+      assert.equal(t.data.val, result[i].val);
+    }
+  });
+
+  it("matches punctuation", () => {
+    let input = "=";
+    let results = [TokenType.kEqual];
+
+    let l = new Lexer(input);
+    for (let i = 0; i < results.length; ++i) {
+      let t = l.next();
+      assert.equal(t.type, results[i]);
+      assert.equal(t.line, i + 1);
+    }
+
+    let t = l.next();
+    assert.equal(t.type, TokenType.kEOF);
+  });
+
+  describe("strings", () => {
+    it("matches strings", () => {
+      let input = "\"GLSL.std.450\"";
+
+      let l = new Lexer(input);
+      let t = l.next();
+      assert.equal(t.type, TokenType.kStringLiteral);
+      assert.equal(t.data, "GLSL.std.450");
+    });
+
+    it("handles unfinished strings", () => {
+      let input = "\"GLSL.std.450";
+
+      let l = new Lexer(input);
+      let t = l.next();
+      assert.equal(t.type, TokenType.kError);
+    });
+
+    it("handles escapes", () => {
+      let input = `"embedded\\"quote"
+"embedded\\\\slash"
+"embedded\\nchar"`;
+      let results = [`embedded\"quote`, `embedded\\slash`, `embeddednchar`];
+
+      let l = new Lexer(input);
+      for (let i = 0; i < results.length; ++i) {
+        let t = l.next();
+        assert.equal(t.type, TokenType.kStringLiteral, results[i]);
+        assert.equal(t.data, results[i]);
+      }
+    });
+  });
+
+  it("matches keywords", () => {
+    let input = "GLSL Function";
+    let results = ["GLSL", "Function"];
+
+    let l = new Lexer(input);
+    for (let i = 0; i < results.length; ++i) {
+      let t = l.next();
+      assert.equal(t.type, TokenType.kIdentifier, results[i]);
+      assert.equal(t.data, results[i]);
+    }
+  });
+});
diff --git a/tools/sva/src/parser.js b/tools/sva/src/parser.js
new file mode 100644
index 0000000..ccf872a
--- /dev/null
+++ b/tools/sva/src/parser.js
@@ -0,0 +1,283 @@
+// Copyright 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import { TokenType } from "./token.js";
+import * as AST from "./ast.js";
+
+export default class Parser {
+  /**
+   * @param {Hash} The SPIR-V grammar
+   * @param {Lexer} The lexer
+   * @return {AST} Attempts to build an AST from the tokens returned by the
+   *               given lexer
+   */
+  constructor(grammar, lexer) {
+    this.grammar_ = grammar;
+    this.lexer_ = lexer;
+
+    this.peek_ = [];
+    this.error_ = "";
+  }
+
+  get error() { return this.error_; }
+
+  next() {
+    return this.peek_.shift() || this.lexer_.next();
+  }
+
+  peek(idx) {
+    while (this.peek_.length <= idx) {
+      this.peek_.push(this.lexer_.next());
+    }
+    return this.peek_[idx];
+  }
+
+  /**
+   * Executes the parser.
+   *
+   * @return {AST|undefined} returns a parsed AST on success or undefined
+   *                         on error. The error message can be retrieved by
+   *                         calling error().
+   */
+  parse() {
+    let ast = new AST.Module();
+    for(;;) {
+      let token = this.next();
+      if (token === TokenType.kError) {
+        this.error_ = token.line() + ": " + token.data();
+        return undefined;
+      }
+      if (token.type === TokenType.kEOF)
+        break;
+
+      let result_id = undefined;
+      if (token.type === TokenType.kResultId) {
+        result_id = token;
+
+        token = this.next();
+        if (token.type !== TokenType.kEqual) {
+          this.error_ = token.line + ": expected = after result id";
+          return undefined;
+        }
+
+        token = this.next();
+      }
+
+      if (token.type !== TokenType.kOp) {
+        this.error_ = token.line + ": expected Op got " + token.type;
+        return undefined;
+      }
+
+      let name = token.data.name;
+      let data = this.getInstructionData(name);
+      let operands = [];
+      let result_type = undefined;
+
+      for (let operand of data.operands) {
+        if (operand.kind === "IdResult") {
+          if (result_id === undefined) {
+            this.error_ = token.line + ": expected result id";
+            return undefined;
+          }
+          let o = new AST.Operand(ast, result_id.data.name, "result_id",
+            result_id.data.val, []);
+          if (o === undefined) {
+            return undefined;
+          }
+          operands.push(o);
+        } else {
+          if (operand.quantifier === "?") {
+            if (this.nextIsNewInstr()) {
+              break;
+            }
+          } else if (operand.quantifier === "*") {
+            while (!this.nextIsNewInstr()) {
+              let o = this.extractOperand(ast, result_type, operand);
+              if (o === undefined) {
+                return undefined;
+              }
+              operands.push(o);
+            }
+            break;
+          }
+
+          let o = this.extractOperand(ast, result_type, operand);
+          if (o === undefined) {
+            return undefined;
+          }
+
+          // Store the result type away so we can use it for context dependent
+          // numbers if needed.
+          if (operand.kind === "IdResultType") {
+            result_type = ast.getType(o.name());
+          }
+
+          operands.push(o);
+        }
+      }
+
+      // Verify only GLSL extended instructions are used
+      if (name === "OpExtInstImport" && operands[1].value() !== "GLSL.std.450") {
+        this.error_ = token.line + ": Only GLSL.std.450 external instructions supported";
+        return undefined;
+      }
+
+      let inst = new AST.Instruction(name, data.opcode, operands);
+
+      ast.addInstruction(inst);
+    }
+    return ast;
+  }
+
+  getInstructionData(name) {
+    return this.grammar_["instructions"][name];
+  }
+
+  nextIsNewInstr() {
+    let n0 = this.peek(0);
+    if (n0.type === TokenType.kOp || n0.type === TokenType.kEOF) {
+      return true;
+    }
+
+    let n1 = this.peek(1);
+    if (n1.type === TokenType.kEOF) {
+      return false;
+    }
+    if (n0.type === TokenType.kResultId && n1.type === TokenType.kEqual)
+      return true;
+
+    return false;
+  }
+
+  extractOperand(ast, result_type, data) {
+    let t = this.next();
+
+    let name = undefined;
+    let kind = undefined;
+    let value = undefined;
+    let params = [];
+
+    // TODO(dsinclair): There are a bunch of missing types here. See
+    // https://github.com/KhronosGroup/SPIRV-Tools/blob/master/source/text.cpp#L210
+    //
+    // LiteralSpecConstantOpInteger
+    // PairLiteralIntegerIdRef
+    // PairIdRefLiteralInteger
+    // PairIdRefIdRef
+    if (data.kind === "IdResult" || data.kind === "IdRef"
+        || data.kind === "IdResultType" || data.kind === "IdScope"
+        || data.kind === "IdMemorySemantics") {
+      if (t.type !== TokenType.kResultId) {
+        this.error_ = t.line + ": expected result id";
+        return undefined;
+      }
+
+      name = t.data.name;
+      kind = "result_id";
+      value = t.data.val;
+    } else if (data.kind === "LiteralString") {
+      if (t.type !== TokenType.kStringLiteral) {
+        this.error_ = t.line + ": expected string not found";
+        return undefined;
+      }
+
+      name = t.data;
+      kind = "string";
+      value = t.data;
+    } else if (data.kind === "LiteralInteger") {
+      if (t.type !== TokenType.kIntegerLiteral) {
+        this.error_ = t.line + ": expected integer not found";
+        return undefined;
+      }
+
+      name = "" + t.data;
+      kind = t.type;
+      value = t.data;
+    } else if (data.kind === "LiteralContextDependentNumber") {
+      if (result_type === undefined) {
+        this.error_ = t.line +
+            ": missing result type for context dependent number";
+        return undefined;
+      }
+      if (t.type !== TokenType.kIntegerLiteral
+          && t.type !== TokenType.kFloatLiteral) {
+        this.error_ = t.line + ": expected number not found";
+        return undefined;
+      }
+
+      name = "" + t.data;
+      kind = result_type.type;
+      value = t.data;
+
+    } else if (data.kind === "LiteralExtInstInteger") {
+      if (t.type !== TokenType.kIdentifier) {
+        this.error_ = t.line + ": expected instruction identifier";
+        return undefined;
+      }
+
+      if (this.grammar_.ext[t.data] === undefined) {
+        this.error_ = t.line + `: unable to find extended instruction (${t.data})`;
+        return undefined;
+      }
+
+      name = t.data;
+      kind = "integer";
+      value = this.grammar_.ext[t.data];
+
+    } else {
+      let d = this.grammar_.operand_kinds[data.kind];
+      if (d === undefined) {
+        this.error_ = t.line + ": expected " + data.kind + " not found";
+        return undefined;
+      }
+
+      let val = d.values[t.data]["value"];
+      let names = [t.data];
+      if (d.type === "BitEnum") {
+        for(;;) {
+          let tmp = this.peek(0);
+          if (tmp.type !== TokenType.kPipe) {
+            break;
+          }
+
+          this.next();  // skip pipe
+          tmp = this.next();
+
+          if (tmp.type !== TokenType.kIdentifier) {
+            this.error_ = tmp.line() + ": expected identifier";
+            return undefined;
+          }
+
+          val |= d.values[tmp.data]["value"];
+          names.push(tmp.data);
+        }
+      }
+
+      name = names.join("|");
+      kind = d.type;
+      value = val;
+
+      for (const op_name of names) {
+        if (d.values[op_name]['params'] === undefined) {
+          continue;
+        }
+
+        for (const param of d.values[op_name]["params"]) {
+          params.push(this.extractOperand(ast, result_type, { kind: param }));
+        }
+      }
+    }
+    return new AST.Operand(ast, name, kind, value, params);
+  }
+}
diff --git a/tools/sva/src/parser_test.js b/tools/sva/src/parser_test.js
new file mode 100644
index 0000000..dffc0b3
--- /dev/null
+++ b/tools/sva/src/parser_test.js
@@ -0,0 +1,489 @@
+// Copyright 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import { assert } from "chai";
+import Lexer from "./lexer";
+import Parser from "./parser";
+import grammar from "./spirv.data.js";
+
+describe("parser", () => {
+  it("parses an opcode", () => {
+    let input = "OpKill";
+    let l = new Lexer(input);
+    let p = new Parser(grammar, l);
+
+    let ast = p.parse();
+    assert.exists(ast);
+    assert.lengthOf(ast.instructions(), 1);
+
+    let inst = ast.instruction(0);
+    assert.equal(inst.name(), "OpKill");
+    assert.equal(inst.opcode(), 252);
+    assert.lengthOf(inst.operands, 0);
+  });
+
+  it("parses an opcode with an identifier", () => {
+    let input = "OpCapability Shader";
+    let l = new Lexer(input);
+    let p = new Parser(grammar, l);
+
+    let ast = p.parse();
+    assert.exists(ast, p.error);
+    assert.lengthOf(ast.instructions(), 1);
+
+    let inst = ast.instruction(0);
+    assert.equal(inst.name(), "OpCapability");
+    assert.equal(inst.opcode(), 17);
+    assert.lengthOf(inst.operands(), 1);
+
+    let op = inst.operand(0);
+    assert.equal(op.name(), "Shader");
+    assert.equal(op.type(), "ValueEnum");
+    assert.equal(op.value(), 1);
+  });
+
+  it("parses an opcode with a result", () => {
+    let input = "%void = OpTypeVoid";
+    let l = new Lexer(input);
+    let p = new Parser(grammar, l);
+
+    let ast = p.parse();
+    assert.exists(ast);
+    assert.lengthOf(ast.instructions(), 1);
+
+    let inst = ast.instruction(0);
+    assert.equal(inst.name(), "OpTypeVoid");
+    assert.equal(inst.opcode(), 19);
+    assert.lengthOf(inst.operands(), 1);
+
+    let op = inst.operand(0);
+    assert.equal(op.name(), "void");
+    assert.equal(op.value(), 1);
+  });
+
+  it("sets module bounds based on numeric result", () => {
+    let input = "%3 = OpTypeVoid";
+
+    let l = new Lexer(input);
+    let p = new Parser(grammar, l);
+
+    let ast = p.parse();
+    assert.exists(ast);
+    assert.equal(ast.getId("next"), 4);
+  });
+
+  it("returns the same value for a named result_id", () => {
+    let input = "%3 = OpTypeFunction %int %int";
+
+    let l = new Lexer(input);
+    let p = new Parser(grammar, l);
+
+    let ast = p.parse();
+    assert.exists(ast);
+    assert.lengthOf(ast.instructions(), 1);
+
+    let inst = ast.instruction(0);
+    let op1 = inst.operand(1);
+    assert.equal(op1.name(), "int");
+    assert.equal(op1.value(), 4);
+
+    let op2 = inst.operand(2);
+    assert.equal(op2.name(), "int");
+    assert.equal(op2.value(), 4);
+  });
+
+  it("parses an opcode with a string", () => {
+    let input = "OpEntryPoint Fragment %main \"main\"";
+
+    let l = new Lexer(input);
+    let p = new Parser(grammar, l);
+
+    let ast = p.parse();
+    assert.exists(ast);
+    assert.lengthOf(ast.instructions(), 1);
+
+    let inst = ast.instruction(0);
+    let op = inst.operand(2);
+    assert.equal(op.name(), "main");
+    assert.equal(op.value(), "main");
+  });
+
+  describe("numerics", () => {
+    describe("integers", () => {
+      it("parses an opcode with an integer", () => {
+        let input = "OpSource GLSL 440";
+
+        let l = new Lexer(input);
+        let p = new Parser(grammar, l);
+
+        let ast = p.parse();
+        assert.exists(ast);
+        assert.lengthOf(ast.instructions(), 1);
+
+        let inst = ast.instruction(0);
+        let op0 = inst.operand(0);
+        assert.equal(op0.name(), "GLSL");
+        assert.equal(op0.type(), "ValueEnum");
+        assert.equal(op0.value(), 2);
+
+        let op1 = inst.operand(1);
+        assert.equal(op1.name(), "440");
+        assert.equal(op1.value(), 440);
+      });
+
+      it("parses an opcode with a hex integer", () => {
+        let input = "OpSource GLSL 0x440";
+
+        let l = new Lexer(input);
+        let p = new Parser(grammar, l);
+
+        let ast = p.parse();
+        assert.exists(ast);
+        assert.lengthOf(ast.instructions(), 1);
+
+        let inst = ast.instruction(0);
+        let op0 = inst.operand(0);
+        assert.equal(op0.name(), "GLSL");
+        assert.equal(op0.type(), "ValueEnum");
+        assert.equal(op0.value(), 2);
+
+        let op1 = inst.operand(1);
+        assert.equal(op1.name(), "1088");
+        assert.equal(op1.value(), 0x440);
+      });
+
+      it.skip("parses immediate integers", () => {
+        // TODO(dsinclair): Support or skip?
+      });
+    });
+
+    describe("floats", () => {
+      it("parses floats", () => {
+        let input = `%float = OpTypeFloat 32
+                     %float1 = OpConstant %float 0.400000006`;
+
+        let l = new Lexer(input);
+        let p = new Parser(grammar, l);
+
+        let ast = p.parse();
+        assert.exists(ast, p.error);
+        assert.lengthOf(ast.instructions(), 2);
+
+        let inst = ast.instruction(1);
+        let op2 = inst.operand(2);
+        assert.equal(op2.value(), 0.400000006);
+      });
+
+      // TODO(dsinclair): Make hex encoded floats parse ...
+      it.skip("parses hex floats", () => {
+        let input = `%float = OpTypeFloat 32
+                     %nfloat = OpConstant %float -0.4p+2
+                     %pfloat = OpConstant %float 0.4p-2
+                     %inf = OpConstant %float32 0x1p+128
+                     %neginf = OpConstant %float32 -0x1p+128
+                     %aNaN = OpConstant %float32 0x1.8p+128
+                     %moreNaN = OpConstant %float32 -0x1.0002p+128`;
+
+        let results = [-40.0, .004, 0x00000, 0x00000, 0x7fc00000, 0xff800100];
+        let l = new Lexer(input);
+        let p = new Parser(grammar, l);
+
+        let ast = p.parse();
+        assert.exists(ast, p.error);
+        assert.lengthOf(ast.instructions(), 7);
+
+        for (const idx in results) {
+          let inst = ast.instruction(idx);
+          let op2 = inst.operand(2);
+          assert.equal(op2.value(), results[idx]);
+        }
+      });
+
+      it("parses a float that looks like an int", () => {
+        let input = `%float = OpTypeFloat 32
+                     %float1 = OpConstant %float 1`;
+
+        let l = new Lexer(input);
+        let p = new Parser(grammar, l);
+
+        let ast = p.parse();
+        assert.exists(ast, p.error);
+        assert.lengthOf(ast.instructions(), 2);
+
+        let inst = ast.instruction(1);
+        let op2 = inst.operand(2);
+        assert.equal(op2.value(), 1);
+        assert.equal(op2.type(), "float");
+      });
+    });
+  });
+
+  describe("enums", () => {
+    it("parses enum values", () => {
+      let input = `%1 = OpTypeFloat 32
+  %30 = OpImageSampleExplicitLod %1 %20 %18 Grad|ConstOffset %22 %24 %29`;
+
+      let vals = [{val: 1, name: "1"},
+        {val: 30, name: "30"},
+        {val: 20, name: "20"},
+        {val: 18, name: "18"},
+        {val: 12, name: "Grad|ConstOffset"}];
+
+      let l = new Lexer(input);
+      let p = new Parser(grammar, l);
+
+      let ast = p.parse();
+      assert.exists(ast, p.error);
+      assert.lengthOf(ast.instructions(), 2);
+
+      let inst = ast.instruction(1);
+      for (let idx in vals) {
+        let op = inst.operand(idx);
+        assert.equal(op.name(), vals[idx].name);
+        assert.equal(op.value(), vals[idx].val);
+      }
+
+      // BitEnum
+      let params = inst.operand(4).params();
+      assert.lengthOf(params, 3);
+      assert.equal(params[0].name(), "22");
+      assert.equal(params[0].value(), 22);
+      assert.equal(params[1].name(), "24");
+      assert.equal(params[1].value(), 24);
+      assert.equal(params[2].name(), "29");
+      assert.equal(params[2].value(), 29);
+    });
+
+    it("parses enumerants with parameters", () => {
+      let input ="OpExecutionMode %main LocalSize 2 3 4";
+
+      let l = new Lexer(input);
+      let p = new Parser(grammar, l);
+
+      let ast = p.parse();
+      assert.exists(ast, p.error);
+      assert.lengthOf(ast.instructions(), 1);
+
+      let inst = ast.instruction(0);
+      assert.equal(inst.name(), "OpExecutionMode");
+      assert.lengthOf(inst.operands(), 2);
+      assert.equal(inst.operand(0).name(), "main");
+      assert.equal(inst.operand(1).name(), "LocalSize");
+
+      let params = inst.operand(1).params();
+      assert.lengthOf(params, 3);
+      assert.equal(params[0].name(), "2");
+      assert.equal(params[1].name(), "3");
+      assert.equal(params[2].name(), "4");
+    });
+  });
+
+  it("parses result into second operand if needed", () => {
+    let input = `%int = OpTypeInt 32 1
+                 %int_3 = OpConstant %int 3`;
+    let l = new Lexer(input);
+    let p = new Parser(grammar, l);
+
+    let ast = p.parse();
+    assert.exists(ast);
+    assert.lengthOf(ast.instructions(), 2);
+
+    let inst = ast.instruction(1);
+    assert.equal(inst.name(), "OpConstant");
+    assert.equal(inst.opcode(), 43);
+    assert.lengthOf(inst.operands(), 3);
+
+    let op0 = inst.operand(0);
+    assert.equal(op0.name(), "int");
+    assert.equal(op0.value(), 1);
+
+    let op1 = inst.operand(1);
+    assert.equal(op1.name(), "int_3");
+    assert.equal(op1.value(), 2);
+
+    let op2 = inst.operand(2);
+    assert.equal(op2.name(), "3");
+    assert.equal(op2.value(), 3);
+  });
+
+  describe("quantifiers", () => {
+    describe("?", () => {
+      it("skips if missing", () => {
+        let input = `OpImageWrite %1 %2 %3
+OpKill`;
+        let l = new Lexer(input);
+        let p = new Parser(grammar, l);
+
+        let ast = p.parse();
+        assert.exists(ast);
+        assert.lengthOf(ast.instructions(), 2);
+
+        let inst = ast.instruction(0);
+        assert.equal(inst.name(), "OpImageWrite");
+        assert.lengthOf(inst.operands(), 3);
+      });
+
+      it("skips if missing at EOF", () => {
+        let input = "OpImageWrite %1 %2 %3";
+        let l = new Lexer(input);
+        let p = new Parser(grammar, l);
+
+        let ast = p.parse();
+        assert.exists(ast);
+        assert.lengthOf(ast.instructions(), 1);
+
+        let inst = ast.instruction(0);
+        assert.equal(inst.name(), "OpImageWrite");
+        assert.lengthOf(inst.operands(), 3);
+      });
+
+      it("extracts if available", () => {
+        let input = `OpImageWrite %1 %2 %3 ConstOffset %2
+OpKill`;
+        let l = new Lexer(input);
+        let p = new Parser(grammar, l);
+
+        let ast = p.parse();
+        assert.exists(ast);
+        assert.lengthOf(ast.instructions(), 2);
+
+        let inst = ast.instruction(0);
+        assert.equal(inst.name(), "OpImageWrite");
+        assert.lengthOf(inst.operands(), 4);
+        assert.equal(inst.operand(3).name(), "ConstOffset");
+      });
+    });
+
+    describe("*", () => {
+      it("skips if missing", () => {
+        let input = `OpEntryPoint Fragment %main "main"
+OpKill`;
+
+        let l = new Lexer(input);
+        let p = new Parser(grammar, l);
+
+        let ast = p.parse();
+        assert.exists(ast);
+        assert.lengthOf(ast.instructions(), 2);
+
+        let inst = ast.instruction(0);
+        assert.equal(inst.name(), "OpEntryPoint");
+        assert.lengthOf(inst.operands(), 3);
+        assert.equal(inst.operand(2).name(), "main");
+      });
+
+      it("extracts one if available", () => {
+        let input = `OpEntryPoint Fragment %main "main" %2
+OpKill`;
+
+        let l = new Lexer(input);
+        let p = new Parser(grammar, l);
+
+        let ast = p.parse();
+        assert.exists(ast);
+        assert.lengthOf(ast.instructions(), 2);
+
+        let inst = ast.instruction(0);
+        assert.equal(inst.name(), "OpEntryPoint");
+        assert.lengthOf(inst.operands(), 4);
+        assert.equal(inst.operand(3).name(), "2");
+      });
+
+      it("extracts multiple if available", () => {
+        let input = `OpEntryPoint Fragment %main "main" %2 %3 %4 %5
+OpKill`;
+
+        let l = new Lexer(input);
+        let p = new Parser(grammar, l);
+
+        let ast = p.parse();
+        assert.exists(ast);
+        assert.lengthOf(ast.instructions(), 2);
+
+        let inst = ast.instruction(0);
+        assert.equal(inst.name(), "OpEntryPoint");
+        assert.lengthOf(inst.operands(), 7);
+        assert.equal(inst.operand(3).name(), "2");
+        assert.equal(inst.operand(4).name(), "3");
+        assert.equal(inst.operand(5).name(), "4");
+        assert.equal(inst.operand(6).name(), "5");
+      });
+    });
+  });
+
+  describe("extended instructions", () => {
+    it("errors on non-glsl extensions", () => {
+      let input = "%1 = OpExtInstImport \"OpenCL.std.100\"";
+
+      let l = new Lexer(input);
+      let p = new Parser(grammar, l);
+
+      assert.isUndefined(p.parse());
+    });
+
+    it("handles extended instructions", () => {
+      let input = `%1 = OpExtInstImport "GLSL.std.450"
+  %44 = OpExtInst %7 %1 Sqrt %43`;
+
+      let l = new Lexer(input);
+      let p = new Parser(grammar, l);
+
+      let ast = p.parse();
+      assert.exists(ast, p.error);
+      assert.lengthOf(ast.instructions(), 2);
+
+      let inst = ast.instruction(1);
+      assert.lengthOf(inst.operands(), 5);
+      assert.equal(inst.operand(3).value(), 31);
+      assert.equal(inst.operand(3).name(), "Sqrt");
+      assert.equal(inst.operand(4).value(), 43);
+      assert.equal(inst.operand(4).name(), "43");
+    });
+  });
+
+  it.skip("handles spec constant ops", () => {
+    // let input = "%sum = OpSpecConstantOp %i32 IAdd %a %b";
+  });
+
+  it("handles OpCopyMemory", () => {
+    let input = "OpCopyMemory %1 %2 " +
+                "Volatile|Nontemporal|MakePointerVisible %3 " +
+                "Aligned|MakePointerAvailable|NonPrivatePointer 16 %4";
+
+    let l = new Lexer(input);
+    let p = new Parser(grammar, l);
+
+    let ast = p.parse();
+    assert.exists(ast, p.error);
+    assert.lengthOf(ast.instructions(), 1);
+
+    let inst = ast.instruction(0);
+    assert.lengthOf(inst.operands(), 4);
+    assert.equal(inst.operand(0).value(), 1);
+    assert.equal(inst.operand(1).value(), 2);
+
+    assert.equal(inst.operand(2).name(),
+        "Volatile|Nontemporal|MakePointerVisible");
+    assert.equal(inst.operand(2).value(), 21);
+    assert.lengthOf(inst.operand(2).params(), 1);
+    assert.equal(inst.operand(2).params()[0].value(), 3);
+
+    assert.equal(inst.operand(3).name(),
+        "Aligned|MakePointerAvailable|NonPrivatePointer");
+    assert.equal(inst.operand(3).value(), 42);
+    assert.lengthOf(inst.operand(3).params(), 2);
+    assert.equal(inst.operand(3).params()[0].value(), 16);
+    assert.equal(inst.operand(3).params()[1].value(), 4);
+  });
+});
diff --git a/tools/sva/src/spirv.data.js b/tools/sva/src/spirv.data.js
new file mode 100644
index 0000000..ba969d8
--- /dev/null
+++ b/tools/sva/src/spirv.data.js
@@ -0,0 +1,4567 @@
+/*Copyright (c) 2014-2016 The Khronos Group Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and/or associated documentation files (the "Materials"),
+to deal in the Materials without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Materials, and to permit persons to whom the
+Materials are furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Materials.
+
+MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS
+STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND
+HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ 
+
+THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS
+IN THE MATERIALS.*/
+
+// THIS FILE IS GENERATED WITH tools/process_grammar.rb
+
+export default {
+  "magic": "0x07230203",
+  "version": [
+    1,
+    5
+  ],
+  "instructions": {
+    "OpNop": {
+      "opcode": 0,
+      "operands": [
+
+      ]
+    },
+    "OpUndef": {
+      "opcode": 1,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        }
+      ]
+    },
+    "OpSourceContinued": {
+      "opcode": 2,
+      "operands": [
+        {
+          "kind": "LiteralString"
+        }
+      ]
+    },
+    "OpSource": {
+      "opcode": 3,
+      "operands": [
+        {
+          "kind": "SourceLanguage"
+        },
+        {
+          "kind": "LiteralInteger"
+        },
+        {
+          "kind": "IdRef",
+          "quantifier": "?"
+        },
+        {
+          "kind": "LiteralString",
+          "quantifier": "?"
+        }
+      ]
+    },
+    "OpSourceExtension": {
+      "opcode": 4,
+      "operands": [
+        {
+          "kind": "LiteralString"
+        }
+      ]
+    },
+    "OpName": {
+      "opcode": 5,
+      "operands": [
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "LiteralString"
+        }
+      ]
+    },
+    "OpMemberName": {
+      "opcode": 6,
+      "operands": [
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "LiteralInteger"
+        },
+        {
+          "kind": "LiteralString"
+        }
+      ]
+    },
+    "OpString": {
+      "opcode": 7,
+      "operands": [
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "LiteralString"
+        }
+      ]
+    },
+    "OpLine": {
+      "opcode": 8,
+      "operands": [
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "LiteralInteger"
+        },
+        {
+          "kind": "LiteralInteger"
+        }
+      ]
+    },
+    "OpExtension": {
+      "opcode": 10,
+      "operands": [
+        {
+          "kind": "LiteralString"
+        }
+      ]
+    },
+    "OpExtInstImport": {
+      "opcode": 11,
+      "operands": [
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "LiteralString"
+        }
+      ]
+    },
+    "OpExtInst": {
+      "opcode": 12,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "LiteralExtInstInteger"
+        },
+        {
+          "kind": "IdRef",
+          "quantifier": "*"
+        }
+      ]
+    },
+    "OpMemoryModel": {
+      "opcode": 14,
+      "operands": [
+        {
+          "kind": "AddressingModel"
+        },
+        {
+          "kind": "MemoryModel"
+        }
+      ]
+    },
+    "OpEntryPoint": {
+      "opcode": 15,
+      "operands": [
+        {
+          "kind": "ExecutionModel"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "LiteralString"
+        },
+        {
+          "kind": "IdRef",
+          "quantifier": "*"
+        }
+      ]
+    },
+    "OpExecutionMode": {
+      "opcode": 16,
+      "operands": [
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "ExecutionMode"
+        }
+      ]
+    },
+    "OpCapability": {
+      "opcode": 17,
+      "operands": [
+        {
+          "kind": "Capability"
+        }
+      ]
+    },
+    "OpTypeVoid": {
+      "opcode": 19,
+      "operands": [
+        {
+          "kind": "IdResult"
+        }
+      ]
+    },
+    "OpTypeBool": {
+      "opcode": 20,
+      "operands": [
+        {
+          "kind": "IdResult"
+        }
+      ]
+    },
+    "OpTypeInt": {
+      "opcode": 21,
+      "operands": [
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "LiteralInteger"
+        },
+        {
+          "kind": "LiteralInteger"
+        }
+      ]
+    },
+    "OpTypeFloat": {
+      "opcode": 22,
+      "operands": [
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "LiteralInteger"
+        }
+      ]
+    },
+    "OpTypeVector": {
+      "opcode": 23,
+      "operands": [
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "LiteralInteger"
+        }
+      ]
+    },
+    "OpTypeMatrix": {
+      "opcode": 24,
+      "operands": [
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "LiteralInteger"
+        }
+      ]
+    },
+    "OpTypeImage": {
+      "opcode": 25,
+      "operands": [
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "Dim"
+        },
+        {
+          "kind": "LiteralInteger"
+        },
+        {
+          "kind": "LiteralInteger"
+        },
+        {
+          "kind": "LiteralInteger"
+        },
+        {
+          "kind": "LiteralInteger"
+        },
+        {
+          "kind": "ImageFormat"
+        },
+        {
+          "kind": "AccessQualifier",
+          "quantifier": "?"
+        }
+      ]
+    },
+    "OpTypeSampler": {
+      "opcode": 26,
+      "operands": [
+        {
+          "kind": "IdResult"
+        }
+      ]
+    },
+    "OpTypeSampledImage": {
+      "opcode": 27,
+      "operands": [
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpTypeArray": {
+      "opcode": 28,
+      "operands": [
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpTypeRuntimeArray": {
+      "opcode": 29,
+      "operands": [
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpTypeStruct": {
+      "opcode": 30,
+      "operands": [
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef",
+          "quantifier": "*"
+        }
+      ]
+    },
+    "OpTypePointer": {
+      "opcode": 32,
+      "operands": [
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "StorageClass"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpTypeFunction": {
+      "opcode": 33,
+      "operands": [
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef",
+          "quantifier": "*"
+        }
+      ]
+    },
+    "OpConstantTrue": {
+      "opcode": 41,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        }
+      ]
+    },
+    "OpConstantFalse": {
+      "opcode": 42,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        }
+      ]
+    },
+    "OpConstant": {
+      "opcode": 43,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "LiteralContextDependentNumber"
+        }
+      ]
+    },
+    "OpConstantComposite": {
+      "opcode": 44,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef",
+          "quantifier": "*"
+        }
+      ]
+    },
+    "OpConstantNull": {
+      "opcode": 46,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        }
+      ]
+    },
+    "OpSpecConstantTrue": {
+      "opcode": 48,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        }
+      ]
+    },
+    "OpSpecConstantFalse": {
+      "opcode": 49,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        }
+      ]
+    },
+    "OpSpecConstant": {
+      "opcode": 50,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "LiteralContextDependentNumber"
+        }
+      ]
+    },
+    "OpSpecConstantComposite": {
+      "opcode": 51,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef",
+          "quantifier": "*"
+        }
+      ]
+    },
+    "OpSpecConstantOp": {
+      "opcode": 52,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "LiteralSpecConstantOpInteger"
+        }
+      ]
+    },
+    "OpFunction": {
+      "opcode": 54,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "FunctionControl"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpFunctionParameter": {
+      "opcode": 55,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        }
+      ]
+    },
+    "OpFunctionEnd": {
+      "opcode": 56,
+      "operands": [
+
+      ]
+    },
+    "OpFunctionCall": {
+      "opcode": 57,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef",
+          "quantifier": "*"
+        }
+      ]
+    },
+    "OpVariable": {
+      "opcode": 59,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "StorageClass"
+        },
+        {
+          "kind": "IdRef",
+          "quantifier": "?"
+        }
+      ]
+    },
+    "OpImageTexelPointer": {
+      "opcode": 60,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpLoad": {
+      "opcode": 61,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "MemoryAccess",
+          "quantifier": "?"
+        }
+      ]
+    },
+    "OpStore": {
+      "opcode": 62,
+      "operands": [
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "MemoryAccess",
+          "quantifier": "?"
+        }
+      ]
+    },
+    "OpCopyMemory": {
+      "opcode": 63,
+      "operands": [
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "MemoryAccess",
+          "quantifier": "?"
+        },
+        {
+          "kind": "MemoryAccess",
+          "quantifier": "?"
+        }
+      ]
+    },
+    "OpAccessChain": {
+      "opcode": 65,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef",
+          "quantifier": "*"
+        }
+      ]
+    },
+    "OpInBoundsAccessChain": {
+      "opcode": 66,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef",
+          "quantifier": "*"
+        }
+      ]
+    },
+    "OpArrayLength": {
+      "opcode": 68,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "LiteralInteger"
+        }
+      ]
+    },
+    "OpDecorate": {
+      "opcode": 71,
+      "operands": [
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "Decoration"
+        }
+      ]
+    },
+    "OpMemberDecorate": {
+      "opcode": 72,
+      "operands": [
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "LiteralInteger"
+        },
+        {
+          "kind": "Decoration"
+        }
+      ]
+    },
+    "OpDecorationGroup": {
+      "opcode": 73,
+      "operands": [
+        {
+          "kind": "IdResult"
+        }
+      ]
+    },
+    "OpGroupDecorate": {
+      "opcode": 74,
+      "operands": [
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef",
+          "quantifier": "*"
+        }
+      ]
+    },
+    "OpGroupMemberDecorate": {
+      "opcode": 75,
+      "operands": [
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "PairIdRefLiteralInteger",
+          "quantifier": "*"
+        }
+      ]
+    },
+    "OpVectorExtractDynamic": {
+      "opcode": 77,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpVectorInsertDynamic": {
+      "opcode": 78,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpVectorShuffle": {
+      "opcode": 79,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "LiteralInteger",
+          "quantifier": "*"
+        }
+      ]
+    },
+    "OpCompositeConstruct": {
+      "opcode": 80,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef",
+          "quantifier": "*"
+        }
+      ]
+    },
+    "OpCompositeExtract": {
+      "opcode": 81,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "LiteralInteger",
+          "quantifier": "*"
+        }
+      ]
+    },
+    "OpCompositeInsert": {
+      "opcode": 82,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "LiteralInteger",
+          "quantifier": "*"
+        }
+      ]
+    },
+    "OpCopyObject": {
+      "opcode": 83,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpTranspose": {
+      "opcode": 84,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpSampledImage": {
+      "opcode": 86,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpImageSampleImplicitLod": {
+      "opcode": 87,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "ImageOperands",
+          "quantifier": "?"
+        }
+      ]
+    },
+    "OpImageSampleExplicitLod": {
+      "opcode": 88,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "ImageOperands"
+        }
+      ]
+    },
+    "OpImageSampleDrefImplicitLod": {
+      "opcode": 89,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "ImageOperands",
+          "quantifier": "?"
+        }
+      ]
+    },
+    "OpImageSampleDrefExplicitLod": {
+      "opcode": 90,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "ImageOperands"
+        }
+      ]
+    },
+    "OpImageSampleProjImplicitLod": {
+      "opcode": 91,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "ImageOperands",
+          "quantifier": "?"
+        }
+      ]
+    },
+    "OpImageSampleProjExplicitLod": {
+      "opcode": 92,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "ImageOperands"
+        }
+      ]
+    },
+    "OpImageSampleProjDrefImplicitLod": {
+      "opcode": 93,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "ImageOperands",
+          "quantifier": "?"
+        }
+      ]
+    },
+    "OpImageSampleProjDrefExplicitLod": {
+      "opcode": 94,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "ImageOperands"
+        }
+      ]
+    },
+    "OpImageFetch": {
+      "opcode": 95,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "ImageOperands",
+          "quantifier": "?"
+        }
+      ]
+    },
+    "OpImageGather": {
+      "opcode": 96,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "ImageOperands",
+          "quantifier": "?"
+        }
+      ]
+    },
+    "OpImageDrefGather": {
+      "opcode": 97,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "ImageOperands",
+          "quantifier": "?"
+        }
+      ]
+    },
+    "OpImageRead": {
+      "opcode": 98,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "ImageOperands",
+          "quantifier": "?"
+        }
+      ]
+    },
+    "OpImageWrite": {
+      "opcode": 99,
+      "operands": [
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "ImageOperands",
+          "quantifier": "?"
+        }
+      ]
+    },
+    "OpImage": {
+      "opcode": 100,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpImageQuerySizeLod": {
+      "opcode": 103,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpImageQuerySize": {
+      "opcode": 104,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpImageQueryLod": {
+      "opcode": 105,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpImageQueryLevels": {
+      "opcode": 106,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpImageQuerySamples": {
+      "opcode": 107,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpConvertFToU": {
+      "opcode": 109,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpConvertFToS": {
+      "opcode": 110,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpConvertSToF": {
+      "opcode": 111,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpConvertUToF": {
+      "opcode": 112,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpUConvert": {
+      "opcode": 113,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpSConvert": {
+      "opcode": 114,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpFConvert": {
+      "opcode": 115,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpQuantizeToF16": {
+      "opcode": 116,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpBitcast": {
+      "opcode": 124,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpSNegate": {
+      "opcode": 126,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpFNegate": {
+      "opcode": 127,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpIAdd": {
+      "opcode": 128,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpFAdd": {
+      "opcode": 129,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpISub": {
+      "opcode": 130,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpFSub": {
+      "opcode": 131,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpIMul": {
+      "opcode": 132,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpFMul": {
+      "opcode": 133,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpUDiv": {
+      "opcode": 134,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpSDiv": {
+      "opcode": 135,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpFDiv": {
+      "opcode": 136,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpUMod": {
+      "opcode": 137,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpSRem": {
+      "opcode": 138,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpSMod": {
+      "opcode": 139,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpFRem": {
+      "opcode": 140,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpFMod": {
+      "opcode": 141,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpVectorTimesScalar": {
+      "opcode": 142,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpMatrixTimesScalar": {
+      "opcode": 143,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpVectorTimesMatrix": {
+      "opcode": 144,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpMatrixTimesVector": {
+      "opcode": 145,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpMatrixTimesMatrix": {
+      "opcode": 146,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpOuterProduct": {
+      "opcode": 147,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpDot": {
+      "opcode": 148,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpIAddCarry": {
+      "opcode": 149,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpISubBorrow": {
+      "opcode": 150,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpUMulExtended": {
+      "opcode": 151,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpSMulExtended": {
+      "opcode": 152,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpAny": {
+      "opcode": 154,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpAll": {
+      "opcode": 155,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpIsNan": {
+      "opcode": 156,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpIsInf": {
+      "opcode": 157,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpLogicalEqual": {
+      "opcode": 164,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpLogicalNotEqual": {
+      "opcode": 165,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpLogicalOr": {
+      "opcode": 166,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpLogicalAnd": {
+      "opcode": 167,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpLogicalNot": {
+      "opcode": 168,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpSelect": {
+      "opcode": 169,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpIEqual": {
+      "opcode": 170,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpINotEqual": {
+      "opcode": 171,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpUGreaterThan": {
+      "opcode": 172,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpSGreaterThan": {
+      "opcode": 173,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpUGreaterThanEqual": {
+      "opcode": 174,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpSGreaterThanEqual": {
+      "opcode": 175,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpULessThan": {
+      "opcode": 176,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpSLessThan": {
+      "opcode": 177,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpULessThanEqual": {
+      "opcode": 178,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpSLessThanEqual": {
+      "opcode": 179,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpFOrdEqual": {
+      "opcode": 180,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpFUnordEqual": {
+      "opcode": 181,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpFOrdNotEqual": {
+      "opcode": 182,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpFUnordNotEqual": {
+      "opcode": 183,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpFOrdLessThan": {
+      "opcode": 184,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpFUnordLessThan": {
+      "opcode": 185,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpFOrdGreaterThan": {
+      "opcode": 186,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpFUnordGreaterThan": {
+      "opcode": 187,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpFOrdLessThanEqual": {
+      "opcode": 188,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpFUnordLessThanEqual": {
+      "opcode": 189,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpFOrdGreaterThanEqual": {
+      "opcode": 190,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpFUnordGreaterThanEqual": {
+      "opcode": 191,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpShiftRightLogical": {
+      "opcode": 194,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpShiftRightArithmetic": {
+      "opcode": 195,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpShiftLeftLogical": {
+      "opcode": 196,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpBitwiseOr": {
+      "opcode": 197,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpBitwiseXor": {
+      "opcode": 198,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpBitwiseAnd": {
+      "opcode": 199,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpNot": {
+      "opcode": 200,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpBitFieldInsert": {
+      "opcode": 201,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpBitFieldSExtract": {
+      "opcode": 202,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpBitFieldUExtract": {
+      "opcode": 203,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpBitReverse": {
+      "opcode": 204,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpBitCount": {
+      "opcode": 205,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpDPdx": {
+      "opcode": 207,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpDPdy": {
+      "opcode": 208,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpFwidth": {
+      "opcode": 209,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpDPdxFine": {
+      "opcode": 210,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpDPdyFine": {
+      "opcode": 211,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpFwidthFine": {
+      "opcode": 212,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpDPdxCoarse": {
+      "opcode": 213,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpDPdyCoarse": {
+      "opcode": 214,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpFwidthCoarse": {
+      "opcode": 215,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpControlBarrier": {
+      "opcode": 224,
+      "operands": [
+        {
+          "kind": "IdScope"
+        },
+        {
+          "kind": "IdScope"
+        },
+        {
+          "kind": "IdMemorySemantics"
+        }
+      ]
+    },
+    "OpMemoryBarrier": {
+      "opcode": 225,
+      "operands": [
+        {
+          "kind": "IdScope"
+        },
+        {
+          "kind": "IdMemorySemantics"
+        }
+      ]
+    },
+    "OpAtomicLoad": {
+      "opcode": 227,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdScope"
+        },
+        {
+          "kind": "IdMemorySemantics"
+        }
+      ]
+    },
+    "OpAtomicStore": {
+      "opcode": 228,
+      "operands": [
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdScope"
+        },
+        {
+          "kind": "IdMemorySemantics"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpAtomicExchange": {
+      "opcode": 229,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdScope"
+        },
+        {
+          "kind": "IdMemorySemantics"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpAtomicCompareExchange": {
+      "opcode": 230,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdScope"
+        },
+        {
+          "kind": "IdMemorySemantics"
+        },
+        {
+          "kind": "IdMemorySemantics"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpAtomicIIncrement": {
+      "opcode": 232,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdScope"
+        },
+        {
+          "kind": "IdMemorySemantics"
+        }
+      ]
+    },
+    "OpAtomicIDecrement": {
+      "opcode": 233,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdScope"
+        },
+        {
+          "kind": "IdMemorySemantics"
+        }
+      ]
+    },
+    "OpAtomicIAdd": {
+      "opcode": 234,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdScope"
+        },
+        {
+          "kind": "IdMemorySemantics"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpAtomicISub": {
+      "opcode": 235,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdScope"
+        },
+        {
+          "kind": "IdMemorySemantics"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpAtomicSMin": {
+      "opcode": 236,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdScope"
+        },
+        {
+          "kind": "IdMemorySemantics"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpAtomicUMin": {
+      "opcode": 237,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdScope"
+        },
+        {
+          "kind": "IdMemorySemantics"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpAtomicSMax": {
+      "opcode": 238,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdScope"
+        },
+        {
+          "kind": "IdMemorySemantics"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpAtomicUMax": {
+      "opcode": 239,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdScope"
+        },
+        {
+          "kind": "IdMemorySemantics"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpAtomicAnd": {
+      "opcode": 240,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdScope"
+        },
+        {
+          "kind": "IdMemorySemantics"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpAtomicOr": {
+      "opcode": 241,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdScope"
+        },
+        {
+          "kind": "IdMemorySemantics"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpAtomicXor": {
+      "opcode": 242,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdScope"
+        },
+        {
+          "kind": "IdMemorySemantics"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpPhi": {
+      "opcode": 245,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "PairIdRefIdRef",
+          "quantifier": "*"
+        }
+      ]
+    },
+    "OpLoopMerge": {
+      "opcode": 246,
+      "operands": [
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "LoopControl"
+        }
+      ]
+    },
+    "OpSelectionMerge": {
+      "opcode": 247,
+      "operands": [
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "SelectionControl"
+        }
+      ]
+    },
+    "OpLabel": {
+      "opcode": 248,
+      "operands": [
+        {
+          "kind": "IdResult"
+        }
+      ]
+    },
+    "OpBranch": {
+      "opcode": 249,
+      "operands": [
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpBranchConditional": {
+      "opcode": 250,
+      "operands": [
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "LiteralInteger",
+          "quantifier": "*"
+        }
+      ]
+    },
+    "OpSwitch": {
+      "opcode": 251,
+      "operands": [
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "PairLiteralIntegerIdRef",
+          "quantifier": "*"
+        }
+      ]
+    },
+    "OpKill": {
+      "opcode": 252,
+      "operands": [
+
+      ]
+    },
+    "OpReturn": {
+      "opcode": 253,
+      "operands": [
+
+      ]
+    },
+    "OpReturnValue": {
+      "opcode": 254,
+      "operands": [
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpUnreachable": {
+      "opcode": 255,
+      "operands": [
+
+      ]
+    },
+    "OpNoLine": {
+      "opcode": 317,
+      "operands": [
+
+      ]
+    },
+    "OpModuleProcessed": {
+      "opcode": 330,
+      "operands": [
+        {
+          "kind": "LiteralString"
+        }
+      ]
+    },
+    "OpExecutionModeId": {
+      "opcode": 331,
+      "operands": [
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "ExecutionMode"
+        }
+      ]
+    },
+    "OpDecorateId": {
+      "opcode": 332,
+      "operands": [
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "Decoration"
+        }
+      ]
+    },
+    "OpCopyLogical": {
+      "opcode": 400,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpPtrEqual": {
+      "opcode": 401,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpPtrNotEqual": {
+      "opcode": 402,
+      "operands": [
+        {
+          "kind": "IdResultType"
+        },
+        {
+          "kind": "IdResult"
+        },
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "IdRef"
+        }
+      ]
+    },
+    "OpDecorateString": {
+      "opcode": 5632,
+      "operands": [
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "Decoration"
+        }
+      ]
+    },
+    "OpDecorateStringGOOGLE": {
+      "opcode": 5632,
+      "operands": [
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "Decoration"
+        }
+      ]
+    },
+    "OpMemberDecorateString": {
+      "opcode": 5633,
+      "operands": [
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "LiteralInteger"
+        },
+        {
+          "kind": "Decoration"
+        }
+      ]
+    },
+    "OpMemberDecorateStringGOOGLE": {
+      "opcode": 5633,
+      "operands": [
+        {
+          "kind": "IdRef"
+        },
+        {
+          "kind": "LiteralInteger"
+        },
+        {
+          "kind": "Decoration"
+        }
+      ]
+    }
+  },
+  "operand_kinds": {
+    "ImageOperands": {
+      "type": "BitEnum",
+      "values": {
+        "None": {
+          "value": 0
+        },
+        "Bias": {
+          "value": 1,
+          "params": [
+            "IdRef"
+          ]
+        },
+        "Lod": {
+          "value": 2,
+          "params": [
+            "IdRef"
+          ]
+        },
+        "Grad": {
+          "value": 4,
+          "params": [
+            "IdRef",
+            "IdRef"
+          ]
+        },
+        "ConstOffset": {
+          "value": 8,
+          "params": [
+            "IdRef"
+          ]
+        },
+        "Sample": {
+          "value": 64,
+          "params": [
+            "IdRef"
+          ]
+        },
+        "MakeTexelAvailable": {
+          "value": 256,
+          "params": [
+            "IdScope"
+          ]
+        },
+        "MakeTexelAvailableKHR": {
+          "value": 256,
+          "params": [
+            "IdScope"
+          ]
+        },
+        "MakeTexelVisible": {
+          "value": 512,
+          "params": [
+            "IdScope"
+          ]
+        },
+        "MakeTexelVisibleKHR": {
+          "value": 512,
+          "params": [
+            "IdScope"
+          ]
+        },
+        "NonPrivateTexel": {
+          "value": 1024
+        },
+        "NonPrivateTexelKHR": {
+          "value": 1024
+        },
+        "VolatileTexel": {
+          "value": 2048
+        },
+        "VolatileTexelKHR": {
+          "value": 2048
+        },
+        "SignExtend": {
+          "value": 4096
+        },
+        "ZeroExtend": {
+          "value": 8192
+        }
+      }
+    },
+    "FPFastMathMode": {
+      "type": "BitEnum",
+      "values": {
+        "None": {
+          "value": 0
+        }
+      }
+    },
+    "SelectionControl": {
+      "type": "BitEnum",
+      "values": {
+        "None": {
+          "value": 0
+        },
+        "Flatten": {
+          "value": 1
+        },
+        "DontFlatten": {
+          "value": 2
+        }
+      }
+    },
+    "LoopControl": {
+      "type": "BitEnum",
+      "values": {
+        "None": {
+          "value": 0
+        },
+        "Unroll": {
+          "value": 1
+        },
+        "DontUnroll": {
+          "value": 2
+        },
+        "DependencyInfinite": {
+          "value": 4
+        },
+        "DependencyLength": {
+          "value": 8,
+          "params": [
+            "LiteralInteger"
+          ]
+        },
+        "MinIterations": {
+          "value": 16,
+          "params": [
+            "LiteralInteger"
+          ]
+        },
+        "MaxIterations": {
+          "value": 32,
+          "params": [
+            "LiteralInteger"
+          ]
+        },
+        "IterationMultiple": {
+          "value": 64,
+          "params": [
+            "LiteralInteger"
+          ]
+        },
+        "PeelCount": {
+          "value": 128,
+          "params": [
+            "LiteralInteger"
+          ]
+        },
+        "PartialCount": {
+          "value": 256,
+          "params": [
+            "LiteralInteger"
+          ]
+        }
+      }
+    },
+    "FunctionControl": {
+      "type": "BitEnum",
+      "values": {
+        "None": {
+          "value": 0
+        },
+        "Inline": {
+          "value": 1
+        },
+        "DontInline": {
+          "value": 2
+        },
+        "Pure": {
+          "value": 4
+        },
+        "Const": {
+          "value": 8
+        }
+      }
+    },
+    "MemorySemantics": {
+      "type": "BitEnum",
+      "values": {
+        "Relaxed": {
+          "value": 0
+        },
+        "None": {
+          "value": 0
+        },
+        "Acquire": {
+          "value": 2
+        },
+        "Release": {
+          "value": 4
+        },
+        "AcquireRelease": {
+          "value": 8
+        },
+        "SequentiallyConsistent": {
+          "value": 16
+        },
+        "UniformMemory": {
+          "value": 64
+        },
+        "SubgroupMemory": {
+          "value": 128
+        },
+        "WorkgroupMemory": {
+          "value": 256
+        },
+        "CrossWorkgroupMemory": {
+          "value": 512
+        },
+        "ImageMemory": {
+          "value": 2048
+        },
+        "OutputMemory": {
+          "value": 4096
+        },
+        "OutputMemoryKHR": {
+          "value": 4096
+        },
+        "MakeAvailable": {
+          "value": 8192
+        },
+        "MakeAvailableKHR": {
+          "value": 8192
+        },
+        "MakeVisible": {
+          "value": 16384
+        },
+        "MakeVisibleKHR": {
+          "value": 16384
+        },
+        "Volatile": {
+          "value": 32768
+        }
+      }
+    },
+    "MemoryAccess": {
+      "type": "BitEnum",
+      "values": {
+        "None": {
+          "value": 0
+        },
+        "Volatile": {
+          "value": 1
+        },
+        "Aligned": {
+          "value": 2,
+          "params": [
+            "LiteralInteger"
+          ]
+        },
+        "Nontemporal": {
+          "value": 4
+        },
+        "MakePointerAvailable": {
+          "value": 8,
+          "params": [
+            "IdScope"
+          ]
+        },
+        "MakePointerAvailableKHR": {
+          "value": 8,
+          "params": [
+            "IdScope"
+          ]
+        },
+        "MakePointerVisible": {
+          "value": 16,
+          "params": [
+            "IdScope"
+          ]
+        },
+        "MakePointerVisibleKHR": {
+          "value": 16,
+          "params": [
+            "IdScope"
+          ]
+        },
+        "NonPrivatePointer": {
+          "value": 32
+        },
+        "NonPrivatePointerKHR": {
+          "value": 32
+        }
+      }
+    },
+    "KernelProfilingInfo": {
+      "type": "BitEnum",
+      "values": {
+        "None": {
+          "value": 0
+        }
+      }
+    },
+    "SourceLanguage": {
+      "type": "ValueEnum",
+      "values": {
+        "Unknown": {
+          "value": 0
+        },
+        "ESSL": {
+          "value": 1
+        },
+        "GLSL": {
+          "value": 2
+        },
+        "OpenCL_C": {
+          "value": 3
+        },
+        "OpenCL_CPP": {
+          "value": 4
+        },
+        "HLSL": {
+          "value": 5
+        }
+      }
+    },
+    "ExecutionModel": {
+      "type": "ValueEnum",
+      "values": {
+        "Vertex": {
+          "value": 0
+        },
+        "Fragment": {
+          "value": 4
+        },
+        "GLCompute": {
+          "value": 5
+        }
+      }
+    },
+    "AddressingModel": {
+      "type": "ValueEnum",
+      "values": {
+        "Logical": {
+          "value": 0
+        }
+      }
+    },
+    "MemoryModel": {
+      "type": "ValueEnum",
+      "values": {
+        "Simple": {
+          "value": 0
+        },
+        "GLSL450": {
+          "value": 1
+        },
+        "Vulkan": {
+          "value": 3
+        },
+        "VulkanKHR": {
+          "value": 3
+        }
+      }
+    },
+    "ExecutionMode": {
+      "type": "ValueEnum",
+      "values": {
+        "PixelCenterInteger": {
+          "value": 6
+        },
+        "OriginUpperLeft": {
+          "value": 7
+        },
+        "OriginLowerLeft": {
+          "value": 8
+        },
+        "EarlyFragmentTests": {
+          "value": 9
+        },
+        "DepthReplacing": {
+          "value": 12
+        },
+        "DepthGreater": {
+          "value": 14
+        },
+        "DepthLess": {
+          "value": 15
+        },
+        "DepthUnchanged": {
+          "value": 16
+        },
+        "LocalSize": {
+          "value": 17,
+          "params": [
+            "LiteralInteger",
+            "LiteralInteger",
+            "LiteralInteger"
+          ]
+        },
+        "LocalSizeId": {
+          "value": 38,
+          "params": [
+            "IdRef",
+            "IdRef",
+            "IdRef"
+          ]
+        }
+      }
+    },
+    "StorageClass": {
+      "type": "ValueEnum",
+      "values": {
+        "UniformConstant": {
+          "value": 0
+        },
+        "Input": {
+          "value": 1
+        },
+        "Uniform": {
+          "value": 2
+        },
+        "Output": {
+          "value": 3
+        },
+        "Workgroup": {
+          "value": 4
+        },
+        "CrossWorkgroup": {
+          "value": 5
+        },
+        "Private": {
+          "value": 6
+        },
+        "Function": {
+          "value": 7
+        },
+        "PushConstant": {
+          "value": 9
+        },
+        "Image": {
+          "value": 11
+        },
+        "StorageBuffer": {
+          "value": 12
+        }
+      }
+    },
+    "Dim": {
+      "type": "ValueEnum",
+      "values": {
+        "1D": {
+          "value": 0
+        },
+        "2D": {
+          "value": 1
+        },
+        "3D": {
+          "value": 2
+        },
+        "Cube": {
+          "value": 3
+        }
+      }
+    },
+    "ImageFormat": {
+      "type": "ValueEnum",
+      "values": {
+        "Unknown": {
+          "value": 0
+        },
+        "Rgba32f": {
+          "value": 1
+        },
+        "Rgba16f": {
+          "value": 2
+        },
+        "R32f": {
+          "value": 3
+        },
+        "Rgba8": {
+          "value": 4
+        },
+        "Rgba8Snorm": {
+          "value": 5
+        },
+        "Rgba32i": {
+          "value": 21
+        },
+        "Rgba16i": {
+          "value": 22
+        },
+        "Rgba8i": {
+          "value": 23
+        },
+        "R32i": {
+          "value": 24
+        },
+        "Rgba32ui": {
+          "value": 30
+        },
+        "Rgba16ui": {
+          "value": 31
+        },
+        "Rgba8ui": {
+          "value": 32
+        },
+        "R32ui": {
+          "value": 33
+        }
+      }
+    },
+    "FPRoundingMode": {
+      "type": "ValueEnum",
+      "values": {
+        "RTE": {
+          "value": 0
+        },
+        "RTZ": {
+          "value": 1
+        },
+        "RTP": {
+          "value": 2
+        },
+        "RTN": {
+          "value": 3
+        }
+      }
+    },
+    "Decoration": {
+      "type": "ValueEnum",
+      "values": {
+        "RelaxedPrecision": {
+          "value": 0
+        },
+        "SpecId": {
+          "value": 1,
+          "params": [
+            "LiteralInteger"
+          ]
+        },
+        "Block": {
+          "value": 2
+        },
+        "BufferBlock": {
+          "value": 3
+        },
+        "RowMajor": {
+          "value": 4
+        },
+        "ColMajor": {
+          "value": 5
+        },
+        "ArrayStride": {
+          "value": 6,
+          "params": [
+            "LiteralInteger"
+          ]
+        },
+        "MatrixStride": {
+          "value": 7,
+          "params": [
+            "LiteralInteger"
+          ]
+        },
+        "GLSLShared": {
+          "value": 8
+        },
+        "GLSLPacked": {
+          "value": 9
+        },
+        "BuiltIn": {
+          "value": 11,
+          "params": [
+            "BuiltIn"
+          ]
+        },
+        "NoPerspective": {
+          "value": 13
+        },
+        "Flat": {
+          "value": 14
+        },
+        "Centroid": {
+          "value": 16
+        },
+        "Invariant": {
+          "value": 18
+        },
+        "Restrict": {
+          "value": 19
+        },
+        "Aliased": {
+          "value": 20
+        },
+        "Volatile": {
+          "value": 21
+        },
+        "Coherent": {
+          "value": 23
+        },
+        "NonWritable": {
+          "value": 24
+        },
+        "NonReadable": {
+          "value": 25
+        },
+        "Uniform": {
+          "value": 26
+        },
+        "UniformId": {
+          "value": 27,
+          "params": [
+            "IdScope"
+          ]
+        },
+        "Location": {
+          "value": 30,
+          "params": [
+            "LiteralInteger"
+          ]
+        },
+        "Component": {
+          "value": 31,
+          "params": [
+            "LiteralInteger"
+          ]
+        },
+        "Index": {
+          "value": 32,
+          "params": [
+            "LiteralInteger"
+          ]
+        },
+        "Binding": {
+          "value": 33,
+          "params": [
+            "LiteralInteger"
+          ]
+        },
+        "DescriptorSet": {
+          "value": 34,
+          "params": [
+            "LiteralInteger"
+          ]
+        },
+        "Offset": {
+          "value": 35,
+          "params": [
+            "LiteralInteger"
+          ]
+        },
+        "FPRoundingMode": {
+          "value": 39,
+          "params": [
+            "FPRoundingMode"
+          ]
+        },
+        "NoContraction": {
+          "value": 42
+        },
+        "NoSignedWrap": {
+          "value": 4469
+        },
+        "NoUnsignedWrap": {
+          "value": 4470
+        },
+        "ExplicitInterpAMD": {
+          "value": 4999
+        },
+        "CounterBuffer": {
+          "value": 5634,
+          "params": [
+            "IdRef"
+          ]
+        },
+        "HlslCounterBufferGOOGLE": {
+          "value": 5634,
+          "params": [
+            "IdRef"
+          ]
+        },
+        "UserSemantic": {
+          "value": 5635,
+          "params": [
+            "LiteralString"
+          ]
+        },
+        "HlslSemanticGOOGLE": {
+          "value": 5635,
+          "params": [
+            "LiteralString"
+          ]
+        },
+        "UserTypeGOOGLE": {
+          "value": 5636,
+          "params": [
+            "LiteralString"
+          ]
+        }
+      }
+    },
+    "BuiltIn": {
+      "type": "ValueEnum",
+      "values": {
+        "Position": {
+          "value": 0
+        },
+        "PointSize": {
+          "value": 1
+        },
+        "VertexId": {
+          "value": 5
+        },
+        "InstanceId": {
+          "value": 6
+        },
+        "FragCoord": {
+          "value": 15
+        },
+        "PointCoord": {
+          "value": 16
+        },
+        "FrontFacing": {
+          "value": 17
+        },
+        "SampleMask": {
+          "value": 20
+        },
+        "FragDepth": {
+          "value": 22
+        },
+        "HelperInvocation": {
+          "value": 23
+        },
+        "NumWorkgroups": {
+          "value": 24
+        },
+        "WorkgroupSize": {
+          "value": 25
+        },
+        "WorkgroupId": {
+          "value": 26
+        },
+        "LocalInvocationId": {
+          "value": 27
+        },
+        "GlobalInvocationId": {
+          "value": 28
+        },
+        "LocalInvocationIndex": {
+          "value": 29
+        },
+        "VertexIndex": {
+          "value": 42
+        },
+        "InstanceIndex": {
+          "value": 43
+        },
+        "BaryCoordNoPerspAMD": {
+          "value": 4992
+        },
+        "BaryCoordNoPerspCentroidAMD": {
+          "value": 4993
+        },
+        "BaryCoordNoPerspSampleAMD": {
+          "value": 4994
+        },
+        "BaryCoordSmoothAMD": {
+          "value": 4995
+        },
+        "BaryCoordSmoothCentroidAMD": {
+          "value": 4996
+        },
+        "BaryCoordSmoothSampleAMD": {
+          "value": 4997
+        },
+        "BaryCoordPullModelAMD": {
+          "value": 4998
+        }
+      }
+    },
+    "Scope": {
+      "type": "ValueEnum",
+      "values": {
+        "CrossDevice": {
+          "value": 0
+        },
+        "Device": {
+          "value": 1
+        },
+        "Workgroup": {
+          "value": 2
+        },
+        "Subgroup": {
+          "value": 3
+        },
+        "Invocation": {
+          "value": 4
+        },
+        "QueueFamily": {
+          "value": 5
+        },
+        "QueueFamilyKHR": {
+          "value": 5
+        }
+      }
+    },
+    "Capability": {
+      "type": "ValueEnum",
+      "values": {
+        "Matrix": {
+          "value": 0
+        },
+        "Shader": {
+          "value": 1
+        },
+        "Geometry": {
+          "value": 2
+        },
+        "Tessellation": {
+          "value": 3
+        },
+        "Addresses": {
+          "value": 4
+        },
+        "Linkage": {
+          "value": 5
+        },
+        "Kernel": {
+          "value": 6
+        },
+        "Float16": {
+          "value": 9
+        },
+        "Float64": {
+          "value": 10
+        },
+        "Int64": {
+          "value": 11
+        },
+        "Groups": {
+          "value": 18
+        },
+        "AtomicStorage": {
+          "value": 21
+        },
+        "Int16": {
+          "value": 22
+        },
+        "ImageGatherExtended": {
+          "value": 25
+        },
+        "StorageImageMultisample": {
+          "value": 27
+        },
+        "UniformBufferArrayDynamicIndexing": {
+          "value": 28
+        },
+        "SampledImageArrayDynamicIndexing": {
+          "value": 29
+        },
+        "StorageBufferArrayDynamicIndexing": {
+          "value": 30
+        },
+        "StorageImageArrayDynamicIndexing": {
+          "value": 31
+        },
+        "ClipDistance": {
+          "value": 32
+        },
+        "CullDistance": {
+          "value": 33
+        },
+        "SampleRateShading": {
+          "value": 35
+        },
+        "SampledRect": {
+          "value": 37
+        },
+        "Int8": {
+          "value": 39
+        },
+        "InputAttachment": {
+          "value": 40
+        },
+        "SparseResidency": {
+          "value": 41
+        },
+        "MinLod": {
+          "value": 42
+        },
+        "Sampled1D": {
+          "value": 43
+        },
+        "Image1D": {
+          "value": 44
+        },
+        "SampledCubeArray": {
+          "value": 45
+        },
+        "SampledBuffer": {
+          "value": 46
+        },
+        "ImageMSArray": {
+          "value": 48
+        },
+        "StorageImageExtendedFormats": {
+          "value": 49
+        },
+        "ImageQuery": {
+          "value": 50
+        },
+        "DerivativeControl": {
+          "value": 51
+        },
+        "InterpolationFunction": {
+          "value": 52
+        },
+        "TransformFeedback": {
+          "value": 53
+        },
+        "StorageImageReadWithoutFormat": {
+          "value": 55
+        },
+        "StorageImageWriteWithoutFormat": {
+          "value": 56
+        },
+        "GroupNonUniform": {
+          "value": 61
+        },
+        "ShaderLayer": {
+          "value": 69
+        },
+        "ShaderViewportIndex": {
+          "value": 70
+        },
+        "SubgroupBallotKHR": {
+          "value": 4423
+        },
+        "DrawParameters": {
+          "value": 4427
+        },
+        "SubgroupVoteKHR": {
+          "value": 4431
+        },
+        "StorageBuffer16BitAccess": {
+          "value": 4433
+        },
+        "StorageUniformBufferBlock16": {
+          "value": 4433
+        },
+        "StoragePushConstant16": {
+          "value": 4435
+        },
+        "StorageInputOutput16": {
+          "value": 4436
+        },
+        "DeviceGroup": {
+          "value": 4437
+        },
+        "MultiView": {
+          "value": 4439
+        },
+        "VariablePointersStorageBuffer": {
+          "value": 4441
+        },
+        "AtomicStorageOps": {
+          "value": 4445
+        },
+        "SampleMaskPostDepthCoverage": {
+          "value": 4447
+        },
+        "StorageBuffer8BitAccess": {
+          "value": 4448
+        },
+        "StoragePushConstant8": {
+          "value": 4450
+        },
+        "DenormPreserve": {
+          "value": 4464
+        },
+        "DenormFlushToZero": {
+          "value": 4465
+        },
+        "SignedZeroInfNanPreserve": {
+          "value": 4466
+        },
+        "RoundingModeRTE": {
+          "value": 4467
+        },
+        "RoundingModeRTZ": {
+          "value": 4468
+        },
+        "Float16ImageAMD": {
+          "value": 5008
+        },
+        "ImageGatherBiasLodAMD": {
+          "value": 5009
+        },
+        "FragmentMaskAMD": {
+          "value": 5010
+        },
+        "StencilExportEXT": {
+          "value": 5013
+        },
+        "ImageReadWriteLodAMD": {
+          "value": 5015
+        },
+        "ShaderClockKHR": {
+          "value": 5055
+        },
+        "FragmentFullyCoveredEXT": {
+          "value": 5265
+        },
+        "MeshShadingNV": {
+          "value": 5266
+        },
+        "ImageFootprintNV": {
+          "value": 5282
+        },
+        "FragmentBarycentricNV": {
+          "value": 5284
+        },
+        "ComputeDerivativeGroupQuadsNV": {
+          "value": 5288
+        },
+        "FragmentDensityEXT": {
+          "value": 5291
+        },
+        "ShadingRateNV": {
+          "value": 5291
+        },
+        "GroupNonUniformPartitionedNV": {
+          "value": 5297
+        },
+        "ShaderNonUniform": {
+          "value": 5301
+        },
+        "ShaderNonUniformEXT": {
+          "value": 5301
+        },
+        "RuntimeDescriptorArray": {
+          "value": 5302
+        },
+        "RuntimeDescriptorArrayEXT": {
+          "value": 5302
+        },
+        "RayTracingNV": {
+          "value": 5340
+        },
+        "VulkanMemoryModel": {
+          "value": 5345
+        },
+        "VulkanMemoryModelKHR": {
+          "value": 5345
+        },
+        "VulkanMemoryModelDeviceScope": {
+          "value": 5346
+        },
+        "VulkanMemoryModelDeviceScopeKHR": {
+          "value": 5346
+        },
+        "PhysicalStorageBufferAddresses": {
+          "value": 5347
+        },
+        "PhysicalStorageBufferAddressesEXT": {
+          "value": 5347
+        },
+        "ComputeDerivativeGroupLinearNV": {
+          "value": 5350
+        },
+        "CooperativeMatrixNV": {
+          "value": 5357
+        },
+        "FragmentShaderSampleInterlockEXT": {
+          "value": 5363
+        },
+        "FragmentShaderShadingRateInterlockEXT": {
+          "value": 5372
+        },
+        "ShaderSMBuiltinsNV": {
+          "value": 5373
+        },
+        "FragmentShaderPixelInterlockEXT": {
+          "value": 5378
+        },
+        "DemoteToHelperInvocationEXT": {
+          "value": 5379
+        },
+        "SubgroupShuffleINTEL": {
+          "value": 5568
+        },
+        "SubgroupBufferBlockIOINTEL": {
+          "value": 5569
+        },
+        "SubgroupImageBlockIOINTEL": {
+          "value": 5570
+        },
+        "SubgroupImageMediaBlockIOINTEL": {
+          "value": 5579
+        },
+        "IntegerFunctions2INTEL": {
+          "value": 5584
+        },
+        "SubgroupAvcMotionEstimationINTEL": {
+          "value": 5696
+        },
+        "SubgroupAvcMotionEstimationIntraINTEL": {
+          "value": 5697
+        },
+        "SubgroupAvcMotionEstimationChromaINTEL": {
+          "value": 5698
+        }
+      }
+    }
+  },
+  "ext": {
+    "Round": 1,
+    "RoundEven": 2,
+    "Trunc": 3,
+    "FAbs": 4,
+    "SAbs": 5,
+    "FSign": 6,
+    "SSign": 7,
+    "Floor": 8,
+    "Ceil": 9,
+    "Fract": 10,
+    "Radians": 11,
+    "Degrees": 12,
+    "Sin": 13,
+    "Cos": 14,
+    "Tan": 15,
+    "Asin": 16,
+    "Acos": 17,
+    "Atan": 18,
+    "Sinh": 19,
+    "Cosh": 20,
+    "Tanh": 21,
+    "Asinh": 22,
+    "Acosh": 23,
+    "Atanh": 24,
+    "Atan2": 25,
+    "Pow": 26,
+    "Exp": 27,
+    "Log": 28,
+    "Exp2": 29,
+    "Log2": 30,
+    "Sqrt": 31,
+    "InverseSqrt": 32,
+    "Determinant": 33,
+    "MatrixInverse": 34,
+    "Modf": 35,
+    "ModfStruct": 36,
+    "FMin": 37,
+    "UMin": 38,
+    "SMin": 39,
+    "FMax": 40,
+    "UMax": 41,
+    "SMax": 42,
+    "FClamp": 43,
+    "UClamp": 44,
+    "SClamp": 45,
+    "FMix": 46,
+    "IMix": 47,
+    "Step": 48,
+    "SmoothStep": 49,
+    "Fma": 50,
+    "Frexp": 51,
+    "FrexpStruct": 52,
+    "Ldexp": 53,
+    "PackSnorm4x8": 54,
+    "PackUnorm4x8": 55,
+    "PackSnorm2x16": 56,
+    "PackUnorm2x16": 57,
+    "PackHalf2x16": 58,
+    "PackDouble2x32": 59,
+    "UnpackSnorm2x16": 60,
+    "UnpackUnorm2x16": 61,
+    "UnpackHalf2x16": 62,
+    "UnpackSnorm4x8": 63,
+    "UnpackUnorm4x8": 64,
+    "UnpackDouble2x32": 65,
+    "Length": 66,
+    "Distance": 67,
+    "Cross": 68,
+    "Normalize": 69,
+    "FaceForward": 70,
+    "Reflect": 71,
+    "Refract": 72,
+    "FindILsb": 73,
+    "FindSMsb": 74,
+    "FindUMsb": 75,
+    "InterpolateAtCentroid": 76,
+    "InterpolateAtSample": 77,
+    "InterpolateAtOffset": 78,
+    "NMin": 79,
+    "NMax": 80,
+    "NClamp": 81
+  }
+}
diff --git a/tools/sva/src/sva.js b/tools/sva/src/sva.js
new file mode 100644
index 0000000..c76ed29
--- /dev/null
+++ b/tools/sva/src/sva.js
@@ -0,0 +1,40 @@
+// Copyright 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import Parser from "./parser.js";
+import Lexer from "./lexer.js";
+import Assembler from "./assembler.js";
+
+import grammar from "./spirv.data.js";
+
+export default class SVA {
+  /**
+   * Attempts to convert |input| SPIR-V assembly into SPIR-V binary.
+   *
+   * @param {String} the input string containing the assembly
+   * @return {Uint32Array|string} returns a Uint32Array containing the binary
+   *                             SPIR-V or a string on error.
+   */
+  static assemble(input) {
+    let l = new Lexer(input);
+    let p = new Parser(grammar, l);
+
+    let ast = p.parse();
+    if (ast === undefined)
+      return p.error;
+
+    let a = new Assembler(ast);
+    return a.assemble();
+  }
+}
diff --git a/tools/sva/src/token.js b/tools/sva/src/token.js
new file mode 100644
index 0000000..3813191
--- /dev/null
+++ b/tools/sva/src/token.js
@@ -0,0 +1,55 @@
+// Copyright 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+const TokenType = {
+  kEOF: "end of file",
+  kError: "error",
+
+  kIdentifier: "identifier",
+
+  kIntegerLiteral: "integer_literal",
+  kFloatLiteral: "float_literal",
+  kStringLiteral: "string_literal",
+  kResultId: "result_id",
+
+  kOp: "Op",
+  kEqual: "=",
+  kPipe: "|",
+};
+
+class Token {
+  /**
+   * @param {TokenType} type The type of token
+   * @param {Integer} line The line number this token was on
+   * @param {Any} data Data attached to the token
+   * @param {Integer} bits If the type is a float or integer the bit width
+   */
+  constructor(type, line, data) {
+    this.type_ = type;
+    this.line_ = line;
+    this.data_ = data;
+    this.bits_ = 0;
+  }
+
+  get type() { return this.type_; }
+  get line() { return this.line_; }
+
+  get data() { return this.data_; }
+  set data(val) { this.data_ = val; }
+
+  get bits() { return this.bits_; }
+  set bits(val) { this.bits_ = val; }
+}
+
+export {Token, TokenType};
diff --git a/tools/sva/tests/empty_main.spv_asm b/tools/sva/tests/empty_main.spv_asm
new file mode 100644
index 0000000..ad6e64b
--- /dev/null
+++ b/tools/sva/tests/empty_main.spv_asm
@@ -0,0 +1,18 @@
+; SPIR-V
+; Version: 1.0
+; Generator: Khronos Glslang Reference Front End; 7
+; Bound: 6
+; Schema: 0
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpSource GLSL 440
+               OpName %main "main"
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
diff --git a/tools/sva/tests/index.html b/tools/sva/tests/index.html
new file mode 100644
index 0000000..dd02847
--- /dev/null
+++ b/tools/sva/tests/index.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<html>
+<head>
+  <meta charset='utf-8'>
+</head>
+<body>
+  <pre id='code'><code></code></pre>
+
+  <script type="module">
+    let c = document.getElementById('code');
+
+    import SVA from "/build/sva.js";
+
+    let assembly = SVA.assemble("OpCapability Shader");
+    if (typeof assembly === "string") {
+      c.innerText = assembly;
+    } else {
+      c.innerText = Array.from(assembly)
+        .map(b => b.toString(16).padStart(8, "0")).join(" ");
+    }
+  </script>
+</body>
+</html>
diff --git a/tools/sva/tests/simple.spv_asm b/tools/sva/tests/simple.spv_asm
new file mode 100644
index 0000000..b4b3f67
--- /dev/null
+++ b/tools/sva/tests/simple.spv_asm
@@ -0,0 +1,30 @@
+; SPIR-V
+; Version: 1.0
+; Generator: Khronos Glslang Reference Front End; 7
+; Bound: 14
+; Schema: 0
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main" %gl_FragColor
+               OpExecutionMode %main OriginUpperLeft
+               OpSource GLSL 330
+               OpName %main "main"
+               OpName %gl_FragColor "gl_FragColor"
+               OpDecorate %gl_FragColor Location 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%gl_FragColor = OpVariable %_ptr_Output_v4float Output
+%float_0_400000006 = OpConstant %float 0.400000006
+%float_0_800000012 = OpConstant %float 0.800000012
+    %float_1 = OpConstant %float 1
+         %13 = OpConstantComposite %v4float %float_0_400000006 %float_0_400000006 %float_0_800000012 %float_1
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpStore %gl_FragColor %13
+               OpReturn
+               OpFunctionEnd
+
diff --git a/tools/sva/tools/process_grammar.rb b/tools/sva/tools/process_grammar.rb
new file mode 100755
index 0000000..1bbff68
--- /dev/null
+++ b/tools/sva/tools/process_grammar.rb
@@ -0,0 +1,119 @@
+#!/usr/bin/env ruby
+
+# Copyright 2019 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require 'json'
+
+GRAMMAR = "../../external/spirv-headers/include/spirv/unified1/spirv.core.grammar.json"
+GLSL = "../../external/spirv-headers/include/spirv/unified1/extinst.glsl.std.450.grammar.json"
+
+CAPABILITIES = %w(
+      Matrix
+      Shader
+      Sampled1D
+      Image1D
+      DerivativeControl
+      ImageQuery
+      VulkanMemoryModel
+)
+
+g = JSON.parse(File.open(GRAMMAR).read)
+magic = g['magic_number']
+vers = [g['major_version'], g['minor_version']]
+instructions = {}
+
+g['instructions'].each do |inst|
+  if (inst.has_key?('capabilities'))
+    skip = true
+    inst['capabilities'].each do |cap|
+      if CAPABILITIES.include?(cap)
+        skip = false
+        break
+      end
+    end
+    next if skip
+  end
+
+  op = {
+    opcode: inst['opcode'],
+    operands: []
+  }
+
+  if !inst['operands'].nil?
+    inst['operands'].each do |operand|
+      operand.delete('name')
+      op[:operands] << operand
+    end
+  end
+
+  instructions[inst['opname']] = op
+end
+
+operand_kinds = {}
+g['operand_kinds'].each do |op_kind|
+  next if op_kind['category'] !~ /Enum/
+
+  kind = {
+    type: op_kind['category'],
+    values: {}
+  }
+
+  op_kind['enumerants'].each do |enum|
+    if (enum.has_key?('capabilities'))
+      skip = true
+      enum['capabilities'].each do |cap|
+        if CAPABILITIES.include?(cap)
+          skip = false
+          break
+        end
+      end
+      next if skip
+    end
+
+    v = if op_kind['category'] == 'BitEnum'
+      enum['value'].to_i(16)
+    else
+      enum['value'].to_i
+    end
+    params = []
+    if enum.has_key?('parameters')
+      enum['parameters'].each do |param|
+        params << param['kind']
+      end
+    end
+    kind[:values][enum['enumerant']] = {value: v}
+    kind[:values][enum['enumerant']][:params] = params unless params.empty?
+  end
+
+  next if kind[:values].empty?
+  operand_kinds[op_kind['kind']] = kind
+end
+
+# We only support GLSL extensions at the moment.
+ext = {}
+glsl = JSON.parse(File.open(GLSL).read)
+glsl['instructions'].each do |inst|
+  ext[inst['opname']] = inst['opcode']
+end
+
+puts "/*#{g['copyright'].join("\n")}*/"
+puts "\n// THIS FILE IS GENERATED WITH tools/process_grammar.rb\n\n"
+puts "export default " + JSON.pretty_generate({
+  magic: magic,
+  version: vers,
+  instructions: instructions,
+  operand_kinds: operand_kinds,
+  ext: ext
+})
diff --git a/tools/sva/yarn.lock b/tools/sva/yarn.lock
new file mode 100644
index 0000000..be19e7c
--- /dev/null
+++ b/tools/sva/yarn.lock
@@ -0,0 +1,1778 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# 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==
+  dependencies:
+    "@babel/highlight" "^7.0.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==
+  dependencies:
+    chalk "^2.0.0"
+    esutils "^2.0.2"
+    js-tokens "^4.0.0"
+
+"@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==
+
+"@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==
+
+"@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==
+
+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==
+  dependencies:
+    mime-types "~2.1.24"
+    negotiator "0.6.2"
+
+acorn-jsx@^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==
+
+acorn@^7.0.0:
+  version "7.0.0"
+  resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.0.0.tgz#26b8d1cd9a9b700350b71c0905546f64d1284e7a"
+  integrity sha512-PaF/MduxijYYt7unVGRuds1vBC9bFxbNf+VWqhOClfdgy7RlVkQqt610ig1/yxTgsDIfW1cWDel5EBbOy3jdtQ==
+
+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"
+
+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=
+
+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==
+  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"
+
+brace-expansion@^1.1.7:
+  version "1.1.11"
+  resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
+  integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
+  dependencies:
+    balanced-match "^1.0.0"
+    concat-map "0.0.1"
+
+browser-stdout@1.3.1:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60"
+  integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==
+
+bytes@3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048"
+  integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=
+
+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@^5.0.0:
+  version "5.3.1"
+  resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
+  integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
+
+chai@^4.2.0:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/chai/-/chai-4.2.0.tgz#760aa72cf20e3795e84b12877ce0e83737aa29e5"
+  integrity sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==
+  dependencies:
+    assertion-error "^1.1.0"
+    check-error "^1.0.2"
+    deep-eql "^3.0.1"
+    get-func-name "^2.0.0"
+    pathval "^1.1.0"
+    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==
+  dependencies:
+    ansi-styles "^3.2.1"
+    escape-string-regexp "^1.0.5"
+    supports-color "^5.3.0"
+
+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==
+  dependencies:
+    ansi-styles "^3.2.1"
+    escape-string-regexp "^1.0.5"
+    supports-color "^5.3.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==
+
+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=
+
+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=
+  dependencies:
+    restore-cursor "^2.0.0"
+
+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=
+
+clipboardy@1.2.3:
+  version "1.2.3"
+  resolved "https://registry.yarnpkg.com/clipboardy/-/clipboardy-1.2.3.tgz#0526361bf78724c1f20be248d428e365433c07ef"
+  integrity sha512-2WNImOvCRe6r63Gk9pShfkwXsVtKCroMAevIbiae021mS850UkWPbevxsBz3tnvjZIEGvlwaqCPsw+4ulzNgJA==
+  dependencies:
+    arch "^2.1.0"
+    execa "^0.8.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==
+  dependencies:
+    string-width "^2.1.1"
+    strip-ansi "^4.0.0"
+    wrap-ansi "^2.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==
+  dependencies:
+    color-name "1.1.3"
+
+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=
+
+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==
+  dependencies:
+    mime-db ">= 1.40.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==
+  dependencies:
+    accepts "~1.3.5"
+    bytes "3.0.0"
+    compressible "~2.0.14"
+    debug "2.6.9"
+    on-headers "~1.0.1"
+    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=
+
+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=
+
+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=
+  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"
+
+debug@2.6.9:
+  version "2.6.9"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
+  integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
+  dependencies:
+    ms "2.0.0"
+
+debug@3.2.6:
+  version "3.2.6"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
+  integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==
+  dependencies:
+    ms "^2.1.1"
+
+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@^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==
+  dependencies:
+    type-detect "^4.0.0"
+
+deep-extend@^0.6.0:
+  version "0.6.0"
+  resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac"
+  integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==
+
+deep-is@~0.1.3:
+  version "0.1.3"
+  resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
+  integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=
+
+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==
+
+doctrine@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961"
+  integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==
+  dependencies:
+    esutils "^2.0.2"
+
+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==
+
+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==
+  dependencies:
+    once "^1.4.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==
+  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"
+    ajv "^6.10.0"
+    chalk "^2.1.0"
+    cross-spawn "^6.0.5"
+    debug "^4.0.1"
+    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"
+    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"
+    import-fresh "^3.0.0"
+    imurmurhash "^0.1.4"
+    inquirer "^6.4.1"
+    is-glob "^4.0.0"
+    js-yaml "^3.13.1"
+    json-stable-stringify-without-jsonify "^1.0.1"
+    levn "^0.3.0"
+    lodash "^4.17.14"
+    minimatch "^3.0.4"
+    mkdirp "^0.5.1"
+    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"
+    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==
+  dependencies:
+    acorn "^7.0.0"
+    acorn-jsx "^5.0.2"
+    eslint-visitor-keys "^1.1.0"
+
+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==
+  dependencies:
+    estraverse "^4.0.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:
+  version "4.3.0"
+  resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d"
+  integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==
+
+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=
+  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@^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-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=
+
+fast-levenshtein@~2.0.4:
+  version "2.0.6"
+  resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
+  integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=
+
+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=
+  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=
+  dependencies:
+    escape-string-regexp "^1.0.5"
+
+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==
+  dependencies:
+    flat-cache "^2.0.1"
+
+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==
+  dependencies:
+    locate-path "^3.0.0"
+
+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==
+  dependencies:
+    flatted "^2.0.0"
+    rimraf "2.6.3"
+    write "1.0.3"
+
+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==
+  dependencies:
+    is-buffer "~2.0.3"
+
+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==
+
+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=
+
+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==
+
+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:
+  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=
+
+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@^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==
+  dependencies:
+    pump "^3.0.0"
+
+glob-parent@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.0.0.tgz#1dc99f0f39b006d3e92c2c284068382f0c20e954"
+  integrity sha512-Z2RwiujPRGluePM6j699ktJYxmPpJKCfpGA13jz2hmFZC7gKetzrWvg5KN3+OsIFmydGyZ1AVwERCq1w/ZZwRg==
+  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==
+  dependencies:
+    fs.realpath "^1.0.0"
+    inflight "^1.0.4"
+    inherits "2"
+    minimatch "^3.0.4"
+    once "^1.3.0"
+    path-is-absolute "^1.0.0"
+
+glob@^7.1.3:
+  version "7.1.4"
+  resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255"
+  integrity sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==
+  dependencies:
+    fs.realpath "^1.0.0"
+    inflight "^1.0.4"
+    inherits "2"
+    minimatch "^3.0.4"
+    once "^1.3.0"
+    path-is-absolute "^1.0.0"
+
+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==
+  dependencies:
+    function-bind "^1.1.1"
+
+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"
+
+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==
+
+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==
+  dependencies:
+    parent-module "^1.0.0"
+    resolve-from "^4.0.0"
+
+imurmurhash@^0.1.4:
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
+  integrity sha1-khi5srkoojixPcT7a21XbyMUU+o=
+
+inflight@^1.0.4:
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
+  integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=
+  dependencies:
+    once "^1.3.0"
+    wrappy "1"
+
+inherits@2:
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
+  integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
+
+ini@~1.3.0:
+  version "1.3.5"
+  resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
+  integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==
+
+inquirer@^6.4.1:
+  version "6.5.2"
+  resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.5.2.tgz#ad50942375d036d327ff528c08bd5fab089928ca"
+  integrity sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ==
+  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"
+
+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-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=
+
+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@^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==
+  dependencies:
+    is-extglob "^2.1.1"
+
+is-promise@^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=
+
+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=
+  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"
+
+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=
+
+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==
+  dependencies:
+    argparse "^1.0.7"
+    esprima "^4.0.0"
+
+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-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=
+
+lcid@^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"
+
+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.15"
+  resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
+  integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
+
+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.33.0:
+  version "1.33.0"
+  resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db"
+  integrity sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==
+
+mime-types@2.1.18:
+  version "2.1.18"
+  resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8"
+  integrity sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==
+  dependencies:
+    mime-db "~1.33.0"
+
+mime-types@~2.1.24:
+  version "2.1.24"
+  resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.24.tgz#b6f8d0b3e951efb77dedeca194cff6d16f676f81"
+  integrity sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==
+  dependencies:
+    mime-db "1.40.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:
+  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==
+  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=
+
+minimist@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
+  integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=
+
+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=
+  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"
+    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"
+    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"
+
+ms@2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
+  integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
+
+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:
+  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=
+
+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=
+
+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==
+
+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==
+
+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==
+  dependencies:
+    object.getownpropertydescriptors "^2.0.3"
+    semver "^5.7.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:
+  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:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
+  integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
+  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=
+  dependencies:
+    mimic-fn "^1.0.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=
+  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"
+
+os-locale@^3.0.0, os-locale@^3.1.0:
+  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==
+  dependencies:
+    execa "^1.0.0"
+    lcid "^2.0.0"
+    mem "^4.0.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==
+  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==
+
+parent-module@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2"
+  integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==
+  dependencies:
+    callsites "^3.0.0"
+
+path-exists@^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-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=
+
+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=
+
+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-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:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.0.tgz#b942e6d4bde653005ef6b71361def8727d0645e0"
+  integrity sha1-uULm1L3mUwBe9rcTYd74cn0GReA=
+
+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=
+
+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"
+
+punycode@^1.3.2:
+  version "1.4.1"
+  resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
+  integrity sha1-wNWmOycYgArY4esPpSachN1BhF4=
+
+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==
+
+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=
+
+rc@^1.0.1, rc@^1.1.6:
+  version "1.2.8"
+  resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed"
+  integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==
+  dependencies:
+    deep-extend "^0.6.0"
+    ini "~1.3.0"
+    minimist "^1.2.0"
+    strip-json-comments "~2.0.1"
+
+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==
+
+registry-auth-token@3.3.2:
+  version "3.3.2"
+  resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-3.3.2.tgz#851fd49038eecb586911115af845260eec983f20"
+  integrity sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==
+  dependencies:
+    rc "^1.1.6"
+    safe-buffer "^5.0.1"
+
+registry-url@3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-3.1.0.tgz#3d4ef870f73dde1d77f0cf9a381432444e174942"
+  integrity sha1-PU74cPc93h138M+aOBQyRE4XSUI=
+  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=
+
+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==
+
+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"
+
+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==
+  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"
+
+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=
+  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"
+
+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==
+
+"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==
+
+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==
+  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"
+    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==
+  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"
+
+set-blocking@^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=
+  dependencies:
+    shebang-regex "^1.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=
+
+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=
+
+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==
+  dependencies:
+    ansi-styles "^3.2.0"
+    astral-regex "^1.0.0"
+    is-fullwidth-code-point "^2.0.0"
+
+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=
+  dependencies:
+    code-point-at "^1.0.0"
+    is-fullwidth-code-point "^1.0.0"
+    strip-ansi "^3.0.0"
+
+"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==
+  dependencies:
+    is-fullwidth-code-point "^2.0.0"
+    strip-ansi "^4.0.0"
+
+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==
+  dependencies:
+    emoji-regex "^7.0.1"
+    is-fullwidth-code-point "^2.0.0"
+    strip-ansi "^5.1.0"
+
+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"
+
+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-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:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
+  integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo=
+
+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==
+  dependencies:
+    has-flag "^3.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==
+  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"
+
+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=
+
+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==
+  dependencies:
+    os-tmpdir "~1.0.2"
+
+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=
+  dependencies:
+    prelude-ls "~1.1.2"
+
+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==
+  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==
+  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=
+
+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==
+  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==
+  dependencies:
+    string-width "^1.0.2 || 2"
+
+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==
+  dependencies:
+    string-width "^2.1.1"
+
+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=
+  dependencies:
+    string-width "^1.0.1"
+    strip-ansi "^3.0.1"
+
+wrappy@1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
+  integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
+
+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==
+  dependencies:
+    mkdirp "^0.5.1"
+
+"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==
+  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"
+    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"
+
+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"
diff --git a/tools/util/cli_consumer.h b/tools/util/cli_consumer.h
index ca3d91b..64a5e46 100644
--- a/tools/util/cli_consumer.h
+++ b/tools/util/cli_consumer.h
@@ -15,7 +15,7 @@
 #ifndef SOURCE_UTIL_CLI_CONSUMMER_H_
 #define SOURCE_UTIL_CLI_CONSUMMER_H_
 
-#include <include/spirv-tools/libspirv.h>
+#include "include/spirv-tools/libspirv.h"
 
 namespace spvtools {
 namespace utils {
diff --git a/tools/val/val.cpp b/tools/val/val.cpp
index 6a8542d..19b8c77 100644
--- a/tools/val/val.cpp
+++ b/tools/val/val.cpp
@@ -74,7 +74,7 @@
 
 int main(int argc, char** argv) {
   const char* inFile = nullptr;
-  spv_target_env target_env = SPV_ENV_UNIVERSAL_1_4;
+  spv_target_env target_env = SPV_ENV_UNIVERSAL_1_5;
   spvtools::ValidatorOptions options;
   bool continue_processing = true;
   int return_code = 0;
@@ -108,12 +108,13 @@
         printf("%s\n", spvSoftwareVersionDetailsString());
         printf(
             "Targets:\n  %s\n  %s\n  %s\n  %s\n  %s\n  %s\n  %s\n  %s\n  %s\n  "
-            "%s\n",
+            "%s\n  %s\n",
             spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_0),
             spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_1),
             spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_2),
             spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_3),
             spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_4),
+            spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_5),
             spvTargetEnvDescription(SPV_ENV_OPENCL_2_2),
             spvTargetEnvDescription(SPV_ENV_VULKAN_1_0),
             spvTargetEnvDescription(SPV_ENV_VULKAN_1_1),
diff --git a/utils/check_copyright.py b/utils/check_copyright.py
index 5a95e32..cfeef80 100755
--- a/utils/check_copyright.py
+++ b/utils/check_copyright.py
@@ -30,7 +30,8 @@
            'LunarG Inc.',
            'Google Inc.',
            'Google LLC',
-           'Pierre Moreau']
+           'Pierre Moreau',
+           'Samsung Inc']
 CURRENT_YEAR='2019'
 
 YEARS = '(2014-2016|2015-2016|2016|2016-2017|2017|2018|2019)'
diff --git a/utils/generate_grammar_tables.py b/utils/generate_grammar_tables.py
index 13f392a..ed24bd0 100755
--- a/utils/generate_grammar_tables.py
+++ b/utils/generate_grammar_tables.py
@@ -653,6 +653,30 @@
 def precondition_operand_kinds(operand_kinds):
     """For operand kinds that have the same number, make sure they all have the
     same extension list."""
+
+    # Map operand kind and value to list of the union of extensions
+    # for same-valued enumerants.
+    exts = {}
+    for kind_entry in operand_kinds:
+        kind = kind_entry.get('kind')
+        for enum_entry in kind_entry.get('enumerants', []):
+            value = enum_entry.get('value')
+            key = kind + '.' + str(value)
+            if key in exts:
+                exts[key].extend(enum_entry.get('extensions', []))
+            else:
+                exts[key] = enum_entry.get('extensions', [])
+            exts[key] = sorted(set(exts[key]))
+
+    # Now make each entry the same list.
+    for kind_entry in operand_kinds:
+        kind = kind_entry.get('kind')
+        for enum_entry in kind_entry.get('enumerants', []):
+            value = enum_entry.get('value')
+            key = kind + '.' + str(value)
+            if len(exts[key]) > 0:
+                enum_entry['extensions'] = exts[key]
+
     return operand_kinds
 
 
diff --git a/utils/update_build_version.py b/utils/update_build_version.py
index 119bb05..321de74 100755
--- a/utils/update_build_version.py
+++ b/utils/update_build_version.py
@@ -86,7 +86,7 @@
     # Linux.
     pattern = re.compile(r'^(v\d+\.\d+(-dev)?) \d\d\d\d-\d\d-\d\d\s*$')
     changes_file = os.path.join(directory, 'CHANGES')
-    with open(changes_file, mode='rU') as f:
+    with open(changes_file, mode='r') as f:
         for line in f.readlines():
             match = pattern.match(line)
             if match:
@@ -116,10 +116,10 @@
             # 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 enviornment variable SOURCE_DATE_EPOCH
+            # 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.date.fromtimestamp(timestamp).isoformat()
+            formatted = datetime.datetime.utcfromtimestamp(timestamp).isoformat()
             return 'unknown hash, {}'.format(formatted)
 
 
diff --git a/utils/vscode/.gitignore b/utils/vscode/.gitignore
new file mode 100644
index 0000000..e934adf
--- /dev/null
+++ b/utils/vscode/.gitignore
@@ -0,0 +1 @@
+cache/
diff --git a/utils/vscode/README.md b/utils/vscode/README.md
new file mode 100644
index 0000000..afbb246
--- /dev/null
+++ b/utils/vscode/README.md
@@ -0,0 +1,7 @@
+# Visual Studio Code extension for SPIR-V disassembly files
+
+This directory holds a Visual Studio Code extension adding syntax highlighting for SPIR-V disassembly files (.spirv)
+
+## Installing (macOS / Linux)
+
+Simply run `install.sh`
diff --git a/utils/vscode/extension.js b/utils/vscode/extension.js
new file mode 100644
index 0000000..f220172
--- /dev/null
+++ b/utils/vscode/extension.js
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2019 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var path = require('path');
+var vscode = require('vscode');
+var langClient = require('vscode-languageclient');
+
+var LanguageClient = langClient.LanguageClient;
+
+// this method is called when your extension is activated
+// your extension is activated the very first time the command is executed
+function activate(context) {
+	let serverModule = path.join(context.extensionPath, 'langsvr');
+	let debugOptions = {};
+
+	// If the extension is launched in debug mode then the debug server options are used
+	// Otherwise the run options are used
+	let serverOptions = {
+		run: { command: serverModule, transport: langClient.stdio },
+		debug: { command: serverModule, transport: langClient.stdio, options: debugOptions }
+	}
+
+	// Options to control the language client
+	let clientOptions = {
+		documentSelector: ['spirv'],
+		synchronize: {
+			// Synchronize the setting section 'spirv' to the server
+			configurationSection: 'spirv',
+			// Notify the server about file changes to .spvasm files contained in the workspace
+			fileEvents: vscode.workspace.createFileSystemWatcher('**/*.spvasm')
+		}
+	}
+
+	// Create the language client and start the client.
+	let disposable = new LanguageClient('spirv', serverOptions, clientOptions).start();
+
+	// Push the disposable to the context's subscriptions so that the
+	// client can be deactivated on extension deactivation
+	context.subscriptions.push(disposable);
+
+	// Set the language configuration here instead of a language configuration
+	// file to work around https://github.com/microsoft/vscode/issues/42649.
+	vscode.languages.setLanguageConfiguration("spirv", {
+		comments: { "lineComment": ";" },
+		wordPattern: /(-?\d*\.\d\w*)|([^\`\~\!\@\#\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g,
+	});
+}
+exports.activate = activate;
+
+// this method is called when your extension is deactivated
+function deactivate() {
+}
+exports.deactivate = deactivate;
\ No newline at end of file
diff --git a/utils/vscode/install.sh b/utils/vscode/install.sh
new file mode 100755
index 0000000..01fc914
--- /dev/null
+++ b/utils/vscode/install.sh
@@ -0,0 +1,32 @@
+#!/usr/bin/env bash
+# Copyright (c) 2019 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -e # Fail on any error.
+
+EXT_PATH=~/.vscode/extensions/google.spirvls-0.0.1
+ROOT_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
+
+go run ${ROOT_PATH}/src/tools/gen-grammar.go --cache ${ROOT_PATH}/cache --template ${ROOT_PATH}/spirv.json.tmpl --out ${ROOT_PATH}/spirv.json
+go run ${ROOT_PATH}/src/tools/gen-grammar.go --cache ${ROOT_PATH}/cache --template ${ROOT_PATH}/src/schema/schema.go.tmpl --out ${ROOT_PATH}/src/schema/schema.go
+
+mkdir -p ${EXT_PATH}
+cp ${ROOT_PATH}/extension.js ${EXT_PATH}
+cp ${ROOT_PATH}/package.json ${EXT_PATH}
+cp ${ROOT_PATH}/spirv.json ${EXT_PATH}
+
+go build -o ${EXT_PATH}/langsvr ${ROOT_PATH}/src/langsvr.go
+
+cd ${EXT_PATH}
+npm install
diff --git a/utils/vscode/package.json b/utils/vscode/package.json
new file mode 100644
index 0000000..76fb348
--- /dev/null
+++ b/utils/vscode/package.json
@@ -0,0 +1,39 @@
+{

+    "name": "spirvls",

+    "description": "Language support for SPIR-V disassembly files",

+    "author": "Google",

+    "license": "Apache-2.0",

+    "version": "0.0.1",

+    "private": true,

+    "publisher": "Google",

+    "engines": {

+        "vscode": "^0.10.10"

+    },

+    "categories": [

+        "Programming Languages"

+    ],

+    "contributes": {

+        "languages": [

+            {

+                "id": "spirv",

+                "extensions": [

+                    "spvasm"

+                ]

+            }

+        ],

+        "grammars": [

+            {

+                "language": "spirv",

+                "scopeName": "source.spirv",

+                "path": "spirv.json"

+            }

+        ]

+    },

+    "dependencies": {

+        "vscode-languageclient": "~4.3.0"

+    },

+    "activationEvents": [

+        "*"

+    ],

+    "main": "./extension.js"

+}

diff --git a/utils/vscode/spirv.json b/utils/vscode/spirv.json
new file mode 100644
index 0000000..30573d4
--- /dev/null
+++ b/utils/vscode/spirv.json
@@ -0,0 +1,212 @@
+{
+	"scopeName": "source.spirv",
+	"name": "SPIR-V",
+	"comment": "Generated by gen-grammar.go --template=../../spirv.json.tmpl --out=../../spirv.json. Do not modify this file directly.",
+	"patterns": [
+		{ "include": "#BitEnum_ImageOperands" },
+		{ "include": "#BitEnum_FPFastMathMode" },
+		{ "include": "#BitEnum_SelectionControl" },
+		{ "include": "#BitEnum_LoopControl" },
+		{ "include": "#BitEnum_FunctionControl" },
+		{ "include": "#BitEnum_MemorySemantics" },
+		{ "include": "#BitEnum_MemoryAccess" },
+		{ "include": "#BitEnum_KernelProfilingInfo" },
+		{ "include": "#ValueEnum_SourceLanguage" },
+		{ "include": "#ValueEnum_ExecutionModel" },
+		{ "include": "#ValueEnum_AddressingModel" },
+		{ "include": "#ValueEnum_MemoryModel" },
+		{ "include": "#ValueEnum_ExecutionMode" },
+		{ "include": "#ValueEnum_StorageClass" },
+		{ "include": "#ValueEnum_Dim" },
+		{ "include": "#ValueEnum_SamplerAddressingMode" },
+		{ "include": "#ValueEnum_SamplerFilterMode" },
+		{ "include": "#ValueEnum_ImageFormat" },
+		{ "include": "#ValueEnum_ImageChannelOrder" },
+		{ "include": "#ValueEnum_ImageChannelDataType" },
+		{ "include": "#ValueEnum_FPRoundingMode" },
+		{ "include": "#ValueEnum_LinkageType" },
+		{ "include": "#ValueEnum_AccessQualifier" },
+		{ "include": "#ValueEnum_FunctionParameterAttribute" },
+		{ "include": "#ValueEnum_Decoration" },
+		{ "include": "#ValueEnum_BuiltIn" },
+		{ "include": "#ValueEnum_Scope" },
+		{ "include": "#ValueEnum_GroupOperation" },
+		{ "include": "#ValueEnum_KernelEnqueueFlags" },
+		{ "include": "#ValueEnum_Capability" },
+		{ "include": "#opcode" },
+		{ "include": "#identifier" },
+		{ "include": "#number" },
+		{ "include": "#string" },
+		{ "include": "#comment" },
+		{ "include": "#operator" }
+	],
+	"repository": { 
+		"BitEnum_ImageOperands": {
+			"match": "\\b(None|Bias|Lod|Grad|ConstOffset|Offset|ConstOffsets|Sample|MinLod|MakeTexelAvailable|MakeTexelAvailableKHR|MakeTexelVisible|MakeTexelVisibleKHR|NonPrivateTexel|NonPrivateTexelKHR|VolatileTexel|VolatileTexelKHR|SignExtend|ZeroExtend)\\b",
+			"name": "keyword.spirv"
+		},
+		"BitEnum_FPFastMathMode": {
+			"match": "\\b(None|NotNaN|NotInf|NSZ|AllowRecip|Fast)\\b",
+			"name": "keyword.spirv"
+		},
+		"BitEnum_SelectionControl": {
+			"match": "\\b(None|Flatten|DontFlatten)\\b",
+			"name": "keyword.spirv"
+		},
+		"BitEnum_LoopControl": {
+			"match": "\\b(None|Unroll|DontUnroll|DependencyInfinite|DependencyLength|MinIterations|MaxIterations|IterationMultiple|PeelCount|PartialCount)\\b",
+			"name": "keyword.spirv"
+		},
+		"BitEnum_FunctionControl": {
+			"match": "\\b(None|Inline|DontInline|Pure|Const)\\b",
+			"name": "keyword.spirv"
+		},
+		"BitEnum_MemorySemantics": {
+			"match": "\\b(Relaxed|None|Acquire|Release|AcquireRelease|SequentiallyConsistent|UniformMemory|SubgroupMemory|WorkgroupMemory|CrossWorkgroupMemory|AtomicCounterMemory|ImageMemory|OutputMemory|OutputMemoryKHR|MakeAvailable|MakeAvailableKHR|MakeVisible|MakeVisibleKHR|Volatile)\\b",
+			"name": "keyword.spirv"
+		},
+		"BitEnum_MemoryAccess": {
+			"match": "\\b(None|Volatile|Aligned|Nontemporal|MakePointerAvailable|MakePointerAvailableKHR|MakePointerVisible|MakePointerVisibleKHR|NonPrivatePointer|NonPrivatePointerKHR)\\b",
+			"name": "keyword.spirv"
+		},
+		"BitEnum_KernelProfilingInfo": {
+			"match": "\\b(None|CmdExecTime)\\b",
+			"name": "keyword.spirv"
+		},
+		"ValueEnum_SourceLanguage": {
+			"match": "\\b(Unknown|ESSL|GLSL|OpenCL_C|OpenCL_CPP|HLSL)\\b",
+			"name": "keyword.spirv"
+		},
+		"ValueEnum_ExecutionModel": {
+			"match": "\\b(Vertex|TessellationControl|TessellationEvaluation|Geometry|Fragment|GLCompute|Kernel|TaskNV|MeshNV|RayGenerationNV|IntersectionNV|AnyHitNV|ClosestHitNV|MissNV|CallableNV)\\b",
+			"name": "keyword.spirv"
+		},
+		"ValueEnum_AddressingModel": {
+			"match": "\\b(Logical|Physical32|Physical64|PhysicalStorageBuffer64|PhysicalStorageBuffer64EXT)\\b",
+			"name": "keyword.spirv"
+		},
+		"ValueEnum_MemoryModel": {
+			"match": "\\b(Simple|GLSL450|OpenCL|Vulkan|VulkanKHR)\\b",
+			"name": "keyword.spirv"
+		},
+		"ValueEnum_ExecutionMode": {
+			"match": "\\b(Invocations|SpacingEqual|SpacingFractionalEven|SpacingFractionalOdd|VertexOrderCw|VertexOrderCcw|PixelCenterInteger|OriginUpperLeft|OriginLowerLeft|EarlyFragmentTests|PointMode|Xfb|DepthReplacing|DepthGreater|DepthLess|DepthUnchanged|LocalSize|LocalSizeHint|InputPoints|InputLines|InputLinesAdjacency|Triangles|InputTrianglesAdjacency|Quads|Isolines|OutputVertices|OutputPoints|OutputLineStrip|OutputTriangleStrip|VecTypeHint|ContractionOff|Initializer|Finalizer|SubgroupSize|SubgroupsPerWorkgroup|SubgroupsPerWorkgroupId|LocalSizeId|LocalSizeHintId|PostDepthCoverage|DenormPreserve|DenormFlushToZero|SignedZeroInfNanPreserve|RoundingModeRTE|RoundingModeRTZ|StencilRefReplacingEXT|OutputLinesNV|OutputPrimitivesNV|DerivativeGroupQuadsNV|DerivativeGroupLinearNV|OutputTrianglesNV|PixelInterlockOrderedEXT|PixelInterlockUnorderedEXT|SampleInterlockOrderedEXT|SampleInterlockUnorderedEXT|ShadingRateInterlockOrderedEXT|ShadingRateInterlockUnorderedEXT)\\b",
+			"name": "keyword.spirv"
+		},
+		"ValueEnum_StorageClass": {
+			"match": "\\b(UniformConstant|Input|Uniform|Output|Workgroup|CrossWorkgroup|Private|Function|Generic|PushConstant|AtomicCounter|Image|StorageBuffer|CallableDataNV|IncomingCallableDataNV|RayPayloadNV|HitAttributeNV|IncomingRayPayloadNV|ShaderRecordBufferNV|PhysicalStorageBuffer|PhysicalStorageBufferEXT)\\b",
+			"name": "keyword.spirv"
+		},
+		"ValueEnum_Dim": {
+			"match": "\\b(1D|2D|3D|Cube|Rect|Buffer|SubpassData)\\b",
+			"name": "keyword.spirv"
+		},
+		"ValueEnum_SamplerAddressingMode": {
+			"match": "\\b(None|ClampToEdge|Clamp|Repeat|RepeatMirrored)\\b",
+			"name": "keyword.spirv"
+		},
+		"ValueEnum_SamplerFilterMode": {
+			"match": "\\b(Nearest|Linear)\\b",
+			"name": "keyword.spirv"
+		},
+		"ValueEnum_ImageFormat": {
+			"match": "\\b(Unknown|Rgba32f|Rgba16f|R32f|Rgba8|Rgba8Snorm|Rg32f|Rg16f|R11fG11fB10f|R16f|Rgba16|Rgb10A2|Rg16|Rg8|R16|R8|Rgba16Snorm|Rg16Snorm|Rg8Snorm|R16Snorm|R8Snorm|Rgba32i|Rgba16i|Rgba8i|R32i|Rg32i|Rg16i|Rg8i|R16i|R8i|Rgba32ui|Rgba16ui|Rgba8ui|R32ui|Rgb10a2ui|Rg32ui|Rg16ui|Rg8ui|R16ui|R8ui)\\b",
+			"name": "keyword.spirv"
+		},
+		"ValueEnum_ImageChannelOrder": {
+			"match": "\\b(R|A|RG|RA|RGB|RGBA|BGRA|ARGB|Intensity|Luminance|Rx|RGx|RGBx|Depth|DepthStencil|sRGB|sRGBx|sRGBA|sBGRA|ABGR)\\b",
+			"name": "keyword.spirv"
+		},
+		"ValueEnum_ImageChannelDataType": {
+			"match": "\\b(SnormInt8|SnormInt16|UnormInt8|UnormInt16|UnormShort565|UnormShort555|UnormInt101010|SignedInt8|SignedInt16|SignedInt32|UnsignedInt8|UnsignedInt16|UnsignedInt32|HalfFloat|Float|UnormInt24|UnormInt101010_2)\\b",
+			"name": "keyword.spirv"
+		},
+		"ValueEnum_FPRoundingMode": {
+			"match": "\\b(RTE|RTZ|RTP|RTN)\\b",
+			"name": "keyword.spirv"
+		},
+		"ValueEnum_LinkageType": {
+			"match": "\\b(Export|Import)\\b",
+			"name": "keyword.spirv"
+		},
+		"ValueEnum_AccessQualifier": {
+			"match": "\\b(ReadOnly|WriteOnly|ReadWrite)\\b",
+			"name": "keyword.spirv"
+		},
+		"ValueEnum_FunctionParameterAttribute": {
+			"match": "\\b(Zext|Sext|ByVal|Sret|NoAlias|NoCapture|NoWrite|NoReadWrite)\\b",
+			"name": "keyword.spirv"
+		},
+		"ValueEnum_Decoration": {
+			"match": "\\b(RelaxedPrecision|SpecId|Block|BufferBlock|RowMajor|ColMajor|ArrayStride|MatrixStride|GLSLShared|GLSLPacked|CPacked|BuiltIn|NoPerspective|Flat|Patch|Centroid|Sample|Invariant|Restrict|Aliased|Volatile|Constant|Coherent|NonWritable|NonReadable|Uniform|UniformId|SaturatedConversion|Stream|Location|Component|Index|Binding|DescriptorSet|Offset|XfbBuffer|XfbStride|FuncParamAttr|FPRoundingMode|FPFastMathMode|LinkageAttributes|NoContraction|InputAttachmentIndex|Alignment|MaxByteOffset|AlignmentId|MaxByteOffsetId|NoSignedWrap|NoUnsignedWrap|ExplicitInterpAMD|OverrideCoverageNV|PassthroughNV|ViewportRelativeNV|SecondaryViewportRelativeNV|PerPrimitiveNV|PerViewNV|PerTaskNV|PerVertexNV|NonUniform|NonUniformEXT|RestrictPointer|RestrictPointerEXT|AliasedPointer|AliasedPointerEXT|CounterBuffer|HlslCounterBufferGOOGLE|UserSemantic|HlslSemanticGOOGLE|UserTypeGOOGLE)\\b",
+			"name": "keyword.spirv"
+		},
+		"ValueEnum_BuiltIn": {
+			"match": "\\b(Position|PointSize|ClipDistance|CullDistance|VertexId|InstanceId|PrimitiveId|InvocationId|Layer|ViewportIndex|TessLevelOuter|TessLevelInner|TessCoord|PatchVertices|FragCoord|PointCoord|FrontFacing|SampleId|SamplePosition|SampleMask|FragDepth|HelperInvocation|NumWorkgroups|WorkgroupSize|WorkgroupId|LocalInvocationId|GlobalInvocationId|LocalInvocationIndex|WorkDim|GlobalSize|EnqueuedWorkgroupSize|GlobalOffset|GlobalLinearId|SubgroupSize|SubgroupMaxSize|NumSubgroups|NumEnqueuedSubgroups|SubgroupId|SubgroupLocalInvocationId|VertexIndex|InstanceIndex|SubgroupEqMask|SubgroupGeMask|SubgroupGtMask|SubgroupLeMask|SubgroupLtMask|SubgroupEqMaskKHR|SubgroupGeMaskKHR|SubgroupGtMaskKHR|SubgroupLeMaskKHR|SubgroupLtMaskKHR|BaseVertex|BaseInstance|DrawIndex|DeviceIndex|ViewIndex|BaryCoordNoPerspAMD|BaryCoordNoPerspCentroidAMD|BaryCoordNoPerspSampleAMD|BaryCoordSmoothAMD|BaryCoordSmoothCentroidAMD|BaryCoordSmoothSampleAMD|BaryCoordPullModelAMD|FragStencilRefEXT|ViewportMaskNV|SecondaryPositionNV|SecondaryViewportMaskNV|PositionPerViewNV|ViewportMaskPerViewNV|FullyCoveredEXT|TaskCountNV|PrimitiveCountNV|PrimitiveIndicesNV|ClipDistancePerViewNV|CullDistancePerViewNV|LayerPerViewNV|MeshViewCountNV|MeshViewIndicesNV|BaryCoordNV|BaryCoordNoPerspNV|FragSizeEXT|FragmentSizeNV|FragInvocationCountEXT|InvocationsPerPixelNV|LaunchIdNV|LaunchSizeNV|WorldRayOriginNV|WorldRayDirectionNV|ObjectRayOriginNV|ObjectRayDirectionNV|RayTminNV|RayTmaxNV|InstanceCustomIndexNV|ObjectToWorldNV|WorldToObjectNV|HitTNV|HitKindNV|IncomingRayFlagsNV|WarpsPerSMNV|SMCountNV|WarpIDNV|SMIDNV)\\b",
+			"name": "keyword.spirv"
+		},
+		"ValueEnum_Scope": {
+			"match": "\\b(CrossDevice|Device|Workgroup|Subgroup|Invocation|QueueFamily|QueueFamilyKHR)\\b",
+			"name": "keyword.spirv"
+		},
+		"ValueEnum_GroupOperation": {
+			"match": "\\b(Reduce|InclusiveScan|ExclusiveScan|ClusteredReduce|PartitionedReduceNV|PartitionedInclusiveScanNV|PartitionedExclusiveScanNV)\\b",
+			"name": "keyword.spirv"
+		},
+		"ValueEnum_KernelEnqueueFlags": {
+			"match": "\\b(NoWait|WaitKernel|WaitWorkGroup)\\b",
+			"name": "keyword.spirv"
+		},
+		"ValueEnum_Capability": {
+			"match": "\\b(Matrix|Shader|Geometry|Tessellation|Addresses|Linkage|Kernel|Vector16|Float16Buffer|Float16|Float64|Int64|Int64Atomics|ImageBasic|ImageReadWrite|ImageMipmap|Pipes|Groups|DeviceEnqueue|LiteralSampler|AtomicStorage|Int16|TessellationPointSize|GeometryPointSize|ImageGatherExtended|StorageImageMultisample|UniformBufferArrayDynamicIndexing|SampledImageArrayDynamicIndexing|StorageBufferArrayDynamicIndexing|StorageImageArrayDynamicIndexing|ClipDistance|CullDistance|ImageCubeArray|SampleRateShading|ImageRect|SampledRect|GenericPointer|Int8|InputAttachment|SparseResidency|MinLod|Sampled1D|Image1D|SampledCubeArray|SampledBuffer|ImageBuffer|ImageMSArray|StorageImageExtendedFormats|ImageQuery|DerivativeControl|InterpolationFunction|TransformFeedback|GeometryStreams|StorageImageReadWithoutFormat|StorageImageWriteWithoutFormat|MultiViewport|SubgroupDispatch|NamedBarrier|PipeStorage|GroupNonUniform|GroupNonUniformVote|GroupNonUniformArithmetic|GroupNonUniformBallot|GroupNonUniformShuffle|GroupNonUniformShuffleRelative|GroupNonUniformClustered|GroupNonUniformQuad|ShaderLayer|ShaderViewportIndex|SubgroupBallotKHR|DrawParameters|SubgroupVoteKHR|StorageBuffer16BitAccess|StorageUniformBufferBlock16|UniformAndStorageBuffer16BitAccess|StorageUniform16|StoragePushConstant16|StorageInputOutput16|DeviceGroup|MultiView|VariablePointersStorageBuffer|VariablePointers|AtomicStorageOps|SampleMaskPostDepthCoverage|StorageBuffer8BitAccess|UniformAndStorageBuffer8BitAccess|StoragePushConstant8|DenormPreserve|DenormFlushToZero|SignedZeroInfNanPreserve|RoundingModeRTE|RoundingModeRTZ|Float16ImageAMD|ImageGatherBiasLodAMD|FragmentMaskAMD|StencilExportEXT|ImageReadWriteLodAMD|ShaderClockKHR|SampleMaskOverrideCoverageNV|GeometryShaderPassthroughNV|ShaderViewportIndexLayerEXT|ShaderViewportIndexLayerNV|ShaderViewportMaskNV|ShaderStereoViewNV|PerViewAttributesNV|FragmentFullyCoveredEXT|MeshShadingNV|ImageFootprintNV|FragmentBarycentricNV|ComputeDerivativeGroupQuadsNV|FragmentDensityEXT|ShadingRateNV|GroupNonUniformPartitionedNV|ShaderNonUniform|ShaderNonUniformEXT|RuntimeDescriptorArray|RuntimeDescriptorArrayEXT|InputAttachmentArrayDynamicIndexing|InputAttachmentArrayDynamicIndexingEXT|UniformTexelBufferArrayDynamicIndexing|UniformTexelBufferArrayDynamicIndexingEXT|StorageTexelBufferArrayDynamicIndexing|StorageTexelBufferArrayDynamicIndexingEXT|UniformBufferArrayNonUniformIndexing|UniformBufferArrayNonUniformIndexingEXT|SampledImageArrayNonUniformIndexing|SampledImageArrayNonUniformIndexingEXT|StorageBufferArrayNonUniformIndexing|StorageBufferArrayNonUniformIndexingEXT|StorageImageArrayNonUniformIndexing|StorageImageArrayNonUniformIndexingEXT|InputAttachmentArrayNonUniformIndexing|InputAttachmentArrayNonUniformIndexingEXT|UniformTexelBufferArrayNonUniformIndexing|UniformTexelBufferArrayNonUniformIndexingEXT|StorageTexelBufferArrayNonUniformIndexing|StorageTexelBufferArrayNonUniformIndexingEXT|RayTracingNV|VulkanMemoryModel|VulkanMemoryModelKHR|VulkanMemoryModelDeviceScope|VulkanMemoryModelDeviceScopeKHR|PhysicalStorageBufferAddresses|PhysicalStorageBufferAddressesEXT|ComputeDerivativeGroupLinearNV|CooperativeMatrixNV|FragmentShaderSampleInterlockEXT|FragmentShaderShadingRateInterlockEXT|ShaderSMBuiltinsNV|FragmentShaderPixelInterlockEXT|DemoteToHelperInvocationEXT|SubgroupShuffleINTEL|SubgroupBufferBlockIOINTEL|SubgroupImageBlockIOINTEL|SubgroupImageMediaBlockIOINTEL|IntegerFunctions2INTEL|SubgroupAvcMotionEstimationINTEL|SubgroupAvcMotionEstimationIntraINTEL|SubgroupAvcMotionEstimationChromaINTEL)\\b",
+			"name": "keyword.spirv"
+		},
+		"opcode": {
+			"match": "(Op[a-zA-Z]+)",
+			"name": "entity.name.function.spirv"
+		},
+		"identifier": {
+			"match": "%[a-zA-Z0-9_]+",
+			"name": "variable.spirv"
+		},
+		"number": {
+			"match": "\\b[0-9]+.?[0-9]*\\b",
+			"name": "constant.numeric.spirv"
+		},
+		"comment": {
+			"match": ";[^\n]*",
+			"name": "comment.line.spirv"
+		},
+		"operator": {
+			"match": "=",
+			"name": "keyword.operator.spirv"
+		},
+		"string": {
+			"begin": "\"",
+			"beginCaptures": {
+				"0": {
+					"name": "punctuation.definition.string.begin.spirv"
+				}
+			},
+			"end": "\"",
+			"endCaptures": {
+				"0": {
+					"name": "punctuation.definition.string.end.spirv"
+				}
+			},
+			"name": "string.quoted.double.spirv",
+			"patterns": [ { "include": "#string_escaped_char" } ]
+		},
+		"string_escaped_char": {
+			"patterns": [
+				{
+					"match": "\\\\([0-7]{3}|[abfnrtv\\\\'\"]|x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8})",
+					"name": "constant.character.escape.spirv"
+				}, {
+					"match": "\\\\[^0-7xuUabfnrtv\\'\"]",
+					"name": "invalid.illegal.unknown-escape.spirv"
+				}
+			]
+		}
+	}
+}
diff --git a/utils/vscode/spirv.json.tmpl b/utils/vscode/spirv.json.tmpl
new file mode 100644
index 0000000..8582d03
--- /dev/null
+++ b/utils/vscode/spirv.json.tmpl
@@ -0,0 +1,67 @@
+{
+	"scopeName": "source.spirv",
+	"name": "SPIR-V",
+	"comment": "Generated by {{GenerateArguments}}. Do not modify this file directly.",
+	"patterns": [
+{{range $o := .OperandKinds}}{{if len $o.Enumerants}}		{ "include": "#{{$o.Category}}_{{$o.Kind}}" },
+{{end}}{{end}}		{ "include": "#opcode" },
+		{ "include": "#identifier" },
+		{ "include": "#number" },
+		{ "include": "#string" },
+		{ "include": "#comment" },
+		{ "include": "#operator" }
+	],
+	"repository": { {{range $o := .OperandKinds}}{{if len $o.Enumerants}}
+		"{{$o.Category}}_{{$o.Kind}}": {
+			"match": "\\b({{OperandKindsMatch $o}})\\b",
+			"name": "keyword.spirv"
+		},{{end}}{{end}}
+		"opcode": {
+			"match": "(Op[a-zA-Z]+)",
+			"name": "entity.name.function.spirv"
+		},
+		"identifier": {
+			"match": "%[a-zA-Z0-9_]+",
+			"name": "variable.spirv"
+		},
+		"number": {
+			"match": "\\b[0-9]+.?[0-9]*\\b",
+			"name": "constant.numeric.spirv"
+		},
+		"comment": {
+			"match": ";[^\n]*",
+			"name": "comment.line.spirv"
+		},
+		"operator": {
+			"match": "=",
+			"name": "keyword.operator.spirv"
+		},
+		"string": {
+			"begin": "\"",
+			"beginCaptures": {
+				"0": {
+					"name": "punctuation.definition.string.begin.spirv"
+				}
+			},
+			"end": "\"",
+			"endCaptures": {
+				"0": {
+					"name": "punctuation.definition.string.end.spirv"
+				}
+			},
+			"name": "string.quoted.double.spirv",
+			"patterns": [ { "include": "#string_escaped_char" } ]
+		},
+		"string_escaped_char": {
+			"patterns": [
+				{
+					"match": "\\\\([0-7]{3}|[abfnrtv\\\\'\"]|x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8})",
+					"name": "constant.character.escape.spirv"
+				}, {
+					"match": "\\\\[^0-7xuUabfnrtv\\'\"]",
+					"name": "invalid.illegal.unknown-escape.spirv"
+				}
+			]
+		}
+	}
+}
diff --git a/utils/vscode/src/grammar/grammar.go b/utils/vscode/src/grammar/grammar.go
new file mode 100644
index 0000000..857a193
--- /dev/null
+++ b/utils/vscode/src/grammar/grammar.go
@@ -0,0 +1,81 @@
+// Copyright (C) 2019 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package grammar holds the JSON type definitions for the SPIR-V grammar schema.
+//
+// See https://www.khronos.org/registry/spir-v/specs/unified1/MachineReadableGrammar.html
+// for more information.
+package grammar
+
+// Root is the top-level structure of the JSON grammar.
+type Root struct {
+	MagicNumber  string        `json:"magic_number"`
+	MajorVersion int           `json:"major_version"`
+	MinorVersion int           `json:"minor_version"`
+	Revision     int           `json:"revision"`
+	Instructions []Instruction `json:"instructions"`
+	OperandKinds []OperandKind `json:"operand_kinds"`
+}
+
+// Instruction holds information about a specific SPIR-V instruction.
+type Instruction struct {
+	Opname   string    `json:"opname"`
+	Class    string    `json:"class"`
+	Opcode   int       `json:"opcode"`
+	Operands []Operand `json:"operands"`
+}
+
+// Operand contains information about a logical operand for an instruction.
+type Operand struct {
+	Kind       string     `json:"kind"`
+	Name       string     `json:"name"`
+	Quantifier Quantifier `json:"quantifier"`
+}
+
+// OperandKind contains information about a specific operand kind.
+type OperandKind struct {
+	Category   string      `json:"category"`
+	Kind       string      `json:"kind"`
+	Enumerants []Enumerant `json:"enumerants"`
+	Bases      []string    `json:"bases"`
+}
+
+// Enumerant contains information about an enumerant in an enum.
+type Enumerant struct {
+	Enumerant    string      `json:"enumerant"`
+	Value        interface{} `json:"value"`
+	Capabilities []string    `json:"capabilities"`
+	Parameters   []Parameter `json:"parameters"`
+	Version      string      `json:"version"`
+}
+
+// Parameter contains information about a logical parameter for an enumerant.
+type Parameter struct {
+	Kind string `json:"kind"`
+	Name string `json:"name"`
+}
+
+// Quantifier indicates the number of times the quantified term may appear.
+type Quantifier string
+
+const (
+	// Once indicates the quantified term may appear exactly once.
+	Once Quantifier = ""
+	// ZeroOrOnce indicates the quantified term may appear zero or one
+	// time; an optional term.
+	ZeroOrOnce Quantifier = "?"
+	// ZeroOrMany indicates the quantified term may appear any number of
+	// times.
+	ZeroOrMany Quantifier = "*"
+)
diff --git a/utils/vscode/src/langsvr.go b/utils/vscode/src/langsvr.go
new file mode 100644
index 0000000..d1b80dc
--- /dev/null
+++ b/utils/vscode/src/langsvr.go
@@ -0,0 +1,527 @@
+// Copyright (C) 2019 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// langsvr implements a Language Server for the SPIRV assembly language.
+package main
+
+import (
+	"context"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"log"
+	"os"
+	"path"
+	"sort"
+	"strings"
+	"sync"
+	"unicode/utf8"
+
+	"./parser"
+	"./schema"
+
+	"./lsp/jsonrpc2"
+	lsp "./lsp/protocol"
+)
+
+// rSpy is a reader 'spy' that wraps an io.Reader, and logs all data that passes
+// through it.
+type rSpy struct {
+	prefix string
+	r      io.Reader
+}
+
+func (s rSpy) Read(p []byte) (n int, err error) {
+	n, err = s.r.Read(p)
+	log.Printf("%v %v", s.prefix, string(p[:n]))
+	return n, err
+}
+
+// wSpy is a reader 'spy' that wraps an io.Writer, and logs all data that passes
+// through it.
+type wSpy struct {
+	prefix string
+	w      io.Writer
+}
+
+func (s wSpy) Write(p []byte) (n int, err error) {
+	n, err = s.w.Write(p)
+	log.Printf("%v %v", s.prefix, string(p))
+	return n, err
+}
+
+// main entry point.
+func main() {
+	// create a log file in the executable's directory.
+	if logfile, err := os.Create(path.Join(path.Dir(os.Args[0]), "log.txt")); err == nil {
+		defer logfile.Close()
+		log.SetOutput(logfile)
+	} else {
+		log.SetOutput(ioutil.Discard)
+	}
+
+	log.Println("language server started")
+
+	stream := jsonrpc2.NewHeaderStream(rSpy{"IDE", os.Stdin}, wSpy{"LS", os.Stdout})
+	s := server{
+		files: map[string]*file{},
+	}
+	s.ctx, s.conn, s.client = lsp.NewServer(context.Background(), stream, &s)
+	if err := s.conn.Run(s.ctx); err != nil {
+		log.Panicln(err)
+		os.Exit(1)
+	}
+
+	log.Println("language server stopped")
+}
+
+type server struct {
+	ctx    context.Context
+	conn   *jsonrpc2.Conn
+	client lsp.Client
+
+	files      map[string]*file
+	filesMutex sync.Mutex
+}
+
+// file represents a source file
+type file struct {
+	fullRange parser.Range
+	res       parser.Results
+}
+
+// tokAt returns the parser token at the given position lp
+func (f *file) tokAt(lp lsp.Position) *parser.Token {
+	toks := f.res.Tokens
+	p := parser.Position{Line: int(lp.Line) + 1, Column: int(lp.Character) + 1}
+	i := sort.Search(len(toks), func(i int) bool { return p.LessThan(toks[i].Range.End) })
+	if i == len(toks) {
+		return nil
+	}
+	if toks[i].Range.Contains(p) {
+		return toks[i]
+	}
+	return nil
+}
+
+func (s *server) DidChangeWorkspaceFolders(ctx context.Context, p *lsp.DidChangeWorkspaceFoldersParams) error {
+	log.Println("server.DidChangeWorkspaceFolders()")
+	return nil
+}
+func (s *server) Initialized(ctx context.Context, p *lsp.InitializedParams) error {
+	log.Println("server.Initialized()")
+	return nil
+}
+func (s *server) Exit(ctx context.Context) error {
+	log.Println("server.Exit()")
+	return nil
+}
+func (s *server) DidChangeConfiguration(ctx context.Context, p *lsp.DidChangeConfigurationParams) error {
+	log.Println("server.DidChangeConfiguration()")
+	return nil
+}
+func (s *server) DidOpen(ctx context.Context, p *lsp.DidOpenTextDocumentParams) error {
+	log.Println("server.DidOpen()")
+	return s.processFile(ctx, p.TextDocument.URI, p.TextDocument.Text)
+}
+func (s *server) DidChange(ctx context.Context, p *lsp.DidChangeTextDocumentParams) error {
+	log.Println("server.DidChange()")
+	return s.processFile(ctx, p.TextDocument.URI, p.ContentChanges[0].Text)
+}
+func (s *server) DidClose(ctx context.Context, p *lsp.DidCloseTextDocumentParams) error {
+	log.Println("server.DidClose()")
+	return nil
+}
+func (s *server) DidSave(ctx context.Context, p *lsp.DidSaveTextDocumentParams) error {
+	log.Println("server.DidSave()")
+	return nil
+}
+func (s *server) WillSave(ctx context.Context, p *lsp.WillSaveTextDocumentParams) error {
+	log.Println("server.WillSave()")
+	return nil
+}
+func (s *server) DidChangeWatchedFiles(ctx context.Context, p *lsp.DidChangeWatchedFilesParams) error {
+	log.Println("server.DidChangeWatchedFiles()")
+	return nil
+}
+func (s *server) Progress(ctx context.Context, p *lsp.ProgressParams) error {
+	log.Println("server.Progress()")
+	return nil
+}
+func (s *server) SetTraceNotification(ctx context.Context, p *lsp.SetTraceParams) error {
+	log.Println("server.SetTraceNotification()")
+	return nil
+}
+func (s *server) LogTraceNotification(ctx context.Context, p *lsp.LogTraceParams) error {
+	log.Println("server.LogTraceNotification()")
+	return nil
+}
+func (s *server) Implementation(ctx context.Context, p *lsp.ImplementationParams) ([]lsp.Location, error) {
+	log.Println("server.Implementation()")
+	return nil, nil
+}
+func (s *server) TypeDefinition(ctx context.Context, p *lsp.TypeDefinitionParams) ([]lsp.Location, error) {
+	log.Println("server.TypeDefinition()")
+	return nil, nil
+}
+func (s *server) DocumentColor(ctx context.Context, p *lsp.DocumentColorParams) ([]lsp.ColorInformation, error) {
+	log.Println("server.DocumentColor()")
+	return nil, nil
+}
+func (s *server) ColorPresentation(ctx context.Context, p *lsp.ColorPresentationParams) ([]lsp.ColorPresentation, error) {
+	log.Println("server.ColorPresentation()")
+	return nil, nil
+}
+func (s *server) FoldingRange(ctx context.Context, p *lsp.FoldingRangeParams) ([]lsp.FoldingRange, error) {
+	log.Println("server.FoldingRange()")
+	return nil, nil
+}
+func (s *server) Declaration(ctx context.Context, p *lsp.DeclarationParams) ([]lsp.DeclarationLink, error) {
+	log.Println("server.Declaration()")
+	return nil, nil
+}
+func (s *server) SelectionRange(ctx context.Context, p *lsp.SelectionRangeParams) ([]lsp.SelectionRange, error) {
+	log.Println("server.SelectionRange()")
+	return nil, nil
+}
+func (s *server) Initialize(ctx context.Context, p *lsp.ParamInitia) (*lsp.InitializeResult, error) {
+	log.Println("server.Initialize()")
+	res := lsp.InitializeResult{
+		Capabilities: lsp.ServerCapabilities{
+			TextDocumentSync: lsp.TextDocumentSyncOptions{
+				OpenClose: true,
+				Change:    lsp.Full, // TODO: Implement incremental
+			},
+			HoverProvider:              true,
+			DefinitionProvider:         true,
+			ReferencesProvider:         true,
+			RenameProvider:             true,
+			DocumentFormattingProvider: true,
+		},
+	}
+	return &res, nil
+}
+func (s *server) Shutdown(ctx context.Context) error {
+	log.Println("server.Shutdown()")
+	return nil
+}
+func (s *server) WillSaveWaitUntil(ctx context.Context, p *lsp.WillSaveTextDocumentParams) ([]lsp.TextEdit, error) {
+	log.Println("server.WillSaveWaitUntil()")
+	return nil, nil
+}
+func (s *server) Completion(ctx context.Context, p *lsp.CompletionParams) (*lsp.CompletionList, error) {
+	log.Println("server.Completion()")
+	return nil, nil
+}
+func (s *server) Resolve(ctx context.Context, p *lsp.CompletionItem) (*lsp.CompletionItem, error) {
+	log.Println("server.Resolve()")
+	return nil, nil
+}
+func (s *server) Hover(ctx context.Context, p *lsp.HoverParams) (*lsp.Hover, error) {
+	log.Println("server.Hover()")
+	f := s.getFile(p.TextDocument.URI)
+	if f == nil {
+		return nil, fmt.Errorf("Unknown file")
+	}
+
+	if tok := f.tokAt(p.Position); tok != nil {
+		sb := strings.Builder{}
+		switch v := f.res.Mappings[tok].(type) {
+		default:
+			sb.WriteString(fmt.Sprintf("<Unhandled type '%T'>", v))
+		case *parser.Instruction:
+			sb.WriteString(fmt.Sprintf("```\n%v\n```", v.Opcode.Opname))
+		case *parser.Identifier:
+			sb.WriteString(fmt.Sprintf("```\n%v\n```", v.Definition.Range.Text(f.res.Lines)))
+		case *parser.Operand:
+			if v.Name != "" {
+				sb.WriteString(strings.Trim(v.Name, `'`))
+				sb.WriteString("\n\n")
+			}
+
+			switch v.Kind.Category {
+			case schema.OperandCategoryBitEnum:
+			case schema.OperandCategoryValueEnum:
+				sb.WriteString("```\n")
+				sb.WriteString(strings.Trim(v.Kind.Kind, `'`))
+				sb.WriteString("\n```")
+			case schema.OperandCategoryID:
+				if s := tok.Text(f.res.Lines); s != "" {
+					if id, ok := f.res.Identifiers[s]; ok && id.Definition != nil {
+						sb.WriteString("```\n")
+						sb.WriteString(id.Definition.Range.Text(f.res.Lines))
+						sb.WriteString("\n```")
+					}
+				}
+			case schema.OperandCategoryLiteral:
+			case schema.OperandCategoryComposite:
+			}
+		case nil:
+		}
+
+		if sb.Len() > 0 {
+			res := lsp.Hover{
+				Contents: lsp.MarkupContent{
+					Kind:  "markdown",
+					Value: sb.String(),
+				},
+			}
+			return &res, nil
+		}
+	}
+
+	return nil, nil
+}
+func (s *server) SignatureHelp(ctx context.Context, p *lsp.SignatureHelpParams) (*lsp.SignatureHelp, error) {
+	log.Println("server.SignatureHelp()")
+	return nil, nil
+}
+func (s *server) Definition(ctx context.Context, p *lsp.DefinitionParams) ([]lsp.Location, error) {
+	log.Println("server.Definition()")
+	if f := s.getFile(p.TextDocument.URI); f != nil {
+		if tok := f.tokAt(p.Position); tok != nil {
+			if s := tok.Text(f.res.Lines); s != "" {
+				if id, ok := f.res.Identifiers[s]; ok {
+					loc := lsp.Location{
+						URI:   p.TextDocument.URI,
+						Range: rangeToLSP(id.Definition.Range),
+					}
+					return []lsp.Location{loc}, nil
+				}
+			}
+		}
+	}
+	return nil, nil
+}
+func (s *server) References(ctx context.Context, p *lsp.ReferenceParams) ([]lsp.Location, error) {
+	log.Println("server.References()")
+	if f := s.getFile(p.TextDocument.URI); f != nil {
+		if tok := f.tokAt(p.Position); tok != nil {
+			if s := tok.Text(f.res.Lines); s != "" {
+				if id, ok := f.res.Identifiers[s]; ok {
+					locs := make([]lsp.Location, len(id.References))
+					for i, r := range id.References {
+						locs[i] = lsp.Location{
+							URI:   p.TextDocument.URI,
+							Range: rangeToLSP(r.Range),
+						}
+					}
+					return locs, nil
+				}
+			}
+		}
+	}
+	return nil, nil
+}
+func (s *server) DocumentHighlight(ctx context.Context, p *lsp.DocumentHighlightParams) ([]lsp.DocumentHighlight, error) {
+	log.Println("server.DocumentHighlight()")
+	return nil, nil
+}
+func (s *server) DocumentSymbol(ctx context.Context, p *lsp.DocumentSymbolParams) ([]lsp.DocumentSymbol, error) {
+	log.Println("server.DocumentSymbol()")
+	return nil, nil
+}
+func (s *server) CodeAction(ctx context.Context, p *lsp.CodeActionParams) ([]lsp.CodeAction, error) {
+	log.Println("server.CodeAction()")
+	return nil, nil
+}
+func (s *server) Symbol(ctx context.Context, p *lsp.WorkspaceSymbolParams) ([]lsp.SymbolInformation, error) {
+	log.Println("server.Symbol()")
+	return nil, nil
+}
+func (s *server) CodeLens(ctx context.Context, p *lsp.CodeLensParams) ([]lsp.CodeLens, error) {
+	log.Println("server.CodeLens()")
+	return nil, nil
+}
+func (s *server) ResolveCodeLens(ctx context.Context, p *lsp.CodeLens) (*lsp.CodeLens, error) {
+	log.Println("server.ResolveCodeLens()")
+	return nil, nil
+}
+func (s *server) DocumentLink(ctx context.Context, p *lsp.DocumentLinkParams) ([]lsp.DocumentLink, error) {
+	log.Println("server.DocumentLink()")
+	return nil, nil
+}
+func (s *server) ResolveDocumentLink(ctx context.Context, p *lsp.DocumentLink) (*lsp.DocumentLink, error) {
+	log.Println("server.ResolveDocumentLink()")
+	return nil, nil
+}
+func (s *server) Formatting(ctx context.Context, p *lsp.DocumentFormattingParams) ([]lsp.TextEdit, error) {
+	log.Println("server.Formatting()")
+	if f := s.getFile(p.TextDocument.URI); f != nil {
+		// Start by measuring the distance from the start of each line to the
+		// first opcode on that line.
+		lineInstOffsets, maxInstOffset, instOffset, curOffset := []int{}, 0, 0, -1
+		for _, t := range f.res.Tokens {
+			curOffset++ // whitespace between tokens
+			switch t.Type {
+			case parser.Ident:
+				if _, isInst := schema.Opcodes[t.Text(f.res.Lines)]; isInst && instOffset == 0 {
+					instOffset = curOffset
+					continue
+				}
+			case parser.Newline:
+				lineInstOffsets = append(lineInstOffsets, instOffset)
+				if instOffset > maxInstOffset {
+					maxInstOffset = instOffset
+				}
+				curOffset, instOffset = -1, 0
+			default:
+				curOffset += utf8.RuneCountInString(t.Text(f.res.Lines))
+			}
+		}
+		lineInstOffsets = append(lineInstOffsets, instOffset)
+
+		// Now rewrite each of the lines, adding padding at the start of the
+		// line for alignment.
+		sb, newline := strings.Builder{}, true
+		for _, t := range f.res.Tokens {
+			if newline {
+				newline = false
+				indent := maxInstOffset - lineInstOffsets[0]
+				lineInstOffsets = lineInstOffsets[1:]
+				switch t.Type {
+				case parser.Newline, parser.Comment:
+				default:
+					for s := 0; s < indent; s++ {
+						sb.WriteRune(' ')
+					}
+				}
+			} else if t.Type != parser.Newline {
+				sb.WriteString(" ")
+			}
+
+			sb.WriteString(t.Text(f.res.Lines))
+			if t.Type == parser.Newline {
+				newline = true
+			}
+		}
+
+		// Every good file ends with a new line.
+		sb.WriteString("\n")
+
+		return []lsp.TextEdit{
+			lsp.TextEdit{
+				Range:   rangeToLSP(f.fullRange),
+				NewText: sb.String(),
+			},
+		}, nil
+	}
+	return nil, nil
+}
+func (s *server) RangeFormatting(ctx context.Context, p *lsp.DocumentRangeFormattingParams) ([]lsp.TextEdit, error) {
+	log.Println("server.RangeFormatting()")
+	return nil, nil
+}
+func (s *server) OnTypeFormatting(ctx context.Context, p *lsp.DocumentOnTypeFormattingParams) ([]lsp.TextEdit, error) {
+	log.Println("server.OnTypeFormatting()")
+	return nil, nil
+}
+func (s *server) Rename(ctx context.Context, p *lsp.RenameParams) (*lsp.WorkspaceEdit, error) {
+	log.Println("server.Rename()")
+	if f := s.getFile(p.TextDocument.URI); f != nil {
+		if tok := f.tokAt(p.Position); tok != nil {
+			if s := tok.Text(f.res.Lines); s != "" {
+				if id, ok := f.res.Identifiers[s]; ok {
+					changes := make([]lsp.TextEdit, len(id.References))
+					for i, r := range id.References {
+						changes[i].Range = rangeToLSP(r.Range)
+						changes[i].NewText = p.NewName
+					}
+					m := map[string][]lsp.TextEdit{}
+					m[p.TextDocument.URI] = changes
+					return &lsp.WorkspaceEdit{Changes: &m}, nil
+				}
+			}
+		}
+	}
+	return nil, nil
+}
+func (s *server) PrepareRename(ctx context.Context, p *lsp.PrepareRenameParams) (*lsp.Range, error) {
+	log.Println("server.PrepareRename()")
+	return nil, nil
+}
+func (s *server) ExecuteCommand(ctx context.Context, p *lsp.ExecuteCommandParams) (interface{}, error) {
+	log.Println("server.ExecuteCommand()")
+	return nil, nil
+}
+
+func (s *server) processFile(ctx context.Context, uri, source string) error {
+	log.Println("server.DidOpen()")
+	res, err := parser.Parse(source)
+	if err != nil {
+		return err
+	}
+	fullRange := parser.Range{
+		Start: parser.Position{Line: 1, Column: 1},
+		End:   parser.Position{Line: len(res.Lines), Column: utf8.RuneCountInString(res.Lines[len(res.Lines)-1]) + 1},
+	}
+
+	s.filesMutex.Lock()
+	s.files[uri] = &file{
+		fullRange: fullRange,
+		res:       res,
+	}
+	s.filesMutex.Unlock()
+
+	dp := lsp.PublishDiagnosticsParams{URI: uri, Diagnostics: make([]lsp.Diagnostic, len(res.Diagnostics))}
+	for i, d := range res.Diagnostics {
+		dp.Diagnostics[i] = diagnosticToLSP(d)
+	}
+	s.client.PublishDiagnostics(ctx, &dp)
+	return nil
+}
+
+func (s *server) getFile(uri string) *file {
+	s.filesMutex.Lock()
+	defer s.filesMutex.Unlock()
+	return s.files[uri]
+}
+
+func diagnosticToLSP(d parser.Diagnostic) lsp.Diagnostic {
+	return lsp.Diagnostic{
+		Range:    rangeToLSP(d.Range),
+		Severity: severityToLSP(d.Severity),
+		Message:  d.Message,
+	}
+}
+
+func severityToLSP(s parser.Severity) lsp.DiagnosticSeverity {
+	switch s {
+	case parser.SeverityError:
+		return lsp.SeverityError
+	case parser.SeverityWarning:
+		return lsp.SeverityWarning
+	case parser.SeverityInformation:
+		return lsp.SeverityInformation
+	case parser.SeverityHint:
+		return lsp.SeverityHint
+	default:
+		log.Panicf("Invalid severity '%d'", int(s))
+		return lsp.SeverityError
+	}
+}
+
+func rangeToLSP(r parser.Range) lsp.Range {
+	return lsp.Range{
+		Start: positionToLSP(r.Start),
+		End:   positionToLSP(r.End),
+	}
+}
+
+func positionToLSP(r parser.Position) lsp.Position {
+	return lsp.Position{
+		Line:      float64(r.Line - 1),
+		Character: float64(r.Column - 1),
+	}
+}
diff --git a/utils/vscode/src/lsp/LICENSE b/utils/vscode/src/lsp/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/utils/vscode/src/lsp/LICENSE
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   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.
diff --git a/utils/vscode/src/lsp/README.md b/utils/vscode/src/lsp/README.md
new file mode 100644
index 0000000..c78f183
--- /dev/null
+++ b/utils/vscode/src/lsp/README.md
@@ -0,0 +1,5 @@
+This directory contains code forked from https://github.com/golang/tools/tree/master/internal/lsp.
+
+This code has been modified to remove unneeded features and dependencies.
+
+Submitted on behalf of a third-party: The Go Authors
diff --git a/utils/vscode/src/lsp/jsonrpc2/handler.go b/utils/vscode/src/lsp/jsonrpc2/handler.go
new file mode 100644
index 0000000..20d4841
--- /dev/null
+++ b/utils/vscode/src/lsp/jsonrpc2/handler.go
@@ -0,0 +1,134 @@
+// Copyright 2019 The Go Authors.
+//
+// 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.
+
+package jsonrpc2
+
+import (
+	"context"
+)
+
+// Handler is the interface used to hook into the message handling of an rpc
+// connection.
+type Handler interface {
+	// Deliver is invoked to handle incoming requests.
+	// If the request returns false from IsNotify then the Handler must eventually
+	// call Reply on the Conn with the supplied request.
+	// Handlers are called synchronously, they should pass the work off to a go
+	// routine if they are going to take a long time.
+	// If Deliver returns true all subsequent handlers will be invoked with
+	// delivered set to true, and should not attempt to deliver the message.
+	Deliver(ctx context.Context, r *Request, delivered bool) bool
+
+	// Cancel is invoked for cancelled outgoing requests.
+	// It is okay to use the connection to send notifications, but the context will
+	// be in the cancelled state, so you must do it with the background context
+	// instead.
+	// If Cancel returns true all subsequent handlers will be invoked with
+	// cancelled set to true, and should not attempt to cancel the message.
+	Cancel(ctx context.Context, conn *Conn, id ID, cancelled bool) bool
+
+	// Log is invoked for all messages flowing through a Conn.
+	// direction indicates if the message being received or sent
+	// id is the message id, if not set it was a notification
+	// elapsed is the time between a call being seen and the response, and is
+	// negative for anything that is not a response.
+	// method is the method name specified in the message
+	// payload is the parameters for a call or notification, and the result for a
+	// response
+
+	// Request is called near the start of processing any request.
+	Request(ctx context.Context, conn *Conn, direction Direction, r *WireRequest) context.Context
+	// Response is called near the start of processing any response.
+	Response(ctx context.Context, conn *Conn, direction Direction, r *WireResponse) context.Context
+	// Done is called when any request is fully processed.
+	// For calls, this means the response has also been processed, for notifies
+	// this is as soon as the message has been written to the stream.
+	// If err is set, it implies the request failed.
+	Done(ctx context.Context, err error)
+	// Read is called with a count each time some data is read from the stream.
+	// The read calls are delayed until after the data has been interpreted so
+	// that it can be attributed to a request/response.
+	Read(ctx context.Context, bytes int64) context.Context
+	// Wrote is called each time some data is written to the stream.
+	Wrote(ctx context.Context, bytes int64) context.Context
+	// Error is called with errors that cannot be delivered through the normal
+	// mechanisms, for instance a failure to process a notify cannot be delivered
+	// back to the other party.
+	Error(ctx context.Context, err error)
+}
+
+// Direction is used to indicate to a logger whether the logged message was being
+// sent or received.
+type Direction bool
+
+const (
+	// Send indicates the message is outgoing.
+	Send = Direction(true)
+	// Receive indicates the message is incoming.
+	Receive = Direction(false)
+)
+
+func (d Direction) String() string {
+	switch d {
+	case Send:
+		return "send"
+	case Receive:
+		return "receive"
+	default:
+		panic("unreachable")
+	}
+}
+
+type EmptyHandler struct{}
+
+func (EmptyHandler) Deliver(ctx context.Context, r *Request, delivered bool) bool {
+	return false
+}
+
+func (EmptyHandler) Cancel(ctx context.Context, conn *Conn, id ID, cancelled bool) bool {
+	return false
+}
+
+func (EmptyHandler) Request(ctx context.Context, conn *Conn, direction Direction, r *WireRequest) context.Context {
+	return ctx
+}
+
+func (EmptyHandler) Response(ctx context.Context, conn *Conn, direction Direction, r *WireResponse) context.Context {
+	return ctx
+}
+
+func (EmptyHandler) Done(ctx context.Context, err error) {
+}
+
+func (EmptyHandler) Read(ctx context.Context, bytes int64) context.Context {
+	return ctx
+}
+
+func (EmptyHandler) Wrote(ctx context.Context, bytes int64) context.Context {
+	return ctx
+}
+
+func (EmptyHandler) Error(ctx context.Context, err error) {}
+
+type defaultHandler struct{ EmptyHandler }
+
+func (defaultHandler) Deliver(ctx context.Context, r *Request, delivered bool) bool {
+	if delivered {
+		return false
+	}
+	if !r.IsNotify() {
+		r.Reply(ctx, nil, NewErrorf(CodeMethodNotFound, "method %q not found", r.Method))
+	}
+	return true
+}
diff --git a/utils/vscode/src/lsp/jsonrpc2/jsonrpc2.go b/utils/vscode/src/lsp/jsonrpc2/jsonrpc2.go
new file mode 100644
index 0000000..b8436d2
--- /dev/null
+++ b/utils/vscode/src/lsp/jsonrpc2/jsonrpc2.go
@@ -0,0 +1,416 @@
+// Copyright 2018 The Go Authors.
+//
+// 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.
+
+// Package jsonrpc2 is a minimal implementation of the JSON RPC 2 spec.
+// https://www.jsonrpc.org/specification
+// It is intended to be compatible with other implementations at the wire level.
+package jsonrpc2
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"sync"
+	"sync/atomic"
+)
+
+// Conn is a JSON RPC 2 client server connection.
+// Conn is bidirectional; it does not have a designated server or client end.
+type Conn struct {
+	seq        int64 // must only be accessed using atomic operations
+	handlers   []Handler
+	stream     Stream
+	err        error
+	pendingMu  sync.Mutex // protects the pending map
+	pending    map[ID]chan *WireResponse
+	handlingMu sync.Mutex // protects the handling map
+	handling   map[ID]*Request
+}
+
+type requestState int
+
+const (
+	requestWaiting = requestState(iota)
+	requestSerial
+	requestParallel
+	requestReplied
+	requestDone
+)
+
+// Request is sent to a server to represent a Call or Notify operaton.
+type Request struct {
+	conn        *Conn
+	cancel      context.CancelFunc
+	state       requestState
+	nextRequest chan struct{}
+
+	// The Wire values of the request.
+	WireRequest
+}
+
+// NewErrorf builds a Error struct for the supplied message and code.
+// If args is not empty, message and args will be passed to Sprintf.
+func NewErrorf(code int64, format string, args ...interface{}) *Error {
+	return &Error{
+		Code:    code,
+		Message: fmt.Sprintf(format, args...),
+	}
+}
+
+// NewConn creates a new connection object around the supplied stream.
+// You must call Run for the connection to be active.
+func NewConn(s Stream) *Conn {
+	conn := &Conn{
+		handlers: []Handler{defaultHandler{}},
+		stream:   s,
+		pending:  make(map[ID]chan *WireResponse),
+		handling: make(map[ID]*Request),
+	}
+	return conn
+}
+
+// AddHandler adds a new handler to the set the connection will invoke.
+// Handlers are invoked in the reverse order of how they were added, this
+// allows the most recent addition to be the first one to attempt to handle a
+// message.
+func (c *Conn) AddHandler(handler Handler) {
+	// prepend the new handlers so we use them first
+	c.handlers = append([]Handler{handler}, c.handlers...)
+}
+
+// Cancel cancels a pending Call on the server side.
+// The call is identified by its id.
+// JSON RPC 2 does not specify a cancel message, so cancellation support is not
+// directly wired in. This method allows a higher level protocol to choose how
+// to propagate the cancel.
+func (c *Conn) Cancel(id ID) {
+	c.handlingMu.Lock()
+	handling, found := c.handling[id]
+	c.handlingMu.Unlock()
+	if found {
+		handling.cancel()
+	}
+}
+
+// Notify is called to send a notification request over the connection.
+// It will return as soon as the notification has been sent, as no response is
+// possible.
+func (c *Conn) Notify(ctx context.Context, method string, params interface{}) (err error) {
+	jsonParams, err := marshalToRaw(params)
+	if err != nil {
+		return fmt.Errorf("marshalling notify parameters: %v", err)
+	}
+	request := &WireRequest{
+		Method: method,
+		Params: jsonParams,
+	}
+	data, err := json.Marshal(request)
+	if err != nil {
+		return fmt.Errorf("marshalling notify request: %v", err)
+	}
+	for _, h := range c.handlers {
+		ctx = h.Request(ctx, c, Send, request)
+	}
+	defer func() {
+		for _, h := range c.handlers {
+			h.Done(ctx, err)
+		}
+	}()
+	n, err := c.stream.Write(ctx, data)
+	for _, h := range c.handlers {
+		ctx = h.Wrote(ctx, n)
+	}
+	return err
+}
+
+// Call sends a request over the connection and then waits for a response.
+// If the response is not an error, it will be decoded into result.
+// result must be of a type you an pass to json.Unmarshal.
+func (c *Conn) Call(ctx context.Context, method string, params, result interface{}) (err error) {
+	// generate a new request identifier
+	id := ID{Number: atomic.AddInt64(&c.seq, 1)}
+	jsonParams, err := marshalToRaw(params)
+	if err != nil {
+		return fmt.Errorf("marshalling call parameters: %v", err)
+	}
+	request := &WireRequest{
+		ID:     &id,
+		Method: method,
+		Params: jsonParams,
+	}
+	// marshal the request now it is complete
+	data, err := json.Marshal(request)
+	if err != nil {
+		return fmt.Errorf("marshalling call request: %v", err)
+	}
+	for _, h := range c.handlers {
+		ctx = h.Request(ctx, c, Send, request)
+	}
+	// we have to add ourselves to the pending map before we send, otherwise we
+	// are racing the response
+	rchan := make(chan *WireResponse)
+	c.pendingMu.Lock()
+	c.pending[id] = rchan
+	c.pendingMu.Unlock()
+	defer func() {
+		// clean up the pending response handler on the way out
+		c.pendingMu.Lock()
+		delete(c.pending, id)
+		c.pendingMu.Unlock()
+		for _, h := range c.handlers {
+			h.Done(ctx, err)
+		}
+	}()
+	// now we are ready to send
+	n, err := c.stream.Write(ctx, data)
+	for _, h := range c.handlers {
+		ctx = h.Wrote(ctx, n)
+	}
+	if err != nil {
+		// sending failed, we will never get a response, so don't leave it pending
+		return err
+	}
+	// now wait for the response
+	select {
+	case response := <-rchan:
+		for _, h := range c.handlers {
+			ctx = h.Response(ctx, c, Receive, response)
+		}
+		// is it an error response?
+		if response.Error != nil {
+			return response.Error
+		}
+		if result == nil || response.Result == nil {
+			return nil
+		}
+		if err := json.Unmarshal(*response.Result, result); err != nil {
+			return fmt.Errorf("unmarshalling result: %v", err)
+		}
+		return nil
+	case <-ctx.Done():
+		// allow the handler to propagate the cancel
+		cancelled := false
+		for _, h := range c.handlers {
+			if h.Cancel(ctx, c, id, cancelled) {
+				cancelled = true
+			}
+		}
+		return ctx.Err()
+	}
+}
+
+// Conn returns the connection that created this request.
+func (r *Request) Conn() *Conn { return r.conn }
+
+// IsNotify returns true if this request is a notification.
+func (r *Request) IsNotify() bool {
+	return r.ID == nil
+}
+
+// Parallel indicates that the system is now allowed to process other requests
+// in parallel with this one.
+// It is safe to call any number of times, but must only be called from the
+// request handling go routine.
+// It is implied by both reply and by the handler returning.
+func (r *Request) Parallel() {
+	if r.state >= requestParallel {
+		return
+	}
+	r.state = requestParallel
+	close(r.nextRequest)
+}
+
+// Reply sends a reply to the given request.
+// It is an error to call this if request was not a call.
+// You must call this exactly once for any given request.
+// It should only be called from the handler go routine.
+// If err is set then result will be ignored.
+// If the request has not yet dropped into parallel mode
+// it will be before this function returns.
+func (r *Request) Reply(ctx context.Context, result interface{}, err error) error {
+	if r.state >= requestReplied {
+		return fmt.Errorf("reply invoked more than once")
+	}
+	if r.IsNotify() {
+		return fmt.Errorf("reply not invoked with a valid call")
+	}
+	// reply ends the handling phase of a call, so if we are not yet
+	// parallel we should be now. The go routine is allowed to continue
+	// to do work after replying, which is why it is important to unlock
+	// the rpc system at this point.
+	r.Parallel()
+	r.state = requestReplied
+
+	var raw *json.RawMessage
+	if err == nil {
+		raw, err = marshalToRaw(result)
+	}
+	response := &WireResponse{
+		Result: raw,
+		ID:     r.ID,
+	}
+	if err != nil {
+		if callErr, ok := err.(*Error); ok {
+			response.Error = callErr
+		} else {
+			response.Error = NewErrorf(0, "%s", err)
+		}
+	}
+	data, err := json.Marshal(response)
+	if err != nil {
+		return err
+	}
+	for _, h := range r.conn.handlers {
+		ctx = h.Response(ctx, r.conn, Send, response)
+	}
+	n, err := r.conn.stream.Write(ctx, data)
+	for _, h := range r.conn.handlers {
+		ctx = h.Wrote(ctx, n)
+	}
+
+	if err != nil {
+		// TODO(iancottrell): if a stream write fails, we really need to shut down
+		// the whole stream
+		return err
+	}
+	return nil
+}
+
+func (c *Conn) setHandling(r *Request, active bool) {
+	if r.ID == nil {
+		return
+	}
+	r.conn.handlingMu.Lock()
+	defer r.conn.handlingMu.Unlock()
+	if active {
+		r.conn.handling[*r.ID] = r
+	} else {
+		delete(r.conn.handling, *r.ID)
+	}
+}
+
+// combined has all the fields of both Request and Response.
+// We can decode this and then work out which it is.
+type combined struct {
+	VersionTag VersionTag       `json:"jsonrpc"`
+	ID         *ID              `json:"id,omitempty"`
+	Method     string           `json:"method"`
+	Params     *json.RawMessage `json:"params,omitempty"`
+	Result     *json.RawMessage `json:"result,omitempty"`
+	Error      *Error           `json:"error,omitempty"`
+}
+
+// Run blocks until the connection is terminated, and returns any error that
+// caused the termination.
+// It must be called exactly once for each Conn.
+// It returns only when the reader is closed or there is an error in the stream.
+func (c *Conn) Run(runCtx context.Context) error {
+	// we need to make the next request "lock" in an unlocked state to allow
+	// the first incoming request to proceed. All later requests are unlocked
+	// by the preceding request going to parallel mode.
+	nextRequest := make(chan struct{})
+	close(nextRequest)
+	for {
+		// get the data for a message
+		data, n, err := c.stream.Read(runCtx)
+		if err != nil {
+			// the stream failed, we cannot continue
+			return err
+		}
+		// read a combined message
+		msg := &combined{}
+		if err := json.Unmarshal(data, msg); err != nil {
+			// a badly formed message arrived, log it and continue
+			// we trust the stream to have isolated the error to just this message
+			for _, h := range c.handlers {
+				h.Error(runCtx, fmt.Errorf("unmarshal failed: %v", err))
+			}
+			continue
+		}
+		// work out which kind of message we have
+		switch {
+		case msg.Method != "":
+			// if method is set it must be a request
+			reqCtx, cancelReq := context.WithCancel(runCtx)
+			thisRequest := nextRequest
+			nextRequest = make(chan struct{})
+			req := &Request{
+				conn:        c,
+				cancel:      cancelReq,
+				nextRequest: nextRequest,
+				WireRequest: WireRequest{
+					VersionTag: msg.VersionTag,
+					Method:     msg.Method,
+					Params:     msg.Params,
+					ID:         msg.ID,
+				},
+			}
+			for _, h := range c.handlers {
+				reqCtx = h.Request(reqCtx, c, Receive, &req.WireRequest)
+				reqCtx = h.Read(reqCtx, n)
+			}
+			c.setHandling(req, true)
+			go func() {
+				<-thisRequest
+				req.state = requestSerial
+				defer func() {
+					c.setHandling(req, false)
+					if !req.IsNotify() && req.state < requestReplied {
+						req.Reply(reqCtx, nil, NewErrorf(CodeInternalError, "method %q did not reply", req.Method))
+					}
+					req.Parallel()
+					for _, h := range c.handlers {
+						h.Done(reqCtx, err)
+					}
+					cancelReq()
+				}()
+				delivered := false
+				for _, h := range c.handlers {
+					if h.Deliver(reqCtx, req, delivered) {
+						delivered = true
+					}
+				}
+			}()
+		case msg.ID != nil:
+			// we have a response, get the pending entry from the map
+			c.pendingMu.Lock()
+			rchan := c.pending[*msg.ID]
+			if rchan != nil {
+				delete(c.pending, *msg.ID)
+			}
+			c.pendingMu.Unlock()
+			// and send the reply to the channel
+			response := &WireResponse{
+				Result: msg.Result,
+				Error:  msg.Error,
+				ID:     msg.ID,
+			}
+			rchan <- response
+			close(rchan)
+		default:
+			for _, h := range c.handlers {
+				h.Error(runCtx, fmt.Errorf("message not a call, notify or response, ignoring"))
+			}
+		}
+	}
+}
+
+func marshalToRaw(obj interface{}) (*json.RawMessage, error) {
+	data, err := json.Marshal(obj)
+	if err != nil {
+		return nil, err
+	}
+	raw := json.RawMessage(data)
+	return &raw, nil
+}
diff --git a/utils/vscode/src/lsp/jsonrpc2/stream.go b/utils/vscode/src/lsp/jsonrpc2/stream.go
new file mode 100644
index 0000000..2c6ad6d
--- /dev/null
+++ b/utils/vscode/src/lsp/jsonrpc2/stream.go
@@ -0,0 +1,160 @@
+// Copyright 2018 The Go Authors.
+//
+// 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.
+
+package jsonrpc2
+
+import (
+	"bufio"
+	"context"
+	"encoding/json"
+	"fmt"
+	"io"
+	"strconv"
+	"strings"
+	"sync"
+)
+
+// Stream abstracts the transport mechanics from the JSON RPC protocol.
+// A Conn reads and writes messages using the stream it was provided on
+// construction, and assumes that each call to Read or Write fully transfers
+// a single message, or returns an error.
+type Stream interface {
+	// Read gets the next message from the stream.
+	// It is never called concurrently.
+	Read(context.Context) ([]byte, int64, error)
+	// Write sends a message to the stream.
+	// It must be safe for concurrent use.
+	Write(context.Context, []byte) (int64, error)
+}
+
+// NewStream returns a Stream built on top of an io.Reader and io.Writer
+// The messages are sent with no wrapping, and rely on json decode consistency
+// to determine message boundaries.
+func NewStream(in io.Reader, out io.Writer) Stream {
+	return &plainStream{
+		in:  json.NewDecoder(in),
+		out: out,
+	}
+}
+
+type plainStream struct {
+	in    *json.Decoder
+	outMu sync.Mutex
+	out   io.Writer
+}
+
+func (s *plainStream) Read(ctx context.Context) ([]byte, int64, error) {
+	select {
+	case <-ctx.Done():
+		return nil, 0, ctx.Err()
+	default:
+	}
+	var raw json.RawMessage
+	if err := s.in.Decode(&raw); err != nil {
+		return nil, 0, err
+	}
+	return raw, int64(len(raw)), nil
+}
+
+func (s *plainStream) Write(ctx context.Context, data []byte) (int64, error) {
+	select {
+	case <-ctx.Done():
+		return 0, ctx.Err()
+	default:
+	}
+	s.outMu.Lock()
+	n, err := s.out.Write(data)
+	s.outMu.Unlock()
+	return int64(n), err
+}
+
+// NewHeaderStream returns a Stream built on top of an io.Reader and io.Writer
+// The messages are sent with HTTP content length and MIME type headers.
+// This is the format used by LSP and others.
+func NewHeaderStream(in io.Reader, out io.Writer) Stream {
+	return &headerStream{
+		in:  bufio.NewReader(in),
+		out: out,
+	}
+}
+
+type headerStream struct {
+	in    *bufio.Reader
+	outMu sync.Mutex
+	out   io.Writer
+}
+
+func (s *headerStream) Read(ctx context.Context) ([]byte, int64, error) {
+	select {
+	case <-ctx.Done():
+		return nil, 0, ctx.Err()
+	default:
+	}
+	var total, length int64
+	// read the header, stop on the first empty line
+	for {
+		line, err := s.in.ReadString('\n')
+		total += int64(len(line))
+		if err != nil {
+			return nil, total, fmt.Errorf("failed reading header line %q", err)
+		}
+		line = strings.TrimSpace(line)
+		// check we have a header line
+		if line == "" {
+			break
+		}
+		colon := strings.IndexRune(line, ':')
+		if colon < 0 {
+			return nil, total, fmt.Errorf("invalid header line %q", line)
+		}
+		name, value := line[:colon], strings.TrimSpace(line[colon+1:])
+		switch name {
+		case "Content-Length":
+			if length, err = strconv.ParseInt(value, 10, 32); err != nil {
+				return nil, total, fmt.Errorf("failed parsing Content-Length: %v", value)
+			}
+			if length <= 0 {
+				return nil, total, fmt.Errorf("invalid Content-Length: %v", length)
+			}
+		default:
+			// ignoring unknown headers
+		}
+	}
+	if length == 0 {
+		return nil, total, fmt.Errorf("missing Content-Length header")
+	}
+	data := make([]byte, length)
+	if _, err := io.ReadFull(s.in, data); err != nil {
+		return nil, total, err
+	}
+	total += length
+	return data, total, nil
+}
+
+func (s *headerStream) Write(ctx context.Context, data []byte) (int64, error) {
+	select {
+	case <-ctx.Done():
+		return 0, ctx.Err()
+	default:
+	}
+	s.outMu.Lock()
+	defer s.outMu.Unlock()
+	n, err := fmt.Fprintf(s.out, "Content-Length: %v\r\n\r\n", len(data))
+	total := int64(n)
+	if err == nil {
+		n, err = s.out.Write(data)
+		total += int64(n)
+	}
+	return total, err
+}
diff --git a/utils/vscode/src/lsp/jsonrpc2/wire.go b/utils/vscode/src/lsp/jsonrpc2/wire.go
new file mode 100644
index 0000000..3e31c34
--- /dev/null
+++ b/utils/vscode/src/lsp/jsonrpc2/wire.go
@@ -0,0 +1,148 @@
+// Copyright 2018 The Go Authors.
+//
+// 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.
+
+package jsonrpc2
+
+import (
+	"encoding/json"
+	"fmt"
+	"strconv"
+)
+
+// this file contains the go forms of the wire specification
+// see http://www.jsonrpc.org/specification for details
+
+const (
+	// CodeUnknownError should be used for all non coded errors.
+	CodeUnknownError = -32001
+	// CodeParseError is used when invalid JSON was received by the server.
+	CodeParseError = -32700
+	//CodeInvalidRequest is used when the JSON sent is not a valid Request object.
+	CodeInvalidRequest = -32600
+	// CodeMethodNotFound should be returned by the handler when the method does
+	// not exist / is not available.
+	CodeMethodNotFound = -32601
+	// CodeInvalidParams should be returned by the handler when method
+	// parameter(s) were invalid.
+	CodeInvalidParams = -32602
+	// CodeInternalError is not currently returned but defined for completeness.
+	CodeInternalError = -32603
+
+	//CodeServerOverloaded is returned when a message was refused due to a
+	//server being temporarily unable to accept any new messages.
+	CodeServerOverloaded = -32000
+)
+
+// WireRequest is sent to a server to represent a Call or Notify operaton.
+type WireRequest struct {
+	// VersionTag is always encoded as the string "2.0"
+	VersionTag VersionTag `json:"jsonrpc"`
+	// Method is a string containing the method name to invoke.
+	Method string `json:"method"`
+	// Params is either a struct or an array with the parameters of the method.
+	Params *json.RawMessage `json:"params,omitempty"`
+	// The id of this request, used to tie the Response back to the request.
+	// Will be either a string or a number. If not set, the Request is a notify,
+	// and no response is possible.
+	ID *ID `json:"id,omitempty"`
+}
+
+// WireResponse is a reply to a Request.
+// It will always have the ID field set to tie it back to a request, and will
+// have either the Result or Error fields set depending on whether it is a
+// success or failure response.
+type WireResponse struct {
+	// VersionTag is always encoded as the string "2.0"
+	VersionTag VersionTag `json:"jsonrpc"`
+	// Result is the response value, and is required on success.
+	Result *json.RawMessage `json:"result,omitempty"`
+	// Error is a structured error response if the call fails.
+	Error *Error `json:"error,omitempty"`
+	// ID must be set and is the identifier of the Request this is a response to.
+	ID *ID `json:"id,omitempty"`
+}
+
+// Error represents a structured error in a Response.
+type Error struct {
+	// Code is an error code indicating the type of failure.
+	Code int64 `json:"code"`
+	// Message is a short description of the error.
+	Message string `json:"message"`
+	// Data is optional structured data containing additional information about the error.
+	Data *json.RawMessage `json:"data"`
+}
+
+// VersionTag is a special 0 sized struct that encodes as the jsonrpc version
+// tag.
+// It will fail during decode if it is not the correct version tag in the
+// stream.
+type VersionTag struct{}
+
+// ID is a Request identifier.
+// Only one of either the Name or Number members will be set, using the
+// number form if the Name is the empty string.
+type ID struct {
+	Name   string
+	Number int64
+}
+
+func (err *Error) Error() string {
+	if err == nil {
+		return ""
+	}
+	return err.Message
+}
+
+func (VersionTag) MarshalJSON() ([]byte, error) {
+	return json.Marshal("2.0")
+}
+
+func (VersionTag) UnmarshalJSON(data []byte) error {
+	version := ""
+	if err := json.Unmarshal(data, &version); err != nil {
+		return err
+	}
+	if version != "2.0" {
+		return fmt.Errorf("Invalid RPC version %v", version)
+	}
+	return nil
+}
+
+// String returns a string representation of the ID.
+// The representation is non ambiguous, string forms are quoted, number forms
+// are preceded by a #
+func (id *ID) String() string {
+	if id == nil {
+		return ""
+	}
+	if id.Name != "" {
+		return strconv.Quote(id.Name)
+	}
+	return "#" + strconv.FormatInt(id.Number, 10)
+}
+
+func (id *ID) MarshalJSON() ([]byte, error) {
+	if id.Name != "" {
+		return json.Marshal(id.Name)
+	}
+	return json.Marshal(id.Number)
+}
+
+func (id *ID) UnmarshalJSON(data []byte) error {
+	*id = ID{}
+	if err := json.Unmarshal(data, &id.Number); err == nil {
+		return nil
+	}
+	return json.Unmarshal(data, &id.Name)
+}
diff --git a/utils/vscode/src/lsp/protocol/context.go b/utils/vscode/src/lsp/protocol/context.go
new file mode 100644
index 0000000..7833d40
--- /dev/null
+++ b/utils/vscode/src/lsp/protocol/context.go
@@ -0,0 +1,29 @@
+// Copyright 2018 The Go Authors.
+//
+// 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.
+
+package protocol
+
+import (
+	"context"
+)
+
+type contextKey int
+
+const (
+	clientKey = contextKey(iota)
+)
+
+func WithClient(ctx context.Context, client Client) context.Context {
+	return context.WithValue(ctx, clientKey, client)
+}
diff --git a/utils/vscode/src/lsp/protocol/doc.go b/utils/vscode/src/lsp/protocol/doc.go
new file mode 100644
index 0000000..3c9198d
--- /dev/null
+++ b/utils/vscode/src/lsp/protocol/doc.go
@@ -0,0 +1,26 @@
+// Copyright 2018 The Go Authors.
+//
+// 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.
+
+// Package protocol contains the structs that map directly to the wire format
+// of the "Language Server Protocol".
+//
+// It is a literal transcription, with unmodified comments, and only the changes
+// required to make it go code.
+// Names are uppercased to export them.
+// All fields have JSON tags added to correct the names.
+// Fields marked with a ? are also marked as "omitempty"
+// Fields that are "|| null" are made pointers
+// Fields that are string or number are left as string
+// Fields that are type "number" are made float64
+package protocol
diff --git a/utils/vscode/src/lsp/protocol/enums.go b/utils/vscode/src/lsp/protocol/enums.go
new file mode 100644
index 0000000..db76ca3
--- /dev/null
+++ b/utils/vscode/src/lsp/protocol/enums.go
@@ -0,0 +1,256 @@
+// Copyright 2018 The Go Authors.
+//
+// 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.
+
+package protocol
+
+import (
+	"fmt"
+)
+
+var (
+	namesTextDocumentSyncKind   [int(Incremental) + 1]string
+	namesInitializeError        [int(UnknownProtocolVersion) + 1]string
+	namesMessageType            [int(Log) + 1]string
+	namesFileChangeType         [int(Deleted) + 1]string
+	namesWatchKind              [int(WatchDelete) + 1]string
+	namesCompletionTriggerKind  [int(TriggerForIncompleteCompletions) + 1]string
+	namesDiagnosticSeverity     [int(SeverityHint) + 1]string
+	namesDiagnosticTag          [int(Unnecessary) + 1]string
+	namesCompletionItemKind     [int(TypeParameterCompletion) + 1]string
+	namesInsertTextFormat       [int(SnippetTextFormat) + 1]string
+	namesDocumentHighlightKind  [int(Write) + 1]string
+	namesSymbolKind             [int(TypeParameter) + 1]string
+	namesTextDocumentSaveReason [int(FocusOut) + 1]string
+)
+
+func init() {
+	namesTextDocumentSyncKind[int(None)] = "None"
+	namesTextDocumentSyncKind[int(Full)] = "Full"
+	namesTextDocumentSyncKind[int(Incremental)] = "Incremental"
+
+	namesInitializeError[int(UnknownProtocolVersion)] = "UnknownProtocolVersion"
+
+	namesMessageType[int(Error)] = "Error"
+	namesMessageType[int(Warning)] = "Warning"
+	namesMessageType[int(Info)] = "Info"
+	namesMessageType[int(Log)] = "Log"
+
+	namesFileChangeType[int(Created)] = "Created"
+	namesFileChangeType[int(Changed)] = "Changed"
+	namesFileChangeType[int(Deleted)] = "Deleted"
+
+	namesWatchKind[int(WatchCreate)] = "WatchCreate"
+	namesWatchKind[int(WatchChange)] = "WatchChange"
+	namesWatchKind[int(WatchDelete)] = "WatchDelete"
+
+	namesCompletionTriggerKind[int(Invoked)] = "Invoked"
+	namesCompletionTriggerKind[int(TriggerCharacter)] = "TriggerCharacter"
+	namesCompletionTriggerKind[int(TriggerForIncompleteCompletions)] = "TriggerForIncompleteCompletions"
+
+	namesDiagnosticSeverity[int(SeverityError)] = "Error"
+	namesDiagnosticSeverity[int(SeverityWarning)] = "Warning"
+	namesDiagnosticSeverity[int(SeverityInformation)] = "Information"
+	namesDiagnosticSeverity[int(SeverityHint)] = "Hint"
+
+	namesDiagnosticTag[int(Unnecessary)] = "Unnecessary"
+
+	namesCompletionItemKind[int(TextCompletion)] = "text"
+	namesCompletionItemKind[int(MethodCompletion)] = "method"
+	namesCompletionItemKind[int(FunctionCompletion)] = "func"
+	namesCompletionItemKind[int(ConstructorCompletion)] = "constructor"
+	namesCompletionItemKind[int(FieldCompletion)] = "field"
+	namesCompletionItemKind[int(VariableCompletion)] = "var"
+	namesCompletionItemKind[int(ClassCompletion)] = "type"
+	namesCompletionItemKind[int(InterfaceCompletion)] = "interface"
+	namesCompletionItemKind[int(ModuleCompletion)] = "package"
+	namesCompletionItemKind[int(PropertyCompletion)] = "property"
+	namesCompletionItemKind[int(UnitCompletion)] = "unit"
+	namesCompletionItemKind[int(ValueCompletion)] = "value"
+	namesCompletionItemKind[int(EnumCompletion)] = "enum"
+	namesCompletionItemKind[int(KeywordCompletion)] = "keyword"
+	namesCompletionItemKind[int(SnippetCompletion)] = "snippet"
+	namesCompletionItemKind[int(ColorCompletion)] = "color"
+	namesCompletionItemKind[int(FileCompletion)] = "file"
+	namesCompletionItemKind[int(ReferenceCompletion)] = "reference"
+	namesCompletionItemKind[int(FolderCompletion)] = "folder"
+	namesCompletionItemKind[int(EnumMemberCompletion)] = "enumMember"
+	namesCompletionItemKind[int(ConstantCompletion)] = "const"
+	namesCompletionItemKind[int(StructCompletion)] = "struct"
+	namesCompletionItemKind[int(EventCompletion)] = "event"
+	namesCompletionItemKind[int(OperatorCompletion)] = "operator"
+	namesCompletionItemKind[int(TypeParameterCompletion)] = "typeParam"
+
+	namesInsertTextFormat[int(PlainTextTextFormat)] = "PlainText"
+	namesInsertTextFormat[int(SnippetTextFormat)] = "Snippet"
+
+	namesDocumentHighlightKind[int(Text)] = "Text"
+	namesDocumentHighlightKind[int(Read)] = "Read"
+	namesDocumentHighlightKind[int(Write)] = "Write"
+
+	namesSymbolKind[int(File)] = "File"
+	namesSymbolKind[int(Module)] = "Module"
+	namesSymbolKind[int(Namespace)] = "Namespace"
+	namesSymbolKind[int(Package)] = "Package"
+	namesSymbolKind[int(Class)] = "Class"
+	namesSymbolKind[int(Method)] = "Method"
+	namesSymbolKind[int(Property)] = "Property"
+	namesSymbolKind[int(Field)] = "Field"
+	namesSymbolKind[int(Constructor)] = "Constructor"
+	namesSymbolKind[int(Enum)] = "Enum"
+	namesSymbolKind[int(Interface)] = "Interface"
+	namesSymbolKind[int(Function)] = "Function"
+	namesSymbolKind[int(Variable)] = "Variable"
+	namesSymbolKind[int(Constant)] = "Constant"
+	namesSymbolKind[int(String)] = "String"
+	namesSymbolKind[int(Number)] = "Number"
+	namesSymbolKind[int(Boolean)] = "Boolean"
+	namesSymbolKind[int(Array)] = "Array"
+	namesSymbolKind[int(Object)] = "Object"
+	namesSymbolKind[int(Key)] = "Key"
+	namesSymbolKind[int(Null)] = "Null"
+	namesSymbolKind[int(EnumMember)] = "EnumMember"
+	namesSymbolKind[int(Struct)] = "Struct"
+	namesSymbolKind[int(Event)] = "Event"
+	namesSymbolKind[int(Operator)] = "Operator"
+	namesSymbolKind[int(TypeParameter)] = "TypeParameter"
+
+	namesTextDocumentSaveReason[int(Manual)] = "Manual"
+	namesTextDocumentSaveReason[int(AfterDelay)] = "AfterDelay"
+	namesTextDocumentSaveReason[int(FocusOut)] = "FocusOut"
+}
+
+func formatEnum(f fmt.State, c rune, i int, names []string, unknown string) {
+	s := ""
+	if i >= 0 && i < len(names) {
+		s = names[i]
+	}
+	if s != "" {
+		fmt.Fprint(f, s)
+	} else {
+		fmt.Fprintf(f, "%s(%d)", unknown, i)
+	}
+}
+
+func parseEnum(s string, names []string) int {
+	for i, name := range names {
+		if s == name {
+			return i
+		}
+	}
+	return 0
+}
+
+func (e TextDocumentSyncKind) Format(f fmt.State, c rune) {
+	formatEnum(f, c, int(e), namesTextDocumentSyncKind[:], "TextDocumentSyncKind")
+}
+
+func ParseTextDocumentSyncKind(s string) TextDocumentSyncKind {
+	return TextDocumentSyncKind(parseEnum(s, namesTextDocumentSyncKind[:]))
+}
+
+func (e InitializeError) Format(f fmt.State, c rune) {
+	formatEnum(f, c, int(e), namesInitializeError[:], "InitializeError")
+}
+
+func ParseInitializeError(s string) InitializeError {
+	return InitializeError(parseEnum(s, namesInitializeError[:]))
+}
+
+func (e MessageType) Format(f fmt.State, c rune) {
+	formatEnum(f, c, int(e), namesMessageType[:], "MessageType")
+}
+
+func ParseMessageType(s string) MessageType {
+	return MessageType(parseEnum(s, namesMessageType[:]))
+}
+
+func (e FileChangeType) Format(f fmt.State, c rune) {
+	formatEnum(f, c, int(e), namesFileChangeType[:], "FileChangeType")
+}
+
+func ParseFileChangeType(s string) FileChangeType {
+	return FileChangeType(parseEnum(s, namesFileChangeType[:]))
+}
+
+func (e WatchKind) Format(f fmt.State, c rune) {
+	formatEnum(f, c, int(e), namesWatchKind[:], "WatchKind")
+}
+
+func ParseWatchKind(s string) WatchKind {
+	return WatchKind(parseEnum(s, namesWatchKind[:]))
+}
+
+func (e CompletionTriggerKind) Format(f fmt.State, c rune) {
+	formatEnum(f, c, int(e), namesCompletionTriggerKind[:], "CompletionTriggerKind")
+}
+
+func ParseCompletionTriggerKind(s string) CompletionTriggerKind {
+	return CompletionTriggerKind(parseEnum(s, namesCompletionTriggerKind[:]))
+}
+
+func (e DiagnosticSeverity) Format(f fmt.State, c rune) {
+	formatEnum(f, c, int(e), namesDiagnosticSeverity[:], "DiagnosticSeverity")
+}
+
+func ParseDiagnosticSeverity(s string) DiagnosticSeverity {
+	return DiagnosticSeverity(parseEnum(s, namesDiagnosticSeverity[:]))
+}
+
+func (e DiagnosticTag) Format(f fmt.State, c rune) {
+	formatEnum(f, c, int(e), namesDiagnosticTag[:], "DiagnosticTag")
+}
+
+func ParseDiagnosticTag(s string) DiagnosticTag {
+	return DiagnosticTag(parseEnum(s, namesDiagnosticTag[:]))
+}
+
+func (e CompletionItemKind) Format(f fmt.State, c rune) {
+	formatEnum(f, c, int(e), namesCompletionItemKind[:], "CompletionItemKind")
+}
+
+func ParseCompletionItemKind(s string) CompletionItemKind {
+	return CompletionItemKind(parseEnum(s, namesCompletionItemKind[:]))
+}
+
+func (e InsertTextFormat) Format(f fmt.State, c rune) {
+	formatEnum(f, c, int(e), namesInsertTextFormat[:], "InsertTextFormat")
+}
+
+func ParseInsertTextFormat(s string) InsertTextFormat {
+	return InsertTextFormat(parseEnum(s, namesInsertTextFormat[:]))
+}
+
+func (e DocumentHighlightKind) Format(f fmt.State, c rune) {
+	formatEnum(f, c, int(e), namesDocumentHighlightKind[:], "DocumentHighlightKind")
+}
+
+func ParseDocumentHighlightKind(s string) DocumentHighlightKind {
+	return DocumentHighlightKind(parseEnum(s, namesDocumentHighlightKind[:]))
+}
+
+func (e SymbolKind) Format(f fmt.State, c rune) {
+	formatEnum(f, c, int(e), namesSymbolKind[:], "SymbolKind")
+}
+
+func ParseSymbolKind(s string) SymbolKind {
+	return SymbolKind(parseEnum(s, namesSymbolKind[:]))
+}
+
+func (e TextDocumentSaveReason) Format(f fmt.State, c rune) {
+	formatEnum(f, c, int(e), namesTextDocumentSaveReason[:], "TextDocumentSaveReason")
+}
+
+func ParseTextDocumentSaveReason(s string) TextDocumentSaveReason {
+	return TextDocumentSaveReason(parseEnum(s, namesTextDocumentSaveReason[:]))
+}
diff --git a/utils/vscode/src/lsp/protocol/log.go b/utils/vscode/src/lsp/protocol/log.go
new file mode 100644
index 0000000..f245881
--- /dev/null
+++ b/utils/vscode/src/lsp/protocol/log.go
@@ -0,0 +1,258 @@
+// Copyright 2018 The Go Authors.
+//
+// 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.
+
+package protocol
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"io"
+	"strings"
+	"sync"
+	"time"
+
+	"../jsonrpc2"
+)
+
+type loggingStream struct {
+	stream jsonrpc2.Stream
+	log    io.Writer
+}
+
+// LoggingStream returns a stream that does LSP protocol logging too
+func LoggingStream(str jsonrpc2.Stream, w io.Writer) jsonrpc2.Stream {
+	return &loggingStream{str, w}
+}
+
+func (s *loggingStream) Read(ctx context.Context) ([]byte, int64, error) {
+	data, count, err := s.stream.Read(ctx)
+	if err == nil {
+		logIn(s.log, data)
+	}
+	return data, count, err
+}
+
+func (s *loggingStream) Write(ctx context.Context, data []byte) (int64, error) {
+	logOut(s.log, data)
+	count, err := s.stream.Write(ctx, data)
+	return count, err
+}
+
+// Combined has all the fields of both Request and Response.
+// We can decode this and then work out which it is.
+type Combined struct {
+	VersionTag jsonrpc2.VersionTag `json:"jsonrpc"`
+	ID         *jsonrpc2.ID        `json:"id,omitempty"`
+	Method     string              `json:"method"`
+	Params     *json.RawMessage    `json:"params,omitempty"`
+	Result     *json.RawMessage    `json:"result,omitempty"`
+	Error      *jsonrpc2.Error     `json:"error,omitempty"`
+}
+
+type req struct {
+	method string
+	start  time.Time
+}
+
+type mapped struct {
+	mu          sync.Mutex
+	clientCalls map[string]req
+	serverCalls map[string]req
+}
+
+var maps = &mapped{
+	sync.Mutex{},
+	make(map[string]req),
+	make(map[string]req),
+}
+
+// these 4 methods are each used exactly once, but it seemed
+// better to have the encapsulation rather than ad hoc mutex
+// code in 4 places
+func (m *mapped) client(id string, del bool) req {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	v := m.clientCalls[id]
+	if del {
+		delete(m.clientCalls, id)
+	}
+	return v
+}
+
+func (m *mapped) server(id string, del bool) req {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	v := m.serverCalls[id]
+	if del {
+		delete(m.serverCalls, id)
+	}
+	return v
+}
+
+func (m *mapped) setClient(id string, r req) {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	m.clientCalls[id] = r
+}
+
+func (m *mapped) setServer(id string, r req) {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	m.serverCalls[id] = r
+}
+
+const eor = "\r\n\r\n\r\n"
+
+func strID(x *jsonrpc2.ID) string {
+	if x == nil {
+		// should never happen, but we need a number
+		return "999999999"
+	}
+	if x.Name != "" {
+		return x.Name
+	}
+	return fmt.Sprintf("%d", x.Number)
+}
+
+func logCommon(outfd io.Writer, data []byte) (*Combined, time.Time, string) {
+	if outfd == nil {
+		return nil, time.Time{}, ""
+	}
+	var v Combined
+	err := json.Unmarshal(data, &v)
+	if err != nil {
+		fmt.Fprintf(outfd, "Unmarshal %v\n", err)
+		panic(err) // do better
+	}
+	tm := time.Now()
+	tmfmt := tm.Format("15:04:05.000 PM")
+	return &v, tm, tmfmt
+}
+
+// logOut and logIn could be combined. "received"<->"Sending", serverCalls<->clientCalls
+// but it wouldn't be a lot shorter or clearer and "shutdown" is a special case
+
+// Writing a message to the client, log it
+func logOut(outfd io.Writer, data []byte) {
+	v, tm, tmfmt := logCommon(outfd, data)
+	if v == nil {
+		return
+	}
+	if v.Error != nil {
+		id := strID(v.ID)
+		fmt.Fprintf(outfd, "[Error - %s] Received #%s %s%s", tmfmt, id, v.Error, eor)
+		return
+	}
+	buf := strings.Builder{}
+	id := strID(v.ID)
+	fmt.Fprintf(&buf, "[Trace - %s] ", tmfmt) // common beginning
+	if v.ID != nil && v.Method != "" && v.Params != nil {
+		fmt.Fprintf(&buf, "Received request '%s - (%s)'.\n", v.Method, id)
+		fmt.Fprintf(&buf, "Params: %s%s", *v.Params, eor)
+		maps.setServer(id, req{method: v.Method, start: tm})
+	} else if v.ID != nil && v.Method == "" && v.Params == nil {
+		cc := maps.client(id, true)
+		elapsed := tm.Sub(cc.start)
+		fmt.Fprintf(&buf, "Received response '%s - (%s)' in %dms.\n",
+			cc.method, id, elapsed/time.Millisecond)
+		if v.Result == nil {
+			fmt.Fprintf(&buf, "Result: {}%s", eor)
+		} else {
+			fmt.Fprintf(&buf, "Result: %s%s", string(*v.Result), eor)
+		}
+	} else if v.ID == nil && v.Method != "" && v.Params != nil {
+		p := "null"
+		if v.Params != nil {
+			p = string(*v.Params)
+		}
+		fmt.Fprintf(&buf, "Received notification '%s'.\n", v.Method)
+		fmt.Fprintf(&buf, "Params: %s%s", p, eor)
+	} else { // for completeness, as it should never happen
+		buf = strings.Builder{} // undo common Trace
+		fmt.Fprintf(&buf, "[Error - %s] on write ID?%v method:%q Params:%v Result:%v Error:%v%s",
+			tmfmt, v.ID != nil, v.Method, v.Params != nil,
+			v.Result != nil, v.Error != nil, eor)
+		p := "null"
+		if v.Params != nil {
+			p = string(*v.Params)
+		}
+		r := "null"
+		if v.Result != nil {
+			r = string(*v.Result)
+		}
+		fmt.Fprintf(&buf, "%s\n%s\n%s%s", p, r, v.Error, eor)
+	}
+	outfd.Write([]byte(buf.String()))
+}
+
+// Got a message from the client, log it
+func logIn(outfd io.Writer, data []byte) {
+	v, tm, tmfmt := logCommon(outfd, data)
+	if v == nil {
+		return
+	}
+	// ID Method Params => Sending request
+	// ID !Method Result(might be null, but !Params) => Sending response (could we get an Error?)
+	// !ID Method Params => Sending notification
+	if v.Error != nil { // does this ever happen?
+		id := strID(v.ID)
+		fmt.Fprintf(outfd, "[Error - %s] Sent #%s %s%s", tmfmt, id, v.Error, eor)
+		return
+	}
+	buf := strings.Builder{}
+	id := strID(v.ID)
+	fmt.Fprintf(&buf, "[Trace - %s] ", tmfmt) // common beginning
+	if v.ID != nil && v.Method != "" && (v.Params != nil || v.Method == "shutdown") {
+		fmt.Fprintf(&buf, "Sending request '%s - (%s)'.\n", v.Method, id)
+		x := "{}"
+		if v.Params != nil {
+			x = string(*v.Params)
+		}
+		fmt.Fprintf(&buf, "Params: %s%s", x, eor)
+		maps.setClient(id, req{method: v.Method, start: tm})
+	} else if v.ID != nil && v.Method == "" && v.Params == nil {
+		sc := maps.server(id, true)
+		elapsed := tm.Sub(sc.start)
+		fmt.Fprintf(&buf, "Sending response '%s - (%s)' took %dms.\n",
+			sc.method, id, elapsed/time.Millisecond)
+		if v.Result == nil {
+			fmt.Fprintf(&buf, "Result: {}%s", eor)
+		} else {
+			fmt.Fprintf(&buf, "Result: %s%s", string(*v.Result), eor)
+		}
+	} else if v.ID == nil && v.Method != "" {
+		p := "null"
+		if v.Params != nil {
+			p = string(*v.Params)
+		}
+		fmt.Fprintf(&buf, "Sending notification '%s'.\n", v.Method)
+		fmt.Fprintf(&buf, "Params: %s%s", p, eor)
+	} else { // for completeness, as it should never happen
+		buf = strings.Builder{} // undo common Trace
+		fmt.Fprintf(&buf, "[Error - %s] on read ID?%v method:%q Params:%v Result:%v Error:%v%s",
+			tmfmt, v.ID != nil, v.Method, v.Params != nil,
+			v.Result != nil, v.Error != nil, eor)
+		p := "null"
+		if v.Params != nil {
+			p = string(*v.Params)
+		}
+		r := "null"
+		if v.Result != nil {
+			r = string(*v.Result)
+		}
+		fmt.Fprintf(&buf, "%s\n%s\n%s%s", p, r, v.Error, eor)
+	}
+	outfd.Write([]byte(buf.String()))
+}
diff --git a/utils/vscode/src/lsp/protocol/protocol.go b/utils/vscode/src/lsp/protocol/protocol.go
new file mode 100644
index 0000000..e396c83
--- /dev/null
+++ b/utils/vscode/src/lsp/protocol/protocol.go
@@ -0,0 +1,86 @@
+// Copyright 2018 The Go Authors.
+//
+// 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.
+
+package protocol
+
+import (
+	"context"
+	"encoding/json"
+	"log"
+
+	"../jsonrpc2"
+)
+
+const (
+	// RequestCancelledError should be used when a request is cancelled early.
+	RequestCancelledError = -32800
+)
+
+type DocumentUri = string
+
+type canceller struct{ jsonrpc2.EmptyHandler }
+
+type clientHandler struct {
+	canceller
+	client Client
+}
+
+type serverHandler struct {
+	canceller
+	server Server
+}
+
+func (canceller) Request(ctx context.Context, conn *jsonrpc2.Conn, direction jsonrpc2.Direction, r *jsonrpc2.WireRequest) context.Context {
+	if direction == jsonrpc2.Receive && r.Method == "$/cancelRequest" {
+		var params CancelParams
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			log.Printf("%v", err)
+		} else {
+			conn.Cancel(params.ID)
+		}
+	}
+	return ctx
+}
+
+func (canceller) Cancel(ctx context.Context, conn *jsonrpc2.Conn, id jsonrpc2.ID, cancelled bool) bool {
+	if cancelled {
+		return false
+	}
+	conn.Notify(ctx, "$/cancelRequest", &CancelParams{ID: id})
+	return true
+}
+
+func NewClient(ctx context.Context, stream jsonrpc2.Stream, client Client) (context.Context, *jsonrpc2.Conn, Server) {
+	ctx = WithClient(ctx, client)
+	conn := jsonrpc2.NewConn(stream)
+	conn.AddHandler(&clientHandler{client: client})
+	return ctx, conn, &serverDispatcher{Conn: conn}
+}
+
+func NewServer(ctx context.Context, stream jsonrpc2.Stream, server Server) (context.Context, *jsonrpc2.Conn, Client) {
+	conn := jsonrpc2.NewConn(stream)
+	client := &clientDispatcher{Conn: conn}
+	ctx = WithClient(ctx, client)
+	conn.AddHandler(&serverHandler{server: server})
+	return ctx, conn, client
+}
+
+func sendParseError(ctx context.Context, req *jsonrpc2.Request, err error) {
+	if _, ok := err.(*jsonrpc2.Error); !ok {
+		err = jsonrpc2.NewErrorf(jsonrpc2.CodeParseError, "%v", err)
+	}
+	if err := req.Reply(ctx, nil, err); err != nil {
+		log.Printf("%v", err)
+	}
+}
diff --git a/utils/vscode/src/lsp/protocol/span.go b/utils/vscode/src/lsp/protocol/span.go
new file mode 100644
index 0000000..33cc2a6
--- /dev/null
+++ b/utils/vscode/src/lsp/protocol/span.go
@@ -0,0 +1,137 @@
+// Copyright 2018 The Go Authors.
+//
+// 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.
+
+// this file contains protocol<->span converters
+
+package protocol
+
+import (
+	"fmt"
+
+	"../span"
+	errors "golang.org/x/xerrors"
+)
+
+type ColumnMapper struct {
+	URI       span.URI
+	Converter *span.TokenConverter
+	Content   []byte
+}
+
+func NewURI(uri span.URI) string {
+	return string(uri)
+}
+
+func (m *ColumnMapper) Location(s span.Span) (Location, error) {
+	rng, err := m.Range(s)
+	if err != nil {
+		return Location{}, err
+	}
+	return Location{URI: NewURI(s.URI()), Range: rng}, nil
+}
+
+func (m *ColumnMapper) Range(s span.Span) (Range, error) {
+	if span.CompareURI(m.URI, s.URI()) != 0 {
+		return Range{}, errors.Errorf("column mapper is for file %q instead of %q", m.URI, s.URI())
+	}
+	s, err := s.WithAll(m.Converter)
+	if err != nil {
+		return Range{}, err
+	}
+	start, err := m.Position(s.Start())
+	if err != nil {
+		return Range{}, err
+	}
+	end, err := m.Position(s.End())
+	if err != nil {
+		return Range{}, err
+	}
+	return Range{Start: start, End: end}, nil
+}
+
+func (m *ColumnMapper) Position(p span.Point) (Position, error) {
+	chr, err := span.ToUTF16Column(p, m.Content)
+	if err != nil {
+		return Position{}, err
+	}
+	return Position{
+		Line:      float64(p.Line() - 1),
+		Character: float64(chr - 1),
+	}, nil
+}
+
+func (m *ColumnMapper) Span(l Location) (span.Span, error) {
+	return m.RangeSpan(l.Range)
+}
+
+func (m *ColumnMapper) RangeSpan(r Range) (span.Span, error) {
+	start, err := m.Point(r.Start)
+	if err != nil {
+		return span.Span{}, err
+	}
+	end, err := m.Point(r.End)
+	if err != nil {
+		return span.Span{}, err
+	}
+	return span.New(m.URI, start, end).WithAll(m.Converter)
+}
+
+func (m *ColumnMapper) PointSpan(p Position) (span.Span, error) {
+	start, err := m.Point(p)
+	if err != nil {
+		return span.Span{}, err
+	}
+	return span.New(m.URI, start, start).WithAll(m.Converter)
+}
+
+func (m *ColumnMapper) Point(p Position) (span.Point, error) {
+	line := int(p.Line) + 1
+	offset, err := m.Converter.ToOffset(line, 1)
+	if err != nil {
+		return span.Point{}, err
+	}
+	lineStart := span.NewPoint(line, 1, offset)
+	return span.FromUTF16Column(lineStart, int(p.Character)+1, m.Content)
+}
+
+func IsPoint(r Range) bool {
+	return r.Start.Line == r.End.Line && r.Start.Character == r.End.Character
+}
+
+func CompareRange(a, b Range) int {
+	if r := ComparePosition(a.Start, b.Start); r != 0 {
+		return r
+	}
+	return ComparePosition(a.End, b.End)
+}
+
+func ComparePosition(a, b Position) int {
+	if a.Line < b.Line {
+		return -1
+	}
+	if a.Line > b.Line {
+		return 1
+	}
+	if a.Character < b.Character {
+		return -1
+	}
+	if a.Character > b.Character {
+		return 1
+	}
+	return 0
+}
+
+func (r Range) Format(f fmt.State, _ rune) {
+	fmt.Fprintf(f, "%v:%v-%v:%v", r.Start.Line, r.Start.Character, r.End.Line, r.End.Character)
+}
diff --git a/utils/vscode/src/lsp/protocol/tsclient.go b/utils/vscode/src/lsp/protocol/tsclient.go
new file mode 100644
index 0000000..2f9beef
--- /dev/null
+++ b/utils/vscode/src/lsp/protocol/tsclient.go
@@ -0,0 +1,221 @@
+// Copyright 2019 The Go Authors.
+//
+// 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.
+
+package protocol
+
+import (
+	"context"
+	"encoding/json"
+	"log"
+
+	"../jsonrpc2"
+)
+
+type Client interface {
+	ShowMessage(context.Context, *ShowMessageParams) error
+	LogMessage(context.Context, *LogMessageParams) error
+	Event(context.Context, *interface{}) error
+	PublishDiagnostics(context.Context, *PublishDiagnosticsParams) error
+	WorkspaceFolders(context.Context) ([]WorkspaceFolder, error)
+	Configuration(context.Context, *ParamConfig) ([]interface{}, error)
+	RegisterCapability(context.Context, *RegistrationParams) error
+	UnregisterCapability(context.Context, *UnregistrationParams) error
+	ShowMessageRequest(context.Context, *ShowMessageRequestParams) (*MessageActionItem, error)
+	ApplyEdit(context.Context, *ApplyWorkspaceEditParams) (*ApplyWorkspaceEditResponse, error)
+}
+
+func (h clientHandler) Deliver(ctx context.Context, r *jsonrpc2.Request, delivered bool) bool {
+	if delivered {
+		return false
+	}
+	if ctx.Err() != nil {
+		r.Reply(ctx, nil, jsonrpc2.NewErrorf(RequestCancelledError, ""))
+		return true
+	}
+	switch r.Method {
+	case "window/showMessage": // notif
+		var params ShowMessageParams
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, r, err)
+			return true
+		}
+		if err := h.client.ShowMessage(ctx, &params); err != nil {
+			log.Printf("%v", err)
+		}
+		return true
+	case "window/logMessage": // notif
+		var params LogMessageParams
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, r, err)
+			return true
+		}
+		if err := h.client.LogMessage(ctx, &params); err != nil {
+			log.Printf("%v", err)
+		}
+		return true
+	case "telemetry/event": // notif
+		var params interface{}
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, r, err)
+			return true
+		}
+		if err := h.client.Event(ctx, &params); err != nil {
+			log.Printf("%v", err)
+		}
+		return true
+	case "textDocument/publishDiagnostics": // notif
+		var params PublishDiagnosticsParams
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, r, err)
+			return true
+		}
+		if err := h.client.PublishDiagnostics(ctx, &params); err != nil {
+			log.Printf("%v", err)
+		}
+		return true
+	case "workspace/workspaceFolders": // req
+		if r.Params != nil {
+			r.Reply(ctx, nil, jsonrpc2.NewErrorf(jsonrpc2.CodeInvalidParams, "Expected no params"))
+			return true
+		}
+		resp, err := h.client.WorkspaceFolders(ctx)
+		if err := r.Reply(ctx, resp, err); err != nil {
+			log.Printf("%v", err)
+		}
+		return true
+	case "workspace/configuration": // req
+		var params ParamConfig
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, r, err)
+			return true
+		}
+		resp, err := h.client.Configuration(ctx, &params)
+		if err := r.Reply(ctx, resp, err); err != nil {
+			log.Printf("%v", err)
+		}
+		return true
+	case "client/registerCapability": // req
+		var params RegistrationParams
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, r, err)
+			return true
+		}
+		err := h.client.RegisterCapability(ctx, &params)
+		if err := r.Reply(ctx, nil, err); err != nil {
+			log.Printf("%v", err)
+		}
+		return true
+	case "client/unregisterCapability": // req
+		var params UnregistrationParams
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, r, err)
+			return true
+		}
+		err := h.client.UnregisterCapability(ctx, &params)
+		if err := r.Reply(ctx, nil, err); err != nil {
+			log.Printf("%v", err)
+		}
+		return true
+	case "window/showMessageRequest": // req
+		var params ShowMessageRequestParams
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, r, err)
+			return true
+		}
+		resp, err := h.client.ShowMessageRequest(ctx, &params)
+		if err := r.Reply(ctx, resp, err); err != nil {
+			log.Printf("%v", err)
+		}
+		return true
+	case "workspace/applyEdit": // req
+		var params ApplyWorkspaceEditParams
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, r, err)
+			return true
+		}
+		resp, err := h.client.ApplyEdit(ctx, &params)
+		if err := r.Reply(ctx, resp, err); err != nil {
+			log.Printf("%v", err)
+		}
+		return true
+
+	default:
+		return false
+	}
+}
+
+type clientDispatcher struct {
+	*jsonrpc2.Conn
+}
+
+func (s *clientDispatcher) ShowMessage(ctx context.Context, params *ShowMessageParams) error {
+	return s.Conn.Notify(ctx, "window/showMessage", params)
+}
+
+func (s *clientDispatcher) LogMessage(ctx context.Context, params *LogMessageParams) error {
+	return s.Conn.Notify(ctx, "window/logMessage", params)
+}
+
+func (s *clientDispatcher) Event(ctx context.Context, params *interface{}) error {
+	return s.Conn.Notify(ctx, "telemetry/event", params)
+}
+
+func (s *clientDispatcher) PublishDiagnostics(ctx context.Context, params *PublishDiagnosticsParams) error {
+	return s.Conn.Notify(ctx, "textDocument/publishDiagnostics", params)
+}
+func (s *clientDispatcher) WorkspaceFolders(ctx context.Context) ([]WorkspaceFolder, error) {
+	var result []WorkspaceFolder
+	if err := s.Conn.Call(ctx, "workspace/workspaceFolders", nil, &result); err != nil {
+		return nil, err
+	}
+	return result, nil
+}
+
+func (s *clientDispatcher) Configuration(ctx context.Context, params *ParamConfig) ([]interface{}, error) {
+	var result []interface{}
+	if err := s.Conn.Call(ctx, "workspace/configuration", params, &result); err != nil {
+		return nil, err
+	}
+	return result, nil
+}
+
+func (s *clientDispatcher) RegisterCapability(ctx context.Context, params *RegistrationParams) error {
+	return s.Conn.Call(ctx, "client/registerCapability", params, nil) // Call, not Notify
+}
+
+func (s *clientDispatcher) UnregisterCapability(ctx context.Context, params *UnregistrationParams) error {
+	return s.Conn.Call(ctx, "client/unregisterCapability", params, nil) // Call, not Notify
+}
+
+func (s *clientDispatcher) ShowMessageRequest(ctx context.Context, params *ShowMessageRequestParams) (*MessageActionItem, error) {
+	var result MessageActionItem
+	if err := s.Conn.Call(ctx, "window/showMessageRequest", params, &result); err != nil {
+		return nil, err
+	}
+	return &result, nil
+}
+
+func (s *clientDispatcher) ApplyEdit(ctx context.Context, params *ApplyWorkspaceEditParams) (*ApplyWorkspaceEditResponse, error) {
+	var result ApplyWorkspaceEditResponse
+	if err := s.Conn.Call(ctx, "workspace/applyEdit", params, &result); err != nil {
+		return nil, err
+	}
+	return &result, nil
+}
+
+// Types constructed to avoid structs as formal argument types
+type ParamConfig struct {
+	ConfigurationParams
+	PartialResultParams
+}
diff --git a/utils/vscode/src/lsp/protocol/tsprotocol.go b/utils/vscode/src/lsp/protocol/tsprotocol.go
new file mode 100644
index 0000000..50543fc
--- /dev/null
+++ b/utils/vscode/src/lsp/protocol/tsprotocol.go
@@ -0,0 +1,4630 @@
+// Copyright 2019 The Go Authors.
+//
+// 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.
+
+// Package protocol contains data types and code for LSP jsonrpcs
+// generated automatically from vscode-languageserver-node
+// commit: 36ac51f057215e6e2e0408384e07ecf564a938da
+// last fetched Tue Sep 24 2019 17:44:28 GMT-0400 (Eastern Daylight Time)
+package protocol
+
+// Code generated (see typescript/README.md) DO NOT EDIT.
+
+/*ImplementationClientCapabilities defined:
+ * Since 3.6.0
+ */
+type ImplementationClientCapabilities struct {
+
+	/*DynamicRegistration defined:
+	 * Whether implementation supports dynamic registration. If this is set to `true`
+	 * the client supports the new `ImplementationRegistrationOptions` return value
+	 * for the corresponding server capability as well.
+	 */
+	DynamicRegistration bool `json:"dynamicRegistration,omitempty"`
+
+	/*LinkSupport defined:
+	 * The client supports additional metadata in the form of definition links.
+	 *
+	 * Since 3.14.0
+	 */
+	LinkSupport bool `json:"linkSupport,omitempty"`
+}
+
+// ImplementationOptions is
+type ImplementationOptions struct {
+	WorkDoneProgressOptions
+}
+
+// ImplementationRegistrationOptions is
+type ImplementationRegistrationOptions struct {
+	TextDocumentRegistrationOptions
+	ImplementationOptions
+	StaticRegistrationOptions
+}
+
+// ImplementationParams is
+type ImplementationParams struct {
+	TextDocumentPositionParams
+	WorkDoneProgressParams
+	PartialResultParams
+}
+
+/*TypeDefinitionClientCapabilities defined:
+ * Since 3.6.0
+ */
+type TypeDefinitionClientCapabilities struct {
+
+	/*DynamicRegistration defined:
+	 * Whether implementation supports dynamic registration. If this is set to `true`
+	 * the client supports the new `TypeDefinitionRegistrationOptions` return value
+	 * for the corresponding server capability as well.
+	 */
+	DynamicRegistration bool `json:"dynamicRegistration,omitempty"`
+
+	/*LinkSupport defined:
+	 * The client supports additional metadata in the form of definition links.
+	 *
+	 * Since 3.14.0
+	 */
+	LinkSupport bool `json:"linkSupport,omitempty"`
+}
+
+// TypeDefinitionOptions is
+type TypeDefinitionOptions struct {
+	WorkDoneProgressOptions
+}
+
+// TypeDefinitionRegistrationOptions is
+type TypeDefinitionRegistrationOptions struct {
+	TextDocumentRegistrationOptions
+	TypeDefinitionOptions
+	StaticRegistrationOptions
+}
+
+// TypeDefinitionParams is
+type TypeDefinitionParams struct {
+	TextDocumentPositionParams
+	WorkDoneProgressParams
+	PartialResultParams
+}
+
+// WorkspaceFoldersInitializeParams is
+type WorkspaceFoldersInitializeParams struct {
+
+	/*WorkspaceFolders defined:
+	 * The actual configured workspace folders.
+	 */
+	WorkspaceFolders []WorkspaceFolder `json:"workspaceFolders"`
+}
+
+// WorkspaceFoldersClientCapabilities is
+type WorkspaceFoldersClientCapabilities struct {
+
+	/*Workspace defined:
+	 * The workspace client capabilities
+	 */
+	Workspace *struct {
+
+		/*WorkspaceFolders defined:
+		 * The client has support for workspace folders
+		 */
+		WorkspaceFolders bool `json:"workspaceFolders,omitempty"`
+	} `json:"workspace,omitempty"`
+}
+
+// WorkspaceFoldersServerCapabilities is
+type WorkspaceFoldersServerCapabilities struct {
+
+	/*Workspace defined:
+	 * The workspace server capabilities
+	 */
+	Workspace *struct {
+
+		// WorkspaceFolders is
+		WorkspaceFolders *struct {
+
+			/*Supported defined:
+			 * The Server has support for workspace folders
+			 */
+			Supported bool `json:"supported,omitempty"`
+
+			/*ChangeNotifications defined:
+			 * Whether the server wants to receive workspace folder
+			 * change notifications.
+			 *
+			 * If a strings is provided the string is treated as a ID
+			 * under which the notification is registed on the client
+			 * side. The ID can be used to unregister for these events
+			 * using the `client/unregisterCapability` request.
+			 */
+			ChangeNotifications string `json:"changeNotifications,omitempty"` // string | boolean
+		} `json:"workspaceFolders,omitempty"`
+	} `json:"workspace,omitempty"`
+}
+
+// WorkspaceFolder is
+type WorkspaceFolder struct {
+
+	/*URI defined:
+	 * The associated URI for this workspace folder.
+	 */
+	URI string `json:"uri"`
+
+	/*Name defined:
+	 * The name of the workspace folder. Used to refer to this
+	 * workspace folder in thge user interface.
+	 */
+	Name string `json:"name"`
+}
+
+/*DidChangeWorkspaceFoldersParams defined:
+ * The parameters of a `workspace/didChangeWorkspaceFolders` notification.
+ */
+type DidChangeWorkspaceFoldersParams struct {
+
+	/*Event defined:
+	 * The actual workspace folder change event.
+	 */
+	Event WorkspaceFoldersChangeEvent `json:"event"`
+}
+
+/*WorkspaceFoldersChangeEvent defined:
+ * The workspace folder change event.
+ */
+type WorkspaceFoldersChangeEvent struct {
+
+	/*Added defined:
+	 * The array of added workspace folders
+	 */
+	Added []WorkspaceFolder `json:"added"`
+
+	/*Removed defined:
+	 * The array of the removed workspace folders
+	 */
+	Removed []WorkspaceFolder `json:"removed"`
+}
+
+// ConfigurationClientCapabilities is
+type ConfigurationClientCapabilities struct {
+
+	/*Workspace defined:
+	 * The workspace client capabilities
+	 */
+	Workspace *struct {
+
+		/*Configuration defined:
+		* The client supports `workspace/configuration` requests.
+		 */
+		Configuration bool `json:"configuration,omitempty"`
+	} `json:"workspace,omitempty"`
+}
+
+// ConfigurationItem is
+type ConfigurationItem struct {
+
+	/*ScopeURI defined:
+	 * The scope to get the configuration section for.
+	 */
+	ScopeURI string `json:"scopeUri,omitempty"`
+
+	/*Section defined:
+	 * The configuration section asked for.
+	 */
+	Section string `json:"section,omitempty"`
+}
+
+/*ConfigurationParams defined:
+ * The parameters of a configuration request.
+ */
+type ConfigurationParams struct {
+
+	// Items is
+	Items []ConfigurationItem `json:"items"`
+}
+
+// DocumentColorClientCapabilities is
+type DocumentColorClientCapabilities struct {
+
+	/*DynamicRegistration defined:
+	 * Whether implementation supports dynamic registration. If this is set to `true`
+	 * the client supports the new `DocumentColorRegistrationOptions` return value
+	 * for the corresponding server capability as well.
+	 */
+	DynamicRegistration bool `json:"dynamicRegistration,omitempty"`
+}
+
+// DocumentColorOptions is
+type DocumentColorOptions struct {
+
+	/*ResolveProvider defined:
+	 * Code lens has a resolve provider as well.
+	 */
+	ResolveProvider bool `json:"resolveProvider,omitempty"`
+	WorkDoneProgressOptions
+}
+
+// DocumentColorRegistrationOptions is
+type DocumentColorRegistrationOptions struct {
+	TextDocumentRegistrationOptions
+	StaticRegistrationOptions
+	DocumentColorOptions
+}
+
+/*DocumentColorParams defined:
+ * Parameters for a [DocumentColorRequest](#DocumentColorRequest).
+ */
+type DocumentColorParams struct {
+
+	/*TextDocument defined:
+	 * The text document.
+	 */
+	TextDocument TextDocumentIdentifier `json:"textDocument"`
+	WorkDoneProgressParams
+	PartialResultParams
+}
+
+/*ColorPresentationParams defined:
+ * Parameters for a [ColorPresentationRequest](#ColorPresentationRequest).
+ */
+type ColorPresentationParams struct {
+
+	/*TextDocument defined:
+	 * The text document.
+	 */
+	TextDocument TextDocumentIdentifier `json:"textDocument"`
+
+	/*Color defined:
+	 * The color to request presentations for.
+	 */
+	Color Color `json:"color"`
+
+	/*Range defined:
+	 * The range where the color would be inserted. Serves as a context.
+	 */
+	Range Range `json:"range"`
+	WorkDoneProgressParams
+	PartialResultParams
+}
+
+// FoldingRangeClientCapabilities is
+type FoldingRangeClientCapabilities struct {
+
+	/*DynamicRegistration defined:
+	 * Whether implementation supports dynamic registration for folding range providers. If this is set to `true`
+	 * the client supports the new `FoldingRangeRegistrationOptions` return value for the corresponding server
+	 * capability as well.
+	 */
+	DynamicRegistration bool `json:"dynamicRegistration,omitempty"`
+
+	/*RangeLimit defined:
+	 * The maximum number of folding ranges that the client prefers to receive per document. The value serves as a
+	 * hint, servers are free to follow the limit.
+	 */
+	RangeLimit float64 `json:"rangeLimit,omitempty"`
+
+	/*LineFoldingOnly defined:
+	 * If set, the client signals that it only supports folding complete lines. If set, client will
+	 * ignore specified `startCharacter` and `endCharacter` properties in a FoldingRange.
+	 */
+	LineFoldingOnly bool `json:"lineFoldingOnly,omitempty"`
+}
+
+// FoldingRangeOptions is
+type FoldingRangeOptions struct {
+	WorkDoneProgressOptions
+}
+
+// FoldingRangeRegistrationOptions is
+type FoldingRangeRegistrationOptions struct {
+	TextDocumentRegistrationOptions
+	FoldingRangeOptions
+	StaticRegistrationOptions
+}
+
+/*FoldingRange defined:
+ * Represents a folding range.
+ */
+type FoldingRange struct {
+
+	/*StartLine defined:
+	 * The zero-based line number from where the folded range starts.
+	 */
+	StartLine float64 `json:"startLine"`
+
+	/*StartCharacter defined:
+	 * The zero-based character offset from where the folded range starts. If not defined, defaults to the length of the start line.
+	 */
+	StartCharacter float64 `json:"startCharacter,omitempty"`
+
+	/*EndLine defined:
+	 * The zero-based line number where the folded range ends.
+	 */
+	EndLine float64 `json:"endLine"`
+
+	/*EndCharacter defined:
+	 * The zero-based character offset before the folded range ends. If not defined, defaults to the length of the end line.
+	 */
+	EndCharacter float64 `json:"endCharacter,omitempty"`
+
+	/*Kind defined:
+	 * Describes the kind of the folding range such as `comment' or 'region'. The kind
+	 * is used to categorize folding ranges and used by commands like 'Fold all comments'. See
+	 * [FoldingRangeKind](#FoldingRangeKind) for an enumeration of standardized kinds.
+	 */
+	Kind string `json:"kind,omitempty"`
+}
+
+/*FoldingRangeParams defined:
+ * Parameters for a [FoldingRangeRequest](#FoldingRangeRequest).
+ */
+type FoldingRangeParams struct {
+
+	/*TextDocument defined:
+	 * The text document.
+	 */
+	TextDocument TextDocumentIdentifier `json:"textDocument"`
+	WorkDoneProgressParams
+	PartialResultParams
+}
+
+/*DeclarationClientCapabilities defined:
+ * Since 3.14.0
+ */
+type DeclarationClientCapabilities struct {
+
+	/*DynamicRegistration defined:
+	 * Whether declaration supports dynamic registration. If this is set to `true`
+	 * the client supports the new `DeclarationRegistrationOptions` return value
+	 * for the corresponding server capability as well.
+	 */
+	DynamicRegistration bool `json:"dynamicRegistration,omitempty"`
+
+	/*LinkSupport defined:
+	 * The client supports additional metadata in the form of declaration links.
+	 */
+	LinkSupport bool `json:"linkSupport,omitempty"`
+}
+
+// DeclarationOptions is
+type DeclarationOptions struct {
+	WorkDoneProgressOptions
+}
+
+// DeclarationRegistrationOptions is
+type DeclarationRegistrationOptions struct {
+	DeclarationOptions
+	TextDocumentRegistrationOptions
+	StaticRegistrationOptions
+}
+
+// DeclarationParams is
+type DeclarationParams struct {
+	TextDocumentPositionParams
+	WorkDoneProgressParams
+	PartialResultParams
+}
+
+// SelectionRangeClientCapabilities is
+type SelectionRangeClientCapabilities struct {
+
+	/*DynamicRegistration defined:
+	 * Whether implementation supports dynamic registration for selection range providers. If this is set to `true`
+	 * the client supports the new `SelectionRangeRegistrationOptions` return value for the corresponding server
+	 * capability as well.
+	 */
+	DynamicRegistration bool `json:"dynamicRegistration,omitempty"`
+}
+
+// SelectionRangeOptions is
+type SelectionRangeOptions struct {
+	WorkDoneProgressOptions
+}
+
+// SelectionRangeRegistrationOptions is
+type SelectionRangeRegistrationOptions struct {
+	SelectionRangeOptions
+	TextDocumentRegistrationOptions
+	StaticRegistrationOptions
+}
+
+/*SelectionRangeParams defined:
+ * A parameter literal used in selection range requests.
+ */
+type SelectionRangeParams struct {
+
+	/*TextDocument defined:
+	 * The text document.
+	 */
+	TextDocument TextDocumentIdentifier `json:"textDocument"`
+
+	/*Positions defined:
+	 * The positions inside the text document.
+	 */
+	Positions []Position `json:"positions"`
+	WorkDoneProgressParams
+	PartialResultParams
+}
+
+/*Registration defined:
+ * General parameters to to register for an notification or to register a provider.
+ */
+type Registration struct {
+
+	/*ID defined:
+	 * The id used to register the request. The id can be used to deregister
+	 * the request again.
+	 */
+	ID string `json:"id"`
+
+	/*Method defined:
+	 * The method to register for.
+	 */
+	Method string `json:"method"`
+
+	/*RegisterOptions defined:
+	 * Options necessary for the registration.
+	 */
+	RegisterOptions interface{} `json:"registerOptions,omitempty"`
+}
+
+// RegistrationParams is
+type RegistrationParams struct {
+
+	// Registrations is
+	Registrations []Registration `json:"registrations"`
+}
+
+/*Unregistration defined:
+ * General parameters to unregister a request or notification.
+ */
+type Unregistration struct {
+
+	/*ID defined:
+	 * The id used to unregister the request or notification. Usually an id
+	 * provided during the register request.
+	 */
+	ID string `json:"id"`
+
+	/*Method defined:
+	 * The method to unregister for.
+	 */
+	Method string `json:"method"`
+}
+
+// UnregistrationParams is
+type UnregistrationParams struct {
+
+	// Unregisterations is
+	Unregisterations []Unregistration `json:"unregisterations"`
+}
+
+// WorkDoneProgressParams is
+type WorkDoneProgressParams struct {
+
+	/*WorkDoneToken defined:
+	 * An optional token that a server can use to report work done progress.
+	 */
+	WorkDoneToken *ProgressToken `json:"workDoneToken,omitempty"`
+}
+
+// PartialResultParams is
+type PartialResultParams struct {
+
+	/*PartialResultToken defined:
+	 * An optional token that a server can use to report partial results (e.g. streaming) to
+	 * the client.
+	 */
+	PartialResultToken *ProgressToken `json:"partialResultToken,omitempty"`
+}
+
+/*TextDocumentPositionParams defined:
+ * A parameter literal used in requests to pass a text document and a position inside that
+ * document.
+ */
+type TextDocumentPositionParams struct {
+
+	/*TextDocument defined:
+	 * The text document.
+	 */
+	TextDocument TextDocumentIdentifier `json:"textDocument"`
+
+	/*Position defined:
+	 * The position inside the text document.
+	 */
+	Position Position `json:"position"`
+}
+
+/*WorkspaceClientCapabilities defined:
+ * Workspace specific client capabilities.
+ */
+type WorkspaceClientCapabilities struct {
+
+	/*ApplyEdit defined:
+	 * The client supports applying batch edits
+	 * to the workspace by supporting the request
+	 * 'workspace/applyEdit'
+	 */
+	ApplyEdit bool `json:"applyEdit,omitempty"`
+
+	/*WorkspaceEdit defined:
+	 * Capabilities specific to `WorkspaceEdit`s
+	 */
+	WorkspaceEdit *WorkspaceEditClientCapabilities `json:"workspaceEdit,omitempty"`
+
+	/*DidChangeConfiguration defined:
+	 * Capabilities specific to the `workspace/didChangeConfiguration` notification.
+	 */
+	DidChangeConfiguration *DidChangeConfigurationClientCapabilities `json:"didChangeConfiguration,omitempty"`
+
+	/*DidChangeWatchedFiles defined:
+	 * Capabilities specific to the `workspace/didChangeWatchedFiles` notification.
+	 */
+	DidChangeWatchedFiles *DidChangeWatchedFilesClientCapabilities `json:"didChangeWatchedFiles,omitempty"`
+
+	/*Symbol defined:
+	 * Capabilities specific to the `workspace/symbol` request.
+	 */
+	Symbol *WorkspaceSymbolClientCapabilities `json:"symbol,omitempty"`
+
+	/*ExecuteCommand defined:
+	 * Capabilities specific to the `workspace/executeCommand` request.
+	 */
+	ExecuteCommand *ExecuteCommandClientCapabilities `json:"executeCommand,omitempty"`
+}
+
+/*TextDocumentClientCapabilities defined:
+ * Text document specific client capabilities.
+ */
+type TextDocumentClientCapabilities struct {
+
+	/*Synchronization defined:
+	 * Defines which synchronization capabilities the client supports.
+	 */
+	Synchronization *TextDocumentSyncClientCapabilities `json:"synchronization,omitempty"`
+
+	/*Completion defined:
+	 * Capabilities specific to the `textDocument/completion`
+	 */
+	Completion *CompletionClientCapabilities `json:"completion,omitempty"`
+
+	/*Hover defined:
+	 * Capabilities specific to the `textDocument/hover`
+	 */
+	Hover *HoverClientCapabilities `json:"hover,omitempty"`
+
+	/*SignatureHelp defined:
+	 * Capabilities specific to the `textDocument/signatureHelp`
+	 */
+	SignatureHelp *SignatureHelpClientCapabilities `json:"signatureHelp,omitempty"`
+
+	/*Declaration defined:
+	 * Capabilities specific to the `textDocument/declaration`
+	 *
+	 * @since 3.14.0
+	 */
+	Declaration *DeclarationClientCapabilities `json:"declaration,omitempty"`
+
+	/*Definition defined:
+	 * Capabilities specific to the `textDocument/definition`
+	 */
+	Definition *DefinitionClientCapabilities `json:"definition,omitempty"`
+
+	/*TypeDefinition defined:
+	 * Capabilities specific to the `textDocument/typeDefinition`
+	 *
+	 * @since 3.6.0
+	 */
+	TypeDefinition *TypeDefinitionClientCapabilities `json:"typeDefinition,omitempty"`
+
+	/*Implementation defined:
+	 * Capabilities specific to the `textDocument/implementation`
+	 *
+	 * @since 3.6.0
+	 */
+	Implementation *ImplementationClientCapabilities `json:"implementation,omitempty"`
+
+	/*References defined:
+	 * Capabilities specific to the `textDocument/references`
+	 */
+	References *ReferenceClientCapabilities `json:"references,omitempty"`
+
+	/*DocumentHighlight defined:
+	 * Capabilities specific to the `textDocument/documentHighlight`
+	 */
+	DocumentHighlight *DocumentHighlightClientCapabilities `json:"documentHighlight,omitempty"`
+
+	/*DocumentSymbol defined:
+	 * Capabilities specific to the `textDocument/documentSymbol`
+	 */
+	DocumentSymbol *DocumentSymbolClientCapabilities `json:"documentSymbol,omitempty"`
+
+	/*CodeAction defined:
+	 * Capabilities specific to the `textDocument/codeAction`
+	 */
+	CodeAction *CodeActionClientCapabilities `json:"codeAction,omitempty"`
+
+	/*CodeLens defined:
+	 * Capabilities specific to the `textDocument/codeLens`
+	 */
+	CodeLens *CodeLensClientCapabilities `json:"codeLens,omitempty"`
+
+	/*DocumentLink defined:
+	 * Capabilities specific to the `textDocument/documentLink`
+	 */
+	DocumentLink *DocumentLinkClientCapabilities `json:"documentLink,omitempty"`
+
+	/*ColorProvider defined:
+	 * Capabilities specific to the `textDocument/documentColor`
+	 */
+	ColorProvider *DocumentColorClientCapabilities `json:"colorProvider,omitempty"`
+
+	/*Formatting defined:
+	 * Capabilities specific to the `textDocument/formatting`
+	 */
+	Formatting *DocumentFormattingClientCapabilities `json:"formatting,omitempty"`
+
+	/*RangeFormatting defined:
+	 * Capabilities specific to the `textDocument/rangeFormatting`
+	 */
+	RangeFormatting *DocumentRangeFormattingClientCapabilities `json:"rangeFormatting,omitempty"`
+
+	/*OnTypeFormatting defined:
+	 * Capabilities specific to the `textDocument/onTypeFormatting`
+	 */
+	OnTypeFormatting *DocumentOnTypeFormattingClientCapabilities `json:"onTypeFormatting,omitempty"`
+
+	/*Rename defined:
+	 * Capabilities specific to the `textDocument/rename`
+	 */
+	Rename *RenameClientCapabilities `json:"rename,omitempty"`
+
+	/*FoldingRange defined:
+	 * Capabilities specific to `textDocument/foldingRange` requests.
+	 *
+	 * @since 3.10.0
+	 */
+	FoldingRange *FoldingRangeClientCapabilities `json:"foldingRange,omitempty"`
+
+	/*SelectionRange defined:
+	 * Capabilities specific to `textDocument/selectionRange` requests
+	 *
+	 * @since 3.15.0
+	 */
+	SelectionRange *SelectionRangeClientCapabilities `json:"selectionRange,omitempty"`
+
+	/*PublishDiagnostics defined:
+	 * Capabilities specific to `textDocument/publishDiagnostics`.
+	 */
+	PublishDiagnostics *PublishDiagnosticsClientCapabilities `json:"publishDiagnostics,omitempty"`
+}
+
+/*InnerClientCapabilities defined:
+ * Defines the capabilities provided by the client.
+ */
+type InnerClientCapabilities struct {
+
+	/*Workspace defined:
+	 * Workspace specific client capabilities.
+	 */
+	Workspace *WorkspaceClientCapabilities `json:"workspace,omitempty"`
+
+	/*TextDocument defined:
+	 * Text document specific client capabilities.
+	 */
+	TextDocument *TextDocumentClientCapabilities `json:"textDocument,omitempty"`
+
+	/*Window defined:
+	 * Window specific client capabilities.
+	 */
+	Window interface{} `json:"window,omitempty"`
+
+	/*Experimental defined:
+	 * Experimental client capabilities.
+	 */
+	Experimental interface{} `json:"experimental,omitempty"`
+}
+
+// ClientCapabilities is
+type ClientCapabilities struct {
+
+	/*Workspace defined:
+	 * Workspace specific client capabilities.
+	 */
+	Workspace struct {
+
+		/*ApplyEdit defined:
+		 * The client supports applying batch edits
+		 * to the workspace by supporting the request
+		 * 'workspace/applyEdit'
+		 */
+		ApplyEdit bool `json:"applyEdit,omitempty"`
+
+		/*WorkspaceEdit defined:
+		 * Capabilities specific to `WorkspaceEdit`s
+		 */
+		WorkspaceEdit WorkspaceEditClientCapabilities `json:"workspaceEdit,omitempty"`
+
+		/*DidChangeConfiguration defined:
+		 * Capabilities specific to the `workspace/didChangeConfiguration` notification.
+		 */
+		DidChangeConfiguration DidChangeConfigurationClientCapabilities `json:"didChangeConfiguration,omitempty"`
+
+		/*DidChangeWatchedFiles defined:
+		 * Capabilities specific to the `workspace/didChangeWatchedFiles` notification.
+		 */
+		DidChangeWatchedFiles DidChangeWatchedFilesClientCapabilities `json:"didChangeWatchedFiles,omitempty"`
+
+		/*Symbol defined:
+		 * Capabilities specific to the `workspace/symbol` request.
+		 */
+		Symbol WorkspaceSymbolClientCapabilities `json:"symbol,omitempty"`
+
+		/*ExecuteCommand defined:
+		 * Capabilities specific to the `workspace/executeCommand` request.
+		 */
+		ExecuteCommand ExecuteCommandClientCapabilities `json:"executeCommand,omitempty"`
+
+		/*WorkspaceFolders defined:
+		 * The client has support for workspace folders
+		 */
+		WorkspaceFolders bool `json:"workspaceFolders,omitempty"`
+
+		/*Configuration defined:
+		* The client supports `workspace/configuration` requests.
+		 */
+		Configuration bool `json:"configuration,omitempty"`
+	} `json:"workspace,omitempty"`
+
+	/*TextDocument defined:
+	 * Text document specific client capabilities.
+	 */
+	TextDocument TextDocumentClientCapabilities `json:"textDocument,omitempty"`
+
+	/*Window defined:
+	 * Window specific client capabilities.
+	 */
+	Window interface{} `json:"window,omitempty"`
+
+	/*Experimental defined:
+	 * Experimental client capabilities.
+	 */
+	Experimental interface{} `json:"experimental,omitempty"`
+
+	/*DynamicRegistration defined:
+	 * Whether implementation supports dynamic registration for selection range providers. If this is set to `true`
+	 * the client supports the new `SelectionRangeRegistrationOptions` return value for the corresponding server
+	 * capability as well.
+	 */
+	DynamicRegistration bool `json:"dynamicRegistration,omitempty"`
+}
+
+/*StaticRegistrationOptions defined:
+ * Static registration options to be returned in the initialize
+ * request.
+ */
+type StaticRegistrationOptions struct {
+
+	/*ID defined:
+	 * The id used to register the request. The id can be used to deregister
+	 * the request again. See also Registration#id.
+	 */
+	ID string `json:"id,omitempty"`
+}
+
+/*TextDocumentRegistrationOptions defined:
+ * General text document registration options.
+ */
+type TextDocumentRegistrationOptions struct {
+
+	/*DocumentSelector defined:
+	 * A document selector to identify the scope of the registration. If set to null
+	 * the document selector provided on the client side will be used.
+	 */
+	DocumentSelector DocumentSelector `json:"documentSelector"`
+}
+
+/*SaveOptions defined:
+ * Save options.
+ */
+type SaveOptions struct {
+
+	/*IncludeText defined:
+	 * The client is supposed to include the content on save.
+	 */
+	IncludeText bool `json:"includeText,omitempty"`
+}
+
+// WorkDoneProgressOptions is
+type WorkDoneProgressOptions struct {
+
+	// WorkDoneProgress is
+	WorkDoneProgress bool `json:"workDoneProgress,omitempty"`
+}
+
+/*InnerServerCapabilities defined:
+ * Defines the capabilities provided by a language
+ * server.
+ */
+type InnerServerCapabilities struct {
+
+	/*TextDocumentSync defined:
+	 * Defines how text documents are synced. Is either a detailed structure defining each notification or
+	 * for backwards compatibility the TextDocumentSyncKind number.
+	 */
+	TextDocumentSync interface{} `json:"textDocumentSync,omitempty"` // TextDocumentSyncOptions | TextDocumentSyncKind
+
+	/*CompletionProvider defined:
+	 * The server provides completion support.
+	 */
+	CompletionProvider *CompletionOptions `json:"completionProvider,omitempty"`
+
+	/*HoverProvider defined:
+	 * The server provides hover support.
+	 */
+	HoverProvider bool `json:"hoverProvider,omitempty"` // boolean | HoverOptions
+
+	/*SignatureHelpProvider defined:
+	 * The server provides signature help support.
+	 */
+	SignatureHelpProvider *SignatureHelpOptions `json:"signatureHelpProvider,omitempty"`
+
+	/*DeclarationProvider defined:
+	 * The server provides Goto Declaration support.
+	 */
+	DeclarationProvider bool `json:"declarationProvider,omitempty"` // boolean | DeclarationOptions | DeclarationRegistrationOptions
+
+	/*DefinitionProvider defined:
+	 * The server provides goto definition support.
+	 */
+	DefinitionProvider bool `json:"definitionProvider,omitempty"` // boolean | DefinitionOptions
+
+	/*TypeDefinitionProvider defined:
+	 * The server provides Goto Type Definition support.
+	 */
+	TypeDefinitionProvider bool `json:"typeDefinitionProvider,omitempty"` // boolean | TypeDefinitionOptions | TypeDefinitionRegistrationOptions
+
+	/*ImplementationProvider defined:
+	 * The server provides Goto Implementation support.
+	 */
+	ImplementationProvider bool `json:"implementationProvider,omitempty"` // boolean | ImplementationOptions | ImplementationRegistrationOptions
+
+	/*ReferencesProvider defined:
+	 * The server provides find references support.
+	 */
+	ReferencesProvider bool `json:"referencesProvider,omitempty"` // boolean | ReferenceOptions
+
+	/*DocumentHighlightProvider defined:
+	 * The server provides document highlight support.
+	 */
+	DocumentHighlightProvider bool `json:"documentHighlightProvider,omitempty"` // boolean | DocumentHighlightOptions
+
+	/*DocumentSymbolProvider defined:
+	 * The server provides document symbol support.
+	 */
+	DocumentSymbolProvider bool `json:"documentSymbolProvider,omitempty"` // boolean | DocumentSymbolOptions
+
+	/*CodeActionProvider defined:
+	 * The server provides code actions. CodeActionOptions may only be
+	 * specified if the client states that it supports
+	 * `codeActionLiteralSupport` in its initial `initialize` request.
+	 */
+	CodeActionProvider interface{} `json:"codeActionProvider,omitempty"` // boolean | CodeActionOptions
+
+	/*CodeLensProvider defined:
+	 * The server provides code lens.
+	 */
+	CodeLensProvider *CodeLensOptions `json:"codeLensProvider,omitempty"`
+
+	/*DocumentLinkProvider defined:
+	 * The server provides document link support.
+	 */
+	DocumentLinkProvider *DocumentLinkOptions `json:"documentLinkProvider,omitempty"`
+
+	/*ColorProvider defined:
+	 * The server provides color provider support.
+	 */
+	ColorProvider bool `json:"colorProvider,omitempty"` // boolean | DocumentColorOptions | DocumentColorRegistrationOptions
+
+	/*WorkspaceSymbolProvider defined:
+	 * The server provides workspace symbol support.
+	 */
+	WorkspaceSymbolProvider bool `json:"workspaceSymbolProvider,omitempty"` // boolean | WorkspaceSymbolOptions
+
+	/*DocumentFormattingProvider defined:
+	 * The server provides document formatting.
+	 */
+	DocumentFormattingProvider bool `json:"documentFormattingProvider,omitempty"` // boolean | DocumentFormattingOptions
+
+	/*DocumentRangeFormattingProvider defined:
+	 * The server provides document range formatting.
+	 */
+	DocumentRangeFormattingProvider bool `json:"documentRangeFormattingProvider,omitempty"` // boolean | DocumentRangeFormattingOptions
+
+	/*DocumentOnTypeFormattingProvider defined:
+	 * The server provides document formatting on typing.
+	 */
+	DocumentOnTypeFormattingProvider *DocumentOnTypeFormattingOptions `json:"documentOnTypeFormattingProvider,omitempty"`
+
+	/*RenameProvider defined:
+	 * The server provides rename support. RenameOptions may only be
+	 * specified if the client states that it supports
+	 * `prepareSupport` in its initial `initialize` request.
+	 */
+	RenameProvider interface{} `json:"renameProvider,omitempty"` // boolean | RenameOptions
+
+	/*FoldingRangeProvider defined:
+	 * The server provides folding provider support.
+	 */
+	FoldingRangeProvider bool `json:"foldingRangeProvider,omitempty"` // boolean | FoldingRangeOptions | FoldingRangeRegistrationOptions
+
+	/*SelectionRangeProvider defined:
+	 * The server provides selection range support.
+	 */
+	SelectionRangeProvider bool `json:"selectionRangeProvider,omitempty"` // boolean | SelectionRangeOptions | SelectionRangeRegistrationOptions
+
+	/*ExecuteCommandProvider defined:
+	 * The server provides execute command support.
+	 */
+	ExecuteCommandProvider *ExecuteCommandOptions `json:"executeCommandProvider,omitempty"`
+
+	/*Experimental defined:
+	 * Experimental server capabilities.
+	 */
+	Experimental interface{} `json:"experimental,omitempty"`
+}
+
+// ServerCapabilities is
+type ServerCapabilities struct {
+
+	/*TextDocumentSync defined:
+	 * Defines how text documents are synced. Is either a detailed structure defining each notification or
+	 * for backwards compatibility the TextDocumentSyncKind number.
+	 */
+	TextDocumentSync interface{} `json:"textDocumentSync,omitempty"` // TextDocumentSyncOptions | TextDocumentSyncKind
+
+	/*CompletionProvider defined:
+	 * The server provides completion support.
+	 */
+	CompletionProvider *CompletionOptions `json:"completionProvider,omitempty"`
+
+	/*HoverProvider defined:
+	 * The server provides hover support.
+	 */
+	HoverProvider bool `json:"hoverProvider,omitempty"` // boolean | HoverOptions
+
+	/*SignatureHelpProvider defined:
+	 * The server provides signature help support.
+	 */
+	SignatureHelpProvider *SignatureHelpOptions `json:"signatureHelpProvider,omitempty"`
+
+	/*DeclarationProvider defined:
+	 * The server provides Goto Declaration support.
+	 */
+	DeclarationProvider bool `json:"declarationProvider,omitempty"` // boolean | DeclarationOptions | DeclarationRegistrationOptions
+
+	/*DefinitionProvider defined:
+	 * The server provides goto definition support.
+	 */
+	DefinitionProvider bool `json:"definitionProvider,omitempty"` // boolean | DefinitionOptions
+
+	/*TypeDefinitionProvider defined:
+	 * The server provides Goto Type Definition support.
+	 */
+	TypeDefinitionProvider bool `json:"typeDefinitionProvider,omitempty"` // boolean | TypeDefinitionOptions | TypeDefinitionRegistrationOptions
+
+	/*ImplementationProvider defined:
+	 * The server provides Goto Implementation support.
+	 */
+	ImplementationProvider bool `json:"implementationProvider,omitempty"` // boolean | ImplementationOptions | ImplementationRegistrationOptions
+
+	/*ReferencesProvider defined:
+	 * The server provides find references support.
+	 */
+	ReferencesProvider bool `json:"referencesProvider,omitempty"` // boolean | ReferenceOptions
+
+	/*DocumentHighlightProvider defined:
+	 * The server provides document highlight support.
+	 */
+	DocumentHighlightProvider bool `json:"documentHighlightProvider,omitempty"` // boolean | DocumentHighlightOptions
+
+	/*DocumentSymbolProvider defined:
+	 * The server provides document symbol support.
+	 */
+	DocumentSymbolProvider bool `json:"documentSymbolProvider,omitempty"` // boolean | DocumentSymbolOptions
+
+	/*CodeActionProvider defined:
+	 * The server provides code actions. CodeActionOptions may only be
+	 * specified if the client states that it supports
+	 * `codeActionLiteralSupport` in its initial `initialize` request.
+	 */
+	CodeActionProvider interface{} `json:"codeActionProvider,omitempty"` // boolean | CodeActionOptions
+
+	/*CodeLensProvider defined:
+	 * The server provides code lens.
+	 */
+	CodeLensProvider *CodeLensOptions `json:"codeLensProvider,omitempty"`
+
+	/*DocumentLinkProvider defined:
+	 * The server provides document link support.
+	 */
+	DocumentLinkProvider *DocumentLinkOptions `json:"documentLinkProvider,omitempty"`
+
+	/*ColorProvider defined:
+	 * The server provides color provider support.
+	 */
+	ColorProvider bool `json:"colorProvider,omitempty"` // boolean | DocumentColorOptions | DocumentColorRegistrationOptions
+
+	/*WorkspaceSymbolProvider defined:
+	 * The server provides workspace symbol support.
+	 */
+	WorkspaceSymbolProvider bool `json:"workspaceSymbolProvider,omitempty"` // boolean | WorkspaceSymbolOptions
+
+	/*DocumentFormattingProvider defined:
+	 * The server provides document formatting.
+	 */
+	DocumentFormattingProvider bool `json:"documentFormattingProvider,omitempty"` // boolean | DocumentFormattingOptions
+
+	/*DocumentRangeFormattingProvider defined:
+	 * The server provides document range formatting.
+	 */
+	DocumentRangeFormattingProvider bool `json:"documentRangeFormattingProvider,omitempty"` // boolean | DocumentRangeFormattingOptions
+
+	/*DocumentOnTypeFormattingProvider defined:
+	 * The server provides document formatting on typing.
+	 */
+	DocumentOnTypeFormattingProvider *DocumentOnTypeFormattingOptions `json:"documentOnTypeFormattingProvider,omitempty"`
+
+	/*RenameProvider defined:
+	 * The server provides rename support. RenameOptions may only be
+	 * specified if the client states that it supports
+	 * `prepareSupport` in its initial `initialize` request.
+	 */
+	RenameProvider interface{} `json:"renameProvider,omitempty"` // boolean | RenameOptions
+
+	/*FoldingRangeProvider defined:
+	 * The server provides folding provider support.
+	 */
+	FoldingRangeProvider bool `json:"foldingRangeProvider,omitempty"` // boolean | FoldingRangeOptions | FoldingRangeRegistrationOptions
+
+	/*SelectionRangeProvider defined:
+	 * The server provides selection range support.
+	 */
+	SelectionRangeProvider bool `json:"selectionRangeProvider,omitempty"` // boolean | SelectionRangeOptions | SelectionRangeRegistrationOptions
+
+	/*ExecuteCommandProvider defined:
+	 * The server provides execute command support.
+	 */
+	ExecuteCommandProvider *ExecuteCommandOptions `json:"executeCommandProvider,omitempty"`
+
+	/*Experimental defined:
+	 * Experimental server capabilities.
+	 */
+	Experimental interface{} `json:"experimental,omitempty"`
+
+	/*Workspace defined:
+	 * The workspace server capabilities
+	 */
+	Workspace *struct {
+
+		// WorkspaceFolders is
+		WorkspaceFolders *struct {
+
+			/*Supported defined:
+			 * The Server has support for workspace folders
+			 */
+			Supported bool `json:"supported,omitempty"`
+
+			/*ChangeNotifications defined:
+			 * Whether the server wants to receive workspace folder
+			 * change notifications.
+			 *
+			 * If a strings is provided the string is treated as a ID
+			 * under which the notification is registed on the client
+			 * side. The ID can be used to unregister for these events
+			 * using the `client/unregisterCapability` request.
+			 */
+			ChangeNotifications string `json:"changeNotifications,omitempty"` // string | boolean
+		} `json:"workspaceFolders,omitempty"`
+	} `json:"workspace,omitempty"`
+}
+
+/*InnerInitializeParams defined:
+ * The initialize parameters
+ */
+type InnerInitializeParams struct {
+
+	/*ProcessID defined:
+	 * The process Id of the parent process that started
+	 * the server.
+	 */
+	ProcessID float64 `json:"processId"`
+
+	/*ClientInfo defined:
+	 * Information about the client
+	 *
+	 * @since 3.15.0
+	 */
+	ClientInfo *struct {
+
+		/*Name defined:
+		 * The name of the client as defined by the client.
+		 */
+		Name string `json:"name"`
+
+		/*Version defined:
+		 * The client's version as defined by the client.
+		 */
+		Version string `json:"version,omitempty"`
+	} `json:"clientInfo,omitempty"`
+
+	/*RootPath defined:
+	 * The rootPath of the workspace. Is null
+	 * if no folder is open.
+	 *
+	 * @deprecated in favour of rootUri.
+	 */
+	RootPath string `json:"rootPath,omitempty"`
+
+	/*RootURI defined:
+	 * The rootUri of the workspace. Is null if no
+	 * folder is open. If both `rootPath` and `rootUri` are set
+	 * `rootUri` wins.
+	 *
+	 * @deprecated in favour of workspaceFolders.
+	 */
+	RootURI DocumentURI `json:"rootUri"`
+
+	/*Capabilities defined:
+	 * The capabilities provided by the client (editor or tool)
+	 */
+	Capabilities ClientCapabilities `json:"capabilities"`
+
+	/*InitializationOptions defined:
+	 * User provided initialization options.
+	 */
+	InitializationOptions interface{} `json:"initializationOptions,omitempty"`
+
+	/*Trace defined:
+	 * The initial trace setting. If omitted trace is disabled ('off').
+	 */
+	Trace string `json:"trace,omitempty"` // 'off' | 'messages' | 'verbose'
+	WorkDoneProgressParams
+}
+
+// InitializeParams is
+type InitializeParams struct {
+
+	/*ProcessID defined:
+	 * The process Id of the parent process that started
+	 * the server.
+	 */
+	ProcessID float64 `json:"processId"`
+
+	/*ClientInfo defined:
+	 * Information about the client
+	 *
+	 * @since 3.15.0
+	 */
+	ClientInfo *struct {
+
+		/*Name defined:
+		 * The name of the client as defined by the client.
+		 */
+		Name string `json:"name"`
+
+		/*Version defined:
+		 * The client's version as defined by the client.
+		 */
+		Version string `json:"version,omitempty"`
+	} `json:"clientInfo,omitempty"`
+
+	/*RootPath defined:
+	 * The rootPath of the workspace. Is null
+	 * if no folder is open.
+	 *
+	 * @deprecated in favour of rootUri.
+	 */
+	RootPath string `json:"rootPath,omitempty"`
+
+	/*RootURI defined:
+	 * The rootUri of the workspace. Is null if no
+	 * folder is open. If both `rootPath` and `rootUri` are set
+	 * `rootUri` wins.
+	 *
+	 * @deprecated in favour of workspaceFolders.
+	 */
+	RootURI DocumentURI `json:"rootUri"`
+
+	/*Capabilities defined:
+	 * The capabilities provided by the client (editor or tool)
+	 */
+	Capabilities ClientCapabilities `json:"capabilities"`
+
+	/*InitializationOptions defined:
+	 * User provided initialization options.
+	 */
+	InitializationOptions interface{} `json:"initializationOptions,omitempty"`
+
+	/*Trace defined:
+	 * The initial trace setting. If omitted trace is disabled ('off').
+	 */
+	Trace string `json:"trace,omitempty"` // 'off' | 'messages' | 'verbose'
+
+	/*WorkspaceFolders defined:
+	 * The actual configured workspace folders.
+	 */
+	WorkspaceFolders []WorkspaceFolder `json:"workspaceFolders"`
+}
+
+/*InitializeResult defined:
+ * The result returned from an initialize request.
+ */
+type InitializeResult struct {
+
+	/*Capabilities defined:
+	 * The capabilities the language server provides.
+	 */
+	Capabilities ServerCapabilities `json:"capabilities"`
+
+	/*ServerInfo defined:
+	 * Information about the server.
+	 *
+	 * @since 3.15.0
+	 */
+	ServerInfo *struct {
+
+		/*Name defined:
+		 * The name of the server as defined by the server.
+		 */
+		Name string `json:"name"`
+
+		/*Version defined:
+		 * The servers's version as defined by the server.
+		 */
+		Version string `json:"version,omitempty"`
+	} `json:"serverInfo,omitempty"`
+
+	/*Custom defined:
+	 * Custom initialization results.
+	 */
+	Custom map[string]interface{} `json:"custom"` // [custom: string]: any;
+}
+
+// InitializedParams is
+type InitializedParams struct {
+}
+
+// DidChangeConfigurationClientCapabilities is
+type DidChangeConfigurationClientCapabilities struct {
+
+	/*DynamicRegistration defined:
+	 * Did change configuration notification supports dynamic registration.
+	 */
+	DynamicRegistration bool `json:"dynamicRegistration,omitempty"`
+}
+
+// DidChangeConfigurationRegistrationOptions is
+type DidChangeConfigurationRegistrationOptions struct {
+
+	// Section is
+	Section string `json:"section,omitempty"` // string | string[]
+}
+
+/*DidChangeConfigurationParams defined:
+ * The parameters of a change configuration notification.
+ */
+type DidChangeConfigurationParams struct {
+
+	/*Settings defined:
+	 * The actual changed settings
+	 */
+	Settings interface{} `json:"settings"`
+}
+
+/*ShowMessageParams defined:
+ * The parameters of a notification message.
+ */
+type ShowMessageParams struct {
+
+	/*Type defined:
+	 * The message type. See {@link MessageType}
+	 */
+	Type MessageType `json:"type"`
+
+	/*Message defined:
+	 * The actual message
+	 */
+	Message string `json:"message"`
+}
+
+// MessageActionItem is
+type MessageActionItem struct {
+
+	/*Title defined:
+	 * A short title like 'Retry', 'Open Log' etc.
+	 */
+	Title string `json:"title"`
+}
+
+// ShowMessageRequestParams is
+type ShowMessageRequestParams struct {
+
+	/*Type defined:
+	 * The message type. See {@link MessageType}
+	 */
+	Type MessageType `json:"type"`
+
+	/*Message defined:
+	 * The actual message
+	 */
+	Message string `json:"message"`
+
+	/*Actions defined:
+	 * The message action items to present.
+	 */
+	Actions []MessageActionItem `json:"actions,omitempty"`
+}
+
+/*LogMessageParams defined:
+ * The log message parameters.
+ */
+type LogMessageParams struct {
+
+	/*Type defined:
+	 * The message type. See {@link MessageType}
+	 */
+	Type MessageType `json:"type"`
+
+	/*Message defined:
+	 * The actual message
+	 */
+	Message string `json:"message"`
+}
+
+// TextDocumentSyncClientCapabilities is
+type TextDocumentSyncClientCapabilities struct {
+
+	/*DynamicRegistration defined:
+	 * Whether text document synchronization supports dynamic registration.
+	 */
+	DynamicRegistration bool `json:"dynamicRegistration,omitempty"`
+
+	/*WillSave defined:
+	 * The client supports sending will save notifications.
+	 */
+	WillSave bool `json:"willSave,omitempty"`
+
+	/*WillSaveWaitUntil defined:
+	 * The client supports sending a will save request and
+	 * waits for a response providing text edits which will
+	 * be applied to the document before it is saved.
+	 */
+	WillSaveWaitUntil bool `json:"willSaveWaitUntil,omitempty"`
+
+	/*DidSave defined:
+	 * The client supports did save notifications.
+	 */
+	DidSave bool `json:"didSave,omitempty"`
+}
+
+// TextDocumentSyncOptions is
+type TextDocumentSyncOptions struct {
+
+	/*OpenClose defined:
+	 * Open and close notifications are sent to the server. If omitted open close notification should not
+	 * be sent.
+	 */
+	OpenClose bool `json:"openClose,omitempty"`
+
+	/*Change defined:
+	 * Change notifications are sent to the server. See TextDocumentSyncKind.None, TextDocumentSyncKind.Full
+	 * and TextDocumentSyncKind.Incremental. If omitted it defaults to TextDocumentSyncKind.None.
+	 */
+	Change TextDocumentSyncKind `json:"change,omitempty"`
+
+	/*WillSave defined:
+	 * If present will save notifications are sent to the server. If omitted the notification should not be
+	 * sent.
+	 */
+	WillSave bool `json:"willSave,omitempty"`
+
+	/*WillSaveWaitUntil defined:
+	 * If present will save wait until requests are sent to the server. If omitted the request should not be
+	 * sent.
+	 */
+	WillSaveWaitUntil bool `json:"willSaveWaitUntil,omitempty"`
+
+	/*Save defined:
+	 * If present save notifications are sent to the server. If omitted the notification should not be
+	 * sent.
+	 */
+	Save *SaveOptions `json:"save,omitempty"`
+}
+
+/*DidOpenTextDocumentParams defined:
+ * The parameters send in a open text document notification
+ */
+type DidOpenTextDocumentParams struct {
+
+	/*TextDocument defined:
+	 * The document that was opened.
+	 */
+	TextDocument TextDocumentItem `json:"textDocument"`
+}
+
+/*DidChangeTextDocumentParams defined:
+ * The change text document notification's parameters.
+ */
+type DidChangeTextDocumentParams struct {
+
+	/*TextDocument defined:
+	 * The document that did change. The version number points
+	 * to the version after all provided content changes have
+	 * been applied.
+	 */
+	TextDocument VersionedTextDocumentIdentifier `json:"textDocument"`
+
+	/*ContentChanges defined:
+	 * The actual content changes. The content changes describe single state changes
+	 * to the document. So if there are two content changes c1 and c2 for a document
+	 * in state S then c1 move the document to S' and c2 to S''.
+	 */
+	ContentChanges []TextDocumentContentChangeEvent `json:"contentChanges"`
+}
+
+/*TextDocumentChangeRegistrationOptions defined:
+ * Describe options to be used when registered for text document change events.
+ */
+type TextDocumentChangeRegistrationOptions struct {
+
+	/*SyncKind defined:
+	 * How documents are synced to the server.
+	 */
+	SyncKind TextDocumentSyncKind `json:"syncKind"`
+	TextDocumentRegistrationOptions
+}
+
+/*DidCloseTextDocumentParams defined:
+ * The parameters send in a close text document notification
+ */
+type DidCloseTextDocumentParams struct {
+
+	/*TextDocument defined:
+	 * The document that was closed.
+	 */
+	TextDocument TextDocumentIdentifier `json:"textDocument"`
+}
+
+/*DidSaveTextDocumentParams defined:
+ * The parameters send in a save text document notification
+ */
+type DidSaveTextDocumentParams struct {
+
+	/*TextDocument defined:
+	 * The document that was closed.
+	 */
+	TextDocument VersionedTextDocumentIdentifier `json:"textDocument"`
+
+	/*Text defined:
+	 * Optional the content when saved. Depends on the includeText value
+	 * when the save notification was requested.
+	 */
+	Text string `json:"text,omitempty"`
+}
+
+/*TextDocumentSaveRegistrationOptions defined:
+ * Save registration options.
+ */
+type TextDocumentSaveRegistrationOptions struct {
+	TextDocumentRegistrationOptions
+	SaveOptions
+}
+
+/*WillSaveTextDocumentParams defined:
+ * The parameters send in a will save text document notification.
+ */
+type WillSaveTextDocumentParams struct {
+
+	/*TextDocument defined:
+	 * The document that will be saved.
+	 */
+	TextDocument TextDocumentIdentifier `json:"textDocument"`
+
+	/*Reason defined:
+	 * The 'TextDocumentSaveReason'.
+	 */
+	Reason TextDocumentSaveReason `json:"reason"`
+}
+
+// DidChangeWatchedFilesClientCapabilities is
+type DidChangeWatchedFilesClientCapabilities struct {
+
+	/*DynamicRegistration defined:
+	 * Did change watched files notification supports dynamic registration. Please note
+	 * that the current protocol doesn't support static configuration for file changes
+	 * from the server side.
+	 */
+	DynamicRegistration bool `json:"dynamicRegistration,omitempty"`
+}
+
+/*DidChangeWatchedFilesParams defined:
+ * The watched files change notification's parameters.
+ */
+type DidChangeWatchedFilesParams struct {
+
+	/*Changes defined:
+	 * The actual file events.
+	 */
+	Changes []FileEvent `json:"changes"`
+}
+
+/*FileEvent defined:
+ * An event describing a file change.
+ */
+type FileEvent struct {
+
+	/*URI defined:
+	 * The file's uri.
+	 */
+	URI DocumentURI `json:"uri"`
+
+	/*Type defined:
+	 * The change type.
+	 */
+	Type FileChangeType `json:"type"`
+}
+
+/*DidChangeWatchedFilesRegistrationOptions defined:
+ * Describe options to be used when registered for text document change events.
+ */
+type DidChangeWatchedFilesRegistrationOptions struct {
+
+	/*Watchers defined:
+	 * The watchers to register.
+	 */
+	Watchers []FileSystemWatcher `json:"watchers"`
+}
+
+// FileSystemWatcher is
+type FileSystemWatcher struct {
+
+	/*GlobPattern defined:
+	 * The  glob pattern to watch. Glob patterns can have the following syntax:
+	 * - `*` to match one or more characters in a path segment
+	 * - `?` to match on one character in a path segment
+	 * - `**` to match any number of path segments, including none
+	 * - `{}` to group conditions (e.g. `**​/*.{ts,js}` matches all TypeScript and JavaScript files)
+	 * - `[]` to declare a range of characters to match in a path segment (e.g., `example.[0-9]` to match on `example.0`, `example.1`, …)
+	 * - `[!...]` to negate a range of characters to match in a path segment (e.g., `example.[!0-9]` to match on `example.a`, `example.b`, but not `example.0`)
+	 */
+	GlobPattern string `json:"globPattern"`
+
+	/*Kind defined:
+	 * The kind of events of interest. If omitted it defaults
+	 * to WatchKind.Create | WatchKind.Change | WatchKind.Delete
+	 * which is 7.
+	 */
+	Kind float64 `json:"kind,omitempty"`
+}
+
+/*PublishDiagnosticsClientCapabilities defined:
+ * The publish diagnostic client capabilities.
+ */
+type PublishDiagnosticsClientCapabilities struct {
+
+	/*RelatedInformation defined:
+	 * Whether the clients accepts diagnostics with related information.
+	 */
+	RelatedInformation bool `json:"relatedInformation,omitempty"`
+
+	/*TagSupport defined:
+	 * Client supports the tag property to provide meta data about a diagnostic.
+	 * Clients supporting tags have to handle unknown tags gracefully.
+	 *
+	 * @since 3.15.0
+	 */
+	TagSupport *struct {
+
+		/*ValueSet defined:
+		 * The tags supported by the client.
+		 */
+		ValueSet []DiagnosticTag `json:"valueSet"`
+	} `json:"tagSupport,omitempty"`
+}
+
+/*PublishDiagnosticsParams defined:
+ * The publish diagnostic notification's parameters.
+ */
+type PublishDiagnosticsParams struct {
+
+	/*URI defined:
+	 * The URI for which diagnostic information is reported.
+	 */
+	URI DocumentURI `json:"uri"`
+
+	/*Version defined:
+	 * Optional the version number of the document the diagnostics are published for.
+	 *
+	 * @since 3.15.0
+	 */
+	Version float64 `json:"version,omitempty"`
+
+	/*Diagnostics defined:
+	 * An array of diagnostic information items.
+	 */
+	Diagnostics []Diagnostic `json:"diagnostics"`
+}
+
+/*CompletionClientCapabilities defined:
+ * Completion client capabilities
+ */
+type CompletionClientCapabilities struct {
+
+	/*DynamicRegistration defined:
+	 * Whether completion supports dynamic registration.
+	 */
+	DynamicRegistration bool `json:"dynamicRegistration,omitempty"`
+
+	/*CompletionItem defined:
+	 * The client supports the following `CompletionItem` specific
+	 * capabilities.
+	 */
+	CompletionItem *struct {
+
+		/*SnippetSupport defined:
+		 * Client supports snippets as insert text.
+		 *
+		 * A snippet can define tab stops and placeholders with `$1`, `$2`
+		 * and `${3:foo}`. `$0` defines the final tab stop, it defaults to
+		 * the end of the snippet. Placeholders with equal identifiers are linked,
+		 * that is typing in one will update others too.
+		 */
+		SnippetSupport bool `json:"snippetSupport,omitempty"`
+
+		/*CommitCharactersSupport defined:
+		 * Client supports commit characters on a completion item.
+		 */
+		CommitCharactersSupport bool `json:"commitCharactersSupport,omitempty"`
+
+		/*DocumentationFormat defined:
+		 * Client supports the follow content formats for the documentation
+		 * property. The order describes the preferred format of the client.
+		 */
+		DocumentationFormat []MarkupKind `json:"documentationFormat,omitempty"`
+
+		/*DeprecatedSupport defined:
+		 * Client supports the deprecated property on a completion item.
+		 */
+		DeprecatedSupport bool `json:"deprecatedSupport,omitempty"`
+
+		/*PreselectSupport defined:
+		 * Client supports the preselect property on a completion item.
+		 */
+		PreselectSupport bool `json:"preselectSupport,omitempty"`
+
+		/*TagSupport defined:
+		 * Client supports the tag property on a completion item. Clients supporting
+		 * tags have to handle unknown tags gracefully. Clients especially need to
+		 * preserve unknown tags when sending a completion item back to the server in
+		 * a resolve call.
+		 *
+		 * @since 3.15.0
+		 */
+		TagSupport *struct {
+
+			/*ValueSet defined:
+			 * The tags supported by the client.
+			 */
+			ValueSet []CompletionItemTag `json:"valueSet"`
+		} `json:"tagSupport,omitempty"`
+	} `json:"completionItem,omitempty"`
+
+	// CompletionItemKind is
+	CompletionItemKind *struct {
+
+		/*ValueSet defined:
+		 * The completion item kind values the client supports. When this
+		 * property exists the client also guarantees that it will
+		 * handle values outside its set gracefully and falls back
+		 * to a default value when unknown.
+		 *
+		 * If this property is not present the client only supports
+		 * the completion items kinds from `Text` to `Reference` as defined in
+		 * the initial version of the protocol.
+		 */
+		ValueSet []CompletionItemKind `json:"valueSet,omitempty"`
+	} `json:"completionItemKind,omitempty"`
+
+	/*ContextSupport defined:
+	 * The client supports to send additional context information for a
+	 * `textDocument/completion` requestion.
+	 */
+	ContextSupport bool `json:"contextSupport,omitempty"`
+}
+
+/*CompletionContext defined:
+ * Contains additional information about the context in which a completion request is triggered.
+ */
+type CompletionContext struct {
+
+	/*TriggerKind defined:
+	 * How the completion was triggered.
+	 */
+	TriggerKind CompletionTriggerKind `json:"triggerKind"`
+
+	/*TriggerCharacter defined:
+	 * The trigger character (a single character) that has trigger code complete.
+	 * Is undefined if `triggerKind !== CompletionTriggerKind.TriggerCharacter`
+	 */
+	TriggerCharacter string `json:"triggerCharacter,omitempty"`
+}
+
+/*CompletionParams defined:
+ * Completion parameters
+ */
+type CompletionParams struct {
+
+	/*Context defined:
+	 * The completion context. This is only available it the client specifies
+	 * to send this using the client capability `textDocument.completion.contextSupport === true`
+	 */
+	Context *CompletionContext `json:"context,omitempty"`
+	TextDocumentPositionParams
+	WorkDoneProgressParams
+	PartialResultParams
+}
+
+/*CompletionOptions defined:
+ * Completion options.
+ */
+type CompletionOptions struct {
+
+	/*TriggerCharacters defined:
+	 * Most tools trigger completion request automatically without explicitly requesting
+	 * it using a keyboard shortcut (e.g. Ctrl+Space). Typically they do so when the user
+	 * starts to type an identifier. For example if the user types `c` in a JavaScript file
+	 * code complete will automatically pop up present `console` besides others as a
+	 * completion item. Characters that make up identifiers don't need to be listed here.
+	 *
+	 * If code complete should automatically be trigger on characters not being valid inside
+	 * an identifier (for example `.` in JavaScript) list them in `triggerCharacters`.
+	 */
+	TriggerCharacters []string `json:"triggerCharacters,omitempty"`
+
+	/*AllCommitCharacters defined:
+	 * The list of all possible characters that commit a completion. This field can be used
+	 * if clients don't support individual commmit characters per completion item. See
+	 * `ClientCapabilities.textDocument.completion.completionItem.commitCharactersSupport`
+	 *
+	 * @since 3.2.0
+	 */
+	AllCommitCharacters []string `json:"allCommitCharacters,omitempty"`
+
+	/*ResolveProvider defined:
+	 * The server provides support to resolve additional
+	 * information for a completion item.
+	 */
+	ResolveProvider bool `json:"resolveProvider,omitempty"`
+	WorkDoneProgressOptions
+}
+
+/*CompletionRegistrationOptions defined:
+ * Registration options for a [CompletionRequest](#CompletionRequest).
+ */
+type CompletionRegistrationOptions struct {
+	TextDocumentRegistrationOptions
+	CompletionOptions
+}
+
+// HoverClientCapabilities is
+type HoverClientCapabilities struct {
+
+	/*DynamicRegistration defined:
+	 * Whether hover supports dynamic registration.
+	 */
+	DynamicRegistration bool `json:"dynamicRegistration,omitempty"`
+
+	/*ContentFormat defined:
+	 * Client supports the follow content formats for the content
+	 * property. The order describes the preferred format of the client.
+	 */
+	ContentFormat []MarkupKind `json:"contentFormat,omitempty"`
+}
+
+/*HoverOptions defined:
+ * Hover options.
+ */
+type HoverOptions struct {
+	WorkDoneProgressOptions
+}
+
+/*HoverParams defined:
+ * Parameters for a [HoverRequest](#HoverRequest).
+ */
+type HoverParams struct {
+	TextDocumentPositionParams
+	WorkDoneProgressParams
+}
+
+/*HoverRegistrationOptions defined:
+ * Registration options for a [HoverRequest](#HoverRequest).
+ */
+type HoverRegistrationOptions struct {
+	TextDocumentRegistrationOptions
+	HoverOptions
+}
+
+/*SignatureHelpClientCapabilities defined:
+ * Client Capabilities for a [SignatureHelpRequest](#SignatureHelpRequest).
+ */
+type SignatureHelpClientCapabilities struct {
+
+	/*DynamicRegistration defined:
+	 * Whether signature help supports dynamic registration.
+	 */
+	DynamicRegistration bool `json:"dynamicRegistration,omitempty"`
+
+	/*SignatureInformation defined:
+	 * The client supports the following `SignatureInformation`
+	 * specific properties.
+	 */
+	SignatureInformation *struct {
+
+		/*DocumentationFormat defined:
+		 * Client supports the follow content formats for the documentation
+		 * property. The order describes the preferred format of the client.
+		 */
+		DocumentationFormat []MarkupKind `json:"documentationFormat,omitempty"`
+
+		/*ParameterInformation defined:
+		 * Client capabilities specific to parameter information.
+		 */
+		ParameterInformation *struct {
+
+			/*LabelOffsetSupport defined:
+			 * The client supports processing label offsets instead of a
+			 * simple label string.
+			 *
+			 * @since 3.14.0
+			 */
+			LabelOffsetSupport bool `json:"labelOffsetSupport,omitempty"`
+		} `json:"parameterInformation,omitempty"`
+	} `json:"signatureInformation,omitempty"`
+
+	/*ContextSupport defined:
+	 * The client supports to send additional context information for a
+	 * `textDocument/signatureHelp` request. A client that opts into
+	 * contextSupport will also support the `retriggerCharacters` on
+	 * `SignatureHelpOptions`.
+	 *
+	 * @since 3.15.0
+	 */
+	ContextSupport bool `json:"contextSupport,omitempty"`
+}
+
+/*SignatureHelpOptions defined:
+ * Server Capabilities for a [SignatureHelpRequest](#SignatureHelpRequest).
+ */
+type SignatureHelpOptions struct {
+
+	/*TriggerCharacters defined:
+	 * List of characters that trigger signature help.
+	 */
+	TriggerCharacters []string `json:"triggerCharacters,omitempty"`
+
+	/*RetriggerCharacters defined:
+	 * List of characters that re-trigger signature help.
+	 *
+	 * These trigger characters are only active when signature help is already showing. All trigger characters
+	 * are also counted as re-trigger characters.
+	 *
+	 * @since 3.15.0
+	 */
+	RetriggerCharacters []string `json:"retriggerCharacters,omitempty"`
+	WorkDoneProgressOptions
+}
+
+/*SignatureHelpContext defined:
+ * Additional information about the context in which a signature help request was triggered.
+ *
+ * @since 3.15.0
+ */
+type SignatureHelpContext struct {
+
+	/*TriggerKind defined:
+	 * Action that caused signature help to be triggered.
+	 */
+	TriggerKind SignatureHelpTriggerKind `json:"triggerKind"`
+
+	/*TriggerCharacter defined:
+	 * Character that caused signature help to be triggered.
+	 *
+	 * This is undefined when `triggerKind !== SignatureHelpTriggerKind.TriggerCharacter`
+	 */
+	TriggerCharacter string `json:"triggerCharacter,omitempty"`
+
+	/*IsRetrigger defined:
+	 * `true` if signature help was already showing when it was triggered.
+	 *
+	 * Retriggers occur when the signature help is already active and can be caused by actions such as
+	 * typing a trigger character, a cursor move, or document content changes.
+	 */
+	IsRetrigger bool `json:"isRetrigger"`
+
+	/*ActiveSignatureHelp defined:
+	 * The currently active `SignatureHelp`.
+	 *
+	 * The `activeSignatureHelp` has its `SignatureHelp.activeSignature` field updated based on
+	 * the user navigating through available signatures.
+	 */
+	ActiveSignatureHelp *SignatureHelp `json:"activeSignatureHelp,omitempty"`
+}
+
+/*SignatureHelpParams defined:
+ * Parameters for a [SignatureHelpRequest](#SignatureHelpRequest).
+ */
+type SignatureHelpParams struct {
+
+	/*Context defined:
+	 * The signature help context. This is only available if the client specifies
+	 * to send this using the client capability `textDocument.signatureHelp.contextSupport === true`
+	 *
+	 * @since 3.15.0
+	 */
+	Context *SignatureHelpContext `json:"context,omitempty"`
+	TextDocumentPositionParams
+	WorkDoneProgressParams
+}
+
+/*SignatureHelpRegistrationOptions defined:
+ * Registration options for a [SignatureHelpRequest](#SignatureHelpRequest).
+ */
+type SignatureHelpRegistrationOptions struct {
+	TextDocumentRegistrationOptions
+	SignatureHelpOptions
+}
+
+/*DefinitionClientCapabilities defined:
+ * Client Capabilities for a [DefinitionRequest](#DefinitionRequest).
+ */
+type DefinitionClientCapabilities struct {
+
+	/*DynamicRegistration defined:
+	 * Whether definition supports dynamic registration.
+	 */
+	DynamicRegistration bool `json:"dynamicRegistration,omitempty"`
+
+	/*LinkSupport defined:
+	 * The client supports additional metadata in the form of definition links.
+	 *
+	 * @since 3.14.0
+	 */
+	LinkSupport bool `json:"linkSupport,omitempty"`
+}
+
+/*DefinitionOptions defined:
+ * Server Capabilities for a [DefinitionRequest](#DefinitionRequest).
+ */
+type DefinitionOptions struct {
+	WorkDoneProgressOptions
+}
+
+/*DefinitionParams defined:
+ * Parameters for a [DefinitionRequest](#DefinitionRequest).
+ */
+type DefinitionParams struct {
+	TextDocumentPositionParams
+	WorkDoneProgressParams
+	PartialResultParams
+}
+
+/*DefinitionRegistrationOptions defined:
+ * Registration options for a [DefinitionRequest](#DefinitionRequest).
+ */
+type DefinitionRegistrationOptions struct {
+	TextDocumentRegistrationOptions
+	DefinitionOptions
+}
+
+/*ReferenceClientCapabilities defined:
+ * Client Capabilities for a [ReferencesRequest](#ReferencesRequest).
+ */
+type ReferenceClientCapabilities struct {
+
+	/*DynamicRegistration defined:
+	 * Whether references supports dynamic registration.
+	 */
+	DynamicRegistration bool `json:"dynamicRegistration,omitempty"`
+}
+
+/*ReferenceParams defined:
+ * Parameters for a [ReferencesRequest](#ReferencesRequest).
+ */
+type ReferenceParams struct {
+
+	// Context is
+	Context ReferenceContext `json:"context"`
+	TextDocumentPositionParams
+	WorkDoneProgressParams
+	PartialResultParams
+}
+
+/*ReferenceOptions defined:
+ * Reference options.
+ */
+type ReferenceOptions struct {
+	WorkDoneProgressOptions
+}
+
+/*ReferenceRegistrationOptions defined:
+ * Registration options for a [ReferencesRequest](#ReferencesRequest).
+ */
+type ReferenceRegistrationOptions struct {
+	TextDocumentRegistrationOptions
+	ReferenceOptions
+}
+
+/*DocumentHighlightClientCapabilities defined:
+ * Client Capabilities for a [DocumentHighlightRequest](#DocumentHighlightRequest).
+ */
+type DocumentHighlightClientCapabilities struct {
+
+	/*DynamicRegistration defined:
+	 * Whether document highlight supports dynamic registration.
+	 */
+	DynamicRegistration bool `json:"dynamicRegistration,omitempty"`
+}
+
+/*DocumentHighlightParams defined:
+ * Parameters for a [DocumentHighlightRequest](#DocumentHighlightRequest).
+ */
+type DocumentHighlightParams struct {
+	TextDocumentPositionParams
+	WorkDoneProgressParams
+	PartialResultParams
+}
+
+/*DocumentHighlightOptions defined:
+ * Provider options for a [DocumentHighlightRequest](#DocumentHighlightRequest).
+ */
+type DocumentHighlightOptions struct {
+	WorkDoneProgressOptions
+}
+
+/*DocumentHighlightRegistrationOptions defined:
+ * Registration options for a [DocumentHighlightRequest](#DocumentHighlightRequest).
+ */
+type DocumentHighlightRegistrationOptions struct {
+	TextDocumentRegistrationOptions
+	DocumentHighlightOptions
+}
+
+/*DocumentSymbolClientCapabilities defined:
+ * Client Capabilities for a [DocumentSymbolRequest](#DocumentSymbolRequest).
+ */
+type DocumentSymbolClientCapabilities struct {
+
+	/*DynamicRegistration defined:
+	 * Whether document symbol supports dynamic registration.
+	 */
+	DynamicRegistration bool `json:"dynamicRegistration,omitempty"`
+
+	/*SymbolKind defined:
+	 * Specific capabilities for the `SymbolKind`.
+	 */
+	SymbolKind *struct {
+
+		/*ValueSet defined:
+		 * The symbol kind values the client supports. When this
+		 * property exists the client also guarantees that it will
+		 * handle values outside its set gracefully and falls back
+		 * to a default value when unknown.
+		 *
+		 * If this property is not present the client only supports
+		 * the symbol kinds from `File` to `Array` as defined in
+		 * the initial version of the protocol.
+		 */
+		ValueSet []SymbolKind `json:"valueSet,omitempty"`
+	} `json:"symbolKind,omitempty"`
+
+	/*HierarchicalDocumentSymbolSupport defined:
+	 * The client support hierarchical document symbols.
+	 */
+	HierarchicalDocumentSymbolSupport bool `json:"hierarchicalDocumentSymbolSupport,omitempty"`
+}
+
+/*DocumentSymbolParams defined:
+ * Parameters for a [DocumentSymbolRequest](#DocumentSymbolRequest).
+ */
+type DocumentSymbolParams struct {
+
+	/*TextDocument defined:
+	 * The text document.
+	 */
+	TextDocument TextDocumentIdentifier `json:"textDocument"`
+	WorkDoneProgressParams
+	PartialResultParams
+}
+
+/*DocumentSymbolOptions defined:
+ * Provider options for a [DocumentSymbolRequest](#DocumentSymbolRequest).
+ */
+type DocumentSymbolOptions struct {
+	WorkDoneProgressOptions
+}
+
+/*DocumentSymbolRegistrationOptions defined:
+ * Registration options for a [DocumentSymbolRequest](#DocumentSymbolRequest).
+ */
+type DocumentSymbolRegistrationOptions struct {
+	TextDocumentRegistrationOptions
+	DocumentSymbolOptions
+}
+
+/*CodeActionClientCapabilities defined:
+ * The Client Capabilities of a [CodeActionRequest](#CodeActionRequest).
+ */
+type CodeActionClientCapabilities struct {
+
+	/*DynamicRegistration defined:
+	 * Whether code action supports dynamic registration.
+	 */
+	DynamicRegistration bool `json:"dynamicRegistration,omitempty"`
+
+	/*CodeActionLiteralSupport defined:
+	 * The client support code action literals as a valid
+	 * response of the `textDocument/codeAction` request.
+	 *
+	 * @since 3.8.0
+	 */
+	CodeActionLiteralSupport *struct {
+
+		/*CodeActionKind defined:
+		 * The code action kind is support with the following value
+		 * set.
+		 */
+		CodeActionKind struct {
+
+			/*ValueSet defined:
+			 * The code action kind values the client supports. When this
+			 * property exists the client also guarantees that it will
+			 * handle values outside its set gracefully and falls back
+			 * to a default value when unknown.
+			 */
+			ValueSet []CodeActionKind `json:"valueSet"`
+		} `json:"codeActionKind"`
+	} `json:"codeActionLiteralSupport,omitempty"`
+
+	/*IsPreferredSupport defined:
+	 * Whether code action supports the `isPreferred` property.
+	 * @since 3.15.0
+	 */
+	IsPreferredSupport bool `json:"isPreferredSupport,omitempty"`
+}
+
+/*CodeActionParams defined:
+ * The parameters of a [CodeActionRequest](#CodeActionRequest).
+ */
+type CodeActionParams struct {
+
+	/*TextDocument defined:
+	 * The document in which the command was invoked.
+	 */
+	TextDocument TextDocumentIdentifier `json:"textDocument"`
+
+	/*Range defined:
+	 * The range for which the command was invoked.
+	 */
+	Range Range `json:"range"`
+
+	/*Context defined:
+	 * Context carrying additional information.
+	 */
+	Context CodeActionContext `json:"context"`
+	WorkDoneProgressParams
+	PartialResultParams
+}
+
+/*CodeActionOptions defined:
+ * Provider options for a [CodeActionRequest](#CodeActionRequest).
+ */
+type CodeActionOptions struct {
+
+	/*CodeActionKinds defined:
+	 * CodeActionKinds that this server may return.
+	 *
+	 * The list of kinds may be generic, such as `CodeActionKind.Refactor`, or the server
+	 * may list out every specific kind they provide.
+	 */
+	CodeActionKinds []CodeActionKind `json:"codeActionKinds,omitempty"`
+	WorkDoneProgressOptions
+}
+
+/*CodeActionRegistrationOptions defined:
+ * Registration options for a [CodeActionRequest](#CodeActionRequest).
+ */
+type CodeActionRegistrationOptions struct {
+	TextDocumentRegistrationOptions
+	CodeActionOptions
+}
+
+/*WorkspaceSymbolClientCapabilities defined:
+ * Client capabilities for a [WorkspaceSymbolRequest](#WorkspaceSymbolRequest).
+ */
+type WorkspaceSymbolClientCapabilities struct {
+
+	/*DynamicRegistration defined:
+	 * Symbol request supports dynamic registration.
+	 */
+	DynamicRegistration bool `json:"dynamicRegistration,omitempty"`
+
+	/*SymbolKind defined:
+	 * Specific capabilities for the `SymbolKind` in the `workspace/symbol` request.
+	 */
+	SymbolKind *struct {
+
+		/*ValueSet defined:
+		 * The symbol kind values the client supports. When this
+		 * property exists the client also guarantees that it will
+		 * handle values outside its set gracefully and falls back
+		 * to a default value when unknown.
+		 *
+		 * If this property is not present the client only supports
+		 * the symbol kinds from `File` to `Array` as defined in
+		 * the initial version of the protocol.
+		 */
+		ValueSet []SymbolKind `json:"valueSet,omitempty"`
+	} `json:"symbolKind,omitempty"`
+}
+
+/*WorkspaceSymbolParams defined:
+ * The parameters of a [WorkspaceSymbolRequest](#WorkspaceSymbolRequest).
+ */
+type WorkspaceSymbolParams struct {
+
+	/*Query defined:
+	 * A query string to filter symbols by. Clients may send an empty
+	 * string here to request all symbols.
+	 */
+	Query string `json:"query"`
+	WorkDoneProgressParams
+	PartialResultParams
+}
+
+/*WorkspaceSymbolOptions defined:
+ * Server capabilities for a [WorkspaceSymbolRequest](#WorkspaceSymbolRequest).
+ */
+type WorkspaceSymbolOptions struct {
+	WorkDoneProgressOptions
+}
+
+/*WorkspaceSymbolRegistrationOptions defined:
+ * Registration options for a [WorkspaceSymbolRequest](#WorkspaceSymbolRequest).
+ */
+type WorkspaceSymbolRegistrationOptions struct {
+	WorkspaceSymbolOptions
+}
+
+/*CodeLensClientCapabilities defined:
+ * The client capabilities  of a [CodeLensRequest](#CodeLensRequest).
+ */
+type CodeLensClientCapabilities struct {
+
+	/*DynamicRegistration defined:
+	 * Whether code lens supports dynamic registration.
+	 */
+	DynamicRegistration bool `json:"dynamicRegistration,omitempty"`
+}
+
+/*CodeLensParams defined:
+ * The parameters of a [CodeLensRequest](#CodeLensRequest).
+ */
+type CodeLensParams struct {
+
+	/*TextDocument defined:
+	 * The document to request code lens for.
+	 */
+	TextDocument TextDocumentIdentifier `json:"textDocument"`
+	WorkDoneProgressParams
+	PartialResultParams
+}
+
+/*CodeLensOptions defined:
+ * Code Lens provider options of a [CodeLensRequest](#CodeLensRequest).
+ */
+type CodeLensOptions struct {
+
+	/*ResolveProvider defined:
+	 * Code lens has a resolve provider as well.
+	 */
+	ResolveProvider bool `json:"resolveProvider,omitempty"`
+	WorkDoneProgressOptions
+}
+
+/*CodeLensRegistrationOptions defined:
+ * Registration options for a [CodeLensRequest](#CodeLensRequest).
+ */
+type CodeLensRegistrationOptions struct {
+	TextDocumentRegistrationOptions
+	CodeLensOptions
+}
+
+/*DocumentLinkClientCapabilities defined:
+ * The client capabilities of a [DocumentLinkRequest](#DocumentLinkRequest).
+ */
+type DocumentLinkClientCapabilities struct {
+
+	/*DynamicRegistration defined:
+	 * Whether document link supports dynamic registration.
+	 */
+	DynamicRegistration bool `json:"dynamicRegistration,omitempty"`
+
+	/*TooltipSupport defined:
+	 * Whether the client support the `tooltip` property on `DocumentLink`.
+	 *
+	 * @since 3.15.0
+	 */
+	TooltipSupport bool `json:"tooltipSupport,omitempty"`
+}
+
+/*DocumentLinkParams defined:
+ * The parameters of a [DocumentLinkRequest](#DocumentLinkRequest).
+ */
+type DocumentLinkParams struct {
+
+	/*TextDocument defined:
+	 * The document to provide document links for.
+	 */
+	TextDocument TextDocumentIdentifier `json:"textDocument"`
+	WorkDoneProgressParams
+	PartialResultParams
+}
+
+/*DocumentLinkOptions defined:
+ * Provider options for a [DocumentLinkRequest](#DocumentLinkRequest).
+ */
+type DocumentLinkOptions struct {
+
+	/*ResolveProvider defined:
+	 * Document links have a resolve provider as well.
+	 */
+	ResolveProvider bool `json:"resolveProvider,omitempty"`
+	WorkDoneProgressOptions
+}
+
+/*DocumentLinkRegistrationOptions defined:
+ * Registration options for a [DocumentLinkRequest](#DocumentLinkRequest).
+ */
+type DocumentLinkRegistrationOptions struct {
+	TextDocumentRegistrationOptions
+	DocumentLinkOptions
+}
+
+/*DocumentFormattingClientCapabilities defined:
+ * Client capabilities of a [DocumentFormattingRequest](#DocumentFormattingRequest).
+ */
+type DocumentFormattingClientCapabilities struct {
+
+	/*DynamicRegistration defined:
+	 * Whether formatting supports dynamic registration.
+	 */
+	DynamicRegistration bool `json:"dynamicRegistration,omitempty"`
+}
+
+/*DocumentFormattingParams defined:
+ * The parameters of a [DocumentFormattingRequest](#DocumentFormattingRequest).
+ */
+type DocumentFormattingParams struct {
+
+	/*TextDocument defined:
+	 * The document to format.
+	 */
+	TextDocument TextDocumentIdentifier `json:"textDocument"`
+
+	/*Options defined:
+	 * The format options
+	 */
+	Options FormattingOptions `json:"options"`
+	WorkDoneProgressParams
+}
+
+/*DocumentFormattingOptions defined:
+ * Provider options for a [DocumentFormattingRequest](#DocumentFormattingRequest).
+ */
+type DocumentFormattingOptions struct {
+	WorkDoneProgressOptions
+}
+
+/*DocumentFormattingRegistrationOptions defined:
+ * Registration options for a [DocumentFormattingRequest](#DocumentFormattingRequest).
+ */
+type DocumentFormattingRegistrationOptions struct {
+	TextDocumentRegistrationOptions
+	DocumentFormattingOptions
+}
+
+/*DocumentRangeFormattingClientCapabilities defined:
+ * Client capabilities of a [DocumentRangeFormattingRequest](#DocumentRangeFormattingRequest).
+ */
+type DocumentRangeFormattingClientCapabilities struct {
+
+	/*DynamicRegistration defined:
+	 * Whether range formatting supports dynamic registration.
+	 */
+	DynamicRegistration bool `json:"dynamicRegistration,omitempty"`
+}
+
+/*DocumentRangeFormattingParams defined:
+ * The parameters of a [DocumentRangeFormattingRequest](#DocumentRangeFormattingRequest).
+ */
+type DocumentRangeFormattingParams struct {
+
+	/*TextDocument defined:
+	 * The document to format.
+	 */
+	TextDocument TextDocumentIdentifier `json:"textDocument"`
+
+	/*Range defined:
+	 * The range to format
+	 */
+	Range Range `json:"range"`
+
+	/*Options defined:
+	 * The format options
+	 */
+	Options FormattingOptions `json:"options"`
+	WorkDoneProgressParams
+}
+
+/*DocumentRangeFormattingOptions defined:
+ * Provider options for a [DocumentRangeFormattingRequest](#DocumentRangeFormattingRequest).
+ */
+type DocumentRangeFormattingOptions struct {
+	WorkDoneProgressOptions
+}
+
+/*DocumentRangeFormattingRegistrationOptions defined:
+ * Registration options for a [DocumentRangeFormattingRequest](#DocumentRangeFormattingRequest).
+ */
+type DocumentRangeFormattingRegistrationOptions struct {
+	TextDocumentRegistrationOptions
+	DocumentRangeFormattingOptions
+}
+
+/*DocumentOnTypeFormattingClientCapabilities defined:
+ * Client capabilities of a [DocumentOnTypeFormattingRequest](#DocumentOnTypeFormattingRequest).
+ */
+type DocumentOnTypeFormattingClientCapabilities struct {
+
+	/*DynamicRegistration defined:
+	 * Whether on type formatting supports dynamic registration.
+	 */
+	DynamicRegistration bool `json:"dynamicRegistration,omitempty"`
+}
+
+/*DocumentOnTypeFormattingParams defined:
+ * The parameters of a [DocumentOnTypeFormattingRequest](#DocumentOnTypeFormattingRequest).
+ */
+type DocumentOnTypeFormattingParams struct {
+
+	/*TextDocument defined:
+	 * The document to format.
+	 */
+	TextDocument TextDocumentIdentifier `json:"textDocument"`
+
+	/*Position defined:
+	 * The position at which this request was send.
+	 */
+	Position Position `json:"position"`
+
+	/*Ch defined:
+	 * The character that has been typed.
+	 */
+	Ch string `json:"ch"`
+
+	/*Options defined:
+	 * The format options.
+	 */
+	Options FormattingOptions `json:"options"`
+}
+
+/*DocumentOnTypeFormattingOptions defined:
+ * Provider options for a [DocumentOnTypeFormattingRequest](#DocumentOnTypeFormattingRequest).
+ */
+type DocumentOnTypeFormattingOptions struct {
+
+	/*FirstTriggerCharacter defined:
+	 * A character on which formatting should be triggered, like `}`.
+	 */
+	FirstTriggerCharacter string `json:"firstTriggerCharacter"`
+
+	/*MoreTriggerCharacter defined:
+	 * More trigger characters.
+	 */
+	MoreTriggerCharacter []string `json:"moreTriggerCharacter,omitempty"`
+}
+
+/*DocumentOnTypeFormattingRegistrationOptions defined:
+ * Registration options for a [DocumentOnTypeFormattingRequest](#DocumentOnTypeFormattingRequest).
+ */
+type DocumentOnTypeFormattingRegistrationOptions struct {
+	TextDocumentRegistrationOptions
+	DocumentOnTypeFormattingOptions
+}
+
+// RenameClientCapabilities is
+type RenameClientCapabilities struct {
+
+	/*DynamicRegistration defined:
+	 * Whether rename supports dynamic registration.
+	 */
+	DynamicRegistration bool `json:"dynamicRegistration,omitempty"`
+
+	/*PrepareSupport defined:
+	 * Client supports testing for validity of rename operations
+	 * before execution.
+	 *
+	 * @since version 3.12.0
+	 */
+	PrepareSupport bool `json:"prepareSupport,omitempty"`
+}
+
+/*RenameParams defined:
+ * The parameters of a [RenameRequest](#RenameRequest).
+ */
+type RenameParams struct {
+
+	/*TextDocument defined:
+	 * The document to rename.
+	 */
+	TextDocument TextDocumentIdentifier `json:"textDocument"`
+
+	/*Position defined:
+	 * The position at which this request was sent.
+	 */
+	Position Position `json:"position"`
+
+	/*NewName defined:
+	 * The new name of the symbol. If the given name is not valid the
+	 * request must return a [ResponseError](#ResponseError) with an
+	 * appropriate message set.
+	 */
+	NewName string `json:"newName"`
+	WorkDoneProgressParams
+}
+
+/*RenameOptions defined:
+ * Provider options for a [RenameRequest](#RenameRequest).
+ */
+type RenameOptions struct {
+
+	/*PrepareProvider defined:
+	 * Renames should be checked and tested before being executed.
+	 *
+	 * @since version 3.12.0
+	 */
+	PrepareProvider bool `json:"prepareProvider,omitempty"`
+	WorkDoneProgressOptions
+}
+
+/*RenameRegistrationOptions defined:
+ * Registration options for a [RenameRequest](#RenameRequest).
+ */
+type RenameRegistrationOptions struct {
+	TextDocumentRegistrationOptions
+	RenameOptions
+}
+
+// PrepareRenameParams is
+type PrepareRenameParams struct {
+	TextDocumentPositionParams
+	WorkDoneProgressParams
+}
+
+/*ExecuteCommandClientCapabilities defined:
+ * The client capabilities of a [ExecuteCommandRequest](#ExecuteCommandRequest).
+ */
+type ExecuteCommandClientCapabilities struct {
+
+	/*DynamicRegistration defined:
+	 * Execute command supports dynamic registration.
+	 */
+	DynamicRegistration bool `json:"dynamicRegistration,omitempty"`
+}
+
+/*ExecuteCommandParams defined:
+ * The parameters of a [ExecuteCommandRequest](#ExecuteCommandRequest).
+ */
+type ExecuteCommandParams struct {
+
+	/*Command defined:
+	 * The identifier of the actual command handler.
+	 */
+	Command string `json:"command"`
+
+	/*Arguments defined:
+	 * Arguments that the command should be invoked with.
+	 */
+	Arguments []interface{} `json:"arguments,omitempty"`
+	WorkDoneProgressParams
+}
+
+/*ExecuteCommandOptions defined:
+ * The server capabilities of a [ExecuteCommandRequest](#ExecuteCommandRequest).
+ */
+type ExecuteCommandOptions struct {
+
+	/*Commands defined:
+	 * The commands to be executed on the server
+	 */
+	Commands []string `json:"commands"`
+	WorkDoneProgressOptions
+}
+
+/*ExecuteCommandRegistrationOptions defined:
+ * Registration options for a [ExecuteCommandRequest](#ExecuteCommandRequest).
+ */
+type ExecuteCommandRegistrationOptions struct {
+	ExecuteCommandOptions
+}
+
+// WorkspaceEditClientCapabilities is
+type WorkspaceEditClientCapabilities struct {
+
+	/*DocumentChanges defined:
+	 * The client supports versioned document changes in `WorkspaceEdit`s
+	 */
+	DocumentChanges bool `json:"documentChanges,omitempty"`
+
+	/*ResourceOperations defined:
+	 * The resource operations the client supports. Clients should at least
+	 * support 'create', 'rename' and 'delete' files and folders.
+	 *
+	 * @since 3.13.0
+	 */
+	ResourceOperations []ResourceOperationKind `json:"resourceOperations,omitempty"`
+
+	/*FailureHandling defined:
+	 * The failure handling strategy of a client if applying the workspace edit
+	 * fails.
+	 *
+	 * @since 3.13.0
+	 */
+	FailureHandling FailureHandlingKind `json:"failureHandling,omitempty"`
+}
+
+/*ApplyWorkspaceEditParams defined:
+ * The parameters passed via a apply workspace edit request.
+ */
+type ApplyWorkspaceEditParams struct {
+
+	/*Label defined:
+	 * An optional label of the workspace edit. This label is
+	 * presented in the user interface for example on an undo
+	 * stack to undo the workspace edit.
+	 */
+	Label string `json:"label,omitempty"`
+
+	/*Edit defined:
+	 * The edits to apply.
+	 */
+	Edit WorkspaceEdit `json:"edit"`
+}
+
+/*ApplyWorkspaceEditResponse defined:
+ * A response returned from the apply workspace edit request.
+ */
+type ApplyWorkspaceEditResponse struct {
+
+	/*Applied defined:
+	 * Indicates whether the edit was applied or not.
+	 */
+	Applied bool `json:"applied"`
+
+	/*FailureReason defined:
+	 * An optional textual description for why the edit was not applied.
+	 * This may be used by the server for diagnostic logging or to provide
+	 * a suitable error for a request that triggered the edit.
+	 */
+	FailureReason string `json:"failureReason,omitempty"`
+
+	/*FailedChange defined:
+	 * Depending on the client's failure handling strategy `failedChange` might
+	 * contain the index of the change that failed. This property is only available
+	 * if the client signals a `failureHandlingStrategy` in its client capabilities.
+	 */
+	FailedChange float64 `json:"failedChange,omitempty"`
+}
+
+/*Position defined:
+ * Position in a text document expressed as zero-based line and character offset.
+ * The offsets are based on a UTF-16 string representation. So a string of the form
+ * `a𐐀b` the character offset of the character `a` is 0, the character offset of `𐐀`
+ * is 1 and the character offset of b is 3 since `𐐀` is represented using two code
+ * units in UTF-16.
+ *
+ * Positions are line end character agnostic. So you can not specify a position that
+ * denotes `\r|\n` or `\n|` where `|` represents the character offset.
+ */
+type Position struct {
+
+	/*Line defined:
+	 * Line position in a document (zero-based).
+	 * If a line number is greater than the number of lines in a document, it defaults back to the number of lines in the document.
+	 * If a line number is negative, it defaults to 0.
+	 */
+	Line float64 `json:"line"`
+
+	/*Character defined:
+	 * Character offset on a line in a document (zero-based). Assuming that the line is
+	 * represented as a string, the `character` value represents the gap between the
+	 * `character` and `character + 1`.
+	 *
+	 * If the character value is greater than the line length it defaults back to the
+	 * line length.
+	 * If a line number is negative, it defaults to 0.
+	 */
+	Character float64 `json:"character"`
+}
+
+/*Range defined:
+ * A range in a text document expressed as (zero-based) start and end positions.
+ *
+ * If you want to specify a range that contains a line including the line ending
+ * character(s) then use an end position denoting the start of the next line.
+ * For example:
+ * ```ts
+ * {
+ *     start: { line: 5, character: 23 }
+ *     end : { line 6, character : 0 }
+ * }
+ * ```
+ */
+type Range struct {
+
+	/*Start defined:
+	 * The range's start position
+	 */
+	Start Position `json:"start"`
+
+	/*End defined:
+	 * The range's end position.
+	 */
+	End Position `json:"end"`
+}
+
+/*Location defined:
+ * Represents a location inside a resource, such as a line
+ * inside a text file.
+ */
+type Location struct {
+
+	// URI is
+	URI DocumentURI `json:"uri"`
+
+	// Range is
+	Range Range `json:"range"`
+}
+
+/*LocationLink defined:
+ * Represents the connection of two locations. Provides additional metadata over normal [locations](#Location),
+ * including an origin range.
+ */
+type LocationLink struct {
+
+	/*OriginSelectionRange defined:
+	 * Span of the origin of this link.
+	 *
+	 * Used as the underlined span for mouse definition hover. Defaults to the word range at
+	 * the definition position.
+	 */
+	OriginSelectionRange *Range `json:"originSelectionRange,omitempty"`
+
+	/*TargetURI defined:
+	 * The target resource identifier of this link.
+	 */
+	TargetURI DocumentURI `json:"targetUri"`
+
+	/*TargetRange defined:
+	 * The full target range of this link. If the target for example is a symbol then target range is the
+	 * range enclosing this symbol not including leading/trailing whitespace but everything else
+	 * like comments. This information is typically used to highlight the range in the editor.
+	 */
+	TargetRange Range `json:"targetRange"`
+
+	/*TargetSelectionRange defined:
+	 * The range that should be selected and revealed when this link is being followed, e.g the name of a function.
+	 * Must be contained by the the `targetRange`. See also `DocumentSymbol#range`
+	 */
+	TargetSelectionRange Range `json:"targetSelectionRange"`
+}
+
+/*Color defined:
+ * Represents a color in RGBA space.
+ */
+type Color struct {
+
+	/*Red defined:
+	 * The red component of this color in the range [0-1].
+	 */
+	Red float64 `json:"red"`
+
+	/*Green defined:
+	 * The green component of this color in the range [0-1].
+	 */
+	Green float64 `json:"green"`
+
+	/*Blue defined:
+	 * The blue component of this color in the range [0-1].
+	 */
+	Blue float64 `json:"blue"`
+
+	/*Alpha defined:
+	 * The alpha component of this color in the range [0-1].
+	 */
+	Alpha float64 `json:"alpha"`
+}
+
+/*ColorInformation defined:
+ * Represents a color range from a document.
+ */
+type ColorInformation struct {
+
+	/*Range defined:
+	 * The range in the document where this color appers.
+	 */
+	Range Range `json:"range"`
+
+	/*Color defined:
+	 * The actual color value for this color range.
+	 */
+	Color Color `json:"color"`
+}
+
+// ColorPresentation is
+type ColorPresentation struct {
+
+	/*Label defined:
+	 * The label of this color presentation. It will be shown on the color
+	 * picker header. By default this is also the text that is inserted when selecting
+	 * this color presentation.
+	 */
+	Label string `json:"label"`
+
+	/*TextEdit defined:
+	 * An [edit](#TextEdit) which is applied to a document when selecting
+	 * this presentation for the color.  When `falsy` the [label](#ColorPresentation.label)
+	 * is used.
+	 */
+	TextEdit *TextEdit `json:"textEdit,omitempty"`
+
+	/*AdditionalTextEdits defined:
+	 * An optional array of additional [text edits](#TextEdit) that are applied when
+	 * selecting this color presentation. Edits must not overlap with the main [edit](#ColorPresentation.textEdit) nor with themselves.
+	 */
+	AdditionalTextEdits []TextEdit `json:"additionalTextEdits,omitempty"`
+}
+
+/*DiagnosticRelatedInformation defined:
+ * Represents a related message and source code location for a diagnostic. This should be
+ * used to point to code locations that cause or related to a diagnostics, e.g when duplicating
+ * a symbol in a scope.
+ */
+type DiagnosticRelatedInformation struct {
+
+	/*Location defined:
+	 * The location of this related diagnostic information.
+	 */
+	Location Location `json:"location"`
+
+	/*Message defined:
+	 * The message of this related diagnostic information.
+	 */
+	Message string `json:"message"`
+}
+
+/*Diagnostic defined:
+ * Represents a diagnostic, such as a compiler error or warning. Diagnostic objects
+ * are only valid in the scope of a resource.
+ */
+type Diagnostic struct {
+
+	/*Range defined:
+	 * The range at which the message applies
+	 */
+	Range Range `json:"range"`
+
+	/*Severity defined:
+	 * The diagnostic's severity. Can be omitted. If omitted it is up to the
+	 * client to interpret diagnostics as error, warning, info or hint.
+	 */
+	Severity DiagnosticSeverity `json:"severity,omitempty"`
+
+	/*Code defined:
+	 * The diagnostic's code, which usually appear in the user interface.
+	 */
+	Code interface{} `json:"code,omitempty"` // number | string
+
+	/*Source defined:
+	 * A human-readable string describing the source of this
+	 * diagnostic, e.g. 'typescript' or 'super lint'. It usually
+	 * appears in the user interface.
+	 */
+	Source string `json:"source,omitempty"`
+
+	/*Message defined:
+	 * The diagnostic's message. It usually appears in the user interface
+	 */
+	Message string `json:"message"`
+
+	/*Tags defined:
+	 * Additional metadata about the diagnostic.
+	 */
+	Tags []DiagnosticTag `json:"tags,omitempty"`
+
+	/*RelatedInformation defined:
+	 * An array of related diagnostic information, e.g. when symbol-names within
+	 * a scope collide all definitions can be marked via this property.
+	 */
+	RelatedInformation []DiagnosticRelatedInformation `json:"relatedInformation,omitempty"`
+}
+
+/*Command defined:
+ * Represents a reference to a command. Provides a title which
+ * will be used to represent a command in the UI and, optionally,
+ * an array of arguments which will be passed to the command handler
+ * function when invoked.
+ */
+type Command struct {
+
+	/*Title defined:
+	 * Title of the command, like `save`.
+	 */
+	Title string `json:"title"`
+
+	/*Command defined:
+	 * The identifier of the actual command handler.
+	 */
+	Command string `json:"command"`
+
+	/*Arguments defined:
+	 * Arguments that the command handler should be
+	 * invoked with.
+	 */
+	Arguments []interface{} `json:"arguments,omitempty"`
+}
+
+/*TextEdit defined:
+ * A text edit applicable to a text document.
+ */
+type TextEdit struct {
+
+	/*Range defined:
+	 * The range of the text document to be manipulated. To insert
+	 * text into a document create a range where start === end.
+	 */
+	Range Range `json:"range"`
+
+	/*NewText defined:
+	 * The string to be inserted. For delete operations use an
+	 * empty string.
+	 */
+	NewText string `json:"newText"`
+}
+
+/*TextDocumentEdit defined:
+ * Describes textual changes on a text document.
+ */
+type TextDocumentEdit struct {
+
+	/*TextDocument defined:
+	 * The text document to change.
+	 */
+	TextDocument VersionedTextDocumentIdentifier `json:"textDocument"`
+
+	/*Edits defined:
+	 * The edits to be applied.
+	 */
+	Edits []TextEdit `json:"edits"`
+}
+
+// ResourceOperation is
+type ResourceOperation struct {
+
+	// Kind is
+	Kind string `json:"kind"`
+}
+
+/*CreateFileOptions defined:
+ * Options to create a file.
+ */
+type CreateFileOptions struct {
+
+	/*Overwrite defined:
+	 * Overwrite existing file. Overwrite wins over `ignoreIfExists`
+	 */
+	Overwrite bool `json:"overwrite,omitempty"`
+
+	/*IgnoreIfExists defined:
+	 * Ignore if exists.
+	 */
+	IgnoreIfExists bool `json:"ignoreIfExists,omitempty"`
+}
+
+/*CreateFile defined:
+ * Create file operation.
+ */
+type CreateFile struct {
+
+	/*Kind defined:
+	 * A create
+	 */
+	Kind string `json:"kind"` // 'create'
+
+	/*URI defined:
+	 * The resource to create.
+	 */
+	URI DocumentURI `json:"uri"`
+
+	/*Options defined:
+	 * Additional options
+	 */
+	Options *CreateFileOptions `json:"options,omitempty"`
+}
+
+/*RenameFileOptions defined:
+ * Rename file options
+ */
+type RenameFileOptions struct {
+
+	/*Overwrite defined:
+	 * Overwrite target if existing. Overwrite wins over `ignoreIfExists`
+	 */
+	Overwrite bool `json:"overwrite,omitempty"`
+
+	/*IgnoreIfExists defined:
+	 * Ignores if target exists.
+	 */
+	IgnoreIfExists bool `json:"ignoreIfExists,omitempty"`
+}
+
+/*RenameFile defined:
+ * Rename file operation
+ */
+type RenameFile struct {
+
+	/*Kind defined:
+	 * A rename
+	 */
+	Kind string `json:"kind"` // 'rename'
+
+	/*OldURI defined:
+	 * The old (existing) location.
+	 */
+	OldURI DocumentURI `json:"oldUri"`
+
+	/*NewURI defined:
+	 * The new location.
+	 */
+	NewURI DocumentURI `json:"newUri"`
+
+	/*Options defined:
+	 * Rename options.
+	 */
+	Options *RenameFileOptions `json:"options,omitempty"`
+}
+
+/*DeleteFileOptions defined:
+ * Delete file options
+ */
+type DeleteFileOptions struct {
+
+	/*Recursive defined:
+	 * Delete the content recursively if a folder is denoted.
+	 */
+	Recursive bool `json:"recursive,omitempty"`
+
+	/*IgnoreIfNotExists defined:
+	 * Ignore the operation if the file doesn't exist.
+	 */
+	IgnoreIfNotExists bool `json:"ignoreIfNotExists,omitempty"`
+}
+
+/*DeleteFile defined:
+ * Delete file operation
+ */
+type DeleteFile struct {
+
+	/*Kind defined:
+	 * A delete
+	 */
+	Kind string `json:"kind"` // 'delete'
+
+	/*URI defined:
+	 * The file to delete.
+	 */
+	URI DocumentURI `json:"uri"`
+
+	/*Options defined:
+	 * Delete options.
+	 */
+	Options *DeleteFileOptions `json:"options,omitempty"`
+}
+
+/*WorkspaceEdit defined:
+ * A workspace edit represents changes to many resources managed in the workspace. The edit
+ * should either provide `changes` or `documentChanges`. If documentChanges are present
+ * they are preferred over `changes` if the client can handle versioned document edits.
+ */
+type WorkspaceEdit struct {
+
+	/*Changes defined:
+	 * Holds changes to existing resources.
+	 */
+	Changes *map[string][]TextEdit `json:"changes,omitempty"` // [uri: string]: TextEdit[];
+
+	/*DocumentChanges defined:
+	 * Depending on the client capability `workspace.workspaceEdit.resourceOperations` document changes
+	 * are either an array of `TextDocumentEdit`s to express changes to n different text documents
+	 * where each text document edit addresses a specific version of a text document. Or it can contain
+	 * above `TextDocumentEdit`s mixed with create, rename and delete file / folder operations.
+	 *
+	 * Whether a client supports versioned document edits is expressed via
+	 * `workspace.workspaceEdit.documentChanges` client capability.
+	 *
+	 * If a client neither supports `documentChanges` nor `workspace.workspaceEdit.resourceOperations` then
+	 * only plain `TextEdit`s using the `changes` property are supported.
+	 */
+	DocumentChanges []TextDocumentEdit `json:"documentChanges,omitempty"` // (TextDocumentEdit | CreateFile | RenameFile | DeleteFile)
+}
+
+/*TextEditChange defined:
+ * A change to capture text edits for existing resources.
+ */
+type TextEditChange struct {
+}
+
+/*TextDocumentIdentifier defined:
+ * A literal to identify a text document in the client.
+ */
+type TextDocumentIdentifier struct {
+
+	/*URI defined:
+	 * The text document's uri.
+	 */
+	URI DocumentURI `json:"uri"`
+}
+
+/*VersionedTextDocumentIdentifier defined:
+ * An identifier to denote a specific version of a text document.
+ */
+type VersionedTextDocumentIdentifier struct {
+
+	/*Version defined:
+	 * The version number of this document. If a versioned text document identifier
+	 * is sent from the server to the client and the file is not open in the editor
+	 * (the server has not received an open notification before) the server can send
+	 * `null` to indicate that the version is unknown and the content on disk is the
+	 * truth (as speced with document content ownership).
+	 */
+	Version float64 `json:"version"`
+	TextDocumentIdentifier
+}
+
+/*TextDocumentItem defined:
+ * An item to transfer a text document from the client to the
+ * server.
+ */
+type TextDocumentItem struct {
+
+	/*URI defined:
+	 * The text document's uri.
+	 */
+	URI DocumentURI `json:"uri"`
+
+	/*LanguageID defined:
+	 * The text document's language identifier
+	 */
+	LanguageID string `json:"languageId"`
+
+	/*Version defined:
+	 * The version number of this document (it will increase after each
+	 * change, including undo/redo).
+	 */
+	Version float64 `json:"version"`
+
+	/*Text defined:
+	 * The content of the opened text document.
+	 */
+	Text string `json:"text"`
+}
+
+/*MarkupContent defined:
+ * A `MarkupContent` literal represents a string value which content is interpreted base on its
+ * kind flag. Currently the protocol supports `plaintext` and `markdown` as markup kinds.
+ *
+ * If the kind is `markdown` then the value can contain fenced code blocks like in GitHub issues.
+ * See https://help.github.com/articles/creating-and-highlighting-code-blocks/#syntax-highlighting
+ *
+ * Here is an example how such a string can be constructed using JavaScript / TypeScript:
+ * ```ts
+ * let markdown: MarkdownContent = {
+ *  kind: MarkupKind.Markdown,
+ *	value: [
+ *		'# Header',
+ *		'Some text',
+ *		'```typescript',
+ *		'someCode();',
+ *		'```'
+ *	].join('\n')
+ * };
+ * ```
+ *
+ * *Please Note* that clients might sanitize the return markdown. A client could decide to
+ * remove HTML from the markdown to avoid script execution.
+ */
+type MarkupContent struct {
+
+	/*Kind defined:
+	 * The type of the Markup
+	 */
+	Kind MarkupKind `json:"kind"`
+
+	/*Value defined:
+	 * The content itself
+	 */
+	Value string `json:"value"`
+}
+
+/*CompletionItem defined:
+ * A completion item represents a text snippet that is
+ * proposed to complete text that is being typed.
+ */
+type CompletionItem struct {
+
+	/*Label defined:
+	 * The label of this completion item. By default
+	 * also the text that is inserted when selecting
+	 * this completion.
+	 */
+	Label string `json:"label"`
+
+	/*Kind defined:
+	 * The kind of this completion item. Based of the kind
+	 * an icon is chosen by the editor.
+	 */
+	Kind CompletionItemKind `json:"kind,omitempty"`
+
+	/*Tags defined:
+	 * Tags for this completion item.
+	 *
+	 * @since 3.15.0
+	 */
+	Tags []CompletionItemTag `json:"tags,omitempty"`
+
+	/*Detail defined:
+	 * A human-readable string with additional information
+	 * about this item, like type or symbol information.
+	 */
+	Detail string `json:"detail,omitempty"`
+
+	/*Documentation defined:
+	 * A human-readable string that represents a doc-comment.
+	 */
+	Documentation string `json:"documentation,omitempty"` // string | MarkupContent
+
+	/*Deprecated defined:
+	 * Indicates if this item is deprecated.
+	 * @deprecated Use `tags` instead.
+	 */
+	Deprecated bool `json:"deprecated,omitempty"`
+
+	/*Preselect defined:
+	 * Select this item when showing.
+	 *
+	 * *Note* that only one completion item can be selected and that the
+	 * tool / client decides which item that is. The rule is that the *first*
+	 * item of those that match best is selected.
+	 */
+	Preselect bool `json:"preselect,omitempty"`
+
+	/*SortText defined:
+	 * A string that should be used when comparing this item
+	 * with other items. When `falsy` the [label](#CompletionItem.label)
+	 * is used.
+	 */
+	SortText string `json:"sortText,omitempty"`
+
+	/*FilterText defined:
+	 * A string that should be used when filtering a set of
+	 * completion items. When `falsy` the [label](#CompletionItem.label)
+	 * is used.
+	 */
+	FilterText string `json:"filterText,omitempty"`
+
+	/*InsertText defined:
+	 * A string that should be inserted into a document when selecting
+	 * this completion. When `falsy` the [label](#CompletionItem.label)
+	 * is used.
+	 *
+	 * The `insertText` is subject to interpretation by the client side.
+	 * Some tools might not take the string literally. For example
+	 * VS Code when code complete is requested in this example `con<cursor position>`
+	 * and a completion item with an `insertText` of `console` is provided it
+	 * will only insert `sole`. Therefore it is recommended to use `textEdit` instead
+	 * since it avoids additional client side interpretation.
+	 */
+	InsertText string `json:"insertText,omitempty"`
+
+	/*InsertTextFormat defined:
+	 * The format of the insert text. The format applies to both the `insertText` property
+	 * and the `newText` property of a provided `textEdit`.
+	 */
+	InsertTextFormat InsertTextFormat `json:"insertTextFormat,omitempty"`
+
+	/*TextEdit defined:
+	 * An [edit](#TextEdit) which is applied to a document when selecting
+	 * this completion. When an edit is provided the value of
+	 * [insertText](#CompletionItem.insertText) is ignored.
+	 *
+	 * *Note:* The text edit's range must be a [single line] and it must contain the position
+	 * at which completion has been requested.
+	 */
+	TextEdit *TextEdit `json:"textEdit,omitempty"`
+
+	/*AdditionalTextEdits defined:
+	 * An optional array of additional [text edits](#TextEdit) that are applied when
+	 * selecting this completion. Edits must not overlap (including the same insert position)
+	 * with the main [edit](#CompletionItem.textEdit) nor with themselves.
+	 *
+	 * Additional text edits should be used to change text unrelated to the current cursor position
+	 * (for example adding an import statement at the top of the file if the completion item will
+	 * insert an unqualified type).
+	 */
+	AdditionalTextEdits []TextEdit `json:"additionalTextEdits,omitempty"`
+
+	/*CommitCharacters defined:
+	 * An optional set of characters that when pressed while this completion is active will accept it first and
+	 * then type that character. *Note* that all commit characters should have `length=1` and that superfluous
+	 * characters will be ignored.
+	 */
+	CommitCharacters []string `json:"commitCharacters,omitempty"`
+
+	/*Command defined:
+	 * An optional [command](#Command) that is executed *after* inserting this completion. *Note* that
+	 * additional modifications to the current document should be described with the
+	 * [additionalTextEdits](#CompletionItem.additionalTextEdits)-property.
+	 */
+	Command *Command `json:"command,omitempty"`
+
+	/*Data defined:
+	 * An data entry field that is preserved on a completion item between
+	 * a [CompletionRequest](#CompletionRequest) and a [CompletionResolveRequest]
+	 * (#CompletionResolveRequest)
+	 */
+	Data interface{} `json:"data,omitempty"`
+}
+
+/*CompletionList defined:
+ * Represents a collection of [completion items](#CompletionItem) to be presented
+ * in the editor.
+ */
+type CompletionList struct {
+
+	/*IsIncomplete defined:
+	 * This list it not complete. Further typing results in recomputing this list.
+	 */
+	IsIncomplete bool `json:"isIncomplete"`
+
+	/*Items defined:
+	 * The completion items.
+	 */
+	Items []CompletionItem `json:"items"`
+}
+
+/*Hover defined:
+ * The result of a hover request.
+ */
+type Hover struct {
+
+	/*Contents defined:
+	 * The hover's content
+	 */
+	Contents MarkupContent `json:"contents"` // MarkupContent | MarkedString | MarkedString[]
+
+	/*Range defined:
+	 * An optional range
+	 */
+	Range *Range `json:"range,omitempty"`
+}
+
+/*ParameterInformation defined:
+ * Represents a parameter of a callable-signature. A parameter can
+ * have a label and a doc-comment.
+ */
+type ParameterInformation struct {
+
+	/*Label defined:
+	 * The label of this parameter information.
+	 *
+	 * Either a string or an inclusive start and exclusive end offsets within its containing
+	 * signature label. (see SignatureInformation.label). The offsets are based on a UTF-16
+	 * string representation as `Position` and `Range` does.
+	 *
+	 * *Note*: a label of type string should be a substring of its containing signature label.
+	 * Its intended use case is to highlight the parameter label part in the `SignatureInformation.label`.
+	 */
+	Label string `json:"label"` // string | [number, number]
+
+	/*Documentation defined:
+	 * The human-readable doc-comment of this signature. Will be shown
+	 * in the UI but can be omitted.
+	 */
+	Documentation string `json:"documentation,omitempty"` // string | MarkupContent
+}
+
+/*SignatureInformation defined:
+ * Represents the signature of something callable. A signature
+ * can have a label, like a function-name, a doc-comment, and
+ * a set of parameters.
+ */
+type SignatureInformation struct {
+
+	/*Label defined:
+	 * The label of this signature. Will be shown in
+	 * the UI.
+	 */
+	Label string `json:"label"`
+
+	/*Documentation defined:
+	 * The human-readable doc-comment of this signature. Will be shown
+	 * in the UI but can be omitted.
+	 */
+	Documentation string `json:"documentation,omitempty"` // string | MarkupContent
+
+	/*Parameters defined:
+	 * The parameters of this signature.
+	 */
+	Parameters []ParameterInformation `json:"parameters,omitempty"`
+}
+
+/*SignatureHelp defined:
+ * Signature help represents the signature of something
+ * callable. There can be multiple signature but only one
+ * active and only one active parameter.
+ */
+type SignatureHelp struct {
+
+	/*Signatures defined:
+	 * One or more signatures.
+	 */
+	Signatures []SignatureInformation `json:"signatures"`
+
+	/*ActiveSignature defined:
+	 * The active signature. Set to `null` if no
+	 * signatures exist.
+	 */
+	ActiveSignature float64 `json:"activeSignature"`
+
+	/*ActiveParameter defined:
+	 * The active parameter of the active signature. Set to `null`
+	 * if the active signature has no parameters.
+	 */
+	ActiveParameter float64 `json:"activeParameter"`
+}
+
+/*ReferenceContext defined:
+ * Value-object that contains additional information when
+ * requesting references.
+ */
+type ReferenceContext struct {
+
+	/*IncludeDeclaration defined:
+	 * Include the declaration of the current symbol.
+	 */
+	IncludeDeclaration bool `json:"includeDeclaration"`
+}
+
+/*DocumentHighlight defined:
+ * A document highlight is a range inside a text document which deserves
+ * special attention. Usually a document highlight is visualized by changing
+ * the background color of its range.
+ */
+type DocumentHighlight struct {
+
+	/*Range defined:
+	 * The range this highlight applies to.
+	 */
+	Range Range `json:"range"`
+
+	/*Kind defined:
+	 * The highlight kind, default is [text](#DocumentHighlightKind.Text).
+	 */
+	Kind *DocumentHighlightKind `json:"kind,omitempty"`
+}
+
+/*SymbolInformation defined:
+ * Represents information about programming constructs like variables, classes,
+ * interfaces etc.
+ */
+type SymbolInformation struct {
+
+	/*Name defined:
+	 * The name of this symbol.
+	 */
+	Name string `json:"name"`
+
+	/*Kind defined:
+	 * The kind of this symbol.
+	 */
+	Kind SymbolKind `json:"kind"`
+
+	/*Deprecated defined:
+	 * Indicates if this symbol is deprecated.
+	 */
+	Deprecated bool `json:"deprecated,omitempty"`
+
+	/*Location defined:
+	 * The location of this symbol. The location's range is used by a tool
+	 * to reveal the location in the editor. If the symbol is selected in the
+	 * tool the range's start information is used to position the cursor. So
+	 * the range usually spans more than the actual symbol's name and does
+	 * normally include thinks like visibility modifiers.
+	 *
+	 * The range doesn't have to denote a node range in the sense of a abstract
+	 * syntax tree. It can therefore not be used to re-construct a hierarchy of
+	 * the symbols.
+	 */
+	Location Location `json:"location"`
+
+	/*ContainerName defined:
+	 * The name of the symbol containing this symbol. This information is for
+	 * user interface purposes (e.g. to render a qualifier in the user interface
+	 * if necessary). It can't be used to re-infer a hierarchy for the document
+	 * symbols.
+	 */
+	ContainerName string `json:"containerName,omitempty"`
+}
+
+/*DocumentSymbol defined:
+ * Represents programming constructs like variables, classes, interfaces etc.
+ * that appear in a document. Document symbols can be hierarchical and they
+ * have two ranges: one that encloses its definition and one that points to
+ * its most interesting range, e.g. the range of an identifier.
+ */
+type DocumentSymbol struct {
+
+	/*Name defined:
+	 * The name of this symbol. Will be displayed in the user interface and therefore must not be
+	 * an empty string or a string only consisting of white spaces.
+	 */
+	Name string `json:"name"`
+
+	/*Detail defined:
+	 * More detail for this symbol, e.g the signature of a function.
+	 */
+	Detail string `json:"detail,omitempty"`
+
+	/*Kind defined:
+	 * The kind of this symbol.
+	 */
+	Kind SymbolKind `json:"kind"`
+
+	/*Deprecated defined:
+	 * Indicates if this symbol is deprecated.
+	 */
+	Deprecated bool `json:"deprecated,omitempty"`
+
+	/*Range defined:
+	 * The range enclosing this symbol not including leading/trailing whitespace but everything else
+	 * like comments. This information is typically used to determine if the the clients cursor is
+	 * inside the symbol to reveal in the symbol in the UI.
+	 */
+	Range Range `json:"range"`
+
+	/*SelectionRange defined:
+	 * The range that should be selected and revealed when this symbol is being picked, e.g the name of a function.
+	 * Must be contained by the the `range`.
+	 */
+	SelectionRange Range `json:"selectionRange"`
+
+	/*Children defined:
+	 * Children of this symbol, e.g. properties of a class.
+	 */
+	Children []DocumentSymbol `json:"children,omitempty"`
+}
+
+/*CodeActionContext defined:
+ * Contains additional diagnostic information about the context in which
+ * a [code action](#CodeActionProvider.provideCodeActions) is run.
+ */
+type CodeActionContext struct {
+
+	/*Diagnostics defined:
+	 * An array of diagnostics known on the client side overlapping the range provided to the
+	 * `textDocument/codeAction` request. They are provied so that the server knows which
+	 * errors are currently presented to the user for the given range. There is no guarantee
+	 * that these accurately reflect the error state of the resource. The primary parameter
+	 * to compute code actions is the provided range.
+	 */
+	Diagnostics []Diagnostic `json:"diagnostics"`
+
+	/*Only defined:
+	 * Requested kind of actions to return.
+	 *
+	 * Actions not of this kind are filtered out by the client before being shown. So servers
+	 * can omit computing them.
+	 */
+	Only []CodeActionKind `json:"only,omitempty"`
+}
+
+/*CodeAction defined:
+ * A code action represents a change that can be performed in code, e.g. to fix a problem or
+ * to refactor code.
+ *
+ * A CodeAction must set either `edit` and/or a `command`. If both are supplied, the `edit` is applied first, then the `command` is executed.
+ */
+type CodeAction struct {
+
+	/*Title defined:
+	 * A short, human-readable, title for this code action.
+	 */
+	Title string `json:"title"`
+
+	/*Kind defined:
+	 * The kind of the code action.
+	 *
+	 * Used to filter code actions.
+	 */
+	Kind CodeActionKind `json:"kind,omitempty"`
+
+	/*Diagnostics defined:
+	 * The diagnostics that this code action resolves.
+	 */
+	Diagnostics []Diagnostic `json:"diagnostics,omitempty"`
+
+	/*IsPreferred defined:
+	 * Marks this as a preferred action. Preferred actions are used by the `auto fix` command and can be targeted
+	 * by keybindings.
+	 *
+	 * A quick fix should be marked preferred if it properly addresses the underlying error.
+	 * A refactoring should be marked preferred if it is the most reasonable choice of actions to take.
+	 *
+	 * @since 3.15.0
+	 */
+	IsPreferred bool `json:"isPreferred,omitempty"`
+
+	/*Edit defined:
+	 * The workspace edit this code action performs.
+	 */
+	Edit *WorkspaceEdit `json:"edit,omitempty"`
+
+	/*Command defined:
+	 * A command this code action executes. If a code action
+	 * provides a edit and a command, first the edit is
+	 * executed and then the command.
+	 */
+	Command *Command `json:"command,omitempty"`
+}
+
+/*CodeLens defined:
+ * A code lens represents a [command](#Command) that should be shown along with
+ * source text, like the number of references, a way to run tests, etc.
+ *
+ * A code lens is _unresolved_ when no command is associated to it. For performance
+ * reasons the creation of a code lens and resolving should be done to two stages.
+ */
+type CodeLens struct {
+
+	/*Range defined:
+	 * The range in which this code lens is valid. Should only span a single line.
+	 */
+	Range Range `json:"range"`
+
+	/*Command defined:
+	 * The command this code lens represents.
+	 */
+	Command *Command `json:"command,omitempty"`
+
+	/*Data defined:
+	 * An data entry field that is preserved on a code lens item between
+	 * a [CodeLensRequest](#CodeLensRequest) and a [CodeLensResolveRequest]
+	 * (#CodeLensResolveRequest)
+	 */
+	Data interface{} `json:"data,omitempty"`
+}
+
+/*FormattingOptions defined:
+ * Value-object describing what options formatting should use.
+ */
+type FormattingOptions struct {
+
+	/*TabSize defined:
+	 * Size of a tab in spaces.
+	 */
+	TabSize float64 `json:"tabSize"`
+
+	/*InsertSpaces defined:
+	 * Prefer spaces over tabs.
+	 */
+	InsertSpaces bool `json:"insertSpaces"`
+
+	/*TrimTrailingWhitespace defined:
+	 * Trim trailing whitespaces on a line.
+	 *
+	 * @since 3.15.0
+	 */
+	TrimTrailingWhitespace bool `json:"trimTrailingWhitespace,omitempty"`
+
+	/*InsertFinalNewline defined:
+	 * Insert a newline character at the end of the file if one does not exist.
+	 *
+	 * @since 3.15.0
+	 */
+	InsertFinalNewline bool `json:"insertFinalNewline,omitempty"`
+
+	/*TrimFinalNewlines defined:
+	 * Trim all newlines after the final newline at the end of the file.
+	 *
+	 * @since 3.15.0
+	 */
+	TrimFinalNewlines bool `json:"trimFinalNewlines,omitempty"`
+
+	/*Key defined:
+	 * Signature for further properties.
+	 */
+	Key map[string]bool `json:"key"` // [key: string]: boolean | number | string | undefined;
+}
+
+/*DocumentLink defined:
+ * A document link is a range in a text document that links to an internal or external resource, like another
+ * text document or a web site.
+ */
+type DocumentLink struct {
+
+	/*Range defined:
+	 * The range this link applies to.
+	 */
+	Range Range `json:"range"`
+
+	/*Target defined:
+	 * The uri this link points to.
+	 */
+	Target string `json:"target,omitempty"`
+
+	/*Tooltip defined:
+	 * The tooltip text when you hover over this link.
+	 *
+	 * If a tooltip is provided, is will be displayed in a string that includes instructions on how to
+	 * trigger the link, such as `{0} (ctrl + click)`. The specific instructions vary depending on OS,
+	 * user settings, and localization.
+	 *
+	 * @since 3.15.0
+	 */
+	Tooltip string `json:"tooltip,omitempty"`
+
+	/*Data defined:
+	 * A data entry field that is preserved on a document link between a
+	 * DocumentLinkRequest and a DocumentLinkResolveRequest.
+	 */
+	Data interface{} `json:"data,omitempty"`
+}
+
+/*SelectionRange defined:
+ * A selection range represents a part of a selection hierarchy. A selection range
+ * may have a parent selection range that contains it.
+ */
+type SelectionRange struct {
+
+	/*Range defined:
+	 * The [range](#Range) of this selection range.
+	 */
+	Range Range `json:"range"`
+
+	/*Parent defined:
+	 * The parent selection range containing this range. Therefore `parent.range` must contain `this.range`.
+	 */
+	Parent *SelectionRange `json:"parent,omitempty"`
+}
+
+/*TextDocument defined:
+ * A simple text document. Not to be implemented.
+ */
+type TextDocument struct {
+
+	/*URI defined:
+	 * The associated URI for this document. Most documents have the __file__-scheme, indicating that they
+	 * represent files on disk. However, some documents may have other schemes indicating that they are not
+	 * available on disk.
+	 *
+	 * @readonly
+	 */
+	URI DocumentURI `json:"uri"`
+
+	/*LanguageID defined:
+	 * The identifier of the language associated with this document.
+	 *
+	 * @readonly
+	 */
+	LanguageID string `json:"languageId"`
+
+	/*Version defined:
+	 * The version number of this document (it will increase after each
+	 * change, including undo/redo).
+	 *
+	 * @readonly
+	 */
+	Version float64 `json:"version"`
+
+	/*LineCount defined:
+	 * The number of lines in this document.
+	 *
+	 * @readonly
+	 */
+	LineCount float64 `json:"lineCount"`
+}
+
+/*TextDocumentChangeEvent defined:
+ * Event to signal changes to a simple text document.
+ */
+type TextDocumentChangeEvent struct {
+
+	/*Document defined:
+	 * The document that has changed.
+	 */
+	Document TextDocument `json:"document"`
+}
+
+// TextDocumentWillSaveEvent is
+type TextDocumentWillSaveEvent struct {
+
+	/*Document defined:
+	 * The document that will be saved
+	 */
+	Document TextDocument `json:"document"`
+
+	/*Reason defined:
+	 * The reason why save was triggered.
+	 */
+	Reason TextDocumentSaveReason `json:"reason"`
+}
+
+/*TextDocumentContentChangeEvent defined:
+ * An event describing a change to a text document. If range and rangeLength are omitted
+ * the new text is considered to be the full content of the document.
+ */
+type TextDocumentContentChangeEvent struct {
+
+	/*Range defined:
+	 * The range of the document that changed.
+	 */
+	Range *Range `json:"range,omitempty"`
+
+	/*RangeLength defined:
+	 * The length of the range that got replaced.
+	 */
+	RangeLength float64 `json:"rangeLength,omitempty"`
+
+	/*Text defined:
+	 * The new text of the document.
+	 */
+	Text string `json:"text"`
+}
+
+// ProgressParams is
+type ProgressParams struct {
+
+	/*Token defined:
+	 * The progress token provided by the client or server.
+	 */
+	Token ProgressToken `json:"token"`
+
+	/*Value defined:
+	 * The progress data.
+	 */
+	Value interface{} `json:"value"`
+}
+
+// SetTraceParams is
+type SetTraceParams struct {
+
+	// Value is
+	Value TraceValues `json:"value"`
+}
+
+// LogTraceParams is
+type LogTraceParams struct {
+
+	// Message is
+	Message string `json:"message"`
+
+	// Verbose is
+	Verbose string `json:"verbose,omitempty"`
+}
+
+// Tracer is
+type Tracer struct {
+}
+
+// FoldingRangeKind defines constants
+type FoldingRangeKind string
+
+// ResourceOperationKind defines constants
+type ResourceOperationKind string
+
+// FailureHandlingKind defines constants
+type FailureHandlingKind string
+
+// InitializeError defines constants
+type InitializeError float64
+
+// MessageType defines constants
+type MessageType float64
+
+// TextDocumentSyncKind defines constants
+type TextDocumentSyncKind float64
+
+// FileChangeType defines constants
+type FileChangeType float64
+
+// WatchKind defines constants
+type WatchKind float64
+
+// CompletionTriggerKind defines constants
+type CompletionTriggerKind float64
+
+// SignatureHelpTriggerKind defines constants
+type SignatureHelpTriggerKind float64
+
+// DiagnosticSeverity defines constants
+type DiagnosticSeverity float64
+
+// DiagnosticTag defines constants
+type DiagnosticTag float64
+
+// MarkupKind defines constants
+type MarkupKind string
+
+// CompletionItemKind defines constants
+type CompletionItemKind float64
+
+// InsertTextFormat defines constants
+type InsertTextFormat float64
+
+// CompletionItemTag defines constants
+type CompletionItemTag float64
+
+// DocumentHighlightKind defines constants
+type DocumentHighlightKind float64
+
+// SymbolKind defines constants
+type SymbolKind float64
+
+// CodeActionKind defines constants
+type CodeActionKind string
+
+// TextDocumentSaveReason defines constants
+type TextDocumentSaveReason float64
+
+// ErrorCodes defines constants
+type ErrorCodes float64
+
+// Touch defines constants
+type Touch float64
+
+// Trace defines constants
+type Trace string
+
+// TraceFormat defines constants
+type TraceFormat string
+
+// ConnectionErrors defines constants
+type ConnectionErrors float64
+
+// ConnectionState defines constants
+type ConnectionState float64
+
+const (
+
+	/*Comment defined:
+	 * Folding range for a comment
+	 */
+	Comment FoldingRangeKind = "comment"
+
+	/*Imports defined:
+	 * Folding range for a imports or includes
+	 */
+	Imports FoldingRangeKind = "imports"
+
+	/*Region defined:
+	 * Folding range for a region (e.g. `#region`)
+	 */
+	Region FoldingRangeKind = "region"
+
+	/*Create defined:
+	 * Supports creating new files and folders.
+	 */
+	Create ResourceOperationKind = "create"
+
+	/*Rename defined:
+	 * Supports renaming existing files and folders.
+	 */
+	Rename ResourceOperationKind = "rename"
+
+	/*Delete defined:
+	 * Supports deleting existing files and folders.
+	 */
+	Delete ResourceOperationKind = "delete"
+
+	/*Abort defined:
+	 * Applying the workspace change is simply aborted if one of the changes provided
+	 * fails. All operations executed before the failing operation stay executed.
+	 */
+	Abort FailureHandlingKind = "abort"
+
+	/*Transactional defined:
+	 * All operations are executed transactional. That means they either all
+	 * succeed or no changes at all are applied to the workspace.
+	 */
+	Transactional FailureHandlingKind = "transactional"
+
+	/*TextOnlyTransactional defined:
+	 * If the workspace edit contains only textual file changes they are executed transactional.
+	 * If resource changes (create, rename or delete file) are part of the change the failure
+	 * handling startegy is abort.
+	 */
+	TextOnlyTransactional FailureHandlingKind = "textOnlyTransactional"
+
+	/*Undo defined:
+	 * The client tries to undo the operations already executed. But there is no
+	 * guaruntee that this is succeeding.
+	 */
+	Undo FailureHandlingKind = "undo"
+
+	/*UnknownProtocolVersion defined:
+	 * If the protocol version provided by the client can't be handled by the server.
+	 * @deprecated This initialize error got replaced by client capabilities. There is
+	 * no version handshake in version 3.0x
+	 */
+	UnknownProtocolVersion InitializeError = 1
+
+	/*Error defined:
+	 * An error message.
+	 */
+	Error MessageType = 1
+
+	/*Warning defined:
+	 * A warning message.
+	 */
+	Warning MessageType = 2
+
+	/*Info defined:
+	 * An information message.
+	 */
+	Info MessageType = 3
+
+	/*Log defined:
+	 * A log message.
+	 */
+	Log MessageType = 4
+
+	/*None defined:
+	 * Documents should not be synced at all.
+	 */
+	None TextDocumentSyncKind = 0
+
+	/*Full defined:
+	 * Documents are synced by always sending the full content
+	 * of the document.
+	 */
+	Full TextDocumentSyncKind = 1
+
+	/*Incremental defined:
+	 * Documents are synced by sending the full content on open.
+	 * After that only incremental updates to the document are
+	 * send.
+	 */
+	Incremental TextDocumentSyncKind = 2
+
+	/*Created defined:
+	 * The file got created.
+	 */
+	Created FileChangeType = 1
+
+	/*Changed defined:
+	 * The file got changed.
+	 */
+	Changed FileChangeType = 2
+
+	/*Deleted defined:
+	 * The file got deleted.
+	 */
+	Deleted FileChangeType = 3
+
+	/*WatchCreate defined:
+	 * Interested in create events.
+	 */
+	WatchCreate WatchKind = 1
+
+	/*WatchChange defined:
+	 * Interested in change events
+	 */
+	WatchChange WatchKind = 2
+
+	/*WatchDelete defined:
+	 * Interested in delete events
+	 */
+	WatchDelete WatchKind = 4
+
+	/*Invoked defined:
+	 * Completion was triggered by typing an identifier (24x7 code
+	 * complete), manual invocation (e.g Ctrl+Space) or via API.
+	 */
+	Invoked CompletionTriggerKind = 1
+
+	/*TriggerCharacter defined:
+	 * Completion was triggered by a trigger character specified by
+	 * the `triggerCharacters` properties of the `CompletionRegistrationOptions`.
+	 */
+	TriggerCharacter CompletionTriggerKind = 2
+
+	/*TriggerForIncompleteCompletions defined:
+	 * Completion was re-triggered as current completion list is incomplete
+	 */
+	TriggerForIncompleteCompletions CompletionTriggerKind = 3
+
+	/*ContentChange defined:
+	 * Signature help was triggered by the cursor moving or by the document content changing.
+	 */
+	ContentChange SignatureHelpTriggerKind = 3
+
+	/*SeverityError defined:
+	 * Reports an error.
+	 */
+	SeverityError DiagnosticSeverity = 1
+
+	/*SeverityWarning defined:
+	 * Reports a warning.
+	 */
+	SeverityWarning DiagnosticSeverity = 2
+
+	/*SeverityInformation defined:
+	 * Reports an information.
+	 */
+	SeverityInformation DiagnosticSeverity = 3
+
+	/*SeverityHint defined:
+	 * Reports a hint.
+	 */
+	SeverityHint DiagnosticSeverity = 4
+
+	/*Unnecessary defined:
+	 * Unused or unnecessary code.
+	 *
+	 * Clients are allowed to render diagnostics with this tag faded out instead of having
+	 * an error squiggle.
+	 */
+	Unnecessary DiagnosticTag = 1
+
+	/*Deprecated defined:
+	 * Deprecated or obsolete code.
+	 *
+	 * Clients are allowed to rendered diagnostics with this tag strike through.
+	 */
+	Deprecated DiagnosticTag = 2
+
+	/*PlainText defined:
+	 * Plain text is supported as a content format
+	 */
+	PlainText MarkupKind = "plaintext"
+
+	/*Markdown defined:
+	 * Markdown is supported as a content format
+	 */
+	Markdown MarkupKind = "markdown"
+
+	// TextCompletion is
+	TextCompletion CompletionItemKind = 1
+
+	// MethodCompletion is
+	MethodCompletion CompletionItemKind = 2
+
+	// FunctionCompletion is
+	FunctionCompletion CompletionItemKind = 3
+
+	// ConstructorCompletion is
+	ConstructorCompletion CompletionItemKind = 4
+
+	// FieldCompletion is
+	FieldCompletion CompletionItemKind = 5
+
+	// VariableCompletion is
+	VariableCompletion CompletionItemKind = 6
+
+	// ClassCompletion is
+	ClassCompletion CompletionItemKind = 7
+
+	// InterfaceCompletion is
+	InterfaceCompletion CompletionItemKind = 8
+
+	// ModuleCompletion is
+	ModuleCompletion CompletionItemKind = 9
+
+	// PropertyCompletion is
+	PropertyCompletion CompletionItemKind = 10
+
+	// UnitCompletion is
+	UnitCompletion CompletionItemKind = 11
+
+	// ValueCompletion is
+	ValueCompletion CompletionItemKind = 12
+
+	// EnumCompletion is
+	EnumCompletion CompletionItemKind = 13
+
+	// KeywordCompletion is
+	KeywordCompletion CompletionItemKind = 14
+
+	// SnippetCompletion is
+	SnippetCompletion CompletionItemKind = 15
+
+	// ColorCompletion is
+	ColorCompletion CompletionItemKind = 16
+
+	// FileCompletion is
+	FileCompletion CompletionItemKind = 17
+
+	// ReferenceCompletion is
+	ReferenceCompletion CompletionItemKind = 18
+
+	// FolderCompletion is
+	FolderCompletion CompletionItemKind = 19
+
+	// EnumMemberCompletion is
+	EnumMemberCompletion CompletionItemKind = 20
+
+	// ConstantCompletion is
+	ConstantCompletion CompletionItemKind = 21
+
+	// StructCompletion is
+	StructCompletion CompletionItemKind = 22
+
+	// EventCompletion is
+	EventCompletion CompletionItemKind = 23
+
+	// OperatorCompletion is
+	OperatorCompletion CompletionItemKind = 24
+
+	// TypeParameterCompletion is
+	TypeParameterCompletion CompletionItemKind = 25
+
+	/*PlainTextTextFormat defined:
+	 * The primary text to be inserted is treated as a plain string.
+	 */
+	PlainTextTextFormat InsertTextFormat = 1
+
+	/*SnippetTextFormat defined:
+	 * The primary text to be inserted is treated as a snippet.
+	 *
+	 * A snippet can define tab stops and placeholders with `$1`, `$2`
+	 * and `${3:foo}`. `$0` defines the final tab stop, it defaults to
+	 * the end of the snippet. Placeholders with equal identifiers are linked,
+	 * that is typing in one will update others too.
+	 *
+	 * See also: https://github.com/Microsoft/vscode/blob/master/src/vs/editor/contrib/snippet/common/snippet.md
+	 */
+	SnippetTextFormat InsertTextFormat = 2
+
+	/*Text defined:
+	 * A textual occurrence.
+	 */
+	Text DocumentHighlightKind = 1
+
+	/*Read defined:
+	 * Read-access of a symbol, like reading a variable.
+	 */
+	Read DocumentHighlightKind = 2
+
+	/*Write defined:
+	 * Write-access of a symbol, like writing to a variable.
+	 */
+	Write DocumentHighlightKind = 3
+
+	// File is
+	File SymbolKind = 1
+
+	// Module is
+	Module SymbolKind = 2
+
+	// Namespace is
+	Namespace SymbolKind = 3
+
+	// Package is
+	Package SymbolKind = 4
+
+	// Class is
+	Class SymbolKind = 5
+
+	// Method is
+	Method SymbolKind = 6
+
+	// Property is
+	Property SymbolKind = 7
+
+	// Field is
+	Field SymbolKind = 8
+
+	// Constructor is
+	Constructor SymbolKind = 9
+
+	// Enum is
+	Enum SymbolKind = 10
+
+	// Interface is
+	Interface SymbolKind = 11
+
+	// Function is
+	Function SymbolKind = 12
+
+	// Variable is
+	Variable SymbolKind = 13
+
+	// Constant is
+	Constant SymbolKind = 14
+
+	// String is
+	String SymbolKind = 15
+
+	// Number is
+	Number SymbolKind = 16
+
+	// Boolean is
+	Boolean SymbolKind = 17
+
+	// Array is
+	Array SymbolKind = 18
+
+	// Object is
+	Object SymbolKind = 19
+
+	// Key is
+	Key SymbolKind = 20
+
+	// Null is
+	Null SymbolKind = 21
+
+	// EnumMember is
+	EnumMember SymbolKind = 22
+
+	// Struct is
+	Struct SymbolKind = 23
+
+	// Event is
+	Event SymbolKind = 24
+
+	// Operator is
+	Operator SymbolKind = 25
+
+	// TypeParameter is
+	TypeParameter SymbolKind = 26
+
+	/*Empty defined:
+	 * Empty kind.
+	 */
+	Empty CodeActionKind = ""
+
+	/*QuickFix defined:
+	 * Base kind for quickfix actions: 'quickfix'
+	 */
+	QuickFix CodeActionKind = "quickfix"
+
+	/*Refactor defined:
+	 * Base kind for refactoring actions: 'refactor'
+	 */
+	Refactor CodeActionKind = "refactor"
+
+	/*RefactorExtract defined:
+	 * Base kind for refactoring extraction actions: 'refactor.extract'
+	 *
+	 * Example extract actions:
+	 *
+	 * - Extract method
+	 * - Extract function
+	 * - Extract variable
+	 * - Extract interface from class
+	 * - ...
+	 */
+	RefactorExtract CodeActionKind = "refactor.extract"
+
+	/*RefactorInline defined:
+	 * Base kind for refactoring inline actions: 'refactor.inline'
+	 *
+	 * Example inline actions:
+	 *
+	 * - Inline function
+	 * - Inline variable
+	 * - Inline constant
+	 * - ...
+	 */
+	RefactorInline CodeActionKind = "refactor.inline"
+
+	/*RefactorRewrite defined:
+	 * Base kind for refactoring rewrite actions: 'refactor.rewrite'
+	 *
+	 * Example rewrite actions:
+	 *
+	 * - Convert JavaScript function to class
+	 * - Add or remove parameter
+	 * - Encapsulate field
+	 * - Make method static
+	 * - Move method to base class
+	 * - ...
+	 */
+	RefactorRewrite CodeActionKind = "refactor.rewrite"
+
+	/*Source defined:
+	 * Base kind for source actions: `source`
+	 *
+	 * Source code actions apply to the entire file.
+	 */
+	Source CodeActionKind = "source"
+
+	/*SourceOrganizeImports defined:
+	 * Base kind for an organize imports source action: `source.organizeImports`
+	 */
+	SourceOrganizeImports CodeActionKind = "source.organizeImports"
+
+	/*Manual defined:
+	 * Manually triggered, e.g. by the user pressing save, by starting debugging,
+	 * or by an API call.
+	 */
+	Manual TextDocumentSaveReason = 1
+
+	/*AfterDelay defined:
+	 * Automatic after a delay.
+	 */
+	AfterDelay TextDocumentSaveReason = 2
+
+	/*FocusOut defined:
+	 * When the editor lost focus.
+	 */
+	FocusOut TextDocumentSaveReason = 3
+
+	// MessageWriteError is
+	MessageWriteError ErrorCodes = 1
+
+	// MessageReadError is
+	MessageReadError ErrorCodes = 2
+
+	// First is
+	First Touch = 1
+
+	// Last is
+	Last Touch = 2
+
+	// JSON is
+	JSON TraceFormat = "json"
+
+	/*Closed defined:
+	 * The connection is closed.
+	 */
+	Closed ConnectionErrors = 1
+
+	/*Disposed defined:
+	 * The connection got disposed.
+	 */
+	Disposed ConnectionErrors = 2
+
+	/*AlreadyListening defined:
+	 * The connection is already in listening mode.
+	 */
+	AlreadyListening ConnectionErrors = 3
+
+	// New is
+	New ConnectionState = 1
+
+	// Listening is
+	Listening ConnectionState = 2
+)
+
+// DocumentFilter is a type
+/**
+ * A document filter denotes a document by different properties like
+ * the [language](#TextDocument.languageId), the [scheme](#Uri.scheme) of
+ * its resource, or a glob-pattern that is applied to the [path](#TextDocument.fileName).
+ *
+ * Glob patterns can have the following syntax:
+ * - `*` to match one or more characters in a path segment
+ * - `?` to match on one character in a path segment
+ * - `**` to match any number of path segments, including none
+ * - `{}` to group conditions (e.g. `**​/*.{ts,js}` matches all TypeScript and JavaScript files)
+ * - `[]` to declare a range of characters to match in a path segment (e.g., `example.[0-9]` to match on `example.0`, `example.1`, …)
+ * - `[!...]` to negate a range of characters to match in a path segment (e.g., `example.[!0-9]` to match on `example.a`, `example.b`, but not `example.0`)
+ *
+ * @sample A language filter that applies to typescript files on disk: `{ language: 'typescript', scheme: 'file' }`
+ * @sample A language filter that applies to all package.json paths: `{ language: 'json', pattern: '**package.json' }`
+ */
+type DocumentFilter = struct {
+
+	/*Language defined: A language id, like `typescript`. */
+	Language string `json:"language,omitempty"`
+
+	/*Scheme defined: A Uri [scheme](#Uri.scheme), like `file` or `untitled`. */
+	Scheme string `json:"scheme,omitempty"`
+
+	/*Pattern defined: A glob pattern, like `*.{ts,js}`. */
+	Pattern string `json:"pattern,omitempty"`
+}
+
+// DocumentSelector is a type
+/**
+ * A document selector is the combination of one or many document filters.
+ *
+ * @sample `let sel:DocumentSelector = [{ language: 'typescript' }, { language: 'json', pattern: '**∕tsconfig.json' }]`;
+ */
+type DocumentSelector = []DocumentFilter
+
+// DocumentURI is a type
+/**
+ * A tagging type for string properties that are actually URIs.
+ */
+type DocumentURI = string
+
+// MarkedString is a type
+/**
+ * MarkedString can be used to render human readable text. It is either a markdown string
+ * or a code-block that provides a language and a code snippet. The language identifier
+ * is semantically equal to the optional language identifier in fenced code blocks in GitHub
+ * issues. See https://help.github.com/articles/creating-and-highlighting-code-blocks/#syntax-highlighting
+ *
+ * The pair of a language and a value is an equivalent to markdown:
+ * ```${language}
+ * ${value}
+ * ```
+ *
+ * Note that markdown strings will be sanitized - that means html will be escaped.
+ * @deprecated use MarkupContent instead.
+ */
+type MarkedString = string
+
+// DefinitionLink is a type
+/**
+ * Information about where a symbol is defined.
+ *
+ * Provides additional metadata over normal [location](#Location) definitions, including the range of
+ * the defining symbol
+ */
+type DefinitionLink = LocationLink
+
+// DeclarationLink is a type
+/**
+ * Information about where a symbol is declared.
+ *
+ * Provides additional metadata over normal [location](#Location) declarations, including the range of
+ * the declaring symbol.
+ *
+ * Servers should prefer returning `DeclarationLink` over `Declaration` if supported
+ * by the client.
+ */
+type DeclarationLink = LocationLink
+
+// LSPMessageType is a type
+/**
+ * A LSP Log Entry.
+ */
+type LSPMessageType = string
+
+// ProgressToken is a type
+type ProgressToken = interface{} // number | string
+// TraceValues is a type
+type TraceValues = string
diff --git a/utils/vscode/src/lsp/protocol/tsserver.go b/utils/vscode/src/lsp/protocol/tsserver.go
new file mode 100644
index 0000000..d760501
--- /dev/null
+++ b/utils/vscode/src/lsp/protocol/tsserver.go
@@ -0,0 +1,842 @@
+// Copyright 2019 The Go Authors.
+//
+// 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.
+
+package protocol
+
+import (
+	"context"
+	"encoding/json"
+	"log"
+
+	"../jsonrpc2"
+)
+
+type Server interface {
+	DidChangeWorkspaceFolders(context.Context, *DidChangeWorkspaceFoldersParams) error
+	Initialized(context.Context, *InitializedParams) error
+	Exit(context.Context) error
+	DidChangeConfiguration(context.Context, *DidChangeConfigurationParams) error
+	DidOpen(context.Context, *DidOpenTextDocumentParams) error
+	DidChange(context.Context, *DidChangeTextDocumentParams) error
+	DidClose(context.Context, *DidCloseTextDocumentParams) error
+	DidSave(context.Context, *DidSaveTextDocumentParams) error
+	WillSave(context.Context, *WillSaveTextDocumentParams) error
+	DidChangeWatchedFiles(context.Context, *DidChangeWatchedFilesParams) error
+	Progress(context.Context, *ProgressParams) error
+	SetTraceNotification(context.Context, *SetTraceParams) error
+	LogTraceNotification(context.Context, *LogTraceParams) error
+	Implementation(context.Context, *ImplementationParams) ([]Location, error)
+	TypeDefinition(context.Context, *TypeDefinitionParams) ([]Location, error)
+	DocumentColor(context.Context, *DocumentColorParams) ([]ColorInformation, error)
+	ColorPresentation(context.Context, *ColorPresentationParams) ([]ColorPresentation, error)
+	FoldingRange(context.Context, *FoldingRangeParams) ([]FoldingRange, error)
+	Declaration(context.Context, *DeclarationParams) ([]DeclarationLink, error)
+	SelectionRange(context.Context, *SelectionRangeParams) ([]SelectionRange, error)
+	Initialize(context.Context, *ParamInitia) (*InitializeResult, error)
+	Shutdown(context.Context) error
+	WillSaveWaitUntil(context.Context, *WillSaveTextDocumentParams) ([]TextEdit, error)
+	Completion(context.Context, *CompletionParams) (*CompletionList, error)
+	Resolve(context.Context, *CompletionItem) (*CompletionItem, error)
+	Hover(context.Context, *HoverParams) (*Hover, error)
+	SignatureHelp(context.Context, *SignatureHelpParams) (*SignatureHelp, error)
+	Definition(context.Context, *DefinitionParams) ([]Location, error)
+	References(context.Context, *ReferenceParams) ([]Location, error)
+	DocumentHighlight(context.Context, *DocumentHighlightParams) ([]DocumentHighlight, error)
+	DocumentSymbol(context.Context, *DocumentSymbolParams) ([]DocumentSymbol, error)
+	CodeAction(context.Context, *CodeActionParams) ([]CodeAction, error)
+	Symbol(context.Context, *WorkspaceSymbolParams) ([]SymbolInformation, error)
+	CodeLens(context.Context, *CodeLensParams) ([]CodeLens, error)
+	ResolveCodeLens(context.Context, *CodeLens) (*CodeLens, error)
+	DocumentLink(context.Context, *DocumentLinkParams) ([]DocumentLink, error)
+	ResolveDocumentLink(context.Context, *DocumentLink) (*DocumentLink, error)
+	Formatting(context.Context, *DocumentFormattingParams) ([]TextEdit, error)
+	RangeFormatting(context.Context, *DocumentRangeFormattingParams) ([]TextEdit, error)
+	OnTypeFormatting(context.Context, *DocumentOnTypeFormattingParams) ([]TextEdit, error)
+	Rename(context.Context, *RenameParams) (*WorkspaceEdit, error)
+	PrepareRename(context.Context, *PrepareRenameParams) (*Range, error)
+	ExecuteCommand(context.Context, *ExecuteCommandParams) (interface{}, error)
+}
+
+func (h serverHandler) Deliver(ctx context.Context, r *jsonrpc2.Request, delivered bool) bool {
+	if delivered {
+		return false
+	}
+	if ctx.Err() != nil {
+		r.Reply(ctx, nil, jsonrpc2.NewErrorf(RequestCancelledError, ""))
+		return true
+	}
+	switch r.Method {
+	case "workspace/didChangeWorkspaceFolders": // notif
+		var params DidChangeWorkspaceFoldersParams
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, r, err)
+			return true
+		}
+		if err := h.server.DidChangeWorkspaceFolders(ctx, &params); err != nil {
+			log.Printf("%v", err)
+		}
+		return true
+	case "initialized": // notif
+		var params InitializedParams
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, r, err)
+			return true
+		}
+		if err := h.server.Initialized(ctx, &params); err != nil {
+			log.Printf("%v", err)
+		}
+		return true
+	case "exit": // notif
+		if err := h.server.Exit(ctx); err != nil {
+			log.Printf("%v", err)
+		}
+		return true
+	case "workspace/didChangeConfiguration": // notif
+		var params DidChangeConfigurationParams
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, r, err)
+			return true
+		}
+		if err := h.server.DidChangeConfiguration(ctx, &params); err != nil {
+			log.Printf("%v", err)
+		}
+		return true
+	case "textDocument/didOpen": // notif
+		var params DidOpenTextDocumentParams
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, r, err)
+			return true
+		}
+		if err := h.server.DidOpen(ctx, &params); err != nil {
+			log.Printf("%v", err)
+		}
+		return true
+	case "textDocument/didChange": // notif
+		var params DidChangeTextDocumentParams
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, r, err)
+			return true
+		}
+		if err := h.server.DidChange(ctx, &params); err != nil {
+			log.Printf("%v", err)
+		}
+		return true
+	case "textDocument/didClose": // notif
+		var params DidCloseTextDocumentParams
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, r, err)
+			return true
+		}
+		if err := h.server.DidClose(ctx, &params); err != nil {
+			log.Printf("%v", err)
+		}
+		return true
+	case "textDocument/didSave": // notif
+		var params DidSaveTextDocumentParams
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, r, err)
+			return true
+		}
+		if err := h.server.DidSave(ctx, &params); err != nil {
+			log.Printf("%v", err)
+		}
+		return true
+	case "textDocument/willSave": // notif
+		var params WillSaveTextDocumentParams
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, r, err)
+			return true
+		}
+		if err := h.server.WillSave(ctx, &params); err != nil {
+			log.Printf("%v", err)
+		}
+		return true
+	case "workspace/didChangeWatchedFiles": // notif
+		var params DidChangeWatchedFilesParams
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, r, err)
+			return true
+		}
+		if err := h.server.DidChangeWatchedFiles(ctx, &params); err != nil {
+			log.Printf("%v", err)
+		}
+		return true
+	case "$/progress": // notif
+		var params ProgressParams
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, r, err)
+			return true
+		}
+		if err := h.server.Progress(ctx, &params); err != nil {
+			log.Printf("%v", err)
+		}
+		return true
+	case "$/setTraceNotification": // notif
+		var params SetTraceParams
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, r, err)
+			return true
+		}
+		if err := h.server.SetTraceNotification(ctx, &params); err != nil {
+			log.Printf("%v", err)
+		}
+		return true
+	case "$/logTraceNotification": // notif
+		var params LogTraceParams
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, r, err)
+			return true
+		}
+		if err := h.server.LogTraceNotification(ctx, &params); err != nil {
+			log.Printf("%v", err)
+		}
+		return true
+	case "textDocument/implementation": // req
+		var params ImplementationParams
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, r, err)
+			return true
+		}
+		resp, err := h.server.Implementation(ctx, &params)
+		if err := r.Reply(ctx, resp, err); err != nil {
+			log.Printf("%v", err)
+		}
+		return true
+	case "textDocument/typeDefinition": // req
+		var params TypeDefinitionParams
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, r, err)
+			return true
+		}
+		resp, err := h.server.TypeDefinition(ctx, &params)
+		if err := r.Reply(ctx, resp, err); err != nil {
+			log.Printf("%v", err)
+		}
+		return true
+	case "textDocument/documentColor": // req
+		var params DocumentColorParams
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, r, err)
+			return true
+		}
+		resp, err := h.server.DocumentColor(ctx, &params)
+		if err := r.Reply(ctx, resp, err); err != nil {
+			log.Printf("%v", err)
+		}
+		return true
+	case "textDocument/colorPresentation": // req
+		var params ColorPresentationParams
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, r, err)
+			return true
+		}
+		resp, err := h.server.ColorPresentation(ctx, &params)
+		if err := r.Reply(ctx, resp, err); err != nil {
+			log.Printf("%v", err)
+		}
+		return true
+	case "textDocument/foldingRange": // req
+		var params FoldingRangeParams
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, r, err)
+			return true
+		}
+		resp, err := h.server.FoldingRange(ctx, &params)
+		if err := r.Reply(ctx, resp, err); err != nil {
+			log.Printf("%v", err)
+		}
+		return true
+	case "textDocument/declaration": // req
+		var params DeclarationParams
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, r, err)
+			return true
+		}
+		resp, err := h.server.Declaration(ctx, &params)
+		if err := r.Reply(ctx, resp, err); err != nil {
+			log.Printf("%v", err)
+		}
+		return true
+	case "textDocument/selectionRange": // req
+		var params SelectionRangeParams
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, r, err)
+			return true
+		}
+		resp, err := h.server.SelectionRange(ctx, &params)
+		if err := r.Reply(ctx, resp, err); err != nil {
+			log.Printf("%v", err)
+		}
+		return true
+	case "initialize": // req
+		var params ParamInitia
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, r, err)
+			return true
+		}
+		resp, err := h.server.Initialize(ctx, &params)
+		if err := r.Reply(ctx, resp, err); err != nil {
+			log.Printf("%v", err)
+		}
+		return true
+	case "shutdown": // req
+		if r.Params != nil {
+			r.Reply(ctx, nil, jsonrpc2.NewErrorf(jsonrpc2.CodeInvalidParams, "Expected no params"))
+			return true
+		}
+		err := h.server.Shutdown(ctx)
+		if err := r.Reply(ctx, nil, err); err != nil {
+			log.Printf("%v", err)
+		}
+		return true
+	case "textDocument/willSaveWaitUntil": // req
+		var params WillSaveTextDocumentParams
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, r, err)
+			return true
+		}
+		resp, err := h.server.WillSaveWaitUntil(ctx, &params)
+		if err := r.Reply(ctx, resp, err); err != nil {
+			log.Printf("%v", err)
+		}
+		return true
+	case "textDocument/completion": // req
+		var params CompletionParams
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, r, err)
+			return true
+		}
+		resp, err := h.server.Completion(ctx, &params)
+		if err := r.Reply(ctx, resp, err); err != nil {
+			log.Printf("%v", err)
+		}
+		return true
+	case "completionItem/resolve": // req
+		var params CompletionItem
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, r, err)
+			return true
+		}
+		resp, err := h.server.Resolve(ctx, &params)
+		if err := r.Reply(ctx, resp, err); err != nil {
+			log.Printf("%v", err)
+		}
+		return true
+	case "textDocument/hover": // req
+		var params HoverParams
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, r, err)
+			return true
+		}
+		resp, err := h.server.Hover(ctx, &params)
+		if err := r.Reply(ctx, resp, err); err != nil {
+			log.Printf("%v", err)
+		}
+		return true
+	case "textDocument/signatureHelp": // req
+		var params SignatureHelpParams
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, r, err)
+			return true
+		}
+		resp, err := h.server.SignatureHelp(ctx, &params)
+		if err := r.Reply(ctx, resp, err); err != nil {
+			log.Printf("%v", err)
+		}
+		return true
+	case "textDocument/definition": // req
+		var params DefinitionParams
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, r, err)
+			return true
+		}
+		resp, err := h.server.Definition(ctx, &params)
+		if err := r.Reply(ctx, resp, err); err != nil {
+			log.Printf("%v", err)
+		}
+		return true
+	case "textDocument/references": // req
+		var params ReferenceParams
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, r, err)
+			return true
+		}
+		resp, err := h.server.References(ctx, &params)
+		if err := r.Reply(ctx, resp, err); err != nil {
+			log.Printf("%v", err)
+		}
+		return true
+	case "textDocument/documentHighlight": // req
+		var params DocumentHighlightParams
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, r, err)
+			return true
+		}
+		resp, err := h.server.DocumentHighlight(ctx, &params)
+		if err := r.Reply(ctx, resp, err); err != nil {
+			log.Printf("%v", err)
+		}
+		return true
+	case "textDocument/documentSymbol": // req
+		var params DocumentSymbolParams
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, r, err)
+			return true
+		}
+		resp, err := h.server.DocumentSymbol(ctx, &params)
+		if err := r.Reply(ctx, resp, err); err != nil {
+			log.Printf("%v", err)
+		}
+		return true
+	case "textDocument/codeAction": // req
+		var params CodeActionParams
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, r, err)
+			return true
+		}
+		resp, err := h.server.CodeAction(ctx, &params)
+		if err := r.Reply(ctx, resp, err); err != nil {
+			log.Printf("%v", err)
+		}
+		return true
+	case "workspace/symbol": // req
+		var params WorkspaceSymbolParams
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, r, err)
+			return true
+		}
+		resp, err := h.server.Symbol(ctx, &params)
+		if err := r.Reply(ctx, resp, err); err != nil {
+			log.Printf("%v", err)
+		}
+		return true
+	case "textDocument/codeLens": // req
+		var params CodeLensParams
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, r, err)
+			return true
+		}
+		resp, err := h.server.CodeLens(ctx, &params)
+		if err := r.Reply(ctx, resp, err); err != nil {
+			log.Printf("%v", err)
+		}
+		return true
+	case "codeLens/resolve": // req
+		var params CodeLens
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, r, err)
+			return true
+		}
+		resp, err := h.server.ResolveCodeLens(ctx, &params)
+		if err := r.Reply(ctx, resp, err); err != nil {
+			log.Printf("%v", err)
+		}
+		return true
+	case "textDocument/documentLink": // req
+		var params DocumentLinkParams
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, r, err)
+			return true
+		}
+		resp, err := h.server.DocumentLink(ctx, &params)
+		if err := r.Reply(ctx, resp, err); err != nil {
+			log.Printf("%v", err)
+		}
+		return true
+	case "documentLink/resolve": // req
+		var params DocumentLink
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, r, err)
+			return true
+		}
+		resp, err := h.server.ResolveDocumentLink(ctx, &params)
+		if err := r.Reply(ctx, resp, err); err != nil {
+			log.Printf("%v", err)
+		}
+		return true
+	case "textDocument/formatting": // req
+		var params DocumentFormattingParams
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, r, err)
+			return true
+		}
+		resp, err := h.server.Formatting(ctx, &params)
+		if err := r.Reply(ctx, resp, err); err != nil {
+			log.Printf("%v", err)
+		}
+		return true
+	case "textDocument/rangeFormatting": // req
+		var params DocumentRangeFormattingParams
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, r, err)
+			return true
+		}
+		resp, err := h.server.RangeFormatting(ctx, &params)
+		if err := r.Reply(ctx, resp, err); err != nil {
+			log.Printf("%v", err)
+		}
+		return true
+	case "textDocument/onTypeFormatting": // req
+		var params DocumentOnTypeFormattingParams
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, r, err)
+			return true
+		}
+		resp, err := h.server.OnTypeFormatting(ctx, &params)
+		if err := r.Reply(ctx, resp, err); err != nil {
+			log.Printf("%v", err)
+		}
+		return true
+	case "textDocument/rename": // req
+		var params RenameParams
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, r, err)
+			return true
+		}
+		resp, err := h.server.Rename(ctx, &params)
+		if err := r.Reply(ctx, resp, err); err != nil {
+			log.Printf("%v", err)
+		}
+		return true
+	case "textDocument/prepareRename": // req
+		var params PrepareRenameParams
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, r, err)
+			return true
+		}
+		resp, err := h.server.PrepareRename(ctx, &params)
+		if err := r.Reply(ctx, resp, err); err != nil {
+			log.Printf("%v", err)
+		}
+		return true
+	case "workspace/executeCommand": // req
+		var params ExecuteCommandParams
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, r, err)
+			return true
+		}
+		resp, err := h.server.ExecuteCommand(ctx, &params)
+		if err := r.Reply(ctx, resp, err); err != nil {
+			log.Printf("%v", err)
+		}
+		return true
+
+	default:
+		return false
+	}
+}
+
+type serverDispatcher struct {
+	*jsonrpc2.Conn
+}
+
+func (s *serverDispatcher) DidChangeWorkspaceFolders(ctx context.Context, params *DidChangeWorkspaceFoldersParams) error {
+	return s.Conn.Notify(ctx, "workspace/didChangeWorkspaceFolders", params)
+}
+
+func (s *serverDispatcher) Initialized(ctx context.Context, params *InitializedParams) error {
+	return s.Conn.Notify(ctx, "initialized", params)
+}
+
+func (s *serverDispatcher) Exit(ctx context.Context) error {
+	return s.Conn.Notify(ctx, "exit", nil)
+}
+
+func (s *serverDispatcher) DidChangeConfiguration(ctx context.Context, params *DidChangeConfigurationParams) error {
+	return s.Conn.Notify(ctx, "workspace/didChangeConfiguration", params)
+}
+
+func (s *serverDispatcher) DidOpen(ctx context.Context, params *DidOpenTextDocumentParams) error {
+	return s.Conn.Notify(ctx, "textDocument/didOpen", params)
+}
+
+func (s *serverDispatcher) DidChange(ctx context.Context, params *DidChangeTextDocumentParams) error {
+	return s.Conn.Notify(ctx, "textDocument/didChange", params)
+}
+
+func (s *serverDispatcher) DidClose(ctx context.Context, params *DidCloseTextDocumentParams) error {
+	return s.Conn.Notify(ctx, "textDocument/didClose", params)
+}
+
+func (s *serverDispatcher) DidSave(ctx context.Context, params *DidSaveTextDocumentParams) error {
+	return s.Conn.Notify(ctx, "textDocument/didSave", params)
+}
+
+func (s *serverDispatcher) WillSave(ctx context.Context, params *WillSaveTextDocumentParams) error {
+	return s.Conn.Notify(ctx, "textDocument/willSave", params)
+}
+
+func (s *serverDispatcher) DidChangeWatchedFiles(ctx context.Context, params *DidChangeWatchedFilesParams) error {
+	return s.Conn.Notify(ctx, "workspace/didChangeWatchedFiles", params)
+}
+
+func (s *serverDispatcher) Progress(ctx context.Context, params *ProgressParams) error {
+	return s.Conn.Notify(ctx, "$/progress", params)
+}
+
+func (s *serverDispatcher) SetTraceNotification(ctx context.Context, params *SetTraceParams) error {
+	return s.Conn.Notify(ctx, "$/setTraceNotification", params)
+}
+
+func (s *serverDispatcher) LogTraceNotification(ctx context.Context, params *LogTraceParams) error {
+	return s.Conn.Notify(ctx, "$/logTraceNotification", params)
+}
+func (s *serverDispatcher) Implementation(ctx context.Context, params *ImplementationParams) ([]Location, error) {
+	var result []Location
+	if err := s.Conn.Call(ctx, "textDocument/implementation", params, &result); err != nil {
+		return nil, err
+	}
+	return result, nil
+}
+
+func (s *serverDispatcher) TypeDefinition(ctx context.Context, params *TypeDefinitionParams) ([]Location, error) {
+	var result []Location
+	if err := s.Conn.Call(ctx, "textDocument/typeDefinition", params, &result); err != nil {
+		return nil, err
+	}
+	return result, nil
+}
+
+func (s *serverDispatcher) DocumentColor(ctx context.Context, params *DocumentColorParams) ([]ColorInformation, error) {
+	var result []ColorInformation
+	if err := s.Conn.Call(ctx, "textDocument/documentColor", params, &result); err != nil {
+		return nil, err
+	}
+	return result, nil
+}
+
+func (s *serverDispatcher) ColorPresentation(ctx context.Context, params *ColorPresentationParams) ([]ColorPresentation, error) {
+	var result []ColorPresentation
+	if err := s.Conn.Call(ctx, "textDocument/colorPresentation", params, &result); err != nil {
+		return nil, err
+	}
+	return result, nil
+}
+
+func (s *serverDispatcher) FoldingRange(ctx context.Context, params *FoldingRangeParams) ([]FoldingRange, error) {
+	var result []FoldingRange
+	if err := s.Conn.Call(ctx, "textDocument/foldingRange", params, &result); err != nil {
+		return nil, err
+	}
+	return result, nil
+}
+
+func (s *serverDispatcher) Declaration(ctx context.Context, params *DeclarationParams) ([]DeclarationLink, error) {
+	var result []DeclarationLink
+	if err := s.Conn.Call(ctx, "textDocument/declaration", params, &result); err != nil {
+		return nil, err
+	}
+	return result, nil
+}
+
+func (s *serverDispatcher) SelectionRange(ctx context.Context, params *SelectionRangeParams) ([]SelectionRange, error) {
+	var result []SelectionRange
+	if err := s.Conn.Call(ctx, "textDocument/selectionRange", params, &result); err != nil {
+		return nil, err
+	}
+	return result, nil
+}
+
+func (s *serverDispatcher) Initialize(ctx context.Context, params *ParamInitia) (*InitializeResult, error) {
+	var result InitializeResult
+	if err := s.Conn.Call(ctx, "initialize", params, &result); err != nil {
+		return nil, err
+	}
+	return &result, nil
+}
+
+func (s *serverDispatcher) Shutdown(ctx context.Context) error {
+	return s.Conn.Call(ctx, "shutdown", nil, nil)
+}
+
+func (s *serverDispatcher) WillSaveWaitUntil(ctx context.Context, params *WillSaveTextDocumentParams) ([]TextEdit, error) {
+	var result []TextEdit
+	if err := s.Conn.Call(ctx, "textDocument/willSaveWaitUntil", params, &result); err != nil {
+		return nil, err
+	}
+	return result, nil
+}
+
+func (s *serverDispatcher) Completion(ctx context.Context, params *CompletionParams) (*CompletionList, error) {
+	var result CompletionList
+	if err := s.Conn.Call(ctx, "textDocument/completion", params, &result); err != nil {
+		return nil, err
+	}
+	return &result, nil
+}
+
+func (s *serverDispatcher) Resolve(ctx context.Context, params *CompletionItem) (*CompletionItem, error) {
+	var result CompletionItem
+	if err := s.Conn.Call(ctx, "completionItem/resolve", params, &result); err != nil {
+		return nil, err
+	}
+	return &result, nil
+}
+
+func (s *serverDispatcher) Hover(ctx context.Context, params *HoverParams) (*Hover, error) {
+	var result Hover
+	if err := s.Conn.Call(ctx, "textDocument/hover", params, &result); err != nil {
+		return nil, err
+	}
+	return &result, nil
+}
+
+func (s *serverDispatcher) SignatureHelp(ctx context.Context, params *SignatureHelpParams) (*SignatureHelp, error) {
+	var result SignatureHelp
+	if err := s.Conn.Call(ctx, "textDocument/signatureHelp", params, &result); err != nil {
+		return nil, err
+	}
+	return &result, nil
+}
+
+func (s *serverDispatcher) Definition(ctx context.Context, params *DefinitionParams) ([]Location, error) {
+	var result []Location
+	if err := s.Conn.Call(ctx, "textDocument/definition", params, &result); err != nil {
+		return nil, err
+	}
+	return result, nil
+}
+
+func (s *serverDispatcher) References(ctx context.Context, params *ReferenceParams) ([]Location, error) {
+	var result []Location
+	if err := s.Conn.Call(ctx, "textDocument/references", params, &result); err != nil {
+		return nil, err
+	}
+	return result, nil
+}
+
+func (s *serverDispatcher) DocumentHighlight(ctx context.Context, params *DocumentHighlightParams) ([]DocumentHighlight, error) {
+	var result []DocumentHighlight
+	if err := s.Conn.Call(ctx, "textDocument/documentHighlight", params, &result); err != nil {
+		return nil, err
+	}
+	return result, nil
+}
+
+func (s *serverDispatcher) DocumentSymbol(ctx context.Context, params *DocumentSymbolParams) ([]DocumentSymbol, error) {
+	var result []DocumentSymbol
+	if err := s.Conn.Call(ctx, "textDocument/documentSymbol", params, &result); err != nil {
+		return nil, err
+	}
+	return result, nil
+}
+
+func (s *serverDispatcher) CodeAction(ctx context.Context, params *CodeActionParams) ([]CodeAction, error) {
+	var result []CodeAction
+	if err := s.Conn.Call(ctx, "textDocument/codeAction", params, &result); err != nil {
+		return nil, err
+	}
+	return result, nil
+}
+
+func (s *serverDispatcher) Symbol(ctx context.Context, params *WorkspaceSymbolParams) ([]SymbolInformation, error) {
+	var result []SymbolInformation
+	if err := s.Conn.Call(ctx, "workspace/symbol", params, &result); err != nil {
+		return nil, err
+	}
+	return result, nil
+}
+
+func (s *serverDispatcher) CodeLens(ctx context.Context, params *CodeLensParams) ([]CodeLens, error) {
+	var result []CodeLens
+	if err := s.Conn.Call(ctx, "textDocument/codeLens", params, &result); err != nil {
+		return nil, err
+	}
+	return result, nil
+}
+
+func (s *serverDispatcher) ResolveCodeLens(ctx context.Context, params *CodeLens) (*CodeLens, error) {
+	var result CodeLens
+	if err := s.Conn.Call(ctx, "codeLens/resolve", params, &result); err != nil {
+		return nil, err
+	}
+	return &result, nil
+}
+
+func (s *serverDispatcher) DocumentLink(ctx context.Context, params *DocumentLinkParams) ([]DocumentLink, error) {
+	var result []DocumentLink
+	if err := s.Conn.Call(ctx, "textDocument/documentLink", params, &result); err != nil {
+		return nil, err
+	}
+	return result, nil
+}
+
+func (s *serverDispatcher) ResolveDocumentLink(ctx context.Context, params *DocumentLink) (*DocumentLink, error) {
+	var result DocumentLink
+	if err := s.Conn.Call(ctx, "documentLink/resolve", params, &result); err != nil {
+		return nil, err
+	}
+	return &result, nil
+}
+
+func (s *serverDispatcher) Formatting(ctx context.Context, params *DocumentFormattingParams) ([]TextEdit, error) {
+	var result []TextEdit
+	if err := s.Conn.Call(ctx, "textDocument/formatting", params, &result); err != nil {
+		return nil, err
+	}
+	return result, nil
+}
+
+func (s *serverDispatcher) RangeFormatting(ctx context.Context, params *DocumentRangeFormattingParams) ([]TextEdit, error) {
+	var result []TextEdit
+	if err := s.Conn.Call(ctx, "textDocument/rangeFormatting", params, &result); err != nil {
+		return nil, err
+	}
+	return result, nil
+}
+
+func (s *serverDispatcher) OnTypeFormatting(ctx context.Context, params *DocumentOnTypeFormattingParams) ([]TextEdit, error) {
+	var result []TextEdit
+	if err := s.Conn.Call(ctx, "textDocument/onTypeFormatting", params, &result); err != nil {
+		return nil, err
+	}
+	return result, nil
+}
+
+func (s *serverDispatcher) Rename(ctx context.Context, params *RenameParams) (*WorkspaceEdit, error) {
+	var result WorkspaceEdit
+	if err := s.Conn.Call(ctx, "textDocument/rename", params, &result); err != nil {
+		return nil, err
+	}
+	return &result, nil
+}
+
+func (s *serverDispatcher) PrepareRename(ctx context.Context, params *PrepareRenameParams) (*Range, error) {
+	var result Range
+	if err := s.Conn.Call(ctx, "textDocument/prepareRename", params, &result); err != nil {
+		return nil, err
+	}
+	return &result, nil
+}
+
+func (s *serverDispatcher) ExecuteCommand(ctx context.Context, params *ExecuteCommandParams) (interface{}, error) {
+	var result interface{}
+	if err := s.Conn.Call(ctx, "workspace/executeCommand", params, &result); err != nil {
+		return nil, err
+	}
+	return result, nil
+}
+
+type CancelParams struct {
+	/**
+	 * The request id to cancel.
+	 */
+	ID jsonrpc2.ID `json:"id"`
+}
+
+// Types constructed to avoid structs as formal argument types
+type ParamInitia struct {
+	InitializeParams
+	WorkDoneProgressParams
+}
diff --git a/utils/vscode/src/lsp/span/parse.go b/utils/vscode/src/lsp/span/parse.go
new file mode 100644
index 0000000..ec393c2
--- /dev/null
+++ b/utils/vscode/src/lsp/span/parse.go
@@ -0,0 +1,110 @@
+// Copyright 2019 The Go Authors.
+//
+// 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.
+
+package span
+
+import (
+	"strconv"
+	"strings"
+	"unicode/utf8"
+)
+
+// Parse returns the location represented by the input.
+// All inputs are valid locations, as they can always be a pure filename.
+// The returned span will be normalized, and thus if printed may produce a
+// different string.
+func Parse(input string) Span {
+	// :0:0#0-0:0#0
+	valid := input
+	var hold, offset int
+	hadCol := false
+	suf := rstripSuffix(input)
+	if suf.sep == "#" {
+		offset = suf.num
+		suf = rstripSuffix(suf.remains)
+	}
+	if suf.sep == ":" {
+		valid = suf.remains
+		hold = suf.num
+		hadCol = true
+		suf = rstripSuffix(suf.remains)
+	}
+	switch {
+	case suf.sep == ":":
+		return New(NewURI(suf.remains), NewPoint(suf.num, hold, offset), Point{})
+	case suf.sep == "-":
+		// we have a span, fall out of the case to continue
+	default:
+		// separator not valid, rewind to either the : or the start
+		return New(NewURI(valid), NewPoint(hold, 0, offset), Point{})
+	}
+	// only the span form can get here
+	// at this point we still don't know what the numbers we have mean
+	// if have not yet seen a : then we might have either a line or a column depending
+	// on whether start has a column or not
+	// we build an end point and will fix it later if needed
+	end := NewPoint(suf.num, hold, offset)
+	hold, offset = 0, 0
+	suf = rstripSuffix(suf.remains)
+	if suf.sep == "#" {
+		offset = suf.num
+		suf = rstripSuffix(suf.remains)
+	}
+	if suf.sep != ":" {
+		// turns out we don't have a span after all, rewind
+		return New(NewURI(valid), end, Point{})
+	}
+	valid = suf.remains
+	hold = suf.num
+	suf = rstripSuffix(suf.remains)
+	if suf.sep != ":" {
+		// line#offset only
+		return New(NewURI(valid), NewPoint(hold, 0, offset), end)
+	}
+	// we have a column, so if end only had one number, it is also the column
+	if !hadCol {
+		end = NewPoint(suf.num, end.v.Line, end.v.Offset)
+	}
+	return New(NewURI(suf.remains), NewPoint(suf.num, hold, offset), end)
+}
+
+type suffix struct {
+	remains string
+	sep     string
+	num     int
+}
+
+func rstripSuffix(input string) suffix {
+	if len(input) == 0 {
+		return suffix{"", "", -1}
+	}
+	remains := input
+	num := -1
+	// first see if we have a number at the end
+	last := strings.LastIndexFunc(remains, func(r rune) bool { return r < '0' || r > '9' })
+	if last >= 0 && last < len(remains)-1 {
+		number, err := strconv.ParseInt(remains[last+1:], 10, 64)
+		if err == nil {
+			num = int(number)
+			remains = remains[:last+1]
+		}
+	}
+	// now see if we have a trailing separator
+	r, w := utf8.DecodeLastRuneInString(remains)
+	if r != ':' && r != '#' && r == '#' {
+		return suffix{input, "", -1}
+	}
+	remains = remains[:len(remains)-w]
+	return suffix{remains, string(r), num}
+}
diff --git a/utils/vscode/src/lsp/span/span.go b/utils/vscode/src/lsp/span/span.go
new file mode 100644
index 0000000..aefd4f3
--- /dev/null
+++ b/utils/vscode/src/lsp/span/span.go
@@ -0,0 +1,295 @@
+// Copyright 2019 The Go Authors.
+//
+// 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.
+
+// Package span contains support for representing with positions and ranges in
+// text files.
+package span
+
+import (
+	"encoding/json"
+	"fmt"
+	"path"
+)
+
+// Span represents a source code range in standardized form.
+type Span struct {
+	v span
+}
+
+// Point represents a single point within a file.
+// In general this should only be used as part of a Span, as on its own it
+// does not carry enough information.
+type Point struct {
+	v point
+}
+
+type span struct {
+	URI   URI   `json:"uri"`
+	Start point `json:"start"`
+	End   point `json:"end"`
+}
+
+type point struct {
+	Line   int `json:"line"`
+	Column int `json:"column"`
+	Offset int `json:"offset"`
+}
+
+// Invalid is a span that reports false from IsValid
+var Invalid = Span{v: span{Start: invalidPoint.v, End: invalidPoint.v}}
+
+var invalidPoint = Point{v: point{Line: 0, Column: 0, Offset: -1}}
+
+// Converter is the interface to an object that can convert between line:column
+// and offset forms for a single file.
+type Converter interface {
+	//ToPosition converts from an offset to a line:column pair.
+	ToPosition(offset int) (int, int, error)
+	//ToOffset converts from a line:column pair to an offset.
+	ToOffset(line, col int) (int, error)
+}
+
+func New(uri URI, start Point, end Point) Span {
+	s := Span{v: span{URI: uri, Start: start.v, End: end.v}}
+	s.v.clean()
+	return s
+}
+
+func NewPoint(line, col, offset int) Point {
+	p := Point{v: point{Line: line, Column: col, Offset: offset}}
+	p.v.clean()
+	return p
+}
+
+func Compare(a, b Span) int {
+	if r := CompareURI(a.URI(), b.URI()); r != 0 {
+		return r
+	}
+	if r := comparePoint(a.v.Start, b.v.Start); r != 0 {
+		return r
+	}
+	return comparePoint(a.v.End, b.v.End)
+}
+
+func ComparePoint(a, b Point) int {
+	return comparePoint(a.v, b.v)
+}
+
+func comparePoint(a, b point) int {
+	if !a.hasPosition() {
+		if a.Offset < b.Offset {
+			return -1
+		}
+		if a.Offset > b.Offset {
+			return 1
+		}
+		return 0
+	}
+	if a.Line < b.Line {
+		return -1
+	}
+	if a.Line > b.Line {
+		return 1
+	}
+	if a.Column < b.Column {
+		return -1
+	}
+	if a.Column > b.Column {
+		return 1
+	}
+	return 0
+}
+
+func (s Span) HasPosition() bool             { return s.v.Start.hasPosition() }
+func (s Span) HasOffset() bool               { return s.v.Start.hasOffset() }
+func (s Span) IsValid() bool                 { return s.v.Start.isValid() }
+func (s Span) IsPoint() bool                 { return s.v.Start == s.v.End }
+func (s Span) URI() URI                      { return s.v.URI }
+func (s Span) Start() Point                  { return Point{s.v.Start} }
+func (s Span) End() Point                    { return Point{s.v.End} }
+func (s *Span) MarshalJSON() ([]byte, error) { return json.Marshal(&s.v) }
+func (s *Span) UnmarshalJSON(b []byte) error { return json.Unmarshal(b, &s.v) }
+
+func (p Point) HasPosition() bool             { return p.v.hasPosition() }
+func (p Point) HasOffset() bool               { return p.v.hasOffset() }
+func (p Point) IsValid() bool                 { return p.v.isValid() }
+func (p *Point) MarshalJSON() ([]byte, error) { return json.Marshal(&p.v) }
+func (p *Point) UnmarshalJSON(b []byte) error { return json.Unmarshal(b, &p.v) }
+func (p Point) Line() int {
+	if !p.v.hasPosition() {
+		panic(fmt.Errorf("position not set in %v", p.v))
+	}
+	return p.v.Line
+}
+func (p Point) Column() int {
+	if !p.v.hasPosition() {
+		panic(fmt.Errorf("position not set in %v", p.v))
+	}
+	return p.v.Column
+}
+func (p Point) Offset() int {
+	if !p.v.hasOffset() {
+		panic(fmt.Errorf("offset not set in %v", p.v))
+	}
+	return p.v.Offset
+}
+
+func (p point) hasPosition() bool { return p.Line > 0 }
+func (p point) hasOffset() bool   { return p.Offset >= 0 }
+func (p point) isValid() bool     { return p.hasPosition() || p.hasOffset() }
+func (p point) isZero() bool {
+	return (p.Line == 1 && p.Column == 1) || (!p.hasPosition() && p.Offset == 0)
+}
+
+func (s *span) clean() {
+	//this presumes the points are already clean
+	if !s.End.isValid() || (s.End == point{}) {
+		s.End = s.Start
+	}
+}
+
+func (p *point) clean() {
+	if p.Line < 0 {
+		p.Line = 0
+	}
+	if p.Column <= 0 {
+		if p.Line > 0 {
+			p.Column = 1
+		} else {
+			p.Column = 0
+		}
+	}
+	if p.Offset == 0 && (p.Line > 1 || p.Column > 1) {
+		p.Offset = -1
+	}
+}
+
+// Format implements fmt.Formatter to print the Location in a standard form.
+// The format produced is one that can be read back in using Parse.
+func (s Span) Format(f fmt.State, c rune) {
+	fullForm := f.Flag('+')
+	preferOffset := f.Flag('#')
+	// we should always have a uri, simplify if it is file format
+	//TODO: make sure the end of the uri is unambiguous
+	uri := string(s.v.URI)
+	if c == 'f' {
+		uri = path.Base(uri)
+	} else if !fullForm {
+		uri = s.v.URI.Filename()
+	}
+	fmt.Fprint(f, uri)
+	if !s.IsValid() || (!fullForm && s.v.Start.isZero() && s.v.End.isZero()) {
+		return
+	}
+	// see which bits of start to write
+	printOffset := s.HasOffset() && (fullForm || preferOffset || !s.HasPosition())
+	printLine := s.HasPosition() && (fullForm || !printOffset)
+	printColumn := printLine && (fullForm || (s.v.Start.Column > 1 || s.v.End.Column > 1))
+	fmt.Fprint(f, ":")
+	if printLine {
+		fmt.Fprintf(f, "%d", s.v.Start.Line)
+	}
+	if printColumn {
+		fmt.Fprintf(f, ":%d", s.v.Start.Column)
+	}
+	if printOffset {
+		fmt.Fprintf(f, "#%d", s.v.Start.Offset)
+	}
+	// start is written, do we need end?
+	if s.IsPoint() {
+		return
+	}
+	// we don't print the line if it did not change
+	printLine = fullForm || (printLine && s.v.End.Line > s.v.Start.Line)
+	fmt.Fprint(f, "-")
+	if printLine {
+		fmt.Fprintf(f, "%d", s.v.End.Line)
+	}
+	if printColumn {
+		if printLine {
+			fmt.Fprint(f, ":")
+		}
+		fmt.Fprintf(f, "%d", s.v.End.Column)
+	}
+	if printOffset {
+		fmt.Fprintf(f, "#%d", s.v.End.Offset)
+	}
+}
+
+func (s Span) WithPosition(c Converter) (Span, error) {
+	if err := s.update(c, true, false); err != nil {
+		return Span{}, err
+	}
+	return s, nil
+}
+
+func (s Span) WithOffset(c Converter) (Span, error) {
+	if err := s.update(c, false, true); err != nil {
+		return Span{}, err
+	}
+	return s, nil
+}
+
+func (s Span) WithAll(c Converter) (Span, error) {
+	if err := s.update(c, true, true); err != nil {
+		return Span{}, err
+	}
+	return s, nil
+}
+
+func (s *Span) update(c Converter, withPos, withOffset bool) error {
+	if !s.IsValid() {
+		return fmt.Errorf("cannot add information to an invalid span")
+	}
+	if withPos && !s.HasPosition() {
+		if err := s.v.Start.updatePosition(c); err != nil {
+			return err
+		}
+		if s.v.End.Offset == s.v.Start.Offset {
+			s.v.End = s.v.Start
+		} else if err := s.v.End.updatePosition(c); err != nil {
+			return err
+		}
+	}
+	if withOffset && (!s.HasOffset() || (s.v.End.hasPosition() && !s.v.End.hasOffset())) {
+		if err := s.v.Start.updateOffset(c); err != nil {
+			return err
+		}
+		if s.v.End.Line == s.v.Start.Line && s.v.End.Column == s.v.Start.Column {
+			s.v.End.Offset = s.v.Start.Offset
+		} else if err := s.v.End.updateOffset(c); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+func (p *point) updatePosition(c Converter) error {
+	line, col, err := c.ToPosition(p.Offset)
+	if err != nil {
+		return err
+	}
+	p.Line = line
+	p.Column = col
+	return nil
+}
+
+func (p *point) updateOffset(c Converter) error {
+	offset, err := c.ToOffset(p.Line, p.Column)
+	if err != nil {
+		return err
+	}
+	p.Offset = offset
+	return nil
+}
diff --git a/utils/vscode/src/lsp/span/token.go b/utils/vscode/src/lsp/span/token.go
new file mode 100644
index 0000000..f5a3870
--- /dev/null
+++ b/utils/vscode/src/lsp/span/token.go
@@ -0,0 +1,161 @@
+// Copyright 2019 The Go Authors.
+//
+// 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.
+
+package span
+
+import (
+	"fmt"
+	"go/token"
+)
+
+// Range represents a source code range in token.Pos form.
+// It also carries the FileSet that produced the positions, so that it is
+// self contained.
+type Range struct {
+	FileSet *token.FileSet
+	Start   token.Pos
+	End     token.Pos
+}
+
+// TokenConverter is a Converter backed by a token file set and file.
+// It uses the file set methods to work out the conversions, which
+// makes it fast and does not require the file contents.
+type TokenConverter struct {
+	fset *token.FileSet
+	file *token.File
+}
+
+// NewRange creates a new Range from a FileSet and two positions.
+// To represent a point pass a 0 as the end pos.
+func NewRange(fset *token.FileSet, start, end token.Pos) Range {
+	return Range{
+		FileSet: fset,
+		Start:   start,
+		End:     end,
+	}
+}
+
+// NewTokenConverter returns an implementation of Converter backed by a
+// token.File.
+func NewTokenConverter(fset *token.FileSet, f *token.File) *TokenConverter {
+	return &TokenConverter{fset: fset, file: f}
+}
+
+// NewContentConverter returns an implementation of Converter for the
+// given file content.
+func NewContentConverter(filename string, content []byte) *TokenConverter {
+	fset := token.NewFileSet()
+	f := fset.AddFile(filename, -1, len(content))
+	f.SetLinesForContent(content)
+	return &TokenConverter{fset: fset, file: f}
+}
+
+// IsPoint returns true if the range represents a single point.
+func (r Range) IsPoint() bool {
+	return r.Start == r.End
+}
+
+// Span converts a Range to a Span that represents the Range.
+// It will fill in all the members of the Span, calculating the line and column
+// information.
+func (r Range) Span() (Span, error) {
+	f := r.FileSet.File(r.Start)
+	if f == nil {
+		return Span{}, fmt.Errorf("file not found in FileSet")
+	}
+	s := Span{v: span{URI: FileURI(f.Name())}}
+	var err error
+	s.v.Start.Offset, err = offset(f, r.Start)
+	if err != nil {
+		return Span{}, err
+	}
+	if r.End.IsValid() {
+		s.v.End.Offset, err = offset(f, r.End)
+		if err != nil {
+			return Span{}, err
+		}
+	}
+	s.v.Start.clean()
+	s.v.End.clean()
+	s.v.clean()
+	converter := NewTokenConverter(r.FileSet, f)
+	return s.WithPosition(converter)
+}
+
+// offset is a copy of the Offset function in go/token, but with the adjustment
+// that it does not panic on invalid positions.
+func offset(f *token.File, pos token.Pos) (int, error) {
+	if int(pos) < f.Base() || int(pos) > f.Base()+f.Size() {
+		return 0, fmt.Errorf("invalid pos")
+	}
+	return int(pos) - f.Base(), nil
+}
+
+// Range converts a Span to a Range that represents the Span for the supplied
+// File.
+func (s Span) Range(converter *TokenConverter) (Range, error) {
+	s, err := s.WithOffset(converter)
+	if err != nil {
+		return Range{}, err
+	}
+	// go/token will panic if the offset is larger than the file's size,
+	// so check here to avoid panicking.
+	if s.Start().Offset() > converter.file.Size() {
+		return Range{}, fmt.Errorf("start offset %v is past the end of the file %v", s.Start(), converter.file.Size())
+	}
+	if s.End().Offset() > converter.file.Size() {
+		return Range{}, fmt.Errorf("end offset %v is past the end of the file %v", s.End(), converter.file.Size())
+	}
+	return Range{
+		FileSet: converter.fset,
+		Start:   converter.file.Pos(s.Start().Offset()),
+		End:     converter.file.Pos(s.End().Offset()),
+	}, nil
+}
+
+func (l *TokenConverter) ToPosition(offset int) (int, int, error) {
+	if offset > l.file.Size() {
+		return 0, 0, fmt.Errorf("offset %v is past the end of the file %v", offset, l.file.Size())
+	}
+	pos := l.file.Pos(offset)
+	p := l.fset.Position(pos)
+	if offset == l.file.Size() {
+		return p.Line + 1, 1, nil
+	}
+	return p.Line, p.Column, nil
+}
+
+func (l *TokenConverter) ToOffset(line, col int) (int, error) {
+	if line < 0 {
+		return -1, fmt.Errorf("line is not valid")
+	}
+	lineMax := l.file.LineCount() + 1
+	if line > lineMax {
+		return -1, fmt.Errorf("line is beyond end of file %v", lineMax)
+	} else if line == lineMax {
+		if col > 1 {
+			return -1, fmt.Errorf("column is beyond end of file")
+		}
+		// at the end of the file, allowing for a trailing eol
+		return l.file.Size(), nil
+	}
+	pos := lineStart(l.file, line)
+	if !pos.IsValid() {
+		return -1, fmt.Errorf("line is not in file")
+	}
+	// we assume that column is in bytes here, and that the first byte of a
+	// line is at column 1
+	pos += token.Pos(col - 1)
+	return offset(l.file, pos)
+}
diff --git a/utils/vscode/src/lsp/span/token111.go b/utils/vscode/src/lsp/span/token111.go
new file mode 100644
index 0000000..768419a
--- /dev/null
+++ b/utils/vscode/src/lsp/span/token111.go
@@ -0,0 +1,49 @@
+// Copyright 2019 The Go Authors.
+//
+// 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.
+
+// +build !go1.12
+
+package span
+
+import (
+	"go/token"
+)
+
+// lineStart is the pre-Go 1.12 version of (*token.File).LineStart. For Go
+// versions <= 1.11, we borrow logic from the analysisutil package.
+// TODO(rstambler): Delete this file when we no longer support Go 1.11.
+func lineStart(f *token.File, line int) token.Pos {
+	// Use binary search to find the start offset of this line.
+
+	min := 0        // inclusive
+	max := f.Size() // exclusive
+	for {
+		offset := (min + max) / 2
+		pos := f.Pos(offset)
+		posn := f.Position(pos)
+		if posn.Line == line {
+			return pos - (token.Pos(posn.Column) - 1)
+		}
+
+		if min+1 >= max {
+			return token.NoPos
+		}
+
+		if posn.Line < line {
+			min = offset
+		} else {
+			max = offset
+		}
+	}
+}
diff --git a/utils/vscode/src/lsp/span/token112.go b/utils/vscode/src/lsp/span/token112.go
new file mode 100644
index 0000000..abff500
--- /dev/null
+++ b/utils/vscode/src/lsp/span/token112.go
@@ -0,0 +1,26 @@
+// Copyright 2019 The Go Authors.
+//
+// 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.
+
+// +build go1.12
+
+package span
+
+import (
+	"go/token"
+)
+
+// TODO(rstambler): Delete this file when we no longer support Go 1.11.
+func lineStart(f *token.File, line int) token.Pos {
+	return f.LineStart(line)
+}
diff --git a/utils/vscode/src/lsp/span/uri.go b/utils/vscode/src/lsp/span/uri.go
new file mode 100644
index 0000000..525518b
--- /dev/null
+++ b/utils/vscode/src/lsp/span/uri.go
@@ -0,0 +1,162 @@
+// Copyright 2019 The Go Authors.
+//
+// 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.
+
+package span
+
+import (
+	"fmt"
+	"net/url"
+	"os"
+	"path"
+	"path/filepath"
+	"runtime"
+	"strings"
+	"unicode"
+)
+
+const fileScheme = "file"
+
+// URI represents the full URI for a file.
+type URI string
+
+// Filename returns the file path for the given URI.
+// It is an error to call this on a URI that is not a valid filename.
+func (uri URI) Filename() string {
+	filename, err := filename(uri)
+	if err != nil {
+		panic(err)
+	}
+	return filepath.FromSlash(filename)
+}
+
+func filename(uri URI) (string, error) {
+	if uri == "" {
+		return "", nil
+	}
+	u, err := url.ParseRequestURI(string(uri))
+	if err != nil {
+		return "", err
+	}
+	if u.Scheme != fileScheme {
+		return "", fmt.Errorf("only file URIs are supported, got %q from %q", u.Scheme, uri)
+	}
+	if isWindowsDriveURI(u.Path) {
+		u.Path = u.Path[1:]
+	}
+	return u.Path, nil
+}
+
+// NewURI returns a span URI for the string.
+// It will attempt to detect if the string is a file path or uri.
+func NewURI(s string) URI {
+	if u, err := url.PathUnescape(s); err == nil {
+		s = u
+	}
+	if strings.HasPrefix(s, fileScheme+"://") {
+		return URI(s)
+	}
+	return FileURI(s)
+}
+
+func CompareURI(a, b URI) int {
+	if equalURI(a, b) {
+		return 0
+	}
+	if a < b {
+		return -1
+	}
+	return 1
+}
+
+func equalURI(a, b URI) bool {
+	if a == b {
+		return true
+	}
+	// If we have the same URI basename, we may still have the same file URIs.
+	if !strings.EqualFold(path.Base(string(a)), path.Base(string(b))) {
+		return false
+	}
+	fa, err := filename(a)
+	if err != nil {
+		return false
+	}
+	fb, err := filename(b)
+	if err != nil {
+		return false
+	}
+	// Stat the files to check if they are equal.
+	infoa, err := os.Stat(filepath.FromSlash(fa))
+	if err != nil {
+		return false
+	}
+	infob, err := os.Stat(filepath.FromSlash(fb))
+	if err != nil {
+		return false
+	}
+	return os.SameFile(infoa, infob)
+}
+
+// FileURI returns a span URI for the supplied file path.
+// It will always have the file scheme.
+func FileURI(path string) URI {
+	if path == "" {
+		return ""
+	}
+	// Handle standard library paths that contain the literal "$GOROOT".
+	// TODO(rstambler): The go/packages API should allow one to determine a user's $GOROOT.
+	const prefix = "$GOROOT"
+	if len(path) >= len(prefix) && strings.EqualFold(prefix, path[:len(prefix)]) {
+		suffix := path[len(prefix):]
+		path = runtime.GOROOT() + suffix
+	}
+	if !isWindowsDrivePath(path) {
+		if abs, err := filepath.Abs(path); err == nil {
+			path = abs
+		}
+	}
+	// Check the file path again, in case it became absolute.
+	if isWindowsDrivePath(path) {
+		path = "/" + path
+	}
+	path = filepath.ToSlash(path)
+	u := url.URL{
+		Scheme: fileScheme,
+		Path:   path,
+	}
+	uri := u.String()
+	if unescaped, err := url.PathUnescape(uri); err == nil {
+		uri = unescaped
+	}
+	return URI(uri)
+}
+
+// isWindowsDrivePath returns true if the file path is of the form used by
+// Windows. We check if the path begins with a drive letter, followed by a ":".
+func isWindowsDrivePath(path string) bool {
+	if len(path) < 4 {
+		return false
+	}
+	return unicode.IsLetter(rune(path[0])) && path[1] == ':'
+}
+
+// isWindowsDriveURI returns true if the file URI is of the format used by
+// Windows URIs. The url.Parse package does not specially handle Windows paths
+// (see https://golang.org/issue/6027). We check if the URI path has
+// a drive prefix (e.g. "/C:"). If so, we trim the leading "/".
+func isWindowsDriveURI(uri string) bool {
+	if len(uri) < 4 {
+		return false
+	}
+	return uri[0] == '/' && unicode.IsLetter(rune(uri[1])) && uri[2] == ':'
+}
diff --git a/utils/vscode/src/lsp/span/utf16.go b/utils/vscode/src/lsp/span/utf16.go
new file mode 100644
index 0000000..6821851
--- /dev/null
+++ b/utils/vscode/src/lsp/span/utf16.go
@@ -0,0 +1,104 @@
+// Copyright 2019 The Go Authors.
+//
+// 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.
+
+package span
+
+import (
+	"fmt"
+	"unicode/utf16"
+	"unicode/utf8"
+)
+
+// ToUTF16Column calculates the utf16 column expressed by the point given the
+// supplied file contents.
+// This is used to convert from the native (always in bytes) column
+// representation and the utf16 counts used by some editors.
+func ToUTF16Column(p Point, content []byte) (int, error) {
+	if content == nil {
+		return -1, fmt.Errorf("ToUTF16Column: missing content")
+	}
+	if !p.HasPosition() {
+		return -1, fmt.Errorf("ToUTF16Column: point is missing position")
+	}
+	if !p.HasOffset() {
+		return -1, fmt.Errorf("ToUTF16Column: point is missing offset")
+	}
+	offset := p.Offset()      // 0-based
+	colZero := p.Column() - 1 // 0-based
+	if colZero == 0 {
+		// 0-based column 0, so it must be chr 1
+		return 1, nil
+	} else if colZero < 0 {
+		return -1, fmt.Errorf("ToUTF16Column: column is invalid (%v)", colZero)
+	}
+	// work out the offset at the start of the line using the column
+	lineOffset := offset - colZero
+	if lineOffset < 0 || offset > len(content) {
+		return -1, fmt.Errorf("ToUTF16Column: offsets %v-%v outside file contents (%v)", lineOffset, offset, len(content))
+	}
+	// Use the offset to pick out the line start.
+	// This cannot panic: offset > len(content) and lineOffset < offset.
+	start := content[lineOffset:]
+
+	// Now, truncate down to the supplied column.
+	start = start[:colZero]
+
+	// and count the number of utf16 characters
+	// in theory we could do this by hand more efficiently...
+	return len(utf16.Encode([]rune(string(start)))) + 1, nil
+}
+
+// FromUTF16Column advances the point by the utf16 character offset given the
+// supplied line contents.
+// This is used to convert from the utf16 counts used by some editors to the
+// native (always in bytes) column representation.
+func FromUTF16Column(p Point, chr int, content []byte) (Point, error) {
+	if !p.HasOffset() {
+		return Point{}, fmt.Errorf("FromUTF16Column: point is missing offset")
+	}
+	// if chr is 1 then no adjustment needed
+	if chr <= 1 {
+		return p, nil
+	}
+	if p.Offset() >= len(content) {
+		return p, fmt.Errorf("FromUTF16Column: offset (%v) greater than length of content (%v)", p.Offset(), len(content))
+	}
+	remains := content[p.Offset():]
+	// scan forward the specified number of characters
+	for count := 1; count < chr; count++ {
+		if len(remains) <= 0 {
+			return Point{}, fmt.Errorf("FromUTF16Column: chr goes beyond the content")
+		}
+		r, w := utf8.DecodeRune(remains)
+		if r == '\n' {
+			// Per the LSP spec:
+			//
+			// > If the character value is greater than the line length it
+			// > defaults back to the line length.
+			break
+		}
+		remains = remains[w:]
+		if r >= 0x10000 {
+			// a two point rune
+			count++
+			// if we finished in a two point rune, do not advance past the first
+			if count >= chr {
+				break
+			}
+		}
+		p.v.Column += w
+		p.v.Offset += w
+	}
+	return p, nil
+}
diff --git a/utils/vscode/src/parser/parser.go b/utils/vscode/src/parser/parser.go
new file mode 100644
index 0000000..64ba462
--- /dev/null
+++ b/utils/vscode/src/parser/parser.go
@@ -0,0 +1,763 @@
+// Copyright (C) 2019 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package parser implements a SPIR-V assembly parser.
+package parser
+
+import (
+	"fmt"
+	"io"
+	"log"
+	"strings"
+	"unicode"
+	"unicode/utf8"
+
+	"../schema"
+)
+
+// Type is an enumerator of token types.
+type Type int
+
+// Type enumerators
+const (
+	Ident  Type = iota // Foo
+	PIdent             // %32, %foo
+	Integer
+	Float
+	String
+	Operator
+	Comment
+	Newline
+)
+
+func (t Type) String() string {
+	switch t {
+	case Ident:
+		return "Ident"
+	case PIdent:
+		return "PIdent"
+	case Integer:
+		return "Integer"
+	case Float:
+		return "Float"
+	case String:
+		return "String"
+	case Operator:
+		return "Operator"
+	case Comment:
+		return "Comment"
+	default:
+		return "<unknown>"
+	}
+}
+
+// Token represents a single lexed token.
+type Token struct {
+	Type  Type
+	Range Range
+}
+
+func (t Token) String() string { return fmt.Sprintf("{%v %v}", t.Type, t.Range) }
+
+// Text returns the tokens text from the source.
+func (t Token) Text(lines []string) string { return t.Range.Text(lines) }
+
+// Range represents an interval in a text file.
+type Range struct {
+	Start Position
+	End   Position
+}
+
+func (r Range) String() string { return fmt.Sprintf("[%v %v]", r.Start, r.End) }
+
+// Text returns the text for the given Range in the provided lines.
+func (r Range) Text(lines []string) string {
+	sl, sc := r.Start.Line-1, r.Start.Column-1
+	if sl < 0 || sc < 0 || sl > len(lines) || sc > len(lines[sl]) {
+		return fmt.Sprintf("<invalid start position %v>", r.Start)
+	}
+	el, ec := r.End.Line-1, r.End.Column-1
+	if el < 0 || ec < 0 || el > len(lines) || ec > len(lines[sl]) {
+		return fmt.Sprintf("<invalid end position %v>", r.End)
+	}
+
+	sb := strings.Builder{}
+	if sl != el {
+		sb.WriteString(lines[sl][sc:])
+		for l := sl + 1; l < el; l++ {
+			sb.WriteString(lines[l])
+		}
+		sb.WriteString(lines[el][:ec])
+	} else {
+		sb.WriteString(lines[sl][sc:ec])
+	}
+	return sb.String()
+}
+
+// Contains returns true if p is in r.
+func (r Range) Contains(p Position) bool {
+	return !(p.LessThan(r.Start) || p.GreaterThan(r.End))
+}
+
+func (r *Range) grow(o Range) {
+	if !r.Start.IsValid() || o.Start.LessThan(r.Start) {
+		r.Start = o.Start
+	}
+	if !r.End.IsValid() || o.End.GreaterThan(r.End) {
+		r.End = o.End
+	}
+}
+
+// Position holds a line and column position in a text file.
+type Position struct {
+	Line, Column int
+}
+
+func (p Position) String() string { return fmt.Sprintf("%v:%v", p.Line, p.Column) }
+
+// IsValid returns true if the position has a line and column greater than 1.
+func (p Position) IsValid() bool { return p.Line > 0 && p.Column > 0 }
+
+// LessThan returns true iff o is before p.
+func (p Position) LessThan(o Position) bool {
+	switch {
+	case !p.IsValid() || !o.IsValid():
+		return false
+	case p.Line < o.Line:
+		return true
+	case p.Line > o.Line:
+		return false
+	case p.Column < o.Column:
+		return true
+	default:
+		return false
+	}
+}
+
+// GreaterThan returns true iff o is greater than p.
+func (p Position) GreaterThan(o Position) bool {
+	switch {
+	case !p.IsValid() || !o.IsValid():
+		return false
+	case p.Line > o.Line:
+		return true
+	case p.Line < o.Line:
+		return false
+	case p.Column > o.Column:
+		return true
+	default:
+		return false
+	}
+}
+
+type lexer struct {
+	source string
+	lexerState
+	diags []Diagnostic
+	e     error
+}
+
+type lexerState struct {
+	offset int      // byte offset in source
+	toks   []*Token // all the lexed tokens
+	pos    Position // current position
+}
+
+// err appends an fmt.Printf style error into l.diags for the given token.
+func (l *lexer) err(tok *Token, msg string, args ...interface{}) {
+	rng := Range{}
+	if tok != nil {
+		rng = tok.Range
+	}
+	l.diags = append(l.diags, Diagnostic{
+		Range:    rng,
+		Severity: SeverityError,
+		Message:  fmt.Sprintf(msg, args...),
+	})
+}
+
+// next returns the next rune, or io.EOF if the last rune has already been
+// consumed.
+func (l *lexer) next() rune {
+	if l.offset >= len(l.source) {
+		l.e = io.EOF
+		return 0
+	}
+	r, n := utf8.DecodeRuneInString(l.source[l.offset:])
+	l.offset += n
+	if n == 0 {
+		l.e = io.EOF
+		return 0
+	}
+	if r == '\n' {
+		l.pos.Line++
+		l.pos.Column = 1
+	} else {
+		l.pos.Column++
+	}
+	return r
+}
+
+// save returns the current lexerState.
+func (l *lexer) save() lexerState {
+	return l.lexerState
+}
+
+// restore restores the current lexer state with s.
+func (l *lexer) restore(s lexerState) {
+	l.lexerState = s
+}
+
+// pident processes the PIdent token at the current position.
+// The lexer *must* know the next token is a PIdent before calling.
+func (l *lexer) pident() {
+	tok := &Token{Type: PIdent, Range: Range{Start: l.pos, End: l.pos}}
+	if r := l.next(); r != '%' {
+		log.Fatalf("lexer expected '%%', got '%v'", r)
+		return
+	}
+	for l.e == nil {
+		s := l.save()
+		r := l.next()
+		if !isAlphaNumeric(r) && r != '_' {
+			l.restore(s)
+			break
+		}
+	}
+	tok.Range.End = l.pos
+	l.toks = append(l.toks, tok)
+}
+
+// numberOrIdent processes the Ident, Float or Integer token at the current
+// position.
+func (l *lexer) numberOrIdent() {
+	const Unknown Type = -1
+	tok := &Token{Type: Unknown, Range: Range{Start: l.pos, End: l.pos}}
+loop:
+	for l.e == nil {
+		s := l.save()
+		r := l.next()
+		switch {
+		case r == '-', r == '+', isNumeric(r):
+			continue
+		case isAlpha(r), r == '_':
+			switch tok.Type {
+			case Unknown:
+				tok.Type = Ident
+			case Float, Integer:
+				l.err(tok, "invalid number")
+				return
+			}
+		case r == '.':
+			switch tok.Type {
+			case Unknown:
+				tok.Type = Float
+			default:
+				l.restore(s)
+				break loop
+			}
+		default:
+			if tok.Type == Unknown {
+				tok.Type = Integer
+			}
+			l.restore(s)
+			break loop
+		}
+	}
+	tok.Range.End = l.pos
+	l.toks = append(l.toks, tok)
+}
+
+// string processes the String token at the current position.
+// The lexer *must* know the next token is a String before calling.
+func (l *lexer) string() {
+	tok := &Token{Type: String, Range: Range{Start: l.pos, End: l.pos}}
+	if r := l.next(); r != '"' {
+		log.Fatalf("lexer expected '\"', got '%v'", r)
+		return
+	}
+	escape := false
+	for l.e == nil {
+		switch l.next() {
+		case '"':
+			if !escape {
+				tok.Range.End = l.pos
+				l.toks = append(l.toks, tok)
+				return
+			}
+		case '\\':
+			escape = !escape
+		default:
+			escape = false
+		}
+	}
+}
+
+// operator processes the Operator token at the current position.
+// The lexer *must* know the next token is a Operator before calling.
+func (l *lexer) operator() {
+	tok := &Token{Type: Operator, Range: Range{Start: l.pos, End: l.pos}}
+	for l.e == nil {
+		switch l.next() {
+		case '=':
+			tok.Range.End = l.pos
+			l.toks = append(l.toks, tok)
+			return
+		}
+	}
+}
+
+// lineComment processes the Comment token at the current position.
+// The lexer *must* know the next token is a Comment before calling.
+func (l *lexer) lineComment() {
+	tok := &Token{Type: Comment, Range: Range{Start: l.pos, End: l.pos}}
+	if r := l.next(); r != ';' {
+		log.Fatalf("lexer expected ';', got '%v'", r)
+		return
+	}
+	for l.e == nil {
+		s := l.save()
+		switch l.next() {
+		case '\n':
+			l.restore(s)
+			tok.Range.End = l.pos
+			l.toks = append(l.toks, tok)
+			return
+		}
+	}
+}
+
+// newline processes the Newline token at the current position.
+// The lexer *must* know the next token is a Newline before calling.
+func (l *lexer) newline() {
+	tok := &Token{Type: Newline, Range: Range{Start: l.pos, End: l.pos}}
+	if r := l.next(); r != '\n' {
+		log.Fatalf("lexer expected '\n', got '%v'", r)
+		return
+	}
+	tok.Range.End = l.pos
+	l.toks = append(l.toks, tok)
+}
+
+// lex returns all the tokens and diagnostics after lexing source.
+func lex(source string) ([]*Token, []Diagnostic, error) {
+	l := lexer{source: source, lexerState: lexerState{pos: Position{1, 1}}}
+
+	lastPos := Position{}
+	for l.e == nil {
+		// Sanity check the parser is making progress
+		if l.pos == lastPos {
+			log.Panicf("Parsing stuck at %v", l.pos)
+		}
+		lastPos = l.pos
+
+		s := l.save()
+		r := l.next()
+		switch {
+		case r == '%':
+			l.restore(s)
+			l.pident()
+		case r == '+' || r == '-' || r == '_' || isAlphaNumeric(r):
+			l.restore(s)
+			l.numberOrIdent()
+		case r == '"':
+			l.restore(s)
+			l.string()
+		case r == '=':
+			l.restore(s)
+			l.operator()
+		case r == ';':
+			l.restore(s)
+			l.lineComment()
+		case r == '\n':
+			l.restore(s)
+			l.newline()
+		}
+	}
+	if l.e != nil && l.e != io.EOF {
+		return nil, nil, l.e
+	}
+	return l.toks, l.diags, nil
+}
+
+func isNumeric(r rune) bool      { return unicode.IsDigit(r) }
+func isAlpha(r rune) bool        { return unicode.IsLetter(r) }
+func isAlphaNumeric(r rune) bool { return isAlpha(r) || isNumeric(r) }
+
+type parser struct {
+	lines    []string               // all source lines
+	toks     []*Token               // all tokens
+	diags    []Diagnostic           // parser emitted diagnostics
+	idents   map[string]*Identifier // identifiers by name
+	mappings map[*Token]interface{} // tokens to semantic map
+	insts    []*Instruction         // all instructions
+}
+
+func (p *parser) parse() error {
+	for i := 0; i < len(p.toks); {
+		if p.newline(i) || p.comment(i) {
+			i++
+			continue
+		}
+		if n := p.instruction(i); n > 0 {
+			i += n
+		} else {
+			p.unexpected(i)
+			i++
+		}
+	}
+	return nil
+}
+
+// instruction parses the instruction starting at the i'th token.
+func (p *parser) instruction(i int) (n int) {
+	inst := &Instruction{}
+
+	switch {
+	case p.opcode(i) != nil:
+		inst.Opcode = p.opcode(i)
+		inst.Tokens = []*Token{p.tok(i)}
+		p.mappings[p.tok(i)] = inst
+		n++
+	case p.opcode(i+2) != nil: // try '%id' '='
+		inst.Result, inst.Opcode = p.pident(i), p.opcode(i+2)
+		if inst.Result == nil || p.operator(i+1) != "=" {
+			return 0
+		}
+		n += 3
+		inst.Tokens = []*Token{p.tok(i), p.tok(i + 1), p.tok(i + 2)}
+		p.mappings[p.tok(i+2)] = inst
+	default:
+		return
+	}
+
+	expectsResult := len(inst.Opcode.Operands) > 0 && IsResult(inst.Opcode.Operands[0].Kind)
+	operands := inst.Opcode.Operands
+	switch {
+	case inst.Result != nil && !expectsResult:
+		p.err(inst.Result, "'%s' does not have a result", inst.Opcode.Opname)
+		return
+	case inst.Result == nil && expectsResult:
+		p.err(p.tok(i), "'%s' expects a result", inst.Opcode.Opname)
+		return
+	case inst.Result != nil && expectsResult:
+		// Check the result is of the correct type
+		o := inst.Opcode.Operands[0]
+		p.operand(o.Name, o.Kind, i, false)
+		operands = operands[1:]
+		p.addIdentDef(inst.Result.Text(p.lines), inst, p.tok(i))
+	}
+
+	for _, o := range operands {
+		if p.newline(i + n) {
+			break
+		}
+
+		switch o.Quantifier {
+		case schema.Once:
+			if op, c := p.operand(o.Name, o.Kind, i+n, false); op != nil {
+				inst.Tokens = append(inst.Tokens, op.Tokens...)
+				n += c
+			}
+		case schema.ZeroOrOnce:
+			if op, c := p.operand(o.Name, o.Kind, i+n, true); op != nil {
+				inst.Tokens = append(inst.Tokens, op.Tokens...)
+				n += c
+			}
+		case schema.ZeroOrMany:
+			for !p.newline(i + n) {
+				if op, c := p.operand(o.Name, o.Kind, i+n, true); op != nil {
+					inst.Tokens = append(inst.Tokens, op.Tokens...)
+					n += c
+				} else {
+					break
+				}
+			}
+		}
+	}
+
+	for _, t := range inst.Tokens {
+		inst.Range.grow(t.Range)
+	}
+
+	p.insts = append(p.insts, inst)
+	return
+}
+
+// operand parses the operand with the name n, kind k, starting at the i'th
+// token.
+func (p *parser) operand(n string, k *schema.OperandKind, i int, optional bool) (*Operand, int) {
+	tok := p.tok(i)
+	if tok == nil {
+		return nil, 0
+	}
+
+	op := &Operand{
+		Name:   n,
+		Kind:   k,
+		Tokens: []*Token{tok},
+	}
+	p.mappings[tok] = op
+
+	switch k.Category {
+	case schema.OperandCategoryBitEnum, schema.OperandCategoryValueEnum:
+		s := tok.Text(p.lines)
+		for _, e := range k.Enumerants {
+			if e.Enumerant == s {
+				n := 1
+				for _, param := range e.Parameters {
+					p, c := p.operand(param.Name, param.Kind, i+n, false)
+					if p != nil {
+						op.Tokens = append(op.Tokens, p.Tokens...)
+						op.Parameters = append(op.Parameters, p)
+					}
+					n += c
+				}
+				return op, n
+			}
+		}
+		if !optional {
+			p.err(p.tok(i), "invalid operand value '%s'", s)
+		}
+		return nil, 0
+
+	case schema.OperandCategoryID:
+		id := p.pident(i)
+		if id != nil {
+			p.addIdentRef(p.tok(i))
+			return op, 1
+		}
+		if !optional {
+			p.err(p.tok(i), "operand requires id, got '%s'", tok.Text(p.lines))
+		}
+		return nil, 0
+
+	case schema.OperandCategoryLiteral:
+		switch tok.Type {
+		case String, Integer, Float:
+			return op, 1
+		}
+		if !optional {
+			p.err(p.tok(i), "operand requires literal, got '%s'", tok.Text(p.lines))
+		}
+		return nil, 0
+
+	case schema.OperandCategoryComposite:
+		n := 1
+		for _, b := range k.Bases {
+			o, c := p.operand(b.Kind, b, i+n, optional)
+			if o != nil {
+				op.Tokens = append(op.Tokens, o.Tokens...)
+			}
+			n += c
+		}
+		return op, n
+
+	default:
+		p.err(p.tok(i), "OperandKind '%s' has unexpected category '%s'", k.Kind, k.Category)
+		return nil, 0
+	}
+}
+
+// tok returns the i'th token, or nil if i is out of bounds.
+func (p *parser) tok(i int) *Token {
+	if i < 0 || i >= len(p.toks) {
+		return nil
+	}
+	return p.toks[i]
+}
+
+// opcode returns the schema.Opcode for the i'th token, or nil if the i'th token
+// does not represent an opcode.
+func (p *parser) opcode(i int) *schema.Opcode {
+	if tok := p.ident(i); tok != nil {
+		name := tok.Text(p.lines)
+		if inst, found := schema.Opcodes[name]; found {
+			return inst
+		}
+	}
+	return nil
+}
+
+// operator returns the operator for the i'th token, or and empty string if the
+// i'th token is not an operator.
+func (p *parser) operator(i int) string {
+	if tok := p.tok(i); tok != nil && tok.Type == Operator {
+		return tok.Text(p.lines)
+	}
+	return ""
+}
+
+// ident returns the i'th token if it is an Ident, otherwise nil.
+func (p *parser) ident(i int) *Token {
+	if tok := p.tok(i); tok != nil && tok.Type == Ident {
+		return tok
+	}
+	return nil
+}
+
+// pident returns the i'th token if it is an PIdent, otherwise nil.
+func (p *parser) pident(i int) *Token {
+	if tok := p.tok(i); tok != nil && tok.Type == PIdent {
+		return tok
+	}
+	return nil
+}
+
+// comment returns true if the i'th token is a Comment, otherwise false.
+func (p *parser) comment(i int) bool {
+	if tok := p.tok(i); tok != nil && tok.Type == Comment {
+		return true
+	}
+	return false
+}
+
+// newline returns true if the i'th token is a Newline, otherwise false.
+func (p *parser) newline(i int) bool {
+	if tok := p.tok(i); tok != nil && tok.Type == Newline {
+		return true
+	}
+	return false
+}
+
+// unexpected emits an 'unexpected token error' for the i'th token.
+func (p *parser) unexpected(i int) {
+	p.err(p.toks[i], "syntax error: unexpected '%s'", p.toks[i].Text(p.lines))
+}
+
+// addIdentDef records the token definition for the instruction inst with the
+// given id.
+func (p *parser) addIdentDef(id string, inst *Instruction, def *Token) {
+	i, existing := p.idents[id]
+	if !existing {
+		i = &Identifier{}
+		p.idents[id] = i
+	}
+	if i.Definition == nil {
+		i.Definition = inst
+	} else {
+		p.err(def, "id '%v' redeclared", id)
+	}
+}
+
+// addIdentRef adds a identifier reference for the token ref.
+func (p *parser) addIdentRef(ref *Token) {
+	id := ref.Text(p.lines)
+	i, existing := p.idents[id]
+	if !existing {
+		i = &Identifier{}
+		p.idents[id] = i
+	}
+	i.References = append(i.References, ref)
+}
+
+// err appends an fmt.Printf style error into l.diags for the given token.
+func (p *parser) err(tok *Token, msg string, args ...interface{}) {
+	rng := Range{}
+	if tok != nil {
+		rng = tok.Range
+	}
+	p.diags = append(p.diags, Diagnostic{
+		Range:    rng,
+		Severity: SeverityError,
+		Message:  fmt.Sprintf(msg, args...),
+	})
+}
+
+// Parse parses the SPIR-V assembly string source, returning the parse results.
+func Parse(source string) (Results, error) {
+	toks, diags, err := lex(source)
+	if err != nil {
+		return Results{}, err
+	}
+	lines := strings.SplitAfter(source, "\n")
+	p := parser{
+		lines:    lines,
+		toks:     toks,
+		idents:   map[string]*Identifier{},
+		mappings: map[*Token]interface{}{},
+	}
+	if err := p.parse(); err != nil {
+		return Results{}, err
+	}
+	diags = append(diags, p.diags...)
+	return Results{
+		Lines:       lines,
+		Tokens:      toks,
+		Diagnostics: p.diags,
+		Identifiers: p.idents,
+		Mappings:    p.mappings,
+	}, nil
+}
+
+// IsResult returns true if k is used to store the result of an instruction.
+func IsResult(k *schema.OperandKind) bool {
+	switch k {
+	case schema.OperandKindIdResult, schema.OperandKindIdResultType:
+		return true
+	default:
+		return false
+	}
+}
+
+// Results holds the output of Parse().
+type Results struct {
+	Lines       []string
+	Tokens      []*Token
+	Diagnostics []Diagnostic
+	Identifiers map[string]*Identifier // identifiers by name
+	Mappings    map[*Token]interface{} // tokens to semantic map
+}
+
+// Instruction describes a single instruction instance
+type Instruction struct {
+	Tokens   []*Token       // all the tokens that make up the instruction
+	Result   *Token         // the token that represents the result of the instruction, or nil
+	Operands []*Operand     // the operands of the instruction
+	Range    Range          // the textual range of the instruction
+	Opcode   *schema.Opcode // the opcode for the instruction
+}
+
+// Operand describes a single operand instance
+type Operand struct {
+	Name       string              // name of the operand
+	Kind       *schema.OperandKind // kind of the operand
+	Tokens     []*Token            // all the tokens that make up the operand
+	Parameters []*Operand          // all the parameters for the operand
+}
+
+// Identifier describes a single, unique SPIR-V identifier (i.e. %32)
+type Identifier struct {
+	Definition *Instruction // where the identifier was defined
+	References []*Token     // all the places the identifier was referenced
+}
+
+// Severity is an enumerator of diagnositc seeverities
+type Severity int
+
+// Severity levels
+const (
+	SeverityError Severity = iota
+	SeverityWarning
+	SeverityInformation
+	SeverityHint
+)
+
+// Diagnostic holds a single diagnostic message that was generated while
+// parsing.
+type Diagnostic struct {
+	Range    Range
+	Severity Severity
+	Message  string
+}
diff --git a/utils/vscode/src/schema/schema.go b/utils/vscode/src/schema/schema.go
new file mode 100755
index 0000000..c7e1ca4
--- /dev/null
+++ b/utils/vscode/src/schema/schema.go
@@ -0,0 +1,18118 @@
+// Copyright (C) 2019 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Generated by gen-grammar.go --template=../schema/schema.go.tmpl --out=../schema/schema.go
+// Do not modify this file directly.
+
+package schema
+
+// Opcode holds information about a specific SPIR-V opcode.
+type Opcode struct {
+	Opname   string
+	Class    string
+	Opcode   int
+	Operands []Operand
+}
+
+// Operand contains information about a logical operand for an instruction.
+type Operand struct {
+	Kind       *OperandKind
+	Name       string
+	Quantifier Quantifier
+}
+
+// OperandKind contains information about a specific operand kind.
+type OperandKind struct {
+	Category   OperandCategory
+	Kind       string
+	Enumerants []Enumerant
+	Bases      []*OperandKind
+}
+
+// Enumerant contains information about an enumerant in an enum.
+type Enumerant struct {
+	Enumerant    string
+	Value        interface{}
+	Capabilities []string
+	Parameters   []Parameter
+	Version      string
+}
+
+// Parameter contains information about a logical parameter for an enumerant.
+type Parameter struct {
+	Kind *OperandKind
+	Name string
+}
+
+// Quantifier indicates the number of times the quantified term may appear.
+type Quantifier string
+
+const (
+	// Once indicates the quantified term may appear exactly once.
+	Once Quantifier = ""
+
+	// ZeroOrOnce indicates the quantified term may appear zero or one
+	// time; an optional term.
+	ZeroOrOnce Quantifier = "?"
+
+	// ZeroOrMany indicates the quantified term may appear any number of
+	// times.
+	ZeroOrMany Quantifier = "*"
+)
+
+// OperandCategory is an enumerator that groups operand kinds.
+type OperandCategory string
+
+const (
+	// OperandCategoryBitEnum describes an operand kind where its value is a
+	// mask, which is formed by combining the bits specified as enumerants in an
+	// enum.
+	OperandCategoryBitEnum = "BitEnum"
+
+	// OperandCategoryValueEnum describes an operand kind where its value is an
+	// enumerant from an enum.
+	OperandCategoryValueEnum = "ValueEnum"
+
+	// OperandCategoryID describes and operand kind where its value is an <id>
+	// definition or reference.
+	OperandCategoryID = "Id"
+
+	// OperandCategoryLiteral describes and operand kind where its value is an
+	// literal number or string.
+	OperandCategoryLiteral = "Literal"
+
+	// OperandCategoryComposite describes and operand kind where its value is
+	// composed from operand values from the above categories.
+	OperandCategoryComposite = "Composite"
+)
+
+var (
+	// Opcodes is a map of opcode name to Opcode description.
+	Opcodes = map[string]*Opcode {
+		"OpNop": OpNop,
+		"OpUndef": OpUndef,
+		"OpSourceContinued": OpSourceContinued,
+		"OpSource": OpSource,
+		"OpSourceExtension": OpSourceExtension,
+		"OpName": OpName,
+		"OpMemberName": OpMemberName,
+		"OpString": OpString,
+		"OpLine": OpLine,
+		"OpExtension": OpExtension,
+		"OpExtInstImport": OpExtInstImport,
+		"OpExtInst": OpExtInst,
+		"OpMemoryModel": OpMemoryModel,
+		"OpEntryPoint": OpEntryPoint,
+		"OpExecutionMode": OpExecutionMode,
+		"OpCapability": OpCapability,
+		"OpTypeVoid": OpTypeVoid,
+		"OpTypeBool": OpTypeBool,
+		"OpTypeInt": OpTypeInt,
+		"OpTypeFloat": OpTypeFloat,
+		"OpTypeVector": OpTypeVector,
+		"OpTypeMatrix": OpTypeMatrix,
+		"OpTypeImage": OpTypeImage,
+		"OpTypeSampler": OpTypeSampler,
+		"OpTypeSampledImage": OpTypeSampledImage,
+		"OpTypeArray": OpTypeArray,
+		"OpTypeRuntimeArray": OpTypeRuntimeArray,
+		"OpTypeStruct": OpTypeStruct,
+		"OpTypeOpaque": OpTypeOpaque,
+		"OpTypePointer": OpTypePointer,
+		"OpTypeFunction": OpTypeFunction,
+		"OpTypeEvent": OpTypeEvent,
+		"OpTypeDeviceEvent": OpTypeDeviceEvent,
+		"OpTypeReserveId": OpTypeReserveId,
+		"OpTypeQueue": OpTypeQueue,
+		"OpTypePipe": OpTypePipe,
+		"OpTypeForwardPointer": OpTypeForwardPointer,
+		"OpConstantTrue": OpConstantTrue,
+		"OpConstantFalse": OpConstantFalse,
+		"OpConstant": OpConstant,
+		"OpConstantComposite": OpConstantComposite,
+		"OpConstantSampler": OpConstantSampler,
+		"OpConstantNull": OpConstantNull,
+		"OpSpecConstantTrue": OpSpecConstantTrue,
+		"OpSpecConstantFalse": OpSpecConstantFalse,
+		"OpSpecConstant": OpSpecConstant,
+		"OpSpecConstantComposite": OpSpecConstantComposite,
+		"OpSpecConstantOp": OpSpecConstantOp,
+		"OpFunction": OpFunction,
+		"OpFunctionParameter": OpFunctionParameter,
+		"OpFunctionEnd": OpFunctionEnd,
+		"OpFunctionCall": OpFunctionCall,
+		"OpVariable": OpVariable,
+		"OpImageTexelPointer": OpImageTexelPointer,
+		"OpLoad": OpLoad,
+		"OpStore": OpStore,
+		"OpCopyMemory": OpCopyMemory,
+		"OpCopyMemorySized": OpCopyMemorySized,
+		"OpAccessChain": OpAccessChain,
+		"OpInBoundsAccessChain": OpInBoundsAccessChain,
+		"OpPtrAccessChain": OpPtrAccessChain,
+		"OpArrayLength": OpArrayLength,
+		"OpGenericPtrMemSemantics": OpGenericPtrMemSemantics,
+		"OpInBoundsPtrAccessChain": OpInBoundsPtrAccessChain,
+		"OpDecorate": OpDecorate,
+		"OpMemberDecorate": OpMemberDecorate,
+		"OpDecorationGroup": OpDecorationGroup,
+		"OpGroupDecorate": OpGroupDecorate,
+		"OpGroupMemberDecorate": OpGroupMemberDecorate,
+		"OpVectorExtractDynamic": OpVectorExtractDynamic,
+		"OpVectorInsertDynamic": OpVectorInsertDynamic,
+		"OpVectorShuffle": OpVectorShuffle,
+		"OpCompositeConstruct": OpCompositeConstruct,
+		"OpCompositeExtract": OpCompositeExtract,
+		"OpCompositeInsert": OpCompositeInsert,
+		"OpCopyObject": OpCopyObject,
+		"OpTranspose": OpTranspose,
+		"OpSampledImage": OpSampledImage,
+		"OpImageSampleImplicitLod": OpImageSampleImplicitLod,
+		"OpImageSampleExplicitLod": OpImageSampleExplicitLod,
+		"OpImageSampleDrefImplicitLod": OpImageSampleDrefImplicitLod,
+		"OpImageSampleDrefExplicitLod": OpImageSampleDrefExplicitLod,
+		"OpImageSampleProjImplicitLod": OpImageSampleProjImplicitLod,
+		"OpImageSampleProjExplicitLod": OpImageSampleProjExplicitLod,
+		"OpImageSampleProjDrefImplicitLod": OpImageSampleProjDrefImplicitLod,
+		"OpImageSampleProjDrefExplicitLod": OpImageSampleProjDrefExplicitLod,
+		"OpImageFetch": OpImageFetch,
+		"OpImageGather": OpImageGather,
+		"OpImageDrefGather": OpImageDrefGather,
+		"OpImageRead": OpImageRead,
+		"OpImageWrite": OpImageWrite,
+		"OpImage": OpImage,
+		"OpImageQueryFormat": OpImageQueryFormat,
+		"OpImageQueryOrder": OpImageQueryOrder,
+		"OpImageQuerySizeLod": OpImageQuerySizeLod,
+		"OpImageQuerySize": OpImageQuerySize,
+		"OpImageQueryLod": OpImageQueryLod,
+		"OpImageQueryLevels": OpImageQueryLevels,
+		"OpImageQuerySamples": OpImageQuerySamples,
+		"OpConvertFToU": OpConvertFToU,
+		"OpConvertFToS": OpConvertFToS,
+		"OpConvertSToF": OpConvertSToF,
+		"OpConvertUToF": OpConvertUToF,
+		"OpUConvert": OpUConvert,
+		"OpSConvert": OpSConvert,
+		"OpFConvert": OpFConvert,
+		"OpQuantizeToF16": OpQuantizeToF16,
+		"OpConvertPtrToU": OpConvertPtrToU,
+		"OpSatConvertSToU": OpSatConvertSToU,
+		"OpSatConvertUToS": OpSatConvertUToS,
+		"OpConvertUToPtr": OpConvertUToPtr,
+		"OpPtrCastToGeneric": OpPtrCastToGeneric,
+		"OpGenericCastToPtr": OpGenericCastToPtr,
+		"OpGenericCastToPtrExplicit": OpGenericCastToPtrExplicit,
+		"OpBitcast": OpBitcast,
+		"OpSNegate": OpSNegate,
+		"OpFNegate": OpFNegate,
+		"OpIAdd": OpIAdd,
+		"OpFAdd": OpFAdd,
+		"OpISub": OpISub,
+		"OpFSub": OpFSub,
+		"OpIMul": OpIMul,
+		"OpFMul": OpFMul,
+		"OpUDiv": OpUDiv,
+		"OpSDiv": OpSDiv,
+		"OpFDiv": OpFDiv,
+		"OpUMod": OpUMod,
+		"OpSRem": OpSRem,
+		"OpSMod": OpSMod,
+		"OpFRem": OpFRem,
+		"OpFMod": OpFMod,
+		"OpVectorTimesScalar": OpVectorTimesScalar,
+		"OpMatrixTimesScalar": OpMatrixTimesScalar,
+		"OpVectorTimesMatrix": OpVectorTimesMatrix,
+		"OpMatrixTimesVector": OpMatrixTimesVector,
+		"OpMatrixTimesMatrix": OpMatrixTimesMatrix,
+		"OpOuterProduct": OpOuterProduct,
+		"OpDot": OpDot,
+		"OpIAddCarry": OpIAddCarry,
+		"OpISubBorrow": OpISubBorrow,
+		"OpUMulExtended": OpUMulExtended,
+		"OpSMulExtended": OpSMulExtended,
+		"OpAny": OpAny,
+		"OpAll": OpAll,
+		"OpIsNan": OpIsNan,
+		"OpIsInf": OpIsInf,
+		"OpIsFinite": OpIsFinite,
+		"OpIsNormal": OpIsNormal,
+		"OpSignBitSet": OpSignBitSet,
+		"OpLessOrGreater": OpLessOrGreater,
+		"OpOrdered": OpOrdered,
+		"OpUnordered": OpUnordered,
+		"OpLogicalEqual": OpLogicalEqual,
+		"OpLogicalNotEqual": OpLogicalNotEqual,
+		"OpLogicalOr": OpLogicalOr,
+		"OpLogicalAnd": OpLogicalAnd,
+		"OpLogicalNot": OpLogicalNot,
+		"OpSelect": OpSelect,
+		"OpIEqual": OpIEqual,
+		"OpINotEqual": OpINotEqual,
+		"OpUGreaterThan": OpUGreaterThan,
+		"OpSGreaterThan": OpSGreaterThan,
+		"OpUGreaterThanEqual": OpUGreaterThanEqual,
+		"OpSGreaterThanEqual": OpSGreaterThanEqual,
+		"OpULessThan": OpULessThan,
+		"OpSLessThan": OpSLessThan,
+		"OpULessThanEqual": OpULessThanEqual,
+		"OpSLessThanEqual": OpSLessThanEqual,
+		"OpFOrdEqual": OpFOrdEqual,
+		"OpFUnordEqual": OpFUnordEqual,
+		"OpFOrdNotEqual": OpFOrdNotEqual,
+		"OpFUnordNotEqual": OpFUnordNotEqual,
+		"OpFOrdLessThan": OpFOrdLessThan,
+		"OpFUnordLessThan": OpFUnordLessThan,
+		"OpFOrdGreaterThan": OpFOrdGreaterThan,
+		"OpFUnordGreaterThan": OpFUnordGreaterThan,
+		"OpFOrdLessThanEqual": OpFOrdLessThanEqual,
+		"OpFUnordLessThanEqual": OpFUnordLessThanEqual,
+		"OpFOrdGreaterThanEqual": OpFOrdGreaterThanEqual,
+		"OpFUnordGreaterThanEqual": OpFUnordGreaterThanEqual,
+		"OpShiftRightLogical": OpShiftRightLogical,
+		"OpShiftRightArithmetic": OpShiftRightArithmetic,
+		"OpShiftLeftLogical": OpShiftLeftLogical,
+		"OpBitwiseOr": OpBitwiseOr,
+		"OpBitwiseXor": OpBitwiseXor,
+		"OpBitwiseAnd": OpBitwiseAnd,
+		"OpNot": OpNot,
+		"OpBitFieldInsert": OpBitFieldInsert,
+		"OpBitFieldSExtract": OpBitFieldSExtract,
+		"OpBitFieldUExtract": OpBitFieldUExtract,
+		"OpBitReverse": OpBitReverse,
+		"OpBitCount": OpBitCount,
+		"OpDPdx": OpDPdx,
+		"OpDPdy": OpDPdy,
+		"OpFwidth": OpFwidth,
+		"OpDPdxFine": OpDPdxFine,
+		"OpDPdyFine": OpDPdyFine,
+		"OpFwidthFine": OpFwidthFine,
+		"OpDPdxCoarse": OpDPdxCoarse,
+		"OpDPdyCoarse": OpDPdyCoarse,
+		"OpFwidthCoarse": OpFwidthCoarse,
+		"OpEmitVertex": OpEmitVertex,
+		"OpEndPrimitive": OpEndPrimitive,
+		"OpEmitStreamVertex": OpEmitStreamVertex,
+		"OpEndStreamPrimitive": OpEndStreamPrimitive,
+		"OpControlBarrier": OpControlBarrier,
+		"OpMemoryBarrier": OpMemoryBarrier,
+		"OpAtomicLoad": OpAtomicLoad,
+		"OpAtomicStore": OpAtomicStore,
+		"OpAtomicExchange": OpAtomicExchange,
+		"OpAtomicCompareExchange": OpAtomicCompareExchange,
+		"OpAtomicCompareExchangeWeak": OpAtomicCompareExchangeWeak,
+		"OpAtomicIIncrement": OpAtomicIIncrement,
+		"OpAtomicIDecrement": OpAtomicIDecrement,
+		"OpAtomicIAdd": OpAtomicIAdd,
+		"OpAtomicISub": OpAtomicISub,
+		"OpAtomicSMin": OpAtomicSMin,
+		"OpAtomicUMin": OpAtomicUMin,
+		"OpAtomicSMax": OpAtomicSMax,
+		"OpAtomicUMax": OpAtomicUMax,
+		"OpAtomicAnd": OpAtomicAnd,
+		"OpAtomicOr": OpAtomicOr,
+		"OpAtomicXor": OpAtomicXor,
+		"OpPhi": OpPhi,
+		"OpLoopMerge": OpLoopMerge,
+		"OpSelectionMerge": OpSelectionMerge,
+		"OpLabel": OpLabel,
+		"OpBranch": OpBranch,
+		"OpBranchConditional": OpBranchConditional,
+		"OpSwitch": OpSwitch,
+		"OpKill": OpKill,
+		"OpReturn": OpReturn,
+		"OpReturnValue": OpReturnValue,
+		"OpUnreachable": OpUnreachable,
+		"OpLifetimeStart": OpLifetimeStart,
+		"OpLifetimeStop": OpLifetimeStop,
+		"OpGroupAsyncCopy": OpGroupAsyncCopy,
+		"OpGroupWaitEvents": OpGroupWaitEvents,
+		"OpGroupAll": OpGroupAll,
+		"OpGroupAny": OpGroupAny,
+		"OpGroupBroadcast": OpGroupBroadcast,
+		"OpGroupIAdd": OpGroupIAdd,
+		"OpGroupFAdd": OpGroupFAdd,
+		"OpGroupFMin": OpGroupFMin,
+		"OpGroupUMin": OpGroupUMin,
+		"OpGroupSMin": OpGroupSMin,
+		"OpGroupFMax": OpGroupFMax,
+		"OpGroupUMax": OpGroupUMax,
+		"OpGroupSMax": OpGroupSMax,
+		"OpReadPipe": OpReadPipe,
+		"OpWritePipe": OpWritePipe,
+		"OpReservedReadPipe": OpReservedReadPipe,
+		"OpReservedWritePipe": OpReservedWritePipe,
+		"OpReserveReadPipePackets": OpReserveReadPipePackets,
+		"OpReserveWritePipePackets": OpReserveWritePipePackets,
+		"OpCommitReadPipe": OpCommitReadPipe,
+		"OpCommitWritePipe": OpCommitWritePipe,
+		"OpIsValidReserveId": OpIsValidReserveId,
+		"OpGetNumPipePackets": OpGetNumPipePackets,
+		"OpGetMaxPipePackets": OpGetMaxPipePackets,
+		"OpGroupReserveReadPipePackets": OpGroupReserveReadPipePackets,
+		"OpGroupReserveWritePipePackets": OpGroupReserveWritePipePackets,
+		"OpGroupCommitReadPipe": OpGroupCommitReadPipe,
+		"OpGroupCommitWritePipe": OpGroupCommitWritePipe,
+		"OpEnqueueMarker": OpEnqueueMarker,
+		"OpEnqueueKernel": OpEnqueueKernel,
+		"OpGetKernelNDrangeSubGroupCount": OpGetKernelNDrangeSubGroupCount,
+		"OpGetKernelNDrangeMaxSubGroupSize": OpGetKernelNDrangeMaxSubGroupSize,
+		"OpGetKernelWorkGroupSize": OpGetKernelWorkGroupSize,
+		"OpGetKernelPreferredWorkGroupSizeMultiple": OpGetKernelPreferredWorkGroupSizeMultiple,
+		"OpRetainEvent": OpRetainEvent,
+		"OpReleaseEvent": OpReleaseEvent,
+		"OpCreateUserEvent": OpCreateUserEvent,
+		"OpIsValidEvent": OpIsValidEvent,
+		"OpSetUserEventStatus": OpSetUserEventStatus,
+		"OpCaptureEventProfilingInfo": OpCaptureEventProfilingInfo,
+		"OpGetDefaultQueue": OpGetDefaultQueue,
+		"OpBuildNDRange": OpBuildNDRange,
+		"OpImageSparseSampleImplicitLod": OpImageSparseSampleImplicitLod,
+		"OpImageSparseSampleExplicitLod": OpImageSparseSampleExplicitLod,
+		"OpImageSparseSampleDrefImplicitLod": OpImageSparseSampleDrefImplicitLod,
+		"OpImageSparseSampleDrefExplicitLod": OpImageSparseSampleDrefExplicitLod,
+		"OpImageSparseSampleProjImplicitLod": OpImageSparseSampleProjImplicitLod,
+		"OpImageSparseSampleProjExplicitLod": OpImageSparseSampleProjExplicitLod,
+		"OpImageSparseSampleProjDrefImplicitLod": OpImageSparseSampleProjDrefImplicitLod,
+		"OpImageSparseSampleProjDrefExplicitLod": OpImageSparseSampleProjDrefExplicitLod,
+		"OpImageSparseFetch": OpImageSparseFetch,
+		"OpImageSparseGather": OpImageSparseGather,
+		"OpImageSparseDrefGather": OpImageSparseDrefGather,
+		"OpImageSparseTexelsResident": OpImageSparseTexelsResident,
+		"OpNoLine": OpNoLine,
+		"OpAtomicFlagTestAndSet": OpAtomicFlagTestAndSet,
+		"OpAtomicFlagClear": OpAtomicFlagClear,
+		"OpImageSparseRead": OpImageSparseRead,
+		"OpSizeOf": OpSizeOf,
+		"OpTypePipeStorage": OpTypePipeStorage,
+		"OpConstantPipeStorage": OpConstantPipeStorage,
+		"OpCreatePipeFromPipeStorage": OpCreatePipeFromPipeStorage,
+		"OpGetKernelLocalSizeForSubgroupCount": OpGetKernelLocalSizeForSubgroupCount,
+		"OpGetKernelMaxNumSubgroups": OpGetKernelMaxNumSubgroups,
+		"OpTypeNamedBarrier": OpTypeNamedBarrier,
+		"OpNamedBarrierInitialize": OpNamedBarrierInitialize,
+		"OpMemoryNamedBarrier": OpMemoryNamedBarrier,
+		"OpModuleProcessed": OpModuleProcessed,
+		"OpExecutionModeId": OpExecutionModeId,
+		"OpDecorateId": OpDecorateId,
+		"OpGroupNonUniformElect": OpGroupNonUniformElect,
+		"OpGroupNonUniformAll": OpGroupNonUniformAll,
+		"OpGroupNonUniformAny": OpGroupNonUniformAny,
+		"OpGroupNonUniformAllEqual": OpGroupNonUniformAllEqual,
+		"OpGroupNonUniformBroadcast": OpGroupNonUniformBroadcast,
+		"OpGroupNonUniformBroadcastFirst": OpGroupNonUniformBroadcastFirst,
+		"OpGroupNonUniformBallot": OpGroupNonUniformBallot,
+		"OpGroupNonUniformInverseBallot": OpGroupNonUniformInverseBallot,
+		"OpGroupNonUniformBallotBitExtract": OpGroupNonUniformBallotBitExtract,
+		"OpGroupNonUniformBallotBitCount": OpGroupNonUniformBallotBitCount,
+		"OpGroupNonUniformBallotFindLSB": OpGroupNonUniformBallotFindLSB,
+		"OpGroupNonUniformBallotFindMSB": OpGroupNonUniformBallotFindMSB,
+		"OpGroupNonUniformShuffle": OpGroupNonUniformShuffle,
+		"OpGroupNonUniformShuffleXor": OpGroupNonUniformShuffleXor,
+		"OpGroupNonUniformShuffleUp": OpGroupNonUniformShuffleUp,
+		"OpGroupNonUniformShuffleDown": OpGroupNonUniformShuffleDown,
+		"OpGroupNonUniformIAdd": OpGroupNonUniformIAdd,
+		"OpGroupNonUniformFAdd": OpGroupNonUniformFAdd,
+		"OpGroupNonUniformIMul": OpGroupNonUniformIMul,
+		"OpGroupNonUniformFMul": OpGroupNonUniformFMul,
+		"OpGroupNonUniformSMin": OpGroupNonUniformSMin,
+		"OpGroupNonUniformUMin": OpGroupNonUniformUMin,
+		"OpGroupNonUniformFMin": OpGroupNonUniformFMin,
+		"OpGroupNonUniformSMax": OpGroupNonUniformSMax,
+		"OpGroupNonUniformUMax": OpGroupNonUniformUMax,
+		"OpGroupNonUniformFMax": OpGroupNonUniformFMax,
+		"OpGroupNonUniformBitwiseAnd": OpGroupNonUniformBitwiseAnd,
+		"OpGroupNonUniformBitwiseOr": OpGroupNonUniformBitwiseOr,
+		"OpGroupNonUniformBitwiseXor": OpGroupNonUniformBitwiseXor,
+		"OpGroupNonUniformLogicalAnd": OpGroupNonUniformLogicalAnd,
+		"OpGroupNonUniformLogicalOr": OpGroupNonUniformLogicalOr,
+		"OpGroupNonUniformLogicalXor": OpGroupNonUniformLogicalXor,
+		"OpGroupNonUniformQuadBroadcast": OpGroupNonUniformQuadBroadcast,
+		"OpGroupNonUniformQuadSwap": OpGroupNonUniformQuadSwap,
+		"OpCopyLogical": OpCopyLogical,
+		"OpPtrEqual": OpPtrEqual,
+		"OpPtrNotEqual": OpPtrNotEqual,
+		"OpPtrDiff": OpPtrDiff,
+		"OpSubgroupBallotKHR": OpSubgroupBallotKHR,
+		"OpSubgroupFirstInvocationKHR": OpSubgroupFirstInvocationKHR,
+		"OpSubgroupAllKHR": OpSubgroupAllKHR,
+		"OpSubgroupAnyKHR": OpSubgroupAnyKHR,
+		"OpSubgroupAllEqualKHR": OpSubgroupAllEqualKHR,
+		"OpSubgroupReadInvocationKHR": OpSubgroupReadInvocationKHR,
+		"OpGroupIAddNonUniformAMD": OpGroupIAddNonUniformAMD,
+		"OpGroupFAddNonUniformAMD": OpGroupFAddNonUniformAMD,
+		"OpGroupFMinNonUniformAMD": OpGroupFMinNonUniformAMD,
+		"OpGroupUMinNonUniformAMD": OpGroupUMinNonUniformAMD,
+		"OpGroupSMinNonUniformAMD": OpGroupSMinNonUniformAMD,
+		"OpGroupFMaxNonUniformAMD": OpGroupFMaxNonUniformAMD,
+		"OpGroupUMaxNonUniformAMD": OpGroupUMaxNonUniformAMD,
+		"OpGroupSMaxNonUniformAMD": OpGroupSMaxNonUniformAMD,
+		"OpFragmentMaskFetchAMD": OpFragmentMaskFetchAMD,
+		"OpFragmentFetchAMD": OpFragmentFetchAMD,
+		"OpReadClockKHR": OpReadClockKHR,
+		"OpImageSampleFootprintNV": OpImageSampleFootprintNV,
+		"OpGroupNonUniformPartitionNV": OpGroupNonUniformPartitionNV,
+		"OpWritePackedPrimitiveIndices4x8NV": OpWritePackedPrimitiveIndices4x8NV,
+		"OpReportIntersectionNV": OpReportIntersectionNV,
+		"OpIgnoreIntersectionNV": OpIgnoreIntersectionNV,
+		"OpTerminateRayNV": OpTerminateRayNV,
+		"OpTraceNV": OpTraceNV,
+		"OpTypeAccelerationStructureNV": OpTypeAccelerationStructureNV,
+		"OpExecuteCallableNV": OpExecuteCallableNV,
+		"OpTypeCooperativeMatrixNV": OpTypeCooperativeMatrixNV,
+		"OpCooperativeMatrixLoadNV": OpCooperativeMatrixLoadNV,
+		"OpCooperativeMatrixStoreNV": OpCooperativeMatrixStoreNV,
+		"OpCooperativeMatrixMulAddNV": OpCooperativeMatrixMulAddNV,
+		"OpCooperativeMatrixLengthNV": OpCooperativeMatrixLengthNV,
+		"OpBeginInvocationInterlockEXT": OpBeginInvocationInterlockEXT,
+		"OpEndInvocationInterlockEXT": OpEndInvocationInterlockEXT,
+		"OpDemoteToHelperInvocationEXT": OpDemoteToHelperInvocationEXT,
+		"OpIsHelperInvocationEXT": OpIsHelperInvocationEXT,
+		"OpSubgroupShuffleINTEL": OpSubgroupShuffleINTEL,
+		"OpSubgroupShuffleDownINTEL": OpSubgroupShuffleDownINTEL,
+		"OpSubgroupShuffleUpINTEL": OpSubgroupShuffleUpINTEL,
+		"OpSubgroupShuffleXorINTEL": OpSubgroupShuffleXorINTEL,
+		"OpSubgroupBlockReadINTEL": OpSubgroupBlockReadINTEL,
+		"OpSubgroupBlockWriteINTEL": OpSubgroupBlockWriteINTEL,
+		"OpSubgroupImageBlockReadINTEL": OpSubgroupImageBlockReadINTEL,
+		"OpSubgroupImageBlockWriteINTEL": OpSubgroupImageBlockWriteINTEL,
+		"OpSubgroupImageMediaBlockReadINTEL": OpSubgroupImageMediaBlockReadINTEL,
+		"OpSubgroupImageMediaBlockWriteINTEL": OpSubgroupImageMediaBlockWriteINTEL,
+		"OpUCountLeadingZerosINTEL": OpUCountLeadingZerosINTEL,
+		"OpUCountTrailingZerosINTEL": OpUCountTrailingZerosINTEL,
+		"OpAbsISubINTEL": OpAbsISubINTEL,
+		"OpAbsUSubINTEL": OpAbsUSubINTEL,
+		"OpIAddSatINTEL": OpIAddSatINTEL,
+		"OpUAddSatINTEL": OpUAddSatINTEL,
+		"OpIAverageINTEL": OpIAverageINTEL,
+		"OpUAverageINTEL": OpUAverageINTEL,
+		"OpIAverageRoundedINTEL": OpIAverageRoundedINTEL,
+		"OpUAverageRoundedINTEL": OpUAverageRoundedINTEL,
+		"OpISubSatINTEL": OpISubSatINTEL,
+		"OpUSubSatINTEL": OpUSubSatINTEL,
+		"OpIMul32x16INTEL": OpIMul32x16INTEL,
+		"OpUMul32x16INTEL": OpUMul32x16INTEL,
+		"OpDecorateString": OpDecorateString,
+		"OpDecorateStringGOOGLE": OpDecorateStringGOOGLE,
+		"OpMemberDecorateString": OpMemberDecorateString,
+		"OpMemberDecorateStringGOOGLE": OpMemberDecorateStringGOOGLE,
+		"OpVmeImageINTEL": OpVmeImageINTEL,
+		"OpTypeVmeImageINTEL": OpTypeVmeImageINTEL,
+		"OpTypeAvcImePayloadINTEL": OpTypeAvcImePayloadINTEL,
+		"OpTypeAvcRefPayloadINTEL": OpTypeAvcRefPayloadINTEL,
+		"OpTypeAvcSicPayloadINTEL": OpTypeAvcSicPayloadINTEL,
+		"OpTypeAvcMcePayloadINTEL": OpTypeAvcMcePayloadINTEL,
+		"OpTypeAvcMceResultINTEL": OpTypeAvcMceResultINTEL,
+		"OpTypeAvcImeResultINTEL": OpTypeAvcImeResultINTEL,
+		"OpTypeAvcImeResultSingleReferenceStreamoutINTEL": OpTypeAvcImeResultSingleReferenceStreamoutINTEL,
+		"OpTypeAvcImeResultDualReferenceStreamoutINTEL": OpTypeAvcImeResultDualReferenceStreamoutINTEL,
+		"OpTypeAvcImeSingleReferenceStreaminINTEL": OpTypeAvcImeSingleReferenceStreaminINTEL,
+		"OpTypeAvcImeDualReferenceStreaminINTEL": OpTypeAvcImeDualReferenceStreaminINTEL,
+		"OpTypeAvcRefResultINTEL": OpTypeAvcRefResultINTEL,
+		"OpTypeAvcSicResultINTEL": OpTypeAvcSicResultINTEL,
+		"OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL": OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL,
+		"OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL": OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL,
+		"OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL": OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL,
+		"OpSubgroupAvcMceSetInterShapePenaltyINTEL": OpSubgroupAvcMceSetInterShapePenaltyINTEL,
+		"OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL": OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL,
+		"OpSubgroupAvcMceSetInterDirectionPenaltyINTEL": OpSubgroupAvcMceSetInterDirectionPenaltyINTEL,
+		"OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL": OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL,
+		"OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL": OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL,
+		"OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL": OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL,
+		"OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL": OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL,
+		"OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL": OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL,
+		"OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL": OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL,
+		"OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL": OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL,
+		"OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL": OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL,
+		"OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL": OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL,
+		"OpSubgroupAvcMceSetAcOnlyHaarINTEL": OpSubgroupAvcMceSetAcOnlyHaarINTEL,
+		"OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL": OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL,
+		"OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL": OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL,
+		"OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL": OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL,
+		"OpSubgroupAvcMceConvertToImePayloadINTEL": OpSubgroupAvcMceConvertToImePayloadINTEL,
+		"OpSubgroupAvcMceConvertToImeResultINTEL": OpSubgroupAvcMceConvertToImeResultINTEL,
+		"OpSubgroupAvcMceConvertToRefPayloadINTEL": OpSubgroupAvcMceConvertToRefPayloadINTEL,
+		"OpSubgroupAvcMceConvertToRefResultINTEL": OpSubgroupAvcMceConvertToRefResultINTEL,
+		"OpSubgroupAvcMceConvertToSicPayloadINTEL": OpSubgroupAvcMceConvertToSicPayloadINTEL,
+		"OpSubgroupAvcMceConvertToSicResultINTEL": OpSubgroupAvcMceConvertToSicResultINTEL,
+		"OpSubgroupAvcMceGetMotionVectorsINTEL": OpSubgroupAvcMceGetMotionVectorsINTEL,
+		"OpSubgroupAvcMceGetInterDistortionsINTEL": OpSubgroupAvcMceGetInterDistortionsINTEL,
+		"OpSubgroupAvcMceGetBestInterDistortionsINTEL": OpSubgroupAvcMceGetBestInterDistortionsINTEL,
+		"OpSubgroupAvcMceGetInterMajorShapeINTEL": OpSubgroupAvcMceGetInterMajorShapeINTEL,
+		"OpSubgroupAvcMceGetInterMinorShapeINTEL": OpSubgroupAvcMceGetInterMinorShapeINTEL,
+		"OpSubgroupAvcMceGetInterDirectionsINTEL": OpSubgroupAvcMceGetInterDirectionsINTEL,
+		"OpSubgroupAvcMceGetInterMotionVectorCountINTEL": OpSubgroupAvcMceGetInterMotionVectorCountINTEL,
+		"OpSubgroupAvcMceGetInterReferenceIdsINTEL": OpSubgroupAvcMceGetInterReferenceIdsINTEL,
+		"OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL": OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL,
+		"OpSubgroupAvcImeInitializeINTEL": OpSubgroupAvcImeInitializeINTEL,
+		"OpSubgroupAvcImeSetSingleReferenceINTEL": OpSubgroupAvcImeSetSingleReferenceINTEL,
+		"OpSubgroupAvcImeSetDualReferenceINTEL": OpSubgroupAvcImeSetDualReferenceINTEL,
+		"OpSubgroupAvcImeRefWindowSizeINTEL": OpSubgroupAvcImeRefWindowSizeINTEL,
+		"OpSubgroupAvcImeAdjustRefOffsetINTEL": OpSubgroupAvcImeAdjustRefOffsetINTEL,
+		"OpSubgroupAvcImeConvertToMcePayloadINTEL": OpSubgroupAvcImeConvertToMcePayloadINTEL,
+		"OpSubgroupAvcImeSetMaxMotionVectorCountINTEL": OpSubgroupAvcImeSetMaxMotionVectorCountINTEL,
+		"OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL": OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL,
+		"OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL": OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL,
+		"OpSubgroupAvcImeSetWeightedSadINTEL": OpSubgroupAvcImeSetWeightedSadINTEL,
+		"OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL": OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL,
+		"OpSubgroupAvcImeEvaluateWithDualReferenceINTEL": OpSubgroupAvcImeEvaluateWithDualReferenceINTEL,
+		"OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL": OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL,
+		"OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL": OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL,
+		"OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL": OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL,
+		"OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL": OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL,
+		"OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL": OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL,
+		"OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL": OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL,
+		"OpSubgroupAvcImeConvertToMceResultINTEL": OpSubgroupAvcImeConvertToMceResultINTEL,
+		"OpSubgroupAvcImeGetSingleReferenceStreaminINTEL": OpSubgroupAvcImeGetSingleReferenceStreaminINTEL,
+		"OpSubgroupAvcImeGetDualReferenceStreaminINTEL": OpSubgroupAvcImeGetDualReferenceStreaminINTEL,
+		"OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL": OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL,
+		"OpSubgroupAvcImeStripDualReferenceStreamoutINTEL": OpSubgroupAvcImeStripDualReferenceStreamoutINTEL,
+		"OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL": OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL,
+		"OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL": OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL,
+		"OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL": OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL,
+		"OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL": OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL,
+		"OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL": OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL,
+		"OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL": OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL,
+		"OpSubgroupAvcImeGetBorderReachedINTEL": OpSubgroupAvcImeGetBorderReachedINTEL,
+		"OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL": OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL,
+		"OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL": OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL,
+		"OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL": OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL,
+		"OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL": OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL,
+		"OpSubgroupAvcFmeInitializeINTEL": OpSubgroupAvcFmeInitializeINTEL,
+		"OpSubgroupAvcBmeInitializeINTEL": OpSubgroupAvcBmeInitializeINTEL,
+		"OpSubgroupAvcRefConvertToMcePayloadINTEL": OpSubgroupAvcRefConvertToMcePayloadINTEL,
+		"OpSubgroupAvcRefSetBidirectionalMixDisableINTEL": OpSubgroupAvcRefSetBidirectionalMixDisableINTEL,
+		"OpSubgroupAvcRefSetBilinearFilterEnableINTEL": OpSubgroupAvcRefSetBilinearFilterEnableINTEL,
+		"OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL": OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL,
+		"OpSubgroupAvcRefEvaluateWithDualReferenceINTEL": OpSubgroupAvcRefEvaluateWithDualReferenceINTEL,
+		"OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL": OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL,
+		"OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL": OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL,
+		"OpSubgroupAvcRefConvertToMceResultINTEL": OpSubgroupAvcRefConvertToMceResultINTEL,
+		"OpSubgroupAvcSicInitializeINTEL": OpSubgroupAvcSicInitializeINTEL,
+		"OpSubgroupAvcSicConfigureSkcINTEL": OpSubgroupAvcSicConfigureSkcINTEL,
+		"OpSubgroupAvcSicConfigureIpeLumaINTEL": OpSubgroupAvcSicConfigureIpeLumaINTEL,
+		"OpSubgroupAvcSicConfigureIpeLumaChromaINTEL": OpSubgroupAvcSicConfigureIpeLumaChromaINTEL,
+		"OpSubgroupAvcSicGetMotionVectorMaskINTEL": OpSubgroupAvcSicGetMotionVectorMaskINTEL,
+		"OpSubgroupAvcSicConvertToMcePayloadINTEL": OpSubgroupAvcSicConvertToMcePayloadINTEL,
+		"OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL": OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL,
+		"OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL": OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL,
+		"OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL": OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL,
+		"OpSubgroupAvcSicSetBilinearFilterEnableINTEL": OpSubgroupAvcSicSetBilinearFilterEnableINTEL,
+		"OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL": OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL,
+		"OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL": OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL,
+		"OpSubgroupAvcSicEvaluateIpeINTEL": OpSubgroupAvcSicEvaluateIpeINTEL,
+		"OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL": OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL,
+		"OpSubgroupAvcSicEvaluateWithDualReferenceINTEL": OpSubgroupAvcSicEvaluateWithDualReferenceINTEL,
+		"OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL": OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL,
+		"OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL": OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL,
+		"OpSubgroupAvcSicConvertToMceResultINTEL": OpSubgroupAvcSicConvertToMceResultINTEL,
+		"OpSubgroupAvcSicGetIpeLumaShapeINTEL": OpSubgroupAvcSicGetIpeLumaShapeINTEL,
+		"OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL": OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL,
+		"OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL": OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL,
+		"OpSubgroupAvcSicGetPackedIpeLumaModesINTEL": OpSubgroupAvcSicGetPackedIpeLumaModesINTEL,
+		"OpSubgroupAvcSicGetIpeChromaModeINTEL": OpSubgroupAvcSicGetIpeChromaModeINTEL,
+		"OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL": OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL,
+		"OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL": OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL,
+		"OpSubgroupAvcSicGetInterRawSadsINTEL": OpSubgroupAvcSicGetInterRawSadsINTEL,
+	}
+
+	OpNop = &Opcode {
+		Opname:   "OpNop",
+		Operands: []Operand {
+		},
+	}
+	OpUndef = &Opcode {
+		Opname:   "OpUndef",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSourceContinued = &Opcode {
+		Opname:   "OpSourceContinued",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindLiteralString,
+				Name:       "'Continued Source'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSource = &Opcode {
+		Opname:   "OpSource",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindSourceLanguage,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Version'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'File'",
+				Quantifier: "?",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralString,
+				Name:       "'Source'",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpSourceExtension = &Opcode {
+		Opname:   "OpSourceExtension",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindLiteralString,
+				Name:       "'Extension'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpName = &Opcode {
+		Opname:   "OpName",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Target'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralString,
+				Name:       "'Name'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpMemberName = &Opcode {
+		Opname:   "OpMemberName",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Type'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Member'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralString,
+				Name:       "'Name'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpString = &Opcode {
+		Opname:   "OpString",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralString,
+				Name:       "'String'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpLine = &Opcode {
+		Opname:   "OpLine",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'File'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Line'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Column'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpExtension = &Opcode {
+		Opname:   "OpExtension",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindLiteralString,
+				Name:       "'Name'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpExtInstImport = &Opcode {
+		Opname:   "OpExtInstImport",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralString,
+				Name:       "'Name'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpExtInst = &Opcode {
+		Opname:   "OpExtInst",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Set'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralExtInstInteger,
+				Name:       "'Instruction'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1', + 'Operand 2', + ...",
+				Quantifier: "*",
+			}, 
+		},
+	}
+	OpMemoryModel = &Opcode {
+		Opname:   "OpMemoryModel",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindAddressingModel,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindMemoryModel,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpEntryPoint = &Opcode {
+		Opname:   "OpEntryPoint",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindExecutionModel,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Entry Point'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralString,
+				Name:       "'Name'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Interface'",
+				Quantifier: "*",
+			}, 
+		},
+	}
+	OpExecutionMode = &Opcode {
+		Opname:   "OpExecutionMode",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Entry Point'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindExecutionMode,
+				Name:       "'Mode'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpCapability = &Opcode {
+		Opname:   "OpCapability",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindCapability,
+				Name:       "'Capability'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpTypeVoid = &Opcode {
+		Opname:   "OpTypeVoid",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpTypeBool = &Opcode {
+		Opname:   "OpTypeBool",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpTypeInt = &Opcode {
+		Opname:   "OpTypeInt",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Width'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Signedness'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpTypeFloat = &Opcode {
+		Opname:   "OpTypeFloat",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Width'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpTypeVector = &Opcode {
+		Opname:   "OpTypeVector",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Component Type'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Component Count'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpTypeMatrix = &Opcode {
+		Opname:   "OpTypeMatrix",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Column Type'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Column Count'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpTypeImage = &Opcode {
+		Opname:   "OpTypeImage",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Sampled Type'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindDim,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Depth'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Arrayed'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'MS'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Sampled'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindImageFormat,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindAccessQualifier,
+				Name:       "",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpTypeSampler = &Opcode {
+		Opname:   "OpTypeSampler",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpTypeSampledImage = &Opcode {
+		Opname:   "OpTypeSampledImage",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Image Type'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpTypeArray = &Opcode {
+		Opname:   "OpTypeArray",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Element Type'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Length'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpTypeRuntimeArray = &Opcode {
+		Opname:   "OpTypeRuntimeArray",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Element Type'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpTypeStruct = &Opcode {
+		Opname:   "OpTypeStruct",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Member 0 type', + 'member 1 type', + ...",
+				Quantifier: "*",
+			}, 
+		},
+	}
+	OpTypeOpaque = &Opcode {
+		Opname:   "OpTypeOpaque",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralString,
+				Name:       "The name of the opaque type.",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpTypePointer = &Opcode {
+		Opname:   "OpTypePointer",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindStorageClass,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Type'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpTypeFunction = &Opcode {
+		Opname:   "OpTypeFunction",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Return Type'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Parameter 0 Type', + 'Parameter 1 Type', + ...",
+				Quantifier: "*",
+			}, 
+		},
+	}
+	OpTypeEvent = &Opcode {
+		Opname:   "OpTypeEvent",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpTypeDeviceEvent = &Opcode {
+		Opname:   "OpTypeDeviceEvent",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpTypeReserveId = &Opcode {
+		Opname:   "OpTypeReserveId",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpTypeQueue = &Opcode {
+		Opname:   "OpTypeQueue",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpTypePipe = &Opcode {
+		Opname:   "OpTypePipe",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindAccessQualifier,
+				Name:       "'Qualifier'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpTypeForwardPointer = &Opcode {
+		Opname:   "OpTypeForwardPointer",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pointer Type'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindStorageClass,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpConstantTrue = &Opcode {
+		Opname:   "OpConstantTrue",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpConstantFalse = &Opcode {
+		Opname:   "OpConstantFalse",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpConstant = &Opcode {
+		Opname:   "OpConstant",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralContextDependentNumber,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpConstantComposite = &Opcode {
+		Opname:   "OpConstantComposite",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Constituents'",
+				Quantifier: "*",
+			}, 
+		},
+	}
+	OpConstantSampler = &Opcode {
+		Opname:   "OpConstantSampler",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindSamplerAddressingMode,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Param'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindSamplerFilterMode,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpConstantNull = &Opcode {
+		Opname:   "OpConstantNull",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSpecConstantTrue = &Opcode {
+		Opname:   "OpSpecConstantTrue",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSpecConstantFalse = &Opcode {
+		Opname:   "OpSpecConstantFalse",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSpecConstant = &Opcode {
+		Opname:   "OpSpecConstant",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralContextDependentNumber,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSpecConstantComposite = &Opcode {
+		Opname:   "OpSpecConstantComposite",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Constituents'",
+				Quantifier: "*",
+			}, 
+		},
+	}
+	OpSpecConstantOp = &Opcode {
+		Opname:   "OpSpecConstantOp",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralSpecConstantOpInteger,
+				Name:       "'Opcode'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpFunction = &Opcode {
+		Opname:   "OpFunction",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindFunctionControl,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Function Type'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpFunctionParameter = &Opcode {
+		Opname:   "OpFunctionParameter",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpFunctionEnd = &Opcode {
+		Opname:   "OpFunctionEnd",
+		Operands: []Operand {
+		},
+	}
+	OpFunctionCall = &Opcode {
+		Opname:   "OpFunctionCall",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Function'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Argument 0', + 'Argument 1', + ...",
+				Quantifier: "*",
+			}, 
+		},
+	}
+	OpVariable = &Opcode {
+		Opname:   "OpVariable",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindStorageClass,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Initializer'",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpImageTexelPointer = &Opcode {
+		Opname:   "OpImageTexelPointer",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Coordinate'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Sample'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpLoad = &Opcode {
+		Opname:   "OpLoad",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pointer'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindMemoryAccess,
+				Name:       "",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpStore = &Opcode {
+		Opname:   "OpStore",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pointer'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Object'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindMemoryAccess,
+				Name:       "",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpCopyMemory = &Opcode {
+		Opname:   "OpCopyMemory",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Target'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Source'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindMemoryAccess,
+				Name:       "",
+				Quantifier: "?",
+			}, 
+			Operand {
+				Kind:       OperandKindMemoryAccess,
+				Name:       "",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpCopyMemorySized = &Opcode {
+		Opname:   "OpCopyMemorySized",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Target'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Source'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Size'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindMemoryAccess,
+				Name:       "",
+				Quantifier: "?",
+			}, 
+			Operand {
+				Kind:       OperandKindMemoryAccess,
+				Name:       "",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpAccessChain = &Opcode {
+		Opname:   "OpAccessChain",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Base'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Indexes'",
+				Quantifier: "*",
+			}, 
+		},
+	}
+	OpInBoundsAccessChain = &Opcode {
+		Opname:   "OpInBoundsAccessChain",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Base'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Indexes'",
+				Quantifier: "*",
+			}, 
+		},
+	}
+	OpPtrAccessChain = &Opcode {
+		Opname:   "OpPtrAccessChain",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Base'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Element'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Indexes'",
+				Quantifier: "*",
+			}, 
+		},
+	}
+	OpArrayLength = &Opcode {
+		Opname:   "OpArrayLength",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Structure'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Array member'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGenericPtrMemSemantics = &Opcode {
+		Opname:   "OpGenericPtrMemSemantics",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pointer'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpInBoundsPtrAccessChain = &Opcode {
+		Opname:   "OpInBoundsPtrAccessChain",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Base'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Element'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Indexes'",
+				Quantifier: "*",
+			}, 
+		},
+	}
+	OpDecorate = &Opcode {
+		Opname:   "OpDecorate",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Target'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindDecoration,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpMemberDecorate = &Opcode {
+		Opname:   "OpMemberDecorate",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Structure Type'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Member'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindDecoration,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpDecorationGroup = &Opcode {
+		Opname:   "OpDecorationGroup",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGroupDecorate = &Opcode {
+		Opname:   "OpGroupDecorate",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Decoration Group'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Targets'",
+				Quantifier: "*",
+			}, 
+		},
+	}
+	OpGroupMemberDecorate = &Opcode {
+		Opname:   "OpGroupMemberDecorate",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Decoration Group'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindPairIdRefLiteralInteger,
+				Name:       "'Targets'",
+				Quantifier: "*",
+			}, 
+		},
+	}
+	OpVectorExtractDynamic = &Opcode {
+		Opname:   "OpVectorExtractDynamic",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Vector'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Index'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpVectorInsertDynamic = &Opcode {
+		Opname:   "OpVectorInsertDynamic",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Vector'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Component'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Index'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpVectorShuffle = &Opcode {
+		Opname:   "OpVectorShuffle",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Vector 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Vector 2'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Components'",
+				Quantifier: "*",
+			}, 
+		},
+	}
+	OpCompositeConstruct = &Opcode {
+		Opname:   "OpCompositeConstruct",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Constituents'",
+				Quantifier: "*",
+			}, 
+		},
+	}
+	OpCompositeExtract = &Opcode {
+		Opname:   "OpCompositeExtract",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Composite'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Indexes'",
+				Quantifier: "*",
+			}, 
+		},
+	}
+	OpCompositeInsert = &Opcode {
+		Opname:   "OpCompositeInsert",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Object'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Composite'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Indexes'",
+				Quantifier: "*",
+			}, 
+		},
+	}
+	OpCopyObject = &Opcode {
+		Opname:   "OpCopyObject",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpTranspose = &Opcode {
+		Opname:   "OpTranspose",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Matrix'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSampledImage = &Opcode {
+		Opname:   "OpSampledImage",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Sampler'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpImageSampleImplicitLod = &Opcode {
+		Opname:   "OpImageSampleImplicitLod",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Sampled Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Coordinate'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindImageOperands,
+				Name:       "",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpImageSampleExplicitLod = &Opcode {
+		Opname:   "OpImageSampleExplicitLod",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Sampled Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Coordinate'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindImageOperands,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpImageSampleDrefImplicitLod = &Opcode {
+		Opname:   "OpImageSampleDrefImplicitLod",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Sampled Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Coordinate'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'D~ref~'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindImageOperands,
+				Name:       "",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpImageSampleDrefExplicitLod = &Opcode {
+		Opname:   "OpImageSampleDrefExplicitLod",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Sampled Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Coordinate'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'D~ref~'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindImageOperands,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpImageSampleProjImplicitLod = &Opcode {
+		Opname:   "OpImageSampleProjImplicitLod",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Sampled Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Coordinate'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindImageOperands,
+				Name:       "",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpImageSampleProjExplicitLod = &Opcode {
+		Opname:   "OpImageSampleProjExplicitLod",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Sampled Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Coordinate'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindImageOperands,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpImageSampleProjDrefImplicitLod = &Opcode {
+		Opname:   "OpImageSampleProjDrefImplicitLod",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Sampled Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Coordinate'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'D~ref~'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindImageOperands,
+				Name:       "",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpImageSampleProjDrefExplicitLod = &Opcode {
+		Opname:   "OpImageSampleProjDrefExplicitLod",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Sampled Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Coordinate'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'D~ref~'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindImageOperands,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpImageFetch = &Opcode {
+		Opname:   "OpImageFetch",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Coordinate'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindImageOperands,
+				Name:       "",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpImageGather = &Opcode {
+		Opname:   "OpImageGather",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Sampled Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Coordinate'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Component'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindImageOperands,
+				Name:       "",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpImageDrefGather = &Opcode {
+		Opname:   "OpImageDrefGather",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Sampled Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Coordinate'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'D~ref~'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindImageOperands,
+				Name:       "",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpImageRead = &Opcode {
+		Opname:   "OpImageRead",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Coordinate'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindImageOperands,
+				Name:       "",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpImageWrite = &Opcode {
+		Opname:   "OpImageWrite",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Coordinate'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Texel'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindImageOperands,
+				Name:       "",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpImage = &Opcode {
+		Opname:   "OpImage",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Sampled Image'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpImageQueryFormat = &Opcode {
+		Opname:   "OpImageQueryFormat",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Image'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpImageQueryOrder = &Opcode {
+		Opname:   "OpImageQueryOrder",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Image'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpImageQuerySizeLod = &Opcode {
+		Opname:   "OpImageQuerySizeLod",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Level of Detail'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpImageQuerySize = &Opcode {
+		Opname:   "OpImageQuerySize",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Image'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpImageQueryLod = &Opcode {
+		Opname:   "OpImageQueryLod",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Sampled Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Coordinate'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpImageQueryLevels = &Opcode {
+		Opname:   "OpImageQueryLevels",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Image'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpImageQuerySamples = &Opcode {
+		Opname:   "OpImageQuerySamples",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Image'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpConvertFToU = &Opcode {
+		Opname:   "OpConvertFToU",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Float Value'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpConvertFToS = &Opcode {
+		Opname:   "OpConvertFToS",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Float Value'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpConvertSToF = &Opcode {
+		Opname:   "OpConvertSToF",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Signed Value'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpConvertUToF = &Opcode {
+		Opname:   "OpConvertUToF",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Unsigned Value'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpUConvert = &Opcode {
+		Opname:   "OpUConvert",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Unsigned Value'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSConvert = &Opcode {
+		Opname:   "OpSConvert",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Signed Value'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpFConvert = &Opcode {
+		Opname:   "OpFConvert",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Float Value'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpQuantizeToF16 = &Opcode {
+		Opname:   "OpQuantizeToF16",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpConvertPtrToU = &Opcode {
+		Opname:   "OpConvertPtrToU",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pointer'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSatConvertSToU = &Opcode {
+		Opname:   "OpSatConvertSToU",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Signed Value'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSatConvertUToS = &Opcode {
+		Opname:   "OpSatConvertUToS",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Unsigned Value'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpConvertUToPtr = &Opcode {
+		Opname:   "OpConvertUToPtr",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Integer Value'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpPtrCastToGeneric = &Opcode {
+		Opname:   "OpPtrCastToGeneric",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pointer'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGenericCastToPtr = &Opcode {
+		Opname:   "OpGenericCastToPtr",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pointer'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGenericCastToPtrExplicit = &Opcode {
+		Opname:   "OpGenericCastToPtrExplicit",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pointer'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindStorageClass,
+				Name:       "'Storage'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpBitcast = &Opcode {
+		Opname:   "OpBitcast",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSNegate = &Opcode {
+		Opname:   "OpSNegate",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpFNegate = &Opcode {
+		Opname:   "OpFNegate",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpIAdd = &Opcode {
+		Opname:   "OpIAdd",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpFAdd = &Opcode {
+		Opname:   "OpFAdd",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpISub = &Opcode {
+		Opname:   "OpISub",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpFSub = &Opcode {
+		Opname:   "OpFSub",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpIMul = &Opcode {
+		Opname:   "OpIMul",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpFMul = &Opcode {
+		Opname:   "OpFMul",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpUDiv = &Opcode {
+		Opname:   "OpUDiv",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSDiv = &Opcode {
+		Opname:   "OpSDiv",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpFDiv = &Opcode {
+		Opname:   "OpFDiv",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpUMod = &Opcode {
+		Opname:   "OpUMod",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSRem = &Opcode {
+		Opname:   "OpSRem",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSMod = &Opcode {
+		Opname:   "OpSMod",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpFRem = &Opcode {
+		Opname:   "OpFRem",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpFMod = &Opcode {
+		Opname:   "OpFMod",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpVectorTimesScalar = &Opcode {
+		Opname:   "OpVectorTimesScalar",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Vector'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Scalar'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpMatrixTimesScalar = &Opcode {
+		Opname:   "OpMatrixTimesScalar",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Matrix'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Scalar'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpVectorTimesMatrix = &Opcode {
+		Opname:   "OpVectorTimesMatrix",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Vector'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Matrix'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpMatrixTimesVector = &Opcode {
+		Opname:   "OpMatrixTimesVector",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Matrix'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Vector'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpMatrixTimesMatrix = &Opcode {
+		Opname:   "OpMatrixTimesMatrix",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'LeftMatrix'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RightMatrix'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpOuterProduct = &Opcode {
+		Opname:   "OpOuterProduct",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Vector 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Vector 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpDot = &Opcode {
+		Opname:   "OpDot",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Vector 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Vector 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpIAddCarry = &Opcode {
+		Opname:   "OpIAddCarry",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpISubBorrow = &Opcode {
+		Opname:   "OpISubBorrow",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpUMulExtended = &Opcode {
+		Opname:   "OpUMulExtended",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSMulExtended = &Opcode {
+		Opname:   "OpSMulExtended",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpAny = &Opcode {
+		Opname:   "OpAny",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Vector'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpAll = &Opcode {
+		Opname:   "OpAll",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Vector'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpIsNan = &Opcode {
+		Opname:   "OpIsNan",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpIsInf = &Opcode {
+		Opname:   "OpIsInf",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpIsFinite = &Opcode {
+		Opname:   "OpIsFinite",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpIsNormal = &Opcode {
+		Opname:   "OpIsNormal",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSignBitSet = &Opcode {
+		Opname:   "OpSignBitSet",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpLessOrGreater = &Opcode {
+		Opname:   "OpLessOrGreater",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpOrdered = &Opcode {
+		Opname:   "OpOrdered",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpUnordered = &Opcode {
+		Opname:   "OpUnordered",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'x'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'y'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpLogicalEqual = &Opcode {
+		Opname:   "OpLogicalEqual",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpLogicalNotEqual = &Opcode {
+		Opname:   "OpLogicalNotEqual",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpLogicalOr = &Opcode {
+		Opname:   "OpLogicalOr",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpLogicalAnd = &Opcode {
+		Opname:   "OpLogicalAnd",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpLogicalNot = &Opcode {
+		Opname:   "OpLogicalNot",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSelect = &Opcode {
+		Opname:   "OpSelect",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Condition'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Object 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Object 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpIEqual = &Opcode {
+		Opname:   "OpIEqual",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpINotEqual = &Opcode {
+		Opname:   "OpINotEqual",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpUGreaterThan = &Opcode {
+		Opname:   "OpUGreaterThan",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSGreaterThan = &Opcode {
+		Opname:   "OpSGreaterThan",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpUGreaterThanEqual = &Opcode {
+		Opname:   "OpUGreaterThanEqual",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSGreaterThanEqual = &Opcode {
+		Opname:   "OpSGreaterThanEqual",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpULessThan = &Opcode {
+		Opname:   "OpULessThan",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSLessThan = &Opcode {
+		Opname:   "OpSLessThan",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpULessThanEqual = &Opcode {
+		Opname:   "OpULessThanEqual",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSLessThanEqual = &Opcode {
+		Opname:   "OpSLessThanEqual",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpFOrdEqual = &Opcode {
+		Opname:   "OpFOrdEqual",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpFUnordEqual = &Opcode {
+		Opname:   "OpFUnordEqual",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpFOrdNotEqual = &Opcode {
+		Opname:   "OpFOrdNotEqual",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpFUnordNotEqual = &Opcode {
+		Opname:   "OpFUnordNotEqual",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpFOrdLessThan = &Opcode {
+		Opname:   "OpFOrdLessThan",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpFUnordLessThan = &Opcode {
+		Opname:   "OpFUnordLessThan",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpFOrdGreaterThan = &Opcode {
+		Opname:   "OpFOrdGreaterThan",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpFUnordGreaterThan = &Opcode {
+		Opname:   "OpFUnordGreaterThan",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpFOrdLessThanEqual = &Opcode {
+		Opname:   "OpFOrdLessThanEqual",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpFUnordLessThanEqual = &Opcode {
+		Opname:   "OpFUnordLessThanEqual",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpFOrdGreaterThanEqual = &Opcode {
+		Opname:   "OpFOrdGreaterThanEqual",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpFUnordGreaterThanEqual = &Opcode {
+		Opname:   "OpFUnordGreaterThanEqual",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpShiftRightLogical = &Opcode {
+		Opname:   "OpShiftRightLogical",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Base'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Shift'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpShiftRightArithmetic = &Opcode {
+		Opname:   "OpShiftRightArithmetic",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Base'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Shift'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpShiftLeftLogical = &Opcode {
+		Opname:   "OpShiftLeftLogical",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Base'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Shift'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpBitwiseOr = &Opcode {
+		Opname:   "OpBitwiseOr",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpBitwiseXor = &Opcode {
+		Opname:   "OpBitwiseXor",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpBitwiseAnd = &Opcode {
+		Opname:   "OpBitwiseAnd",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpNot = &Opcode {
+		Opname:   "OpNot",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpBitFieldInsert = &Opcode {
+		Opname:   "OpBitFieldInsert",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Base'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Insert'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Offset'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Count'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpBitFieldSExtract = &Opcode {
+		Opname:   "OpBitFieldSExtract",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Base'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Offset'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Count'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpBitFieldUExtract = &Opcode {
+		Opname:   "OpBitFieldUExtract",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Base'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Offset'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Count'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpBitReverse = &Opcode {
+		Opname:   "OpBitReverse",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Base'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpBitCount = &Opcode {
+		Opname:   "OpBitCount",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Base'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpDPdx = &Opcode {
+		Opname:   "OpDPdx",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'P'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpDPdy = &Opcode {
+		Opname:   "OpDPdy",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'P'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpFwidth = &Opcode {
+		Opname:   "OpFwidth",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'P'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpDPdxFine = &Opcode {
+		Opname:   "OpDPdxFine",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'P'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpDPdyFine = &Opcode {
+		Opname:   "OpDPdyFine",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'P'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpFwidthFine = &Opcode {
+		Opname:   "OpFwidthFine",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'P'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpDPdxCoarse = &Opcode {
+		Opname:   "OpDPdxCoarse",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'P'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpDPdyCoarse = &Opcode {
+		Opname:   "OpDPdyCoarse",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'P'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpFwidthCoarse = &Opcode {
+		Opname:   "OpFwidthCoarse",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'P'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpEmitVertex = &Opcode {
+		Opname:   "OpEmitVertex",
+		Operands: []Operand {
+		},
+	}
+	OpEndPrimitive = &Opcode {
+		Opname:   "OpEndPrimitive",
+		Operands: []Operand {
+		},
+	}
+	OpEmitStreamVertex = &Opcode {
+		Opname:   "OpEmitStreamVertex",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Stream'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpEndStreamPrimitive = &Opcode {
+		Opname:   "OpEndStreamPrimitive",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Stream'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpControlBarrier = &Opcode {
+		Opname:   "OpControlBarrier",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Memory'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdMemorySemantics,
+				Name:       "'Semantics'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpMemoryBarrier = &Opcode {
+		Opname:   "OpMemoryBarrier",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Memory'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdMemorySemantics,
+				Name:       "'Semantics'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpAtomicLoad = &Opcode {
+		Opname:   "OpAtomicLoad",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pointer'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Memory'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdMemorySemantics,
+				Name:       "'Semantics'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpAtomicStore = &Opcode {
+		Opname:   "OpAtomicStore",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pointer'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Memory'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdMemorySemantics,
+				Name:       "'Semantics'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpAtomicExchange = &Opcode {
+		Opname:   "OpAtomicExchange",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pointer'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Memory'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdMemorySemantics,
+				Name:       "'Semantics'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpAtomicCompareExchange = &Opcode {
+		Opname:   "OpAtomicCompareExchange",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pointer'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Memory'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdMemorySemantics,
+				Name:       "'Equal'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdMemorySemantics,
+				Name:       "'Unequal'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Comparator'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpAtomicCompareExchangeWeak = &Opcode {
+		Opname:   "OpAtomicCompareExchangeWeak",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pointer'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Memory'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdMemorySemantics,
+				Name:       "'Equal'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdMemorySemantics,
+				Name:       "'Unequal'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Comparator'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpAtomicIIncrement = &Opcode {
+		Opname:   "OpAtomicIIncrement",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pointer'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Memory'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdMemorySemantics,
+				Name:       "'Semantics'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpAtomicIDecrement = &Opcode {
+		Opname:   "OpAtomicIDecrement",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pointer'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Memory'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdMemorySemantics,
+				Name:       "'Semantics'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpAtomicIAdd = &Opcode {
+		Opname:   "OpAtomicIAdd",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pointer'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Memory'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdMemorySemantics,
+				Name:       "'Semantics'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpAtomicISub = &Opcode {
+		Opname:   "OpAtomicISub",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pointer'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Memory'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdMemorySemantics,
+				Name:       "'Semantics'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpAtomicSMin = &Opcode {
+		Opname:   "OpAtomicSMin",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pointer'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Memory'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdMemorySemantics,
+				Name:       "'Semantics'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpAtomicUMin = &Opcode {
+		Opname:   "OpAtomicUMin",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pointer'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Memory'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdMemorySemantics,
+				Name:       "'Semantics'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpAtomicSMax = &Opcode {
+		Opname:   "OpAtomicSMax",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pointer'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Memory'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdMemorySemantics,
+				Name:       "'Semantics'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpAtomicUMax = &Opcode {
+		Opname:   "OpAtomicUMax",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pointer'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Memory'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdMemorySemantics,
+				Name:       "'Semantics'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpAtomicAnd = &Opcode {
+		Opname:   "OpAtomicAnd",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pointer'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Memory'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdMemorySemantics,
+				Name:       "'Semantics'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpAtomicOr = &Opcode {
+		Opname:   "OpAtomicOr",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pointer'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Memory'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdMemorySemantics,
+				Name:       "'Semantics'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpAtomicXor = &Opcode {
+		Opname:   "OpAtomicXor",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pointer'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Memory'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdMemorySemantics,
+				Name:       "'Semantics'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpPhi = &Opcode {
+		Opname:   "OpPhi",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindPairIdRefIdRef,
+				Name:       "'Variable, Parent, ...'",
+				Quantifier: "*",
+			}, 
+		},
+	}
+	OpLoopMerge = &Opcode {
+		Opname:   "OpLoopMerge",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Merge Block'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Continue Target'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLoopControl,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSelectionMerge = &Opcode {
+		Opname:   "OpSelectionMerge",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Merge Block'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindSelectionControl,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpLabel = &Opcode {
+		Opname:   "OpLabel",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpBranch = &Opcode {
+		Opname:   "OpBranch",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Target Label'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpBranchConditional = &Opcode {
+		Opname:   "OpBranchConditional",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Condition'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'True Label'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'False Label'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Branch weights'",
+				Quantifier: "*",
+			}, 
+		},
+	}
+	OpSwitch = &Opcode {
+		Opname:   "OpSwitch",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Selector'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Default'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindPairLiteralIntegerIdRef,
+				Name:       "'Target'",
+				Quantifier: "*",
+			}, 
+		},
+	}
+	OpKill = &Opcode {
+		Opname:   "OpKill",
+		Operands: []Operand {
+		},
+	}
+	OpReturn = &Opcode {
+		Opname:   "OpReturn",
+		Operands: []Operand {
+		},
+	}
+	OpReturnValue = &Opcode {
+		Opname:   "OpReturnValue",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpUnreachable = &Opcode {
+		Opname:   "OpUnreachable",
+		Operands: []Operand {
+		},
+	}
+	OpLifetimeStart = &Opcode {
+		Opname:   "OpLifetimeStart",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pointer'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Size'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpLifetimeStop = &Opcode {
+		Opname:   "OpLifetimeStop",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pointer'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Size'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGroupAsyncCopy = &Opcode {
+		Opname:   "OpGroupAsyncCopy",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Destination'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Source'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Num Elements'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Stride'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Event'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGroupWaitEvents = &Opcode {
+		Opname:   "OpGroupWaitEvents",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Num Events'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Events List'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGroupAll = &Opcode {
+		Opname:   "OpGroupAll",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Predicate'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGroupAny = &Opcode {
+		Opname:   "OpGroupAny",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Predicate'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGroupBroadcast = &Opcode {
+		Opname:   "OpGroupBroadcast",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'LocalId'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGroupIAdd = &Opcode {
+		Opname:   "OpGroupIAdd",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindGroupOperation,
+				Name:       "'Operation'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'X'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGroupFAdd = &Opcode {
+		Opname:   "OpGroupFAdd",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindGroupOperation,
+				Name:       "'Operation'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'X'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGroupFMin = &Opcode {
+		Opname:   "OpGroupFMin",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindGroupOperation,
+				Name:       "'Operation'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'X'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGroupUMin = &Opcode {
+		Opname:   "OpGroupUMin",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindGroupOperation,
+				Name:       "'Operation'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'X'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGroupSMin = &Opcode {
+		Opname:   "OpGroupSMin",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindGroupOperation,
+				Name:       "'Operation'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'X'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGroupFMax = &Opcode {
+		Opname:   "OpGroupFMax",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindGroupOperation,
+				Name:       "'Operation'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'X'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGroupUMax = &Opcode {
+		Opname:   "OpGroupUMax",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindGroupOperation,
+				Name:       "'Operation'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'X'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGroupSMax = &Opcode {
+		Opname:   "OpGroupSMax",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindGroupOperation,
+				Name:       "'Operation'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'X'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpReadPipe = &Opcode {
+		Opname:   "OpReadPipe",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pipe'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pointer'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Packet Size'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Packet Alignment'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpWritePipe = &Opcode {
+		Opname:   "OpWritePipe",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pipe'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pointer'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Packet Size'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Packet Alignment'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpReservedReadPipe = &Opcode {
+		Opname:   "OpReservedReadPipe",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pipe'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Reserve Id'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Index'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pointer'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Packet Size'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Packet Alignment'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpReservedWritePipe = &Opcode {
+		Opname:   "OpReservedWritePipe",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pipe'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Reserve Id'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Index'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pointer'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Packet Size'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Packet Alignment'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpReserveReadPipePackets = &Opcode {
+		Opname:   "OpReserveReadPipePackets",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pipe'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Num Packets'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Packet Size'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Packet Alignment'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpReserveWritePipePackets = &Opcode {
+		Opname:   "OpReserveWritePipePackets",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pipe'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Num Packets'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Packet Size'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Packet Alignment'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpCommitReadPipe = &Opcode {
+		Opname:   "OpCommitReadPipe",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pipe'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Reserve Id'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Packet Size'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Packet Alignment'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpCommitWritePipe = &Opcode {
+		Opname:   "OpCommitWritePipe",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pipe'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Reserve Id'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Packet Size'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Packet Alignment'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpIsValidReserveId = &Opcode {
+		Opname:   "OpIsValidReserveId",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Reserve Id'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGetNumPipePackets = &Opcode {
+		Opname:   "OpGetNumPipePackets",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pipe'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Packet Size'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Packet Alignment'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGetMaxPipePackets = &Opcode {
+		Opname:   "OpGetMaxPipePackets",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pipe'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Packet Size'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Packet Alignment'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGroupReserveReadPipePackets = &Opcode {
+		Opname:   "OpGroupReserveReadPipePackets",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pipe'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Num Packets'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Packet Size'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Packet Alignment'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGroupReserveWritePipePackets = &Opcode {
+		Opname:   "OpGroupReserveWritePipePackets",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pipe'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Num Packets'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Packet Size'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Packet Alignment'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGroupCommitReadPipe = &Opcode {
+		Opname:   "OpGroupCommitReadPipe",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pipe'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Reserve Id'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Packet Size'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Packet Alignment'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGroupCommitWritePipe = &Opcode {
+		Opname:   "OpGroupCommitWritePipe",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pipe'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Reserve Id'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Packet Size'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Packet Alignment'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpEnqueueMarker = &Opcode {
+		Opname:   "OpEnqueueMarker",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Queue'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Num Events'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Wait Events'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Ret Event'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpEnqueueKernel = &Opcode {
+		Opname:   "OpEnqueueKernel",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Queue'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Flags'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'ND Range'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Num Events'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Wait Events'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Ret Event'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Invoke'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Param'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Param Size'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Param Align'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Local Size'",
+				Quantifier: "*",
+			}, 
+		},
+	}
+	OpGetKernelNDrangeSubGroupCount = &Opcode {
+		Opname:   "OpGetKernelNDrangeSubGroupCount",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'ND Range'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Invoke'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Param'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Param Size'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Param Align'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGetKernelNDrangeMaxSubGroupSize = &Opcode {
+		Opname:   "OpGetKernelNDrangeMaxSubGroupSize",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'ND Range'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Invoke'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Param'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Param Size'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Param Align'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGetKernelWorkGroupSize = &Opcode {
+		Opname:   "OpGetKernelWorkGroupSize",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Invoke'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Param'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Param Size'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Param Align'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGetKernelPreferredWorkGroupSizeMultiple = &Opcode {
+		Opname:   "OpGetKernelPreferredWorkGroupSizeMultiple",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Invoke'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Param'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Param Size'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Param Align'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpRetainEvent = &Opcode {
+		Opname:   "OpRetainEvent",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Event'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpReleaseEvent = &Opcode {
+		Opname:   "OpReleaseEvent",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Event'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpCreateUserEvent = &Opcode {
+		Opname:   "OpCreateUserEvent",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpIsValidEvent = &Opcode {
+		Opname:   "OpIsValidEvent",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Event'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSetUserEventStatus = &Opcode {
+		Opname:   "OpSetUserEventStatus",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Event'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Status'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpCaptureEventProfilingInfo = &Opcode {
+		Opname:   "OpCaptureEventProfilingInfo",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Event'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Profiling Info'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGetDefaultQueue = &Opcode {
+		Opname:   "OpGetDefaultQueue",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpBuildNDRange = &Opcode {
+		Opname:   "OpBuildNDRange",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'GlobalWorkSize'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'LocalWorkSize'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'GlobalWorkOffset'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpImageSparseSampleImplicitLod = &Opcode {
+		Opname:   "OpImageSparseSampleImplicitLod",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Sampled Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Coordinate'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindImageOperands,
+				Name:       "",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpImageSparseSampleExplicitLod = &Opcode {
+		Opname:   "OpImageSparseSampleExplicitLod",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Sampled Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Coordinate'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindImageOperands,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpImageSparseSampleDrefImplicitLod = &Opcode {
+		Opname:   "OpImageSparseSampleDrefImplicitLod",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Sampled Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Coordinate'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'D~ref~'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindImageOperands,
+				Name:       "",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpImageSparseSampleDrefExplicitLod = &Opcode {
+		Opname:   "OpImageSparseSampleDrefExplicitLod",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Sampled Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Coordinate'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'D~ref~'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindImageOperands,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpImageSparseSampleProjImplicitLod = &Opcode {
+		Opname:   "OpImageSparseSampleProjImplicitLod",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Sampled Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Coordinate'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindImageOperands,
+				Name:       "",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpImageSparseSampleProjExplicitLod = &Opcode {
+		Opname:   "OpImageSparseSampleProjExplicitLod",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Sampled Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Coordinate'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindImageOperands,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpImageSparseSampleProjDrefImplicitLod = &Opcode {
+		Opname:   "OpImageSparseSampleProjDrefImplicitLod",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Sampled Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Coordinate'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'D~ref~'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindImageOperands,
+				Name:       "",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpImageSparseSampleProjDrefExplicitLod = &Opcode {
+		Opname:   "OpImageSparseSampleProjDrefExplicitLod",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Sampled Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Coordinate'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'D~ref~'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindImageOperands,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpImageSparseFetch = &Opcode {
+		Opname:   "OpImageSparseFetch",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Coordinate'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindImageOperands,
+				Name:       "",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpImageSparseGather = &Opcode {
+		Opname:   "OpImageSparseGather",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Sampled Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Coordinate'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Component'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindImageOperands,
+				Name:       "",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpImageSparseDrefGather = &Opcode {
+		Opname:   "OpImageSparseDrefGather",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Sampled Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Coordinate'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'D~ref~'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindImageOperands,
+				Name:       "",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpImageSparseTexelsResident = &Opcode {
+		Opname:   "OpImageSparseTexelsResident",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Resident Code'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpNoLine = &Opcode {
+		Opname:   "OpNoLine",
+		Operands: []Operand {
+		},
+	}
+	OpAtomicFlagTestAndSet = &Opcode {
+		Opname:   "OpAtomicFlagTestAndSet",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pointer'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Memory'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdMemorySemantics,
+				Name:       "'Semantics'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpAtomicFlagClear = &Opcode {
+		Opname:   "OpAtomicFlagClear",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pointer'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Memory'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdMemorySemantics,
+				Name:       "'Semantics'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpImageSparseRead = &Opcode {
+		Opname:   "OpImageSparseRead",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Coordinate'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindImageOperands,
+				Name:       "",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpSizeOf = &Opcode {
+		Opname:   "OpSizeOf",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pointer'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpTypePipeStorage = &Opcode {
+		Opname:   "OpTypePipeStorage",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpConstantPipeStorage = &Opcode {
+		Opname:   "OpConstantPipeStorage",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Packet Size'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Packet Alignment'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Capacity'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpCreatePipeFromPipeStorage = &Opcode {
+		Opname:   "OpCreatePipeFromPipeStorage",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pipe Storage'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGetKernelLocalSizeForSubgroupCount = &Opcode {
+		Opname:   "OpGetKernelLocalSizeForSubgroupCount",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Subgroup Count'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Invoke'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Param'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Param Size'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Param Align'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGetKernelMaxNumSubgroups = &Opcode {
+		Opname:   "OpGetKernelMaxNumSubgroups",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Invoke'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Param'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Param Size'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Param Align'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpTypeNamedBarrier = &Opcode {
+		Opname:   "OpTypeNamedBarrier",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpNamedBarrierInitialize = &Opcode {
+		Opname:   "OpNamedBarrierInitialize",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Subgroup Count'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpMemoryNamedBarrier = &Opcode {
+		Opname:   "OpMemoryNamedBarrier",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Named Barrier'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Memory'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdMemorySemantics,
+				Name:       "'Semantics'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpModuleProcessed = &Opcode {
+		Opname:   "OpModuleProcessed",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindLiteralString,
+				Name:       "'Process'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpExecutionModeId = &Opcode {
+		Opname:   "OpExecutionModeId",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Entry Point'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindExecutionMode,
+				Name:       "'Mode'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpDecorateId = &Opcode {
+		Opname:   "OpDecorateId",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Target'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindDecoration,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGroupNonUniformElect = &Opcode {
+		Opname:   "OpGroupNonUniformElect",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGroupNonUniformAll = &Opcode {
+		Opname:   "OpGroupNonUniformAll",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Predicate'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGroupNonUniformAny = &Opcode {
+		Opname:   "OpGroupNonUniformAny",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Predicate'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGroupNonUniformAllEqual = &Opcode {
+		Opname:   "OpGroupNonUniformAllEqual",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGroupNonUniformBroadcast = &Opcode {
+		Opname:   "OpGroupNonUniformBroadcast",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Id'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGroupNonUniformBroadcastFirst = &Opcode {
+		Opname:   "OpGroupNonUniformBroadcastFirst",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGroupNonUniformBallot = &Opcode {
+		Opname:   "OpGroupNonUniformBallot",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Predicate'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGroupNonUniformInverseBallot = &Opcode {
+		Opname:   "OpGroupNonUniformInverseBallot",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGroupNonUniformBallotBitExtract = &Opcode {
+		Opname:   "OpGroupNonUniformBallotBitExtract",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Index'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGroupNonUniformBallotBitCount = &Opcode {
+		Opname:   "OpGroupNonUniformBallotBitCount",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindGroupOperation,
+				Name:       "'Operation'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGroupNonUniformBallotFindLSB = &Opcode {
+		Opname:   "OpGroupNonUniformBallotFindLSB",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGroupNonUniformBallotFindMSB = &Opcode {
+		Opname:   "OpGroupNonUniformBallotFindMSB",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGroupNonUniformShuffle = &Opcode {
+		Opname:   "OpGroupNonUniformShuffle",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Id'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGroupNonUniformShuffleXor = &Opcode {
+		Opname:   "OpGroupNonUniformShuffleXor",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Mask'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGroupNonUniformShuffleUp = &Opcode {
+		Opname:   "OpGroupNonUniformShuffleUp",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Delta'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGroupNonUniformShuffleDown = &Opcode {
+		Opname:   "OpGroupNonUniformShuffleDown",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Delta'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGroupNonUniformIAdd = &Opcode {
+		Opname:   "OpGroupNonUniformIAdd",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindGroupOperation,
+				Name:       "'Operation'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'ClusterSize'",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpGroupNonUniformFAdd = &Opcode {
+		Opname:   "OpGroupNonUniformFAdd",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindGroupOperation,
+				Name:       "'Operation'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'ClusterSize'",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpGroupNonUniformIMul = &Opcode {
+		Opname:   "OpGroupNonUniformIMul",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindGroupOperation,
+				Name:       "'Operation'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'ClusterSize'",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpGroupNonUniformFMul = &Opcode {
+		Opname:   "OpGroupNonUniformFMul",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindGroupOperation,
+				Name:       "'Operation'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'ClusterSize'",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpGroupNonUniformSMin = &Opcode {
+		Opname:   "OpGroupNonUniformSMin",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindGroupOperation,
+				Name:       "'Operation'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'ClusterSize'",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpGroupNonUniformUMin = &Opcode {
+		Opname:   "OpGroupNonUniformUMin",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindGroupOperation,
+				Name:       "'Operation'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'ClusterSize'",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpGroupNonUniformFMin = &Opcode {
+		Opname:   "OpGroupNonUniformFMin",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindGroupOperation,
+				Name:       "'Operation'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'ClusterSize'",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpGroupNonUniformSMax = &Opcode {
+		Opname:   "OpGroupNonUniformSMax",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindGroupOperation,
+				Name:       "'Operation'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'ClusterSize'",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpGroupNonUniformUMax = &Opcode {
+		Opname:   "OpGroupNonUniformUMax",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindGroupOperation,
+				Name:       "'Operation'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'ClusterSize'",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpGroupNonUniformFMax = &Opcode {
+		Opname:   "OpGroupNonUniformFMax",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindGroupOperation,
+				Name:       "'Operation'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'ClusterSize'",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpGroupNonUniformBitwiseAnd = &Opcode {
+		Opname:   "OpGroupNonUniformBitwiseAnd",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindGroupOperation,
+				Name:       "'Operation'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'ClusterSize'",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpGroupNonUniformBitwiseOr = &Opcode {
+		Opname:   "OpGroupNonUniformBitwiseOr",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindGroupOperation,
+				Name:       "'Operation'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'ClusterSize'",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpGroupNonUniformBitwiseXor = &Opcode {
+		Opname:   "OpGroupNonUniformBitwiseXor",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindGroupOperation,
+				Name:       "'Operation'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'ClusterSize'",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpGroupNonUniformLogicalAnd = &Opcode {
+		Opname:   "OpGroupNonUniformLogicalAnd",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindGroupOperation,
+				Name:       "'Operation'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'ClusterSize'",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpGroupNonUniformLogicalOr = &Opcode {
+		Opname:   "OpGroupNonUniformLogicalOr",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindGroupOperation,
+				Name:       "'Operation'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'ClusterSize'",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpGroupNonUniformLogicalXor = &Opcode {
+		Opname:   "OpGroupNonUniformLogicalXor",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindGroupOperation,
+				Name:       "'Operation'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'ClusterSize'",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpGroupNonUniformQuadBroadcast = &Opcode {
+		Opname:   "OpGroupNonUniformQuadBroadcast",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Index'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGroupNonUniformQuadSwap = &Opcode {
+		Opname:   "OpGroupNonUniformQuadSwap",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Direction'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpCopyLogical = &Opcode {
+		Opname:   "OpCopyLogical",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpPtrEqual = &Opcode {
+		Opname:   "OpPtrEqual",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpPtrNotEqual = &Opcode {
+		Opname:   "OpPtrNotEqual",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpPtrDiff = &Opcode {
+		Opname:   "OpPtrDiff",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupBallotKHR = &Opcode {
+		Opname:   "OpSubgroupBallotKHR",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Predicate'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupFirstInvocationKHR = &Opcode {
+		Opname:   "OpSubgroupFirstInvocationKHR",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAllKHR = &Opcode {
+		Opname:   "OpSubgroupAllKHR",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Predicate'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAnyKHR = &Opcode {
+		Opname:   "OpSubgroupAnyKHR",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Predicate'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAllEqualKHR = &Opcode {
+		Opname:   "OpSubgroupAllEqualKHR",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Predicate'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupReadInvocationKHR = &Opcode {
+		Opname:   "OpSubgroupReadInvocationKHR",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Index'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGroupIAddNonUniformAMD = &Opcode {
+		Opname:   "OpGroupIAddNonUniformAMD",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindGroupOperation,
+				Name:       "'Operation'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'X'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGroupFAddNonUniformAMD = &Opcode {
+		Opname:   "OpGroupFAddNonUniformAMD",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindGroupOperation,
+				Name:       "'Operation'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'X'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGroupFMinNonUniformAMD = &Opcode {
+		Opname:   "OpGroupFMinNonUniformAMD",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindGroupOperation,
+				Name:       "'Operation'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'X'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGroupUMinNonUniformAMD = &Opcode {
+		Opname:   "OpGroupUMinNonUniformAMD",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindGroupOperation,
+				Name:       "'Operation'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'X'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGroupSMinNonUniformAMD = &Opcode {
+		Opname:   "OpGroupSMinNonUniformAMD",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindGroupOperation,
+				Name:       "'Operation'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'X'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGroupFMaxNonUniformAMD = &Opcode {
+		Opname:   "OpGroupFMaxNonUniformAMD",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindGroupOperation,
+				Name:       "'Operation'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'X'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGroupUMaxNonUniformAMD = &Opcode {
+		Opname:   "OpGroupUMaxNonUniformAMD",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindGroupOperation,
+				Name:       "'Operation'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'X'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGroupSMaxNonUniformAMD = &Opcode {
+		Opname:   "OpGroupSMaxNonUniformAMD",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindGroupOperation,
+				Name:       "'Operation'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'X'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpFragmentMaskFetchAMD = &Opcode {
+		Opname:   "OpFragmentMaskFetchAMD",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Coordinate'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpFragmentFetchAMD = &Opcode {
+		Opname:   "OpFragmentFetchAMD",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Coordinate'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Fragment Index'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpReadClockKHR = &Opcode {
+		Opname:   "OpReadClockKHR",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpImageSampleFootprintNV = &Opcode {
+		Opname:   "OpImageSampleFootprintNV",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Sampled Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Coordinate'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Granularity'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Coarse'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindImageOperands,
+				Name:       "",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpGroupNonUniformPartitionNV = &Opcode {
+		Opname:   "OpGroupNonUniformPartitionNV",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpWritePackedPrimitiveIndices4x8NV = &Opcode {
+		Opname:   "OpWritePackedPrimitiveIndices4x8NV",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Index Offset'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Packed Indices'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpReportIntersectionNV = &Opcode {
+		Opname:   "OpReportIntersectionNV",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Hit'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'HitKind'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpIgnoreIntersectionNV = &Opcode {
+		Opname:   "OpIgnoreIntersectionNV",
+		Operands: []Operand {
+		},
+	}
+	OpTerminateRayNV = &Opcode {
+		Opname:   "OpTerminateRayNV",
+		Operands: []Operand {
+		},
+	}
+	OpTraceNV = &Opcode {
+		Opname:   "OpTraceNV",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Accel'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Ray Flags'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Cull Mask'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'SBT Offset'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'SBT Stride'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Miss Index'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Ray Origin'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Ray Tmin'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Ray Direction'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Ray Tmax'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'PayloadId'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpTypeAccelerationStructureNV = &Opcode {
+		Opname:   "OpTypeAccelerationStructureNV",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpExecuteCallableNV = &Opcode {
+		Opname:   "OpExecuteCallableNV",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'SBT Index'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Callable DataId'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpTypeCooperativeMatrixNV = &Opcode {
+		Opname:   "OpTypeCooperativeMatrixNV",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Component Type'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Rows'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Columns'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpCooperativeMatrixLoadNV = &Opcode {
+		Opname:   "OpCooperativeMatrixLoadNV",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pointer'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Stride'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Column Major'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindMemoryAccess,
+				Name:       "",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpCooperativeMatrixStoreNV = &Opcode {
+		Opname:   "OpCooperativeMatrixStoreNV",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pointer'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Object'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Stride'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Column Major'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindMemoryAccess,
+				Name:       "",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpCooperativeMatrixMulAddNV = &Opcode {
+		Opname:   "OpCooperativeMatrixMulAddNV",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'A'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'B'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'C'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpCooperativeMatrixLengthNV = &Opcode {
+		Opname:   "OpCooperativeMatrixLengthNV",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Type'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpBeginInvocationInterlockEXT = &Opcode {
+		Opname:   "OpBeginInvocationInterlockEXT",
+		Operands: []Operand {
+		},
+	}
+	OpEndInvocationInterlockEXT = &Opcode {
+		Opname:   "OpEndInvocationInterlockEXT",
+		Operands: []Operand {
+		},
+	}
+	OpDemoteToHelperInvocationEXT = &Opcode {
+		Opname:   "OpDemoteToHelperInvocationEXT",
+		Operands: []Operand {
+		},
+	}
+	OpIsHelperInvocationEXT = &Opcode {
+		Opname:   "OpIsHelperInvocationEXT",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupShuffleINTEL = &Opcode {
+		Opname:   "OpSubgroupShuffleINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Data'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'InvocationId'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupShuffleDownINTEL = &Opcode {
+		Opname:   "OpSubgroupShuffleDownINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Current'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Next'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Delta'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupShuffleUpINTEL = &Opcode {
+		Opname:   "OpSubgroupShuffleUpINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Previous'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Current'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Delta'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupShuffleXorINTEL = &Opcode {
+		Opname:   "OpSubgroupShuffleXorINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Data'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupBlockReadINTEL = &Opcode {
+		Opname:   "OpSubgroupBlockReadINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Ptr'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupBlockWriteINTEL = &Opcode {
+		Opname:   "OpSubgroupBlockWriteINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Ptr'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Data'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupImageBlockReadINTEL = &Opcode {
+		Opname:   "OpSubgroupImageBlockReadINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Coordinate'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupImageBlockWriteINTEL = &Opcode {
+		Opname:   "OpSubgroupImageBlockWriteINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Coordinate'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Data'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupImageMediaBlockReadINTEL = &Opcode {
+		Opname:   "OpSubgroupImageMediaBlockReadINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Coordinate'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Width'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Height'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupImageMediaBlockWriteINTEL = &Opcode {
+		Opname:   "OpSubgroupImageMediaBlockWriteINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Coordinate'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Width'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Height'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Data'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpUCountLeadingZerosINTEL = &Opcode {
+		Opname:   "OpUCountLeadingZerosINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpUCountTrailingZerosINTEL = &Opcode {
+		Opname:   "OpUCountTrailingZerosINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpAbsISubINTEL = &Opcode {
+		Opname:   "OpAbsISubINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpAbsUSubINTEL = &Opcode {
+		Opname:   "OpAbsUSubINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpIAddSatINTEL = &Opcode {
+		Opname:   "OpIAddSatINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpUAddSatINTEL = &Opcode {
+		Opname:   "OpUAddSatINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpIAverageINTEL = &Opcode {
+		Opname:   "OpIAverageINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpUAverageINTEL = &Opcode {
+		Opname:   "OpUAverageINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpIAverageRoundedINTEL = &Opcode {
+		Opname:   "OpIAverageRoundedINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpUAverageRoundedINTEL = &Opcode {
+		Opname:   "OpUAverageRoundedINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpISubSatINTEL = &Opcode {
+		Opname:   "OpISubSatINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpUSubSatINTEL = &Opcode {
+		Opname:   "OpUSubSatINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpIMul32x16INTEL = &Opcode {
+		Opname:   "OpIMul32x16INTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpUMul32x16INTEL = &Opcode {
+		Opname:   "OpUMul32x16INTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpDecorateString = &Opcode {
+		Opname:   "OpDecorateString",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Target'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindDecoration,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpDecorateStringGOOGLE = &Opcode {
+		Opname:   "OpDecorateStringGOOGLE",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Target'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindDecoration,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpMemberDecorateString = &Opcode {
+		Opname:   "OpMemberDecorateString",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Struct Type'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Member'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindDecoration,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpMemberDecorateStringGOOGLE = &Opcode {
+		Opname:   "OpMemberDecorateStringGOOGLE",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Struct Type'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Member'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindDecoration,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpVmeImageINTEL = &Opcode {
+		Opname:   "OpVmeImageINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Image Type'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Sampler'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpTypeVmeImageINTEL = &Opcode {
+		Opname:   "OpTypeVmeImageINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Image Type'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpTypeAvcImePayloadINTEL = &Opcode {
+		Opname:   "OpTypeAvcImePayloadINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpTypeAvcRefPayloadINTEL = &Opcode {
+		Opname:   "OpTypeAvcRefPayloadINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpTypeAvcSicPayloadINTEL = &Opcode {
+		Opname:   "OpTypeAvcSicPayloadINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpTypeAvcMcePayloadINTEL = &Opcode {
+		Opname:   "OpTypeAvcMcePayloadINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpTypeAvcMceResultINTEL = &Opcode {
+		Opname:   "OpTypeAvcMceResultINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpTypeAvcImeResultINTEL = &Opcode {
+		Opname:   "OpTypeAvcImeResultINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpTypeAvcImeResultSingleReferenceStreamoutINTEL = &Opcode {
+		Opname:   "OpTypeAvcImeResultSingleReferenceStreamoutINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpTypeAvcImeResultDualReferenceStreamoutINTEL = &Opcode {
+		Opname:   "OpTypeAvcImeResultDualReferenceStreamoutINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpTypeAvcImeSingleReferenceStreaminINTEL = &Opcode {
+		Opname:   "OpTypeAvcImeSingleReferenceStreaminINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpTypeAvcImeDualReferenceStreaminINTEL = &Opcode {
+		Opname:   "OpTypeAvcImeDualReferenceStreaminINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpTypeAvcRefResultINTEL = &Opcode {
+		Opname:   "OpTypeAvcRefResultINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpTypeAvcSicResultINTEL = &Opcode {
+		Opname:   "OpTypeAvcSicResultINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Slice Type'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Qp'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Reference Base Penalty'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Slice Type'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Qp'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcMceSetInterShapePenaltyINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcMceSetInterShapePenaltyINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Packed Shape Penalty'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Slice Type'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Qp'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcMceSetInterDirectionPenaltyINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcMceSetInterDirectionPenaltyINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Direction Cost'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Slice Type'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Qp'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Slice Type'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Qp'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Packed Cost Center Delta'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Packed Cost Table'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Cost Precision'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Slice Type'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Qp'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcMceSetAcOnlyHaarINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcMceSetAcOnlyHaarINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Source Field Polarity'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Reference Field Polarity'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Forward Reference Field Polarity'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Backward Reference Field Polarity'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcMceConvertToImePayloadINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcMceConvertToImePayloadINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcMceConvertToImeResultINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcMceConvertToImeResultINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcMceConvertToRefPayloadINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcMceConvertToRefPayloadINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcMceConvertToRefResultINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcMceConvertToRefResultINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcMceConvertToSicPayloadINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcMceConvertToSicPayloadINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcMceConvertToSicResultINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcMceConvertToSicResultINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcMceGetMotionVectorsINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcMceGetMotionVectorsINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcMceGetInterDistortionsINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcMceGetInterDistortionsINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcMceGetBestInterDistortionsINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcMceGetBestInterDistortionsINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcMceGetInterMajorShapeINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcMceGetInterMajorShapeINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcMceGetInterMinorShapeINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcMceGetInterMinorShapeINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcMceGetInterDirectionsINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcMceGetInterDirectionsINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcMceGetInterMotionVectorCountINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcMceGetInterMotionVectorCountINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcMceGetInterReferenceIdsINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcMceGetInterReferenceIdsINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Packed Reference Ids'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Packed Reference Parameter Field Polarities'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcImeInitializeINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcImeInitializeINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Src Coord'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Partition Mask'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'SAD Adjustment'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcImeSetSingleReferenceINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcImeSetSingleReferenceINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Ref Offset'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Search Window Config'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcImeSetDualReferenceINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcImeSetDualReferenceINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Fwd Ref Offset'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Bwd Ref Offset'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'id> Search Window Config'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcImeRefWindowSizeINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcImeRefWindowSizeINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Search Window Config'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Dual Ref'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcImeAdjustRefOffsetINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcImeAdjustRefOffsetINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Ref Offset'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Src Coord'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Ref Window Size'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Image Size'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcImeConvertToMcePayloadINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcImeConvertToMcePayloadINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcImeSetMaxMotionVectorCountINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcImeSetMaxMotionVectorCountINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Max Motion Vector Count'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Threshold'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcImeSetWeightedSadINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcImeSetWeightedSadINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Packed Sad Weights'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Src Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Ref Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcImeEvaluateWithDualReferenceINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcImeEvaluateWithDualReferenceINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Src Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Fwd Ref Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Bwd Ref Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Src Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Ref Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Streamin Components'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Src Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Fwd Ref Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Bwd Ref Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Streamin Components'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Src Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Ref Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Src Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Fwd Ref Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Bwd Ref Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Src Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Ref Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Streamin Components'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Src Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Fwd Ref Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Bwd Ref Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Streamin Components'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcImeConvertToMceResultINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcImeConvertToMceResultINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcImeGetSingleReferenceStreaminINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcImeGetSingleReferenceStreaminINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcImeGetDualReferenceStreaminINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcImeGetDualReferenceStreaminINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcImeStripDualReferenceStreamoutINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcImeStripDualReferenceStreamoutINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Major Shape'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Major Shape'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Major Shape'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Major Shape'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Direction'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Major Shape'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Direction'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Major Shape'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Direction'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcImeGetBorderReachedINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcImeGetBorderReachedINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Image Select'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcFmeInitializeINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcFmeInitializeINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Src Coord'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Motion Vectors'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Major Shapes'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Minor Shapes'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Direction'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pixel Resolution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Sad Adjustment'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcBmeInitializeINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcBmeInitializeINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Src Coord'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Motion Vectors'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Major Shapes'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Minor Shapes'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Direction'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pixel Resolution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Bidirectional Weight'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Sad Adjustment'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcRefConvertToMcePayloadINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcRefConvertToMcePayloadINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcRefSetBidirectionalMixDisableINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcRefSetBidirectionalMixDisableINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcRefSetBilinearFilterEnableINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcRefSetBilinearFilterEnableINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Src Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Ref Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcRefEvaluateWithDualReferenceINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcRefEvaluateWithDualReferenceINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Src Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Fwd Ref Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Bwd Ref Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Src Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Packed Reference Ids'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Src Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Packed Reference Ids'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Packed Reference Field Polarities'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcRefConvertToMceResultINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcRefConvertToMceResultINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcSicInitializeINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcSicInitializeINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Src Coord'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcSicConfigureSkcINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcSicConfigureSkcINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Skip Block Partition Type'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Skip Motion Vector Mask'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Motion Vectors'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Bidirectional Weight'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Sad Adjustment'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcSicConfigureIpeLumaINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcSicConfigureIpeLumaINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Luma Intra Partition Mask'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Intra Neighbour Availabilty'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Left Edge Luma Pixels'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Upper Left Corner Luma Pixel'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Upper Edge Luma Pixels'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Upper Right Edge Luma Pixels'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Sad Adjustment'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcSicConfigureIpeLumaChromaINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcSicConfigureIpeLumaChromaINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Luma Intra Partition Mask'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Intra Neighbour Availabilty'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Left Edge Luma Pixels'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Upper Left Corner Luma Pixel'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Upper Edge Luma Pixels'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Upper Right Edge Luma Pixels'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Left Edge Chroma Pixels'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Upper Left Corner Chroma Pixel'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Upper Edge Chroma Pixels'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Sad Adjustment'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcSicGetMotionVectorMaskINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcSicGetMotionVectorMaskINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Skip Block Partition Type'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Direction'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcSicConvertToMcePayloadINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcSicConvertToMcePayloadINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Packed Shape Penalty'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Luma Mode Penalty'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Luma Packed Neighbor Modes'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Luma Packed Non Dc Penalty'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Chroma Mode Base Penalty'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcSicSetBilinearFilterEnableINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcSicSetBilinearFilterEnableINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Packed Sad Coefficients'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Block Based Skip Type'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcSicEvaluateIpeINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcSicEvaluateIpeINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Src Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Src Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Ref Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcSicEvaluateWithDualReferenceINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcSicEvaluateWithDualReferenceINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Src Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Fwd Ref Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Bwd Ref Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Src Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Packed Reference Ids'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Src Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Packed Reference Ids'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Packed Reference Field Polarities'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcSicConvertToMceResultINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcSicConvertToMceResultINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcSicGetIpeLumaShapeINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcSicGetIpeLumaShapeINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcSicGetPackedIpeLumaModesINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcSicGetPackedIpeLumaModesINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcSicGetIpeChromaModeINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcSicGetIpeChromaModeINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupAvcSicGetInterRawSadsINTEL = &Opcode {
+		Opname:   "OpSubgroupAvcSicGetInterRawSadsINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+
+
+	OperandKindImageOperands = &OperandKind {
+		Kind:       "ImageOperands",
+		Category:   "BitEnum",
+		Enumerants: []Enumerant {
+			Enumerant{
+				Enumerant:    "None",
+				Value:        0x0000,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Bias",
+				Value:        0x0001,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{{OperandKindIdRef, ""},},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Lod",
+				Value:        0x0002,
+				Capabilities: []string{},
+				Parameters:   []Parameter{{OperandKindIdRef, ""},},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Grad",
+				Value:        0x0004,
+				Capabilities: []string{},
+				Parameters:   []Parameter{{OperandKindIdRef, ""},{OperandKindIdRef, ""},},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "ConstOffset",
+				Value:        0x0008,
+				Capabilities: []string{},
+				Parameters:   []Parameter{{OperandKindIdRef, ""},},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Offset",
+				Value:        0x0010,
+				Capabilities: []string{"ImageGatherExtended",},
+				Parameters:   []Parameter{{OperandKindIdRef, ""},},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "ConstOffsets",
+				Value:        0x0020,
+				Capabilities: []string{"ImageGatherExtended",},
+				Parameters:   []Parameter{{OperandKindIdRef, ""},},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Sample",
+				Value:        0x0040,
+				Capabilities: []string{},
+				Parameters:   []Parameter{{OperandKindIdRef, ""},},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "MinLod",
+				Value:        0x0080,
+				Capabilities: []string{"MinLod",},
+				Parameters:   []Parameter{{OperandKindIdRef, ""},},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "MakeTexelAvailable",
+				Value:        0x0100,
+				Capabilities: []string{"VulkanMemoryModel",},
+				Parameters:   []Parameter{{OperandKindIdScope, ""},},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "MakeTexelAvailableKHR",
+				Value:        0x0100,
+				Capabilities: []string{"VulkanMemoryModel",},
+				Parameters:   []Parameter{{OperandKindIdScope, ""},},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "MakeTexelVisible",
+				Value:        0x0200,
+				Capabilities: []string{"VulkanMemoryModel",},
+				Parameters:   []Parameter{{OperandKindIdScope, ""},},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "MakeTexelVisibleKHR",
+				Value:        0x0200,
+				Capabilities: []string{"VulkanMemoryModel",},
+				Parameters:   []Parameter{{OperandKindIdScope, ""},},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "NonPrivateTexel",
+				Value:        0x0400,
+				Capabilities: []string{"VulkanMemoryModel",},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "NonPrivateTexelKHR",
+				Value:        0x0400,
+				Capabilities: []string{"VulkanMemoryModel",},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "VolatileTexel",
+				Value:        0x0800,
+				Capabilities: []string{"VulkanMemoryModel",},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "VolatileTexelKHR",
+				Value:        0x0800,
+				Capabilities: []string{"VulkanMemoryModel",},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "SignExtend",
+				Value:        0x1000,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "1.4",
+			},
+			Enumerant{
+				Enumerant:    "ZeroExtend",
+				Value:        0x2000,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "1.4",
+			},
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindFPFastMathMode = &OperandKind {
+		Kind:       "FPFastMathMode",
+		Category:   "BitEnum",
+		Enumerants: []Enumerant {
+			Enumerant{
+				Enumerant:    "None",
+				Value:        0x0000,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "NotNaN",
+				Value:        0x0001,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "NotInf",
+				Value:        0x0002,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "NSZ",
+				Value:        0x0004,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "AllowRecip",
+				Value:        0x0008,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Fast",
+				Value:        0x0010,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindSelectionControl = &OperandKind {
+		Kind:       "SelectionControl",
+		Category:   "BitEnum",
+		Enumerants: []Enumerant {
+			Enumerant{
+				Enumerant:    "None",
+				Value:        0x0000,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Flatten",
+				Value:        0x0001,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "DontFlatten",
+				Value:        0x0002,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindLoopControl = &OperandKind {
+		Kind:       "LoopControl",
+		Category:   "BitEnum",
+		Enumerants: []Enumerant {
+			Enumerant{
+				Enumerant:    "None",
+				Value:        0x0000,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Unroll",
+				Value:        0x0001,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "DontUnroll",
+				Value:        0x0002,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "DependencyInfinite",
+				Value:        0x0004,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "1.1",
+			},
+			Enumerant{
+				Enumerant:    "DependencyLength",
+				Value:        0x0008,
+				Capabilities: []string{},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, ""},},
+				Version:      "1.1",
+			},
+			Enumerant{
+				Enumerant:    "MinIterations",
+				Value:        0x0010,
+				Capabilities: []string{},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, ""},},
+				Version:      "1.4",
+			},
+			Enumerant{
+				Enumerant:    "MaxIterations",
+				Value:        0x0020,
+				Capabilities: []string{},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, ""},},
+				Version:      "1.4",
+			},
+			Enumerant{
+				Enumerant:    "IterationMultiple",
+				Value:        0x0040,
+				Capabilities: []string{},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, ""},},
+				Version:      "1.4",
+			},
+			Enumerant{
+				Enumerant:    "PeelCount",
+				Value:        0x0080,
+				Capabilities: []string{},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, ""},},
+				Version:      "1.4",
+			},
+			Enumerant{
+				Enumerant:    "PartialCount",
+				Value:        0x0100,
+				Capabilities: []string{},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, ""},},
+				Version:      "1.4",
+			},
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindFunctionControl = &OperandKind {
+		Kind:       "FunctionControl",
+		Category:   "BitEnum",
+		Enumerants: []Enumerant {
+			Enumerant{
+				Enumerant:    "None",
+				Value:        0x0000,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Inline",
+				Value:        0x0001,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "DontInline",
+				Value:        0x0002,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Pure",
+				Value:        0x0004,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Const",
+				Value:        0x0008,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindMemorySemantics = &OperandKind {
+		Kind:       "MemorySemantics",
+		Category:   "BitEnum",
+		Enumerants: []Enumerant {
+			Enumerant{
+				Enumerant:    "Relaxed",
+				Value:        0x0000,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "None",
+				Value:        0x0000,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Acquire",
+				Value:        0x0002,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Release",
+				Value:        0x0004,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "AcquireRelease",
+				Value:        0x0008,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "SequentiallyConsistent",
+				Value:        0x0010,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "UniformMemory",
+				Value:        0x0040,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "SubgroupMemory",
+				Value:        0x0080,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "WorkgroupMemory",
+				Value:        0x0100,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "CrossWorkgroupMemory",
+				Value:        0x0200,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "AtomicCounterMemory",
+				Value:        0x0400,
+				Capabilities: []string{"AtomicStorage",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "ImageMemory",
+				Value:        0x0800,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "OutputMemory",
+				Value:        0x1000,
+				Capabilities: []string{"VulkanMemoryModel",},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "OutputMemoryKHR",
+				Value:        0x1000,
+				Capabilities: []string{"VulkanMemoryModel",},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "MakeAvailable",
+				Value:        0x2000,
+				Capabilities: []string{"VulkanMemoryModel",},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "MakeAvailableKHR",
+				Value:        0x2000,
+				Capabilities: []string{"VulkanMemoryModel",},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "MakeVisible",
+				Value:        0x4000,
+				Capabilities: []string{"VulkanMemoryModel",},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "MakeVisibleKHR",
+				Value:        0x4000,
+				Capabilities: []string{"VulkanMemoryModel",},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "Volatile",
+				Value:        0x8000,
+				Capabilities: []string{"VulkanMemoryModel",},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindMemoryAccess = &OperandKind {
+		Kind:       "MemoryAccess",
+		Category:   "BitEnum",
+		Enumerants: []Enumerant {
+			Enumerant{
+				Enumerant:    "None",
+				Value:        0x0000,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Volatile",
+				Value:        0x0001,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Aligned",
+				Value:        0x0002,
+				Capabilities: []string{},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, ""},},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Nontemporal",
+				Value:        0x0004,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "MakePointerAvailable",
+				Value:        0x0008,
+				Capabilities: []string{"VulkanMemoryModel",},
+				Parameters:   []Parameter{{OperandKindIdScope, ""},},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "MakePointerAvailableKHR",
+				Value:        0x0008,
+				Capabilities: []string{"VulkanMemoryModel",},
+				Parameters:   []Parameter{{OperandKindIdScope, ""},},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "MakePointerVisible",
+				Value:        0x0010,
+				Capabilities: []string{"VulkanMemoryModel",},
+				Parameters:   []Parameter{{OperandKindIdScope, ""},},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "MakePointerVisibleKHR",
+				Value:        0x0010,
+				Capabilities: []string{"VulkanMemoryModel",},
+				Parameters:   []Parameter{{OperandKindIdScope, ""},},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "NonPrivatePointer",
+				Value:        0x0020,
+				Capabilities: []string{"VulkanMemoryModel",},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "NonPrivatePointerKHR",
+				Value:        0x0020,
+				Capabilities: []string{"VulkanMemoryModel",},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindKernelProfilingInfo = &OperandKind {
+		Kind:       "KernelProfilingInfo",
+		Category:   "BitEnum",
+		Enumerants: []Enumerant {
+			Enumerant{
+				Enumerant:    "None",
+				Value:        0x0000,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "CmdExecTime",
+				Value:        0x0001,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindSourceLanguage = &OperandKind {
+		Kind:       "SourceLanguage",
+		Category:   "ValueEnum",
+		Enumerants: []Enumerant {
+			Enumerant{
+				Enumerant:    "Unknown",
+				Value:        0,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "ESSL",
+				Value:        1,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "GLSL",
+				Value:        2,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "OpenCL_C",
+				Value:        3,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "OpenCL_CPP",
+				Value:        4,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "HLSL",
+				Value:        5,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindExecutionModel = &OperandKind {
+		Kind:       "ExecutionModel",
+		Category:   "ValueEnum",
+		Enumerants: []Enumerant {
+			Enumerant{
+				Enumerant:    "Vertex",
+				Value:        0,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "TessellationControl",
+				Value:        1,
+				Capabilities: []string{"Tessellation",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "TessellationEvaluation",
+				Value:        2,
+				Capabilities: []string{"Tessellation",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Geometry",
+				Value:        3,
+				Capabilities: []string{"Geometry",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Fragment",
+				Value:        4,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "GLCompute",
+				Value:        5,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Kernel",
+				Value:        6,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "TaskNV",
+				Value:        5267,
+				Capabilities: []string{"MeshShadingNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "MeshNV",
+				Value:        5268,
+				Capabilities: []string{"MeshShadingNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "RayGenerationNV",
+				Value:        5313,
+				Capabilities: []string{"RayTracingNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "IntersectionNV",
+				Value:        5314,
+				Capabilities: []string{"RayTracingNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "AnyHitNV",
+				Value:        5315,
+				Capabilities: []string{"RayTracingNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "ClosestHitNV",
+				Value:        5316,
+				Capabilities: []string{"RayTracingNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "MissNV",
+				Value:        5317,
+				Capabilities: []string{"RayTracingNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "CallableNV",
+				Value:        5318,
+				Capabilities: []string{"RayTracingNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindAddressingModel = &OperandKind {
+		Kind:       "AddressingModel",
+		Category:   "ValueEnum",
+		Enumerants: []Enumerant {
+			Enumerant{
+				Enumerant:    "Logical",
+				Value:        0,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Physical32",
+				Value:        1,
+				Capabilities: []string{"Addresses",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Physical64",
+				Value:        2,
+				Capabilities: []string{"Addresses",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "PhysicalStorageBuffer64",
+				Value:        5348,
+				Capabilities: []string{"PhysicalStorageBufferAddresses",},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "PhysicalStorageBuffer64EXT",
+				Value:        5348,
+				Capabilities: []string{"PhysicalStorageBufferAddresses",},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindMemoryModel = &OperandKind {
+		Kind:       "MemoryModel",
+		Category:   "ValueEnum",
+		Enumerants: []Enumerant {
+			Enumerant{
+				Enumerant:    "Simple",
+				Value:        0,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "GLSL450",
+				Value:        1,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "OpenCL",
+				Value:        2,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Vulkan",
+				Value:        3,
+				Capabilities: []string{"VulkanMemoryModel",},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "VulkanKHR",
+				Value:        3,
+				Capabilities: []string{"VulkanMemoryModel",},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindExecutionMode = &OperandKind {
+		Kind:       "ExecutionMode",
+		Category:   "ValueEnum",
+		Enumerants: []Enumerant {
+			Enumerant{
+				Enumerant:    "Invocations",
+				Value:        0,
+				Capabilities: []string{"Geometry",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Number of <<Invocation,invocations>>'"},},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "SpacingEqual",
+				Value:        1,
+				Capabilities: []string{"Tessellation",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "SpacingFractionalEven",
+				Value:        2,
+				Capabilities: []string{"Tessellation",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "SpacingFractionalOdd",
+				Value:        3,
+				Capabilities: []string{"Tessellation",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "VertexOrderCw",
+				Value:        4,
+				Capabilities: []string{"Tessellation",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "VertexOrderCcw",
+				Value:        5,
+				Capabilities: []string{"Tessellation",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "PixelCenterInteger",
+				Value:        6,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "OriginUpperLeft",
+				Value:        7,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "OriginLowerLeft",
+				Value:        8,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "EarlyFragmentTests",
+				Value:        9,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "PointMode",
+				Value:        10,
+				Capabilities: []string{"Tessellation",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Xfb",
+				Value:        11,
+				Capabilities: []string{"TransformFeedback",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "DepthReplacing",
+				Value:        12,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "DepthGreater",
+				Value:        14,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "DepthLess",
+				Value:        15,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "DepthUnchanged",
+				Value:        16,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "LocalSize",
+				Value:        17,
+				Capabilities: []string{},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'x size'"},{OperandKindLiteralInteger, "'y size'"},{OperandKindLiteralInteger, "'z size'"},},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "LocalSizeHint",
+				Value:        18,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'x size'"},{OperandKindLiteralInteger, "'y size'"},{OperandKindLiteralInteger, "'z size'"},},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "InputPoints",
+				Value:        19,
+				Capabilities: []string{"Geometry",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "InputLines",
+				Value:        20,
+				Capabilities: []string{"Geometry",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "InputLinesAdjacency",
+				Value:        21,
+				Capabilities: []string{"Geometry",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Triangles",
+				Value:        22,
+				Capabilities: []string{"Geometry","Tessellation",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "InputTrianglesAdjacency",
+				Value:        23,
+				Capabilities: []string{"Geometry",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Quads",
+				Value:        24,
+				Capabilities: []string{"Tessellation",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Isolines",
+				Value:        25,
+				Capabilities: []string{"Tessellation",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "OutputVertices",
+				Value:        26,
+				Capabilities: []string{"Geometry","Tessellation","MeshShadingNV",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Vertex count'"},},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "OutputPoints",
+				Value:        27,
+				Capabilities: []string{"Geometry","MeshShadingNV",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "OutputLineStrip",
+				Value:        28,
+				Capabilities: []string{"Geometry",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "OutputTriangleStrip",
+				Value:        29,
+				Capabilities: []string{"Geometry",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "VecTypeHint",
+				Value:        30,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Vector type'"},},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "ContractionOff",
+				Value:        31,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Initializer",
+				Value:        33,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "1.1",
+			},
+			Enumerant{
+				Enumerant:    "Finalizer",
+				Value:        34,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "1.1",
+			},
+			Enumerant{
+				Enumerant:    "SubgroupSize",
+				Value:        35,
+				Capabilities: []string{"SubgroupDispatch",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Subgroup Size'"},},
+				Version:      "1.1",
+			},
+			Enumerant{
+				Enumerant:    "SubgroupsPerWorkgroup",
+				Value:        36,
+				Capabilities: []string{"SubgroupDispatch",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Subgroups Per Workgroup'"},},
+				Version:      "1.1",
+			},
+			Enumerant{
+				Enumerant:    "SubgroupsPerWorkgroupId",
+				Value:        37,
+				Capabilities: []string{"SubgroupDispatch",},
+				Parameters:   []Parameter{{OperandKindIdRef, "'Subgroups Per Workgroup'"},},
+				Version:      "1.2",
+			},
+			Enumerant{
+				Enumerant:    "LocalSizeId",
+				Value:        38,
+				Capabilities: []string{},
+				Parameters:   []Parameter{{OperandKindIdRef, "'x size'"},{OperandKindIdRef, "'y size'"},{OperandKindIdRef, "'z size'"},},
+				Version:      "1.2",
+			},
+			Enumerant{
+				Enumerant:    "LocalSizeHintId",
+				Value:        39,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{{OperandKindIdRef, "'Local Size Hint'"},},
+				Version:      "1.2",
+			},
+			Enumerant{
+				Enumerant:    "PostDepthCoverage",
+				Value:        4446,
+				Capabilities: []string{"SampleMaskPostDepthCoverage",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "DenormPreserve",
+				Value:        4459,
+				Capabilities: []string{"DenormPreserve",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Target Width'"},},
+				Version:      "1.4",
+			},
+			Enumerant{
+				Enumerant:    "DenormFlushToZero",
+				Value:        4460,
+				Capabilities: []string{"DenormFlushToZero",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Target Width'"},},
+				Version:      "1.4",
+			},
+			Enumerant{
+				Enumerant:    "SignedZeroInfNanPreserve",
+				Value:        4461,
+				Capabilities: []string{"SignedZeroInfNanPreserve",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Target Width'"},},
+				Version:      "1.4",
+			},
+			Enumerant{
+				Enumerant:    "RoundingModeRTE",
+				Value:        4462,
+				Capabilities: []string{"RoundingModeRTE",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Target Width'"},},
+				Version:      "1.4",
+			},
+			Enumerant{
+				Enumerant:    "RoundingModeRTZ",
+				Value:        4463,
+				Capabilities: []string{"RoundingModeRTZ",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Target Width'"},},
+				Version:      "1.4",
+			},
+			Enumerant{
+				Enumerant:    "StencilRefReplacingEXT",
+				Value:        5027,
+				Capabilities: []string{"StencilExportEXT",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "OutputLinesNV",
+				Value:        5269,
+				Capabilities: []string{"MeshShadingNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "OutputPrimitivesNV",
+				Value:        5270,
+				Capabilities: []string{"MeshShadingNV",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Primitive count'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "DerivativeGroupQuadsNV",
+				Value:        5289,
+				Capabilities: []string{"ComputeDerivativeGroupQuadsNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "DerivativeGroupLinearNV",
+				Value:        5290,
+				Capabilities: []string{"ComputeDerivativeGroupLinearNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "OutputTrianglesNV",
+				Value:        5298,
+				Capabilities: []string{"MeshShadingNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "PixelInterlockOrderedEXT",
+				Value:        5366,
+				Capabilities: []string{"FragmentShaderPixelInterlockEXT",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "PixelInterlockUnorderedEXT",
+				Value:        5367,
+				Capabilities: []string{"FragmentShaderPixelInterlockEXT",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "SampleInterlockOrderedEXT",
+				Value:        5368,
+				Capabilities: []string{"FragmentShaderSampleInterlockEXT",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "SampleInterlockUnorderedEXT",
+				Value:        5369,
+				Capabilities: []string{"FragmentShaderSampleInterlockEXT",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "ShadingRateInterlockOrderedEXT",
+				Value:        5370,
+				Capabilities: []string{"FragmentShaderShadingRateInterlockEXT",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "ShadingRateInterlockUnorderedEXT",
+				Value:        5371,
+				Capabilities: []string{"FragmentShaderShadingRateInterlockEXT",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindStorageClass = &OperandKind {
+		Kind:       "StorageClass",
+		Category:   "ValueEnum",
+		Enumerants: []Enumerant {
+			Enumerant{
+				Enumerant:    "UniformConstant",
+				Value:        0,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Input",
+				Value:        1,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Uniform",
+				Value:        2,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Output",
+				Value:        3,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Workgroup",
+				Value:        4,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "CrossWorkgroup",
+				Value:        5,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Private",
+				Value:        6,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Function",
+				Value:        7,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Generic",
+				Value:        8,
+				Capabilities: []string{"GenericPointer",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "PushConstant",
+				Value:        9,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "AtomicCounter",
+				Value:        10,
+				Capabilities: []string{"AtomicStorage",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Image",
+				Value:        11,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "StorageBuffer",
+				Value:        12,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "1.3",
+			},
+			Enumerant{
+				Enumerant:    "CallableDataNV",
+				Value:        5328,
+				Capabilities: []string{"RayTracingNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "IncomingCallableDataNV",
+				Value:        5329,
+				Capabilities: []string{"RayTracingNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "RayPayloadNV",
+				Value:        5338,
+				Capabilities: []string{"RayTracingNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "HitAttributeNV",
+				Value:        5339,
+				Capabilities: []string{"RayTracingNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "IncomingRayPayloadNV",
+				Value:        5342,
+				Capabilities: []string{"RayTracingNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "ShaderRecordBufferNV",
+				Value:        5343,
+				Capabilities: []string{"RayTracingNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "PhysicalStorageBuffer",
+				Value:        5349,
+				Capabilities: []string{"PhysicalStorageBufferAddresses",},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "PhysicalStorageBufferEXT",
+				Value:        5349,
+				Capabilities: []string{"PhysicalStorageBufferAddresses",},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindDim = &OperandKind {
+		Kind:       "Dim",
+		Category:   "ValueEnum",
+		Enumerants: []Enumerant {
+			Enumerant{
+				Enumerant:    "1D",
+				Value:        0,
+				Capabilities: []string{"Sampled1D","Image1D",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "2D",
+				Value:        1,
+				Capabilities: []string{"Shader","Kernel","ImageMSArray",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "3D",
+				Value:        2,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Cube",
+				Value:        3,
+				Capabilities: []string{"Shader","ImageCubeArray",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Rect",
+				Value:        4,
+				Capabilities: []string{"SampledRect","ImageRect",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Buffer",
+				Value:        5,
+				Capabilities: []string{"SampledBuffer","ImageBuffer",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "SubpassData",
+				Value:        6,
+				Capabilities: []string{"InputAttachment",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindSamplerAddressingMode = &OperandKind {
+		Kind:       "SamplerAddressingMode",
+		Category:   "ValueEnum",
+		Enumerants: []Enumerant {
+			Enumerant{
+				Enumerant:    "None",
+				Value:        0,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "ClampToEdge",
+				Value:        1,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Clamp",
+				Value:        2,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Repeat",
+				Value:        3,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "RepeatMirrored",
+				Value:        4,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindSamplerFilterMode = &OperandKind {
+		Kind:       "SamplerFilterMode",
+		Category:   "ValueEnum",
+		Enumerants: []Enumerant {
+			Enumerant{
+				Enumerant:    "Nearest",
+				Value:        0,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Linear",
+				Value:        1,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindImageFormat = &OperandKind {
+		Kind:       "ImageFormat",
+		Category:   "ValueEnum",
+		Enumerants: []Enumerant {
+			Enumerant{
+				Enumerant:    "Unknown",
+				Value:        0,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Rgba32f",
+				Value:        1,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Rgba16f",
+				Value:        2,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "R32f",
+				Value:        3,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Rgba8",
+				Value:        4,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Rgba8Snorm",
+				Value:        5,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Rg32f",
+				Value:        6,
+				Capabilities: []string{"StorageImageExtendedFormats",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Rg16f",
+				Value:        7,
+				Capabilities: []string{"StorageImageExtendedFormats",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "R11fG11fB10f",
+				Value:        8,
+				Capabilities: []string{"StorageImageExtendedFormats",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "R16f",
+				Value:        9,
+				Capabilities: []string{"StorageImageExtendedFormats",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Rgba16",
+				Value:        10,
+				Capabilities: []string{"StorageImageExtendedFormats",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Rgb10A2",
+				Value:        11,
+				Capabilities: []string{"StorageImageExtendedFormats",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Rg16",
+				Value:        12,
+				Capabilities: []string{"StorageImageExtendedFormats",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Rg8",
+				Value:        13,
+				Capabilities: []string{"StorageImageExtendedFormats",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "R16",
+				Value:        14,
+				Capabilities: []string{"StorageImageExtendedFormats",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "R8",
+				Value:        15,
+				Capabilities: []string{"StorageImageExtendedFormats",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Rgba16Snorm",
+				Value:        16,
+				Capabilities: []string{"StorageImageExtendedFormats",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Rg16Snorm",
+				Value:        17,
+				Capabilities: []string{"StorageImageExtendedFormats",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Rg8Snorm",
+				Value:        18,
+				Capabilities: []string{"StorageImageExtendedFormats",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "R16Snorm",
+				Value:        19,
+				Capabilities: []string{"StorageImageExtendedFormats",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "R8Snorm",
+				Value:        20,
+				Capabilities: []string{"StorageImageExtendedFormats",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Rgba32i",
+				Value:        21,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Rgba16i",
+				Value:        22,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Rgba8i",
+				Value:        23,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "R32i",
+				Value:        24,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Rg32i",
+				Value:        25,
+				Capabilities: []string{"StorageImageExtendedFormats",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Rg16i",
+				Value:        26,
+				Capabilities: []string{"StorageImageExtendedFormats",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Rg8i",
+				Value:        27,
+				Capabilities: []string{"StorageImageExtendedFormats",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "R16i",
+				Value:        28,
+				Capabilities: []string{"StorageImageExtendedFormats",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "R8i",
+				Value:        29,
+				Capabilities: []string{"StorageImageExtendedFormats",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Rgba32ui",
+				Value:        30,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Rgba16ui",
+				Value:        31,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Rgba8ui",
+				Value:        32,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "R32ui",
+				Value:        33,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Rgb10a2ui",
+				Value:        34,
+				Capabilities: []string{"StorageImageExtendedFormats",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Rg32ui",
+				Value:        35,
+				Capabilities: []string{"StorageImageExtendedFormats",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Rg16ui",
+				Value:        36,
+				Capabilities: []string{"StorageImageExtendedFormats",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Rg8ui",
+				Value:        37,
+				Capabilities: []string{"StorageImageExtendedFormats",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "R16ui",
+				Value:        38,
+				Capabilities: []string{"StorageImageExtendedFormats",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "R8ui",
+				Value:        39,
+				Capabilities: []string{"StorageImageExtendedFormats",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindImageChannelOrder = &OperandKind {
+		Kind:       "ImageChannelOrder",
+		Category:   "ValueEnum",
+		Enumerants: []Enumerant {
+			Enumerant{
+				Enumerant:    "R",
+				Value:        0,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "A",
+				Value:        1,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "RG",
+				Value:        2,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "RA",
+				Value:        3,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "RGB",
+				Value:        4,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "RGBA",
+				Value:        5,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "BGRA",
+				Value:        6,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "ARGB",
+				Value:        7,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Intensity",
+				Value:        8,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Luminance",
+				Value:        9,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Rx",
+				Value:        10,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "RGx",
+				Value:        11,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "RGBx",
+				Value:        12,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Depth",
+				Value:        13,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "DepthStencil",
+				Value:        14,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "sRGB",
+				Value:        15,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "sRGBx",
+				Value:        16,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "sRGBA",
+				Value:        17,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "sBGRA",
+				Value:        18,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "ABGR",
+				Value:        19,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindImageChannelDataType = &OperandKind {
+		Kind:       "ImageChannelDataType",
+		Category:   "ValueEnum",
+		Enumerants: []Enumerant {
+			Enumerant{
+				Enumerant:    "SnormInt8",
+				Value:        0,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "SnormInt16",
+				Value:        1,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "UnormInt8",
+				Value:        2,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "UnormInt16",
+				Value:        3,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "UnormShort565",
+				Value:        4,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "UnormShort555",
+				Value:        5,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "UnormInt101010",
+				Value:        6,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "SignedInt8",
+				Value:        7,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "SignedInt16",
+				Value:        8,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "SignedInt32",
+				Value:        9,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "UnsignedInt8",
+				Value:        10,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "UnsignedInt16",
+				Value:        11,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "UnsignedInt32",
+				Value:        12,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "HalfFloat",
+				Value:        13,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Float",
+				Value:        14,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "UnormInt24",
+				Value:        15,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "UnormInt101010_2",
+				Value:        16,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindFPRoundingMode = &OperandKind {
+		Kind:       "FPRoundingMode",
+		Category:   "ValueEnum",
+		Enumerants: []Enumerant {
+			Enumerant{
+				Enumerant:    "RTE",
+				Value:        0,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "RTZ",
+				Value:        1,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "RTP",
+				Value:        2,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "RTN",
+				Value:        3,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindLinkageType = &OperandKind {
+		Kind:       "LinkageType",
+		Category:   "ValueEnum",
+		Enumerants: []Enumerant {
+			Enumerant{
+				Enumerant:    "Export",
+				Value:        0,
+				Capabilities: []string{"Linkage",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Import",
+				Value:        1,
+				Capabilities: []string{"Linkage",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindAccessQualifier = &OperandKind {
+		Kind:       "AccessQualifier",
+		Category:   "ValueEnum",
+		Enumerants: []Enumerant {
+			Enumerant{
+				Enumerant:    "ReadOnly",
+				Value:        0,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "WriteOnly",
+				Value:        1,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "ReadWrite",
+				Value:        2,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindFunctionParameterAttribute = &OperandKind {
+		Kind:       "FunctionParameterAttribute",
+		Category:   "ValueEnum",
+		Enumerants: []Enumerant {
+			Enumerant{
+				Enumerant:    "Zext",
+				Value:        0,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Sext",
+				Value:        1,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "ByVal",
+				Value:        2,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Sret",
+				Value:        3,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "NoAlias",
+				Value:        4,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "NoCapture",
+				Value:        5,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "NoWrite",
+				Value:        6,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "NoReadWrite",
+				Value:        7,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindDecoration = &OperandKind {
+		Kind:       "Decoration",
+		Category:   "ValueEnum",
+		Enumerants: []Enumerant {
+			Enumerant{
+				Enumerant:    "RelaxedPrecision",
+				Value:        0,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "SpecId",
+				Value:        1,
+				Capabilities: []string{"Shader","Kernel",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Specialization Constant ID'"},},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Block",
+				Value:        2,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "BufferBlock",
+				Value:        3,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "RowMajor",
+				Value:        4,
+				Capabilities: []string{"Matrix",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "ColMajor",
+				Value:        5,
+				Capabilities: []string{"Matrix",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "ArrayStride",
+				Value:        6,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Array Stride'"},},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "MatrixStride",
+				Value:        7,
+				Capabilities: []string{"Matrix",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Matrix Stride'"},},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "GLSLShared",
+				Value:        8,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "GLSLPacked",
+				Value:        9,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "CPacked",
+				Value:        10,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "BuiltIn",
+				Value:        11,
+				Capabilities: []string{},
+				Parameters:   []Parameter{{OperandKindBuiltIn, ""},},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "NoPerspective",
+				Value:        13,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Flat",
+				Value:        14,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Patch",
+				Value:        15,
+				Capabilities: []string{"Tessellation",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Centroid",
+				Value:        16,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Sample",
+				Value:        17,
+				Capabilities: []string{"SampleRateShading",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Invariant",
+				Value:        18,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Restrict",
+				Value:        19,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Aliased",
+				Value:        20,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Volatile",
+				Value:        21,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Constant",
+				Value:        22,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Coherent",
+				Value:        23,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "NonWritable",
+				Value:        24,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "NonReadable",
+				Value:        25,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Uniform",
+				Value:        26,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "UniformId",
+				Value:        27,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{{OperandKindIdScope, "'Execution'"},},
+				Version:      "1.4",
+			},
+			Enumerant{
+				Enumerant:    "SaturatedConversion",
+				Value:        28,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Stream",
+				Value:        29,
+				Capabilities: []string{"GeometryStreams",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Stream Number'"},},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Location",
+				Value:        30,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Location'"},},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Component",
+				Value:        31,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Component'"},},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Index",
+				Value:        32,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Index'"},},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Binding",
+				Value:        33,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Binding Point'"},},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "DescriptorSet",
+				Value:        34,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Descriptor Set'"},},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Offset",
+				Value:        35,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Byte Offset'"},},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "XfbBuffer",
+				Value:        36,
+				Capabilities: []string{"TransformFeedback",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'XFB Buffer Number'"},},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "XfbStride",
+				Value:        37,
+				Capabilities: []string{"TransformFeedback",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'XFB Stride'"},},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "FuncParamAttr",
+				Value:        38,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{{OperandKindFunctionParameterAttribute, "'Function Parameter Attribute'"},},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "FPRoundingMode",
+				Value:        39,
+				Capabilities: []string{},
+				Parameters:   []Parameter{{OperandKindFPRoundingMode, "'Floating-Point Rounding Mode'"},},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "FPFastMathMode",
+				Value:        40,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{{OperandKindFPFastMathMode, "'Fast-Math Mode'"},},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "LinkageAttributes",
+				Value:        41,
+				Capabilities: []string{"Linkage",},
+				Parameters:   []Parameter{{OperandKindLiteralString, "'Name'"},{OperandKindLinkageType, "'Linkage Type'"},},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "NoContraction",
+				Value:        42,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "InputAttachmentIndex",
+				Value:        43,
+				Capabilities: []string{"InputAttachment",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Attachment Index'"},},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Alignment",
+				Value:        44,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Alignment'"},},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "MaxByteOffset",
+				Value:        45,
+				Capabilities: []string{"Addresses",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Max Byte Offset'"},},
+				Version:      "1.1",
+			},
+			Enumerant{
+				Enumerant:    "AlignmentId",
+				Value:        46,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{{OperandKindIdRef, "'Alignment'"},},
+				Version:      "1.2",
+			},
+			Enumerant{
+				Enumerant:    "MaxByteOffsetId",
+				Value:        47,
+				Capabilities: []string{"Addresses",},
+				Parameters:   []Parameter{{OperandKindIdRef, "'Max Byte Offset'"},},
+				Version:      "1.2",
+			},
+			Enumerant{
+				Enumerant:    "NoSignedWrap",
+				Value:        4469,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "1.4",
+			},
+			Enumerant{
+				Enumerant:    "NoUnsignedWrap",
+				Value:        4470,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "1.4",
+			},
+			Enumerant{
+				Enumerant:    "ExplicitInterpAMD",
+				Value:        4999,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "OverrideCoverageNV",
+				Value:        5248,
+				Capabilities: []string{"SampleMaskOverrideCoverageNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "PassthroughNV",
+				Value:        5250,
+				Capabilities: []string{"GeometryShaderPassthroughNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "ViewportRelativeNV",
+				Value:        5252,
+				Capabilities: []string{"ShaderViewportMaskNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "SecondaryViewportRelativeNV",
+				Value:        5256,
+				Capabilities: []string{"ShaderStereoViewNV",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Offset'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "PerPrimitiveNV",
+				Value:        5271,
+				Capabilities: []string{"MeshShadingNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "PerViewNV",
+				Value:        5272,
+				Capabilities: []string{"MeshShadingNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "PerTaskNV",
+				Value:        5273,
+				Capabilities: []string{"MeshShadingNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "PerVertexNV",
+				Value:        5285,
+				Capabilities: []string{"FragmentBarycentricNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "NonUniform",
+				Value:        5300,
+				Capabilities: []string{"ShaderNonUniform",},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "NonUniformEXT",
+				Value:        5300,
+				Capabilities: []string{"ShaderNonUniform",},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "RestrictPointer",
+				Value:        5355,
+				Capabilities: []string{"PhysicalStorageBufferAddresses",},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "RestrictPointerEXT",
+				Value:        5355,
+				Capabilities: []string{"PhysicalStorageBufferAddresses",},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "AliasedPointer",
+				Value:        5356,
+				Capabilities: []string{"PhysicalStorageBufferAddresses",},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "AliasedPointerEXT",
+				Value:        5356,
+				Capabilities: []string{"PhysicalStorageBufferAddresses",},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "CounterBuffer",
+				Value:        5634,
+				Capabilities: []string{},
+				Parameters:   []Parameter{{OperandKindIdRef, "'Counter Buffer'"},},
+				Version:      "1.4",
+			},
+			Enumerant{
+				Enumerant:    "HlslCounterBufferGOOGLE",
+				Value:        5634,
+				Capabilities: []string{},
+				Parameters:   []Parameter{{OperandKindIdRef, "'Counter Buffer'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "UserSemantic",
+				Value:        5635,
+				Capabilities: []string{},
+				Parameters:   []Parameter{{OperandKindLiteralString, "'Semantic'"},},
+				Version:      "1.4",
+			},
+			Enumerant{
+				Enumerant:    "HlslSemanticGOOGLE",
+				Value:        5635,
+				Capabilities: []string{},
+				Parameters:   []Parameter{{OperandKindLiteralString, "'Semantic'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "UserTypeGOOGLE",
+				Value:        5636,
+				Capabilities: []string{},
+				Parameters:   []Parameter{{OperandKindLiteralString, "'User Type'"},},
+				Version:      "None",
+			},
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindBuiltIn = &OperandKind {
+		Kind:       "BuiltIn",
+		Category:   "ValueEnum",
+		Enumerants: []Enumerant {
+			Enumerant{
+				Enumerant:    "Position",
+				Value:        0,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "PointSize",
+				Value:        1,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "ClipDistance",
+				Value:        3,
+				Capabilities: []string{"ClipDistance",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "CullDistance",
+				Value:        4,
+				Capabilities: []string{"CullDistance",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "VertexId",
+				Value:        5,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "InstanceId",
+				Value:        6,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "PrimitiveId",
+				Value:        7,
+				Capabilities: []string{"Geometry","Tessellation","RayTracingNV",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "InvocationId",
+				Value:        8,
+				Capabilities: []string{"Geometry","Tessellation",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Layer",
+				Value:        9,
+				Capabilities: []string{"Geometry","ShaderLayer","ShaderViewportIndexLayerEXT",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "ViewportIndex",
+				Value:        10,
+				Capabilities: []string{"MultiViewport","ShaderViewportIndex","ShaderViewportIndexLayerEXT",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "TessLevelOuter",
+				Value:        11,
+				Capabilities: []string{"Tessellation",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "TessLevelInner",
+				Value:        12,
+				Capabilities: []string{"Tessellation",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "TessCoord",
+				Value:        13,
+				Capabilities: []string{"Tessellation",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "PatchVertices",
+				Value:        14,
+				Capabilities: []string{"Tessellation",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "FragCoord",
+				Value:        15,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "PointCoord",
+				Value:        16,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "FrontFacing",
+				Value:        17,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "SampleId",
+				Value:        18,
+				Capabilities: []string{"SampleRateShading",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "SamplePosition",
+				Value:        19,
+				Capabilities: []string{"SampleRateShading",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "SampleMask",
+				Value:        20,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "FragDepth",
+				Value:        22,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "HelperInvocation",
+				Value:        23,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "NumWorkgroups",
+				Value:        24,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "WorkgroupSize",
+				Value:        25,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "WorkgroupId",
+				Value:        26,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "LocalInvocationId",
+				Value:        27,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "GlobalInvocationId",
+				Value:        28,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "LocalInvocationIndex",
+				Value:        29,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "WorkDim",
+				Value:        30,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "GlobalSize",
+				Value:        31,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "EnqueuedWorkgroupSize",
+				Value:        32,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "GlobalOffset",
+				Value:        33,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "GlobalLinearId",
+				Value:        34,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "SubgroupSize",
+				Value:        36,
+				Capabilities: []string{"Kernel","GroupNonUniform","SubgroupBallotKHR",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "SubgroupMaxSize",
+				Value:        37,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "NumSubgroups",
+				Value:        38,
+				Capabilities: []string{"Kernel","GroupNonUniform",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "NumEnqueuedSubgroups",
+				Value:        39,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "SubgroupId",
+				Value:        40,
+				Capabilities: []string{"Kernel","GroupNonUniform",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "SubgroupLocalInvocationId",
+				Value:        41,
+				Capabilities: []string{"Kernel","GroupNonUniform","SubgroupBallotKHR",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "VertexIndex",
+				Value:        42,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "InstanceIndex",
+				Value:        43,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "SubgroupEqMask",
+				Value:        4416,
+				Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",},
+				Parameters:   []Parameter{},
+				Version:      "1.3",
+			},
+			Enumerant{
+				Enumerant:    "SubgroupGeMask",
+				Value:        4417,
+				Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",},
+				Parameters:   []Parameter{},
+				Version:      "1.3",
+			},
+			Enumerant{
+				Enumerant:    "SubgroupGtMask",
+				Value:        4418,
+				Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",},
+				Parameters:   []Parameter{},
+				Version:      "1.3",
+			},
+			Enumerant{
+				Enumerant:    "SubgroupLeMask",
+				Value:        4419,
+				Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",},
+				Parameters:   []Parameter{},
+				Version:      "1.3",
+			},
+			Enumerant{
+				Enumerant:    "SubgroupLtMask",
+				Value:        4420,
+				Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",},
+				Parameters:   []Parameter{},
+				Version:      "1.3",
+			},
+			Enumerant{
+				Enumerant:    "SubgroupEqMaskKHR",
+				Value:        4416,
+				Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",},
+				Parameters:   []Parameter{},
+				Version:      "1.3",
+			},
+			Enumerant{
+				Enumerant:    "SubgroupGeMaskKHR",
+				Value:        4417,
+				Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",},
+				Parameters:   []Parameter{},
+				Version:      "1.3",
+			},
+			Enumerant{
+				Enumerant:    "SubgroupGtMaskKHR",
+				Value:        4418,
+				Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",},
+				Parameters:   []Parameter{},
+				Version:      "1.3",
+			},
+			Enumerant{
+				Enumerant:    "SubgroupLeMaskKHR",
+				Value:        4419,
+				Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",},
+				Parameters:   []Parameter{},
+				Version:      "1.3",
+			},
+			Enumerant{
+				Enumerant:    "SubgroupLtMaskKHR",
+				Value:        4420,
+				Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",},
+				Parameters:   []Parameter{},
+				Version:      "1.3",
+			},
+			Enumerant{
+				Enumerant:    "BaseVertex",
+				Value:        4424,
+				Capabilities: []string{"DrawParameters",},
+				Parameters:   []Parameter{},
+				Version:      "1.3",
+			},
+			Enumerant{
+				Enumerant:    "BaseInstance",
+				Value:        4425,
+				Capabilities: []string{"DrawParameters",},
+				Parameters:   []Parameter{},
+				Version:      "1.3",
+			},
+			Enumerant{
+				Enumerant:    "DrawIndex",
+				Value:        4426,
+				Capabilities: []string{"DrawParameters","MeshShadingNV",},
+				Parameters:   []Parameter{},
+				Version:      "1.3",
+			},
+			Enumerant{
+				Enumerant:    "DeviceIndex",
+				Value:        4438,
+				Capabilities: []string{"DeviceGroup",},
+				Parameters:   []Parameter{},
+				Version:      "1.3",
+			},
+			Enumerant{
+				Enumerant:    "ViewIndex",
+				Value:        4440,
+				Capabilities: []string{"MultiView",},
+				Parameters:   []Parameter{},
+				Version:      "1.3",
+			},
+			Enumerant{
+				Enumerant:    "BaryCoordNoPerspAMD",
+				Value:        4992,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "BaryCoordNoPerspCentroidAMD",
+				Value:        4993,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "BaryCoordNoPerspSampleAMD",
+				Value:        4994,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "BaryCoordSmoothAMD",
+				Value:        4995,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "BaryCoordSmoothCentroidAMD",
+				Value:        4996,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "BaryCoordSmoothSampleAMD",
+				Value:        4997,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "BaryCoordPullModelAMD",
+				Value:        4998,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "FragStencilRefEXT",
+				Value:        5014,
+				Capabilities: []string{"StencilExportEXT",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "ViewportMaskNV",
+				Value:        5253,
+				Capabilities: []string{"ShaderViewportMaskNV","MeshShadingNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "SecondaryPositionNV",
+				Value:        5257,
+				Capabilities: []string{"ShaderStereoViewNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "SecondaryViewportMaskNV",
+				Value:        5258,
+				Capabilities: []string{"ShaderStereoViewNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "PositionPerViewNV",
+				Value:        5261,
+				Capabilities: []string{"PerViewAttributesNV","MeshShadingNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "ViewportMaskPerViewNV",
+				Value:        5262,
+				Capabilities: []string{"PerViewAttributesNV","MeshShadingNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "FullyCoveredEXT",
+				Value:        5264,
+				Capabilities: []string{"FragmentFullyCoveredEXT",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "TaskCountNV",
+				Value:        5274,
+				Capabilities: []string{"MeshShadingNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "PrimitiveCountNV",
+				Value:        5275,
+				Capabilities: []string{"MeshShadingNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "PrimitiveIndicesNV",
+				Value:        5276,
+				Capabilities: []string{"MeshShadingNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "ClipDistancePerViewNV",
+				Value:        5277,
+				Capabilities: []string{"MeshShadingNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "CullDistancePerViewNV",
+				Value:        5278,
+				Capabilities: []string{"MeshShadingNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "LayerPerViewNV",
+				Value:        5279,
+				Capabilities: []string{"MeshShadingNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "MeshViewCountNV",
+				Value:        5280,
+				Capabilities: []string{"MeshShadingNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "MeshViewIndicesNV",
+				Value:        5281,
+				Capabilities: []string{"MeshShadingNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "BaryCoordNV",
+				Value:        5286,
+				Capabilities: []string{"FragmentBarycentricNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "BaryCoordNoPerspNV",
+				Value:        5287,
+				Capabilities: []string{"FragmentBarycentricNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "FragSizeEXT",
+				Value:        5292,
+				Capabilities: []string{"FragmentDensityEXT","ShadingRateNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "FragmentSizeNV",
+				Value:        5292,
+				Capabilities: []string{"ShadingRateNV","FragmentDensityEXT",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "FragInvocationCountEXT",
+				Value:        5293,
+				Capabilities: []string{"FragmentDensityEXT","ShadingRateNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "InvocationsPerPixelNV",
+				Value:        5293,
+				Capabilities: []string{"ShadingRateNV","FragmentDensityEXT",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "LaunchIdNV",
+				Value:        5319,
+				Capabilities: []string{"RayTracingNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "LaunchSizeNV",
+				Value:        5320,
+				Capabilities: []string{"RayTracingNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "WorldRayOriginNV",
+				Value:        5321,
+				Capabilities: []string{"RayTracingNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "WorldRayDirectionNV",
+				Value:        5322,
+				Capabilities: []string{"RayTracingNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "ObjectRayOriginNV",
+				Value:        5323,
+				Capabilities: []string{"RayTracingNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "ObjectRayDirectionNV",
+				Value:        5324,
+				Capabilities: []string{"RayTracingNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "RayTminNV",
+				Value:        5325,
+				Capabilities: []string{"RayTracingNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "RayTmaxNV",
+				Value:        5326,
+				Capabilities: []string{"RayTracingNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "InstanceCustomIndexNV",
+				Value:        5327,
+				Capabilities: []string{"RayTracingNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "ObjectToWorldNV",
+				Value:        5330,
+				Capabilities: []string{"RayTracingNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "WorldToObjectNV",
+				Value:        5331,
+				Capabilities: []string{"RayTracingNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "HitTNV",
+				Value:        5332,
+				Capabilities: []string{"RayTracingNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "HitKindNV",
+				Value:        5333,
+				Capabilities: []string{"RayTracingNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "IncomingRayFlagsNV",
+				Value:        5351,
+				Capabilities: []string{"RayTracingNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "WarpsPerSMNV",
+				Value:        5374,
+				Capabilities: []string{"ShaderSMBuiltinsNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "SMCountNV",
+				Value:        5375,
+				Capabilities: []string{"ShaderSMBuiltinsNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "WarpIDNV",
+				Value:        5376,
+				Capabilities: []string{"ShaderSMBuiltinsNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "SMIDNV",
+				Value:        5377,
+				Capabilities: []string{"ShaderSMBuiltinsNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindScope = &OperandKind {
+		Kind:       "Scope",
+		Category:   "ValueEnum",
+		Enumerants: []Enumerant {
+			Enumerant{
+				Enumerant:    "CrossDevice",
+				Value:        0,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Device",
+				Value:        1,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Workgroup",
+				Value:        2,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Subgroup",
+				Value:        3,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Invocation",
+				Value:        4,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "QueueFamily",
+				Value:        5,
+				Capabilities: []string{"VulkanMemoryModel",},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "QueueFamilyKHR",
+				Value:        5,
+				Capabilities: []string{"VulkanMemoryModel",},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindGroupOperation = &OperandKind {
+		Kind:       "GroupOperation",
+		Category:   "ValueEnum",
+		Enumerants: []Enumerant {
+			Enumerant{
+				Enumerant:    "Reduce",
+				Value:        0,
+				Capabilities: []string{"Kernel","GroupNonUniformArithmetic","GroupNonUniformBallot",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "InclusiveScan",
+				Value:        1,
+				Capabilities: []string{"Kernel","GroupNonUniformArithmetic","GroupNonUniformBallot",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "ExclusiveScan",
+				Value:        2,
+				Capabilities: []string{"Kernel","GroupNonUniformArithmetic","GroupNonUniformBallot",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "ClusteredReduce",
+				Value:        3,
+				Capabilities: []string{"GroupNonUniformClustered",},
+				Parameters:   []Parameter{},
+				Version:      "1.3",
+			},
+			Enumerant{
+				Enumerant:    "PartitionedReduceNV",
+				Value:        6,
+				Capabilities: []string{"GroupNonUniformPartitionedNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "PartitionedInclusiveScanNV",
+				Value:        7,
+				Capabilities: []string{"GroupNonUniformPartitionedNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "PartitionedExclusiveScanNV",
+				Value:        8,
+				Capabilities: []string{"GroupNonUniformPartitionedNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindKernelEnqueueFlags = &OperandKind {
+		Kind:       "KernelEnqueueFlags",
+		Category:   "ValueEnum",
+		Enumerants: []Enumerant {
+			Enumerant{
+				Enumerant:    "NoWait",
+				Value:        0,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "WaitKernel",
+				Value:        1,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "WaitWorkGroup",
+				Value:        2,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindCapability = &OperandKind {
+		Kind:       "Capability",
+		Category:   "ValueEnum",
+		Enumerants: []Enumerant {
+			Enumerant{
+				Enumerant:    "Matrix",
+				Value:        0,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Shader",
+				Value:        1,
+				Capabilities: []string{"Matrix",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Geometry",
+				Value:        2,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Tessellation",
+				Value:        3,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Addresses",
+				Value:        4,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Linkage",
+				Value:        5,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Kernel",
+				Value:        6,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Vector16",
+				Value:        7,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Float16Buffer",
+				Value:        8,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Float16",
+				Value:        9,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Float64",
+				Value:        10,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Int64",
+				Value:        11,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Int64Atomics",
+				Value:        12,
+				Capabilities: []string{"Int64",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "ImageBasic",
+				Value:        13,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "ImageReadWrite",
+				Value:        14,
+				Capabilities: []string{"ImageBasic",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "ImageMipmap",
+				Value:        15,
+				Capabilities: []string{"ImageBasic",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Pipes",
+				Value:        17,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Groups",
+				Value:        18,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "DeviceEnqueue",
+				Value:        19,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "LiteralSampler",
+				Value:        20,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "AtomicStorage",
+				Value:        21,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Int16",
+				Value:        22,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "TessellationPointSize",
+				Value:        23,
+				Capabilities: []string{"Tessellation",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "GeometryPointSize",
+				Value:        24,
+				Capabilities: []string{"Geometry",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "ImageGatherExtended",
+				Value:        25,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "StorageImageMultisample",
+				Value:        27,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "UniformBufferArrayDynamicIndexing",
+				Value:        28,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "SampledImageArrayDynamicIndexing",
+				Value:        29,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "StorageBufferArrayDynamicIndexing",
+				Value:        30,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "StorageImageArrayDynamicIndexing",
+				Value:        31,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "ClipDistance",
+				Value:        32,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "CullDistance",
+				Value:        33,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "ImageCubeArray",
+				Value:        34,
+				Capabilities: []string{"SampledCubeArray",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "SampleRateShading",
+				Value:        35,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "ImageRect",
+				Value:        36,
+				Capabilities: []string{"SampledRect",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "SampledRect",
+				Value:        37,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "GenericPointer",
+				Value:        38,
+				Capabilities: []string{"Addresses",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Int8",
+				Value:        39,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "InputAttachment",
+				Value:        40,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "SparseResidency",
+				Value:        41,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "MinLod",
+				Value:        42,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Sampled1D",
+				Value:        43,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "Image1D",
+				Value:        44,
+				Capabilities: []string{"Sampled1D",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "SampledCubeArray",
+				Value:        45,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "SampledBuffer",
+				Value:        46,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "ImageBuffer",
+				Value:        47,
+				Capabilities: []string{"SampledBuffer",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "ImageMSArray",
+				Value:        48,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "StorageImageExtendedFormats",
+				Value:        49,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "ImageQuery",
+				Value:        50,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "DerivativeControl",
+				Value:        51,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "InterpolationFunction",
+				Value:        52,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "TransformFeedback",
+				Value:        53,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "GeometryStreams",
+				Value:        54,
+				Capabilities: []string{"Geometry",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "StorageImageReadWithoutFormat",
+				Value:        55,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "StorageImageWriteWithoutFormat",
+				Value:        56,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "MultiViewport",
+				Value:        57,
+				Capabilities: []string{"Geometry",},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
+				Enumerant:    "SubgroupDispatch",
+				Value:        58,
+				Capabilities: []string{"DeviceEnqueue",},
+				Parameters:   []Parameter{},
+				Version:      "1.1",
+			},
+			Enumerant{
+				Enumerant:    "NamedBarrier",
+				Value:        59,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "1.1",
+			},
+			Enumerant{
+				Enumerant:    "PipeStorage",
+				Value:        60,
+				Capabilities: []string{"Pipes",},
+				Parameters:   []Parameter{},
+				Version:      "1.1",
+			},
+			Enumerant{
+				Enumerant:    "GroupNonUniform",
+				Value:        61,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "1.3",
+			},
+			Enumerant{
+				Enumerant:    "GroupNonUniformVote",
+				Value:        62,
+				Capabilities: []string{"GroupNonUniform",},
+				Parameters:   []Parameter{},
+				Version:      "1.3",
+			},
+			Enumerant{
+				Enumerant:    "GroupNonUniformArithmetic",
+				Value:        63,
+				Capabilities: []string{"GroupNonUniform",},
+				Parameters:   []Parameter{},
+				Version:      "1.3",
+			},
+			Enumerant{
+				Enumerant:    "GroupNonUniformBallot",
+				Value:        64,
+				Capabilities: []string{"GroupNonUniform",},
+				Parameters:   []Parameter{},
+				Version:      "1.3",
+			},
+			Enumerant{
+				Enumerant:    "GroupNonUniformShuffle",
+				Value:        65,
+				Capabilities: []string{"GroupNonUniform",},
+				Parameters:   []Parameter{},
+				Version:      "1.3",
+			},
+			Enumerant{
+				Enumerant:    "GroupNonUniformShuffleRelative",
+				Value:        66,
+				Capabilities: []string{"GroupNonUniform",},
+				Parameters:   []Parameter{},
+				Version:      "1.3",
+			},
+			Enumerant{
+				Enumerant:    "GroupNonUniformClustered",
+				Value:        67,
+				Capabilities: []string{"GroupNonUniform",},
+				Parameters:   []Parameter{},
+				Version:      "1.3",
+			},
+			Enumerant{
+				Enumerant:    "GroupNonUniformQuad",
+				Value:        68,
+				Capabilities: []string{"GroupNonUniform",},
+				Parameters:   []Parameter{},
+				Version:      "1.3",
+			},
+			Enumerant{
+				Enumerant:    "ShaderLayer",
+				Value:        69,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "ShaderViewportIndex",
+				Value:        70,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "SubgroupBallotKHR",
+				Value:        4423,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "DrawParameters",
+				Value:        4427,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "1.3",
+			},
+			Enumerant{
+				Enumerant:    "SubgroupVoteKHR",
+				Value:        4431,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "StorageBuffer16BitAccess",
+				Value:        4433,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "1.3",
+			},
+			Enumerant{
+				Enumerant:    "StorageUniformBufferBlock16",
+				Value:        4433,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "1.3",
+			},
+			Enumerant{
+				Enumerant:    "UniformAndStorageBuffer16BitAccess",
+				Value:        4434,
+				Capabilities: []string{"StorageBuffer16BitAccess","StorageUniformBufferBlock16",},
+				Parameters:   []Parameter{},
+				Version:      "1.3",
+			},
+			Enumerant{
+				Enumerant:    "StorageUniform16",
+				Value:        4434,
+				Capabilities: []string{"StorageBuffer16BitAccess","StorageUniformBufferBlock16",},
+				Parameters:   []Parameter{},
+				Version:      "1.3",
+			},
+			Enumerant{
+				Enumerant:    "StoragePushConstant16",
+				Value:        4435,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "1.3",
+			},
+			Enumerant{
+				Enumerant:    "StorageInputOutput16",
+				Value:        4436,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "1.3",
+			},
+			Enumerant{
+				Enumerant:    "DeviceGroup",
+				Value:        4437,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "1.3",
+			},
+			Enumerant{
+				Enumerant:    "MultiView",
+				Value:        4439,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "1.3",
+			},
+			Enumerant{
+				Enumerant:    "VariablePointersStorageBuffer",
+				Value:        4441,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "1.3",
+			},
+			Enumerant{
+				Enumerant:    "VariablePointers",
+				Value:        4442,
+				Capabilities: []string{"VariablePointersStorageBuffer",},
+				Parameters:   []Parameter{},
+				Version:      "1.3",
+			},
+			Enumerant{
+				Enumerant:    "AtomicStorageOps",
+				Value:        4445,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "SampleMaskPostDepthCoverage",
+				Value:        4447,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "StorageBuffer8BitAccess",
+				Value:        4448,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "UniformAndStorageBuffer8BitAccess",
+				Value:        4449,
+				Capabilities: []string{"StorageBuffer8BitAccess",},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "StoragePushConstant8",
+				Value:        4450,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "DenormPreserve",
+				Value:        4464,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "1.4",
+			},
+			Enumerant{
+				Enumerant:    "DenormFlushToZero",
+				Value:        4465,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "1.4",
+			},
+			Enumerant{
+				Enumerant:    "SignedZeroInfNanPreserve",
+				Value:        4466,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "1.4",
+			},
+			Enumerant{
+				Enumerant:    "RoundingModeRTE",
+				Value:        4467,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "1.4",
+			},
+			Enumerant{
+				Enumerant:    "RoundingModeRTZ",
+				Value:        4468,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "1.4",
+			},
+			Enumerant{
+				Enumerant:    "Float16ImageAMD",
+				Value:        5008,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "ImageGatherBiasLodAMD",
+				Value:        5009,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "FragmentMaskAMD",
+				Value:        5010,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "StencilExportEXT",
+				Value:        5013,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "ImageReadWriteLodAMD",
+				Value:        5015,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "ShaderClockKHR",
+				Value:        5055,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "SampleMaskOverrideCoverageNV",
+				Value:        5249,
+				Capabilities: []string{"SampleRateShading",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "GeometryShaderPassthroughNV",
+				Value:        5251,
+				Capabilities: []string{"Geometry",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "ShaderViewportIndexLayerEXT",
+				Value:        5254,
+				Capabilities: []string{"MultiViewport",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "ShaderViewportIndexLayerNV",
+				Value:        5254,
+				Capabilities: []string{"MultiViewport",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "ShaderViewportMaskNV",
+				Value:        5255,
+				Capabilities: []string{"ShaderViewportIndexLayerNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "ShaderStereoViewNV",
+				Value:        5259,
+				Capabilities: []string{"ShaderViewportMaskNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "PerViewAttributesNV",
+				Value:        5260,
+				Capabilities: []string{"MultiView",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "FragmentFullyCoveredEXT",
+				Value:        5265,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "MeshShadingNV",
+				Value:        5266,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "ImageFootprintNV",
+				Value:        5282,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "FragmentBarycentricNV",
+				Value:        5284,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "ComputeDerivativeGroupQuadsNV",
+				Value:        5288,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "FragmentDensityEXT",
+				Value:        5291,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "ShadingRateNV",
+				Value:        5291,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "GroupNonUniformPartitionedNV",
+				Value:        5297,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "ShaderNonUniform",
+				Value:        5301,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "ShaderNonUniformEXT",
+				Value:        5301,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "RuntimeDescriptorArray",
+				Value:        5302,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "RuntimeDescriptorArrayEXT",
+				Value:        5302,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "InputAttachmentArrayDynamicIndexing",
+				Value:        5303,
+				Capabilities: []string{"InputAttachment",},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "InputAttachmentArrayDynamicIndexingEXT",
+				Value:        5303,
+				Capabilities: []string{"InputAttachment",},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "UniformTexelBufferArrayDynamicIndexing",
+				Value:        5304,
+				Capabilities: []string{"SampledBuffer",},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "UniformTexelBufferArrayDynamicIndexingEXT",
+				Value:        5304,
+				Capabilities: []string{"SampledBuffer",},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "StorageTexelBufferArrayDynamicIndexing",
+				Value:        5305,
+				Capabilities: []string{"ImageBuffer",},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "StorageTexelBufferArrayDynamicIndexingEXT",
+				Value:        5305,
+				Capabilities: []string{"ImageBuffer",},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "UniformBufferArrayNonUniformIndexing",
+				Value:        5306,
+				Capabilities: []string{"ShaderNonUniform",},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "UniformBufferArrayNonUniformIndexingEXT",
+				Value:        5306,
+				Capabilities: []string{"ShaderNonUniform",},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "SampledImageArrayNonUniformIndexing",
+				Value:        5307,
+				Capabilities: []string{"ShaderNonUniform",},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "SampledImageArrayNonUniformIndexingEXT",
+				Value:        5307,
+				Capabilities: []string{"ShaderNonUniform",},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "StorageBufferArrayNonUniformIndexing",
+				Value:        5308,
+				Capabilities: []string{"ShaderNonUniform",},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "StorageBufferArrayNonUniformIndexingEXT",
+				Value:        5308,
+				Capabilities: []string{"ShaderNonUniform",},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "StorageImageArrayNonUniformIndexing",
+				Value:        5309,
+				Capabilities: []string{"ShaderNonUniform",},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "StorageImageArrayNonUniformIndexingEXT",
+				Value:        5309,
+				Capabilities: []string{"ShaderNonUniform",},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "InputAttachmentArrayNonUniformIndexing",
+				Value:        5310,
+				Capabilities: []string{"InputAttachment","ShaderNonUniform",},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "InputAttachmentArrayNonUniformIndexingEXT",
+				Value:        5310,
+				Capabilities: []string{"InputAttachment","ShaderNonUniform",},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "UniformTexelBufferArrayNonUniformIndexing",
+				Value:        5311,
+				Capabilities: []string{"SampledBuffer","ShaderNonUniform",},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "UniformTexelBufferArrayNonUniformIndexingEXT",
+				Value:        5311,
+				Capabilities: []string{"SampledBuffer","ShaderNonUniform",},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "StorageTexelBufferArrayNonUniformIndexing",
+				Value:        5312,
+				Capabilities: []string{"ImageBuffer","ShaderNonUniform",},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "StorageTexelBufferArrayNonUniformIndexingEXT",
+				Value:        5312,
+				Capabilities: []string{"ImageBuffer","ShaderNonUniform",},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "RayTracingNV",
+				Value:        5340,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "VulkanMemoryModel",
+				Value:        5345,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "VulkanMemoryModelKHR",
+				Value:        5345,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "VulkanMemoryModelDeviceScope",
+				Value:        5346,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "VulkanMemoryModelDeviceScopeKHR",
+				Value:        5346,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "PhysicalStorageBufferAddresses",
+				Value:        5347,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "PhysicalStorageBufferAddressesEXT",
+				Value:        5347,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "1.5",
+			},
+			Enumerant{
+				Enumerant:    "ComputeDerivativeGroupLinearNV",
+				Value:        5350,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "CooperativeMatrixNV",
+				Value:        5357,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "FragmentShaderSampleInterlockEXT",
+				Value:        5363,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "FragmentShaderShadingRateInterlockEXT",
+				Value:        5372,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "ShaderSMBuiltinsNV",
+				Value:        5373,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "FragmentShaderPixelInterlockEXT",
+				Value:        5378,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "DemoteToHelperInvocationEXT",
+				Value:        5379,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "SubgroupShuffleINTEL",
+				Value:        5568,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "SubgroupBufferBlockIOINTEL",
+				Value:        5569,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "SubgroupImageBlockIOINTEL",
+				Value:        5570,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "SubgroupImageMediaBlockIOINTEL",
+				Value:        5579,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "IntegerFunctions2INTEL",
+				Value:        5584,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "SubgroupAvcMotionEstimationINTEL",
+				Value:        5696,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "SubgroupAvcMotionEstimationIntraINTEL",
+				Value:        5697,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "SubgroupAvcMotionEstimationChromaINTEL",
+				Value:        5698,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindIdResultType = &OperandKind {
+		Kind:       "IdResultType",
+		Category:   "Id",
+		Enumerants: []Enumerant {
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindIdResult = &OperandKind {
+		Kind:       "IdResult",
+		Category:   "Id",
+		Enumerants: []Enumerant {
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindIdMemorySemantics = &OperandKind {
+		Kind:       "IdMemorySemantics",
+		Category:   "Id",
+		Enumerants: []Enumerant {
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindIdScope = &OperandKind {
+		Kind:       "IdScope",
+		Category:   "Id",
+		Enumerants: []Enumerant {
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindIdRef = &OperandKind {
+		Kind:       "IdRef",
+		Category:   "Id",
+		Enumerants: []Enumerant {
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindLiteralInteger = &OperandKind {
+		Kind:       "LiteralInteger",
+		Category:   "Literal",
+		Enumerants: []Enumerant {
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindLiteralString = &OperandKind {
+		Kind:       "LiteralString",
+		Category:   "Literal",
+		Enumerants: []Enumerant {
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindLiteralContextDependentNumber = &OperandKind {
+		Kind:       "LiteralContextDependentNumber",
+		Category:   "Literal",
+		Enumerants: []Enumerant {
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindLiteralExtInstInteger = &OperandKind {
+		Kind:       "LiteralExtInstInteger",
+		Category:   "Literal",
+		Enumerants: []Enumerant {
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindLiteralSpecConstantOpInteger = &OperandKind {
+		Kind:       "LiteralSpecConstantOpInteger",
+		Category:   "Literal",
+		Enumerants: []Enumerant {
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindPairLiteralIntegerIdRef = &OperandKind {
+		Kind:       "PairLiteralIntegerIdRef",
+		Category:   "Composite",
+		Enumerants: []Enumerant {
+		},
+		Bases:      []*OperandKind {OperandKindLiteralInteger,OperandKindIdRef,},
+	}
+	OperandKindPairIdRefLiteralInteger = &OperandKind {
+		Kind:       "PairIdRefLiteralInteger",
+		Category:   "Composite",
+		Enumerants: []Enumerant {
+		},
+		Bases:      []*OperandKind {OperandKindIdRef,OperandKindLiteralInteger,},
+	}
+	OperandKindPairIdRefIdRef = &OperandKind {
+		Kind:       "PairIdRefIdRef",
+		Category:   "Composite",
+		Enumerants: []Enumerant {
+		},
+		Bases:      []*OperandKind {OperandKindIdRef,OperandKindIdRef,},
+	}
+
+)
diff --git a/utils/vscode/src/schema/schema.go.tmpl b/utils/vscode/src/schema/schema.go.tmpl
new file mode 100644
index 0000000..b584058
--- /dev/null
+++ b/utils/vscode/src/schema/schema.go.tmpl
@@ -0,0 +1,133 @@
+// Copyright (C) 2019 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Generated by {{GenerateArguments}}
+// Do not modify this file directly.
+
+package schema
+
+// Opcode holds information about a specific SPIR-V opcode.
+type Opcode struct {
+	Opname   string
+	Class    string
+	Opcode   int
+	Operands []Operand
+}
+
+// Operand contains information about a logical operand for an instruction.
+type Operand struct {
+	Kind       *OperandKind
+	Name       string
+	Quantifier Quantifier
+}
+
+// OperandKind contains information about a specific operand kind.
+type OperandKind struct {
+	Category   OperandCategory
+	Kind       string
+	Enumerants []Enumerant
+	Bases      []*OperandKind
+}
+
+// Enumerant contains information about an enumerant in an enum.
+type Enumerant struct {
+	Enumerant    string
+	Value        interface{}
+	Capabilities []string
+	Parameters   []Parameter
+	Version      string
+}
+
+// Parameter contains information about a logical parameter for an enumerant.
+type Parameter struct {
+	Kind *OperandKind
+	Name string
+}
+
+// Quantifier indicates the number of times the quantified term may appear.
+type Quantifier string
+
+const (
+	// Once indicates the quantified term may appear exactly once.
+	Once Quantifier = ""
+
+	// ZeroOrOnce indicates the quantified term may appear zero or one
+	// time; an optional term.
+	ZeroOrOnce Quantifier = "?"
+
+	// ZeroOrMany indicates the quantified term may appear any number of
+	// times.
+	ZeroOrMany Quantifier = "*"
+)
+
+// OperandCategory is an enumerator that groups operand kinds.
+type OperandCategory string
+
+const (
+	// OperandCategoryBitEnum describes an operand kind where its value is a
+	// mask, which is formed by combining the bits specified as enumerants in an
+	// enum.
+	OperandCategoryBitEnum = "BitEnum"
+
+	// OperandCategoryValueEnum describes an operand kind where its value is an
+	// enumerant from an enum.
+	OperandCategoryValueEnum = "ValueEnum"
+
+	// OperandCategoryID describes and operand kind where its value is an <id>
+	// definition or reference.
+	OperandCategoryID = "Id"
+
+	// OperandCategoryLiteral describes and operand kind where its value is an
+	// literal number or string.
+	OperandCategoryLiteral = "Literal"
+
+	// OperandCategoryComposite describes and operand kind where its value is
+	// composed from operand values from the above categories.
+	OperandCategoryComposite = "Composite"
+)
+
+var (
+	// Opcodes is a map of opcode name to Opcode description.
+	Opcodes = map[string]*Opcode {•{{range $i := .Instructions}}
+		"{{$i.Opname}}": {{$i.Opname}},{{end}}
+	}
+
+{{range $i := .Instructions}}	{{$i.Opname}} = &Opcode {
+		Opname:   "{{$i.Opname}}",
+		Operands: []Operand {•{{range $i := $i.Operands}}
+			Operand {
+				Kind:       OperandKind{{$i.Kind}},
+				Name:       "{{Replace $i.Name "\n" " "}}",
+				Quantifier: "{{$i.Quantifier}}",
+			}, {{end}}
+		},
+	}
+{{end}}
+
+{{range $o := .OperandKinds}}	OperandKind{{$o.Kind}} = &OperandKind {
+		Kind:       "{{$o.Kind}}",
+		Category:   "{{$o.Category}}",
+		Enumerants: []Enumerant {•{{range $e := $o.Enumerants}}
+			Enumerant{
+				Enumerant:    "{{$e.Enumerant}}",
+				Value:        {{$e.Value}},
+				Capabilities: []string{•{{range $c := $e.Capabilities}}"{{$c}}",{{end}}•},
+				Parameters:   []Parameter{•{{range $p := $e.Parameters}}{•OperandKind{{$p.Kind}}, "{{$p.Name}}"•},{{end}}•},
+				Version:      "{{$e.Version}}",
+			},{{end}}
+		},
+		Bases:      []*OperandKind {•{{range $b := $o.Bases}}OperandKind{{$b}},{{end}}•},
+	}
+{{end}}
+)
diff --git a/utils/vscode/src/tools/gen-grammar.go b/utils/vscode/src/tools/gen-grammar.go
new file mode 100644
index 0000000..f9980b9
--- /dev/null
+++ b/utils/vscode/src/tools/gen-grammar.go
@@ -0,0 +1,168 @@
+// Copyright (C) 2019 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// gen-grammar generates the spirv.json grammar file from the official SPIR-V
+// grammar JSON file.
+package main
+
+import (
+	"bytes"
+	"encoding/json"
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"net/http"
+	"os"
+	"path/filepath"
+	"runtime"
+	"strings"
+	"text/template"
+
+	"github.com/pkg/errors"
+
+	"../grammar"
+)
+
+const (
+	spirvGrammarURL  = "https://raw.githubusercontent.com/KhronosGroup/SPIRV-Headers/master/include/spirv/unified1/spirv.core.grammar.json"
+	spirvGrammarName = "spirv.core.grammar.json"
+)
+
+var (
+	templatePath = flag.String("template", "", "Path to input template file (required)")
+	outputPath   = flag.String("out", "", "Path to output generated file (required)")
+	cachePath    = flag.String("cache", "", "Cache directory for downloaded files (optional)")
+
+	thisDir = func() string {
+		_, file, _, _ := runtime.Caller(1)
+		return filepath.Dir(file)
+	}()
+)
+
+func main() {
+	flag.Parse()
+	if *templatePath == "" || *outputPath == "" {
+		flag.Usage()
+		os.Exit(1)
+	}
+	if err := run(); err != nil {
+		fmt.Fprintln(os.Stderr, err)
+		os.Exit(1)
+	}
+}
+
+func run() error {
+	tf, err := ioutil.ReadFile(*templatePath)
+	if err != nil {
+		return errors.Wrap(err, "Could not open template file")
+	}
+	t, err := template.New("tmpl").
+		Funcs(template.FuncMap{
+			"GenerateArguments": func() string {
+				relPath := func(path string) string {
+					rel, err := filepath.Rel(thisDir, path)
+					if err != nil {
+						return path
+					}
+					return rel
+				}
+				args := []string{
+					"--template=" + relPath(*templatePath),
+					"--out=" + relPath(*outputPath),
+				}
+				return "gen-grammar.go " + strings.Join(args, " ")
+			},
+			"OperandKindsMatch": func(k grammar.OperandKind) string {
+				sb := strings.Builder{}
+				for i, e := range k.Enumerants {
+					if i > 0 {
+						sb.WriteString("|")
+					}
+					sb.WriteString(e.Enumerant)
+				}
+				return sb.String()
+			},
+			"Replace": strings.ReplaceAll,
+		}).Parse(string(tf))
+	if err != nil {
+		return errors.Wrap(err, "Failed to parse template")
+	}
+
+	file, err := getOrDownload(spirvGrammarName, spirvGrammarURL)
+	if err != nil {
+		return errors.Wrap(err, "Failed to load grammar file")
+	}
+
+	g := grammar.Root{}
+	if err := json.NewDecoder(bytes.NewReader(file)).Decode(&g); err != nil {
+		return errors.Wrap(err, "Failed to parse grammar file")
+	}
+
+	buf := bytes.Buffer{}
+	if err := t.Execute(&buf, g); err != nil {
+		return errors.Wrap(err, "Failed to execute template")
+	}
+
+	out := buf.String()
+	out = strings.ReplaceAll(out, "•", "")
+
+	if err := ioutil.WriteFile(*outputPath, []byte(out), 0777); err != nil {
+		return errors.Wrap(err, "Failed to write output file")
+	}
+
+	return nil
+}
+
+// getOrDownload loads the specific file from the cache, or downloads the file
+// from the given url.
+func getOrDownload(name, url string) ([]byte, error) {
+	if *cachePath != "" {
+		if err := os.MkdirAll(*cachePath, 0777); err == nil {
+			path := filepath.Join(*cachePath, name)
+			if isFile(path) {
+				return ioutil.ReadFile(path)
+			}
+		}
+	}
+	resp, err := http.Get(url)
+	if err != nil {
+		return nil, err
+	}
+	data, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		return nil, err
+	}
+	if *cachePath != "" {
+		ioutil.WriteFile(filepath.Join(*cachePath, name), data, 0777)
+	}
+	return data, nil
+}
+
+// isFile returns true if path is a file.
+func isFile(path string) bool {
+	s, err := os.Stat(path)
+	if err != nil {
+		return false
+	}
+	return !s.IsDir()
+}
+
+// isDir returns true if path is a directory.
+func isDir(path string) bool {
+	s, err := os.Stat(path)
+	if err != nil {
+		return false
+	}
+	return s.IsDir()
+}