Snap for 7775125 from 9ff53333585d20c277a80b58abc4c24f9252e5a9 to sc-v2-release

Change-Id: Ifba1d27fb78bc09f22f4afa7c970983160b13b04
diff --git a/Android.bp b/Android.bp
index ef10e4e..07bd32b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -139,5 +139,6 @@
     cppflags: [
         "-Wno-implicit-fallthrough",
         "-Wno-sign-conversion",
+        "-Wno-switch",
     ],
 }
diff --git a/BUILD.gn b/BUILD.gn
index 845eb29..19ee77a 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -19,6 +19,7 @@
 }
 
 spirv_headers = spirv_tools_spirv_headers_dir
+spirv_is_winuwp = is_win && target_os == "winuwp"
 
 template("spvtools_core_tables") {
   assert(defined(invoker.version), "Need version in $target_name generation.")
@@ -32,13 +33,14 @@
         "${spirv_headers}/include/spirv/$version/spirv.core.grammar.json"
     core_insts_file = "${target_gen_dir}/core.insts-$version.inc"
     operand_kinds_file = "${target_gen_dir}/operand.kinds-$version.inc"
-    debuginfo_insts_file = "${spirv_headers}/include/spirv/unified1/extinst.debuginfo.grammar.json"
+    debuginfo_insts_file =
+        "${spirv_headers}/include/spirv/unified1/extinst.debuginfo.grammar.json"
     cldebuginfo100_insts_file = "${spirv_headers}/include/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json"
 
     sources = [
+      cldebuginfo100_insts_file,
       core_json_file,
       debuginfo_insts_file,
-      cldebuginfo100_insts_file,
     ]
     outputs = [
       core_insts_file,
@@ -69,7 +71,8 @@
 
     core_json_file =
         "${spirv_headers}/include/spirv/$version/spirv.core.grammar.json"
-    debuginfo_insts_file = "${spirv_headers}/include/spirv/unified1/extinst.debuginfo.grammar.json"
+    debuginfo_insts_file =
+        "${spirv_headers}/include/spirv/unified1/extinst.debuginfo.grammar.json"
     cldebuginfo100_insts_file = "${spirv_headers}/include/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json"
 
     extension_enum_file = "${target_gen_dir}/extension_enum.inc"
@@ -110,7 +113,8 @@
     core_json_file =
         "${spirv_headers}/include/spirv/$version/spirv.core.grammar.json"
     glsl_json_file = "${spirv_headers}/include/spirv/${version}/extinst.glsl.std.450.grammar.json"
-    debuginfo_insts_file = "${spirv_headers}/include/spirv/unified1/extinst.debuginfo.grammar.json"
+    debuginfo_insts_file =
+        "${spirv_headers}/include/spirv/unified1/extinst.debuginfo.grammar.json"
     cldebuginfo100_insts_file = "${spirv_headers}/include/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json"
 
     glsl_insts_file = "${target_gen_dir}/glsl.std.450.insts.inc"
@@ -133,9 +137,7 @@
       debuginfo_insts_file,
       cldebuginfo100_insts_file,
     ]
-    outputs = [
-      glsl_insts_file,
-    ]
+    outputs = [ glsl_insts_file ]
   }
 }
 
@@ -150,7 +152,8 @@
     core_json_file =
         "${spirv_headers}/include/spirv/$version/spirv.core.grammar.json"
     opencl_json_file = "${spirv_headers}/include/spirv/${version}/extinst.opencl.std.100.grammar.json"
-    debuginfo_insts_file = "${spirv_headers}/include/spirv/unified1/extinst.debuginfo.grammar.json"
+    debuginfo_insts_file =
+        "${spirv_headers}/include/spirv/unified1/extinst.debuginfo.grammar.json"
     cldebuginfo100_insts_file = "${spirv_headers}/include/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json"
 
     opencl_insts_file = "${target_gen_dir}/opencl.std.insts.inc"
@@ -173,9 +176,7 @@
       debuginfo_insts_file,
       cldebuginfo100_insts_file,
     ]
-    outputs = [
-      opencl_insts_file,
-    ]
+    outputs = [ opencl_insts_file ]
   }
 }
 
@@ -194,12 +195,8 @@
       "--extinst-output-path",
       rebase_path(extinst_output_path, root_build_dir),
     ]
-    inputs = [
-      invoker.grammar_file,
-    ]
-    outputs = [
-      "${extinst_output_path}",
-    ]
+    inputs = [ invoker.grammar_file ]
+    outputs = [ "${extinst_output_path}" ]
   }
 }
 
@@ -210,7 +207,8 @@
     script = "utils/generate_grammar_tables.py"
 
     name = invoker.name
-    extinst_vendor_grammar = "${spirv_headers}/include/spirv/unified1/extinst.${name}.grammar.json"
+    extinst_vendor_grammar =
+        "${spirv_headers}/include/spirv/unified1/extinst.${name}.grammar.json"
     extinst_file = "${target_gen_dir}/${name}.insts.inc"
 
     args = [
@@ -219,14 +217,10 @@
       "--vendor-insts-output",
       rebase_path(extinst_file, root_build_dir),
       "--vendor-operand-kind-prefix",
-      invoker.operand_kind_prefix
+      invoker.operand_kind_prefix,
     ]
-    inputs = [
-      extinst_vendor_grammar,
-    ]
-    outputs = [
-      extinst_file,
-    ]
+    inputs = [ extinst_vendor_grammar ]
+    outputs = [ extinst_file ]
   }
 }
 
@@ -237,12 +231,8 @@
   xml_file = "${spirv_headers}/include/spirv/spir-v.xml"
   inc_file = "${target_gen_dir}/generators.inc"
 
-  sources = [
-    xml_file,
-  ]
-  outputs = [
-    inc_file,
-  ]
+  sources = [ xml_file ]
+  outputs = [ inc_file ]
   args = [
     "--xml",
     rebase_path(xml_file, root_build_dir),
@@ -257,9 +247,7 @@
   src_dir = "."
   inc_file = "${target_gen_dir}/build-version.inc"
 
-  outputs = [
-    inc_file,
-  ]
+  outputs = [ inc_file ]
   args = [
     rebase_path(src_dir, root_build_dir),
     rebase_path(inc_file, root_build_dir),
@@ -280,7 +268,8 @@
 }
 spvtools_language_header("debuginfo") {
   name = "DebugInfo"
-  grammar_file = "${spirv_headers}/include/spirv/unified1/extinst.debuginfo.grammar.json"
+  grammar_file =
+      "${spirv_headers}/include/spirv/unified1/extinst.debuginfo.grammar.json"
 }
 spvtools_language_header("cldebuginfo100") {
   name = "OpenCLDebugInfo100"
@@ -288,13 +277,34 @@
 }
 
 spvtools_vendor_tables = [
-  ["spv-amd-shader-explicit-vertex-parameter", "...nil..."],
-  ["spv-amd-shader-trinary-minmax", "...nil..."],
-  ["spv-amd-gcn-shader", "...nil..."],
-  ["spv-amd-shader-ballot", "...nil..."],
-  ["debuginfo", "...nil..."],
-  ["opencl.debuginfo.100", "CLDEBUG100_"],
-  ["nonsemantic.clspvreflection", "...nil..."],
+  [
+    "spv-amd-shader-explicit-vertex-parameter",
+    "...nil...",
+  ],
+  [
+    "spv-amd-shader-trinary-minmax",
+    "...nil...",
+  ],
+  [
+    "spv-amd-gcn-shader",
+    "...nil...",
+  ],
+  [
+    "spv-amd-shader-ballot",
+    "...nil...",
+  ],
+  [
+    "debuginfo",
+    "...nil...",
+  ],
+  [
+    "opencl.debuginfo.100",
+    "CLDEBUG100_",
+  ],
+  [
+    "nonsemantic.clspvreflection",
+    "...nil...",
+  ],
 ]
 
 foreach(table_def, spvtools_vendor_tables) {
@@ -317,11 +327,15 @@
 
   configs = [ ":spvtools_public_config" ]
 
+  cflags = []
   if (is_clang) {
-    cflags = [
+    cflags += [
       "-Wno-implicit-fallthrough",
       "-Wno-newline-eof",
     ]
+  } else if (!is_win) {
+    # Work around a false-positive on a Skia GCC 10 builder.
+    cflags += [ "-Wno-format-truncation" ]
   }
 }
 
@@ -342,8 +356,8 @@
     ":spvtools_core_tables_unified1",
     ":spvtools_generators_inc",
     ":spvtools_glsl_tables_glsl1-0",
-    ":spvtools_language_header_debuginfo",
     ":spvtools_language_header_cldebuginfo100",
+    ":spvtools_language_header_debuginfo",
     ":spvtools_opencl_tables_opencl1-0",
   ]
   foreach(table_def, spvtools_vendor_tables) {
@@ -486,9 +500,7 @@
     ":spvtools_language_header_cldebuginfo100",
     ":spvtools_language_header_debuginfo",
   ]
-  public_deps = [
-    ":spvtools_headers",
-  ]
+  public_deps = [ ":spvtools_headers" ]
 
   if (build_with_chromium) {
     configs -= [ "//build/config/compiler:chromium_code" ]
@@ -539,10 +551,10 @@
     "source/opt/dead_insert_elim_pass.h",
     "source/opt/dead_variable_elimination.cpp",
     "source/opt/dead_variable_elimination.h",
-    "source/opt/decoration_manager.cpp",
-    "source/opt/decoration_manager.h",
     "source/opt/debug_info_manager.cpp",
     "source/opt/debug_info_manager.h",
+    "source/opt/decoration_manager.cpp",
+    "source/opt/decoration_manager.h",
     "source/opt/def_use_manager.cpp",
     "source/opt/def_use_manager.h",
     "source/opt/desc_sroa.cpp",
@@ -598,6 +610,8 @@
     "source/opt/instruction_list.h",
     "source/opt/instrument_pass.cpp",
     "source/opt/instrument_pass.h",
+    "source/opt/interp_fixup_pass.cpp",
+    "source/opt/interp_fixup_pass.h",
     "source/opt/ir_builder.h",
     "source/opt/ir_context.cpp",
     "source/opt/ir_context.h",
@@ -709,9 +723,7 @@
     ":spvtools_language_header_debuginfo",
     ":spvtools_vendor_tables_spv-amd-shader-ballot",
   ]
-  public_deps = [
-    ":spvtools_headers",
-  ]
+  public_deps = [ ":spvtools_headers" ]
 
   if (build_with_chromium) {
     configs -= [ "//build/config/compiler:chromium_code" ]
@@ -721,17 +733,13 @@
 }
 
 static_library("spvtools_link") {
-  sources = [
-    "source/link/linker.cpp",
-  ]
+  sources = [ "source/link/linker.cpp" ]
   deps = [
     ":spvtools",
     ":spvtools_opt",
     ":spvtools_val",
   ]
-  public_deps = [
-    ":spvtools_headers",
-  ]
+  public_deps = [ ":spvtools_headers" ]
   if (build_with_chromium) {
     configs -= [ "//build/config/compiler:chromium_code" ]
     configs += [ "//build/config/compiler:no_chromium_code" ]
@@ -804,9 +812,7 @@
     ":spvtools",
     ":spvtools_opt",
   ]
-  public_deps = [
-    ":spvtools_headers",
-  ]
+  public_deps = [ ":spvtools_headers" ]
   if (build_with_chromium) {
     configs -= [ "//build/config/compiler:chromium_code" ]
     configs += [ "//build/config/compiler:no_chromium_code" ]
@@ -841,10 +847,10 @@
       "test/comment_test.cpp",
       "test/enum_set_test.cpp",
       "test/enum_string_mapping_test.cpp",
+      "test/ext_inst.cldebug100_test.cpp",
       "test/ext_inst.debuginfo_test.cpp",
       "test/ext_inst.glsl_test.cpp",
       "test/ext_inst.opencl_test.cpp",
-      "test/ext_inst.cldebug100_test.cpp",
       "test/fix_word_test.cpp",
       "test/generator_magic_number_test.cpp",
       "test/hex_float_test.cpp",
@@ -891,8 +897,8 @@
 
     deps = [
       ":spvtools",
-      ":spvtools_language_header_debuginfo",
       ":spvtools_language_header_cldebuginfo100",
+      ":spvtools_language_header_debuginfo",
       ":spvtools_val",
       "//testing/gmock",
       "//testing/gtest",
@@ -912,9 +918,7 @@
 if (spirv_tools_standalone) {
   group("fuzzers") {
     testonly = true
-    deps = [
-      "test/fuzzers",
-    ]
+    deps = [ "test/fuzzers" ]
   }
 }
 
@@ -923,16 +927,12 @@
     "tools/util/cli_consumer.cpp",
     "tools/util/cli_consumer.h",
   ]
-  deps = [
-    ":spvtools_headers",
-  ]
+  deps = [ ":spvtools_headers" ]
   configs += [ ":spvtools_internal_config" ]
 }
 
 source_set("spvtools_software_version") {
-  sources = [
-    "source/software_version.cpp",
-  ]
+  sources = [ "source/software_version.cpp" ]
   deps = [
     ":spvtools_build_version",
     ":spvtools_headers",
@@ -941,9 +941,7 @@
 }
 
 executable("spirv-as") {
-  sources = [
-    "tools/as/as.cpp",
-  ]
+  sources = [ "tools/as/as.cpp" ]
   deps = [
     ":spvtools",
     ":spvtools_software_version",
@@ -952,9 +950,7 @@
 }
 
 executable("spirv-dis") {
-  sources = [
-    "tools/dis/dis.cpp",
-  ]
+  sources = [ "tools/dis/dis.cpp" ]
   deps = [
     ":spvtools",
     ":spvtools_software_version",
@@ -963,9 +959,7 @@
 }
 
 executable("spirv-val") {
-  sources = [
-    "tools/val/val.cpp",
-  ]
+  sources = [ "tools/val/val.cpp" ]
   deps = [
     ":spvtools",
     ":spvtools_software_version",
@@ -989,9 +983,7 @@
 }
 
 executable("spirv-opt") {
-  sources = [
-    "tools/opt/opt.cpp",
-  ]
+  sources = [ "tools/opt/opt.cpp" ]
   deps = [
     ":spvtools",
     ":spvtools_opt",
@@ -1003,9 +995,7 @@
 }
 
 executable("spirv-link") {
-  sources = [
-    "tools/link/linker.cpp",
-  ]
+  sources = [ "tools/link/linker.cpp" ]
   deps = [
     ":spvtools",
     ":spvtools_link",
@@ -1016,12 +1006,10 @@
   configs += [ ":spvtools_internal_config" ]
 }
 
-if (!is_ios) {
-  # iOS does not allow std::system calls which spirv-reduce requires
+if (!is_ios && !spirv_is_winuwp) {
+  # iOS and UWP do not allow std::system calls which spirv-reduce requires
   executable("spirv-reduce") {
-    sources = [
-      "tools/reduce/reduce.cpp",
-    ]
+    sources = [ "tools/reduce/reduce.cpp" ]
     deps = [
       ":spvtools",
       ":spvtools_opt",
@@ -1043,7 +1031,7 @@
     ":spirv-opt",
     ":spirv-val",
   ]
-  if (!is_ios) {
+  if (!is_ios && !spirv_is_winuwp) {
     deps += [ ":spirv-reduce" ]
   }
 }
diff --git a/CHANGES b/CHANGES
index d7a49f7..467655c 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,7 +1,92 @@
 Revision history for SPIRV-Tools
 
-v2020.7-dev 2020-12-07
- - Start v2020.7-dev
+v2021.3-dev 2021-06-22
+ - Start v2021.3-dev
+
+v2021.2 2021-06-18
+ - General
+    - Support SPV_KHR_subgroup_uniform_control_flow (#4318)
+    - Support Intel extensions for fixed point and hls-float (#4321)
+    - Fix crash when optimizing shaders with DebugPrintf (#4280)
+
+ - Validator
+    - Support Vulkan Storage Class for Execution Model (#4212)
+
+ - Optimizer
+    - Handle SPV_KHR_vulkan_memory_model in dead-code elimination (#4320)
+    - Support folding OpBitcast with numeric constants (#4247)
+
+ - Fuzz
+    - Add tests for MaybeGet* functions in fuzzerutil (#4284)
+    - Fix OutlineFunction in presence of unreachable blocks (#4308)
+    - Fix def-use update in PermutePhiOperands (#4309)
+    - Swap positions of two functions in a module (#4236)
+
+v2021.1 2021-04-19
+ - General
+    - Support SPV_KHR_linkonce_odr, SPV_KHR_expect_assume (#4161)
+    - Fixes for the vscode language server extension (#4150)
+ - Validator
+    - Add validation for SPV_EXT_shader_atomic_float_min_max (#4105)
+    - Add Vulkan Execution Scope checks (#4183)
+    - Vulkan 64-bit OpAtomicStore check (#4163)
+ - Optimizer
+    - Add interpolate legalization pass (#4220)
+ - Fuzz
+    - Various performance optimizations
+    - Do not add too many dead blocks (#4217)
+    - Add WGSL compatibility flag to context (#4193)
+    - Add persistent state to the fuzzer (#4137)
+
+v2020.7 2021-02-16
+ - General
+    - Support pending Intel extensions (#4116)
+    - Remove WebGPU support (#4108)
+ - Validator
+    - Vulkan image gather constant component (#4133)
+    - Add Vulkan PSB64 convert VUID (#4122)
+    - Validate SPV_KHR_workgroup_memory_explicit_layout (#4128)
+    - Validate VK_KHR_zero_initialize_workgroup_memory (#4124)
+    - Add Vulkan image gather offset VUID (#4118)
+    - Label Vulkan atomic semantics VUIDs (#4120)
+    - Label VUID 04662 (#4123)
+    - Label VUID 04683 (#4121)
+    - Add Vulkan EXT builtins (#4115)
+    - Validate Sampled=1 for Vulkan ImageQuerySizeLod, ImageQueryLevels, ImageQueryLod (#4103)
+    - Add Vulkan Memory Scope VUs (#4106)
+    - Add Vulkan Addressing Model check (#4107)
+    - Vulkan atomic storage class (#4079)
+    - Label standalone Vulkan VUID (#4091)
+    - Add Vulkan decroation VUID (#4090)
+    - Add Vulkan FP Mode VUID (#4088)
+    - Fix Vulkan image sampled check (#4085)
+    - Add Vulkan ForwardPointer VUID (#4089)
+    - Add Vulkan ImageTexelPointer format check (#4087)
+    - Add Vulkan Group Operation VUID (#4086)
+    - Add first StandAlone VUID 04633 (#4077)
+    - Add Subgroup VUIDs (#4074)
+    - validate return type of OpImageRead (#4072)
+    - tighter validation of multisampled images (#4059)
+    - validate OpTypeImage Sampled values for environemnts (#4064)
+    - validate StorageImageMultisampled capability (#4062)
+    - Add last TessLevelOuter and TessLevelInner VUID (#4055)
+    - Add last ClipDistance and CullDistance VUID (#4054)
+    - Add last ViewportIndex and Layer VUID (#4053)
+    - Add last Position VUID (#4052)
+    - Allow forward pointer to be used in types generally (#4044)
+ - Optimizer
+    - Mark module as modified if convert-to-half removes decorations (#4127)
+    - Fix binding number calculation in desc sroa (#4095)
+    - Run DCE when SPV_KHR_shader_clock is used (#4049)
+ - Debug Info
+    - Set correct scope and line info for DebugValue (#4125)
+    - Avoid integrity check failures caused by propagating line instructions (#4096)
+ - Linker
+    - Linker usability improvements (#4084)
+ - Instrumentation
+    - Generate differentiated error codes for buffer oob checking (#4097)
+ - Fuzz
+    - Fix OpPhi handling in DuplicateRegionWithSelection (#4065)
 
 v2020.6 2020-12-07
  - General
diff --git a/DEPS b/DEPS
index 75581c5..5f64eca 100644
--- a/DEPS
+++ b/DEPS
@@ -4,9 +4,9 @@
   'github': 'https://github.com',
 
   'effcee_revision': '2ec8f8738118cc483b67c04a759fee53496c5659',
-  'googletest_revision': '3af06fe1664d30f98de1e78c53a7087e842a2547',
-  're2_revision': 'ca11026a032ce2a3de4b3c389ee53d2bdc8794d6',
-  'spirv_headers_revision': 'faa570afbc91ac73d594d787486bcf8f2df1ace0',
+  'googletest_revision': 'b7d472f1225c5a64943821d8483fecb469d3f382',
+  're2_revision': 'f8e389f3acdc2517562924239e2a188037393683',
+  'spirv_headers_revision': 'f95c3b3761ee1b1903f54ae69b526ed6f0edc3b9',
 }
 
 deps = {
diff --git a/docs/spirv-fuzz.md b/docs/spirv-fuzz.md
index e5439e3..5716b35 100644
--- a/docs/spirv-fuzz.md
+++ b/docs/spirv-fuzz.md
@@ -53,7 +53,7 @@
 
 The `IsApplicable` method should have a comment in the header file describing the conditions for applicability in simple terms.  These conditions should be implemented in the body of this method in the `.cpp` file.
 
-In the case of `TransformationSetSelectionControl`, `IsApplicable` involves checking that `block_id` is indeed the id of a block that has an `OpSelectoinMerge` instruction, and that `selection_control` is a valid selection mask.
+In the case of `TransformationSetSelectionControl`, `IsApplicable` involves checking that `block_id` is indeed the id of a block that has an `OpSelectionMerge` instruction, and that `selection_control` is a valid selection mask.
 
 The `Apply` method should have a comment in the header file summarising the result of applying the transformation.  It should be implemented in the `.cpp` file, and you should assume that `IsApplicable` holds whenever `Apply` is invoked.
 
diff --git a/include/spirv-tools/instrument.hpp b/include/spirv-tools/instrument.hpp
index 2b47a56..a19491f 100644
--- a/include/spirv-tools/instrument.hpp
+++ b/include/spirv-tools/instrument.hpp
@@ -170,11 +170,15 @@
 static const int kInstErrorBindlessBounds = 0;
 static const int kInstErrorBindlessUninit = 1;
 static const int kInstErrorBuffAddrUnallocRef = 2;
-static const int kInstErrorBindlessBuffOOB = 3;
+// Deleted: static const int kInstErrorBindlessBuffOOB = 3;
+// This comment will will remain for 2 releases to allow
+// for the transition of all builds. Buffer OOB is
+// generating the following four differentiated codes instead:
 static const int kInstErrorBuffOOBUniform = 4;
 static const int kInstErrorBuffOOBStorage = 5;
 static const int kInstErrorBuffOOBUniformTexel = 6;
 static const int kInstErrorBuffOOBStorageTexel = 7;
+static const int kInstErrorMax = kInstErrorBuffOOBStorageTexel;
 
 // Direct Input Buffer Offsets
 //
diff --git a/include/spirv-tools/libspirv.h b/include/spirv-tools/libspirv.h
index 658c4dd..039dab1 100644
--- a/include/spirv-tools/libspirv.h
+++ b/include/spirv-tools/libspirv.h
@@ -113,6 +113,9 @@
 // Sometimes we also need to be able to express the fact that an operand
 // is a member of an optional tuple of values.  In that case the first member
 // would be optional, and the subsequent members would be required.
+//
+// NOTE: Although we don't promise binary compatibility, as a courtesy, please
+// add new enum values at the end.
 typedef enum spv_operand_type_t {
   // A sentinel value.
   SPV_OPERAND_TYPE_NONE = 0,
@@ -167,12 +170,8 @@
   SPV_OPERAND_TYPE_KERNEL_ENQ_FLAGS,              // SPIR-V Sec 3.29
   SPV_OPERAND_TYPE_KERNEL_PROFILING_INFO,         // SPIR-V Sec 3.30
   SPV_OPERAND_TYPE_CAPABILITY,                    // SPIR-V Sec 3.31
-  SPV_OPERAND_TYPE_RAY_FLAGS,                     // SPIR-V Sec 3.RF
-  SPV_OPERAND_TYPE_RAY_QUERY_INTERSECTION,        // SPIR-V Sec 3.RQIntersection
-  SPV_OPERAND_TYPE_RAY_QUERY_COMMITTED_INTERSECTION_TYPE,  // SPIR-V Sec
-                                                           // 3.RQCommitted
-  SPV_OPERAND_TYPE_RAY_QUERY_CANDIDATE_INTERSECTION_TYPE,  // SPIR-V Sec
-                                                           // 3.RQCandidate
+
+  // NOTE: New concrete enum values should be added at the end.
 
   // Set 5:  Operands that are a single word bitmask.
   // Sometimes a set bit indicates the instruction requires still more operands.
@@ -184,7 +183,10 @@
   SPV_OPERAND_TYPE_MEMORY_ACCESS,          // SPIR-V Sec 3.26
   SPV_OPERAND_TYPE_FRAGMENT_SHADING_RATE,  // SPIR-V Sec 3.FSR
 
-// The remaining operand types are only used internally by the assembler.
+// NOTE: New concrete enum values should be added at the end.
+
+// The "optional" and "variable"  operand types are only used internally by
+// the assembler and the binary parser.
 // There are two categories:
 //    Optional : expands to 0 or 1 operand, like ? in regular expressions.
 //    Variable : expands to 0, 1 or many operands or pairs of operands.
@@ -264,6 +266,24 @@
   // https://github.com/intel/llvm/blob/39fa9b0cbfbae88327118990a05c5b387b56d2ef/sycl/doc/extensions/SPIRV/SPV_INTEL_float_controls2.asciidoc
   SPV_OPERAND_TYPE_FPDENORM_MODE,     // Sec 3.17 FP Denorm Mode
   SPV_OPERAND_TYPE_FPOPERATION_MODE,  // Sec 3.18 FP Operation Mode
+  // A value enum from https://github.com/KhronosGroup/SPIRV-Headers/pull/177
+  SPV_OPERAND_TYPE_QUANTIZATION_MODES,
+  // A value enum from https://github.com/KhronosGroup/SPIRV-Headers/pull/177
+  SPV_OPERAND_TYPE_OVERFLOW_MODES,
+
+  // Concrete operand types for the provisional Vulkan ray tracing feature.
+  SPV_OPERAND_TYPE_RAY_FLAGS,               // SPIR-V Sec 3.RF
+  SPV_OPERAND_TYPE_RAY_QUERY_INTERSECTION,  // SPIR-V Sec 3.RQIntersection
+  SPV_OPERAND_TYPE_RAY_QUERY_COMMITTED_INTERSECTION_TYPE,  // SPIR-V Sec
+                                                           // 3.RQCommitted
+  SPV_OPERAND_TYPE_RAY_QUERY_CANDIDATE_INTERSECTION_TYPE,  // SPIR-V Sec
+                                                           // 3.RQCandidate
+
+  // Concrete operand types for integer dot product.
+  // Packed vector format
+  SPV_OPERAND_TYPE_PACKED_VECTOR_FORMAT,  // SPIR-V Sec 3.x
+  // An optional packed vector format
+  SPV_OPERAND_TYPE_OPTIONAL_PACKED_VECTOR_FORMAT,
 
   // This is a sentinel value, and does not represent an operand type.
   // It should come last.
@@ -588,6 +608,8 @@
 // 3) Pointers that are actaul parameters on function calls do not have to point
 //    to the same type pointed as the formal parameter.  The types just need to
 //    logically match.
+// 4) GLSLstd450 Interpolate* instructions can have a load of an interpolant
+//    for a first argument.
 SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetBeforeHlslLegalization(
     spv_validator_options options, bool val);
 
@@ -671,7 +693,7 @@
 // Creates a reducer options object with default options. Returns a valid
 // options object. The object remains valid until it is passed into
 // |spvReducerOptionsDestroy|.
-SPIRV_TOOLS_EXPORT spv_reducer_options spvReducerOptionsCreate();
+SPIRV_TOOLS_EXPORT spv_reducer_options spvReducerOptionsCreate(void);
 
 // Destroys the given reducer options object.
 SPIRV_TOOLS_EXPORT void spvReducerOptionsDestroy(spv_reducer_options options);
@@ -698,7 +720,7 @@
 // Creates a fuzzer options object with default options. Returns a valid
 // options object. The object remains valid until it is passed into
 // |spvFuzzerOptionsDestroy|.
-SPIRV_TOOLS_EXPORT spv_fuzzer_options spvFuzzerOptionsCreate();
+SPIRV_TOOLS_EXPORT spv_fuzzer_options spvFuzzerOptionsCreate(void);
 
 // Destroys the given fuzzer options object.
 SPIRV_TOOLS_EXPORT void spvFuzzerOptionsDestroy(spv_fuzzer_options options);
diff --git a/include/spirv-tools/libspirv.hpp b/include/spirv-tools/libspirv.hpp
index e7e7fc7..0c31a18 100644
--- a/include/spirv-tools/libspirv.hpp
+++ b/include/spirv-tools/libspirv.hpp
@@ -136,6 +136,8 @@
   // 3) Pointers that are actaul parameters on function calls do not have to
   //    point to the same type pointed as the formal parameter.  The types just
   //    need to logically match.
+  // 4) GLSLstd450 Interpolate* instructions can have a load of an interpolant
+  //    for a first argument.
   void SetBeforeHlslLegalization(bool val) {
     spvValidatorOptionsSetBeforeHlslLegalization(options_, val);
   }
diff --git a/include/spirv-tools/optimizer.hpp b/include/spirv-tools/optimizer.hpp
index 1683d07..e8b5b69 100644
--- a/include/spirv-tools/optimizer.hpp
+++ b/include/spirv-tools/optimizer.hpp
@@ -838,6 +838,15 @@
 // capabilities.
 Optimizer::PassToken CreateAmdExtToKhrPass();
 
+// Replaces the internal version of GLSLstd450 InterpolateAt* extended
+// instructions with the externally valid version. The internal version allows
+// an OpLoad of the interpolant for the first argument. This pass removes the
+// OpLoad and replaces it with its pointer. glslang and possibly other
+// frontends will create the internal version for HLSL. This pass will be part
+// of HLSL legalization and should be called after interpolants have been
+// propagated into their final positions.
+Optimizer::PassToken CreateInterpolateFixupPass();
+
 }  // namespace spvtools
 
 #endif  // INCLUDE_SPIRV_TOOLS_OPTIMIZER_HPP_
diff --git a/kokoro/check-format/build.sh b/kokoro/check-format/build.sh
index 0c4b8d1..8a5df9a 100644
--- a/kokoro/check-format/build.sh
+++ b/kokoro/check-format/build.sh
@@ -35,7 +35,8 @@
 cd external && cd googletest && git reset --hard 1fb1bb23bb8418dc73a5a9a82bbed31dc610fec7 && cd .. && cd ..
 git clone --depth=1 https://github.com/google/effcee              external/effcee
 git clone --depth=1 https://github.com/google/re2                 external/re2
-curl -L http://llvm.org/svn/llvm-project/cfe/trunk/tools/clang-format/clang-format-diff.py -o utils/clang-format-diff.py;
+# The --fail flag causes the command to fail on HTTP error response codes, like 404.
+curl -L --fail https://raw.githubusercontent.com/llvm/llvm-project/main/clang/tools/clang-format/clang-format-diff.py -o utils/clang-format-diff.py
 
 echo $(date): Check formatting...
 ./utils/check_code_format.sh;
diff --git a/kokoro/scripts/windows/build.bat b/kokoro/scripts/windows/build.bat
index a4ce792..c964320 100644
--- a/kokoro/scripts/windows/build.bat
+++ b/kokoro/scripts/windows/build.bat
@@ -41,9 +41,6 @@
 ) else if %VS_VERSION% == 2015 (
   call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x64
   echo "Using VS 2015..."
-) else if %VS_VERSION% == 2013 (
-  call "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" x64
-  echo "Using VS 2013..."
 )
 
 cd %SRC%
@@ -62,15 +59,8 @@
 
 set CMAKE_FLAGS=-DCMAKE_INSTALL_PREFIX=%KOKORO_ARTIFACTS_DIR%\install -GNinja -DCMAKE_BUILD_TYPE=%BUILD_TYPE% -DRE2_BUILD_TESTING=OFF -DCMAKE_C_COMPILER=cl.exe -DCMAKE_CXX_COMPILER=cl.exe
 
-:: Skip building tests for VS2013
-if %VS_VERSION% == 2013 (
-  set CMAKE_FLAGS=%CMAKE_FLAGS% -DSPIRV_SKIP_TESTS=ON
-)
-
-:: Skip building spirv-fuzz for VS2013; it relies on protobufs which VS2013 cannot handle.
-if %VS_VERSION% NEQ 2013 (
-  set CMAKE_FLAGS=%CMAKE_FLAGS% -DSPIRV_BUILD_FUZZER=ON
-)
+:: Build spirv-fuzz
+set CMAKE_FLAGS=%CMAKE_FLAGS% -DSPIRV_BUILD_FUZZER=ON
 
 cmake %CMAKE_FLAGS% ..
 
@@ -85,13 +75,11 @@
 setlocal ENABLEDELAYEDEXPANSION
 
 :: ################################################
-:: Run the tests (We no longer run tests on VS2013)
+:: Run the tests
 :: ################################################
 echo "Running Tests... %DATE% %TIME%"
-if %VS_VERSION% NEQ 2013 (
-  ctest -C %BUILD_TYPE% --output-on-failure --timeout 300
-  if !ERRORLEVEL! NEQ 0 exit /b !ERRORLEVEL!
-)
+ctest -C %BUILD_TYPE% --output-on-failure --timeout 300
+if !ERRORLEVEL! NEQ 0 exit /b !ERRORLEVEL!
 echo "Tests Completed %DATE% %TIME%"
 
 :: ################################################
@@ -105,5 +93,4 @@
 rm -rf %SRC%\build
 rm -rf %SRC%\external
 
-exit /b 0
-
+exit /b 0
\ No newline at end of file
diff --git a/kokoro/windows-msvc-2013-release/continuous.cfg b/kokoro/windows-msvc-2013-release/continuous.cfg
deleted file mode 100644
index 5dfcba6..0000000
--- a/kokoro/windows-msvc-2013-release/continuous.cfg
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright (c) 2018 Google LLC.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Continuous build configuration.
-build_file: "SPIRV-Tools/kokoro/windows-msvc-2013-release/build.bat"
diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt
index 65087f2..6633bc9 100644
--- a/source/CMakeLists.txt
+++ b/source/CMakeLists.txt
@@ -359,7 +359,7 @@
   )
   set_property(TARGET ${target} PROPERTY FOLDER "SPIRV-Tools libraries")
   spvtools_check_symbol_exports(${target})
-  add_dependencies(${target} core_tables enum_string_mapping extinst_tables)
+  add_dependencies(${target} spirv-tools-build-version core_tables enum_string_mapping extinst_tables)
 endfunction()
 
 # Always build ${SPIRV_TOOLS}-shared. This is expected distro packages, and
@@ -402,6 +402,12 @@
   endif()
 endif()
 
+if (ANDROID)
+    foreach(target ${SPIRV_TOOLS_TARGETS})
+        target_link_libraries(${target} PRIVATE android log)
+    endforeach()
+endif()
+
 if(ENABLE_SPIRV_TOOLS_INSTALL)
   install(TARGETS ${SPIRV_TOOLS_TARGETS} EXPORT ${SPIRV_TOOLS}Targets
     RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
diff --git a/source/binary.cpp b/source/binary.cpp
index 7448721..090cccf 100644
--- a/source/binary.cpp
+++ b/source/binary.cpp
@@ -657,12 +657,18 @@
     case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_OPERATION:
     case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_IMPORTED_ENTITY:
     case SPV_OPERAND_TYPE_FPDENORM_MODE:
-    case SPV_OPERAND_TYPE_FPOPERATION_MODE: {
+    case SPV_OPERAND_TYPE_FPOPERATION_MODE:
+    case SPV_OPERAND_TYPE_QUANTIZATION_MODES:
+    case SPV_OPERAND_TYPE_OVERFLOW_MODES:
+    case SPV_OPERAND_TYPE_PACKED_VECTOR_FORMAT:
+    case SPV_OPERAND_TYPE_OPTIONAL_PACKED_VECTOR_FORMAT: {
       // A single word that is a plain enum value.
 
       // Map an optional operand type to its corresponding concrete type.
       if (type == SPV_OPERAND_TYPE_OPTIONAL_ACCESS_QUALIFIER)
         parsed_operand.type = SPV_OPERAND_TYPE_ACCESS_QUALIFIER;
+      if (type == SPV_OPERAND_TYPE_OPTIONAL_PACKED_VECTOR_FORMAT)
+        parsed_operand.type = SPV_OPERAND_TYPE_PACKED_VECTOR_FORMAT;
 
       spv_operand_desc entry;
       if (grammar_.lookupOperand(type, word, &entry)) {
diff --git a/source/disassemble.cpp b/source/disassemble.cpp
index 966a59c..c553988 100644
--- a/source/disassemble.cpp
+++ b/source/disassemble.cpp
@@ -328,7 +328,9 @@
     case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_OPERATION:
     case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_IMPORTED_ENTITY:
     case SPV_OPERAND_TYPE_FPDENORM_MODE:
-    case SPV_OPERAND_TYPE_FPOPERATION_MODE: {
+    case SPV_OPERAND_TYPE_FPOPERATION_MODE:
+    case SPV_OPERAND_TYPE_QUANTIZATION_MODES:
+    case SPV_OPERAND_TYPE_OVERFLOW_MODES: {
       spv_operand_desc entry;
       if (grammar_.lookupOperand(operand.type, word, &entry))
         assert(false && "should have caught this earlier");
@@ -345,7 +347,17 @@
       EmitMaskOperand(operand.type, word);
       break;
     default:
-      assert(false && "unhandled or invalid case");
+      if (spvOperandIsConcreteMask(operand.type)) {
+        EmitMaskOperand(operand.type, word);
+      } else if (spvOperandIsConcrete(operand.type)) {
+        spv_operand_desc entry;
+        if (grammar_.lookupOperand(operand.type, word, &entry))
+          assert(false && "should have caught this earlier");
+        stream_ << entry->name;
+      } else {
+        assert(false && "unhandled or invalid case");
+      }
+      break;
   }
   ResetColor();
 }
diff --git a/source/fuzz/CMakeLists.txt b/source/fuzz/CMakeLists.txt
index d3aa9f1..a806166 100644
--- a/source/fuzz/CMakeLists.txt
+++ b/source/fuzz/CMakeLists.txt
@@ -37,6 +37,7 @@
 
   set(SPIRV_TOOLS_FUZZ_SOURCES
         added_function_reducer.h
+        available_instructions.h
         call_graph.h
         comparator_deep_blocks_first.h
         counter_overflow_id_source.h
@@ -100,6 +101,7 @@
         fuzzer_pass_outline_functions.h
         fuzzer_pass_permute_blocks.h
         fuzzer_pass_permute_function_parameters.h
+        fuzzer_pass_permute_function_variables.h
         fuzzer_pass_permute_instructions.h
         fuzzer_pass_permute_phi_operands.h
         fuzzer_pass_propagate_instructions_down.h
@@ -119,6 +121,7 @@
         fuzzer_pass_split_blocks.h
         fuzzer_pass_swap_commutable_operands.h
         fuzzer_pass_swap_conditional_branch_operands.h
+        fuzzer_pass_swap_functions.h
         fuzzer_pass_toggle_access_chain_instruction.h
         fuzzer_pass_wrap_regions_in_selections.h
         fuzzer_util.h
@@ -221,6 +224,8 @@
         transformation_store.h
         transformation_swap_commutable_operands.h
         transformation_swap_conditional_branch_operands.h
+        transformation_swap_function_variables.h
+        transformation_swap_two_functions.h
         transformation_toggle_access_chain_instruction.h
         transformation_vector_shuffle.h
         transformation_wrap_early_terminator_in_function.h
@@ -229,6 +234,7 @@
         ${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.h
 
         added_function_reducer.cpp
+        available_instructions.cpp
         call_graph.cpp
         counter_overflow_id_source.cpp
         data_descriptor.cpp
@@ -290,6 +296,7 @@
         fuzzer_pass_outline_functions.cpp
         fuzzer_pass_permute_blocks.cpp
         fuzzer_pass_permute_function_parameters.cpp
+        fuzzer_pass_permute_function_variables.cpp
         fuzzer_pass_permute_instructions.cpp
         fuzzer_pass_permute_phi_operands.cpp
         fuzzer_pass_propagate_instructions_down.cpp
@@ -309,6 +316,7 @@
         fuzzer_pass_split_blocks.cpp
         fuzzer_pass_swap_commutable_operands.cpp
         fuzzer_pass_swap_conditional_branch_operands.cpp
+        fuzzer_pass_swap_functions.cpp
         fuzzer_pass_toggle_access_chain_instruction.cpp
         fuzzer_pass_wrap_regions_in_selections.cpp
         fuzzer_util.cpp
@@ -409,6 +417,8 @@
         transformation_store.cpp
         transformation_swap_commutable_operands.cpp
         transformation_swap_conditional_branch_operands.cpp
+        transformation_swap_function_variables.cpp
+        transformation_swap_two_functions.cpp
         transformation_toggle_access_chain_instruction.cpp
         transformation_vector_shuffle.cpp
         transformation_wrap_early_terminator_in_function.cpp
diff --git a/source/fuzz/available_instructions.cpp b/source/fuzz/available_instructions.cpp
new file mode 100644
index 0000000..e25ed90
--- /dev/null
+++ b/source/fuzz/available_instructions.cpp
@@ -0,0 +1,191 @@
+// Copyright (c) 2021 Alastair F. Donaldson
+//
+// 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/available_instructions.h"
+#include "source/fuzz/fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+AvailableInstructions::AvailableInstructions(
+    opt::IRContext* ir_context,
+    const std::function<bool(opt::IRContext*, opt::Instruction*)>& predicate)
+    : ir_context_(ir_context) {
+  // Consider all global declarations
+  for (auto& global : ir_context->module()->types_values()) {
+    if (predicate(ir_context, &global)) {
+      available_globals_.push_back(&global);
+    }
+  }
+
+  // Consider every function
+  for (auto& function : *ir_context->module()) {
+    // Identify those function parameters that satisfy the predicate.
+    std::vector<opt::Instruction*> available_params_for_function;
+    function.ForEachParam(
+        [&predicate, ir_context,
+         &available_params_for_function](opt::Instruction* param) {
+          if (predicate(ir_context, param)) {
+            available_params_for_function.push_back(param);
+          }
+        });
+
+    // Consider every reachable block in the function.
+    auto dominator_analysis = ir_context->GetDominatorAnalysis(&function);
+    for (auto& block : function) {
+      if (!fuzzerutil::BlockIsReachableInItsFunction(ir_context, &block)) {
+        // The block is not reachable.
+        continue;
+      }
+      if (&block == &*function.begin()) {
+        // The function entry block is special: only the relevant globals and
+        // function parameters are available at its entry point.
+        num_available_at_block_entry_.insert(
+            {&block,
+             static_cast<uint32_t>(available_params_for_function.size() +
+                                   available_globals_.size())});
+      } else {
+        // |block| is not the entry block and is reachable, so it must have an
+        // immediate dominator. The number of instructions available on entry to
+        // |block| is thus the number of instructions available on entry to the
+        // immediate dominator + the number of instructions generated_by_block
+        // by the immediate dominator.
+        auto immediate_dominator =
+            dominator_analysis->ImmediateDominator(&block);
+        assert(immediate_dominator != nullptr &&
+               "The block is reachable so should have an immediate dominator.");
+        assert(generated_by_block_.count(immediate_dominator) != 0 &&
+               "Immediate dominator should have already been processed.");
+        assert(num_available_at_block_entry_.count(immediate_dominator) != 0 &&
+               "Immediate dominator should have already been processed.");
+        num_available_at_block_entry_.insert(
+            {&block,
+             static_cast<uint32_t>(
+                 generated_by_block_.at(immediate_dominator).size()) +
+                 num_available_at_block_entry_.at(immediate_dominator)});
+      }
+      // Now consider each instruction in the block.
+      std::vector<opt::Instruction*> generated_by_block;
+      for (auto& inst : block) {
+        assert(num_available_at_block_entry_.count(&block) != 0 &&
+               "Block should have already been processed.");
+        // The number of available instructions before |inst| is the number
+        // available at the start of the block + the number of relevant
+        // instructions generated by the block so far.
+        num_available_before_instruction_.insert(
+            {&inst, num_available_at_block_entry_.at(&block) +
+                        static_cast<uint32_t>(generated_by_block.size())});
+        if (predicate(ir_context, &inst)) {
+          // This instruction satisfies the predicate, so note that it is
+          // generated by |block|.
+          generated_by_block.push_back(&inst);
+        }
+      }
+      generated_by_block_.emplace(&block, std::move(generated_by_block));
+    }
+    available_params_.emplace(&function,
+                              std::move(available_params_for_function));
+  }
+}
+
+AvailableInstructions::AvailableBeforeInstruction
+AvailableInstructions::GetAvailableBeforeInstruction(
+    opt::Instruction* inst) const {
+  assert(num_available_before_instruction_.count(inst) != 0 &&
+         "Availability can only be queried for reachable instructions.");
+  return {*this, inst};
+}
+
+AvailableInstructions::AvailableBeforeInstruction::AvailableBeforeInstruction(
+    const AvailableInstructions& available_instructions, opt::Instruction* inst)
+    : available_instructions_(available_instructions), inst_(inst) {}
+
+uint32_t AvailableInstructions::AvailableBeforeInstruction::size() const {
+  return available_instructions_.num_available_before_instruction_.at(inst_);
+}
+
+bool AvailableInstructions::AvailableBeforeInstruction::empty() const {
+  return size() == 0;
+}
+
+opt::Instruction* AvailableInstructions::AvailableBeforeInstruction::operator[](
+    uint32_t index) const {
+  assert(index < size() && "Index out of bounds.");
+
+  // First, check the cache to see whether we can return the available
+  // instruction in constant time.
+  auto cached_result = index_cache.find(index);
+  if (cached_result != index_cache.end()) {
+    return cached_result->second;
+  }
+
+  // Next check whether the index falls into the global region.
+  if (index < available_instructions_.available_globals_.size()) {
+    auto result = available_instructions_.available_globals_[index];
+    index_cache.insert({index, result});
+    return result;
+  }
+
+  auto block = available_instructions_.ir_context_->get_instr_block(inst_);
+  auto function = block->GetParent();
+
+  // Next check whether the index falls into the available instructions that
+  // correspond to function parameters.
+  if (index <
+      available_instructions_.available_globals_.size() +
+          available_instructions_.available_params_.at(function).size()) {
+    auto result = available_instructions_.available_params_.at(
+        function)[index - available_instructions_.available_globals_.size()];
+    index_cache.insert({index, result});
+    return result;
+  }
+
+  auto dominator_analysis =
+      available_instructions_.ir_context_->GetDominatorAnalysis(function);
+
+  // Now the expensive part (which is why we have the cache): walk the dominator
+  // tree backwards starting from the block containing |inst_| until we get to
+  // the block in which the instruction corresponding to |index| exists.
+  for (auto* ancestor = block; true;
+       ancestor = dominator_analysis->ImmediateDominator(ancestor)) {
+    uint32_t num_available_at_ancestor_entry =
+        available_instructions_.num_available_at_block_entry_.at(ancestor);
+    if (index_cache.count(num_available_at_ancestor_entry) == 0) {
+      // This is the first time we have traversed this block, so we populate the
+      // cache with the index of each instruction, so that if a future index
+      // query relates to indices associated with this block we can return the
+      // result in constant time.
+      auto& generated_by_ancestor =
+          available_instructions_.generated_by_block_.at(ancestor);
+      for (uint32_t local_index = 0; local_index < generated_by_ancestor.size();
+           local_index++) {
+        index_cache.insert({num_available_at_ancestor_entry + local_index,
+                            generated_by_ancestor[local_index]});
+      }
+    }
+    if (index >= num_available_at_ancestor_entry) {
+      // This block contains the instruction we want, so by now it will be in
+      // the cache.
+      return index_cache.at(index);
+    }
+    assert(ancestor != &*function->begin() &&
+           "By construction we should find a block associated with the index.");
+  }
+
+  assert(false && "Unreachable.");
+  return nullptr;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/source/fuzz/available_instructions.h b/source/fuzz/available_instructions.h
new file mode 100644
index 0000000..5c0b417
--- /dev/null
+++ b/source/fuzz/available_instructions.h
@@ -0,0 +1,111 @@
+// Copyright (c) 2021 Alastair F. Donaldson
+//
+// 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_AVAILABLE_INSTRUCTIONS_H_
+#define SOURCE_FUZZ_AVAILABLE_INSTRUCTIONS_H_
+
+#include <unordered_map>
+#include <vector>
+
+#include "source/opt/instruction.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// A class for allowing efficient querying of the instruction that satisfy a
+// particular predicate that are available before a given instruction.
+// Availability information is only computed for instructions in *reachable*
+// basic blocks.
+class AvailableInstructions {
+ public:
+  // The outer class captures availability information for a whole module, and
+  // each instance of this inner class captures availability for a particular
+  // instruction.
+  class AvailableBeforeInstruction {
+   public:
+    AvailableBeforeInstruction(
+        const AvailableInstructions& available_instructions,
+        opt::Instruction* inst);
+
+    // Returns the number of instructions that are available before the
+    // instruction associated with this class.
+    uint32_t size() const;
+
+    // Returns true if and only if |size()| is 0.
+    bool empty() const;
+
+    // Requires |index| < |size()|. Returns the ith available instruction.
+    opt::Instruction* operator[](uint32_t index) const;
+
+   private:
+    // A references to an instance of the outer class.
+    const AvailableInstructions& available_instructions_;
+
+    // The instruction for which availability information is captured.
+    opt::Instruction* inst_;
+
+    // A cache to improve the efficiency of the [] operator. The [] operator
+    // requires walking the instruction's dominator tree to find an instruction
+    // at a particular index, which is a linear time operation. By inserting all
+    // instructions that are traversed during this search into a cache, future
+    // lookups will take constant time unless they require traversing the
+    // dominator tree more deeply.
+    mutable std::unordered_map<uint32_t, opt::Instruction*> index_cache;
+  };
+
+  // Constructs availability instructions for |ir_context|, where instructions
+  // are only available if they satisfy |predicate|.
+  AvailableInstructions(
+      opt::IRContext* ir_context,
+      const std::function<bool(opt::IRContext*, opt::Instruction*)>& predicate);
+
+  // Yields instruction availability for |inst|.
+  AvailableBeforeInstruction GetAvailableBeforeInstruction(
+      opt::Instruction* inst) const;
+
+ private:
+  // The module in which all instructions are contained.
+  opt::IRContext* ir_context_;
+
+  // The global instructions that satisfy the predicate.
+  std::vector<opt::Instruction*> available_globals_;
+
+  // Per function, the parameters that satisfy the predicate.
+  std::unordered_map<opt::Function*, std::vector<opt::Instruction*>>
+      available_params_;
+
+  // The number of instructions that satisfy the predicate and that are
+  // available at the entry to a block. For the entry block of a function this
+  // is the number of available globals + the number of available function
+  // parameters. For any other block it is the number of available instructions
+  // for the blocks immediate dominator + the number of instructions generated
+  // by the immediate dominator.
+  std::unordered_map<opt::BasicBlock*, uint32_t> num_available_at_block_entry_;
+
+  // For each block this records those instructions in the block that satisfy
+  // the predicate.
+  std::unordered_map<opt::BasicBlock*, std::vector<opt::Instruction*>>
+      generated_by_block_;
+
+  // For each instruction this records how many instructions satisfying the
+  // predicate are available before the instruction.
+  std::unordered_map<opt::Instruction*, uint32_t>
+      num_available_before_instruction_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_AVAILABLE_INSTRUCTIONS_H_
diff --git a/source/fuzz/force_render_red.cpp b/source/fuzz/force_render_red.cpp
index fd0587a..3267487 100644
--- a/source/fuzz/force_render_red.cpp
+++ b/source/fuzz/force_render_red.cpp
@@ -19,12 +19,10 @@
 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
 #include "source/fuzz/transformation_context.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"
 
 namespace spvtools {
 namespace fuzz {
@@ -160,8 +158,8 @@
     const spv_target_env& target_env, spv_validator_options validator_options,
     const std::vector<uint32_t>& binary_in,
     const spvtools::fuzz::protobufs::FactSequence& initial_facts,
+    const MessageConsumer& message_consumer,
     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, {},
diff --git a/source/fuzz/force_render_red.h b/source/fuzz/force_render_red.h
index b51c72b..5b8eab1 100644
--- a/source/fuzz/force_render_red.h
+++ b/source/fuzz/force_render_red.h
@@ -41,7 +41,7 @@
     const spv_target_env& target_env, spv_validator_options validator_options,
     const std::vector<uint32_t>& binary_in,
     const spvtools::fuzz::protobufs::FactSequence& initial_facts,
-    std::vector<uint32_t>* binary_out);
+    const MessageConsumer& message_consumer, std::vector<uint32_t>* binary_out);
 
 }  // namespace fuzz
 }  // namespace spvtools
diff --git a/source/fuzz/fuzzer.cpp b/source/fuzz/fuzzer.cpp
index 40da497..fe88a55 100644
--- a/source/fuzz/fuzzer.cpp
+++ b/source/fuzz/fuzzer.cpp
@@ -17,9 +17,7 @@
 #include <cassert>
 #include <memory>
 #include <numeric>
-#include <sstream>
 
-#include "source/fuzz/fact_manager/fact_manager.h"
 #include "source/fuzz/fuzzer_context.h"
 #include "source/fuzz/fuzzer_pass_add_access_chains.h"
 #include "source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.h"
@@ -69,6 +67,7 @@
 #include "source/fuzz/fuzzer_pass_outline_functions.h"
 #include "source/fuzz/fuzzer_pass_permute_blocks.h"
 #include "source/fuzz/fuzzer_pass_permute_function_parameters.h"
+#include "source/fuzz/fuzzer_pass_permute_function_variables.h"
 #include "source/fuzz/fuzzer_pass_permute_instructions.h"
 #include "source/fuzz/fuzzer_pass_permute_phi_operands.h"
 #include "source/fuzz/fuzzer_pass_propagate_instructions_down.h"
@@ -88,12 +87,10 @@
 #include "source/fuzz/fuzzer_pass_split_blocks.h"
 #include "source/fuzz/fuzzer_pass_swap_commutable_operands.h"
 #include "source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h"
+#include "source/fuzz/fuzzer_pass_swap_functions.h"
 #include "source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h"
 #include "source/fuzz/fuzzer_pass_wrap_regions_in_selections.h"
 #include "source/fuzz/pass_management/repeated_pass_manager.h"
-#include "source/fuzz/pass_management/repeated_pass_manager_looped_with_recommendations.h"
-#include "source/fuzz/pass_management/repeated_pass_manager_random_with_recommendations.h"
-#include "source/fuzz/pass_management/repeated_pass_manager_simple.h"
 #include "source/fuzz/pass_management/repeated_pass_recommender_standard.h"
 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
 #include "source/fuzz/transformation_context.h"
@@ -104,35 +101,149 @@
 namespace spvtools {
 namespace fuzz {
 
-namespace {
-const uint32_t kIdBoundGap = 100;
-
-}  // namespace
-
-Fuzzer::Fuzzer(spv_target_env target_env, MessageConsumer consumer,
-               const std::vector<uint32_t>& binary_in,
-               const protobufs::FactSequence& initial_facts,
+Fuzzer::Fuzzer(std::unique_ptr<opt::IRContext> ir_context,
+               std::unique_ptr<TransformationContext> transformation_context,
+               std::unique_ptr<FuzzerContext> fuzzer_context,
+               MessageConsumer consumer,
                const std::vector<fuzzerutil::ModuleSupplier>& donor_suppliers,
-               std::unique_ptr<RandomGenerator> random_generator,
                bool enable_all_passes,
                RepeatedPassStrategy repeated_pass_strategy,
                bool validate_after_each_fuzzer_pass,
                spv_validator_options validator_options)
-    : target_env_(target_env),
-      consumer_(std::move(consumer)),
-      binary_in_(binary_in),
-      initial_facts_(initial_facts),
-      donor_suppliers_(donor_suppliers),
-      random_generator_(std::move(random_generator)),
+    : consumer_(std::move(consumer)),
       enable_all_passes_(enable_all_passes),
-      repeated_pass_strategy_(repeated_pass_strategy),
       validate_after_each_fuzzer_pass_(validate_after_each_fuzzer_pass),
       validator_options_(validator_options),
       num_repeated_passes_applied_(0),
-      ir_context_(nullptr),
-      fuzzer_context_(nullptr),
-      transformation_context_(nullptr),
-      transformation_sequence_out_() {}
+      is_valid_(true),
+      ir_context_(std::move(ir_context)),
+      transformation_context_(std::move(transformation_context)),
+      fuzzer_context_(std::move(fuzzer_context)),
+      transformation_sequence_out_(),
+      pass_instances_(),
+      repeated_pass_recommender_(nullptr),
+      repeated_pass_manager_(nullptr),
+      final_passes_() {
+  assert(ir_context_ && "IRContext is not initialized");
+  assert(fuzzer_context_ && "FuzzerContext is not initialized");
+  assert(transformation_context_ && "TransformationContext is not initialized");
+  assert(fuzzerutil::IsValidAndWellFormed(ir_context_.get(), validator_options_,
+                                          consumer_) &&
+         "IRContext is invalid");
+
+  // The following passes are likely to be very useful: many other passes
+  // introduce synonyms, irrelevant ids and constants that these passes can work
+  // with.  We thus enable them with high probability.
+  MaybeAddRepeatedPass<FuzzerPassObfuscateConstants>(90, &pass_instances_);
+  MaybeAddRepeatedPass<FuzzerPassApplyIdSynonyms>(90, &pass_instances_);
+  MaybeAddRepeatedPass<FuzzerPassReplaceIrrelevantIds>(90, &pass_instances_);
+
+  do {
+    // Each call to MaybeAddRepeatedPass randomly decides whether the given pass
+    // should be enabled, and adds an instance of the pass to |pass_instances|
+    // if it is enabled.
+    MaybeAddRepeatedPass<FuzzerPassAddAccessChains>(&pass_instances_);
+    MaybeAddRepeatedPass<FuzzerPassAddBitInstructionSynonyms>(&pass_instances_);
+    MaybeAddRepeatedPass<FuzzerPassAddCompositeExtract>(&pass_instances_);
+    MaybeAddRepeatedPass<FuzzerPassAddCompositeInserts>(&pass_instances_);
+    MaybeAddRepeatedPass<FuzzerPassAddCompositeTypes>(&pass_instances_);
+    MaybeAddRepeatedPass<FuzzerPassAddCopyMemory>(&pass_instances_);
+    MaybeAddRepeatedPass<FuzzerPassAddDeadBlocks>(&pass_instances_);
+    MaybeAddRepeatedPass<FuzzerPassAddDeadBreaks>(&pass_instances_);
+    MaybeAddRepeatedPass<FuzzerPassAddDeadContinues>(&pass_instances_);
+    MaybeAddRepeatedPass<FuzzerPassAddEquationInstructions>(&pass_instances_);
+    MaybeAddRepeatedPass<FuzzerPassAddFunctionCalls>(&pass_instances_);
+    MaybeAddRepeatedPass<FuzzerPassAddGlobalVariables>(&pass_instances_);
+    MaybeAddRepeatedPass<FuzzerPassAddImageSampleUnusedComponents>(
+        &pass_instances_);
+    MaybeAddRepeatedPass<FuzzerPassAddLoads>(&pass_instances_);
+    MaybeAddRepeatedPass<FuzzerPassAddLocalVariables>(&pass_instances_);
+    MaybeAddRepeatedPass<FuzzerPassAddLoopPreheaders>(&pass_instances_);
+    MaybeAddRepeatedPass<FuzzerPassAddLoopsToCreateIntConstantSynonyms>(
+        &pass_instances_);
+    MaybeAddRepeatedPass<FuzzerPassAddOpPhiSynonyms>(&pass_instances_);
+    MaybeAddRepeatedPass<FuzzerPassAddParameters>(&pass_instances_);
+    MaybeAddRepeatedPass<FuzzerPassAddRelaxedDecorations>(&pass_instances_);
+    MaybeAddRepeatedPass<FuzzerPassAddStores>(&pass_instances_);
+    MaybeAddRepeatedPass<FuzzerPassAddSynonyms>(&pass_instances_);
+    MaybeAddRepeatedPass<FuzzerPassAddVectorShuffleInstructions>(
+        &pass_instances_);
+    MaybeAddRepeatedPass<FuzzerPassConstructComposites>(&pass_instances_);
+    MaybeAddRepeatedPass<FuzzerPassCopyObjects>(&pass_instances_);
+    MaybeAddRepeatedPass<FuzzerPassDonateModules>(&pass_instances_,
+                                                  donor_suppliers);
+    MaybeAddRepeatedPass<FuzzerPassDuplicateRegionsWithSelections>(
+        &pass_instances_);
+    MaybeAddRepeatedPass<FuzzerPassExpandVectorReductions>(&pass_instances_);
+    MaybeAddRepeatedPass<FuzzerPassFlattenConditionalBranches>(
+        &pass_instances_);
+    MaybeAddRepeatedPass<FuzzerPassInlineFunctions>(&pass_instances_);
+    MaybeAddRepeatedPass<FuzzerPassInvertComparisonOperators>(&pass_instances_);
+    MaybeAddRepeatedPass<FuzzerPassMakeVectorOperationsDynamic>(
+        &pass_instances_);
+    MaybeAddRepeatedPass<FuzzerPassMergeBlocks>(&pass_instances_);
+    MaybeAddRepeatedPass<FuzzerPassMergeFunctionReturns>(&pass_instances_);
+    MaybeAddRepeatedPass<FuzzerPassMutatePointers>(&pass_instances_);
+    MaybeAddRepeatedPass<FuzzerPassOutlineFunctions>(&pass_instances_);
+    MaybeAddRepeatedPass<FuzzerPassPermuteBlocks>(&pass_instances_);
+    MaybeAddRepeatedPass<FuzzerPassPermuteFunctionParameters>(&pass_instances_);
+    MaybeAddRepeatedPass<FuzzerPassPermuteInstructions>(&pass_instances_);
+    MaybeAddRepeatedPass<FuzzerPassPropagateInstructionsDown>(&pass_instances_);
+    MaybeAddRepeatedPass<FuzzerPassPropagateInstructionsUp>(&pass_instances_);
+    MaybeAddRepeatedPass<FuzzerPassPushIdsThroughVariables>(&pass_instances_);
+    MaybeAddRepeatedPass<FuzzerPassReplaceAddsSubsMulsWithCarryingExtended>(
+        &pass_instances_);
+    MaybeAddRepeatedPass<FuzzerPassReplaceBranchesFromDeadBlocksWithExits>(
+        &pass_instances_);
+    MaybeAddRepeatedPass<FuzzerPassReplaceCopyMemoriesWithLoadsStores>(
+        &pass_instances_);
+    MaybeAddRepeatedPass<FuzzerPassReplaceCopyObjectsWithStoresLoads>(
+        &pass_instances_);
+    MaybeAddRepeatedPass<FuzzerPassReplaceLoadsStoresWithCopyMemories>(
+        &pass_instances_);
+    MaybeAddRepeatedPass<FuzzerPassReplaceParameterWithGlobal>(
+        &pass_instances_);
+    MaybeAddRepeatedPass<FuzzerPassReplaceLinearAlgebraInstructions>(
+        &pass_instances_);
+    MaybeAddRepeatedPass<FuzzerPassReplaceOpPhiIdsFromDeadPredecessors>(
+        &pass_instances_);
+    MaybeAddRepeatedPass<FuzzerPassReplaceOpSelectsWithConditionalBranches>(
+        &pass_instances_);
+    MaybeAddRepeatedPass<FuzzerPassReplaceParamsWithStruct>(&pass_instances_);
+    MaybeAddRepeatedPass<FuzzerPassSplitBlocks>(&pass_instances_);
+    MaybeAddRepeatedPass<FuzzerPassSwapBranchConditionalOperands>(
+        &pass_instances_);
+    MaybeAddRepeatedPass<FuzzerPassWrapRegionsInSelections>(&pass_instances_);
+    // There is a theoretical possibility that no pass instances were created
+    // until now; loop again if so.
+  } while (pass_instances_.GetPasses().empty());
+
+  repeated_pass_recommender_ = MakeUnique<RepeatedPassRecommenderStandard>(
+      &pass_instances_, fuzzer_context_.get());
+  repeated_pass_manager_ = RepeatedPassManager::Create(
+      repeated_pass_strategy, fuzzer_context_.get(), &pass_instances_,
+      repeated_pass_recommender_.get());
+
+  MaybeAddFinalPass<FuzzerPassAdjustBranchWeights>(&final_passes_);
+  MaybeAddFinalPass<FuzzerPassAdjustFunctionControls>(&final_passes_);
+  MaybeAddFinalPass<FuzzerPassAdjustLoopControls>(&final_passes_);
+  MaybeAddFinalPass<FuzzerPassAdjustMemoryOperandsMasks>(&final_passes_);
+  MaybeAddFinalPass<FuzzerPassAdjustSelectionControls>(&final_passes_);
+  MaybeAddFinalPass<FuzzerPassAddNoContractionDecorations>(&final_passes_);
+  if (!fuzzer_context_->IsWgslCompatible()) {
+    // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/4214):
+    //  this is disabled temporarily due to some issues in the Tint compiler.
+    //  Enable it back when the issues are resolved.
+    MaybeAddFinalPass<FuzzerPassInterchangeSignednessOfIntegerOperands>(
+        &final_passes_);
+  }
+  MaybeAddFinalPass<FuzzerPassInterchangeZeroLikeConstants>(&final_passes_);
+  MaybeAddFinalPass<FuzzerPassPermuteFunctionVariables>(&final_passes_);
+  MaybeAddFinalPass<FuzzerPassPermutePhiOperands>(&final_passes_);
+  MaybeAddFinalPass<FuzzerPassSwapCommutableOperands>(&final_passes_);
+  MaybeAddFinalPass<FuzzerPassSwapFunctions>(&final_passes_);
+  MaybeAddFinalPass<FuzzerPassToggleAccessChainInstruction>(&final_passes_);
+}
 
 Fuzzer::~Fuzzer() = default;
 
@@ -165,240 +276,107 @@
                                           consumer_);
 }
 
-Fuzzer::FuzzerResult Fuzzer::Run() {
-  // Check compatibility between the library version being linked with and the
-  // header files being used.
-  GOOGLE_PROTOBUF_VERIFY_VERSION;
+opt::IRContext* Fuzzer::GetIRContext() { return ir_context_.get(); }
 
-  assert(ir_context_ == nullptr && fuzzer_context_ == nullptr &&
-         transformation_context_ == nullptr &&
-         transformation_sequence_out_.transformation_size() == 0 &&
-         "'Run' must not be invoked more than once.");
-
-  spvtools::SpirvTools tools(target_env_);
-  tools.SetMessageConsumer(consumer_);
-  if (!tools.IsValid()) {
-    consumer_(SPV_MSG_ERROR, nullptr, {},
-              "Failed to create SPIRV-Tools interface; stopping.");
-    return {Fuzzer::FuzzerResultStatus::kFailedToCreateSpirvToolsInterface,
-            std::vector<uint32_t>(), protobufs::TransformationSequence()};
-  }
-
-  // Initial binary should be valid.
-  if (!tools.Validate(&binary_in_[0], binary_in_.size(), validator_options_)) {
-    consumer_(SPV_MSG_ERROR, nullptr, {},
-              "Initial binary is invalid; stopping.");
-    return {Fuzzer::FuzzerResultStatus::kInitialBinaryInvalid,
-            std::vector<uint32_t>(), protobufs::TransformationSequence()};
-  }
-
-  // Build the module from the input binary.
-  ir_context_ =
-      BuildModule(target_env_, consumer_, binary_in_.data(), binary_in_.size());
-  assert(ir_context_);
-
-  // The fuzzer will introduce new ids into the module.  The module's id bound
-  // gives the smallest id that can be used for this purpose.  We add an offset
-  // to this so that there is a sizeable gap between the ids used in the
-  // original module and the ids used for fuzzing, as a readability aid.
-  //
-  // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2541) consider the
-  //  case where the maximum id bound is reached.
-  auto minimum_fresh_id = ir_context_->module()->id_bound() + kIdBoundGap;
-  fuzzer_context_ =
-      MakeUnique<FuzzerContext>(random_generator_.get(), minimum_fresh_id);
-
-  transformation_context_ = MakeUnique<TransformationContext>(
-      MakeUnique<FactManager>(ir_context_.get()), validator_options_);
-  transformation_context_->GetFactManager()->AddInitialFacts(consumer_,
-                                                             initial_facts_);
-
-  RepeatedPassInstances pass_instances{};
-
-  // The following passes are likely to be very useful: many other passes
-  // introduce synonyms, irrelevant ids and constants that these passes can work
-  // with.  We thus enable them with high probability.
-  MaybeAddRepeatedPass<FuzzerPassObfuscateConstants>(90, &pass_instances);
-  MaybeAddRepeatedPass<FuzzerPassApplyIdSynonyms>(90, &pass_instances);
-  MaybeAddRepeatedPass<FuzzerPassReplaceIrrelevantIds>(90, &pass_instances);
-
-  do {
-    // Each call to MaybeAddRepeatedPass randomly decides whether the given pass
-    // should be enabled, and adds an instance of the pass to |pass_instances|
-    // if it is enabled.
-    MaybeAddRepeatedPass<FuzzerPassAddAccessChains>(&pass_instances);
-    MaybeAddRepeatedPass<FuzzerPassAddBitInstructionSynonyms>(&pass_instances);
-    MaybeAddRepeatedPass<FuzzerPassAddCompositeExtract>(&pass_instances);
-    MaybeAddRepeatedPass<FuzzerPassAddCompositeInserts>(&pass_instances);
-    MaybeAddRepeatedPass<FuzzerPassAddCompositeTypes>(&pass_instances);
-    MaybeAddRepeatedPass<FuzzerPassAddCopyMemory>(&pass_instances);
-    MaybeAddRepeatedPass<FuzzerPassAddDeadBlocks>(&pass_instances);
-    MaybeAddRepeatedPass<FuzzerPassAddDeadBreaks>(&pass_instances);
-    MaybeAddRepeatedPass<FuzzerPassAddDeadContinues>(&pass_instances);
-    MaybeAddRepeatedPass<FuzzerPassAddEquationInstructions>(&pass_instances);
-    MaybeAddRepeatedPass<FuzzerPassAddFunctionCalls>(&pass_instances);
-    MaybeAddRepeatedPass<FuzzerPassAddGlobalVariables>(&pass_instances);
-    MaybeAddRepeatedPass<FuzzerPassAddImageSampleUnusedComponents>(
-        &pass_instances);
-    MaybeAddRepeatedPass<FuzzerPassAddLoads>(&pass_instances);
-    MaybeAddRepeatedPass<FuzzerPassAddLocalVariables>(&pass_instances);
-    MaybeAddRepeatedPass<FuzzerPassAddLoopPreheaders>(&pass_instances);
-    MaybeAddRepeatedPass<FuzzerPassAddLoopsToCreateIntConstantSynonyms>(
-        &pass_instances);
-    MaybeAddRepeatedPass<FuzzerPassAddOpPhiSynonyms>(&pass_instances);
-    MaybeAddRepeatedPass<FuzzerPassAddParameters>(&pass_instances);
-    MaybeAddRepeatedPass<FuzzerPassAddRelaxedDecorations>(&pass_instances);
-    MaybeAddRepeatedPass<FuzzerPassAddStores>(&pass_instances);
-    MaybeAddRepeatedPass<FuzzerPassAddSynonyms>(&pass_instances);
-    MaybeAddRepeatedPass<FuzzerPassAddVectorShuffleInstructions>(
-        &pass_instances);
-    MaybeAddRepeatedPass<FuzzerPassConstructComposites>(&pass_instances);
-    MaybeAddRepeatedPass<FuzzerPassCopyObjects>(&pass_instances);
-    MaybeAddRepeatedPass<FuzzerPassDonateModules>(&pass_instances,
-                                                  donor_suppliers_);
-    MaybeAddRepeatedPass<FuzzerPassDuplicateRegionsWithSelections>(
-        &pass_instances);
-    MaybeAddRepeatedPass<FuzzerPassExpandVectorReductions>(&pass_instances);
-    MaybeAddRepeatedPass<FuzzerPassFlattenConditionalBranches>(&pass_instances);
-    MaybeAddRepeatedPass<FuzzerPassInlineFunctions>(&pass_instances);
-    MaybeAddRepeatedPass<FuzzerPassInvertComparisonOperators>(&pass_instances);
-    MaybeAddRepeatedPass<FuzzerPassMakeVectorOperationsDynamic>(
-        &pass_instances);
-    MaybeAddRepeatedPass<FuzzerPassMergeBlocks>(&pass_instances);
-    MaybeAddRepeatedPass<FuzzerPassMergeFunctionReturns>(&pass_instances);
-    MaybeAddRepeatedPass<FuzzerPassMutatePointers>(&pass_instances);
-    MaybeAddRepeatedPass<FuzzerPassOutlineFunctions>(&pass_instances);
-    MaybeAddRepeatedPass<FuzzerPassPermuteBlocks>(&pass_instances);
-    MaybeAddRepeatedPass<FuzzerPassPermuteFunctionParameters>(&pass_instances);
-    MaybeAddRepeatedPass<FuzzerPassPermuteInstructions>(&pass_instances);
-    MaybeAddRepeatedPass<FuzzerPassPropagateInstructionsDown>(&pass_instances);
-    MaybeAddRepeatedPass<FuzzerPassPropagateInstructionsUp>(&pass_instances);
-    MaybeAddRepeatedPass<FuzzerPassPushIdsThroughVariables>(&pass_instances);
-    MaybeAddRepeatedPass<FuzzerPassReplaceAddsSubsMulsWithCarryingExtended>(
-        &pass_instances);
-    MaybeAddRepeatedPass<FuzzerPassReplaceBranchesFromDeadBlocksWithExits>(
-        &pass_instances);
-    MaybeAddRepeatedPass<FuzzerPassReplaceCopyMemoriesWithLoadsStores>(
-        &pass_instances);
-    MaybeAddRepeatedPass<FuzzerPassReplaceCopyObjectsWithStoresLoads>(
-        &pass_instances);
-    MaybeAddRepeatedPass<FuzzerPassReplaceLoadsStoresWithCopyMemories>(
-        &pass_instances);
-    MaybeAddRepeatedPass<FuzzerPassReplaceParameterWithGlobal>(&pass_instances);
-    MaybeAddRepeatedPass<FuzzerPassReplaceLinearAlgebraInstructions>(
-        &pass_instances);
-    MaybeAddRepeatedPass<FuzzerPassReplaceOpPhiIdsFromDeadPredecessors>(
-        &pass_instances);
-    MaybeAddRepeatedPass<FuzzerPassReplaceOpSelectsWithConditionalBranches>(
-        &pass_instances);
-    MaybeAddRepeatedPass<FuzzerPassReplaceParamsWithStruct>(&pass_instances);
-    MaybeAddRepeatedPass<FuzzerPassSplitBlocks>(&pass_instances);
-    MaybeAddRepeatedPass<FuzzerPassSwapBranchConditionalOperands>(
-        &pass_instances);
-    MaybeAddRepeatedPass<FuzzerPassWrapRegionsInSelections>(&pass_instances);
-    // There is a theoretical possibility that no pass instances were created
-    // until now; loop again if so.
-  } while (pass_instances.GetPasses().empty());
-
-  RepeatedPassRecommenderStandard pass_recommender(&pass_instances,
-                                                   fuzzer_context_.get());
-
-  std::unique_ptr<RepeatedPassManager> repeated_pass_manager = nullptr;
-  switch (repeated_pass_strategy_) {
-    case RepeatedPassStrategy::kSimple:
-      repeated_pass_manager = MakeUnique<RepeatedPassManagerSimple>(
-          fuzzer_context_.get(), &pass_instances);
-      break;
-    case RepeatedPassStrategy::kLoopedWithRecommendations:
-      repeated_pass_manager =
-          MakeUnique<RepeatedPassManagerLoopedWithRecommendations>(
-              fuzzer_context_.get(), &pass_instances, &pass_recommender);
-      break;
-    case RepeatedPassStrategy::kRandomWithRecommendations:
-      repeated_pass_manager =
-          MakeUnique<RepeatedPassManagerRandomWithRecommendations>(
-              fuzzer_context_.get(), &pass_instances, &pass_recommender);
-      break;
-  }
-
-  do {
-    if (!ApplyPassAndCheckValidity(
-            repeated_pass_manager->ChoosePass(transformation_sequence_out_))) {
-      return {Fuzzer::FuzzerResultStatus::kFuzzerPassLedToInvalidModule,
-              std::vector<uint32_t>(), protobufs::TransformationSequence()};
-    }
-  } while (ShouldContinueFuzzing());
-
-  // 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;
-  MaybeAddFinalPass<FuzzerPassAdjustBranchWeights>(&final_passes);
-  MaybeAddFinalPass<FuzzerPassAdjustFunctionControls>(&final_passes);
-  MaybeAddFinalPass<FuzzerPassAdjustLoopControls>(&final_passes);
-  MaybeAddFinalPass<FuzzerPassAdjustMemoryOperandsMasks>(&final_passes);
-  MaybeAddFinalPass<FuzzerPassAdjustSelectionControls>(&final_passes);
-  MaybeAddFinalPass<FuzzerPassAddNoContractionDecorations>(&final_passes);
-  MaybeAddFinalPass<FuzzerPassInterchangeSignednessOfIntegerOperands>(
-      &final_passes);
-  MaybeAddFinalPass<FuzzerPassInterchangeZeroLikeConstants>(&final_passes);
-  MaybeAddFinalPass<FuzzerPassPermutePhiOperands>(&final_passes);
-  MaybeAddFinalPass<FuzzerPassSwapCommutableOperands>(&final_passes);
-  MaybeAddFinalPass<FuzzerPassToggleAccessChainInstruction>(&final_passes);
-  for (auto& pass : final_passes) {
-    if (!ApplyPassAndCheckValidity(pass.get())) {
-      return {Fuzzer::FuzzerResultStatus::kFuzzerPassLedToInvalidModule,
-              std::vector<uint32_t>(), protobufs::TransformationSequence()};
-    }
-  }
-  // Encode the module as a binary.
-  std::vector<uint32_t> binary_out;
-  ir_context_->module()->ToBinary(&binary_out, false);
-
-  return {Fuzzer::FuzzerResultStatus::kComplete, std::move(binary_out),
-          std::move(transformation_sequence_out_)};
+const protobufs::TransformationSequence& Fuzzer::GetTransformationSequence()
+    const {
+  return transformation_sequence_out_;
 }
 
-bool Fuzzer::ShouldContinueFuzzing() {
-  // There's a risk that fuzzing could get stuck, if none of the enabled fuzzer
-  // passes are able to apply any transformations.  To guard against this we
-  // count the number of times some repeated pass has been applied and ensure
-  // that fuzzing stops if the number of repeated passes hits the limit on the
-  // number of transformations that can be applied.
-  assert(
-      num_repeated_passes_applied_ <=
-          fuzzer_context_->GetTransformationLimit() &&
-      "The number of repeated passes applied must not exceed its upper limit.");
-  if (ir_context_->module()->id_bound() >= fuzzer_context_->GetIdBoundLimit()) {
-    return false;
-  }
-  if (num_repeated_passes_applied_ ==
-      fuzzer_context_->GetTransformationLimit()) {
-    // Stop because fuzzing has got stuck.
-    return false;
-  }
-  auto transformations_applied_so_far =
+Fuzzer::Result Fuzzer::Run(uint32_t num_of_transformations_to_apply) {
+  assert(is_valid_ && "The module was invalidated during the previous fuzzing");
+
+  const auto initial_num_of_transformations =
       static_cast<uint32_t>(transformation_sequence_out_.transformation_size());
-  if (transformations_applied_so_far >=
-      fuzzer_context_->GetTransformationLimit()) {
-    // Stop because we have reached the transformation limit.
-    return false;
+
+  auto status = Status::kComplete;
+  do {
+    if (!ApplyPassAndCheckValidity(
+            repeated_pass_manager_->ChoosePass(transformation_sequence_out_))) {
+      status = Status::kFuzzerPassLedToInvalidModule;
+      break;
+    }
+
+    // Check that the module is small enough.
+    if (ir_context_->module()->id_bound() >=
+        fuzzer_context_->GetIdBoundLimit()) {
+      status = Status::kModuleTooBig;
+      break;
+    }
+
+    auto transformations_applied_so_far = static_cast<uint32_t>(
+        transformation_sequence_out_.transformation_size());
+    assert(transformations_applied_so_far >= initial_num_of_transformations &&
+           "Number of transformations cannot decrease");
+
+    // Check if we've already applied the maximum number of transformations.
+    if (transformations_applied_so_far >=
+        fuzzer_context_->GetTransformationLimit()) {
+      status = Status::kTransformationLimitReached;
+      break;
+    }
+
+    // Check that we've not got stuck (this can happen if the only available
+    // fuzzer passes are not able to apply any transformations, or can only
+    // apply very few transformations).
+    if (num_repeated_passes_applied_ >=
+        fuzzer_context_->GetTransformationLimit()) {
+      status = Status::kFuzzerStuck;
+      break;
+    }
+
+    // Check whether we've exceeded the number of transformations we can apply
+    // in a single call to this method.
+    if (num_of_transformations_to_apply != 0 &&
+        transformations_applied_so_far - initial_num_of_transformations >=
+            num_of_transformations_to_apply) {
+      status = Status::kComplete;
+      break;
+    }
+
+  } while (ShouldContinueRepeatedPasses(num_of_transformations_to_apply == 0));
+
+  if (status != Status::kFuzzerPassLedToInvalidModule) {
+    // We apply this transformations despite the fact that we might exceed
+    // |num_of_transformations_to_apply|. This is not a problem for us since
+    // these fuzzer passes are relatively simple yet might trigger some bugs.
+    for (auto& pass : final_passes_) {
+      if (!ApplyPassAndCheckValidity(pass.get())) {
+        status = Status::kFuzzerPassLedToInvalidModule;
+        break;
+      }
+    }
   }
-  // If we have applied T transformations so far, and the limit on the number of
-  // transformations to apply is L (where T < L), the chance that we will
-  // continue fuzzing is:
-  //
-  //     1 - T/(2*L)
-  //
-  // That is, the chance of continuing decreases as more transformations are
-  // applied.  Using 2*L instead of L increases the number of transformations
-  // that are applied on average.
-  auto chance_of_continuing = static_cast<uint32_t>(
-      100.0 * (1.0 - (static_cast<double>(transformations_applied_so_far) /
-                      (2.0 * static_cast<double>(
-                                 fuzzer_context_->GetTransformationLimit())))));
-  if (!fuzzer_context_->ChoosePercentage(chance_of_continuing)) {
-    // We have probabilistically decided to stop.
-    return false;
+
+  is_valid_ = status != Status::kFuzzerPassLedToInvalidModule;
+  return {status, static_cast<uint32_t>(
+                      transformation_sequence_out_.transformation_size()) !=
+                      initial_num_of_transformations};
+}
+
+bool Fuzzer::ShouldContinueRepeatedPasses(
+    bool continue_fuzzing_probabilistically) {
+  if (continue_fuzzing_probabilistically) {
+    // If we have applied T transformations so far, and the limit on the number
+    // of transformations to apply is L (where T < L), the chance that we will
+    // continue fuzzing is:
+    //
+    //     1 - T/(2*L)
+    //
+    // That is, the chance of continuing decreases as more transformations are
+    // applied.  Using 2*L instead of L increases the number of transformations
+    // that are applied on average.
+    auto transformations_applied_so_far = static_cast<uint32_t>(
+        transformation_sequence_out_.transformation_size());
+    auto chance_of_continuing = static_cast<uint32_t>(
+        100.0 *
+        (1.0 - (static_cast<double>(transformations_applied_so_far) /
+                (2.0 * static_cast<double>(
+                           fuzzer_context_->GetTransformationLimit())))));
+    if (!fuzzer_context_->ChoosePercentage(chance_of_continuing)) {
+      // We have probabilistically decided to stop.
+      return false;
+    }
   }
   // Continue fuzzing!
   num_repeated_passes_applied_++;
diff --git a/source/fuzz/fuzzer.h b/source/fuzz/fuzzer.h
index 774457f..1e7d686 100644
--- a/source/fuzz/fuzzer.h
+++ b/source/fuzz/fuzzer.h
@@ -23,6 +23,7 @@
 #include "source/fuzz/fuzzer_pass.h"
 #include "source/fuzz/fuzzer_util.h"
 #include "source/fuzz/pass_management/repeated_pass_instances.h"
+#include "source/fuzz/pass_management/repeated_pass_manager.h"
 #include "source/fuzz/pass_management/repeated_pass_recommender.h"
 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
 #include "source/fuzz/random_generator.h"
@@ -37,33 +38,28 @@
 class Fuzzer {
  public:
   // Possible statuses that can result from running the fuzzer.
-  enum class FuzzerResultStatus {
+  enum class Status {
     kComplete,
-    kFailedToCreateSpirvToolsInterface,
+    kModuleTooBig,
+    kTransformationLimitReached,
+    kFuzzerStuck,
     kFuzzerPassLedToInvalidModule,
-    kInitialBinaryInvalid,
   };
 
-  struct FuzzerResult {
-    FuzzerResultStatus status;
-    std::vector<uint32_t> transformed_binary;
-    protobufs::TransformationSequence applied_transformations;
+  struct Result {
+    // Status of the fuzzing session.
+    Status status;
+
+    // Equals to true if new transformations were applied during the previous
+    // fuzzing session.
+    bool is_changed;
   };
 
-  // Each field of this enum corresponds to an available repeated pass
-  // strategy, and is used to decide which kind of RepeatedPassManager object
-  // to create.
-  enum class RepeatedPassStrategy {
-    kSimple,
-    kRandomWithRecommendations,
-    kLoopedWithRecommendations
-  };
-
-  Fuzzer(spv_target_env target_env, MessageConsumer consumer,
-         const std::vector<uint32_t>& binary_in,
-         const protobufs::FactSequence& initial_facts,
+  Fuzzer(std::unique_ptr<opt::IRContext> ir_context,
+         std::unique_ptr<TransformationContext> transformation_context,
+         std::unique_ptr<FuzzerContext> fuzzer_context,
+         MessageConsumer consumer,
          const std::vector<fuzzerutil::ModuleSupplier>& donor_suppliers,
-         std::unique_ptr<RandomGenerator> random_generator,
          bool enable_all_passes, RepeatedPassStrategy repeated_pass_strategy,
          bool validate_after_each_fuzzer_pass,
          spv_validator_options validator_options);
@@ -76,15 +72,23 @@
 
   ~Fuzzer();
 
-  // Transforms |binary_in_| by running a number of randomized fuzzer passes.
-  // Initial facts about the input binary and the context in which it will
-  // execute are provided via |initial_facts_|.  A source of donor modules to be
-  // used by transformations is provided via |donor_suppliers_|.  On success,
-  // returns a successful result status together with the transformed binary and
-  // the sequence of transformations that were applied.  Otherwise, returns an
-  // appropriate result status together with an empty binary and empty
-  // transformation sequence.
-  FuzzerResult Run();
+  // Transforms |ir_context_| by running a number of randomized fuzzer passes.
+  // Initial facts about the input binary and the context in which it will be
+  // executed are provided with |transformation_context_|.
+  // |num_of_transformations| is equal to the maximum number of transformations
+  // applied in a single call to this method. This parameter is ignored if its
+  // value is equal to 0. Because fuzzing cannot stop mid way through a fuzzer
+  // pass, fuzzing will stop after the fuzzer pass that exceeds
+  // |num_of_transformations| has completed, so that the total number of
+  // transformations may be somewhat larger than this number.
+  Result Run(uint32_t num_of_transformations_to_apply);
+
+  // Returns the current IR context. It may be invalid if the Run method
+  // returned Status::kFuzzerPassLedToInvalidModule previously.
+  opt::IRContext* GetIRContext();
+
+  // Returns the sequence of applied transformations.
+  const protobufs::TransformationSequence& GetTransformationSequence() const;
 
  private:
   // A convenience method to add a repeated fuzzer pass to |pass_instances| with
@@ -119,7 +123,9 @@
 
   // Decides whether to apply more repeated passes. The probability decreases as
   // the number of transformations that have been applied increases.
-  bool ShouldContinueFuzzing();
+  // The described probability is only applied if
+  // |continue_fuzzing_probabilistically| is true.
+  bool ShouldContinueRepeatedPasses(bool continue_fuzzing_probabilistically);
 
   // Applies |pass|, which must be a pass constructed with |ir_context|.
   // If |validate_after_each_fuzzer_pass_| is not set, true is always returned.
@@ -128,57 +134,59 @@
   // instruction has a distinct unique id.
   bool ApplyPassAndCheckValidity(FuzzerPass* pass) const;
 
-  // Target environment.
-  const spv_target_env target_env_;
-
   // Message consumer that will be invoked once for each message communicated
   // from the library.
-  MessageConsumer consumer_;
-
-  // The initial binary to which fuzzing should be applied.
-  const std::vector<uint32_t>& binary_in_;
-
-  // Initial facts known to hold in advance of applying any transformations.
-  const protobufs::FactSequence& initial_facts_;
-
-  // A source of modules whose contents can be donated into the module being
-  // fuzzed.
-  const std::vector<fuzzerutil::ModuleSupplier>& donor_suppliers_;
-
-  // Random number generator to control decision making during fuzzing.
-  std::unique_ptr<RandomGenerator> random_generator_;
+  const MessageConsumer consumer_;
 
   // Determines whether all passes should be enabled, vs. having passes be
   // probabilistically enabled.
-  bool enable_all_passes_;
-
-  // Controls which type of RepeatedPassManager object to create.
-  RepeatedPassStrategy repeated_pass_strategy_;
+  const bool enable_all_passes_;
 
   // Determines whether the validator should be invoked after every fuzzer pass.
-  bool validate_after_each_fuzzer_pass_;
+  const bool validate_after_each_fuzzer_pass_;
 
   // Options to control validation.
-  spv_validator_options validator_options_;
+  const spv_validator_options validator_options_;
 
   // The number of repeated fuzzer passes that have been applied is kept track
   // of, in order to enforce a hard limit on the number of times such passes
   // can be applied.
   uint32_t num_repeated_passes_applied_;
 
+  // We use this to determine whether we can continue fuzzing incrementally
+  // since the previous call to the Run method could've returned
+  // kFuzzerPassLedToInvalidModule.
+  bool is_valid_;
+
   // Intermediate representation for the module being fuzzed, which gets
   // mutated as fuzzing proceeds.
   std::unique_ptr<opt::IRContext> ir_context_;
 
+  // Contextual information that is required in order to apply
+  // transformations.
+  std::unique_ptr<TransformationContext> transformation_context_;
+
   // Provides probabilities that control the fuzzing process.
   std::unique_ptr<FuzzerContext> fuzzer_context_;
 
-  // Contextual information that is required in order to apply transformations.
-  std::unique_ptr<TransformationContext> transformation_context_;
-
-  // The sequence of transformations that have been applied during fuzzing.  It
+  // The sequence of transformations that have been applied during fuzzing. It
   // is initially empty and grows as fuzzer passes are applied.
   protobufs::TransformationSequence transformation_sequence_out_;
+
+  // This object contains instances of all fuzzer passes that will participate
+  // in the fuzzing.
+  RepeatedPassInstances pass_instances_;
+
+  // This object defines the recommendation logic for fuzzer passes.
+  std::unique_ptr<RepeatedPassRecommender> repeated_pass_recommender_;
+
+  // This object manager a list of fuzzer pass and their available
+  // recommendations.
+  std::unique_ptr<RepeatedPassManager> repeated_pass_manager_;
+
+  // 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_;
 };
 
 }  // namespace fuzz
diff --git a/source/fuzz/fuzzer_context.cpp b/source/fuzz/fuzzer_context.cpp
index 47bf4e2..9e9650f 100644
--- a/source/fuzz/fuzzer_context.cpp
+++ b/source/fuzz/fuzzer_context.cpp
@@ -21,6 +21,12 @@
 
 namespace {
 
+// An offset between the the module's id bound and the minimum fresh id.
+//
+// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2541): consider
+//  the case where the maximum id bound is reached.
+const uint32_t kIdBoundGap = 100;
+
 // Limits to help control the overall fuzzing process and rein in individual
 // fuzzer passes.
 const uint32_t kIdBoundLimit = 50000;
@@ -114,6 +120,8 @@
 const std::pair<uint32_t, uint32_t> kChanceOfMutatingPointer = {20, 90};
 const std::pair<uint32_t, uint32_t> kChanceOfObfuscatingConstant = {10, 90};
 const std::pair<uint32_t, uint32_t> kChanceOfOutliningFunction = {10, 90};
+const std::pair<uint32_t, uint32_t> kChanceOfPermutingFunctionVariables = {30,
+                                                                           90};
 const std::pair<uint32_t, uint32_t> kChanceOfPermutingInstructions = {20, 70};
 const std::pair<uint32_t, uint32_t> kChanceOfPermutingParameters = {30, 90};
 const std::pair<uint32_t, uint32_t> kChanceOfPermutingPhiOperands = {30, 90};
@@ -145,8 +153,11 @@
 const std::pair<uint32_t, uint32_t> kChanceOfReplacingParametersWithStruct = {
     20, 40};
 const std::pair<uint32_t, uint32_t> kChanceOfSplittingBlock = {40, 95};
+const std::pair<uint32_t, uint32_t>
+    kChanceOfSwappingAnotherPairOfFunctionVariables = {30, 90};
 const std::pair<uint32_t, uint32_t> kChanceOfSwappingConditionalBranchOperands =
     {10, 70};
+const std::pair<uint32_t, uint32_t> kChanceOfSwappingFunctions = {10, 90};
 const std::pair<uint32_t, uint32_t> kChanceOfTogglingAccessChainInstruction = {
     20, 90};
 const std::pair<uint32_t, uint32_t> kChanceOfWrappingRegionInSelection = {70,
@@ -177,10 +188,11 @@
 
 }  // namespace
 
-FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
-                             uint32_t min_fresh_id)
-    : random_generator_(random_generator),
+FuzzerContext::FuzzerContext(std::unique_ptr<RandomGenerator> random_generator,
+                             uint32_t min_fresh_id, bool is_wgsl_compatible)
+    : random_generator_(std::move(random_generator)),
       next_fresh_id_(min_fresh_id),
+      is_wgsl_compatible_(is_wgsl_compatible),
       max_equivalence_class_size_for_data_synonym_fact_closure_(
           kDefaultMaxEquivalenceClassSizeForDataSynonymFactClosure),
       max_loop_control_partial_count_(kDefaultMaxLoopControlPartialCount),
@@ -308,6 +320,8 @@
       ChooseBetweenMinAndMax(kChanceOfObfuscatingConstant);
   chance_of_outlining_function_ =
       ChooseBetweenMinAndMax(kChanceOfOutliningFunction);
+  chance_of_permuting_function_variables_ =
+      ChooseBetweenMinAndMax(kChanceOfPermutingFunctionVariables);
   chance_of_permuting_instructions_ =
       ChooseBetweenMinAndMax(kChanceOfPermutingInstructions);
   chance_of_permuting_parameters_ =
@@ -345,8 +359,12 @@
   chance_of_replacing_parameters_with_struct_ =
       ChooseBetweenMinAndMax(kChanceOfReplacingParametersWithStruct);
   chance_of_splitting_block_ = ChooseBetweenMinAndMax(kChanceOfSplittingBlock);
+  chance_of_swapping_another_pair_of_function_variables_ =
+      ChooseBetweenMinAndMax(kChanceOfSwappingAnotherPairOfFunctionVariables);
   chance_of_swapping_conditional_branch_operands_ =
       ChooseBetweenMinAndMax(kChanceOfSwappingConditionalBranchOperands);
+  chance_of_swapping_functions_ =
+      ChooseBetweenMinAndMax(kChanceOfSwappingFunctions);
   chance_of_toggling_access_chain_instruction_ =
       ChooseBetweenMinAndMax(kChanceOfTogglingAccessChainInstruction);
   chance_of_wrapping_region_in_selection_ =
@@ -403,5 +421,9 @@
   return kTransformationLimit;
 }
 
+uint32_t FuzzerContext::GetMinFreshId(opt::IRContext* ir_context) {
+  return ir_context->module()->id_bound() + kIdBoundGap;
+}
+
 }  // namespace fuzz
 }  // namespace spvtools
diff --git a/source/fuzz/fuzzer_context.h b/source/fuzz/fuzzer_context.h
index 8c51041..ef2cc2c 100644
--- a/source/fuzz/fuzzer_context.h
+++ b/source/fuzz/fuzzer_context.h
@@ -21,6 +21,7 @@
 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
 #include "source/fuzz/random_generator.h"
 #include "source/opt/function.h"
+#include "source/opt/ir_context.h"
 
 namespace spvtools {
 namespace fuzz {
@@ -32,7 +33,8 @@
  public:
   // Constructs a fuzzer context with a given random generator and the minimum
   // value that can be used for fresh ids.
-  FuzzerContext(RandomGenerator* random_generator, uint32_t min_fresh_id);
+  FuzzerContext(std::unique_ptr<RandomGenerator> random_generator,
+                uint32_t min_fresh_id, bool is_wgsl_compatible);
 
   ~FuzzerContext();
 
@@ -64,7 +66,7 @@
   }
 
   // Randomly shuffles a |sequence| between |lo| and |hi| indices inclusively.
-  // |lo| and |hi| must be valid indices to the |sequence|
+  // |lo| and |hi| must be valid indices to the |sequence|.
   template <typename T>
   void Shuffle(std::vector<T>* sequence, size_t lo, size_t hi) const {
     auto& array = *sequence;
@@ -89,7 +91,7 @@
     }
   }
 
-  // Ramdomly shuffles a |sequence|
+  // Randomly shuffles a |sequence|.
   template <typename T>
   void Shuffle(std::vector<T>* sequence) const {
     if (!sequence->empty()) {
@@ -102,7 +104,7 @@
   uint32_t GetFreshId();
 
   // Returns a vector of |count| fresh ids.
-  std::vector<uint32_t> GetFreshIds(const uint32_t count);
+  std::vector<uint32_t> GetFreshIds(uint32_t count);
 
   // A suggested limit on the id bound for the module being fuzzed.  This is
   // useful for deciding when to stop the overall fuzzing process.  Furthermore,
@@ -115,6 +117,14 @@
   // fuzzer passes.
   uint32_t GetTransformationLimit() const;
 
+  // Returns the minimum fresh id that can be used given the |ir_context|.
+  static uint32_t GetMinFreshId(opt::IRContext* ir_context);
+
+  // Returns true if all transformations should be compatible with WGSL.
+  bool IsWgslCompatible() const {
+    return is_wgsl_compatible_;
+  }
+
   // Probabilities associated with applying various transformations.
   // Keep them in alphabetical order.
   uint32_t GetChanceOfAcceptingRepeatedPassRecommendation() const {
@@ -293,6 +303,9 @@
   uint32_t GetChanceOfOutliningFunction() const {
     return chance_of_outlining_function_;
   }
+  uint32_t GetChanceOfPermutingFunctionVariables() const {
+    return chance_of_permuting_function_variables_;
+  }
   uint32_t GetChanceOfPermutingInstructions() const {
     return chance_of_permuting_instructions_;
   }
@@ -350,9 +363,17 @@
   uint32_t GetChanceOfSplittingBlock() const {
     return chance_of_splitting_block_;
   }
+  uint32_t GetChanceOfSwappingAnotherPairOfFunctionVariables() const {
+    return chance_of_swapping_another_pair_of_function_variables_;
+  }
   uint32_t GetChanceOfSwappingConditionalBranchOperands() const {
     return chance_of_swapping_conditional_branch_operands_;
   }
+
+  uint32_t GetChanceOfSwappingFunctions() const {
+    return chance_of_swapping_functions_;
+  }
+
   uint32_t GetChanceOfTogglingAccessChainInstruction() const {
     return chance_of_toggling_access_chain_instruction_;
   }
@@ -442,15 +463,18 @@
     return random_generator_->RandomUint32(max_unused_component_count) + 1;
   }
   bool GoDeeperInConstantObfuscation(uint32_t depth) {
-    return go_deeper_in_constant_obfuscation_(depth, random_generator_);
+    return go_deeper_in_constant_obfuscation_(depth, random_generator_.get());
   }
 
  private:
   // The source of randomness.
-  RandomGenerator* random_generator_;
+  std::unique_ptr<RandomGenerator> random_generator_;
   // The next fresh id to be issued.
   uint32_t next_fresh_id_;
 
+  // True if all transformations should be compatible with WGSL spec.
+  bool is_wgsl_compatible_;
+
   // Probabilities associated with applying various transformations.
   // Keep them in alphabetical order.
   uint32_t chance_of_accepting_repeated_pass_recommendation_;
@@ -513,6 +537,7 @@
   uint32_t chance_of_mutating_pointer_;
   uint32_t chance_of_obfuscating_constant_;
   uint32_t chance_of_outlining_function_;
+  uint32_t chance_of_permuting_function_variables_;
   uint32_t chance_of_permuting_instructions_;
   uint32_t chance_of_permuting_parameters_;
   uint32_t chance_of_permuting_phi_operands_;
@@ -532,7 +557,9 @@
   uint32_t chance_of_replacing_parameters_with_globals_;
   uint32_t chance_of_replacing_parameters_with_struct_;
   uint32_t chance_of_splitting_block_;
+  uint32_t chance_of_swapping_another_pair_of_function_variables_;
   uint32_t chance_of_swapping_conditional_branch_operands_;
+  uint32_t chance_of_swapping_functions_;
   uint32_t chance_of_toggling_access_chain_instruction_;
   uint32_t chance_of_wrapping_region_in_selection_;
 
diff --git a/source/fuzz/fuzzer_pass.cpp b/source/fuzz/fuzzer_pass.cpp
index 486f18f..f67efc6 100644
--- a/source/fuzz/fuzzer_pass.cpp
+++ b/source/fuzz/fuzzer_pass.cpp
@@ -184,6 +184,35 @@
   }
 }
 
+void FuzzerPass::ApplyTransformation(const Transformation& transformation) {
+  assert(transformation.IsApplicable(GetIRContext(),
+                                     *GetTransformationContext()) &&
+         "Transformation should be applicable by construction.");
+  transformation.Apply(GetIRContext(), GetTransformationContext());
+  auto transformation_message = transformation.ToMessage();
+  assert(transformation_message.transformation_case() !=
+             protobufs::Transformation::TRANSFORMATION_NOT_SET &&
+         "Bad transformation.");
+  *GetTransformations()->add_transformation() =
+      std::move(transformation_message);
+}
+
+bool FuzzerPass::MaybeApplyTransformation(
+    const Transformation& transformation) {
+  if (transformation.IsApplicable(GetIRContext(),
+                                  *GetTransformationContext())) {
+    transformation.Apply(GetIRContext(), GetTransformationContext());
+    auto transformation_message = transformation.ToMessage();
+    assert(transformation_message.transformation_case() !=
+               protobufs::Transformation::TRANSFORMATION_NOT_SET &&
+           "Bad transformation.");
+    *GetTransformations()->add_transformation() =
+        std::move(transformation_message);
+    return true;
+  }
+  return false;
+}
+
 uint32_t FuzzerPass::FindOrCreateBoolType() {
   if (auto existing_id = fuzzerutil::MaybeGetBoolType(GetIRContext())) {
     return existing_id;
diff --git a/source/fuzz/fuzzer_pass.h b/source/fuzz/fuzzer_pass.h
index da3f239..ec25485 100644
--- a/source/fuzz/fuzzer_pass.h
+++ b/source/fuzz/fuzzer_pass.h
@@ -106,37 +106,13 @@
 
   // A generic helper for applying a transformation that should be applicable
   // by construction, and adding it to the sequence of applied transformations.
-  void ApplyTransformation(const Transformation& transformation) {
-    assert(transformation.IsApplicable(GetIRContext(),
-                                       *GetTransformationContext()) &&
-           "Transformation should be applicable by construction.");
-    transformation.Apply(GetIRContext(), GetTransformationContext());
-    protobufs::Transformation transformation_message =
-        transformation.ToMessage();
-    assert(transformation_message.transformation_case() !=
-               protobufs::Transformation::TRANSFORMATION_NOT_SET &&
-           "Bad transformation.");
-    *GetTransformations()->add_transformation() = transformation_message;
-  }
+  void ApplyTransformation(const Transformation& transformation);
 
   // A generic helper for applying a transformation only if it is applicable.
   // If it is applicable, the transformation is applied and then added to the
   // sequence of applied transformations and the function returns true.
   // Otherwise, the function returns false.
-  bool MaybeApplyTransformation(const Transformation& transformation) {
-    if (transformation.IsApplicable(GetIRContext(),
-                                    *GetTransformationContext())) {
-      transformation.Apply(GetIRContext(), GetTransformationContext());
-      protobufs::Transformation transformation_message =
-          transformation.ToMessage();
-      assert(transformation_message.transformation_case() !=
-                 protobufs::Transformation::TRANSFORMATION_NOT_SET &&
-             "Bad transformation.");
-      *GetTransformations()->add_transformation() = transformation_message;
-      return true;
-    }
-    return false;
-  }
+  bool MaybeApplyTransformation(const Transformation& transformation);
 
   // Returns the id of an OpTypeBool instruction.  If such an instruction does
   // not exist, a transformation is applied to add it.
diff --git a/source/fuzz/fuzzer_pass_add_access_chains.cpp b/source/fuzz/fuzzer_pass_add_access_chains.cpp
index 11155f2..c498642 100644
--- a/source/fuzz/fuzzer_pass_add_access_chains.cpp
+++ b/source/fuzz/fuzzer_pass_add_access_chains.cpp
@@ -27,8 +27,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassAddAccessChains::~FuzzerPassAddAccessChains() = default;
-
 void FuzzerPassAddAccessChains::Apply() {
   ForEachInstructionWithInstructionDescriptor(
       [this](opt::Function* function, opt::BasicBlock* block,
diff --git a/source/fuzz/fuzzer_pass_add_access_chains.h b/source/fuzz/fuzzer_pass_add_access_chains.h
index 8649296..e80c2c6 100644
--- a/source/fuzz/fuzzer_pass_add_access_chains.h
+++ b/source/fuzz/fuzzer_pass_add_access_chains.h
@@ -30,8 +30,6 @@
                             FuzzerContext* fuzzer_context,
                             protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassAddAccessChains();
-
   void Apply() override;
 };
 
diff --git a/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.cpp b/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.cpp
index 7b9ac4e..78abf5b 100644
--- a/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.cpp
+++ b/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.cpp
@@ -28,9 +28,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassAddBitInstructionSynonyms::~FuzzerPassAddBitInstructionSynonyms() =
-    default;
-
 void FuzzerPassAddBitInstructionSynonyms::Apply() {
   for (auto& function : *GetIRContext()->module()) {
     for (auto& block : function) {
@@ -48,22 +45,11 @@
           continue;
         }
 
-        // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3557):
-        //  Right now we only support certain operations. When this issue is
-        //  addressed the following conditional can use the function
-        //  |spvOpcodeIsBit|.
-        if (instruction.opcode() != SpvOpBitwiseOr &&
-            instruction.opcode() != SpvOpBitwiseXor &&
-            instruction.opcode() != SpvOpBitwiseAnd &&
-            instruction.opcode() != SpvOpNot) {
-          continue;
-        }
-
-        // Right now, only integer operands are supported.
-        if (GetIRContext()
-                ->get_type_mgr()
-                ->GetType(instruction.type_id())
-                ->AsVector()) {
+        // Make sure fuzzer never applies a transformation to a bitwise
+        // instruction with differently signed operands, only integer operands
+        // are supported and bitwise operations are supported only.
+        if (!TransformationAddBitInstructionSynonym::IsInstructionSupported(
+                GetIRContext(), &instruction)) {
           continue;
         }
 
diff --git a/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.h b/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.h
index 0194425..28f9577 100644
--- a/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.h
+++ b/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.h
@@ -30,8 +30,6 @@
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassAddBitInstructionSynonyms();
-
   void Apply() override;
 };
 
diff --git a/source/fuzz/fuzzer_pass_add_composite_extract.cpp b/source/fuzz/fuzzer_pass_add_composite_extract.cpp
index 132a49d..19f9902 100644
--- a/source/fuzz/fuzzer_pass_add_composite_extract.cpp
+++ b/source/fuzz/fuzzer_pass_add_composite_extract.cpp
@@ -14,6 +14,7 @@
 
 #include "source/fuzz/fuzzer_pass_add_composite_extract.h"
 
+#include "source/fuzz/available_instructions.h"
 #include "source/fuzz/fuzzer_context.h"
 #include "source/fuzz/fuzzer_util.h"
 #include "source/fuzz/instruction_descriptor.h"
@@ -29,8 +30,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassAddCompositeExtract::~FuzzerPassAddCompositeExtract() = default;
-
 void FuzzerPassAddCompositeExtract::Apply() {
   std::vector<const protobufs::DataDescriptor*> composite_synonyms;
   for (const auto* dd :
@@ -41,14 +40,16 @@
     }
   }
 
-  // We don't want to invalidate the module every time we apply this
-  // transformation since rebuilding DominatorAnalysis can be expensive, so we
-  // collect up the transformations we wish to apply and apply them all later.
-  std::vector<TransformationCompositeExtract> transformations;
+  AvailableInstructions available_composites(
+      GetIRContext(), [](opt::IRContext* ir_context, opt::Instruction* inst) {
+        return inst->type_id() && inst->result_id() &&
+               fuzzerutil::IsCompositeType(
+                   ir_context->get_type_mgr()->GetType(inst->type_id()));
+      });
 
   ForEachInstructionWithInstructionDescriptor(
-      [this, &composite_synonyms, &transformations](
-          opt::Function* function, opt::BasicBlock* block,
+      [this, &available_composites, &composite_synonyms](
+          opt::Function* /*unused*/, opt::BasicBlock* /*unused*/,
           opt::BasicBlock::iterator inst_it,
           const protobufs::InstructionDescriptor& instruction_descriptor) {
         if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCompositeExtract,
@@ -61,14 +62,6 @@
           return;
         }
 
-        auto available_composites = FindAvailableInstructions(
-            function, block, inst_it,
-            [](opt::IRContext* ir_context, opt::Instruction* inst) {
-              return inst->type_id() && inst->result_id() &&
-                     fuzzerutil::IsCompositeType(
-                         ir_context->get_type_mgr()->GetType(inst->type_id()));
-            });
-
         std::vector<const protobufs::DataDescriptor*> available_synonyms;
         for (const auto* dd : composite_synonyms) {
           if (fuzzerutil::IdIsAvailableBeforeInstruction(
@@ -77,18 +70,21 @@
           }
         }
 
-        if (available_synonyms.empty() && available_composites.empty()) {
+        auto candidate_composites =
+            available_composites.GetAvailableBeforeInstruction(&*inst_it);
+
+        if (available_synonyms.empty() && candidate_composites.empty()) {
           return;
         }
 
         uint32_t composite_id = 0;
         std::vector<uint32_t> indices;
 
-        if (available_synonyms.empty() || (!available_composites.empty() &&
+        if (available_synonyms.empty() || (!candidate_composites.empty() &&
                                            GetFuzzerContext()->ChooseEven())) {
           const auto* inst =
-              available_composites[GetFuzzerContext()->RandomIndex(
-                  available_composites)];
+              candidate_composites[GetFuzzerContext()->RandomIndex(
+                  candidate_composites)];
           composite_id = inst->result_id();
 
           auto type_id = inst->type_id();
@@ -153,14 +149,10 @@
         assert(composite_id != 0 && !indices.empty() &&
                "Composite object should have been chosen correctly");
 
-        transformations.emplace_back(instruction_descriptor,
-                                     GetFuzzerContext()->GetFreshId(),
-                                     composite_id, indices);
+        ApplyTransformation(TransformationCompositeExtract(
+            instruction_descriptor, GetFuzzerContext()->GetFreshId(),
+            composite_id, indices));
       });
-
-  for (const auto& transformation : transformations) {
-    ApplyTransformation(transformation);
-  }
 }
 
 }  // namespace fuzz
diff --git a/source/fuzz/fuzzer_pass_add_composite_extract.h b/source/fuzz/fuzzer_pass_add_composite_extract.h
index 8bcb825..32ac190 100644
--- a/source/fuzz/fuzzer_pass_add_composite_extract.h
+++ b/source/fuzz/fuzzer_pass_add_composite_extract.h
@@ -29,8 +29,6 @@
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassAddCompositeExtract() override;
-
   void Apply() override;
 };
 
diff --git a/source/fuzz/fuzzer_pass_add_composite_inserts.cpp b/source/fuzz/fuzzer_pass_add_composite_inserts.cpp
index e58c754..cf314d3 100644
--- a/source/fuzz/fuzzer_pass_add_composite_inserts.cpp
+++ b/source/fuzz/fuzzer_pass_add_composite_inserts.cpp
@@ -29,8 +29,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassAddCompositeInserts::~FuzzerPassAddCompositeInserts() = default;
-
 void FuzzerPassAddCompositeInserts::Apply() {
   ForEachInstructionWithInstructionDescriptor(
       [this](opt::Function* function, opt::BasicBlock* block,
diff --git a/source/fuzz/fuzzer_pass_add_composite_inserts.h b/source/fuzz/fuzzer_pass_add_composite_inserts.h
index c4f5103..4d511f6 100644
--- a/source/fuzz/fuzzer_pass_add_composite_inserts.h
+++ b/source/fuzz/fuzzer_pass_add_composite_inserts.h
@@ -29,7 +29,6 @@
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassAddCompositeInserts();
   void Apply() override;
 
   // Checks if any component of a composite is a pointer.
diff --git a/source/fuzz/fuzzer_pass_add_composite_types.cpp b/source/fuzz/fuzzer_pass_add_composite_types.cpp
index c4d8d1c..3dfbd69 100644
--- a/source/fuzz/fuzzer_pass_add_composite_types.cpp
+++ b/source/fuzz/fuzzer_pass_add_composite_types.cpp
@@ -28,8 +28,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassAddCompositeTypes::~FuzzerPassAddCompositeTypes() = default;
-
 void FuzzerPassAddCompositeTypes::Apply() {
   MaybeAddMissingVectorTypes();
   MaybeAddMissingMatrixTypes();
@@ -125,7 +123,9 @@
         break;
       case SpvOpTypeStruct: {
         if (!fuzzerutil::MembersHaveBuiltInDecoration(GetIRContext(),
-                                                      inst.result_id())) {
+                                                      inst.result_id()) &&
+            !fuzzerutil::HasBlockOrBufferBlockDecoration(GetIRContext(),
+                                                         inst.result_id())) {
           candidates.push_back(inst.result_id());
         }
       } break;
diff --git a/source/fuzz/fuzzer_pass_add_composite_types.h b/source/fuzz/fuzzer_pass_add_composite_types.h
index 87bc0ff..89d48f8 100644
--- a/source/fuzz/fuzzer_pass_add_composite_types.h
+++ b/source/fuzz/fuzzer_pass_add_composite_types.h
@@ -29,8 +29,6 @@
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassAddCompositeTypes();
-
   void Apply() override;
 
  private:
diff --git a/source/fuzz/fuzzer_pass_add_copy_memory.cpp b/source/fuzz/fuzzer_pass_add_copy_memory.cpp
index d98619c..b654927 100644
--- a/source/fuzz/fuzzer_pass_add_copy_memory.cpp
+++ b/source/fuzz/fuzzer_pass_add_copy_memory.cpp
@@ -29,8 +29,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassAddCopyMemory::~FuzzerPassAddCopyMemory() = default;
-
 void FuzzerPassAddCopyMemory::Apply() {
   ForEachInstructionWithInstructionDescriptor(
       [this](opt::Function* function, opt::BasicBlock* block,
diff --git a/source/fuzz/fuzzer_pass_add_copy_memory.h b/source/fuzz/fuzzer_pass_add_copy_memory.h
index 321e4a1..0f7db0c 100644
--- a/source/fuzz/fuzzer_pass_add_copy_memory.h
+++ b/source/fuzz/fuzzer_pass_add_copy_memory.h
@@ -29,8 +29,6 @@
                           FuzzerContext* fuzzer_context,
                           protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassAddCopyMemory() override;
-
   void Apply() override;
 };
 
diff --git a/source/fuzz/fuzzer_pass_add_dead_blocks.cpp b/source/fuzz/fuzzer_pass_add_dead_blocks.cpp
index 84ed1fb..8c166a2 100644
--- a/source/fuzz/fuzzer_pass_add_dead_blocks.cpp
+++ b/source/fuzz/fuzzer_pass_add_dead_blocks.cpp
@@ -14,12 +14,20 @@
 
 #include "source/fuzz/fuzzer_pass_add_dead_blocks.h"
 
+#include <algorithm>
+
 #include "source/fuzz/fuzzer_util.h"
 #include "source/fuzz/transformation_add_dead_block.h"
 
 namespace spvtools {
 namespace fuzz {
 
+namespace {
+
+const size_t kMaxTransformationsInOnePass = 100U;
+
+}  // namespace
+
 FuzzerPassAddDeadBlocks::FuzzerPassAddDeadBlocks(
     opt::IRContext* ir_context, TransformationContext* transformation_context,
     FuzzerContext* fuzzer_context,
@@ -27,8 +35,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassAddDeadBlocks::~FuzzerPassAddDeadBlocks() = default;
-
 void FuzzerPassAddDeadBlocks::Apply() {
   // We iterate over all blocks in the module collecting up those at which we
   // might add a branch to a new dead block.  We then loop over all such
@@ -57,9 +63,18 @@
           GetFuzzerContext()->GetFreshId(), block.id(), condition_value));
     }
   }
-  // Apply all those transformations that are in fact applicable.
-  for (auto& transformation : candidate_transformations) {
-    MaybeApplyTransformation(transformation);
+  // Applying transformations can be expensive as each transformation requires
+  // dominator information and also invalidates dominator information. We thus
+  // limit the number of transformations that one application of this fuzzer
+  // pass can apply. We choose to do this after identifying all the
+  // transformations that we *might* want to apply, rather than breaking the
+  // above loops once the limit is reached, to avoid biasing towards
+  // transformations that target early parts of the module.
+  GetFuzzerContext()->Shuffle(&candidate_transformations);
+  for (size_t i = 0; i < std::min(kMaxTransformationsInOnePass,
+                                  candidate_transformations.size());
+       i++) {
+    MaybeApplyTransformation(candidate_transformations[i]);
   }
 }
 
diff --git a/source/fuzz/fuzzer_pass_add_dead_blocks.h b/source/fuzz/fuzzer_pass_add_dead_blocks.h
index d78f088..a87c05c 100644
--- a/source/fuzz/fuzzer_pass_add_dead_blocks.h
+++ b/source/fuzz/fuzzer_pass_add_dead_blocks.h
@@ -29,8 +29,6 @@
                           FuzzerContext* fuzzer_context,
                           protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassAddDeadBlocks();
-
   void Apply() override;
 };
 
diff --git a/source/fuzz/fuzzer_pass_add_dead_breaks.cpp b/source/fuzz/fuzzer_pass_add_dead_breaks.cpp
index e726c63..0c18da9 100644
--- a/source/fuzz/fuzzer_pass_add_dead_breaks.cpp
+++ b/source/fuzz/fuzzer_pass_add_dead_breaks.cpp
@@ -28,8 +28,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassAddDeadBreaks::~FuzzerPassAddDeadBreaks() = default;
-
 void FuzzerPassAddDeadBreaks::Apply() {
   // We first collect up lots of possibly-applicable transformations.
   std::vector<TransformationAddDeadBreak> candidate_transformations;
diff --git a/source/fuzz/fuzzer_pass_add_dead_breaks.h b/source/fuzz/fuzzer_pass_add_dead_breaks.h
index c379eed..d1086fc 100644
--- a/source/fuzz/fuzzer_pass_add_dead_breaks.h
+++ b/source/fuzz/fuzzer_pass_add_dead_breaks.h
@@ -28,8 +28,6 @@
                           FuzzerContext* fuzzer_context,
                           protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassAddDeadBreaks();
-
   void Apply() override;
 };
 
diff --git a/source/fuzz/fuzzer_pass_add_dead_continues.cpp b/source/fuzz/fuzzer_pass_add_dead_continues.cpp
index 24617ae..1ab40b7 100644
--- a/source/fuzz/fuzzer_pass_add_dead_continues.cpp
+++ b/source/fuzz/fuzzer_pass_add_dead_continues.cpp
@@ -28,8 +28,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassAddDeadContinues::~FuzzerPassAddDeadContinues() = default;
-
 void FuzzerPassAddDeadContinues::Apply() {
   // Consider every block in every function.
   for (auto& function : *GetIRContext()->module()) {
diff --git a/source/fuzz/fuzzer_pass_add_dead_continues.h b/source/fuzz/fuzzer_pass_add_dead_continues.h
index b2acb93..bf0009e 100644
--- a/source/fuzz/fuzzer_pass_add_dead_continues.h
+++ b/source/fuzz/fuzzer_pass_add_dead_continues.h
@@ -28,8 +28,6 @@
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassAddDeadContinues();
-
   void Apply() override;
 };
 
diff --git a/source/fuzz/fuzzer_pass_add_equation_instructions.cpp b/source/fuzz/fuzzer_pass_add_equation_instructions.cpp
index 6376c9f..15554b7 100644
--- a/source/fuzz/fuzzer_pass_add_equation_instructions.cpp
+++ b/source/fuzz/fuzzer_pass_add_equation_instructions.cpp
@@ -49,9 +49,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassAddEquationInstructions::~FuzzerPassAddEquationInstructions() =
-    default;
-
 void FuzzerPassAddEquationInstructions::Apply() {
   ForEachInstructionWithInstructionDescriptor(
       [this](opt::Function* function, opt::BasicBlock* block,
diff --git a/source/fuzz/fuzzer_pass_add_equation_instructions.h b/source/fuzz/fuzzer_pass_add_equation_instructions.h
index 9ce581e..dbec5ba 100644
--- a/source/fuzz/fuzzer_pass_add_equation_instructions.h
+++ b/source/fuzz/fuzzer_pass_add_equation_instructions.h
@@ -31,8 +31,6 @@
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassAddEquationInstructions();
-
   void Apply() override;
 
  private:
diff --git a/source/fuzz/fuzzer_pass_add_function_calls.cpp b/source/fuzz/fuzzer_pass_add_function_calls.cpp
index 7400557..2240696 100644
--- a/source/fuzz/fuzzer_pass_add_function_calls.cpp
+++ b/source/fuzz/fuzzer_pass_add_function_calls.cpp
@@ -30,8 +30,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassAddFunctionCalls::~FuzzerPassAddFunctionCalls() = default;
-
 void FuzzerPassAddFunctionCalls::Apply() {
   ForEachInstructionWithInstructionDescriptor(
       [this](opt::Function* function, opt::BasicBlock* block,
diff --git a/source/fuzz/fuzzer_pass_add_function_calls.h b/source/fuzz/fuzzer_pass_add_function_calls.h
index 4ed8791..081510c 100644
--- a/source/fuzz/fuzzer_pass_add_function_calls.h
+++ b/source/fuzz/fuzzer_pass_add_function_calls.h
@@ -29,8 +29,6 @@
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassAddFunctionCalls();
-
   void Apply() override;
 
  private:
diff --git a/source/fuzz/fuzzer_pass_add_global_variables.cpp b/source/fuzz/fuzzer_pass_add_global_variables.cpp
index 9a45a37..0679741 100644
--- a/source/fuzz/fuzzer_pass_add_global_variables.cpp
+++ b/source/fuzz/fuzzer_pass_add_global_variables.cpp
@@ -27,8 +27,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassAddGlobalVariables::~FuzzerPassAddGlobalVariables() = default;
-
 void FuzzerPassAddGlobalVariables::Apply() {
   SpvStorageClass variable_storage_class = SpvStorageClassPrivate;
   for (auto& entry_point : GetIRContext()->module()->entry_points()) {
diff --git a/source/fuzz/fuzzer_pass_add_global_variables.h b/source/fuzz/fuzzer_pass_add_global_variables.h
index a907d36..3745c5c 100644
--- a/source/fuzz/fuzzer_pass_add_global_variables.h
+++ b/source/fuzz/fuzzer_pass_add_global_variables.h
@@ -29,8 +29,6 @@
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassAddGlobalVariables();
-
   void Apply() override;
 };
 
diff --git a/source/fuzz/fuzzer_pass_add_image_sample_unused_components.cpp b/source/fuzz/fuzzer_pass_add_image_sample_unused_components.cpp
index 3095bb6..c10db72 100644
--- a/source/fuzz/fuzzer_pass_add_image_sample_unused_components.cpp
+++ b/source/fuzz/fuzzer_pass_add_image_sample_unused_components.cpp
@@ -31,9 +31,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassAddImageSampleUnusedComponents::
-    ~FuzzerPassAddImageSampleUnusedComponents() = default;
-
 void FuzzerPassAddImageSampleUnusedComponents::Apply() {
   // SPIR-V module to help understand the transformation.
   //
diff --git a/source/fuzz/fuzzer_pass_add_image_sample_unused_components.h b/source/fuzz/fuzzer_pass_add_image_sample_unused_components.h
index 26374c3..f5dea8b 100644
--- a/source/fuzz/fuzzer_pass_add_image_sample_unused_components.h
+++ b/source/fuzz/fuzzer_pass_add_image_sample_unused_components.h
@@ -30,8 +30,6 @@
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassAddImageSampleUnusedComponents();
-
   void Apply() override;
 };
 
diff --git a/source/fuzz/fuzzer_pass_add_loads.cpp b/source/fuzz/fuzzer_pass_add_loads.cpp
index 256255b..2e50da2 100644
--- a/source/fuzz/fuzzer_pass_add_loads.cpp
+++ b/source/fuzz/fuzzer_pass_add_loads.cpp
@@ -27,8 +27,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassAddLoads::~FuzzerPassAddLoads() = default;
-
 void FuzzerPassAddLoads::Apply() {
   ForEachInstructionWithInstructionDescriptor(
       [this](opt::Function* function, opt::BasicBlock* block,
diff --git a/source/fuzz/fuzzer_pass_add_loads.h b/source/fuzz/fuzzer_pass_add_loads.h
index c4d5b27..3913c62 100644
--- a/source/fuzz/fuzzer_pass_add_loads.h
+++ b/source/fuzz/fuzzer_pass_add_loads.h
@@ -28,8 +28,6 @@
                      FuzzerContext* fuzzer_context,
                      protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassAddLoads();
-
   void Apply() override;
 };
 
diff --git a/source/fuzz/fuzzer_pass_add_local_variables.cpp b/source/fuzz/fuzzer_pass_add_local_variables.cpp
index ef8b5d0..a50b0b0 100644
--- a/source/fuzz/fuzzer_pass_add_local_variables.cpp
+++ b/source/fuzz/fuzzer_pass_add_local_variables.cpp
@@ -28,8 +28,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassAddLocalVariables::~FuzzerPassAddLocalVariables() = default;
-
 void FuzzerPassAddLocalVariables::Apply() {
   auto basic_type_ids_and_pointers =
       GetAvailableBasicTypesAndPointers(SpvStorageClassFunction);
diff --git a/source/fuzz/fuzzer_pass_add_local_variables.h b/source/fuzz/fuzzer_pass_add_local_variables.h
index 08d26d8..d73dae2 100644
--- a/source/fuzz/fuzzer_pass_add_local_variables.h
+++ b/source/fuzz/fuzzer_pass_add_local_variables.h
@@ -29,8 +29,6 @@
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassAddLocalVariables();
-
   void Apply() override;
 };
 
diff --git a/source/fuzz/fuzzer_pass_add_loop_preheaders.cpp b/source/fuzz/fuzzer_pass_add_loop_preheaders.cpp
index bdc3151..1cfed86 100644
--- a/source/fuzz/fuzzer_pass_add_loop_preheaders.cpp
+++ b/source/fuzz/fuzzer_pass_add_loop_preheaders.cpp
@@ -27,8 +27,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassAddLoopPreheaders::~FuzzerPassAddLoopPreheaders() = default;
-
 void FuzzerPassAddLoopPreheaders::Apply() {
   for (auto& function : *GetIRContext()->module()) {
     // Keep track of all the loop headers we want to add a preheader to.
diff --git a/source/fuzz/fuzzer_pass_add_loop_preheaders.h b/source/fuzz/fuzzer_pass_add_loop_preheaders.h
index a835056..8ac2dac 100644
--- a/source/fuzz/fuzzer_pass_add_loop_preheaders.h
+++ b/source/fuzz/fuzzer_pass_add_loop_preheaders.h
@@ -32,8 +32,6 @@
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassAddLoopPreheaders();
-
   void Apply() override;
 };
 
diff --git a/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.cpp b/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.cpp
index 31a5779..69e0697 100644
--- a/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.cpp
+++ b/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.cpp
@@ -33,9 +33,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassAddLoopsToCreateIntConstantSynonyms::
-    ~FuzzerPassAddLoopsToCreateIntConstantSynonyms() = default;
-
 void FuzzerPassAddLoopsToCreateIntConstantSynonyms::Apply() {
   std::vector<uint32_t> constants;
 
diff --git a/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.h b/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.h
index ee98c4e..2eacef5 100644
--- a/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.h
+++ b/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.h
@@ -30,8 +30,6 @@
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassAddLoopsToCreateIntConstantSynonyms();
-
   void Apply() override;
 
  private:
diff --git a/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp b/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp
index 09627d0..d075310 100644
--- a/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp
+++ b/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp
@@ -26,9 +26,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassAddNoContractionDecorations::
-    ~FuzzerPassAddNoContractionDecorations() = default;
-
 void FuzzerPassAddNoContractionDecorations::Apply() {
   // Consider every instruction in every block in every function.
   for (auto& function : *GetIRContext()->module()) {
diff --git a/source/fuzz/fuzzer_pass_add_no_contraction_decorations.h b/source/fuzz/fuzzer_pass_add_no_contraction_decorations.h
index c0c1e09..74212d8 100644
--- a/source/fuzz/fuzzer_pass_add_no_contraction_decorations.h
+++ b/source/fuzz/fuzzer_pass_add_no_contraction_decorations.h
@@ -28,8 +28,6 @@
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassAddNoContractionDecorations() override;
-
   void Apply() override;
 };
 
diff --git a/source/fuzz/fuzzer_pass_add_opphi_synonyms.cpp b/source/fuzz/fuzzer_pass_add_opphi_synonyms.cpp
index 2b339ca..be6e7ea 100644
--- a/source/fuzz/fuzzer_pass_add_opphi_synonyms.cpp
+++ b/source/fuzz/fuzzer_pass_add_opphi_synonyms.cpp
@@ -27,8 +27,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassAddOpPhiSynonyms::~FuzzerPassAddOpPhiSynonyms() = default;
-
 void FuzzerPassAddOpPhiSynonyms::Apply() {
   // Get a list of synonymous ids with the same type that can be used in the
   // same OpPhi instruction.
diff --git a/source/fuzz/fuzzer_pass_add_opphi_synonyms.h b/source/fuzz/fuzzer_pass_add_opphi_synonyms.h
index 6c7d752..9077118 100644
--- a/source/fuzz/fuzzer_pass_add_opphi_synonyms.h
+++ b/source/fuzz/fuzzer_pass_add_opphi_synonyms.h
@@ -30,8 +30,6 @@
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassAddOpPhiSynonyms() override;
-
   void Apply() override;
 
   // Computes the equivalence classes for the non-pointer and non-irrelevant ids
diff --git a/source/fuzz/fuzzer_pass_add_parameters.cpp b/source/fuzz/fuzzer_pass_add_parameters.cpp
index 35532e9..784653d 100644
--- a/source/fuzz/fuzzer_pass_add_parameters.cpp
+++ b/source/fuzz/fuzzer_pass_add_parameters.cpp
@@ -29,8 +29,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassAddParameters::~FuzzerPassAddParameters() = default;
-
 void FuzzerPassAddParameters::Apply() {
   // Compute type candidates for the new parameter.
   std::vector<uint32_t> type_candidates;
diff --git a/source/fuzz/fuzzer_pass_add_parameters.h b/source/fuzz/fuzzer_pass_add_parameters.h
index f1261ae..47dde39 100644
--- a/source/fuzz/fuzzer_pass_add_parameters.h
+++ b/source/fuzz/fuzzer_pass_add_parameters.h
@@ -32,8 +32,6 @@
                           FuzzerContext* fuzzer_context,
                           protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassAddParameters() override;
-
   void Apply() override;
 
  private:
diff --git a/source/fuzz/fuzzer_pass_add_relaxed_decorations.cpp b/source/fuzz/fuzzer_pass_add_relaxed_decorations.cpp
index a2497df..58c6d1b 100644
--- a/source/fuzz/fuzzer_pass_add_relaxed_decorations.cpp
+++ b/source/fuzz/fuzzer_pass_add_relaxed_decorations.cpp
@@ -26,8 +26,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassAddRelaxedDecorations::~FuzzerPassAddRelaxedDecorations() = default;
-
 void FuzzerPassAddRelaxedDecorations::Apply() {
   // Consider every instruction in every block in every function.
   for (auto& function : *GetIRContext()->module()) {
diff --git a/source/fuzz/fuzzer_pass_add_relaxed_decorations.h b/source/fuzz/fuzzer_pass_add_relaxed_decorations.h
index 897b821..723c4a0 100644
--- a/source/fuzz/fuzzer_pass_add_relaxed_decorations.h
+++ b/source/fuzz/fuzzer_pass_add_relaxed_decorations.h
@@ -28,8 +28,6 @@
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassAddRelaxedDecorations() override;
-
   void Apply() override;
 };
 
diff --git a/source/fuzz/fuzzer_pass_add_stores.cpp b/source/fuzz/fuzzer_pass_add_stores.cpp
index 46efc64..f89428d 100644
--- a/source/fuzz/fuzzer_pass_add_stores.cpp
+++ b/source/fuzz/fuzzer_pass_add_stores.cpp
@@ -27,8 +27,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassAddStores::~FuzzerPassAddStores() = default;
-
 void FuzzerPassAddStores::Apply() {
   ForEachInstructionWithInstructionDescriptor(
       [this](opt::Function* function, opt::BasicBlock* block,
diff --git a/source/fuzz/fuzzer_pass_add_stores.h b/source/fuzz/fuzzer_pass_add_stores.h
index 55ec67f..9519c38 100644
--- a/source/fuzz/fuzzer_pass_add_stores.h
+++ b/source/fuzz/fuzzer_pass_add_stores.h
@@ -30,8 +30,6 @@
                       FuzzerContext* fuzzer_context,
                       protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassAddStores();
-
   void Apply() override;
 };
 
diff --git a/source/fuzz/fuzzer_pass_add_synonyms.cpp b/source/fuzz/fuzzer_pass_add_synonyms.cpp
index 2fa0700..fd866f9 100644
--- a/source/fuzz/fuzzer_pass_add_synonyms.cpp
+++ b/source/fuzz/fuzzer_pass_add_synonyms.cpp
@@ -29,8 +29,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassAddSynonyms::~FuzzerPassAddSynonyms() = default;
-
 void FuzzerPassAddSynonyms::Apply() {
   ForEachInstructionWithInstructionDescriptor(
       [this](opt::Function* function, opt::BasicBlock* block,
@@ -81,6 +79,8 @@
           case protobufs::TransformationAddSynonym::ADD_ZERO:
           case protobufs::TransformationAddSynonym::SUB_ZERO:
           case protobufs::TransformationAddSynonym::LOGICAL_OR:
+          case protobufs::TransformationAddSynonym::BITWISE_OR:
+          case protobufs::TransformationAddSynonym::BITWISE_XOR:
             // Create a zero constant to be used as an operand of the synonymous
             // instruction.
             FindOrCreateZeroConstant(existing_synonym->type_id(), false);
diff --git a/source/fuzz/fuzzer_pass_add_synonyms.h b/source/fuzz/fuzzer_pass_add_synonyms.h
index dcfb938..ccf4a88 100644
--- a/source/fuzz/fuzzer_pass_add_synonyms.h
+++ b/source/fuzz/fuzzer_pass_add_synonyms.h
@@ -29,8 +29,6 @@
                         FuzzerContext* fuzzer_context,
                         protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassAddSynonyms() override;
-
   void Apply() override;
 };
 
diff --git a/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp b/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp
index 453448b..e2eaaa0 100644
--- a/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp
+++ b/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp
@@ -28,9 +28,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassAddVectorShuffleInstructions::
-    ~FuzzerPassAddVectorShuffleInstructions() = default;
-
 void FuzzerPassAddVectorShuffleInstructions::Apply() {
   ForEachInstructionWithInstructionDescriptor(
       [this](opt::Function* function, opt::BasicBlock* block,
diff --git a/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h b/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h
index 99b9f24..c5af374 100644
--- a/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h
+++ b/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h
@@ -28,8 +28,6 @@
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassAddVectorShuffleInstructions();
-
   void Apply() override;
 };
 
diff --git a/source/fuzz/fuzzer_pass_adjust_branch_weights.cpp b/source/fuzz/fuzzer_pass_adjust_branch_weights.cpp
index 1d6d434..3c4f380 100644
--- a/source/fuzz/fuzzer_pass_adjust_branch_weights.cpp
+++ b/source/fuzz/fuzzer_pass_adjust_branch_weights.cpp
@@ -28,8 +28,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassAdjustBranchWeights::~FuzzerPassAdjustBranchWeights() = default;
-
 void FuzzerPassAdjustBranchWeights::Apply() {
   // For all OpBranchConditional instructions,
   // randomly applies the transformation.
diff --git a/source/fuzz/fuzzer_pass_adjust_branch_weights.h b/source/fuzz/fuzzer_pass_adjust_branch_weights.h
index 5b2b33f..de2f33d 100644
--- a/source/fuzz/fuzzer_pass_adjust_branch_weights.h
+++ b/source/fuzz/fuzzer_pass_adjust_branch_weights.h
@@ -30,8 +30,6 @@
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassAdjustBranchWeights();
-
   void Apply() override;
 };
 
diff --git a/source/fuzz/fuzzer_pass_adjust_function_controls.cpp b/source/fuzz/fuzzer_pass_adjust_function_controls.cpp
index aa62d2f..b38bd21 100644
--- a/source/fuzz/fuzzer_pass_adjust_function_controls.cpp
+++ b/source/fuzz/fuzzer_pass_adjust_function_controls.cpp
@@ -26,8 +26,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassAdjustFunctionControls::~FuzzerPassAdjustFunctionControls() = default;
-
 void FuzzerPassAdjustFunctionControls::Apply() {
   // Consider every function in the module.
   for (auto& function : *GetIRContext()->module()) {
diff --git a/source/fuzz/fuzzer_pass_adjust_function_controls.h b/source/fuzz/fuzzer_pass_adjust_function_controls.h
index 5fdbe43..5ef32a1 100644
--- a/source/fuzz/fuzzer_pass_adjust_function_controls.h
+++ b/source/fuzz/fuzzer_pass_adjust_function_controls.h
@@ -28,8 +28,6 @@
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassAdjustFunctionControls() override;
-
   void Apply() override;
 };
 
diff --git a/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp b/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp
index f7addff..0f7cf03 100644
--- a/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp
+++ b/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp
@@ -26,8 +26,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassAdjustLoopControls::~FuzzerPassAdjustLoopControls() = default;
-
 void FuzzerPassAdjustLoopControls::Apply() {
   // Consider every merge instruction in the module (via looking through all
   // functions and blocks).
diff --git a/source/fuzz/fuzzer_pass_adjust_loop_controls.h b/source/fuzz/fuzzer_pass_adjust_loop_controls.h
index f133b2d..4ca670b 100644
--- a/source/fuzz/fuzzer_pass_adjust_loop_controls.h
+++ b/source/fuzz/fuzzer_pass_adjust_loop_controls.h
@@ -28,8 +28,6 @@
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassAdjustLoopControls() override;
-
   void Apply() override;
 };
 
diff --git a/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp b/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp
index 68f0ca7..778d43f 100644
--- a/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp
+++ b/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp
@@ -27,9 +27,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassAdjustMemoryOperandsMasks::~FuzzerPassAdjustMemoryOperandsMasks() =
-    default;
-
 void FuzzerPassAdjustMemoryOperandsMasks::Apply() {
   // Consider every block in every function.
   for (auto& function : *GetIRContext()->module()) {
diff --git a/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h b/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h
index 699dcb5..a068b8d 100644
--- a/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h
+++ b/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h
@@ -29,8 +29,6 @@
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassAdjustMemoryOperandsMasks();
-
   void Apply() override;
 };
 
diff --git a/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp b/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp
index 83b1854..d9b4e29 100644
--- a/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp
+++ b/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp
@@ -26,9 +26,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassAdjustSelectionControls::~FuzzerPassAdjustSelectionControls() =
-    default;
-
 void FuzzerPassAdjustSelectionControls::Apply() {
   // Consider every merge instruction in the module (via looking through all
   // functions and blocks).
diff --git a/source/fuzz/fuzzer_pass_adjust_selection_controls.h b/source/fuzz/fuzzer_pass_adjust_selection_controls.h
index 910b40d..6931f94 100644
--- a/source/fuzz/fuzzer_pass_adjust_selection_controls.h
+++ b/source/fuzz/fuzzer_pass_adjust_selection_controls.h
@@ -28,8 +28,6 @@
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassAdjustSelectionControls() override;
-
   void Apply() override;
 };
 
diff --git a/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp b/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp
index 38553d2..7120831 100644
--- a/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp
+++ b/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp
@@ -32,8 +32,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassApplyIdSynonyms::~FuzzerPassApplyIdSynonyms() = default;
-
 void FuzzerPassApplyIdSynonyms::Apply() {
   // Compute a closure of data synonym facts, to enrich the pool of synonyms
   // that are available.
diff --git a/source/fuzz/fuzzer_pass_apply_id_synonyms.h b/source/fuzz/fuzzer_pass_apply_id_synonyms.h
index 5deac10..b402b50 100644
--- a/source/fuzz/fuzzer_pass_apply_id_synonyms.h
+++ b/source/fuzz/fuzzer_pass_apply_id_synonyms.h
@@ -30,8 +30,6 @@
                             FuzzerContext* fuzzer_context,
                             protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassApplyIdSynonyms() override;
-
   void Apply() override;
 
  private:
diff --git a/source/fuzz/fuzzer_pass_construct_composites.cpp b/source/fuzz/fuzzer_pass_construct_composites.cpp
index 584fa1a..1a174cf 100644
--- a/source/fuzz/fuzzer_pass_construct_composites.cpp
+++ b/source/fuzz/fuzzer_pass_construct_composites.cpp
@@ -16,6 +16,7 @@
 
 #include <memory>
 
+#include "source/fuzz/available_instructions.h"
 #include "source/fuzz/fuzzer_util.h"
 #include "source/fuzz/transformation_composite_construct.h"
 
@@ -29,8 +30,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassConstructComposites::~FuzzerPassConstructComposites() = default;
-
 void FuzzerPassConstructComposites::Apply() {
   // Gather up the ids of all composite types, but skip block-/buffer
   // block-decorated struct types.
@@ -44,12 +43,40 @@
     }
   }
 
+  if (composite_type_ids.empty()) {
+    // There are no composite types, so this fuzzer pass cannot do anything.
+    return;
+  }
+
+  AvailableInstructions available_composite_constituents(
+      GetIRContext(),
+      [this](opt::IRContext* ir_context, opt::Instruction* inst) -> bool {
+        if (!inst->result_id() || !inst->type_id()) {
+          return false;
+        }
+
+        // If the id is irrelevant, we can use it since it will not
+        // participate in DataSynonym fact. Otherwise, we should be able
+        // to produce a synonym out of the id.
+        return GetTransformationContext()->GetFactManager()->IdIsIrrelevant(
+                   inst->result_id()) ||
+               fuzzerutil::CanMakeSynonymOf(ir_context,
+                                            *GetTransformationContext(), inst);
+      });
+
   ForEachInstructionWithInstructionDescriptor(
-      [this, &composite_type_ids](
-          opt::Function* function, opt::BasicBlock* block,
+      [this, &available_composite_constituents, &composite_type_ids](
+          opt::Function* /*unused*/, opt::BasicBlock* /*unused*/,
           opt::BasicBlock::iterator inst_it,
           const protobufs::InstructionDescriptor& instruction_descriptor)
           -> void {
+        // Randomly decide whether to try inserting a composite construction
+        // here.
+        if (!GetFuzzerContext()->ChoosePercentage(
+                GetFuzzerContext()->GetChanceOfConstructingComposite())) {
+          return;
+        }
+
         // Check whether it is legitimate to insert a composite construction
         // before the instruction.
         if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(
@@ -57,36 +84,21 @@
           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;
-        auto available_instructions = FindAvailableInstructions(
-            function, block, inst_it,
-            [this](opt::IRContext* ir_context, opt::Instruction* inst) {
-              if (!inst->result_id() || !inst->type_id()) {
-                return false;
-              }
-
-              // If the id is irrelevant, we can use it since it will not
-              // participate in DataSynonym fact. Otherwise, we should be able
-              // to produce a synonym out of the id.
-              return GetTransformationContext()
-                         ->GetFactManager()
-                         ->IdIsIrrelevant(inst->result_id()) ||
-                     fuzzerutil::CanMakeSynonymOf(
-                         ir_context, *GetTransformationContext(), inst);
-            });
-        for (auto instruction : available_instructions) {
-          RecordAvailableInstruction(instruction,
-                                     &type_id_to_available_instructions);
+        auto available_instructions =
+            available_composite_constituents.GetAvailableBeforeInstruction(
+                &*inst_it);
+        for (uint32_t available_instruction_index = 0;
+             available_instruction_index < available_instructions.size();
+             available_instruction_index++) {
+          opt::Instruction* inst =
+              available_instructions[available_instruction_index];
+          type_id_to_available_instructions[inst->type_id()].push_back(
+              inst->result_id());
         }
 
         // At this point, |composite_type_ids| captures all the composite types
@@ -94,68 +106,41 @@
         // 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;
+        // Now we choose a composite type to construct, building it from
+        // available constituent components and using zero constants if suitable
+        // components are not available.
+
         std::vector<uint32_t> constructor_arguments;
+        uint32_t chosen_composite_type =
+            composite_type_ids[GetFuzzerContext()->RandomIndex(
+                composite_type_ids)];
 
-        // 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 next_composite_to_try_constructing =
-              GetFuzzerContext()->RemoveAtRandomIndex(
-                  &composites_to_try_constructing);
-
-          // Now try to construct a composite of this type, using an appropriate
-          // helper method depending on the kind of composite type.
-          auto composite_type_inst = GetIRContext()->get_def_use_mgr()->GetDef(
-              next_composite_to_try_constructing);
-          switch (composite_type_inst->opcode()) {
-            case SpvOpTypeArray:
-              constructor_arguments = FindComponentsToConstructArray(
-                  *composite_type_inst, type_id_to_available_instructions);
-              break;
-            case SpvOpTypeMatrix:
-              constructor_arguments = FindComponentsToConstructMatrix(
-                  *composite_type_inst, type_id_to_available_instructions);
-              break;
-            case SpvOpTypeStruct:
-              constructor_arguments = FindComponentsToConstructStruct(
-                  *composite_type_inst, type_id_to_available_instructions);
-              break;
-            case SpvOpTypeVector:
-              constructor_arguments = FindComponentsToConstructVector(
-                  *composite_type_inst, type_id_to_available_instructions);
-              break;
-            default:
-              assert(false &&
-                     "The space of possible composite types should be covered "
-                     "by the above cases.");
-              break;
-          }
-          if (!constructor_arguments.empty()) {
-            // We succeeded!  Note the composite type we finally settled on, and
-            // exit from the loop.
-            chosen_composite_type = next_composite_to_try_constructing;
+        // Construct a composite of this type, using an appropriate helper
+        // method depending on the kind of composite type.
+        auto composite_type_inst =
+            GetIRContext()->get_def_use_mgr()->GetDef(chosen_composite_type);
+        switch (composite_type_inst->opcode()) {
+          case SpvOpTypeArray:
+            constructor_arguments = FindComponentsToConstructArray(
+                *composite_type_inst, type_id_to_available_instructions);
             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.empty());
-          return;
+          case SpvOpTypeMatrix:
+            constructor_arguments = FindComponentsToConstructMatrix(
+                *composite_type_inst, type_id_to_available_instructions);
+            break;
+          case SpvOpTypeStruct:
+            constructor_arguments = FindComponentsToConstructStruct(
+                *composite_type_inst, type_id_to_available_instructions);
+            break;
+          case SpvOpTypeVector:
+            constructor_arguments = FindComponentsToConstructVector(
+                *composite_type_inst, type_id_to_available_instructions);
+            break;
+          default:
+            assert(false &&
+                   "The space of possible composite types should be covered "
+                   "by the above cases.");
+            break;
         }
         assert(!constructor_arguments.empty());
 
@@ -166,15 +151,6 @@
       });
 }
 
-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::vector<uint32_t>
 FuzzerPassConstructComposites::FindComponentsToConstructArray(
     const opt::Instruction& array_type_instruction,
@@ -190,13 +166,6 @@
   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 {};
-  }
-
   uint32_t array_length =
       GetIRContext()
           ->get_def_use_mgr()
@@ -205,10 +174,14 @@
 
   std::vector<uint32_t> result;
   for (uint32_t index = 0; index < array_length; index++) {
-    result.push_back(available_instructions
-                         ->second[GetFuzzerContext()->RandomIndex(
-                             available_instructions->second)]
-                         ->result_id());
+    if (available_instructions == type_id_to_available_instructions.cend()) {
+      // No suitable instructions are available, so use a zero constant
+      result.push_back(FindOrCreateZeroConstant(element_type_id, true));
+    } else {
+      result.push_back(
+          available_instructions->second[GetFuzzerContext()->RandomIndex(
+              available_instructions->second)]);
+    }
   }
   return result;
 }
@@ -228,19 +201,17 @@
   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 {};
-  }
   std::vector<uint32_t> result;
   for (uint32_t index = 0;
        index < matrix_type_instruction.GetSingleWordInOperand(1); index++) {
-    result.push_back(available_instructions
-                         ->second[GetFuzzerContext()->RandomIndex(
-                             available_instructions->second)]
-                         ->result_id());
+    if (available_instructions == type_id_to_available_instructions.cend()) {
+      // No suitable components are available, so use a zero constant.
+      result.push_back(FindOrCreateZeroConstant(element_type_id, true));
+    } else {
+      result.push_back(
+          available_instructions->second[GetFuzzerContext()->RandomIndex(
+              available_instructions->second)]);
+    }
   }
   return result;
 }
@@ -263,14 +234,14 @@
     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 {};
+      // No suitable component is available for this element type, so use a zero
+      // constant.
+      result.push_back(FindOrCreateZeroConstant(element_type_id, true));
+    } else {
+      result.push_back(
+          available_instructions->second[GetFuzzerContext()->RandomIndex(
+              available_instructions->second)]);
     }
-    result.push_back(available_instructions
-                         ->second[GetFuzzerContext()->RandomIndex(
-                             available_instructions->second)]
-                         ->result_id());
   }
   return result;
 }
@@ -325,12 +296,13 @@
   // (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;
+
+  // The instructions result ids we will use to construct the vector, in no
+  // particular order at this stage.
+  std::vector<uint32_t> result;
 
   while (vector_slots_used < element_count) {
-    std::vector<opt::Instruction*> instructions_to_choose_from;
+    std::vector<uint32_t> instructions_to_choose_from;
     for (auto& entry : smaller_vector_type_id_to_width) {
       if (entry.second >
           std::min(element_count - 1, element_count - vector_slots_used)) {
@@ -345,19 +317,16 @@
                                          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 {};
-    }
-    auto instruction_to_use =
-        instructions_to_choose_from[GetFuzzerContext()->RandomIndex(
-            instructions_to_choose_from)];
-    instructions_to_use.push_back(instruction_to_use);
+    // If there are no instructions to choose from then use a zero constant,
+    // otherwise select one of the instructions at random.
+    uint32_t id_of_instruction_to_use =
+        instructions_to_choose_from.empty()
+            ? FindOrCreateZeroConstant(element_type_id, true)
+            : instructions_to_choose_from[GetFuzzerContext()->RandomIndex(
+                  instructions_to_choose_from)];
+    opt::Instruction* instruction_to_use =
+        GetIRContext()->get_def_use_mgr()->GetDef(id_of_instruction_to_use);
+    result.push_back(instruction_to_use->result_id());
     auto chosen_type =
         GetIRContext()->get_type_mgr()->GetType(instruction_to_use->type_id());
     if (chosen_type->AsVector()) {
@@ -373,14 +342,7 @@
   }
   assert(vector_slots_used == element_count);
 
-  std::vector<uint32_t> result;
-  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);
+  GetFuzzerContext()->Shuffle(&result);
   return result;
 }
 
diff --git a/source/fuzz/fuzzer_pass_construct_composites.h b/source/fuzz/fuzzer_pass_construct_composites.h
index c140bde..333ac93 100644
--- a/source/fuzz/fuzzer_pass_construct_composites.h
+++ b/source/fuzz/fuzzer_pass_construct_composites.h
@@ -15,7 +15,7 @@
 #ifndef SOURCE_FUZZ_FUZZER_PASS_CONSTRUCT_COMPOSITES_H_
 #define SOURCE_FUZZ_FUZZER_PASS_CONSTRUCT_COMPOSITES_H_
 
-#include <map>
+#include <unordered_map>
 #include <vector>
 
 #include "source/fuzz/fuzzer_pass.h"
@@ -31,23 +31,12 @@
       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);
+  // Used to map a type id to the ids of relevant instructions of the type.
+  using TypeIdToInstructions =
+      std::unordered_map<uint32_t, std::vector<uint32_t>>;
 
   // Requires that |array_type_instruction| has opcode OpTypeArray.
   // Attempts to find suitable instruction result ids from the values of
diff --git a/source/fuzz/fuzzer_pass_copy_objects.cpp b/source/fuzz/fuzzer_pass_copy_objects.cpp
index 9f7bbd6..09b5368 100644
--- a/source/fuzz/fuzzer_pass_copy_objects.cpp
+++ b/source/fuzz/fuzzer_pass_copy_objects.cpp
@@ -28,8 +28,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassCopyObjects::~FuzzerPassCopyObjects() = default;
-
 void FuzzerPassCopyObjects::Apply() {
   ForEachInstructionWithInstructionDescriptor(
       [this](opt::Function* function, opt::BasicBlock* block,
diff --git a/source/fuzz/fuzzer_pass_copy_objects.h b/source/fuzz/fuzzer_pass_copy_objects.h
index 8de382e..461cd97 100644
--- a/source/fuzz/fuzzer_pass_copy_objects.h
+++ b/source/fuzz/fuzzer_pass_copy_objects.h
@@ -28,8 +28,6 @@
                         FuzzerContext* fuzzer_context,
                         protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassCopyObjects();
-
   void Apply() override;
 };
 
diff --git a/source/fuzz/fuzzer_pass_donate_modules.cpp b/source/fuzz/fuzzer_pass_donate_modules.cpp
index 4cd4804..2f2ed50 100644
--- a/source/fuzz/fuzzer_pass_donate_modules.cpp
+++ b/source/fuzz/fuzzer_pass_donate_modules.cpp
@@ -50,8 +50,6 @@
                  transformations),
       donor_suppliers_(donor_suppliers) {}
 
-FuzzerPassDonateModules::~FuzzerPassDonateModules() = default;
-
 void FuzzerPassDonateModules::Apply() {
   // If there are no donor suppliers, this fuzzer pass is a no-op.
   if (donor_suppliers_.empty()) {
@@ -1202,11 +1200,14 @@
         false);
   }
 
-  // Add the function in a livesafe manner.
-  ApplyTransformation(TransformationAddFunction(
+  // Try to add the function in a livesafe manner. This may fail due to edge
+  // cases, e.g. where adding loop limiters changes dominance such that the
+  // module becomes invalid. It would be ideal to handle all such edge cases,
+  // but as they are rare it is more pragmatic to bail out of making the
+  // function livesafe if the transformation's precondition fails to hold.
+  return MaybeApplyTransformation(TransformationAddFunction(
       donated_instructions, loop_limiter_variable_id, loop_limit, loop_limiters,
       kill_unreachable_return_value_id, access_chain_clamping_info));
-  return true;
 }
 
 }  // namespace fuzz
diff --git a/source/fuzz/fuzzer_pass_donate_modules.h b/source/fuzz/fuzzer_pass_donate_modules.h
index 0424cec..1581a8a 100644
--- a/source/fuzz/fuzzer_pass_donate_modules.h
+++ b/source/fuzz/fuzzer_pass_donate_modules.h
@@ -33,8 +33,6 @@
       protobufs::TransformationSequence* transformations,
       const std::vector<fuzzerutil::ModuleSupplier>& donor_suppliers);
 
-  ~FuzzerPassDonateModules();
-
   void Apply() override;
 
   // Donates the global declarations and functions of |donor_ir_context| into
diff --git a/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.cpp b/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.cpp
index 1651a9d..e08d65b 100644
--- a/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.cpp
+++ b/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.cpp
@@ -29,9 +29,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassDuplicateRegionsWithSelections::
-    ~FuzzerPassDuplicateRegionsWithSelections() = default;
-
 void FuzzerPassDuplicateRegionsWithSelections::Apply() {
   // Iterate over all of the functions in the module.
   for (auto& function : *GetIRContext()->module()) {
diff --git a/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.h b/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.h
index 3fae698..7cb1197 100644
--- a/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.h
+++ b/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.h
@@ -31,8 +31,6 @@
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassDuplicateRegionsWithSelections() override;
-
   void Apply() override;
 };
 
diff --git a/source/fuzz/fuzzer_pass_expand_vector_reductions.cpp b/source/fuzz/fuzzer_pass_expand_vector_reductions.cpp
index 1416fe0..e25dcbc 100644
--- a/source/fuzz/fuzzer_pass_expand_vector_reductions.cpp
+++ b/source/fuzz/fuzzer_pass_expand_vector_reductions.cpp
@@ -28,8 +28,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassExpandVectorReductions::~FuzzerPassExpandVectorReductions() = default;
-
 void FuzzerPassExpandVectorReductions::Apply() {
   for (auto& function : *GetIRContext()->module()) {
     for (auto& block : function) {
diff --git a/source/fuzz/fuzzer_pass_expand_vector_reductions.h b/source/fuzz/fuzzer_pass_expand_vector_reductions.h
index ae3238b..ed0225d 100644
--- a/source/fuzz/fuzzer_pass_expand_vector_reductions.h
+++ b/source/fuzz/fuzzer_pass_expand_vector_reductions.h
@@ -30,8 +30,6 @@
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassExpandVectorReductions();
-
   void Apply() override;
 };
 
diff --git a/source/fuzz/fuzzer_pass_flatten_conditional_branches.cpp b/source/fuzz/fuzzer_pass_flatten_conditional_branches.cpp
index 1e21aa5..84da7a7 100644
--- a/source/fuzz/fuzzer_pass_flatten_conditional_branches.cpp
+++ b/source/fuzz/fuzzer_pass_flatten_conditional_branches.cpp
@@ -30,9 +30,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassFlattenConditionalBranches::~FuzzerPassFlattenConditionalBranches() =
-    default;
-
 void FuzzerPassFlattenConditionalBranches::Apply() {
   for (auto& function : *GetIRContext()->module()) {
     // Get all the selection headers that we want to flatten. We need to collect
diff --git a/source/fuzz/fuzzer_pass_flatten_conditional_branches.h b/source/fuzz/fuzzer_pass_flatten_conditional_branches.h
index 76f7782..e7d7dc3 100644
--- a/source/fuzz/fuzzer_pass_flatten_conditional_branches.h
+++ b/source/fuzz/fuzzer_pass_flatten_conditional_branches.h
@@ -27,8 +27,6 @@
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassFlattenConditionalBranches() override;
-
   void Apply() override;
 
  private:
diff --git a/source/fuzz/fuzzer_pass_inline_functions.cpp b/source/fuzz/fuzzer_pass_inline_functions.cpp
index 90160d8..405afd8 100644
--- a/source/fuzz/fuzzer_pass_inline_functions.cpp
+++ b/source/fuzz/fuzzer_pass_inline_functions.cpp
@@ -29,8 +29,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassInlineFunctions::~FuzzerPassInlineFunctions() = default;
-
 void FuzzerPassInlineFunctions::Apply() {
   // |function_call_instructions| are the instructions that will be inlined.
   // First, they will be collected and then do the inlining in another loop.
diff --git a/source/fuzz/fuzzer_pass_inline_functions.h b/source/fuzz/fuzzer_pass_inline_functions.h
index 37295d1..a6ed8ca 100644
--- a/source/fuzz/fuzzer_pass_inline_functions.h
+++ b/source/fuzz/fuzzer_pass_inline_functions.h
@@ -30,8 +30,6 @@
                             FuzzerContext* fuzzer_context,
                             protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassInlineFunctions() override;
-
   void Apply() override;
 };
 
diff --git a/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.cpp b/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.cpp
index 0e40b49..675cae9 100644
--- a/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.cpp
+++ b/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.cpp
@@ -31,10 +31,10 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassInterchangeSignednessOfIntegerOperands::
-    ~FuzzerPassInterchangeSignednessOfIntegerOperands() = default;
-
 void FuzzerPassInterchangeSignednessOfIntegerOperands::Apply() {
+  assert(!GetFuzzerContext()->IsWgslCompatible() &&
+         "Cannot interchange signedness in WGSL");
+
   // Make vector keeping track of all the uses we want to replace.
   // This is a vector of pairs, where the first element is an id use descriptor
   // identifying the use of a constant id and the second is the id that should
diff --git a/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.h b/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.h
index 06882f4..11b8fb6 100644
--- a/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.h
+++ b/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.h
@@ -34,8 +34,6 @@
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassInterchangeSignednessOfIntegerOperands() override;
-
   void Apply() override;
 
  private:
diff --git a/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp b/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp
index 20575e1..5d0a222 100644
--- a/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp
+++ b/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp
@@ -29,9 +29,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassInterchangeZeroLikeConstants::
-    ~FuzzerPassInterchangeZeroLikeConstants() = default;
-
 uint32_t FuzzerPassInterchangeZeroLikeConstants::FindOrCreateToggledConstant(
     opt::Instruction* declaration) {
   // |declaration| must not be a specialization constant because we do not know
diff --git a/source/fuzz/fuzzer_pass_interchange_zero_like_constants.h b/source/fuzz/fuzzer_pass_interchange_zero_like_constants.h
index ef0f765..012f03d 100644
--- a/source/fuzz/fuzzer_pass_interchange_zero_like_constants.h
+++ b/source/fuzz/fuzzer_pass_interchange_zero_like_constants.h
@@ -35,8 +35,6 @@
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassInterchangeZeroLikeConstants() override;
-
   void Apply() override;
 
  private:
diff --git a/source/fuzz/fuzzer_pass_invert_comparison_operators.cpp b/source/fuzz/fuzzer_pass_invert_comparison_operators.cpp
index de4ff1d..542748e 100644
--- a/source/fuzz/fuzzer_pass_invert_comparison_operators.cpp
+++ b/source/fuzz/fuzzer_pass_invert_comparison_operators.cpp
@@ -28,9 +28,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassInvertComparisonOperators::~FuzzerPassInvertComparisonOperators() =
-    default;
-
 void FuzzerPassInvertComparisonOperators::Apply() {
   GetIRContext()->module()->ForEachInst([this](const opt::Instruction* inst) {
     if (!TransformationInvertComparisonOperator::IsInversionSupported(
diff --git a/source/fuzz/fuzzer_pass_invert_comparison_operators.h b/source/fuzz/fuzzer_pass_invert_comparison_operators.h
index 9c80bbb..5f015c2 100644
--- a/source/fuzz/fuzzer_pass_invert_comparison_operators.h
+++ b/source/fuzz/fuzzer_pass_invert_comparison_operators.h
@@ -29,8 +29,6 @@
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassInvertComparisonOperators() override;
-
   void Apply() override;
 };
 
diff --git a/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.cpp b/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.cpp
index f4f2a80..7bf07a4 100644
--- a/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.cpp
+++ b/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.cpp
@@ -28,9 +28,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassMakeVectorOperationsDynamic::
-    ~FuzzerPassMakeVectorOperationsDynamic() = default;
-
 void FuzzerPassMakeVectorOperationsDynamic::Apply() {
   for (auto& function : *GetIRContext()->module()) {
     for (auto& block : function) {
diff --git a/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.h b/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.h
index dd51cde..da27825 100644
--- a/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.h
+++ b/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.h
@@ -29,8 +29,6 @@
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassMakeVectorOperationsDynamic() override;
-
   void Apply() override;
 };
 
diff --git a/source/fuzz/fuzzer_pass_merge_blocks.cpp b/source/fuzz/fuzzer_pass_merge_blocks.cpp
index e66fc44..a8ec784 100644
--- a/source/fuzz/fuzzer_pass_merge_blocks.cpp
+++ b/source/fuzz/fuzzer_pass_merge_blocks.cpp
@@ -28,8 +28,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassMergeBlocks::~FuzzerPassMergeBlocks() = default;
-
 void FuzzerPassMergeBlocks::Apply() {
   // First we populate a sequence of transformations that we might consider
   // applying.
diff --git a/source/fuzz/fuzzer_pass_merge_blocks.h b/source/fuzz/fuzzer_pass_merge_blocks.h
index 1a6c2c2..66cf4c6 100644
--- a/source/fuzz/fuzzer_pass_merge_blocks.h
+++ b/source/fuzz/fuzzer_pass_merge_blocks.h
@@ -28,8 +28,6 @@
                         FuzzerContext* fuzzer_context,
                         protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassMergeBlocks();
-
   void Apply() override;
 };
 
diff --git a/source/fuzz/fuzzer_pass_merge_function_returns.cpp b/source/fuzz/fuzzer_pass_merge_function_returns.cpp
index fc9c74d..ee82eca 100644
--- a/source/fuzz/fuzzer_pass_merge_function_returns.cpp
+++ b/source/fuzz/fuzzer_pass_merge_function_returns.cpp
@@ -30,8 +30,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassMergeFunctionReturns::~FuzzerPassMergeFunctionReturns() = default;
-
 void FuzzerPassMergeFunctionReturns::Apply() {
   // The pass might add new functions to the module (due to wrapping early
   // terminator instructions in function calls), so we record the functions that
@@ -174,8 +172,9 @@
     }
 
     // Get the ids needed by the transformation.
-    uint32_t outer_header_id = GetFuzzerContext()->GetFreshId();
-    uint32_t outer_return_id = GetFuzzerContext()->GetFreshId();
+    const uint32_t outer_header_id = GetFuzzerContext()->GetFreshId();
+    const uint32_t unreachable_continue_id = GetFuzzerContext()->GetFreshId();
+    const uint32_t outer_return_id = GetFuzzerContext()->GetFreshId();
 
     bool function_is_void =
         GetIRContext()->get_type_mgr()->GetType(function->type_id())->AsVoid();
@@ -211,8 +210,8 @@
     // Apply the transformation if it is applicable (it could be inapplicable if
     // adding new predecessors to merge blocks breaks dominance rules).
     MaybeApplyTransformation(TransformationMergeFunctionReturns(
-        function->result_id(), outer_header_id, outer_return_id, return_val_id,
-        returnable_val_id, merge_blocks_info));
+        function->result_id(), outer_header_id, unreachable_continue_id,
+        outer_return_id, return_val_id, returnable_val_id, merge_blocks_info));
   }
 }
 
diff --git a/source/fuzz/fuzzer_pass_merge_function_returns.h b/source/fuzz/fuzzer_pass_merge_function_returns.h
index 3b5a668..ddd2c9d 100644
--- a/source/fuzz/fuzzer_pass_merge_function_returns.h
+++ b/source/fuzz/fuzzer_pass_merge_function_returns.h
@@ -33,8 +33,6 @@
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassMergeFunctionReturns();
-
   void Apply() override;
 
  private:
diff --git a/source/fuzz/fuzzer_pass_mutate_pointers.cpp b/source/fuzz/fuzzer_pass_mutate_pointers.cpp
index 89f5f5c..f021a97 100644
--- a/source/fuzz/fuzzer_pass_mutate_pointers.cpp
+++ b/source/fuzz/fuzzer_pass_mutate_pointers.cpp
@@ -28,8 +28,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassMutatePointers::~FuzzerPassMutatePointers() = default;
-
 void FuzzerPassMutatePointers::Apply() {
   ForEachInstructionWithInstructionDescriptor(
       [this](opt::Function* function, opt::BasicBlock* block,
diff --git a/source/fuzz/fuzzer_pass_mutate_pointers.h b/source/fuzz/fuzzer_pass_mutate_pointers.h
index f77523e..5ef6a2a 100644
--- a/source/fuzz/fuzzer_pass_mutate_pointers.h
+++ b/source/fuzz/fuzzer_pass_mutate_pointers.h
@@ -28,8 +28,6 @@
                            FuzzerContext* fuzzer_context,
                            protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassMutatePointers() override;
-
   void Apply() override;
 };
 
diff --git a/source/fuzz/fuzzer_pass_obfuscate_constants.cpp b/source/fuzz/fuzzer_pass_obfuscate_constants.cpp
index d87662e..32318e8 100644
--- a/source/fuzz/fuzzer_pass_obfuscate_constants.cpp
+++ b/source/fuzz/fuzzer_pass_obfuscate_constants.cpp
@@ -34,8 +34,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassObfuscateConstants::~FuzzerPassObfuscateConstants() = default;
-
 void FuzzerPassObfuscateConstants::ObfuscateBoolConstantViaConstantPair(
     uint32_t depth, const protobufs::IdUseDescriptor& bool_constant_use,
     const std::vector<SpvOp>& greater_than_opcodes,
diff --git a/source/fuzz/fuzzer_pass_obfuscate_constants.h b/source/fuzz/fuzzer_pass_obfuscate_constants.h
index d48b37f..82b1092 100644
--- a/source/fuzz/fuzzer_pass_obfuscate_constants.h
+++ b/source/fuzz/fuzzer_pass_obfuscate_constants.h
@@ -32,8 +32,6 @@
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassObfuscateConstants() override;
-
   void Apply() override;
 
  private:
diff --git a/source/fuzz/fuzzer_pass_outline_functions.cpp b/source/fuzz/fuzzer_pass_outline_functions.cpp
index 4210125..bfde61f 100644
--- a/source/fuzz/fuzzer_pass_outline_functions.cpp
+++ b/source/fuzz/fuzzer_pass_outline_functions.cpp
@@ -31,8 +31,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassOutlineFunctions::~FuzzerPassOutlineFunctions() = default;
-
 void FuzzerPassOutlineFunctions::Apply() {
   std::vector<opt::Function*> original_functions;
   for (auto& function : *GetIRContext()->module()) {
diff --git a/source/fuzz/fuzzer_pass_outline_functions.h b/source/fuzz/fuzzer_pass_outline_functions.h
index 02022aa..45e52ff 100644
--- a/source/fuzz/fuzzer_pass_outline_functions.h
+++ b/source/fuzz/fuzzer_pass_outline_functions.h
@@ -29,8 +29,6 @@
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassOutlineFunctions();
-
   void Apply() override;
 
   // Returns a block suitable to be an entry block for a region that can be
diff --git a/source/fuzz/fuzzer_pass_permute_blocks.cpp b/source/fuzz/fuzzer_pass_permute_blocks.cpp
index 24c16fb..769c49f 100644
--- a/source/fuzz/fuzzer_pass_permute_blocks.cpp
+++ b/source/fuzz/fuzzer_pass_permute_blocks.cpp
@@ -26,8 +26,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassPermuteBlocks::~FuzzerPassPermuteBlocks() = default;
-
 void FuzzerPassPermuteBlocks::Apply() {
   // For now we do something very simple: we randomly decide whether to move a
   // block, and for each block that we do move, we push it down as far as we
diff --git a/source/fuzz/fuzzer_pass_permute_blocks.h b/source/fuzz/fuzzer_pass_permute_blocks.h
index e5a672c..e40178e 100644
--- a/source/fuzz/fuzzer_pass_permute_blocks.h
+++ b/source/fuzz/fuzzer_pass_permute_blocks.h
@@ -29,8 +29,6 @@
                           FuzzerContext* fuzzer_context,
                           protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassPermuteBlocks() override;
-
   void Apply() override;
 };
 
diff --git a/source/fuzz/fuzzer_pass_permute_function_parameters.cpp b/source/fuzz/fuzzer_pass_permute_function_parameters.cpp
index de6b03f..9a61bea 100644
--- a/source/fuzz/fuzzer_pass_permute_function_parameters.cpp
+++ b/source/fuzz/fuzzer_pass_permute_function_parameters.cpp
@@ -32,9 +32,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassPermuteFunctionParameters::~FuzzerPassPermuteFunctionParameters() =
-    default;
-
 void FuzzerPassPermuteFunctionParameters::Apply() {
   for (const auto& function : *GetIRContext()->module()) {
     uint32_t function_id = function.result_id();
diff --git a/source/fuzz/fuzzer_pass_permute_function_parameters.h b/source/fuzz/fuzzer_pass_permute_function_parameters.h
index bc1031c..a4bf2ca 100644
--- a/source/fuzz/fuzzer_pass_permute_function_parameters.h
+++ b/source/fuzz/fuzzer_pass_permute_function_parameters.h
@@ -34,8 +34,6 @@
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassPermuteFunctionParameters() override;
-
   void Apply() override;
 };
 
diff --git a/source/fuzz/fuzzer_pass_permute_function_variables.cpp b/source/fuzz/fuzzer_pass_permute_function_variables.cpp
new file mode 100644
index 0000000..a4e19e3
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_permute_function_variables.cpp
@@ -0,0 +1,73 @@
+// Copyright (c) 2021 Mostafa Ashraf
+//
+// 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_permute_function_variables.h"
+
+#include <algorithm>
+#include <numeric>
+#include <vector>
+
+#include "source/fuzz/fuzzer_context.h"
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_swap_function_variables.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassPermuteFunctionVariables::FuzzerPassPermuteFunctionVariables(
+    opt::IRContext* ir_context, TransformationContext* transformation_context,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}  // Here we call parent constructor.
+
+void FuzzerPassPermuteFunctionVariables::Apply() {
+  // Permuting OpVariable instructions in each function.
+  for (auto& function : *GetIRContext()->module()) {
+    if (!GetFuzzerContext()->ChoosePercentage(
+            GetFuzzerContext()->GetChanceOfPermutingFunctionVariables())) {
+      continue;
+    }
+
+    auto first_block = function.entry().get();
+
+    std::vector<opt::Instruction*> variables;
+    for (auto& instruction : *first_block) {
+      if (instruction.opcode() == SpvOpVariable) {
+        variables.push_back(&instruction);
+      }
+    }
+    if (variables.size() <= 1) {
+      continue;
+    }
+    do {
+      uint32_t instruction_1_index = GetFuzzerContext()->RandomIndex(variables);
+      uint32_t instruction_2_index = GetFuzzerContext()->RandomIndex(variables);
+
+      if (instruction_1_index != instruction_2_index) {
+        ApplyTransformation(TransformationSwapFunctionVariables(
+            variables[instruction_1_index]->result_id(),
+            variables[instruction_2_index]->result_id()));
+      }
+
+    } while (GetFuzzerContext()->ChoosePercentage(
+                 GetFuzzerContext()
+                     ->GetChanceOfSwappingAnotherPairOfFunctionVariables()) &&
+             variables.size() > 2);
+  }
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/source/fuzz/fuzzer_pass_permute_function_variables.h b/source/fuzz/fuzzer_pass_permute_function_variables.h
new file mode 100644
index 0000000..47f1de2
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_permute_function_variables.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2021 Mostafa Ashraf
+//
+// 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_PERMUTE_FUNCTION_VARIABLES_H_
+#define SOURCE_FUZZ_FUZZER_PASS_PERMUTE_FUNCTION_VARIABLES_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// This fuzzer pass permutes variables in functions in the module.
+class FuzzerPassPermuteFunctionVariables : public FuzzerPass {
+ public:
+  FuzzerPassPermuteFunctionVariables(
+      opt::IRContext* ir_context, TransformationContext* transformation_context,
+      FuzzerContext* fuzzer_context,
+      protobufs::TransformationSequence* transformations);
+
+  void Apply() override;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_PERMUTE_FUNCTION_VARIABLES_H_
diff --git a/source/fuzz/fuzzer_pass_permute_instructions.cpp b/source/fuzz/fuzzer_pass_permute_instructions.cpp
index 6867053..f17e018 100644
--- a/source/fuzz/fuzzer_pass_permute_instructions.cpp
+++ b/source/fuzz/fuzzer_pass_permute_instructions.cpp
@@ -29,8 +29,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassPermuteInstructions::~FuzzerPassPermuteInstructions() = default;
-
 void FuzzerPassPermuteInstructions::Apply() {
   // We are iterating over all instructions in all basic blocks.
   for (auto& function : *GetIRContext()->module()) {
diff --git a/source/fuzz/fuzzer_pass_permute_instructions.h b/source/fuzz/fuzzer_pass_permute_instructions.h
index e02ddfa..027101d 100644
--- a/source/fuzz/fuzzer_pass_permute_instructions.h
+++ b/source/fuzz/fuzzer_pass_permute_instructions.h
@@ -29,8 +29,6 @@
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassPermuteInstructions() override;
-
   void Apply() override;
 };
 
diff --git a/source/fuzz/fuzzer_pass_permute_phi_operands.cpp b/source/fuzz/fuzzer_pass_permute_phi_operands.cpp
index c379c53..f2cc523 100644
--- a/source/fuzz/fuzzer_pass_permute_phi_operands.cpp
+++ b/source/fuzz/fuzzer_pass_permute_phi_operands.cpp
@@ -32,8 +32,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassPermutePhiOperands::~FuzzerPassPermutePhiOperands() = default;
-
 void FuzzerPassPermutePhiOperands::Apply() {
   ForEachInstructionWithInstructionDescriptor(
       [this](opt::Function* /*unused*/, opt::BasicBlock* /*unused*/,
diff --git a/source/fuzz/fuzzer_pass_permute_phi_operands.h b/source/fuzz/fuzzer_pass_permute_phi_operands.h
index 974c2c1..7999956 100644
--- a/source/fuzz/fuzzer_pass_permute_phi_operands.h
+++ b/source/fuzz/fuzzer_pass_permute_phi_operands.h
@@ -29,8 +29,6 @@
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassPermutePhiOperands() override;
-
   void Apply() override;
 };
 
diff --git a/source/fuzz/fuzzer_pass_propagate_instructions_down.cpp b/source/fuzz/fuzzer_pass_propagate_instructions_down.cpp
index 7a115ae..af27a5d 100644
--- a/source/fuzz/fuzzer_pass_propagate_instructions_down.cpp
+++ b/source/fuzz/fuzzer_pass_propagate_instructions_down.cpp
@@ -27,9 +27,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassPropagateInstructionsDown::~FuzzerPassPropagateInstructionsDown() =
-    default;
-
 void FuzzerPassPropagateInstructionsDown::Apply() {
   for (const auto& function : *GetIRContext()->module()) {
     std::vector<const opt::BasicBlock*> reachable_blocks;
diff --git a/source/fuzz/fuzzer_pass_propagate_instructions_down.h b/source/fuzz/fuzzer_pass_propagate_instructions_down.h
index 536bf00..a2a0aac 100644
--- a/source/fuzz/fuzzer_pass_propagate_instructions_down.h
+++ b/source/fuzz/fuzzer_pass_propagate_instructions_down.h
@@ -28,8 +28,6 @@
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassPropagateInstructionsDown() override;
-
   void Apply() override;
 };
 
diff --git a/source/fuzz/fuzzer_pass_propagate_instructions_up.cpp b/source/fuzz/fuzzer_pass_propagate_instructions_up.cpp
index 16ec680..8cd7437 100644
--- a/source/fuzz/fuzzer_pass_propagate_instructions_up.cpp
+++ b/source/fuzz/fuzzer_pass_propagate_instructions_up.cpp
@@ -29,9 +29,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassPropagateInstructionsUp::~FuzzerPassPropagateInstructionsUp() =
-    default;
-
 void FuzzerPassPropagateInstructionsUp::Apply() {
   for (const auto& function : *GetIRContext()->module()) {
     for (const auto& block : function) {
diff --git a/source/fuzz/fuzzer_pass_propagate_instructions_up.h b/source/fuzz/fuzzer_pass_propagate_instructions_up.h
index d915b31..b89be48 100644
--- a/source/fuzz/fuzzer_pass_propagate_instructions_up.h
+++ b/source/fuzz/fuzzer_pass_propagate_instructions_up.h
@@ -29,8 +29,6 @@
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassPropagateInstructionsUp() override;
-
   void Apply() override;
 };
 
diff --git a/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp b/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp
index 8d9acaa..54e589c 100644
--- a/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp
+++ b/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp
@@ -28,9 +28,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassPushIdsThroughVariables::~FuzzerPassPushIdsThroughVariables() =
-    default;
-
 void FuzzerPassPushIdsThroughVariables::Apply() {
   ForEachInstructionWithInstructionDescriptor(
       [this](opt::Function* function, opt::BasicBlock* block,
diff --git a/source/fuzz/fuzzer_pass_push_ids_through_variables.h b/source/fuzz/fuzzer_pass_push_ids_through_variables.h
index 3ad5404..53008ee 100644
--- a/source/fuzz/fuzzer_pass_push_ids_through_variables.h
+++ b/source/fuzz/fuzzer_pass_push_ids_through_variables.h
@@ -30,8 +30,6 @@
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassPushIdsThroughVariables() override;
-
   void Apply() override;
 };
 
diff --git a/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.cpp b/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.cpp
index 139dc6e..8a83d3b 100644
--- a/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.cpp
+++ b/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.cpp
@@ -33,9 +33,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassReplaceAddsSubsMulsWithCarryingExtended::
-    ~FuzzerPassReplaceAddsSubsMulsWithCarryingExtended() = default;
-
 void FuzzerPassReplaceAddsSubsMulsWithCarryingExtended::Apply() {
   std::vector<opt::Instruction> instructions_for_transformation;
   for (auto& function : *GetIRContext()->module()) {
diff --git a/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h b/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h
index dd39e6b..0e29a6c 100644
--- a/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h
+++ b/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h
@@ -31,8 +31,6 @@
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassReplaceAddsSubsMulsWithCarryingExtended() override;
-
   void Apply() override;
 };
 
diff --git a/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.cpp b/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.cpp
index e6bebea..a516f3d 100644
--- a/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.cpp
+++ b/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.cpp
@@ -32,9 +32,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassReplaceBranchesFromDeadBlocksWithExits::
-    ~FuzzerPassReplaceBranchesFromDeadBlocksWithExits() = default;
-
 void FuzzerPassReplaceBranchesFromDeadBlocksWithExits::Apply() {
   // OpKill can only be used as a terminator in a function that is guaranteed
   // to be executed with the Fragment execution model.  We conservatively only
diff --git a/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.h b/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.h
index 62164b3..ab7e00e 100644
--- a/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.h
+++ b/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.h
@@ -30,8 +30,6 @@
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassReplaceBranchesFromDeadBlocksWithExits() override;
-
   void Apply() override;
 };
 
diff --git a/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.cpp b/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.cpp
index 6847146..f17339a 100644
--- a/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.cpp
+++ b/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.cpp
@@ -30,9 +30,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassReplaceCopyMemoriesWithLoadsStores::
-    ~FuzzerPassReplaceCopyMemoriesWithLoadsStores() = default;
-
 void FuzzerPassReplaceCopyMemoriesWithLoadsStores::Apply() {
   GetIRContext()->module()->ForEachInst([this](opt::Instruction* instruction) {
     // Randomly decide whether to replace the OpCopyMemory.
diff --git a/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h b/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h
index 9c24ac7..cffe1cb 100644
--- a/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h
+++ b/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h
@@ -30,8 +30,6 @@
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassReplaceCopyMemoriesWithLoadsStores() override;
-
   void Apply() override;
 };
 
diff --git a/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.cpp b/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.cpp
index e372924..24f2255 100644
--- a/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.cpp
+++ b/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.cpp
@@ -30,9 +30,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassReplaceCopyObjectsWithStoresLoads::
-    ~FuzzerPassReplaceCopyObjectsWithStoresLoads() = default;
-
 void FuzzerPassReplaceCopyObjectsWithStoresLoads::Apply() {
   GetIRContext()->module()->ForEachInst([this](opt::Instruction* instruction) {
     // Randomly decide whether to replace OpCopyObject.
diff --git a/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h b/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h
index ae03a45..e7b11ce 100644
--- a/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h
+++ b/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h
@@ -30,8 +30,6 @@
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassReplaceCopyObjectsWithStoresLoads() override;
-
   void Apply() override;
 };
 
diff --git a/source/fuzz/fuzzer_pass_replace_irrelevant_ids.cpp b/source/fuzz/fuzzer_pass_replace_irrelevant_ids.cpp
index 432addb..7e9d7ba 100644
--- a/source/fuzz/fuzzer_pass_replace_irrelevant_ids.cpp
+++ b/source/fuzz/fuzzer_pass_replace_irrelevant_ids.cpp
@@ -31,8 +31,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassReplaceIrrelevantIds::~FuzzerPassReplaceIrrelevantIds() = default;
-
 void FuzzerPassReplaceIrrelevantIds::Apply() {
   // Keep track of the irrelevant ids. This includes all the ids that are
   // irrelevant according to the fact manager and that are still present in the
diff --git a/source/fuzz/fuzzer_pass_replace_irrelevant_ids.h b/source/fuzz/fuzzer_pass_replace_irrelevant_ids.h
index ab3f01d..1dc6b5d 100644
--- a/source/fuzz/fuzzer_pass_replace_irrelevant_ids.h
+++ b/source/fuzz/fuzzer_pass_replace_irrelevant_ids.h
@@ -30,8 +30,6 @@
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassReplaceIrrelevantIds();
-
   void Apply() override;
 };
 }  // namespace fuzz
diff --git a/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp b/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp
index c3e6578..0890c2f 100644
--- a/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp
+++ b/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp
@@ -30,9 +30,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassReplaceLinearAlgebraInstructions::
-    ~FuzzerPassReplaceLinearAlgebraInstructions() = default;
-
 void FuzzerPassReplaceLinearAlgebraInstructions::Apply() {
   // For each instruction, checks whether it is a linear algebra instruction. In
   // this case, the transformation is randomly applied.
diff --git a/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h b/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h
index 2c6126c..5d2f204 100644
--- a/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h
+++ b/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h
@@ -29,8 +29,6 @@
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassReplaceLinearAlgebraInstructions();
-
   void Apply() override;
 };
 
diff --git a/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.cpp b/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.cpp
index 7690ac4..f2cf80f 100644
--- a/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.cpp
+++ b/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.cpp
@@ -31,9 +31,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassReplaceLoadsStoresWithCopyMemories::
-    ~FuzzerPassReplaceLoadsStoresWithCopyMemories() = default;
-
 void FuzzerPassReplaceLoadsStoresWithCopyMemories::Apply() {
   // We look for matching pairs of instructions OpLoad and
   // OpStore within the same block. Potential instructions OpLoad to be matched
diff --git a/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h b/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h
index 67871db..f30fc2b 100644
--- a/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h
+++ b/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h
@@ -30,8 +30,6 @@
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassReplaceLoadsStoresWithCopyMemories() override;
-
   void Apply() override;
 };
 
diff --git a/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.cpp b/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.cpp
index 433cf74..b0a3d57 100644
--- a/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.cpp
+++ b/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.cpp
@@ -28,9 +28,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassReplaceOpPhiIdsFromDeadPredecessors::
-    ~FuzzerPassReplaceOpPhiIdsFromDeadPredecessors() = default;
-
 void FuzzerPassReplaceOpPhiIdsFromDeadPredecessors::Apply() {
   // Keep a vector of the transformations to apply.
   std::vector<TransformationReplaceOpPhiIdFromDeadPredecessor> transformations;
diff --git a/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.h b/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.h
index 972c5f9..a2bc188 100644
--- a/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.h
+++ b/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.h
@@ -29,8 +29,6 @@
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassReplaceOpPhiIdsFromDeadPredecessors();
-
   void Apply() override;
 };
 }  // namespace fuzz
diff --git a/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.cpp b/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.cpp
index c3db0ef..10bb90a 100644
--- a/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.cpp
+++ b/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.cpp
@@ -31,9 +31,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassReplaceOpSelectsWithConditionalBranches::
-    ~FuzzerPassReplaceOpSelectsWithConditionalBranches() = default;
-
 void FuzzerPassReplaceOpSelectsWithConditionalBranches::Apply() {
   // Keep track of the instructions that we want to replace. We need to collect
   // them in a vector, since it's not safe to modify the module while iterating
diff --git a/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.h b/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.h
index 04c6cc6..ec74389 100644
--- a/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.h
+++ b/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.h
@@ -29,8 +29,6 @@
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassReplaceOpSelectsWithConditionalBranches() override;
-
   void Apply() override;
 
  private:
diff --git a/source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp b/source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp
index 6b3a63b..5c256bb 100644
--- a/source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp
+++ b/source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp
@@ -31,9 +31,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassReplaceParameterWithGlobal::~FuzzerPassReplaceParameterWithGlobal() =
-    default;
-
 void FuzzerPassReplaceParameterWithGlobal::Apply() {
   for (const auto& function : *GetIRContext()->module()) {
     if (fuzzerutil::FunctionIsEntryPoint(GetIRContext(),
diff --git a/source/fuzz/fuzzer_pass_replace_parameter_with_global.h b/source/fuzz/fuzzer_pass_replace_parameter_with_global.h
index 25011bd..2ae4946 100644
--- a/source/fuzz/fuzzer_pass_replace_parameter_with_global.h
+++ b/source/fuzz/fuzzer_pass_replace_parameter_with_global.h
@@ -29,8 +29,6 @@
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassReplaceParameterWithGlobal() override;
-
   void Apply() override;
 };
 
diff --git a/source/fuzz/fuzzer_pass_replace_params_with_struct.cpp b/source/fuzz/fuzzer_pass_replace_params_with_struct.cpp
index 0e0610f..c045e19 100644
--- a/source/fuzz/fuzzer_pass_replace_params_with_struct.cpp
+++ b/source/fuzz/fuzzer_pass_replace_params_with_struct.cpp
@@ -31,9 +31,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassReplaceParamsWithStruct::~FuzzerPassReplaceParamsWithStruct() =
-    default;
-
 void FuzzerPassReplaceParamsWithStruct::Apply() {
   for (const auto& function : *GetIRContext()->module()) {
     auto params =
diff --git a/source/fuzz/fuzzer_pass_replace_params_with_struct.h b/source/fuzz/fuzzer_pass_replace_params_with_struct.h
index ed1aa6b..f17f520 100644
--- a/source/fuzz/fuzzer_pass_replace_params_with_struct.h
+++ b/source/fuzz/fuzzer_pass_replace_params_with_struct.h
@@ -29,8 +29,6 @@
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassReplaceParamsWithStruct() override;
-
   void Apply() override;
 };
 
diff --git a/source/fuzz/fuzzer_pass_split_blocks.cpp b/source/fuzz/fuzzer_pass_split_blocks.cpp
index 481cd96..7b49355 100644
--- a/source/fuzz/fuzzer_pass_split_blocks.cpp
+++ b/source/fuzz/fuzzer_pass_split_blocks.cpp
@@ -29,8 +29,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassSplitBlocks::~FuzzerPassSplitBlocks() = default;
-
 void FuzzerPassSplitBlocks::Apply() {
   // Gather up pointers to all the blocks in the module.  We are then able to
   // iterate over these pointers and split the blocks to which they point;
diff --git a/source/fuzz/fuzzer_pass_split_blocks.h b/source/fuzz/fuzzer_pass_split_blocks.h
index 0ece48a..58f10dd 100644
--- a/source/fuzz/fuzzer_pass_split_blocks.h
+++ b/source/fuzz/fuzzer_pass_split_blocks.h
@@ -29,8 +29,6 @@
                         FuzzerContext* fuzzer_context,
                         protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassSplitBlocks() override;
-
   void Apply() override;
 };
 
diff --git a/source/fuzz/fuzzer_pass_swap_commutable_operands.cpp b/source/fuzz/fuzzer_pass_swap_commutable_operands.cpp
index 321e8ef..27fadd1 100644
--- a/source/fuzz/fuzzer_pass_swap_commutable_operands.cpp
+++ b/source/fuzz/fuzzer_pass_swap_commutable_operands.cpp
@@ -28,8 +28,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassSwapCommutableOperands::~FuzzerPassSwapCommutableOperands() = default;
-
 void FuzzerPassSwapCommutableOperands::Apply() {
   auto context = GetIRContext();
   // Iterates over the module's instructions and checks whether it is
diff --git a/source/fuzz/fuzzer_pass_swap_commutable_operands.h b/source/fuzz/fuzzer_pass_swap_commutable_operands.h
index 74d937d..93de172 100644
--- a/source/fuzz/fuzzer_pass_swap_commutable_operands.h
+++ b/source/fuzz/fuzzer_pass_swap_commutable_operands.h
@@ -30,8 +30,6 @@
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassSwapCommutableOperands();
-
   void Apply() override;
 };
 
diff --git a/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.cpp b/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.cpp
index 9433a61..b145b3b 100644
--- a/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.cpp
+++ b/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.cpp
@@ -31,9 +31,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassSwapBranchConditionalOperands::
-    ~FuzzerPassSwapBranchConditionalOperands() = default;
-
 void FuzzerPassSwapBranchConditionalOperands::Apply() {
   ForEachInstructionWithInstructionDescriptor(
       [this](opt::Function* /*unused*/, opt::BasicBlock* /*unused*/,
diff --git a/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h b/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h
index f84f3ba..0137f38 100644
--- a/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h
+++ b/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h
@@ -29,8 +29,6 @@
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassSwapBranchConditionalOperands() override;
-
   void Apply() override;
 };
 
diff --git a/source/fuzz/fuzzer_pass_swap_functions.cpp b/source/fuzz/fuzzer_pass_swap_functions.cpp
new file mode 100644
index 0000000..171f6cb
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_swap_functions.cpp
@@ -0,0 +1,52 @@
+// Copyright (c) 2021 Shiyu Liu
+//
+// 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_swap_functions.h"
+
+#include "source/fuzz/fuzzer_context.h"
+#include "source/fuzz/transformation_swap_two_functions.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassSwapFunctions::FuzzerPassSwapFunctions(
+    opt::IRContext* ir_context, TransformationContext* transformation_context,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
+
+void FuzzerPassSwapFunctions::Apply() {
+  // Collect all function ids in a module.
+  std::vector<uint32_t> function_ids;
+  for (auto& function : *GetIRContext()->module()) {
+    function_ids.emplace_back(function.result_id());
+  }
+
+  // Iterate through every combination of id i & j where i!=j.
+  for (size_t i = 0; i < function_ids.size(); ++i) {
+    for (size_t j = i + 1; j < function_ids.size(); ++j) {
+      // Perform function swap randomly.
+      if (!GetFuzzerContext()->ChoosePercentage(
+              GetFuzzerContext()->GetChanceOfSwappingFunctions())) {
+        continue;
+      }
+      TransformationSwapTwoFunctions transformation(function_ids[i],
+                                                    function_ids[j]);
+    }
+  }
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/source/fuzz/fuzzer_pass_swap_functions.h b/source/fuzz/fuzzer_pass_swap_functions.h
new file mode 100644
index 0000000..ac551f6
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_swap_functions.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2021 Shiyu Liu
+//
+// 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_SWAP_FUNCTIONS_H_
+#define SOURCE_FUZZ_FUZZER_PASS_SWAP_FUNCTIONS_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Randomly swap functions within a module.
+class FuzzerPassSwapFunctions : public FuzzerPass {
+ public:
+  FuzzerPassSwapFunctions(opt::IRContext* ir_context,
+                          TransformationContext* transformation_context,
+                          FuzzerContext* fuzzer_context,
+                          protobufs::TransformationSequence* transformations);
+
+  void Apply() override;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_SWAP_FUNCTIONS_H_
diff --git a/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp b/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp
index 4f26cba..e5afd9e 100644
--- a/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp
+++ b/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp
@@ -28,9 +28,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassToggleAccessChainInstruction::
-    ~FuzzerPassToggleAccessChainInstruction() = default;
-
 void FuzzerPassToggleAccessChainInstruction::Apply() {
   auto context = GetIRContext();
   // Iterates over the module's instructions and checks whether it is
diff --git a/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h b/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h
index d77c7cb..ff2f5d4 100644
--- a/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h
+++ b/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h
@@ -29,8 +29,6 @@
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassToggleAccessChainInstruction();
-
   void Apply() override;
 };
 
diff --git a/source/fuzz/fuzzer_pass_wrap_regions_in_selections.cpp b/source/fuzz/fuzzer_pass_wrap_regions_in_selections.cpp
index e6cdca4..66bbcd8 100644
--- a/source/fuzz/fuzzer_pass_wrap_regions_in_selections.cpp
+++ b/source/fuzz/fuzzer_pass_wrap_regions_in_selections.cpp
@@ -29,9 +29,6 @@
     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
                  transformations) {}
 
-FuzzerPassWrapRegionsInSelections::~FuzzerPassWrapRegionsInSelections() =
-    default;
-
 void FuzzerPassWrapRegionsInSelections::Apply() {
   for (auto& function : *GetIRContext()->module()) {
     if (!GetFuzzerContext()->ChoosePercentage(
diff --git a/source/fuzz/fuzzer_pass_wrap_regions_in_selections.h b/source/fuzz/fuzzer_pass_wrap_regions_in_selections.h
index eb28d20..822c308 100644
--- a/source/fuzz/fuzzer_pass_wrap_regions_in_selections.h
+++ b/source/fuzz/fuzzer_pass_wrap_regions_in_selections.h
@@ -29,8 +29,6 @@
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassWrapRegionsInSelections() override;
-
   void Apply() override;
 
  private:
diff --git a/source/fuzz/fuzzer_util.cpp b/source/fuzz/fuzzer_util.cpp
index 8c14db4..08b927e 100644
--- a/source/fuzz/fuzzer_util.cpp
+++ b/source/fuzz/fuzzer_util.cpp
@@ -25,6 +25,25 @@
 namespace fuzzerutil {
 namespace {
 
+// A utility class that uses RAII to change and restore the terminator
+// instruction of the |block|.
+class ChangeTerminatorRAII {
+ public:
+  explicit ChangeTerminatorRAII(opt::BasicBlock* block,
+                                opt::Instruction new_terminator)
+      : block_(block), old_terminator_(std::move(*block->terminator())) {
+    *block_->terminator() = std::move(new_terminator);
+  }
+
+  ~ChangeTerminatorRAII() {
+    *block_->terminator() = std::move(old_terminator_);
+  }
+
+ private:
+  opt::BasicBlock* block_;
+  opt::Instruction old_terminator_;
+};
+
 uint32_t MaybeGetOpConstant(opt::IRContext* ir_context,
                             const TransformationContext& transformation_context,
                             const std::vector<uint32_t>& words,
@@ -47,6 +66,34 @@
     [](spv_message_level_t, const char*, const spv_position_t&,
        const char*) -> void {};
 
+bool BuildIRContext(spv_target_env target_env,
+                    const spvtools::MessageConsumer& message_consumer,
+                    const std::vector<uint32_t>& binary_in,
+                    spv_validator_options validator_options,
+                    std::unique_ptr<spvtools::opt::IRContext>* ir_context) {
+  SpirvTools tools(target_env);
+  tools.SetMessageConsumer(message_consumer);
+  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.data(), binary_in.size(), validator_options)) {
+    message_consumer(SPV_MSG_ERROR, nullptr, {},
+                     "Initial binary is invalid; stopping.");
+    return false;
+  }
+
+  // Build the module from the input binary.
+  auto result = BuildModule(target_env, message_consumer, binary_in.data(),
+                            binary_in.size());
+  assert(result && "IRContext must be valid");
+  *ir_context = std::move(result);
+  return true;
+}
+
 bool IsFreshId(opt::IRContext* context, uint32_t id) {
   return !context->get_def_use_mgr()->GetDef(id);
 }
@@ -135,35 +182,46 @@
   return true;
 }
 
-void AddUnreachableEdgeAndUpdateOpPhis(
-    opt::IRContext* context, opt::BasicBlock* bb_from, opt::BasicBlock* bb_to,
-    uint32_t bool_id,
-    const google::protobuf::RepeatedField<google::protobuf::uint32>& phi_ids) {
-  assert(PhiIdsOkForNewEdge(context, bb_from, bb_to, phi_ids) &&
-         "Precondition on phi_ids is not satisfied");
+opt::Instruction CreateUnreachableEdgeInstruction(opt::IRContext* ir_context,
+                                                  uint32_t bb_from_id,
+                                                  uint32_t bb_to_id,
+                                                  uint32_t bool_id) {
+  const auto* bb_from = MaybeFindBlock(ir_context, bb_from_id);
+  assert(bb_from && "|bb_from_id| is invalid");
+  assert(MaybeFindBlock(ir_context, bb_to_id) && "|bb_to_id| is invalid");
   assert(bb_from->terminator()->opcode() == SpvOpBranch &&
          "Precondition on terminator of bb_from is not satisfied");
 
   // Get the id of the boolean constant to be used as the condition.
-  auto condition_inst = context->get_def_use_mgr()->GetDef(bool_id);
+  auto condition_inst = ir_context->get_def_use_mgr()->GetDef(bool_id);
   assert(condition_inst &&
          (condition_inst->opcode() == SpvOpConstantTrue ||
           condition_inst->opcode() == SpvOpConstantFalse) &&
          "|bool_id| is invalid");
 
   auto condition_value = condition_inst->opcode() == SpvOpConstantTrue;
-
-  const bool from_to_edge_already_exists = bb_from->IsSuccessor(bb_to);
-  auto successor = bb_from->terminator()->GetSingleWordInOperand(0);
+  auto successor_id = bb_from->terminator()->GetSingleWordInOperand(0);
 
   // Add the dead branch, by turning OpBranch into OpBranchConditional, and
   // ordering the targets depending on whether the given boolean corresponds to
   // true or false.
-  bb_from->terminator()->SetOpcode(SpvOpBranchConditional);
-  bb_from->terminator()->SetInOperands(
+  return opt::Instruction(
+      ir_context, SpvOpBranchConditional, 0, 0,
       {{SPV_OPERAND_TYPE_ID, {bool_id}},
-       {SPV_OPERAND_TYPE_ID, {condition_value ? successor : bb_to->id()}},
-       {SPV_OPERAND_TYPE_ID, {condition_value ? bb_to->id() : successor}}});
+       {SPV_OPERAND_TYPE_ID, {condition_value ? successor_id : bb_to_id}},
+       {SPV_OPERAND_TYPE_ID, {condition_value ? bb_to_id : successor_id}}});
+}
+
+void AddUnreachableEdgeAndUpdateOpPhis(
+    opt::IRContext* context, opt::BasicBlock* bb_from, opt::BasicBlock* bb_to,
+    uint32_t bool_id,
+    const google::protobuf::RepeatedField<google::protobuf::uint32>& phi_ids) {
+  assert(PhiIdsOkForNewEdge(context, bb_from, bb_to, phi_ids) &&
+         "Precondition on phi_ids is not satisfied");
+
+  const bool from_to_edge_already_exists = bb_from->IsSuccessor(bb_to);
+  *bb_from->terminator() = CreateUnreachableEdgeInstruction(
+      context, bb_from->id(), bb_to->id(), bool_id);
 
   // Update OpPhi instructions in the target block if this branch adds a
   // previously non-existent edge from source to target.
@@ -410,7 +468,7 @@
   std::vector<uint32_t> binary;
   context->module()->ToBinary(&binary, false);
   SpirvTools tools(context->grammar().target_env());
-  tools.SetMessageConsumer(consumer);
+  tools.SetMessageConsumer(std::move(consumer));
   return tools.Validate(binary.data(), binary.size(), validator_options);
 }
 
@@ -747,14 +805,15 @@
 
 bool GlobalVariablesMustBeDeclaredInEntryPointInterfaces(
     const opt::IRContext* ir_context) {
-  // TODO(afd): We capture the universal environments for which this requirement
-  //  holds.  The check should be refined on demand for other target
-  //  environments.
+  // TODO(afd): We capture the environments for which this requirement holds.
+  //  The check should be refined on demand for other target environments.
   switch (ir_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:
+    case SPV_ENV_VULKAN_1_0:
+    case SPV_ENV_VULKAN_1_1:
       return false;
     default:
       return true;
@@ -778,9 +837,10 @@
   }
 }
 
-void AddGlobalVariable(opt::IRContext* context, uint32_t result_id,
-                       uint32_t type_id, SpvStorageClass storage_class,
-                       uint32_t initializer_id) {
+opt::Instruction* AddGlobalVariable(opt::IRContext* context, uint32_t result_id,
+                                    uint32_t type_id,
+                                    SpvStorageClass storage_class,
+                                    uint32_t initializer_id) {
   // Check various preconditions.
   assert(result_id != 0 && "Result id can't be 0");
 
@@ -815,16 +875,20 @@
     operands.push_back({SPV_OPERAND_TYPE_ID, {initializer_id}});
   }
 
-  context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
-      context, SpvOpVariable, type_id, result_id, std::move(operands)));
+  auto new_instruction = MakeUnique<opt::Instruction>(
+      context, SpvOpVariable, type_id, result_id, std::move(operands));
+  auto result = new_instruction.get();
+  context->module()->AddGlobalValue(std::move(new_instruction));
 
   AddVariableIdToEntryPointInterfaces(context, result_id);
   UpdateModuleIdBound(context, result_id);
+
+  return result;
 }
 
-void AddLocalVariable(opt::IRContext* context, uint32_t result_id,
-                      uint32_t type_id, uint32_t function_id,
-                      uint32_t initializer_id) {
+opt::Instruction* AddLocalVariable(opt::IRContext* context, uint32_t result_id,
+                                   uint32_t type_id, uint32_t function_id,
+                                   uint32_t initializer_id) {
   // Check various preconditions.
   assert(result_id != 0 && "Result id can't be 0");
 
@@ -845,13 +909,17 @@
   auto* function = FindFunction(context, function_id);
   assert(function && "Function id is invalid");
 
-  function->begin()->begin()->InsertBefore(MakeUnique<opt::Instruction>(
+  auto new_instruction = MakeUnique<opt::Instruction>(
       context, SpvOpVariable, type_id, result_id,
       opt::Instruction::OperandList{
           {SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}},
-          {SPV_OPERAND_TYPE_ID, {initializer_id}}}));
+          {SPV_OPERAND_TYPE_ID, {initializer_id}}});
+  auto result = new_instruction.get();
+  function->begin()->begin()->InsertBefore(std::move(new_instruction));
 
   UpdateModuleIdBound(context, result_id);
+
+  return result;
 }
 
 bool HasDuplicates(const std::vector<uint32_t>& arr) {
@@ -1355,74 +1423,6 @@
   return 0;
 }
 
-void AddIntegerType(opt::IRContext* ir_context, uint32_t result_id,
-                    uint32_t width, bool is_signed) {
-  ir_context->module()->AddType(MakeUnique<opt::Instruction>(
-      ir_context, SpvOpTypeInt, 0, result_id,
-      opt::Instruction::OperandList{
-          {SPV_OPERAND_TYPE_LITERAL_INTEGER, {width}},
-          {SPV_OPERAND_TYPE_LITERAL_INTEGER, {is_signed ? 1u : 0u}}}));
-
-  UpdateModuleIdBound(ir_context, result_id);
-}
-
-void AddFloatType(opt::IRContext* ir_context, uint32_t result_id,
-                  uint32_t width) {
-  ir_context->module()->AddType(MakeUnique<opt::Instruction>(
-      ir_context, SpvOpTypeFloat, 0, result_id,
-      opt::Instruction::OperandList{
-          {SPV_OPERAND_TYPE_LITERAL_INTEGER, {width}}}));
-
-  UpdateModuleIdBound(ir_context, result_id);
-}
-
-void AddVectorType(opt::IRContext* ir_context, uint32_t result_id,
-                   uint32_t component_type_id, uint32_t element_count) {
-  const auto* component_type =
-      ir_context->get_type_mgr()->GetType(component_type_id);
-  (void)component_type;  // Make compiler happy in release mode.
-  assert(component_type &&
-         (component_type->AsInteger() || component_type->AsFloat() ||
-          component_type->AsBool()) &&
-         "|component_type_id| is invalid");
-  assert(element_count >= 2 && element_count <= 4 &&
-         "Precondition: component count must be in range [2, 4].");
-  ir_context->module()->AddType(MakeUnique<opt::Instruction>(
-      ir_context, SpvOpTypeVector, 0, result_id,
-      opt::Instruction::OperandList{
-          {SPV_OPERAND_TYPE_ID, {component_type_id}},
-          {SPV_OPERAND_TYPE_LITERAL_INTEGER, {element_count}}}));
-
-  UpdateModuleIdBound(ir_context, result_id);
-}
-
-void AddStructType(opt::IRContext* ir_context, uint32_t result_id,
-                   const std::vector<uint32_t>& component_type_ids) {
-  opt::Instruction::OperandList operands;
-  operands.reserve(component_type_ids.size());
-
-  for (auto type_id : component_type_ids) {
-    const auto* type = ir_context->get_type_mgr()->GetType(type_id);
-    (void)type;  // Make compiler happy in release mode.
-    assert(type && !type->AsFunction() && "Component's type id is invalid");
-
-    if (type->AsStruct()) {
-      // From the spec for the BuiltIn decoration:
-      // - When applied to a structure-type member, that structure type cannot
-      //   be contained as a member of another structure type.
-      assert(!MembersHaveBuiltInDecoration(ir_context, type_id) &&
-             "A member struct has BuiltIn members");
-    }
-
-    operands.push_back({SPV_OPERAND_TYPE_ID, {type_id}});
-  }
-
-  ir_context->AddType(MakeUnique<opt::Instruction>(
-      ir_context, SpvOpTypeStruct, 0, result_id, std::move(operands)));
-
-  UpdateModuleIdBound(ir_context, result_id);
-}
-
 std::vector<uint32_t> IntToWords(uint64_t value, uint32_t width,
                                  bool is_signed) {
   assert(width <= 64 && "The bit width should not be more than 64 bits");
@@ -1827,6 +1827,113 @@
   return result;
 }
 
+bool NewTerminatorPreservesDominationRules(opt::IRContext* ir_context,
+                                           uint32_t block_id,
+                                           opt::Instruction new_terminator) {
+  auto* mutated_block = MaybeFindBlock(ir_context, block_id);
+  assert(mutated_block && "|block_id| is invalid");
+
+  ChangeTerminatorRAII change_terminator_raii(mutated_block,
+                                              std::move(new_terminator));
+  opt::DominatorAnalysis dominator_analysis;
+  dominator_analysis.InitializeTree(*ir_context->cfg(),
+                                    mutated_block->GetParent());
+
+  // Check that each dominator appears before each dominated block.
+  std::unordered_map<uint32_t, size_t> positions;
+  for (const auto& block : *mutated_block->GetParent()) {
+    positions[block.id()] = positions.size();
+  }
+
+  std::queue<uint32_t> q({mutated_block->GetParent()->begin()->id()});
+  std::unordered_set<uint32_t> visited;
+  while (!q.empty()) {
+    auto block = q.front();
+    q.pop();
+    visited.insert(block);
+
+    auto success = ir_context->cfg()->block(block)->WhileEachSuccessorLabel(
+        [&positions, &visited, &dominator_analysis, block, &q](uint32_t id) {
+          if (id == block) {
+            // Handle the case when loop header and continue target are the same
+            // block.
+            return true;
+          }
+
+          if (dominator_analysis.Dominates(block, id) &&
+              positions[block] > positions[id]) {
+            // |block| dominates |id| but appears after |id| - violates
+            // domination rules.
+            return false;
+          }
+
+          if (!visited.count(id)) {
+            q.push(id);
+          }
+
+          return true;
+        });
+
+    if (!success) {
+      return false;
+    }
+  }
+
+  // For each instruction in the |block->GetParent()| function check whether
+  // all its dependencies satisfy domination rules (i.e. all id operands
+  // dominate that instruction).
+  for (const auto& block : *mutated_block->GetParent()) {
+    if (!dominator_analysis.IsReachable(&block)) {
+      // If some block is not reachable then we don't need to worry about the
+      // preservation of domination rules for its instructions.
+      continue;
+    }
+
+    for (const auto& inst : block) {
+      for (uint32_t i = 0; i < inst.NumInOperands();
+           i += inst.opcode() == SpvOpPhi ? 2 : 1) {
+        const auto& operand = inst.GetInOperand(i);
+        if (!spvIsInIdType(operand.type)) {
+          continue;
+        }
+
+        if (MaybeFindBlock(ir_context, operand.words[0])) {
+          // Ignore operands that refer to OpLabel instructions.
+          continue;
+        }
+
+        const auto* dependency_block =
+            ir_context->get_instr_block(operand.words[0]);
+        if (!dependency_block) {
+          // A global instruction always dominates all instructions in any
+          // function.
+          continue;
+        }
+
+        auto domination_target_id = inst.opcode() == SpvOpPhi
+                                        ? inst.GetSingleWordInOperand(i + 1)
+                                        : block.id();
+
+        if (!dominator_analysis.Dominates(dependency_block->id(),
+                                          domination_target_id)) {
+          return false;
+        }
+      }
+    }
+  }
+
+  return true;
+}
+
+opt::Module::iterator GetFunctionIterator(opt::IRContext* ir_context,
+                                          uint32_t function_id) {
+  return std::find_if(ir_context->module()->begin(),
+                      ir_context->module()->end(),
+                      [function_id](const opt::Function& f) {
+                        return f.result_id() == function_id;
+                      });
+}
+
 }  // namespace fuzzerutil
 }  // namespace fuzz
 }  // namespace spvtools
diff --git a/source/fuzz/fuzzer_util.h b/source/fuzz/fuzzer_util.h
index 4e6ec36..dd7bd96 100644
--- a/source/fuzz/fuzzer_util.h
+++ b/source/fuzz/fuzzer_util.h
@@ -24,6 +24,7 @@
 #include "source/opt/basic_block.h"
 #include "source/opt/instruction.h"
 #include "source/opt/ir_context.h"
+#include "source/opt/module.h"
 #include "spirv-tools/libspirv.hpp"
 
 namespace spvtools {
@@ -38,6 +39,15 @@
 // Function type that produces a SPIR-V module.
 using ModuleSupplier = std::function<std::unique_ptr<opt::IRContext>()>;
 
+// Builds a new opt::IRContext object. Returns true if successful and changes
+// the |ir_context| parameter. Otherwise (if any errors occur), returns false
+// and |ir_context| remains unchanged.
+bool BuildIRContext(spv_target_env target_env,
+                    const spvtools::MessageConsumer& message_consumer,
+                    const std::vector<uint32_t>& binary_in,
+                    spv_validator_options validator_options,
+                    std::unique_ptr<spvtools::opt::IRContext>* ir_context);
+
 // Returns true if and only if the module does not define the given id.
 bool IsFreshId(opt::IRContext* context, uint32_t id);
 
@@ -59,6 +69,16 @@
     opt::IRContext* context, opt::BasicBlock* bb_from, opt::BasicBlock* bb_to,
     const google::protobuf::RepeatedField<google::protobuf::uint32>& phi_ids);
 
+// Returns an OpBranchConditional instruction that will create an unreachable
+// branch from |bb_from_id| to |bb_to_id|. |bool_id| must be a result id of
+// either OpConstantTrue or OpConstantFalse. Based on the opcode of |bool_id|,
+// operands of the returned instruction will be positioned in a way that the
+// branch from |bb_from_id| to |bb_to_id| is always unreachable.
+opt::Instruction CreateUnreachableEdgeInstruction(opt::IRContext* ir_context,
+                                                  uint32_t bb_from_id,
+                                                  uint32_t bb_to_id,
+                                                  uint32_t bool_id);
+
 // Requires that |bool_id| is a valid result id of either OpConstantTrue or
 // OpConstantFalse, that PhiIdsOkForNewEdge(context, bb_from, bb_to, phi_ids)
 // holds, and that bb_from ends with "OpBranch %some_block".  Turns OpBranch
@@ -284,9 +304,12 @@
 // - |initializer_id| must be 0 if |storage_class| is Workgroup, and otherwise
 //   may either be 0 or the id of a constant whose type is the pointee type of
 //   |type_id|.
-void AddGlobalVariable(opt::IRContext* context, uint32_t result_id,
-                       uint32_t type_id, SpvStorageClass storage_class,
-                       uint32_t initializer_id);
+//
+// Returns a pointer to the new global variable instruction.
+opt::Instruction* AddGlobalVariable(opt::IRContext* context, uint32_t result_id,
+                                    uint32_t type_id,
+                                    SpvStorageClass storage_class,
+                                    uint32_t initializer_id);
 
 // Adds an instruction to the start of |function_id|, of the form:
 //   |result_id| = OpVariable |type_id| Function |initializer_id|.
@@ -296,9 +319,11 @@
 // - |initializer_id| must be the id of a constant with the same type as the
 //   pointer's pointee type.
 // - |function_id| must be the id of a function.
-void AddLocalVariable(opt::IRContext* context, uint32_t result_id,
-                      uint32_t type_id, uint32_t function_id,
-                      uint32_t initializer_id);
+//
+// Returns a pointer to the new local variable instruction.
+opt::Instruction* AddLocalVariable(opt::IRContext* context, uint32_t result_id,
+                                   uint32_t type_id, uint32_t function_id,
+                                   uint32_t initializer_id);
 
 // Returns true if the vector |arr| has duplicates.
 bool HasDuplicates(const std::vector<uint32_t>& arr);
@@ -476,30 +501,6 @@
     const TransformationContext& transformation_context, bool value,
     bool is_irrelevant);
 
-// Creates a new OpTypeInt instruction in the module. Updates module's id bound
-// to accommodate for |result_id|.
-void AddIntegerType(opt::IRContext* ir_context, uint32_t result_id,
-                    uint32_t width, bool is_signed);
-
-// Creates a new OpTypeFloat instruction in the module. Updates module's id
-// bound to accommodate for |result_id|.
-void AddFloatType(opt::IRContext* ir_context, uint32_t result_id,
-                  uint32_t width);
-
-// Creates a new OpTypeVector instruction in the module. |component_type_id|
-// must be a valid result id of an OpTypeInt, OpTypeFloat or OpTypeBool
-// instruction in the module. |element_count| must be in the range [2, 4].
-// Updates module's id bound to accommodate for |result_id|.
-void AddVectorType(opt::IRContext* ir_context, uint32_t result_id,
-                   uint32_t component_type_id, uint32_t element_count);
-
-// Creates a new OpTypeStruct instruction in the module. Updates module's id
-// bound to accommodate for |result_id|. |component_type_ids| may not contain
-// a result id of an OpTypeFunction. if |component_type_ids| contains a result
-// of an OpTypeStruct instruction, that struct may not have BuiltIn members.
-void AddStructType(opt::IRContext* ir_context, uint32_t result_id,
-                   const std::vector<uint32_t>& component_type_ids);
-
 // Returns a vector of words representing the integer |value|, only considering
 // the last |width| bits. The last |width| bits are sign-extended if the value
 // is signed, zero-extended if it is unsigned.
@@ -588,6 +589,20 @@
 std::set<uint32_t> GetReachableReturnBlocks(opt::IRContext* ir_context,
                                             uint32_t function_id);
 
+// Returns true if changing terminator instruction to |new_terminator| in the
+// basic block with id |block_id| preserves domination rules and valid block
+// order (i.e. dominator must always appear before dominated in the CFG).
+// Returns false otherwise.
+bool NewTerminatorPreservesDominationRules(opt::IRContext* ir_context,
+                                           uint32_t block_id,
+                                           opt::Instruction new_terminator);
+
+// Return the iterator that points to the function with the corresponding
+// function id. If the function is not found, return the pointer pointing to
+// module()->end().
+opt::Module::iterator GetFunctionIterator(opt::IRContext* ir_context,
+                                          uint32_t function_id);
+
 }  // namespace fuzzerutil
 }  // namespace fuzz
 }  // namespace spvtools
diff --git a/source/fuzz/instruction_descriptor.cpp b/source/fuzz/instruction_descriptor.cpp
index c0cc5e5..fb1ff76 100644
--- a/source/fuzz/instruction_descriptor.cpp
+++ b/source/fuzz/instruction_descriptor.cpp
@@ -20,37 +20,32 @@
 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++;
-        }
+  auto block = context->get_instr_block(
+      instruction_descriptor.base_instruction_result_id());
+  if (block == nullptr) {
+    return nullptr;
+  }
+  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;
       }
-      if (found_base) {
-        // We found the base instruction, but did not find the target
-        // instruction in the same block.
-        return nullptr;
-      }
+      num_ignored++;
     }
   }
   return nullptr;
diff --git a/source/fuzz/pass_management/repeated_pass_manager.cpp b/source/fuzz/pass_management/repeated_pass_manager.cpp
index 032f264..ec44373 100644
--- a/source/fuzz/pass_management/repeated_pass_manager.cpp
+++ b/source/fuzz/pass_management/repeated_pass_manager.cpp
@@ -14,6 +14,10 @@
 
 #include "source/fuzz/pass_management/repeated_pass_manager.h"
 
+#include "source/fuzz/pass_management/repeated_pass_manager_looped_with_recommendations.h"
+#include "source/fuzz/pass_management/repeated_pass_manager_random_with_recommendations.h"
+#include "source/fuzz/pass_management/repeated_pass_manager_simple.h"
+
 namespace spvtools {
 namespace fuzz {
 
@@ -23,5 +27,25 @@
 
 RepeatedPassManager::~RepeatedPassManager() = default;
 
+std::unique_ptr<RepeatedPassManager> RepeatedPassManager::Create(
+    RepeatedPassStrategy strategy, FuzzerContext* fuzzer_context,
+    RepeatedPassInstances* pass_instances,
+    RepeatedPassRecommender* pass_recommender) {
+  switch (strategy) {
+    case RepeatedPassStrategy::kSimple:
+      return MakeUnique<RepeatedPassManagerSimple>(fuzzer_context,
+                                                   pass_instances);
+    case RepeatedPassStrategy::kLoopedWithRecommendations:
+      return MakeUnique<RepeatedPassManagerLoopedWithRecommendations>(
+          fuzzer_context, pass_instances, pass_recommender);
+    case RepeatedPassStrategy::kRandomWithRecommendations:
+      return MakeUnique<RepeatedPassManagerRandomWithRecommendations>(
+          fuzzer_context, pass_instances, pass_recommender);
+  }
+
+  assert(false && "Unreachable");
+  return nullptr;
+}
+
 }  // namespace fuzz
 }  // namespace spvtools
diff --git a/source/fuzz/pass_management/repeated_pass_manager.h b/source/fuzz/pass_management/repeated_pass_manager.h
index 1c23179..1e6ae3e 100644
--- a/source/fuzz/pass_management/repeated_pass_manager.h
+++ b/source/fuzz/pass_management/repeated_pass_manager.h
@@ -18,11 +18,21 @@
 #include "source/fuzz/fuzzer_context.h"
 #include "source/fuzz/fuzzer_pass.h"
 #include "source/fuzz/pass_management/repeated_pass_instances.h"
+#include "source/fuzz/pass_management/repeated_pass_recommender.h"
 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
 
 namespace spvtools {
 namespace fuzz {
 
+// Each field of this enum corresponds to an available repeated pass
+// strategy, and is used to decide which kind of RepeatedPassManager object
+// to create.
+enum class RepeatedPassStrategy {
+  kSimple,
+  kRandomWithRecommendations,
+  kLoopedWithRecommendations
+};
+
 // An interface to encapsulate the manner in which the sequence of repeated
 // passes that are applied during fuzzing is chosen.  An implementation of this
 // interface could, for example, keep track of the history of passes that have
@@ -40,6 +50,12 @@
   virtual FuzzerPass* ChoosePass(
       const protobufs::TransformationSequence& applied_transformations) = 0;
 
+  // Creates a corresponding RepeatedPassManager based on the |strategy|.
+  static std::unique_ptr<RepeatedPassManager> Create(
+      RepeatedPassStrategy strategy, FuzzerContext* fuzzer_context,
+      RepeatedPassInstances* pass_instances,
+      RepeatedPassRecommender* pass_recommender);
+
  protected:
   FuzzerContext* GetFuzzerContext() { return fuzzer_context_; }
 
diff --git a/source/fuzz/protobufs/spvtoolsfuzz.proto b/source/fuzz/protobufs/spvtoolsfuzz.proto
index 770a2dd..657e807 100644
--- a/source/fuzz/protobufs/spvtoolsfuzz.proto
+++ b/source/fuzz/protobufs/spvtoolsfuzz.proto
@@ -557,6 +557,8 @@
     TransformationWrapEarlyTerminatorInFunction wrap_early_terminator_in_function = 83;
     TransformationMergeFunctionReturns merge_function_returns = 84;
     TransformationExpandVectorReduction expand_vector_reduction = 85;
+    TransformationSwapFunctionVariables swap_function_variables = 86;
+    TransformationSwapTwoFunctions swap_two_functions = 87;
     // Add additional option using the next available number.
   }
 }
@@ -1134,6 +1136,14 @@
     // New synonym is derived by applying OpLogicalAnd to |result_id| with the second
     // operand being 'true'.
     LOGICAL_AND = 5;
+
+    // New synonym is derived by applying OpBitwiseOr to |result_id| with the second
+    // operand being 0 taken with the same bit length as |result_id|
+    BITWISE_OR = 6;
+
+    // New synonym is derived by applying OpBitwiseXor to |result_id| with the second
+    // operand being 0 taken with the same bit length as |result_id|
+    BITWISE_XOR = 7;
   }
 
   // Type of the synonym to create. See SynonymType for more details.
@@ -1632,6 +1642,9 @@
   // A fresh id for the header of the new outer loop.
   uint32 outer_header_id = 2;
 
+  // A fresh id for an unreachable continue construct for the new outer loop.
+  uint32 unreachable_continue_id = 7;
+
   // A fresh id for the new return block of the function,
   // i.e. the merge block of the new outer loop.
   uint32 outer_return_id = 3;
@@ -2260,6 +2273,24 @@
 
 }
 
+message TransformationSwapFunctionVariables {
+  // A transformation that swaps function variables
+
+  // Result id of the first variable.
+  uint32 result_id1 = 1;
+  // Result id of the second variable.
+  uint32 result_id2 = 2;
+
+}
+
+message TransformationSwapTwoFunctions {
+  // A transformation that swaps the position of two functions within the same module.
+
+  // the IDs for the two functions that are swapped.
+  uint32 function_id1 = 1;
+  uint32 function_id2 = 2;
+}
+
 message TransformationToggleAccessChainInstruction {
 
   // A transformation that toggles an access chain instruction.
diff --git a/source/fuzz/transformation.cpp b/source/fuzz/transformation.cpp
index ebbc393..4ea0c77 100644
--- a/source/fuzz/transformation.cpp
+++ b/source/fuzz/transformation.cpp
@@ -98,6 +98,8 @@
 #include "source/fuzz/transformation_store.h"
 #include "source/fuzz/transformation_swap_commutable_operands.h"
 #include "source/fuzz/transformation_swap_conditional_branch_operands.h"
+#include "source/fuzz/transformation_swap_function_variables.h"
+#include "source/fuzz/transformation_swap_two_functions.h"
 #include "source/fuzz/transformation_toggle_access_chain_instruction.h"
 #include "source/fuzz/transformation_vector_shuffle.h"
 #include "source/fuzz/transformation_wrap_early_terminator_in_function.h"
@@ -361,6 +363,12 @@
         kSwapConditionalBranchOperands:
       return MakeUnique<TransformationSwapConditionalBranchOperands>(
           message.swap_conditional_branch_operands());
+    case protobufs::Transformation::TransformationCase::kSwapFunctionVariables:
+      return MakeUnique<TransformationSwapFunctionVariables>(
+          message.swap_function_variables());
+    case protobufs::Transformation::TransformationCase::kSwapTwoFunctions:
+      return MakeUnique<TransformationSwapTwoFunctions>(
+          message.swap_two_functions());
     case protobufs::Transformation::TransformationCase::
         kToggleAccessChainInstruction:
       return MakeUnique<TransformationToggleAccessChainInstruction>(
diff --git a/source/fuzz/transformation_access_chain.cpp b/source/fuzz/transformation_access_chain.cpp
index daf73d6..3fe9e65 100644
--- a/source/fuzz/transformation_access_chain.cpp
+++ b/source/fuzz/transformation_access_chain.cpp
@@ -23,8 +23,8 @@
 namespace fuzz {
 
 TransformationAccessChain::TransformationAccessChain(
-    const spvtools::fuzz::protobufs::TransformationAccessChain& message)
-    : message_(message) {}
+    protobufs::TransformationAccessChain message)
+    : message_(std::move(message)) {}
 
 TransformationAccessChain::TransformationAccessChain(
     uint32_t fresh_id, uint32_t pointer_id,
@@ -122,7 +122,7 @@
 
       bool successful;
       std::tie(successful, index_value) =
-          GetIndexValue(ir_context, index_id, subobject_type_id);
+          GetStructIndexValue(ir_context, index_id, subobject_type_id);
 
       if (!successful) {
         return false;
@@ -228,6 +228,11 @@
 
   uint32_t id_pairs_used = 0;
 
+  opt::Instruction* instruction_to_insert_before =
+      FindInstruction(message_.instruction_to_insert_before(), ir_context);
+  opt::BasicBlock* enclosing_block =
+      ir_context->get_instr_block(instruction_to_insert_before);
+
   // Go through the index ids in turn.
   for (auto index_id : message_.index_id()) {
     uint32_t index_value;
@@ -242,7 +247,7 @@
       // It is a struct: we need to retrieve the integer value.
 
       index_value =
-          GetIndexValue(ir_context, index_id, subobject_type_id).second;
+          GetStructIndexValue(ir_context, index_id, subobject_type_id).second;
 
       new_index_id = index_id;
 
@@ -280,29 +285,37 @@
 
       // Clamp the integer and add the corresponding instructions in the module
       // if |add_clamping_instructions| is set.
-      auto instruction_to_insert_before =
-          FindInstruction(message_.instruction_to_insert_before(), ir_context);
 
       // Compare the index with the bound via an instruction of the form:
       //   %fresh_ids.first = OpULessThanEqual %bool %int_id %bound_minus_one.
       fuzzerutil::UpdateModuleIdBound(ir_context, fresh_ids.first());
-      instruction_to_insert_before->InsertBefore(MakeUnique<opt::Instruction>(
+      auto comparison_instruction = MakeUnique<opt::Instruction>(
           ir_context, SpvOpULessThanEqual, bool_type_id, fresh_ids.first(),
           opt::Instruction::OperandList(
               {{SPV_OPERAND_TYPE_ID, {index_instruction->result_id()}},
-               {SPV_OPERAND_TYPE_ID, {bound_minus_one_id}}})));
+               {SPV_OPERAND_TYPE_ID, {bound_minus_one_id}}}));
+      auto comparison_instruction_ptr = comparison_instruction.get();
+      instruction_to_insert_before->InsertBefore(
+          std::move(comparison_instruction));
+      ir_context->get_def_use_mgr()->AnalyzeInstDefUse(
+          comparison_instruction_ptr);
+      ir_context->set_instr_block(comparison_instruction_ptr, enclosing_block);
 
       // Select the index if in-bounds, otherwise one less than the bound:
       //   %fresh_ids.second = OpSelect %int_type %fresh_ids.first %int_id
       //                           %bound_minus_one
       fuzzerutil::UpdateModuleIdBound(ir_context, fresh_ids.second());
-      instruction_to_insert_before->InsertBefore(MakeUnique<opt::Instruction>(
+      auto select_instruction = MakeUnique<opt::Instruction>(
           ir_context, SpvOpSelect, int_type_inst->result_id(),
           fresh_ids.second(),
           opt::Instruction::OperandList(
               {{SPV_OPERAND_TYPE_ID, {fresh_ids.first()}},
                {SPV_OPERAND_TYPE_ID, {index_instruction->result_id()}},
-               {SPV_OPERAND_TYPE_ID, {bound_minus_one_id}}})));
+               {SPV_OPERAND_TYPE_ID, {bound_minus_one_id}}}));
+      auto select_instruction_ptr = select_instruction.get();
+      instruction_to_insert_before->InsertBefore(std::move(select_instruction));
+      ir_context->get_def_use_mgr()->AnalyzeInstDefUse(select_instruction_ptr);
+      ir_context->set_instr_block(select_instruction_ptr, enclosing_block);
 
       new_index_id = fresh_ids.second();
 
@@ -326,13 +339,14 @@
   // Add the access chain instruction to the module, and update the module's
   // id bound.
   fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
-  FindInstruction(message_.instruction_to_insert_before(), ir_context)
-      ->InsertBefore(MakeUnique<opt::Instruction>(
-          ir_context, SpvOpAccessChain, result_type, message_.fresh_id(),
-          operands));
-
-  // Conservatively invalidate all analyses.
-  ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+  auto access_chain_instruction = MakeUnique<opt::Instruction>(
+      ir_context, SpvOpAccessChain, result_type, message_.fresh_id(), operands);
+  auto access_chain_instruction_ptr = access_chain_instruction.get();
+  instruction_to_insert_before->InsertBefore(
+      std::move(access_chain_instruction));
+  ir_context->get_def_use_mgr()->AnalyzeInstDefUse(
+      access_chain_instruction_ptr);
+  ir_context->set_instr_block(access_chain_instruction_ptr, enclosing_block);
 
   // If the base pointer's pointee value was irrelevant, the same is true of
   // the pointee value of the result of this access chain.
@@ -349,9 +363,12 @@
   return result;
 }
 
-std::pair<bool, uint32_t> TransformationAccessChain::GetIndexValue(
+std::pair<bool, uint32_t> TransformationAccessChain::GetStructIndexValue(
     opt::IRContext* ir_context, uint32_t index_id,
     uint32_t object_type_id) const {
+  assert(ir_context->get_def_use_mgr()->GetDef(object_type_id)->opcode() ==
+             SpvOpTypeStruct &&
+         "Precondition: the type must be a struct type.");
   if (!ValidIndexToComposite(ir_context, index_id, object_type_id)) {
     return {false, 0};
   }
@@ -360,10 +377,9 @@
   uint32_t bound = fuzzerutil::GetBoundForCompositeIndex(
       *ir_context->get_def_use_mgr()->GetDef(object_type_id), ir_context);
 
-  // The index must be a constant
-  if (!spvOpcodeIsConstant(index_instruction->opcode())) {
-    return {false, 0};
-  }
+  // Ensure that the index given must represent a constant.
+  assert(spvOpcodeIsConstant(index_instruction->opcode()) &&
+         "A non-constant index should already have been rejected.");
 
   // The index must be in bounds.
   uint32_t value = index_instruction->GetSingleWordInOperand(0);
diff --git a/source/fuzz/transformation_access_chain.h b/source/fuzz/transformation_access_chain.h
index 02cdc32..4e4fd2b 100644
--- a/source/fuzz/transformation_access_chain.h
+++ b/source/fuzz/transformation_access_chain.h
@@ -28,7 +28,7 @@
 class TransformationAccessChain : public Transformation {
  public:
   explicit TransformationAccessChain(
-      const protobufs::TransformationAccessChain& message);
+      protobufs::TransformationAccessChain message);
 
   TransformationAccessChain(
       uint32_t fresh_id, uint32_t pointer_id,
@@ -83,13 +83,13 @@
  private:
   // Returns {false, 0} in each of the following cases:
   // - |index_id| does not correspond to a 32-bit integer constant
-  // - the object being indexed is not a composite type
+  // - |object_type_id| must be a struct type
   // - the constant at |index_id| is out of bounds.
   // Otherwise, returns {true, value}, where value is the value of the constant
   // at |index_id|.
-  std::pair<bool, uint32_t> GetIndexValue(opt::IRContext* ir_context,
-                                          uint32_t index_id,
-                                          uint32_t object_type_id) const;
+  std::pair<bool, uint32_t> GetStructIndexValue(opt::IRContext* ir_context,
+                                                uint32_t index_id,
+                                                uint32_t object_type_id) const;
 
   // Returns true if |index_id| corresponds, in the given context, to a 32-bit
   // integer which can be used to index an object of the type specified by
diff --git a/source/fuzz/transformation_add_bit_instruction_synonym.cpp b/source/fuzz/transformation_add_bit_instruction_synonym.cpp
index 6cdfdfb..636c0a3 100644
--- a/source/fuzz/transformation_add_bit_instruction_synonym.cpp
+++ b/source/fuzz/transformation_add_bit_instruction_synonym.cpp
@@ -21,9 +21,8 @@
 namespace fuzz {
 
 TransformationAddBitInstructionSynonym::TransformationAddBitInstructionSynonym(
-    const spvtools::fuzz::protobufs::TransformationAddBitInstructionSynonym&
-        message)
-    : message_(message) {}
+    protobufs::TransformationAddBitInstructionSynonym message)
+    : message_(std::move(message)) {}
 
 TransformationAddBitInstructionSynonym::TransformationAddBitInstructionSynonym(
     const uint32_t instruction_result_id,
@@ -40,20 +39,9 @@
   auto instruction =
       ir_context->get_def_use_mgr()->GetDef(message_.instruction_result_id());
 
-  // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3557):
-  //  Right now we only support certain operations. When this issue is addressed
-  //  the following conditional can use the function |spvOpcodeIsBit|.
-  // |instruction| must be defined and must be a supported bit instruction.
-  if (!instruction || (instruction->opcode() != SpvOpBitwiseOr &&
-                       instruction->opcode() != SpvOpBitwiseXor &&
-                       instruction->opcode() != SpvOpBitwiseAnd &&
-                       instruction->opcode() != SpvOpNot)) {
-    return false;
-  }
-
-  // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3792):
-  //  Right now, only integer operands are supported.
-  if (ir_context->get_type_mgr()->GetType(instruction->type_id())->AsVector()) {
+  // Checks on: only integer operands are supported, instructions are bitwise
+  // operations only. Signedness of the operands must be the same.
+  if (!IsInstructionSupported(ir_context, instruction)) {
     return false;
   }
 
@@ -112,6 +100,65 @@
   }
 }
 
+bool TransformationAddBitInstructionSynonym::IsInstructionSupported(
+    opt::IRContext* ir_context, opt::Instruction* instruction) {
+  // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3557):
+  //  Right now we only support certain operations. When this issue is addressed
+  //  the following conditional can use the function |spvOpcodeIsBit|.
+  // |instruction| must be defined and must be a supported bit instruction.
+  if (!instruction || (instruction->opcode() != SpvOpBitwiseOr &&
+                       instruction->opcode() != SpvOpBitwiseXor &&
+                       instruction->opcode() != SpvOpBitwiseAnd &&
+                       instruction->opcode() != SpvOpNot)) {
+    return false;
+  }
+
+  // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3792):
+  //  Right now, only integer operands are supported.
+  if (ir_context->get_type_mgr()->GetType(instruction->type_id())->AsVector()) {
+    return false;
+  }
+
+  if (instruction->opcode() == SpvOpNot) {
+    auto operand = instruction->GetInOperand(0).words[0];
+    auto operand_inst = ir_context->get_def_use_mgr()->GetDef(operand);
+    auto operand_type =
+        ir_context->get_type_mgr()->GetType(operand_inst->type_id());
+    auto operand_sign = operand_type->AsInteger()->IsSigned();
+
+    auto type_id_sign = ir_context->get_type_mgr()
+                            ->GetType(instruction->type_id())
+                            ->AsInteger()
+                            ->IsSigned();
+
+    return operand_sign == type_id_sign;
+
+  } else {
+    // Other BitWise operations that takes two operands.
+    auto first_operand = instruction->GetInOperand(0).words[0];
+    auto first_operand_inst =
+        ir_context->get_def_use_mgr()->GetDef(first_operand);
+    auto first_operand_type =
+        ir_context->get_type_mgr()->GetType(first_operand_inst->type_id());
+    auto first_operand_sign = first_operand_type->AsInteger()->IsSigned();
+
+    auto second_operand = instruction->GetInOperand(1).words[0];
+    auto second_operand_inst =
+        ir_context->get_def_use_mgr()->GetDef(second_operand);
+    auto second_operand_type =
+        ir_context->get_type_mgr()->GetType(second_operand_inst->type_id());
+    auto second_operand_sign = second_operand_type->AsInteger()->IsSigned();
+
+    auto type_id_sign = ir_context->get_type_mgr()
+                            ->GetType(instruction->type_id())
+                            ->AsInteger()
+                            ->IsSigned();
+
+    return first_operand_sign == second_operand_sign &&
+           first_operand_sign == type_id_sign;
+  }
+}
+
 protobufs::Transformation TransformationAddBitInstructionSynonym::ToMessage()
     const {
   protobufs::Transformation result;
diff --git a/source/fuzz/transformation_add_bit_instruction_synonym.h b/source/fuzz/transformation_add_bit_instruction_synonym.h
index ed1a0af..40b947a 100644
--- a/source/fuzz/transformation_add_bit_instruction_synonym.h
+++ b/source/fuzz/transformation_add_bit_instruction_synonym.h
@@ -103,7 +103,7 @@
 class TransformationAddBitInstructionSynonym : public Transformation {
  public:
   explicit TransformationAddBitInstructionSynonym(
-      const protobufs::TransformationAddBitInstructionSynonym& message);
+      protobufs::TransformationAddBitInstructionSynonym message);
 
   TransformationAddBitInstructionSynonym(
       const uint32_t instruction_result_id,
@@ -128,6 +128,14 @@
   static uint32_t GetRequiredFreshIdCount(opt::IRContext* ir_context,
                                           opt::Instruction* bit_instruction);
 
+  //   Returns true if:
+  // - A |bit_instruction| is one of OpBitwiseOr, OpBitwiseAnd, OpBitwiseXor or
+  // OpNot.
+  // - |bit_instruction|'s operands are scalars.
+  // - The operands have the same signedness.
+  static bool IsInstructionSupported(opt::IRContext* ir_context,
+                                     opt::Instruction* instruction);
+
  private:
   protobufs::TransformationAddBitInstructionSynonym message_;
 
diff --git a/source/fuzz/transformation_add_constant_boolean.cpp b/source/fuzz/transformation_add_constant_boolean.cpp
index 937fdbc..3935432 100644
--- a/source/fuzz/transformation_add_constant_boolean.cpp
+++ b/source/fuzz/transformation_add_constant_boolean.cpp
@@ -21,8 +21,8 @@
 namespace fuzz {
 
 TransformationAddConstantBoolean::TransformationAddConstantBoolean(
-    const protobufs::TransformationAddConstantBoolean& message)
-    : message_(message) {}
+    protobufs::TransformationAddConstantBoolean message)
+    : message_(std::move(message)) {}
 
 TransformationAddConstantBoolean::TransformationAddConstantBoolean(
     uint32_t fresh_id, bool is_true, bool is_irrelevant) {
@@ -42,14 +42,18 @@
     TransformationContext* transformation_context) const {
   // Add the boolean constant to the module, ensuring the module's id bound is
   // high enough.
+  auto new_instruction = MakeUnique<opt::Instruction>(
+      ir_context, message_.is_true() ? SpvOpConstantTrue : SpvOpConstantFalse,
+      fuzzerutil::MaybeGetBoolType(ir_context), message_.fresh_id(),
+      opt::Instruction::OperandList());
+  auto new_instruction_ptr = new_instruction.get();
+  ir_context->module()->AddGlobalValue(std::move(new_instruction));
   fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
-  ir_context->module()->AddGlobalValue(
-      message_.is_true() ? SpvOpConstantTrue : SpvOpConstantFalse,
-      message_.fresh_id(), fuzzerutil::MaybeGetBoolType(ir_context));
-  // We have added an instruction to the module, so need to be careful about the
-  // validity of existing analyses.
-  ir_context->InvalidateAnalysesExceptFor(
-      opt::IRContext::Analysis::kAnalysisNone);
+
+  // Inform the def-use manager about the new instruction. Invalidate the
+  // constant manager as we have added a new constant.
+  ir_context->get_def_use_mgr()->AnalyzeInstDef(new_instruction_ptr);
+  ir_context->InvalidateAnalyses(opt::IRContext::kAnalysisConstants);
 
   if (message_.is_irrelevant()) {
     transformation_context->GetFactManager()->AddFactIdIsIrrelevant(
diff --git a/source/fuzz/transformation_add_constant_boolean.h b/source/fuzz/transformation_add_constant_boolean.h
index d1d04ef..7f31471 100644
--- a/source/fuzz/transformation_add_constant_boolean.h
+++ b/source/fuzz/transformation_add_constant_boolean.h
@@ -26,7 +26,7 @@
 class TransformationAddConstantBoolean : public Transformation {
  public:
   explicit TransformationAddConstantBoolean(
-      const protobufs::TransformationAddConstantBoolean& message);
+      protobufs::TransformationAddConstantBoolean message);
 
   TransformationAddConstantBoolean(uint32_t fresh_id, bool is_true,
                                    bool is_irrelevant);
diff --git a/source/fuzz/transformation_add_constant_composite.cpp b/source/fuzz/transformation_add_constant_composite.cpp
index 0260321..e6cd5a9 100644
--- a/source/fuzz/transformation_add_constant_composite.cpp
+++ b/source/fuzz/transformation_add_constant_composite.cpp
@@ -22,9 +22,8 @@
 namespace fuzz {
 
 TransformationAddConstantComposite::TransformationAddConstantComposite(
-    const spvtools::fuzz::protobufs::TransformationAddConstantComposite&
-        message)
-    : message_(message) {}
+    spvtools::fuzz::protobufs::TransformationAddConstantComposite message)
+    : message_(std::move(message)) {}
 
 TransformationAddConstantComposite::TransformationAddConstantComposite(
     uint32_t fresh_id, uint32_t type_id,
@@ -120,14 +119,17 @@
   for (auto constituent_id : message_.constituent_id()) {
     in_operands.push_back({SPV_OPERAND_TYPE_ID, {constituent_id}});
   }
-  ir_context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
+  auto new_instruction = MakeUnique<opt::Instruction>(
       ir_context, SpvOpConstantComposite, message_.type_id(),
-      message_.fresh_id(), in_operands));
+      message_.fresh_id(), in_operands);
+  auto new_instruction_ptr = new_instruction.get();
+  ir_context->module()->AddGlobalValue(std::move(new_instruction));
   fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
-  // We have added an instruction to the module, so need to be careful about the
-  // validity of existing analyses.
-  ir_context->InvalidateAnalysesExceptFor(
-      opt::IRContext::Analysis::kAnalysisNone);
+
+  // Inform the def-use manager of the new instruction. Invalidate the constant
+  // manager as we have added a new constant.
+  ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr);
+  ir_context->InvalidateAnalyses(opt::IRContext::kAnalysisConstants);
 
   if (message_.is_irrelevant()) {
     transformation_context->GetFactManager()->AddFactIdIsIrrelevant(
diff --git a/source/fuzz/transformation_add_constant_composite.h b/source/fuzz/transformation_add_constant_composite.h
index 9e9222d..94e7a92 100644
--- a/source/fuzz/transformation_add_constant_composite.h
+++ b/source/fuzz/transformation_add_constant_composite.h
@@ -28,7 +28,7 @@
 class TransformationAddConstantComposite : public Transformation {
  public:
   explicit TransformationAddConstantComposite(
-      const protobufs::TransformationAddConstantComposite& message);
+      protobufs::TransformationAddConstantComposite message);
 
   TransformationAddConstantComposite(
       uint32_t fresh_id, uint32_t type_id,
diff --git a/source/fuzz/transformation_add_constant_null.cpp b/source/fuzz/transformation_add_constant_null.cpp
index 3c66ab1..32544e6 100644
--- a/source/fuzz/transformation_add_constant_null.cpp
+++ b/source/fuzz/transformation_add_constant_null.cpp
@@ -20,8 +20,8 @@
 namespace fuzz {
 
 TransformationAddConstantNull::TransformationAddConstantNull(
-    const spvtools::fuzz::protobufs::TransformationAddConstantNull& message)
-    : message_(message) {}
+    spvtools::fuzz::protobufs::TransformationAddConstantNull message)
+    : message_(std::move(message)) {}
 
 TransformationAddConstantNull::TransformationAddConstantNull(uint32_t fresh_id,
                                                              uint32_t type_id) {
@@ -46,14 +46,18 @@
 }
 
 void TransformationAddConstantNull::Apply(
-    opt::IRContext* context, TransformationContext* /*unused*/) const {
-  context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
-      context, SpvOpConstantNull, message_.type_id(), message_.fresh_id(),
-      opt::Instruction::OperandList()));
-  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
-  // We have added an instruction to the module, so need to be careful about the
-  // validity of existing analyses.
-  context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+    opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
+  auto new_instruction = MakeUnique<opt::Instruction>(
+      ir_context, SpvOpConstantNull, message_.type_id(), message_.fresh_id(),
+      opt::Instruction::OperandList());
+  auto new_instruction_ptr = new_instruction.get();
+  ir_context->module()->AddGlobalValue(std::move(new_instruction));
+  fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
+
+  // Inform the def-use manager about the new instruction. Invalidate the
+  // constant manager as we have added a new constant.
+  ir_context->get_def_use_mgr()->AnalyzeInstDef(new_instruction_ptr);
+  ir_context->InvalidateAnalyses(opt::IRContext::kAnalysisConstants);
 }
 
 protobufs::Transformation TransformationAddConstantNull::ToMessage() const {
diff --git a/source/fuzz/transformation_add_constant_null.h b/source/fuzz/transformation_add_constant_null.h
index bd08b1d..bb1d1b7 100644
--- a/source/fuzz/transformation_add_constant_null.h
+++ b/source/fuzz/transformation_add_constant_null.h
@@ -26,7 +26,7 @@
 class TransformationAddConstantNull : public Transformation {
  public:
   explicit TransformationAddConstantNull(
-      const protobufs::TransformationAddConstantNull& message);
+      protobufs::TransformationAddConstantNull message);
 
   TransformationAddConstantNull(uint32_t fresh_id, uint32_t type_id);
 
@@ -34,12 +34,12 @@
   // - |message_.type_id| must be the id of a type for which it is acceptable
   //   to create a null constant
   bool IsApplicable(
-      opt::IRContext* context,
+      opt::IRContext* ir_context,
       const TransformationContext& transformation_context) const override;
 
   // Adds an OpConstantNull instruction to the module, with |message_.type_id|
   // as its type.  The instruction has result id |message_.fresh_id|.
-  void Apply(opt::IRContext* context,
+  void Apply(opt::IRContext* ir_context,
              TransformationContext* transformation_context) const override;
 
   std::unordered_set<uint32_t> GetFreshIds() const override;
diff --git a/source/fuzz/transformation_add_constant_scalar.cpp b/source/fuzz/transformation_add_constant_scalar.cpp
index 9a6642a..a2d95fb 100644
--- a/source/fuzz/transformation_add_constant_scalar.cpp
+++ b/source/fuzz/transformation_add_constant_scalar.cpp
@@ -20,8 +20,8 @@
 namespace fuzz {
 
 TransformationAddConstantScalar::TransformationAddConstantScalar(
-    const spvtools::fuzz::protobufs::TransformationAddConstantScalar& message)
-    : message_(message) {}
+    spvtools::fuzz::protobufs::TransformationAddConstantScalar message)
+    : message_(std::move(message)) {}
 
 TransformationAddConstantScalar::TransformationAddConstantScalar(
     uint32_t fresh_id, uint32_t type_id, const std::vector<uint32_t>& words,
@@ -64,19 +64,21 @@
 void TransformationAddConstantScalar::Apply(
     opt::IRContext* ir_context,
     TransformationContext* transformation_context) const {
-  ir_context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
+  auto new_instruction = MakeUnique<opt::Instruction>(
       ir_context, SpvOpConstant, message_.type_id(), message_.fresh_id(),
       opt::Instruction::OperandList(
           {{SPV_OPERAND_TYPE_LITERAL_INTEGER,
             std::vector<uint32_t>(message_.word().begin(),
-                                  message_.word().end())}})));
+                                  message_.word().end())}}));
+  auto new_instruction_ptr = new_instruction.get();
+  ir_context->module()->AddGlobalValue(std::move(new_instruction));
 
   fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
 
-  // We have added an instruction to the module, so need to be careful about the
-  // validity of existing analyses.
-  ir_context->InvalidateAnalysesExceptFor(
-      opt::IRContext::Analysis::kAnalysisNone);
+  // Inform the def-use manager about the new instruction. Invalidate the
+  // constant manager as we have added a new constant.
+  ir_context->get_def_use_mgr()->AnalyzeInstDef(new_instruction_ptr);
+  ir_context->InvalidateAnalyses(opt::IRContext::kAnalysisConstants);
 
   if (message_.is_irrelevant()) {
     transformation_context->GetFactManager()->AddFactIdIsIrrelevant(
diff --git a/source/fuzz/transformation_add_constant_scalar.h b/source/fuzz/transformation_add_constant_scalar.h
index 3f23907..adb0735 100644
--- a/source/fuzz/transformation_add_constant_scalar.h
+++ b/source/fuzz/transformation_add_constant_scalar.h
@@ -28,7 +28,7 @@
 class TransformationAddConstantScalar : public Transformation {
  public:
   explicit TransformationAddConstantScalar(
-      const protobufs::TransformationAddConstantScalar& message);
+      protobufs::TransformationAddConstantScalar message);
 
   TransformationAddConstantScalar(uint32_t fresh_id, uint32_t type_id,
                                   const std::vector<uint32_t>& words,
diff --git a/source/fuzz/transformation_add_copy_memory.cpp b/source/fuzz/transformation_add_copy_memory.cpp
index 44bb9c5..5eb4bdc 100644
--- a/source/fuzz/transformation_add_copy_memory.cpp
+++ b/source/fuzz/transformation_add_copy_memory.cpp
@@ -22,8 +22,8 @@
 namespace fuzz {
 
 TransformationAddCopyMemory::TransformationAddCopyMemory(
-    const protobufs::TransformationAddCopyMemory& message)
-    : message_(message) {}
+    protobufs::TransformationAddCopyMemory message)
+    : message_(std::move(message)) {}
 
 TransformationAddCopyMemory::TransformationAddCopyMemory(
     const protobufs::InstructionDescriptor& instruction_descriptor,
@@ -99,15 +99,8 @@
   auto* insert_before_inst =
       FindInstruction(message_.instruction_descriptor(), ir_context);
   assert(insert_before_inst);
-
-  auto insert_before_iter = fuzzerutil::GetIteratorForInstruction(
-      ir_context->get_instr_block(insert_before_inst), insert_before_inst);
-
-  insert_before_iter.InsertBefore(MakeUnique<opt::Instruction>(
-      ir_context, SpvOpCopyMemory, 0, 0,
-      opt::Instruction::OperandList{
-          {SPV_OPERAND_TYPE_ID, {message_.fresh_id()}},
-          {SPV_OPERAND_TYPE_ID, {message_.source_id()}}}));
+  opt::BasicBlock* enclosing_block =
+      ir_context->get_instr_block(insert_before_inst);
 
   // Add global or local variable to copy memory into.
   auto storage_class = static_cast<SpvStorageClass>(message_.storage_class());
@@ -118,23 +111,35 @@
       storage_class);
 
   if (storage_class == SpvStorageClassPrivate) {
-    fuzzerutil::AddGlobalVariable(ir_context, message_.fresh_id(), type_id,
-                                  storage_class, message_.initializer_id());
+    opt::Instruction* new_global =
+        fuzzerutil::AddGlobalVariable(ir_context, message_.fresh_id(), type_id,
+                                      storage_class, message_.initializer_id());
+    ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_global);
   } else {
     assert(storage_class == SpvStorageClassFunction &&
            "Storage class can be either Private or Function");
-    fuzzerutil::AddLocalVariable(ir_context, message_.fresh_id(), type_id,
-                                 ir_context->get_instr_block(insert_before_inst)
-                                     ->GetParent()
-                                     ->result_id(),
-                                 message_.initializer_id());
+    opt::Function* enclosing_function = enclosing_block->GetParent();
+    opt::Instruction* new_local = fuzzerutil::AddLocalVariable(
+        ir_context, message_.fresh_id(), type_id,
+        enclosing_function->result_id(), message_.initializer_id());
+    ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_local);
+    ir_context->set_instr_block(new_local, &*enclosing_function->entry());
   }
 
-  fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
+  auto insert_before_iter = fuzzerutil::GetIteratorForInstruction(
+      enclosing_block, insert_before_inst);
 
-  // Make sure our changes are analyzed
-  ir_context->InvalidateAnalysesExceptFor(
-      opt::IRContext::Analysis::kAnalysisNone);
+  auto new_instruction = MakeUnique<opt::Instruction>(
+      ir_context, SpvOpCopyMemory, 0, 0,
+      opt::Instruction::OperandList{
+          {SPV_OPERAND_TYPE_ID, {message_.fresh_id()}},
+          {SPV_OPERAND_TYPE_ID, {message_.source_id()}}});
+  auto new_instruction_ptr = new_instruction.get();
+  insert_before_iter.InsertBefore(std::move(new_instruction));
+  ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr);
+  ir_context->set_instr_block(new_instruction_ptr, enclosing_block);
+
+  fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
 
   // Even though the copy memory instruction will - at least temporarily - lead
   // to the destination and source pointers referring to identical values, this
diff --git a/source/fuzz/transformation_add_copy_memory.h b/source/fuzz/transformation_add_copy_memory.h
index cc42f1e..b25652f 100644
--- a/source/fuzz/transformation_add_copy_memory.h
+++ b/source/fuzz/transformation_add_copy_memory.h
@@ -26,7 +26,7 @@
 class TransformationAddCopyMemory : public Transformation {
  public:
   explicit TransformationAddCopyMemory(
-      const protobufs::TransformationAddCopyMemory& message);
+      protobufs::TransformationAddCopyMemory message);
 
   TransformationAddCopyMemory(
       const protobufs::InstructionDescriptor& instruction_descriptor,
diff --git a/source/fuzz/transformation_add_dead_block.cpp b/source/fuzz/transformation_add_dead_block.cpp
index 5dce356..82e8cd8 100644
--- a/source/fuzz/transformation_add_dead_block.cpp
+++ b/source/fuzz/transformation_add_dead_block.cpp
@@ -20,8 +20,8 @@
 namespace fuzz {
 
 TransformationAddDeadBlock::TransformationAddDeadBlock(
-    const spvtools::fuzz::protobufs::TransformationAddDeadBlock& message)
-    : message_(message) {}
+    protobufs::TransformationAddDeadBlock message)
+    : message_(std::move(message)) {}
 
 TransformationAddDeadBlock::TransformationAddDeadBlock(uint32_t fresh_id,
                                                        uint32_t existing_block,
diff --git a/source/fuzz/transformation_add_dead_block.h b/source/fuzz/transformation_add_dead_block.h
index 50af6b0..d8b3c2a 100644
--- a/source/fuzz/transformation_add_dead_block.h
+++ b/source/fuzz/transformation_add_dead_block.h
@@ -26,7 +26,7 @@
 class TransformationAddDeadBlock : public Transformation {
  public:
   explicit TransformationAddDeadBlock(
-      const protobufs::TransformationAddDeadBlock& message);
+      protobufs::TransformationAddDeadBlock message);
 
   TransformationAddDeadBlock(uint32_t fresh_id, uint32_t existing_block,
                              bool condition_value);
diff --git a/source/fuzz/transformation_add_dead_break.cpp b/source/fuzz/transformation_add_dead_break.cpp
index bc938d4..ad46ce7 100644
--- a/source/fuzz/transformation_add_dead_break.cpp
+++ b/source/fuzz/transformation_add_dead_break.cpp
@@ -24,8 +24,8 @@
 namespace fuzz {
 
 TransformationAddDeadBreak::TransformationAddDeadBreak(
-    const spvtools::fuzz::protobufs::TransformationAddDeadBreak& message)
-    : message_(message) {}
+    protobufs::TransformationAddDeadBreak message)
+    : message_(std::move(message)) {}
 
 TransformationAddDeadBreak::TransformationAddDeadBreak(
     uint32_t from_block, uint32_t to_block, bool break_condition_value,
@@ -112,9 +112,10 @@
     const TransformationContext& transformation_context) const {
   // First, we check that a constant with the same value as
   // |message_.break_condition_value| is present.
-  if (!fuzzerutil::MaybeGetBoolConstant(ir_context, transformation_context,
-                                        message_.break_condition_value(),
-                                        false)) {
+  const auto bool_id =
+      fuzzerutil::MaybeGetBoolConstant(ir_context, transformation_context,
+                                       message_.break_condition_value(), false);
+  if (!bool_id) {
     // The required constant is not present, so the transformation cannot be
     // applied.
     return false;
@@ -171,25 +172,23 @@
   }
 
   // Adding the dead break is only valid if SPIR-V rules related to dominance
-  // hold.  Rather than checking these rules explicitly, we defer to the
-  // validator.  We make a clone of the module, apply the transformation to the
-  // clone, and check whether the transformed clone is valid.
-  //
-  // In principle some of the above checks could be removed, with more reliance
-  // being places on the validator.  This should be revisited if we are sure
-  // the validator is complete with respect to checking structured control flow
-  // rules.
-  auto cloned_context = fuzzerutil::CloneIRContext(ir_context);
-  ApplyImpl(cloned_context.get(), transformation_context);
-  return fuzzerutil::IsValid(cloned_context.get(),
-                             transformation_context.GetValidatorOptions(),
-                             fuzzerutil::kSilentMessageConsumer);
+  // hold.
+  return fuzzerutil::NewTerminatorPreservesDominationRules(
+      ir_context, message_.from_block(),
+      fuzzerutil::CreateUnreachableEdgeInstruction(
+          ir_context, message_.from_block(), message_.to_block(), bool_id));
 }
 
 void TransformationAddDeadBreak::Apply(
     opt::IRContext* ir_context,
     TransformationContext* transformation_context) const {
-  ApplyImpl(ir_context, *transformation_context);
+  fuzzerutil::AddUnreachableEdgeAndUpdateOpPhis(
+      ir_context, ir_context->cfg()->block(message_.from_block()),
+      ir_context->cfg()->block(message_.to_block()),
+      fuzzerutil::MaybeGetBoolConstant(ir_context, *transformation_context,
+                                       message_.break_condition_value(), false),
+      message_.phi_id());
+
   // Invalidate all analyses
   ir_context->InvalidateAnalysesExceptFor(
       opt::IRContext::Analysis::kAnalysisNone);
@@ -201,17 +200,6 @@
   return result;
 }
 
-void TransformationAddDeadBreak::ApplyImpl(
-    spvtools::opt::IRContext* ir_context,
-    const TransformationContext& transformation_context) const {
-  fuzzerutil::AddUnreachableEdgeAndUpdateOpPhis(
-      ir_context, ir_context->cfg()->block(message_.from_block()),
-      ir_context->cfg()->block(message_.to_block()),
-      fuzzerutil::MaybeGetBoolConstant(ir_context, transformation_context,
-                                       message_.break_condition_value(), false),
-      message_.phi_id());
-}
-
 std::unordered_set<uint32_t> TransformationAddDeadBreak::GetFreshIds() const {
   return std::unordered_set<uint32_t>();
 }
diff --git a/source/fuzz/transformation_add_dead_break.h b/source/fuzz/transformation_add_dead_break.h
index afb8dc7..8c1ab4a 100644
--- a/source/fuzz/transformation_add_dead_break.h
+++ b/source/fuzz/transformation_add_dead_break.h
@@ -28,7 +28,7 @@
 class TransformationAddDeadBreak : public Transformation {
  public:
   explicit TransformationAddDeadBreak(
-      const protobufs::TransformationAddDeadBreak& message);
+      protobufs::TransformationAddDeadBreak message);
 
   TransformationAddDeadBreak(uint32_t from_block, uint32_t to_block,
                              bool break_condition_value,
@@ -71,15 +71,6 @@
   bool AddingBreakRespectsStructuredControlFlow(opt::IRContext* ir_context,
                                                 opt::BasicBlock* bb_from) const;
 
-  // Used by 'Apply' to actually apply the transformation to the module of
-  // interest, and by 'IsApplicable' to do a dry-run of the transformation on a
-  // cloned module, in order to check that the transformation leads to a valid
-  // module.  This is only invoked by 'IsApplicable' after certain basic
-  // applicability checks have been made, ensuring that the invocation of this
-  // method is legal.
-  void ApplyImpl(opt::IRContext* ir_context,
-                 const TransformationContext& transformation_context) const;
-
   protobufs::TransformationAddDeadBreak message_;
 };
 
diff --git a/source/fuzz/transformation_add_dead_continue.cpp b/source/fuzz/transformation_add_dead_continue.cpp
index 18b3c39..be6294e 100644
--- a/source/fuzz/transformation_add_dead_continue.cpp
+++ b/source/fuzz/transformation_add_dead_continue.cpp
@@ -20,8 +20,8 @@
 namespace fuzz {
 
 TransformationAddDeadContinue::TransformationAddDeadContinue(
-    const spvtools::fuzz::protobufs::TransformationAddDeadContinue& message)
-    : message_(message) {}
+    protobufs::TransformationAddDeadContinue message)
+    : message_(std::move(message)) {}
 
 TransformationAddDeadContinue::TransformationAddDeadContinue(
     uint32_t from_block, bool continue_condition_value,
@@ -38,9 +38,10 @@
     const TransformationContext& transformation_context) const {
   // First, we check that a constant with the same value as
   // |message_.continue_condition_value| is present.
-  if (!fuzzerutil::MaybeGetBoolConstant(ir_context, transformation_context,
-                                        message_.continue_condition_value(),
-                                        false)) {
+  const auto bool_id = fuzzerutil::MaybeGetBoolConstant(
+      ir_context, transformation_context, message_.continue_condition_value(),
+      false);
+  if (!bool_id) {
     // The required constant is not present, so the transformation cannot be
     // applied.
     return false;
@@ -111,39 +112,16 @@
   }
 
   // Adding the dead break is only valid if SPIR-V rules related to dominance
-  // hold.  Rather than checking these rules explicitly, we defer to the
-  // validator.  We make a clone of the module, apply the transformation to the
-  // clone, and check whether the transformed clone is valid.
-  //
-  // In principle some of the above checks could be removed, with more reliance
-  // being placed on the validator.  This should be revisited if we are sure
-  // the validator is complete with respect to checking structured control flow
-  // rules.
-  auto cloned_context = fuzzerutil::CloneIRContext(ir_context);
-  ApplyImpl(cloned_context.get(), transformation_context);
-  return fuzzerutil::IsValid(cloned_context.get(),
-                             transformation_context.GetValidatorOptions(),
-                             fuzzerutil::kSilentMessageConsumer);
+  // hold.
+  return fuzzerutil::NewTerminatorPreservesDominationRules(
+      ir_context, message_.from_block(),
+      fuzzerutil::CreateUnreachableEdgeInstruction(
+          ir_context, message_.from_block(), continue_block, bool_id));
 }
 
 void TransformationAddDeadContinue::Apply(
     opt::IRContext* ir_context,
     TransformationContext* transformation_context) const {
-  ApplyImpl(ir_context, *transformation_context);
-  // Invalidate all analyses
-  ir_context->InvalidateAnalysesExceptFor(
-      opt::IRContext::Analysis::kAnalysisNone);
-}
-
-protobufs::Transformation TransformationAddDeadContinue::ToMessage() const {
-  protobufs::Transformation result;
-  *result.mutable_add_dead_continue() = message_;
-  return result;
-}
-
-void TransformationAddDeadContinue::ApplyImpl(
-    spvtools::opt::IRContext* ir_context,
-    const TransformationContext& transformation_context) const {
   auto bb_from = ir_context->cfg()->block(message_.from_block());
   auto continue_block =
       bb_from->IsLoopHeader()
@@ -153,10 +131,20 @@
   assert(continue_block && "message_.from_block must be in a loop.");
   fuzzerutil::AddUnreachableEdgeAndUpdateOpPhis(
       ir_context, bb_from, ir_context->cfg()->block(continue_block),
-      fuzzerutil::MaybeGetBoolConstant(ir_context, transformation_context,
+      fuzzerutil::MaybeGetBoolConstant(ir_context, *transformation_context,
                                        message_.continue_condition_value(),
                                        false),
       message_.phi_id());
+
+  // Invalidate all analyses
+  ir_context->InvalidateAnalysesExceptFor(
+      opt::IRContext::Analysis::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationAddDeadContinue::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_add_dead_continue() = message_;
+  return result;
 }
 
 std::unordered_set<uint32_t> TransformationAddDeadContinue::GetFreshIds()
diff --git a/source/fuzz/transformation_add_dead_continue.h b/source/fuzz/transformation_add_dead_continue.h
index 27527e7..9463aeb 100644
--- a/source/fuzz/transformation_add_dead_continue.h
+++ b/source/fuzz/transformation_add_dead_continue.h
@@ -28,7 +28,7 @@
 class TransformationAddDeadContinue : public Transformation {
  public:
   explicit TransformationAddDeadContinue(
-      const protobufs::TransformationAddDeadContinue& message);
+      protobufs::TransformationAddDeadContinue message);
 
   TransformationAddDeadContinue(uint32_t from_block,
                                 bool continue_condition_value,
@@ -68,15 +68,6 @@
   protobufs::Transformation ToMessage() const override;
 
  private:
-  // Used by 'Apply' to actually apply the transformation to the module of
-  // interest, and by 'IsApplicable' to do a dry-run of the transformation on a
-  // cloned module, in order to check that the transformation leads to a valid
-  // module.  This is only invoked by 'IsApplicable' after certain basic
-  // applicability checks have been made, ensuring that the invocation of this
-  // method is legal.
-  void ApplyImpl(opt::IRContext* ir_context,
-                 const TransformationContext& transformation_context) const;
-
   protobufs::TransformationAddDeadContinue message_;
 };
 
diff --git a/source/fuzz/transformation_add_early_terminator_wrapper.cpp b/source/fuzz/transformation_add_early_terminator_wrapper.cpp
index 9f86070..547398a 100644
--- a/source/fuzz/transformation_add_early_terminator_wrapper.cpp
+++ b/source/fuzz/transformation_add_early_terminator_wrapper.cpp
@@ -22,9 +22,8 @@
 
 TransformationAddEarlyTerminatorWrapper::
     TransformationAddEarlyTerminatorWrapper(
-        const spvtools::fuzz::protobufs::
-            TransformationAddEarlyTerminatorWrapper& message)
-    : message_(message) {}
+        protobufs::TransformationAddEarlyTerminatorWrapper message)
+    : message_(std::move(message)) {}
 
 TransformationAddEarlyTerminatorWrapper::
     TransformationAddEarlyTerminatorWrapper(uint32_t function_fresh_id,
diff --git a/source/fuzz/transformation_add_early_terminator_wrapper.h b/source/fuzz/transformation_add_early_terminator_wrapper.h
index 273037e..97cc527 100644
--- a/source/fuzz/transformation_add_early_terminator_wrapper.h
+++ b/source/fuzz/transformation_add_early_terminator_wrapper.h
@@ -26,7 +26,7 @@
 class TransformationAddEarlyTerminatorWrapper : public Transformation {
  public:
   explicit TransformationAddEarlyTerminatorWrapper(
-      const protobufs::TransformationAddEarlyTerminatorWrapper& message);
+      protobufs::TransformationAddEarlyTerminatorWrapper message);
 
   TransformationAddEarlyTerminatorWrapper(uint32_t function_fresh_id,
                                           uint32_t label_fresh_id,
diff --git a/source/fuzz/transformation_add_function.cpp b/source/fuzz/transformation_add_function.cpp
index 9799373..06cd657 100644
--- a/source/fuzz/transformation_add_function.cpp
+++ b/source/fuzz/transformation_add_function.cpp
@@ -21,8 +21,8 @@
 namespace fuzz {
 
 TransformationAddFunction::TransformationAddFunction(
-    const spvtools::fuzz::protobufs::TransformationAddFunction& message)
-    : message_(message) {}
+    protobufs::TransformationAddFunction message)
+    : message_(std::move(message)) {}
 
 TransformationAddFunction::TransformationAddFunction(
     const std::vector<protobufs::Instruction>& instructions) {
diff --git a/source/fuzz/transformation_add_function.h b/source/fuzz/transformation_add_function.h
index e5381d1..c41eee3 100644
--- a/source/fuzz/transformation_add_function.h
+++ b/source/fuzz/transformation_add_function.h
@@ -26,7 +26,7 @@
 class TransformationAddFunction : public Transformation {
  public:
   explicit TransformationAddFunction(
-      const protobufs::TransformationAddFunction& message);
+      protobufs::TransformationAddFunction message);
 
   // Creates a transformation to add a non live-safe function.
   explicit TransformationAddFunction(
diff --git a/source/fuzz/transformation_add_global_undef.cpp b/source/fuzz/transformation_add_global_undef.cpp
index 7a90b82..eb390ea 100644
--- a/source/fuzz/transformation_add_global_undef.cpp
+++ b/source/fuzz/transformation_add_global_undef.cpp
@@ -20,8 +20,8 @@
 namespace fuzz {
 
 TransformationAddGlobalUndef::TransformationAddGlobalUndef(
-    const spvtools::fuzz::protobufs::TransformationAddGlobalUndef& message)
-    : message_(message) {}
+    spvtools::fuzz::protobufs::TransformationAddGlobalUndef message)
+    : message_(std::move(message)) {}
 
 TransformationAddGlobalUndef::TransformationAddGlobalUndef(uint32_t fresh_id,
                                                            uint32_t type_id) {
@@ -42,14 +42,14 @@
 
 void TransformationAddGlobalUndef::Apply(
     opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
-  ir_context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
+  auto new_instruction = MakeUnique<opt::Instruction>(
       ir_context, SpvOpUndef, message_.type_id(), message_.fresh_id(),
-      opt::Instruction::OperandList()));
+      opt::Instruction::OperandList());
+  auto new_instruction_ptr = new_instruction.get();
+  ir_context->module()->AddGlobalValue(std::move(new_instruction));
   fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
-  // We have added an instruction to the module, so need to be careful about the
-  // validity of existing analyses.
-  ir_context->InvalidateAnalysesExceptFor(
-      opt::IRContext::Analysis::kAnalysisNone);
+  // Inform the def-use manager about the new instruction.
+  ir_context->get_def_use_mgr()->AnalyzeInstDef(new_instruction_ptr);
 }
 
 protobufs::Transformation TransformationAddGlobalUndef::ToMessage() const {
diff --git a/source/fuzz/transformation_add_global_undef.h b/source/fuzz/transformation_add_global_undef.h
index 717dc9a..37542c3 100644
--- a/source/fuzz/transformation_add_global_undef.h
+++ b/source/fuzz/transformation_add_global_undef.h
@@ -26,7 +26,7 @@
 class TransformationAddGlobalUndef : public Transformation {
  public:
   explicit TransformationAddGlobalUndef(
-      const protobufs::TransformationAddGlobalUndef& message);
+      protobufs::TransformationAddGlobalUndef message);
 
   TransformationAddGlobalUndef(uint32_t fresh_id, uint32_t type_id);
 
diff --git a/source/fuzz/transformation_add_global_variable.cpp b/source/fuzz/transformation_add_global_variable.cpp
index dd04e48..814d01b 100644
--- a/source/fuzz/transformation_add_global_variable.cpp
+++ b/source/fuzz/transformation_add_global_variable.cpp
@@ -20,8 +20,8 @@
 namespace fuzz {
 
 TransformationAddGlobalVariable::TransformationAddGlobalVariable(
-    const spvtools::fuzz::protobufs::TransformationAddGlobalVariable& message)
-    : message_(message) {}
+    spvtools::fuzz::protobufs::TransformationAddGlobalVariable message)
+    : message_(std::move(message)) {}
 
 TransformationAddGlobalVariable::TransformationAddGlobalVariable(
     uint32_t fresh_id, uint32_t type_id, SpvStorageClass storage_class,
@@ -93,15 +93,13 @@
 void TransformationAddGlobalVariable::Apply(
     opt::IRContext* ir_context,
     TransformationContext* transformation_context) const {
-  fuzzerutil::AddGlobalVariable(
+  opt::Instruction* new_instruction = fuzzerutil::AddGlobalVariable(
       ir_context, message_.fresh_id(), message_.type_id(),
       static_cast<SpvStorageClass>(message_.storage_class()),
       message_.initializer_id());
 
-  // We have added an instruction to the module, so need to be careful about the
-  // validity of existing analyses.
-  ir_context->InvalidateAnalysesExceptFor(
-      opt::IRContext::Analysis::kAnalysisNone);
+  // Inform the def-use manager about the new instruction.
+  ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction);
 
   if (message_.value_is_irrelevant()) {
     transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
diff --git a/source/fuzz/transformation_add_global_variable.h b/source/fuzz/transformation_add_global_variable.h
index 8d46edb..d74d48a 100644
--- a/source/fuzz/transformation_add_global_variable.h
+++ b/source/fuzz/transformation_add_global_variable.h
@@ -26,7 +26,7 @@
 class TransformationAddGlobalVariable : public Transformation {
  public:
   explicit TransformationAddGlobalVariable(
-      const protobufs::TransformationAddGlobalVariable& message);
+      protobufs::TransformationAddGlobalVariable message);
 
   TransformationAddGlobalVariable(uint32_t fresh_id, uint32_t type_id,
                                   SpvStorageClass storage_class,
diff --git a/source/fuzz/transformation_add_image_sample_unused_components.cpp b/source/fuzz/transformation_add_image_sample_unused_components.cpp
index ab48f0b..1ead82b 100644
--- a/source/fuzz/transformation_add_image_sample_unused_components.cpp
+++ b/source/fuzz/transformation_add_image_sample_unused_components.cpp
@@ -22,9 +22,8 @@
 
 TransformationAddImageSampleUnusedComponents::
     TransformationAddImageSampleUnusedComponents(
-        const spvtools::fuzz::protobufs::
-            TransformationAddImageSampleUnusedComponents& message)
-    : message_(message) {}
+        protobufs::TransformationAddImageSampleUnusedComponents message)
+    : message_(std::move(message)) {}
 
 TransformationAddImageSampleUnusedComponents::
     TransformationAddImageSampleUnusedComponents(
diff --git a/source/fuzz/transformation_add_image_sample_unused_components.h b/source/fuzz/transformation_add_image_sample_unused_components.h
index 7486c76..7b13f9f 100644
--- a/source/fuzz/transformation_add_image_sample_unused_components.h
+++ b/source/fuzz/transformation_add_image_sample_unused_components.h
@@ -26,7 +26,7 @@
 class TransformationAddImageSampleUnusedComponents : public Transformation {
  public:
   explicit TransformationAddImageSampleUnusedComponents(
-      const protobufs::TransformationAddImageSampleUnusedComponents& message);
+      protobufs::TransformationAddImageSampleUnusedComponents message);
 
   TransformationAddImageSampleUnusedComponents(
       uint32_t coordinate_with_unused_components_id,
diff --git a/source/fuzz/transformation_add_local_variable.cpp b/source/fuzz/transformation_add_local_variable.cpp
index 0a7a3da..21768d2 100644
--- a/source/fuzz/transformation_add_local_variable.cpp
+++ b/source/fuzz/transformation_add_local_variable.cpp
@@ -20,8 +20,8 @@
 namespace fuzz {
 
 TransformationAddLocalVariable::TransformationAddLocalVariable(
-    const spvtools::fuzz::protobufs::TransformationAddLocalVariable& message)
-    : message_(message) {}
+    spvtools::fuzz::protobufs::TransformationAddLocalVariable message)
+    : message_(std::move(message)) {}
 
 TransformationAddLocalVariable::TransformationAddLocalVariable(
     uint32_t fresh_id, uint32_t type_id, uint32_t function_id,
@@ -70,11 +70,17 @@
 void TransformationAddLocalVariable::Apply(
     opt::IRContext* ir_context,
     TransformationContext* transformation_context) const {
-  fuzzerutil::AddLocalVariable(ir_context, message_.fresh_id(),
-                               message_.type_id(), message_.function_id(),
-                               message_.initializer_id());
+  opt::Instruction* new_instruction = fuzzerutil::AddLocalVariable(
+      ir_context, message_.fresh_id(), message_.type_id(),
+      message_.function_id(), message_.initializer_id());
 
-  ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+  // Inform the def-use manager about the new instruction.
+  ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction);
+  ir_context->set_instr_block(
+      new_instruction,
+      fuzzerutil::FindFunction(ir_context, message_.function_id())
+          ->entry()
+          .get());
 
   if (message_.value_is_irrelevant()) {
     transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
diff --git a/source/fuzz/transformation_add_local_variable.h b/source/fuzz/transformation_add_local_variable.h
index 963079f..b008a1c 100644
--- a/source/fuzz/transformation_add_local_variable.h
+++ b/source/fuzz/transformation_add_local_variable.h
@@ -26,7 +26,7 @@
 class TransformationAddLocalVariable : public Transformation {
  public:
   explicit TransformationAddLocalVariable(
-      const protobufs::TransformationAddLocalVariable& message);
+      protobufs::TransformationAddLocalVariable message);
 
   TransformationAddLocalVariable(uint32_t fresh_id, uint32_t type_id,
                                  uint32_t function_id, uint32_t initializer_id,
diff --git a/source/fuzz/transformation_add_loop_preheader.cpp b/source/fuzz/transformation_add_loop_preheader.cpp
index 3d50fa9..71ab18d 100644
--- a/source/fuzz/transformation_add_loop_preheader.cpp
+++ b/source/fuzz/transformation_add_loop_preheader.cpp
@@ -20,8 +20,8 @@
 namespace spvtools {
 namespace fuzz {
 TransformationAddLoopPreheader::TransformationAddLoopPreheader(
-    const protobufs::TransformationAddLoopPreheader& message)
-    : message_(message) {}
+    protobufs::TransformationAddLoopPreheader message)
+    : message_(std::move(message)) {}
 
 TransformationAddLoopPreheader::TransformationAddLoopPreheader(
     uint32_t loop_header_block, uint32_t fresh_id,
diff --git a/source/fuzz/transformation_add_loop_preheader.h b/source/fuzz/transformation_add_loop_preheader.h
index 05448f3..9d2c565 100644
--- a/source/fuzz/transformation_add_loop_preheader.h
+++ b/source/fuzz/transformation_add_loop_preheader.h
@@ -23,7 +23,7 @@
 class TransformationAddLoopPreheader : public Transformation {
  public:
   explicit TransformationAddLoopPreheader(
-      const protobufs::TransformationAddLoopPreheader& message);
+      protobufs::TransformationAddLoopPreheader message);
 
   TransformationAddLoopPreheader(uint32_t loop_header_block, uint32_t fresh_id,
                                  std::vector<uint32_t> phi_id);
diff --git a/source/fuzz/transformation_add_loop_to_create_int_constant_synonym.cpp b/source/fuzz/transformation_add_loop_to_create_int_constant_synonym.cpp
index 45d3fc8..657fafa 100644
--- a/source/fuzz/transformation_add_loop_to_create_int_constant_synonym.cpp
+++ b/source/fuzz/transformation_add_loop_to_create_int_constant_synonym.cpp
@@ -23,9 +23,8 @@
 
 TransformationAddLoopToCreateIntConstantSynonym::
     TransformationAddLoopToCreateIntConstantSynonym(
-        const protobufs::TransformationAddLoopToCreateIntConstantSynonym&
-            message)
-    : message_(message) {}
+        protobufs::TransformationAddLoopToCreateIntConstantSynonym message)
+    : message_(std::move(message)) {}
 
 TransformationAddLoopToCreateIntConstantSynonym::
     TransformationAddLoopToCreateIntConstantSynonym(
diff --git a/source/fuzz/transformation_add_loop_to_create_int_constant_synonym.h b/source/fuzz/transformation_add_loop_to_create_int_constant_synonym.h
index 67c3bcd..a6dfe63 100644
--- a/source/fuzz/transformation_add_loop_to_create_int_constant_synonym.h
+++ b/source/fuzz/transformation_add_loop_to_create_int_constant_synonym.h
@@ -22,8 +22,7 @@
 class TransformationAddLoopToCreateIntConstantSynonym : public Transformation {
  public:
   explicit TransformationAddLoopToCreateIntConstantSynonym(
-      const protobufs::TransformationAddLoopToCreateIntConstantSynonym&
-          message);
+      protobufs::TransformationAddLoopToCreateIntConstantSynonym message);
 
   TransformationAddLoopToCreateIntConstantSynonym(
       uint32_t constant_id, uint32_t initial_val_id, uint32_t step_val_id,
diff --git a/source/fuzz/transformation_add_no_contraction_decoration.cpp b/source/fuzz/transformation_add_no_contraction_decoration.cpp
index 29a871d..992a216 100644
--- a/source/fuzz/transformation_add_no_contraction_decoration.cpp
+++ b/source/fuzz/transformation_add_no_contraction_decoration.cpp
@@ -21,9 +21,8 @@
 
 TransformationAddNoContractionDecoration::
     TransformationAddNoContractionDecoration(
-        const spvtools::fuzz::protobufs::
-            TransformationAddNoContractionDecoration& message)
-    : message_(message) {}
+        protobufs::TransformationAddNoContractionDecoration message)
+    : message_(std::move(message)) {}
 
 TransformationAddNoContractionDecoration::
     TransformationAddNoContractionDecoration(uint32_t result_id) {
diff --git a/source/fuzz/transformation_add_no_contraction_decoration.h b/source/fuzz/transformation_add_no_contraction_decoration.h
index f5a34e8..2f78d42 100644
--- a/source/fuzz/transformation_add_no_contraction_decoration.h
+++ b/source/fuzz/transformation_add_no_contraction_decoration.h
@@ -26,7 +26,7 @@
 class TransformationAddNoContractionDecoration : public Transformation {
  public:
   explicit TransformationAddNoContractionDecoration(
-      const protobufs::TransformationAddNoContractionDecoration& message);
+      protobufs::TransformationAddNoContractionDecoration message);
 
   explicit TransformationAddNoContractionDecoration(uint32_t fresh_id);
 
diff --git a/source/fuzz/transformation_add_opphi_synonym.cpp b/source/fuzz/transformation_add_opphi_synonym.cpp
index 227c433..3c4698a 100644
--- a/source/fuzz/transformation_add_opphi_synonym.cpp
+++ b/source/fuzz/transformation_add_opphi_synonym.cpp
@@ -19,8 +19,8 @@
 namespace spvtools {
 namespace fuzz {
 TransformationAddOpPhiSynonym::TransformationAddOpPhiSynonym(
-    const protobufs::TransformationAddOpPhiSynonym& message)
-    : message_(message) {}
+    protobufs::TransformationAddOpPhiSynonym message)
+    : message_(std::move(message)) {}
 
 TransformationAddOpPhiSynonym::TransformationAddOpPhiSynonym(
     uint32_t block_id, const std::map<uint32_t, uint32_t>& preds_to_ids,
diff --git a/source/fuzz/transformation_add_opphi_synonym.h b/source/fuzz/transformation_add_opphi_synonym.h
index 3b68abe..39ebea8 100644
--- a/source/fuzz/transformation_add_opphi_synonym.h
+++ b/source/fuzz/transformation_add_opphi_synonym.h
@@ -22,7 +22,7 @@
 class TransformationAddOpPhiSynonym : public Transformation {
  public:
   explicit TransformationAddOpPhiSynonym(
-      const protobufs::TransformationAddOpPhiSynonym& message);
+      protobufs::TransformationAddOpPhiSynonym message);
 
   TransformationAddOpPhiSynonym(
       uint32_t block_id, const std::map<uint32_t, uint32_t>& preds_to_ids,
diff --git a/source/fuzz/transformation_add_parameter.cpp b/source/fuzz/transformation_add_parameter.cpp
index 9ed0bfb..48de3e8 100644
--- a/source/fuzz/transformation_add_parameter.cpp
+++ b/source/fuzz/transformation_add_parameter.cpp
@@ -20,8 +20,8 @@
 namespace fuzz {
 
 TransformationAddParameter::TransformationAddParameter(
-    const protobufs::TransformationAddParameter& message)
-    : message_(message) {}
+    protobufs::TransformationAddParameter message)
+    : message_(std::move(message)) {}
 
 TransformationAddParameter::TransformationAddParameter(
     uint32_t function_id, uint32_t parameter_fresh_id,
diff --git a/source/fuzz/transformation_add_parameter.h b/source/fuzz/transformation_add_parameter.h
index a33521d..0bc096e 100644
--- a/source/fuzz/transformation_add_parameter.h
+++ b/source/fuzz/transformation_add_parameter.h
@@ -26,7 +26,7 @@
 class TransformationAddParameter : public Transformation {
  public:
   explicit TransformationAddParameter(
-      const protobufs::TransformationAddParameter& message);
+      protobufs::TransformationAddParameter message);
 
   TransformationAddParameter(uint32_t function_id, uint32_t parameter_fresh_id,
                              uint32_t parameter_type_id,
diff --git a/source/fuzz/transformation_add_relaxed_decoration.cpp b/source/fuzz/transformation_add_relaxed_decoration.cpp
index 7b51305..b66a1a8 100644
--- a/source/fuzz/transformation_add_relaxed_decoration.cpp
+++ b/source/fuzz/transformation_add_relaxed_decoration.cpp
@@ -20,9 +20,8 @@
 namespace fuzz {
 
 TransformationAddRelaxedDecoration::TransformationAddRelaxedDecoration(
-    const spvtools::fuzz::protobufs::TransformationAddRelaxedDecoration&
-        message)
-    : message_(message) {}
+    protobufs::TransformationAddRelaxedDecoration message)
+    : message_(std::move(message)) {}
 
 TransformationAddRelaxedDecoration::TransformationAddRelaxedDecoration(
     uint32_t result_id) {
diff --git a/source/fuzz/transformation_add_relaxed_decoration.h b/source/fuzz/transformation_add_relaxed_decoration.h
index 3f8bf3e..c016349 100644
--- a/source/fuzz/transformation_add_relaxed_decoration.h
+++ b/source/fuzz/transformation_add_relaxed_decoration.h
@@ -26,7 +26,7 @@
 class TransformationAddRelaxedDecoration : public Transformation {
  public:
   explicit TransformationAddRelaxedDecoration(
-      const protobufs::TransformationAddRelaxedDecoration& message);
+      protobufs::TransformationAddRelaxedDecoration message);
 
   explicit TransformationAddRelaxedDecoration(uint32_t fresh_id);
 
diff --git a/source/fuzz/transformation_add_synonym.cpp b/source/fuzz/transformation_add_synonym.cpp
index a516916..a1949fb 100644
--- a/source/fuzz/transformation_add_synonym.cpp
+++ b/source/fuzz/transformation_add_synonym.cpp
@@ -102,14 +102,19 @@
     opt::IRContext* ir_context,
     TransformationContext* transformation_context) const {
   // Add a synonymous instruction.
-  FindInstruction(message_.insert_before(), ir_context)
-      ->InsertBefore(
-          MakeSynonymousInstruction(ir_context, *transformation_context));
+  auto new_instruction =
+      MakeSynonymousInstruction(ir_context, *transformation_context);
+  auto new_instruction_ptr = new_instruction.get();
+  auto insert_before = FindInstruction(message_.insert_before(), ir_context);
+  insert_before->InsertBefore(std::move(new_instruction));
 
   fuzzerutil::UpdateModuleIdBound(ir_context, message_.synonym_fresh_id());
 
-  ir_context->InvalidateAnalysesExceptFor(
-      opt::IRContext::Analysis::kAnalysisNone);
+  // Inform the def-use manager about the new instruction and record its basic
+  // block.
+  ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr);
+  ir_context->set_instr_block(new_instruction_ptr,
+                              ir_context->get_instr_block(insert_before));
 
   // Propagate PointeeValueIsIrrelevant fact.
   const auto* new_synonym_type = ir_context->get_type_mgr()->GetType(
@@ -165,6 +170,18 @@
 
       return type->AsInteger() || type->AsFloat();
     }
+    case protobufs::TransformationAddSynonym::BITWISE_OR:
+    case protobufs::TransformationAddSynonym::BITWISE_XOR: {
+      // The instruction must be either an integer or a vector of integers.
+      const auto* type = ir_context->get_type_mgr()->GetType(inst->type_id());
+      assert(type && "Instruction's result id is invalid");
+
+      if (const auto* vector = type->AsVector()) {
+        return vector->element_type()->AsInteger();
+      }
+
+      return type->AsInteger();
+    }
     case protobufs::TransformationAddSynonym::COPY_OBJECT:
       // All checks for OpCopyObject are handled by
       // fuzzerutil::CanMakeSynonymOf.
@@ -190,68 +207,56 @@
   auto synonym_type_id =
       fuzzerutil::GetTypeId(ir_context, message_.result_id());
   assert(synonym_type_id && "Synonym has invalid type id");
+  auto opcode = SpvOpNop;
+  const auto* synonym_type =
+      ir_context->get_type_mgr()->GetType(synonym_type_id);
+  assert(synonym_type && "Synonym has invalid type");
+
+  auto is_integral = (synonym_type->AsVector() &&
+                      synonym_type->AsVector()->element_type()->AsInteger()) ||
+                     synonym_type->AsInteger();
 
   switch (message_.synonym_type()) {
     case protobufs::TransformationAddSynonym::SUB_ZERO:
+      opcode = is_integral ? SpvOpISub : SpvOpFSub;
+      break;
     case protobufs::TransformationAddSynonym::MUL_ONE:
-    case protobufs::TransformationAddSynonym::ADD_ZERO: {
-      const auto* synonym_type =
-          ir_context->get_type_mgr()->GetType(synonym_type_id);
-      assert(synonym_type && "Synonym has invalid type");
+      opcode = is_integral ? SpvOpIMul : SpvOpFMul;
+      break;
+    case protobufs::TransformationAddSynonym::ADD_ZERO:
+      opcode = is_integral ? SpvOpIAdd : SpvOpFAdd;
+      break;
+    case protobufs::TransformationAddSynonym::LOGICAL_OR:
+      opcode = SpvOpLogicalOr;
+      break;
+    case protobufs::TransformationAddSynonym::LOGICAL_AND:
+      opcode = SpvOpLogicalAnd;
+      break;
+    case protobufs::TransformationAddSynonym::BITWISE_OR:
+      opcode = SpvOpBitwiseOr;
+      break;
+    case protobufs::TransformationAddSynonym::BITWISE_XOR:
+      opcode = SpvOpBitwiseXor;
+      break;
 
-      // Compute instruction's opcode based on the type of the operand.
-      // We have already checked that the operand is either a scalar or a vector
-      // of either integers or floats.
-      auto is_integral =
-          (synonym_type->AsVector() &&
-           synonym_type->AsVector()->element_type()->AsInteger()) ||
-          synonym_type->AsInteger();
-      auto opcode = SpvOpNop;
-      switch (message_.synonym_type()) {
-        case protobufs::TransformationAddSynonym::SUB_ZERO:
-          opcode = is_integral ? SpvOpISub : SpvOpFSub;
-          break;
-        case protobufs::TransformationAddSynonym::MUL_ONE:
-          opcode = is_integral ? SpvOpIMul : SpvOpFMul;
-          break;
-        case protobufs::TransformationAddSynonym::ADD_ZERO:
-          opcode = is_integral ? SpvOpIAdd : SpvOpFAdd;
-          break;
-        default:
-          assert(false && "Unreachable");
-          break;
-      }
-
-      return MakeUnique<opt::Instruction>(
-          ir_context, opcode, synonym_type_id, message_.synonym_fresh_id(),
-          opt::Instruction::OperandList{
-              {SPV_OPERAND_TYPE_ID, {message_.result_id()}},
-              {SPV_OPERAND_TYPE_ID,
-               {MaybeGetConstantId(ir_context, transformation_context)}}});
-    }
     case protobufs::TransformationAddSynonym::COPY_OBJECT:
       return MakeUnique<opt::Instruction>(
           ir_context, SpvOpCopyObject, synonym_type_id,
           message_.synonym_fresh_id(),
           opt::Instruction::OperandList{
               {SPV_OPERAND_TYPE_ID, {message_.result_id()}}});
-    case protobufs::TransformationAddSynonym::LOGICAL_OR:
-    case protobufs::TransformationAddSynonym::LOGICAL_AND: {
-      auto opcode = message_.synonym_type() ==
-                            protobufs::TransformationAddSynonym::LOGICAL_OR
-                        ? SpvOpLogicalOr
-                        : SpvOpLogicalAnd;
-      return MakeUnique<opt::Instruction>(
-          ir_context, opcode, synonym_type_id, message_.synonym_fresh_id(),
-          opt::Instruction::OperandList{
-              {SPV_OPERAND_TYPE_ID, {message_.result_id()}},
-              {SPV_OPERAND_TYPE_ID,
-               {MaybeGetConstantId(ir_context, transformation_context)}}});
-    }
+
     default:
       assert(false && "Unhandled synonym type");
       return nullptr;
   }
+
+  return MakeUnique<opt::Instruction>(
+      ir_context, opcode, synonym_type_id, message_.synonym_fresh_id(),
+      opt::Instruction::OperandList{
+          {SPV_OPERAND_TYPE_ID, {message_.result_id()}},
+          {SPV_OPERAND_TYPE_ID,
+           {MaybeGetConstantId(ir_context, transformation_context)}}});
 }
 
 uint32_t TransformationAddSynonym::MaybeGetConstantId(
@@ -268,6 +273,8 @@
     case protobufs::TransformationAddSynonym::ADD_ZERO:
     case protobufs::TransformationAddSynonym::SUB_ZERO:
     case protobufs::TransformationAddSynonym::LOGICAL_OR:
+    case protobufs::TransformationAddSynonym::BITWISE_OR:
+    case protobufs::TransformationAddSynonym::BITWISE_XOR:
       return fuzzerutil::MaybeGetZeroConstant(
           ir_context, transformation_context, synonym_type_id, false);
     case protobufs::TransformationAddSynonym::MUL_ONE:
@@ -314,6 +321,8 @@
     case protobufs::TransformationAddSynonym::LOGICAL_OR:
     case protobufs::TransformationAddSynonym::MUL_ONE:
     case protobufs::TransformationAddSynonym::LOGICAL_AND:
+    case protobufs::TransformationAddSynonym::BITWISE_OR:
+    case protobufs::TransformationAddSynonym::BITWISE_XOR:
       return true;
     default:
       return false;
diff --git a/source/fuzz/transformation_add_type_array.cpp b/source/fuzz/transformation_add_type_array.cpp
index c9f6a87..45bc8df 100644
--- a/source/fuzz/transformation_add_type_array.cpp
+++ b/source/fuzz/transformation_add_type_array.cpp
@@ -20,8 +20,8 @@
 namespace fuzz {
 
 TransformationAddTypeArray::TransformationAddTypeArray(
-    const spvtools::fuzz::protobufs::TransformationAddTypeArray& message)
-    : message_(message) {}
+    protobufs::TransformationAddTypeArray message)
+    : message_(std::move(message)) {}
 
 TransformationAddTypeArray::TransformationAddTypeArray(uint32_t fresh_id,
                                                        uint32_t element_type_id,
@@ -39,9 +39,11 @@
   }
   auto element_type =
       ir_context->get_type_mgr()->GetType(message_.element_type_id());
-  if (!element_type || element_type->AsFunction()) {
-    // The element type id either does not refer to a type, or refers to a
-    // function type; both are illegal.
+  if (!element_type || element_type->AsFunction() ||
+      fuzzerutil::HasBlockOrBufferBlockDecoration(ir_context,
+                                                  message_.element_type_id())) {
+    // The element type id either does not refer to a type, refers to a function
+    // type, or refers to a block-decorated struct. These cases are all illegal.
     return false;
   }
   auto constant =
@@ -69,13 +71,17 @@
   opt::Instruction::OperandList in_operands;
   in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.element_type_id()}});
   in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.size_id()}});
-  ir_context->module()->AddType(MakeUnique<opt::Instruction>(
-      ir_context, SpvOpTypeArray, 0, message_.fresh_id(), in_operands));
+  auto type_instruction = MakeUnique<opt::Instruction>(
+      ir_context, SpvOpTypeArray, 0, message_.fresh_id(), in_operands);
+  auto type_instruction_ptr = type_instruction.get();
+  ir_context->module()->AddType(std::move(type_instruction));
+
   fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
-  // We have added an instruction to the module, so need to be careful about the
-  // validity of existing analyses.
-  ir_context->InvalidateAnalysesExceptFor(
-      opt::IRContext::Analysis::kAnalysisNone);
+
+  // Inform the def use manager that there is a new definition. Invalidate the
+  // type manager since we have added a new type.
+  ir_context->get_def_use_mgr()->AnalyzeInstDef(type_instruction_ptr);
+  ir_context->InvalidateAnalyses(opt::IRContext::kAnalysisTypes);
 }
 
 protobufs::Transformation TransformationAddTypeArray::ToMessage() const {
diff --git a/source/fuzz/transformation_add_type_array.h b/source/fuzz/transformation_add_type_array.h
index ebefc23..3162497 100644
--- a/source/fuzz/transformation_add_type_array.h
+++ b/source/fuzz/transformation_add_type_array.h
@@ -26,15 +26,17 @@
 class TransformationAddTypeArray : public Transformation {
  public:
   explicit TransformationAddTypeArray(
-      const protobufs::TransformationAddTypeArray& message);
+      protobufs::TransformationAddTypeArray message);
 
   TransformationAddTypeArray(uint32_t fresh_id, uint32_t element_type_id,
                              uint32_t size_id);
 
   // - |message_.fresh_id| must be fresh
   // - |message_.element_type_id| must be the id of a non-function type
+  // - |message_.member_type_id| must not be the result id of an OpTypeStruct
+  //   instruction that has the Block or BufferBlock decoration
   // - |message_.size_id| must be the id of a 32-bit integer constant that is
-  //   positive when interpreted as signed.
+  //   positive when interpreted as signed
   bool IsApplicable(
       opt::IRContext* ir_context,
       const TransformationContext& transformation_context) const override;
diff --git a/source/fuzz/transformation_add_type_boolean.cpp b/source/fuzz/transformation_add_type_boolean.cpp
index ebbfabc..30ff43e 100644
--- a/source/fuzz/transformation_add_type_boolean.cpp
+++ b/source/fuzz/transformation_add_type_boolean.cpp
@@ -20,8 +20,8 @@
 namespace fuzz {
 
 TransformationAddTypeBoolean::TransformationAddTypeBoolean(
-    const spvtools::fuzz::protobufs::TransformationAddTypeBoolean& message)
-    : message_(message) {}
+    protobufs::TransformationAddTypeBoolean message)
+    : message_(std::move(message)) {}
 
 TransformationAddTypeBoolean::TransformationAddTypeBoolean(uint32_t fresh_id) {
   message_.set_fresh_id(fresh_id);
@@ -42,13 +42,17 @@
 void TransformationAddTypeBoolean::Apply(
     opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
   opt::Instruction::OperandList empty_operands;
-  ir_context->module()->AddType(MakeUnique<opt::Instruction>(
-      ir_context, SpvOpTypeBool, 0, message_.fresh_id(), empty_operands));
+  auto type_instruction = MakeUnique<opt::Instruction>(
+      ir_context, SpvOpTypeBool, 0, message_.fresh_id(), empty_operands);
+  auto type_instruction_ptr = type_instruction.get();
+  ir_context->module()->AddType(std::move(type_instruction));
+
   fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
-  // We have added an instruction to the module, so need to be careful about the
-  // validity of existing analyses.
-  ir_context->InvalidateAnalysesExceptFor(
-      opt::IRContext::Analysis::kAnalysisNone);
+
+  // Inform the def use manager that there is a new definition. Invalidate the
+  // type manager since we have added a new type.
+  ir_context->get_def_use_mgr()->AnalyzeInstDef(type_instruction_ptr);
+  ir_context->InvalidateAnalyses(opt::IRContext::kAnalysisTypes);
 }
 
 protobufs::Transformation TransformationAddTypeBoolean::ToMessage() const {
diff --git a/source/fuzz/transformation_add_type_boolean.h b/source/fuzz/transformation_add_type_boolean.h
index 25c9272..ee64015 100644
--- a/source/fuzz/transformation_add_type_boolean.h
+++ b/source/fuzz/transformation_add_type_boolean.h
@@ -25,7 +25,7 @@
 class TransformationAddTypeBoolean : public Transformation {
  public:
   explicit TransformationAddTypeBoolean(
-      const protobufs::TransformationAddTypeBoolean& message);
+      protobufs::TransformationAddTypeBoolean message);
 
   explicit TransformationAddTypeBoolean(uint32_t fresh_id);
 
diff --git a/source/fuzz/transformation_add_type_float.cpp b/source/fuzz/transformation_add_type_float.cpp
index da3f3e4..1b88b25 100644
--- a/source/fuzz/transformation_add_type_float.cpp
+++ b/source/fuzz/transformation_add_type_float.cpp
@@ -26,8 +26,8 @@
 }
 
 TransformationAddTypeFloat::TransformationAddTypeFloat(
-    const spvtools::fuzz::protobufs::TransformationAddTypeFloat& message)
-    : message_(message) {}
+    protobufs::TransformationAddTypeFloat message)
+    : message_(std::move(message)) {}
 
 bool TransformationAddTypeFloat::IsApplicable(
     opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
@@ -65,11 +65,18 @@
 
 void TransformationAddTypeFloat::Apply(
     opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
-  fuzzerutil::AddFloatType(ir_context, message_.fresh_id(), message_.width());
-  // We have added an instruction to the module, so need to be careful about the
-  // validity of existing analyses.
-  ir_context->InvalidateAnalysesExceptFor(
-      opt::IRContext::Analysis::kAnalysisNone);
+  auto type_instruction = MakeUnique<opt::Instruction>(
+      ir_context, SpvOpTypeFloat, 0, message_.fresh_id(),
+      opt::Instruction::OperandList{
+          {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.width()}}});
+  auto type_instruction_ptr = type_instruction.get();
+  ir_context->module()->AddType(std::move(type_instruction));
+  fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
+
+  // Inform the def use manager that there is a new definition, and invalidate
+  // the type manager since we have added a new type.
+  ir_context->get_def_use_mgr()->AnalyzeInstDef(type_instruction_ptr);
+  ir_context->InvalidateAnalyses(opt::IRContext::kAnalysisTypes);
 }
 
 protobufs::Transformation TransformationAddTypeFloat::ToMessage() const {
diff --git a/source/fuzz/transformation_add_type_float.h b/source/fuzz/transformation_add_type_float.h
index 30cd0fc..e049d9a 100644
--- a/source/fuzz/transformation_add_type_float.h
+++ b/source/fuzz/transformation_add_type_float.h
@@ -26,7 +26,7 @@
 class TransformationAddTypeFloat : public Transformation {
  public:
   explicit TransformationAddTypeFloat(
-      const protobufs::TransformationAddTypeFloat& message);
+      protobufs::TransformationAddTypeFloat message);
 
   TransformationAddTypeFloat(uint32_t fresh_id, uint32_t width);
 
diff --git a/source/fuzz/transformation_add_type_function.cpp b/source/fuzz/transformation_add_type_function.cpp
index b6cddfc..e96067f 100644
--- a/source/fuzz/transformation_add_type_function.cpp
+++ b/source/fuzz/transformation_add_type_function.cpp
@@ -22,8 +22,8 @@
 namespace fuzz {
 
 TransformationAddTypeFunction::TransformationAddTypeFunction(
-    const spvtools::fuzz::protobufs::TransformationAddTypeFunction& message)
-    : message_(message) {}
+    protobufs::TransformationAddTypeFunction message)
+    : message_(std::move(message)) {}
 
 TransformationAddTypeFunction::TransformationAddTypeFunction(
     uint32_t fresh_id, uint32_t return_type_id,
diff --git a/source/fuzz/transformation_add_type_function.h b/source/fuzz/transformation_add_type_function.h
index 59ded2b..7104457 100644
--- a/source/fuzz/transformation_add_type_function.h
+++ b/source/fuzz/transformation_add_type_function.h
@@ -28,7 +28,7 @@
 class TransformationAddTypeFunction : public Transformation {
  public:
   explicit TransformationAddTypeFunction(
-      const protobufs::TransformationAddTypeFunction& message);
+      protobufs::TransformationAddTypeFunction message);
 
   TransformationAddTypeFunction(uint32_t fresh_id, uint32_t return_type_id,
                                 const std::vector<uint32_t>& argument_type_ids);
diff --git a/source/fuzz/transformation_add_type_int.cpp b/source/fuzz/transformation_add_type_int.cpp
index 253ea15..d4ef981 100644
--- a/source/fuzz/transformation_add_type_int.cpp
+++ b/source/fuzz/transformation_add_type_int.cpp
@@ -20,8 +20,8 @@
 namespace fuzz {
 
 TransformationAddTypeInt::TransformationAddTypeInt(
-    const spvtools::fuzz::protobufs::TransformationAddTypeInt& message)
-    : message_(message) {}
+    protobufs::TransformationAddTypeInt message)
+    : message_(std::move(message)) {}
 
 TransformationAddTypeInt::TransformationAddTypeInt(uint32_t fresh_id,
                                                    uint32_t width,
@@ -74,12 +74,21 @@
 
 void TransformationAddTypeInt::Apply(opt::IRContext* ir_context,
                                      TransformationContext* /*unused*/) const {
-  fuzzerutil::AddIntegerType(ir_context, message_.fresh_id(), message_.width(),
-                             message_.is_signed());
-  // We have added an instruction to the module, so need to be careful about the
-  // validity of existing analyses.
-  ir_context->InvalidateAnalysesExceptFor(
-      opt::IRContext::Analysis::kAnalysisNone);
+  auto type_instruction = MakeUnique<opt::Instruction>(
+      ir_context, SpvOpTypeInt, 0, message_.fresh_id(),
+      opt::Instruction::OperandList{
+          {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.width()}},
+          {SPV_OPERAND_TYPE_LITERAL_INTEGER,
+           {message_.is_signed() ? 1u : 0u}}});
+  auto type_instruction_ptr = type_instruction.get();
+  ir_context->module()->AddType(std::move(type_instruction));
+
+  fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
+
+  // Inform the def use manager that there is a new definition. Invalidate the
+  // type manager since we have added a new type.
+  ir_context->get_def_use_mgr()->AnalyzeInstDef(type_instruction_ptr);
+  ir_context->InvalidateAnalyses(opt::IRContext::kAnalysisTypes);
 }
 
 protobufs::Transformation TransformationAddTypeInt::ToMessage() const {
diff --git a/source/fuzz/transformation_add_type_int.h b/source/fuzz/transformation_add_type_int.h
index 20c90ca..dc67b7d 100644
--- a/source/fuzz/transformation_add_type_int.h
+++ b/source/fuzz/transformation_add_type_int.h
@@ -26,7 +26,7 @@
 class TransformationAddTypeInt : public Transformation {
  public:
   explicit TransformationAddTypeInt(
-      const protobufs::TransformationAddTypeInt& message);
+      protobufs::TransformationAddTypeInt message);
 
   TransformationAddTypeInt(uint32_t fresh_id, uint32_t width, bool is_signed);
 
diff --git a/source/fuzz/transformation_add_type_matrix.cpp b/source/fuzz/transformation_add_type_matrix.cpp
index cecebb4..b574b01 100644
--- a/source/fuzz/transformation_add_type_matrix.cpp
+++ b/source/fuzz/transformation_add_type_matrix.cpp
@@ -20,8 +20,8 @@
 namespace fuzz {
 
 TransformationAddTypeMatrix::TransformationAddTypeMatrix(
-    const spvtools::fuzz::protobufs::TransformationAddTypeMatrix& message)
-    : message_(message) {}
+    protobufs::TransformationAddTypeMatrix message)
+    : message_(std::move(message)) {}
 
 TransformationAddTypeMatrix::TransformationAddTypeMatrix(
     uint32_t fresh_id, uint32_t column_type_id, uint32_t column_count) {
@@ -52,13 +52,17 @@
   in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.column_type_id()}});
   in_operands.push_back(
       {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.column_count()}});
-  ir_context->module()->AddType(MakeUnique<opt::Instruction>(
-      ir_context, SpvOpTypeMatrix, 0, message_.fresh_id(), in_operands));
+  auto type_instruction = MakeUnique<opt::Instruction>(
+      ir_context, SpvOpTypeMatrix, 0, message_.fresh_id(), in_operands);
+  auto type_instruction_ptr = type_instruction.get();
+  ir_context->module()->AddType(std::move(type_instruction));
+
   fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
-  // We have added an instruction to the module, so need to be careful about the
-  // validity of existing analyses.
-  ir_context->InvalidateAnalysesExceptFor(
-      opt::IRContext::Analysis::kAnalysisNone);
+
+  // Inform the def use manager that there is a new definition. Invalidate the
+  // type manager since we have added a new type.
+  ir_context->get_def_use_mgr()->AnalyzeInstDef(type_instruction_ptr);
+  ir_context->InvalidateAnalyses(opt::IRContext::kAnalysisTypes);
 }
 
 protobufs::Transformation TransformationAddTypeMatrix::ToMessage() const {
diff --git a/source/fuzz/transformation_add_type_matrix.h b/source/fuzz/transformation_add_type_matrix.h
index f1d4165..b4788b1 100644
--- a/source/fuzz/transformation_add_type_matrix.h
+++ b/source/fuzz/transformation_add_type_matrix.h
@@ -26,7 +26,7 @@
 class TransformationAddTypeMatrix : public Transformation {
  public:
   explicit TransformationAddTypeMatrix(
-      const protobufs::TransformationAddTypeMatrix& message);
+      protobufs::TransformationAddTypeMatrix message);
 
   TransformationAddTypeMatrix(uint32_t fresh_id, uint32_t column_type_id,
                               uint32_t column_count);
diff --git a/source/fuzz/transformation_add_type_pointer.cpp b/source/fuzz/transformation_add_type_pointer.cpp
index f74768d..c6c3945 100644
--- a/source/fuzz/transformation_add_type_pointer.cpp
+++ b/source/fuzz/transformation_add_type_pointer.cpp
@@ -20,8 +20,8 @@
 namespace fuzz {
 
 TransformationAddTypePointer::TransformationAddTypePointer(
-    const spvtools::fuzz::protobufs::TransformationAddTypePointer& message)
-    : message_(message) {}
+    protobufs::TransformationAddTypePointer message)
+    : message_(std::move(message)) {}
 
 TransformationAddTypePointer::TransformationAddTypePointer(
     uint32_t fresh_id, SpvStorageClass storage_class, uint32_t base_type_id) {
@@ -47,13 +47,17 @@
   opt::Instruction::OperandList in_operands = {
       {SPV_OPERAND_TYPE_STORAGE_CLASS, {message_.storage_class()}},
       {SPV_OPERAND_TYPE_ID, {message_.base_type_id()}}};
-  ir_context->module()->AddType(MakeUnique<opt::Instruction>(
-      ir_context, SpvOpTypePointer, 0, message_.fresh_id(), in_operands));
+  auto type_instruction = MakeUnique<opt::Instruction>(
+      ir_context, SpvOpTypePointer, 0, message_.fresh_id(), in_operands);
+  auto type_instruction_ptr = type_instruction.get();
+  ir_context->module()->AddType(std::move(type_instruction));
+
   fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
-  // We have added an instruction to the module, so need to be careful about the
-  // validity of existing analyses.
-  ir_context->InvalidateAnalysesExceptFor(
-      opt::IRContext::Analysis::kAnalysisNone);
+
+  // Inform the def use manager that there is a new definition. Invalidate the
+  // type manager since we have added a new type.
+  ir_context->get_def_use_mgr()->AnalyzeInstDef(type_instruction_ptr);
+  ir_context->InvalidateAnalyses(opt::IRContext::kAnalysisTypes);
 }
 
 protobufs::Transformation TransformationAddTypePointer::ToMessage() const {
diff --git a/source/fuzz/transformation_add_type_pointer.h b/source/fuzz/transformation_add_type_pointer.h
index 3f686e9..8468c14 100644
--- a/source/fuzz/transformation_add_type_pointer.h
+++ b/source/fuzz/transformation_add_type_pointer.h
@@ -26,7 +26,7 @@
 class TransformationAddTypePointer : public Transformation {
  public:
   explicit TransformationAddTypePointer(
-      const protobufs::TransformationAddTypePointer& message);
+      protobufs::TransformationAddTypePointer message);
 
   TransformationAddTypePointer(uint32_t fresh_id, SpvStorageClass storage_class,
                                uint32_t base_type_id);
diff --git a/source/fuzz/transformation_add_type_struct.cpp b/source/fuzz/transformation_add_type_struct.cpp
index b20ffb0..d7f0711 100644
--- a/source/fuzz/transformation_add_type_struct.cpp
+++ b/source/fuzz/transformation_add_type_struct.cpp
@@ -20,8 +20,8 @@
 namespace fuzz {
 
 TransformationAddTypeStruct::TransformationAddTypeStruct(
-    const spvtools::fuzz::protobufs::TransformationAddTypeStruct& message)
-    : message_(message) {}
+    protobufs::TransformationAddTypeStruct message)
+    : message_(std::move(message)) {}
 
 TransformationAddTypeStruct::TransformationAddTypeStruct(
     uint32_t fresh_id, const std::vector<uint32_t>& member_type_ids) {
@@ -39,9 +39,11 @@
   }
   for (auto member_type : message_.member_type_id()) {
     auto type = ir_context->get_type_mgr()->GetType(member_type);
-    if (!type || type->AsFunction()) {
-      // The member type id either does not refer to a type, or refers to a
-      // function type; both are illegal.
+    if (!type || type->AsFunction() ||
+        fuzzerutil::HasBlockOrBufferBlockDecoration(ir_context, member_type)) {
+      // The member type id either does not refer to a type, refers to a
+      // function type, or refers to a block-decorated struct. These cases are
+      // all illegal.
       return false;
     }
 
@@ -58,14 +60,36 @@
 
 void TransformationAddTypeStruct::Apply(
     opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
-  fuzzerutil::AddStructType(
-      ir_context, message_.fresh_id(),
-      std::vector<uint32_t>(message_.member_type_id().begin(),
-                            message_.member_type_id().end()));
-  // We have added an instruction to the module, so need to be careful about the
-  // validity of existing analyses.
-  ir_context->InvalidateAnalysesExceptFor(
-      opt::IRContext::Analysis::kAnalysisNone);
+  opt::Instruction::OperandList operands;
+  operands.reserve(message_.member_type_id().size());
+
+  for (auto type_id : message_.member_type_id()) {
+    const auto* type = ir_context->get_type_mgr()->GetType(type_id);
+    (void)type;  // Make compiler happy in release mode.
+    assert(type && !type->AsFunction() && "Component's type id is invalid");
+
+    if (type->AsStruct()) {
+      // From the spec for the BuiltIn decoration:
+      // - When applied to a structure-type member, that structure type cannot
+      //   be contained as a member of another structure type.
+      assert(!fuzzerutil::MembersHaveBuiltInDecoration(ir_context, type_id) &&
+             "A member struct has BuiltIn members");
+    }
+
+    operands.push_back({SPV_OPERAND_TYPE_ID, {type_id}});
+  }
+
+  auto type_instruction = MakeUnique<opt::Instruction>(
+      ir_context, SpvOpTypeStruct, 0, message_.fresh_id(), std::move(operands));
+  auto type_instruction_ptr = type_instruction.get();
+  ir_context->AddType(std::move(type_instruction));
+
+  fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
+
+  // Inform the def use manager that there is a new definition. Invalidate the
+  // type manager since we have added a new type.
+  ir_context->get_def_use_mgr()->AnalyzeInstDef(type_instruction_ptr);
+  ir_context->InvalidateAnalyses(opt::IRContext::kAnalysisTypes);
 }
 
 protobufs::Transformation TransformationAddTypeStruct::ToMessage() const {
diff --git a/source/fuzz/transformation_add_type_struct.h b/source/fuzz/transformation_add_type_struct.h
index 94be42a..6a8dce7 100644
--- a/source/fuzz/transformation_add_type_struct.h
+++ b/source/fuzz/transformation_add_type_struct.h
@@ -28,7 +28,7 @@
 class TransformationAddTypeStruct : public Transformation {
  public:
   explicit TransformationAddTypeStruct(
-      const protobufs::TransformationAddTypeStruct& message);
+      protobufs::TransformationAddTypeStruct message);
 
   TransformationAddTypeStruct(uint32_t fresh_id,
                               const std::vector<uint32_t>& component_type_ids);
@@ -37,7 +37,9 @@
   // - |message_.member_type_id| must be a sequence of non-function type ids
   // - |message_.member_type_id| may not contain a result id of an OpTypeStruct
   //   instruction with BuiltIn members (i.e. members of the struct are
-  //   decorated via OpMemberDecorate with BuiltIn decoration).
+  //   decorated via OpMemberDecorate with BuiltIn decoration)
+  // - |message_.member_type_id| may not contain a result id of an OpTypeStruct
+  //   instruction that has the Block or BufferBlock decoration
   bool IsApplicable(
       opt::IRContext* ir_context,
       const TransformationContext& transformation_context) const override;
diff --git a/source/fuzz/transformation_add_type_vector.cpp b/source/fuzz/transformation_add_type_vector.cpp
index a3b0010..4da0ff0 100644
--- a/source/fuzz/transformation_add_type_vector.cpp
+++ b/source/fuzz/transformation_add_type_vector.cpp
@@ -20,8 +20,8 @@
 namespace fuzz {
 
 TransformationAddTypeVector::TransformationAddTypeVector(
-    const spvtools::fuzz::protobufs::TransformationAddTypeVector& message)
-    : message_(message) {}
+    protobufs::TransformationAddTypeVector message)
+    : message_(std::move(message)) {}
 
 TransformationAddTypeVector::TransformationAddTypeVector(
     uint32_t fresh_id, uint32_t component_type_id, uint32_t component_count) {
@@ -46,13 +46,30 @@
 
 void TransformationAddTypeVector::Apply(
     opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
-  fuzzerutil::AddVectorType(ir_context, message_.fresh_id(),
-                            message_.component_type_id(),
-                            message_.component_count());
-  // We have added an instruction to the module, so need to be careful about the
-  // validity of existing analyses.
-  ir_context->InvalidateAnalysesExceptFor(
-      opt::IRContext::Analysis::kAnalysisNone);
+  const auto* component_type =
+      ir_context->get_type_mgr()->GetType(message_.component_type_id());
+  (void)component_type;  // Make compiler happy in release mode.
+  assert(component_type &&
+         (component_type->AsInteger() || component_type->AsFloat() ||
+          component_type->AsBool()) &&
+         "|component_type_id| is invalid");
+  assert(message_.component_count() >= 2 && message_.component_count() <= 4 &&
+         "Precondition: component count must be in range [2, 4].");
+
+  auto type_instruction = MakeUnique<opt::Instruction>(
+      ir_context, SpvOpTypeVector, 0, message_.fresh_id(),
+      opt::Instruction::OperandList{
+          {SPV_OPERAND_TYPE_ID, {message_.component_type_id()}},
+          {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.component_count()}}});
+  auto type_instruction_ptr = type_instruction.get();
+  ir_context->module()->AddType(std::move(type_instruction));
+
+  fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
+
+  // Inform the def use manager that there is a new definition. Invalidate the
+  // type manager since we have added a new type.
+  ir_context->get_def_use_mgr()->AnalyzeInstDef(type_instruction_ptr);
+  ir_context->InvalidateAnalyses(opt::IRContext::kAnalysisTypes);
 }
 
 protobufs::Transformation TransformationAddTypeVector::ToMessage() const {
diff --git a/source/fuzz/transformation_add_type_vector.h b/source/fuzz/transformation_add_type_vector.h
index c25d565..43460ce 100644
--- a/source/fuzz/transformation_add_type_vector.h
+++ b/source/fuzz/transformation_add_type_vector.h
@@ -26,7 +26,7 @@
 class TransformationAddTypeVector : public Transformation {
  public:
   explicit TransformationAddTypeVector(
-      const protobufs::TransformationAddTypeVector& message);
+      protobufs::TransformationAddTypeVector message);
 
   TransformationAddTypeVector(uint32_t fresh_id, uint32_t component_type_id,
                               uint32_t component_count);
diff --git a/source/fuzz/transformation_adjust_branch_weights.cpp b/source/fuzz/transformation_adjust_branch_weights.cpp
index 8b74ed3..21fef25 100644
--- a/source/fuzz/transformation_adjust_branch_weights.cpp
+++ b/source/fuzz/transformation_adjust_branch_weights.cpp
@@ -28,8 +28,8 @@
 }  // namespace
 
 TransformationAdjustBranchWeights::TransformationAdjustBranchWeights(
-    const spvtools::fuzz::protobufs::TransformationAdjustBranchWeights& message)
-    : message_(message) {}
+    protobufs::TransformationAdjustBranchWeights message)
+    : message_(std::move(message)) {}
 
 TransformationAdjustBranchWeights::TransformationAdjustBranchWeights(
     const protobufs::InstructionDescriptor& instruction_descriptor,
diff --git a/source/fuzz/transformation_adjust_branch_weights.h b/source/fuzz/transformation_adjust_branch_weights.h
index 4d451a5..41eceeb 100644
--- a/source/fuzz/transformation_adjust_branch_weights.h
+++ b/source/fuzz/transformation_adjust_branch_weights.h
@@ -26,7 +26,7 @@
 class TransformationAdjustBranchWeights : public Transformation {
  public:
   explicit TransformationAdjustBranchWeights(
-      const protobufs::TransformationAdjustBranchWeights& message);
+      protobufs::TransformationAdjustBranchWeights message);
 
   TransformationAdjustBranchWeights(
       const protobufs::InstructionDescriptor& instruction_descriptor,
diff --git a/source/fuzz/transformation_composite_construct.cpp b/source/fuzz/transformation_composite_construct.cpp
index f0de1ae..0cd2308 100644
--- a/source/fuzz/transformation_composite_construct.cpp
+++ b/source/fuzz/transformation_composite_construct.cpp
@@ -23,8 +23,8 @@
 namespace fuzz {
 
 TransformationCompositeConstruct::TransformationCompositeConstruct(
-    const protobufs::TransformationCompositeConstruct& message)
-    : message_(message) {}
+    protobufs::TransformationCompositeConstruct message)
+    : message_(std::move(message)) {}
 
 TransformationCompositeConstruct::TransformationCompositeConstruct(
     uint32_t composite_type_id, std::vector<uint32_t> component,
@@ -120,12 +120,18 @@
   }
 
   // Insert an OpCompositeConstruct instruction.
-  insert_before.InsertBefore(MakeUnique<opt::Instruction>(
+  auto new_instruction = MakeUnique<opt::Instruction>(
       ir_context, SpvOpCompositeConstruct, message_.composite_type_id(),
-      message_.fresh_id(), in_operands));
+      message_.fresh_id(), in_operands);
+  auto new_instruction_ptr = new_instruction.get();
+  insert_before.InsertBefore(std::move(new_instruction));
+  ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr);
+  ir_context->set_instr_block(new_instruction_ptr, destination_block);
 
   fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
-  ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+
+  // No analyses need to be invalidated since the transformation is local to a
+  // block and the def-use and instruction-to-block mappings have been updated.
 
   AddDataSynonymFacts(ir_context, transformation_context);
 }
diff --git a/source/fuzz/transformation_composite_construct.h b/source/fuzz/transformation_composite_construct.h
index 3a3e43c..cc44a61 100644
--- a/source/fuzz/transformation_composite_construct.h
+++ b/source/fuzz/transformation_composite_construct.h
@@ -26,7 +26,7 @@
 class TransformationCompositeConstruct : public Transformation {
  public:
   explicit TransformationCompositeConstruct(
-      const protobufs::TransformationCompositeConstruct& message);
+      protobufs::TransformationCompositeConstruct message);
 
   TransformationCompositeConstruct(
       uint32_t composite_type_id, std::vector<uint32_t> component,
diff --git a/source/fuzz/transformation_composite_extract.cpp b/source/fuzz/transformation_composite_extract.cpp
index 2aff02f..647cd74 100644
--- a/source/fuzz/transformation_composite_extract.cpp
+++ b/source/fuzz/transformation_composite_extract.cpp
@@ -24,8 +24,8 @@
 namespace fuzz {
 
 TransformationCompositeExtract::TransformationCompositeExtract(
-    const spvtools::fuzz::protobufs::TransformationCompositeExtract& message)
-    : message_(message) {}
+    protobufs::TransformationCompositeExtract message)
+    : message_(std::move(message)) {}
 
 TransformationCompositeExtract::TransformationCompositeExtract(
     const protobufs::InstructionDescriptor& instruction_to_insert_before,
@@ -89,15 +89,20 @@
   auto extracted_type = fuzzerutil::WalkCompositeTypeIndices(
       ir_context, composite_instruction->type_id(), message_.index());
 
-  FindInstruction(message_.instruction_to_insert_before(), ir_context)
-      ->InsertBefore(MakeUnique<opt::Instruction>(
+  auto insert_before =
+      FindInstruction(message_.instruction_to_insert_before(), ir_context);
+  opt::Instruction* new_instruction =
+      insert_before->InsertBefore(MakeUnique<opt::Instruction>(
           ir_context, SpvOpCompositeExtract, extracted_type,
           message_.fresh_id(), extract_operands));
+  ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction);
+  ir_context->set_instr_block(new_instruction,
+                              ir_context->get_instr_block(insert_before));
 
   fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
 
-  ir_context->InvalidateAnalysesExceptFor(
-      opt::IRContext::Analysis::kAnalysisNone);
+  // No analyses need to be invalidated since the transformation is local to a
+  // block and the def-use and instruction-to-block mappings have been updated.
 
   AddDataSynonymFacts(ir_context, transformation_context);
 }
diff --git a/source/fuzz/transformation_composite_extract.h b/source/fuzz/transformation_composite_extract.h
index 0f5348a..0682a61 100644
--- a/source/fuzz/transformation_composite_extract.h
+++ b/source/fuzz/transformation_composite_extract.h
@@ -26,7 +26,7 @@
 class TransformationCompositeExtract : public Transformation {
  public:
   explicit TransformationCompositeExtract(
-      const protobufs::TransformationCompositeExtract& message);
+      protobufs::TransformationCompositeExtract message);
 
   TransformationCompositeExtract(
       const protobufs::InstructionDescriptor& instruction_to_insert_before,
diff --git a/source/fuzz/transformation_composite_insert.cpp b/source/fuzz/transformation_composite_insert.cpp
index cc68141..05162bf 100644
--- a/source/fuzz/transformation_composite_insert.cpp
+++ b/source/fuzz/transformation_composite_insert.cpp
@@ -14,7 +14,6 @@
 
 #include "transformation_composite_insert.h"
 
-#include "source/fuzz/fuzzer_pass_add_composite_inserts.h"
 #include "source/fuzz/fuzzer_util.h"
 #include "source/fuzz/instruction_descriptor.h"
 
@@ -22,8 +21,8 @@
 namespace fuzz {
 
 TransformationCompositeInsert::TransformationCompositeInsert(
-    const spvtools::fuzz::protobufs::TransformationCompositeInsert& message)
-    : message_(message) {}
+    protobufs::TransformationCompositeInsert message)
+    : message_(std::move(message)) {}
 
 TransformationCompositeInsert::TransformationCompositeInsert(
     const protobufs::InstructionDescriptor& instruction_to_insert_before,
@@ -124,15 +123,21 @@
   auto composite_type_id =
       fuzzerutil::GetTypeId(ir_context, message_.composite_id());
 
-  FindInstruction(message_.instruction_to_insert_before(), ir_context)
-      ->InsertBefore(MakeUnique<opt::Instruction>(
-          ir_context, SpvOpCompositeInsert, composite_type_id,
-          message_.fresh_id(), std::move(in_operands)));
+  auto insert_before =
+      FindInstruction(message_.instruction_to_insert_before(), ir_context);
+  auto new_instruction = MakeUnique<opt::Instruction>(
+      ir_context, SpvOpCompositeInsert, composite_type_id, message_.fresh_id(),
+      std::move(in_operands));
+  auto new_instruction_ptr = new_instruction.get();
+  insert_before->InsertBefore(std::move(new_instruction));
 
   fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
 
-  // We have modified the module so most analyzes are now invalid.
-  ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+  // Inform the def-use manager about the new instruction and record its basic
+  // block.
+  ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr);
+  ir_context->set_instr_block(new_instruction_ptr,
+                              ir_context->get_instr_block(insert_before));
 
   // Add data synonym facts that arise from the insertion.
   AddDataSynonymFacts(ir_context, transformation_context);
diff --git a/source/fuzz/transformation_composite_insert.h b/source/fuzz/transformation_composite_insert.h
index f229014..413d41f 100644
--- a/source/fuzz/transformation_composite_insert.h
+++ b/source/fuzz/transformation_composite_insert.h
@@ -26,7 +26,7 @@
 class TransformationCompositeInsert : public Transformation {
  public:
   explicit TransformationCompositeInsert(
-      const protobufs::TransformationCompositeInsert& message);
+      protobufs::TransformationCompositeInsert message);
 
   TransformationCompositeInsert(
       const protobufs::InstructionDescriptor& instruction_to_insert_before,
diff --git a/source/fuzz/transformation_compute_data_synonym_fact_closure.cpp b/source/fuzz/transformation_compute_data_synonym_fact_closure.cpp
index f727052..7ae9df8 100644
--- a/source/fuzz/transformation_compute_data_synonym_fact_closure.cpp
+++ b/source/fuzz/transformation_compute_data_synonym_fact_closure.cpp
@@ -19,9 +19,8 @@
 
 TransformationComputeDataSynonymFactClosure::
     TransformationComputeDataSynonymFactClosure(
-        const spvtools::fuzz::protobufs::
-            TransformationComputeDataSynonymFactClosure& message)
-    : message_(message) {}
+        protobufs::TransformationComputeDataSynonymFactClosure message)
+    : message_(std::move(message)) {}
 
 TransformationComputeDataSynonymFactClosure::
     TransformationComputeDataSynonymFactClosure(
diff --git a/source/fuzz/transformation_compute_data_synonym_fact_closure.h b/source/fuzz/transformation_compute_data_synonym_fact_closure.h
index dedabe2..c61b26e 100644
--- a/source/fuzz/transformation_compute_data_synonym_fact_closure.h
+++ b/source/fuzz/transformation_compute_data_synonym_fact_closure.h
@@ -26,7 +26,7 @@
 class TransformationComputeDataSynonymFactClosure : public Transformation {
  public:
   explicit TransformationComputeDataSynonymFactClosure(
-      const protobufs::TransformationComputeDataSynonymFactClosure& message);
+      protobufs::TransformationComputeDataSynonymFactClosure message);
 
   explicit TransformationComputeDataSynonymFactClosure(
       uint32_t maximum_equivalence_class_size);
diff --git a/source/fuzz/transformation_duplicate_region_with_selection.cpp b/source/fuzz/transformation_duplicate_region_with_selection.cpp
index 2ac6259..dee1207 100644
--- a/source/fuzz/transformation_duplicate_region_with_selection.cpp
+++ b/source/fuzz/transformation_duplicate_region_with_selection.cpp
@@ -21,9 +21,8 @@
 
 TransformationDuplicateRegionWithSelection::
     TransformationDuplicateRegionWithSelection(
-        const spvtools::fuzz::protobufs::
-            TransformationDuplicateRegionWithSelection& message)
-    : message_(message) {}
+        protobufs::TransformationDuplicateRegionWithSelection message)
+    : message_(std::move(message)) {}
 
 TransformationDuplicateRegionWithSelection::
     TransformationDuplicateRegionWithSelection(
diff --git a/source/fuzz/transformation_duplicate_region_with_selection.h b/source/fuzz/transformation_duplicate_region_with_selection.h
index a2b9a43..30e3c37 100644
--- a/source/fuzz/transformation_duplicate_region_with_selection.h
+++ b/source/fuzz/transformation_duplicate_region_with_selection.h
@@ -26,7 +26,7 @@
 class TransformationDuplicateRegionWithSelection : public Transformation {
  public:
   explicit TransformationDuplicateRegionWithSelection(
-      const protobufs::TransformationDuplicateRegionWithSelection& message);
+      protobufs::TransformationDuplicateRegionWithSelection message);
 
   explicit TransformationDuplicateRegionWithSelection(
       uint32_t new_entry_fresh_id, uint32_t condition_id,
diff --git a/source/fuzz/transformation_equation_instruction.cpp b/source/fuzz/transformation_equation_instruction.cpp
index 32e8351..1e5dae9 100644
--- a/source/fuzz/transformation_equation_instruction.cpp
+++ b/source/fuzz/transformation_equation_instruction.cpp
@@ -21,8 +21,8 @@
 namespace fuzz {
 
 TransformationEquationInstruction::TransformationEquationInstruction(
-    const spvtools::fuzz::protobufs::TransformationEquationInstruction& message)
-    : message_(message) {}
+    protobufs::TransformationEquationInstruction message)
+    : message_(std::move(message)) {}
 
 TransformationEquationInstruction::TransformationEquationInstruction(
     uint32_t fresh_id, SpvOp opcode, const std::vector<uint32_t>& in_operand_id,
@@ -84,13 +84,17 @@
     rhs_id.push_back(id);
   }
 
-  FindInstruction(message_.instruction_to_insert_before(), ir_context)
-      ->InsertBefore(MakeUnique<opt::Instruction>(
+  auto insert_before =
+      FindInstruction(message_.instruction_to_insert_before(), ir_context);
+  opt::Instruction* new_instruction =
+      insert_before->InsertBefore(MakeUnique<opt::Instruction>(
           ir_context, static_cast<SpvOp>(message_.opcode()),
           MaybeGetResultTypeId(ir_context), message_.fresh_id(),
           std::move(in_operands)));
 
-  ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+  ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction);
+  ir_context->set_instr_block(new_instruction,
+                              ir_context->get_instr_block(insert_before));
 
   // Add an equation fact as long as the result id is not irrelevant (it could
   // be if we are inserting into a dead block).
diff --git a/source/fuzz/transformation_equation_instruction.h b/source/fuzz/transformation_equation_instruction.h
index 4afc85f..ae32a1a 100644
--- a/source/fuzz/transformation_equation_instruction.h
+++ b/source/fuzz/transformation_equation_instruction.h
@@ -28,7 +28,7 @@
 class TransformationEquationInstruction : public Transformation {
  public:
   explicit TransformationEquationInstruction(
-      const protobufs::TransformationEquationInstruction& message);
+      protobufs::TransformationEquationInstruction message);
 
   TransformationEquationInstruction(
       uint32_t fresh_id, SpvOp opcode,
diff --git a/source/fuzz/transformation_expand_vector_reduction.cpp b/source/fuzz/transformation_expand_vector_reduction.cpp
index 640aea2..9938706 100644
--- a/source/fuzz/transformation_expand_vector_reduction.cpp
+++ b/source/fuzz/transformation_expand_vector_reduction.cpp
@@ -21,9 +21,8 @@
 namespace fuzz {
 
 TransformationExpandVectorReduction::TransformationExpandVectorReduction(
-    const spvtools::fuzz::protobufs::TransformationExpandVectorReduction&
-        message)
-    : message_(message) {}
+    protobufs::TransformationExpandVectorReduction message)
+    : message_(std::move(message)) {}
 
 TransformationExpandVectorReduction::TransformationExpandVectorReduction(
     const uint32_t instruction_result_id,
diff --git a/source/fuzz/transformation_expand_vector_reduction.h b/source/fuzz/transformation_expand_vector_reduction.h
index e4cc953..6ee2cef 100644
--- a/source/fuzz/transformation_expand_vector_reduction.h
+++ b/source/fuzz/transformation_expand_vector_reduction.h
@@ -70,7 +70,7 @@
 class TransformationExpandVectorReduction : public Transformation {
  public:
   explicit TransformationExpandVectorReduction(
-      const protobufs::TransformationExpandVectorReduction& message);
+      protobufs::TransformationExpandVectorReduction message);
 
   TransformationExpandVectorReduction(const uint32_t instruction_result_id,
                                       const std::vector<uint32_t>& fresh_ids);
diff --git a/source/fuzz/transformation_flatten_conditional_branch.cpp b/source/fuzz/transformation_flatten_conditional_branch.cpp
index fdee513..b8c6de0 100644
--- a/source/fuzz/transformation_flatten_conditional_branch.cpp
+++ b/source/fuzz/transformation_flatten_conditional_branch.cpp
@@ -21,8 +21,8 @@
 namespace fuzz {
 
 TransformationFlattenConditionalBranch::TransformationFlattenConditionalBranch(
-    const protobufs::TransformationFlattenConditionalBranch& message)
-    : message_(message) {}
+    protobufs::TransformationFlattenConditionalBranch message)
+    : message_(std::move(message)) {}
 
 TransformationFlattenConditionalBranch::TransformationFlattenConditionalBranch(
     uint32_t header_block_id, bool true_branch_first,
@@ -844,7 +844,9 @@
     case SPV_ENV_UNIVERSAL_1_0:
     case SPV_ENV_UNIVERSAL_1_1:
     case SPV_ENV_UNIVERSAL_1_2:
-    case SPV_ENV_UNIVERSAL_1_3: {
+    case SPV_ENV_UNIVERSAL_1_3:
+    case SPV_ENV_VULKAN_1_0:
+    case SPV_ENV_VULKAN_1_1: {
       return true;
     }
     default:
diff --git a/source/fuzz/transformation_flatten_conditional_branch.h b/source/fuzz/transformation_flatten_conditional_branch.h
index 9bdae93..4efff67 100644
--- a/source/fuzz/transformation_flatten_conditional_branch.h
+++ b/source/fuzz/transformation_flatten_conditional_branch.h
@@ -23,7 +23,7 @@
 class TransformationFlattenConditionalBranch : public Transformation {
  public:
   explicit TransformationFlattenConditionalBranch(
-      const protobufs::TransformationFlattenConditionalBranch& message);
+      protobufs::TransformationFlattenConditionalBranch message);
 
   TransformationFlattenConditionalBranch(
       uint32_t header_block_id, bool true_branch_first,
diff --git a/source/fuzz/transformation_function_call.cpp b/source/fuzz/transformation_function_call.cpp
index ec95c32..0f88ce5 100644
--- a/source/fuzz/transformation_function_call.cpp
+++ b/source/fuzz/transformation_function_call.cpp
@@ -22,8 +22,8 @@
 namespace fuzz {
 
 TransformationFunctionCall::TransformationFunctionCall(
-    const spvtools::fuzz::protobufs::TransformationFunctionCall& message)
-    : message_(message) {}
+    protobufs::TransformationFunctionCall message)
+    : message_(std::move(message)) {}
 
 TransformationFunctionCall::TransformationFunctionCall(
     uint32_t fresh_id, uint32_t callee_id,
diff --git a/source/fuzz/transformation_function_call.h b/source/fuzz/transformation_function_call.h
index e220d83..a2aaf36 100644
--- a/source/fuzz/transformation_function_call.h
+++ b/source/fuzz/transformation_function_call.h
@@ -26,7 +26,7 @@
 class TransformationFunctionCall : public Transformation {
  public:
   explicit TransformationFunctionCall(
-      const protobufs::TransformationFunctionCall& message);
+      protobufs::TransformationFunctionCall message);
 
   TransformationFunctionCall(
       uint32_t fresh_id, uint32_t callee_id,
diff --git a/source/fuzz/transformation_inline_function.cpp b/source/fuzz/transformation_inline_function.cpp
index f997491..a48b817 100644
--- a/source/fuzz/transformation_inline_function.cpp
+++ b/source/fuzz/transformation_inline_function.cpp
@@ -21,8 +21,8 @@
 namespace fuzz {
 
 TransformationInlineFunction::TransformationInlineFunction(
-    const spvtools::fuzz::protobufs::TransformationInlineFunction& message)
-    : message_(message) {}
+    protobufs::TransformationInlineFunction message)
+    : message_(std::move(message)) {}
 
 TransformationInlineFunction::TransformationInlineFunction(
     uint32_t function_call_id,
diff --git a/source/fuzz/transformation_inline_function.h b/source/fuzz/transformation_inline_function.h
index 8105d92..f4dc410 100644
--- a/source/fuzz/transformation_inline_function.h
+++ b/source/fuzz/transformation_inline_function.h
@@ -26,7 +26,7 @@
 class TransformationInlineFunction : public Transformation {
  public:
   explicit TransformationInlineFunction(
-      const protobufs::TransformationInlineFunction& message);
+      protobufs::TransformationInlineFunction message);
 
   TransformationInlineFunction(
       uint32_t function_call_id,
diff --git a/source/fuzz/transformation_load.cpp b/source/fuzz/transformation_load.cpp
index f8b3513..e22f8dd 100644
--- a/source/fuzz/transformation_load.cpp
+++ b/source/fuzz/transformation_load.cpp
@@ -20,9 +20,8 @@
 namespace spvtools {
 namespace fuzz {
 
-TransformationLoad::TransformationLoad(
-    const spvtools::fuzz::protobufs::TransformationLoad& message)
-    : message_(message) {}
+TransformationLoad::TransformationLoad(protobufs::TransformationLoad message)
+    : message_(std::move(message)) {}
 
 TransformationLoad::TransformationLoad(
     uint32_t fresh_id, uint32_t pointer_id,
@@ -84,12 +83,19 @@
   uint32_t result_type = fuzzerutil::GetPointeeTypeIdFromPointerType(
       ir_context, fuzzerutil::GetTypeId(ir_context, message_.pointer_id()));
   fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
-  FindInstruction(message_.instruction_to_insert_before(), ir_context)
-      ->InsertBefore(MakeUnique<opt::Instruction>(
-          ir_context, SpvOpLoad, result_type, message_.fresh_id(),
-          opt::Instruction::OperandList(
-              {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}})));
-  ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+  auto insert_before =
+      FindInstruction(message_.instruction_to_insert_before(), ir_context);
+  auto new_instruction = MakeUnique<opt::Instruction>(
+      ir_context, SpvOpLoad, result_type, message_.fresh_id(),
+      opt::Instruction::OperandList(
+          {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}}));
+  auto new_instruction_ptr = new_instruction.get();
+  insert_before->InsertBefore(std::move(new_instruction));
+  // Inform the def-use manager about the new instruction and record its basic
+  // block.
+  ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr);
+  ir_context->set_instr_block(new_instruction_ptr,
+                              ir_context->get_instr_block(insert_before));
 }
 
 protobufs::Transformation TransformationLoad::ToMessage() const {
diff --git a/source/fuzz/transformation_load.h b/source/fuzz/transformation_load.h
index 683bba5..d10b007 100644
--- a/source/fuzz/transformation_load.h
+++ b/source/fuzz/transformation_load.h
@@ -25,7 +25,7 @@
 
 class TransformationLoad : public Transformation {
  public:
-  explicit TransformationLoad(const protobufs::TransformationLoad& message);
+  explicit TransformationLoad(protobufs::TransformationLoad message);
 
   TransformationLoad(
       uint32_t fresh_id, uint32_t pointer_id,
diff --git a/source/fuzz/transformation_make_vector_operation_dynamic.cpp b/source/fuzz/transformation_make_vector_operation_dynamic.cpp
index d6d5140..bd0664c 100644
--- a/source/fuzz/transformation_make_vector_operation_dynamic.cpp
+++ b/source/fuzz/transformation_make_vector_operation_dynamic.cpp
@@ -22,9 +22,8 @@
 
 TransformationMakeVectorOperationDynamic::
     TransformationMakeVectorOperationDynamic(
-        const spvtools::fuzz::protobufs::
-            TransformationMakeVectorOperationDynamic& message)
-    : message_(message) {}
+        protobufs::TransformationMakeVectorOperationDynamic message)
+    : message_(std::move(message)) {}
 
 TransformationMakeVectorOperationDynamic::
     TransformationMakeVectorOperationDynamic(uint32_t instruction_result_id,
diff --git a/source/fuzz/transformation_make_vector_operation_dynamic.h b/source/fuzz/transformation_make_vector_operation_dynamic.h
index d1765c5..e444f40 100644
--- a/source/fuzz/transformation_make_vector_operation_dynamic.h
+++ b/source/fuzz/transformation_make_vector_operation_dynamic.h
@@ -26,7 +26,7 @@
 class TransformationMakeVectorOperationDynamic : public Transformation {
  public:
   explicit TransformationMakeVectorOperationDynamic(
-      const protobufs::TransformationMakeVectorOperationDynamic& message);
+      protobufs::TransformationMakeVectorOperationDynamic message);
 
   TransformationMakeVectorOperationDynamic(uint32_t instruction_result_id,
                                            uint32_t constant_index_id);
diff --git a/source/fuzz/transformation_merge_blocks.cpp b/source/fuzz/transformation_merge_blocks.cpp
index 2a9e90c..2223679 100644
--- a/source/fuzz/transformation_merge_blocks.cpp
+++ b/source/fuzz/transformation_merge_blocks.cpp
@@ -21,8 +21,8 @@
 namespace fuzz {
 
 TransformationMergeBlocks::TransformationMergeBlocks(
-    const spvtools::fuzz::protobufs::TransformationMergeBlocks& message)
-    : message_(message) {}
+    protobufs::TransformationMergeBlocks message)
+    : message_(std::move(message)) {}
 
 TransformationMergeBlocks::TransformationMergeBlocks(uint32_t block_id) {
   message_.set_block_id(block_id);
diff --git a/source/fuzz/transformation_merge_blocks.h b/source/fuzz/transformation_merge_blocks.h
index d9a0ca0..f6306c5 100644
--- a/source/fuzz/transformation_merge_blocks.h
+++ b/source/fuzz/transformation_merge_blocks.h
@@ -26,7 +26,7 @@
 class TransformationMergeBlocks : public Transformation {
  public:
   explicit TransformationMergeBlocks(
-      const protobufs::TransformationMergeBlocks& message);
+      protobufs::TransformationMergeBlocks message);
 
   TransformationMergeBlocks(uint32_t block_id);
 
diff --git a/source/fuzz/transformation_merge_function_returns.cpp b/source/fuzz/transformation_merge_function_returns.cpp
index c7cb557..022e1b6 100644
--- a/source/fuzz/transformation_merge_function_returns.cpp
+++ b/source/fuzz/transformation_merge_function_returns.cpp
@@ -21,15 +21,17 @@
 namespace fuzz {
 
 TransformationMergeFunctionReturns::TransformationMergeFunctionReturns(
-    const protobufs::TransformationMergeFunctionReturns& message)
-    : message_(message) {}
+    protobufs::TransformationMergeFunctionReturns message)
+    : message_(std::move(message)) {}
 
 TransformationMergeFunctionReturns::TransformationMergeFunctionReturns(
-    uint32_t function_id, uint32_t outer_header_id, uint32_t outer_return_id,
+    uint32_t function_id, uint32_t outer_header_id,
+    uint32_t unreachable_continue_id, uint32_t outer_return_id,
     uint32_t return_val_id, uint32_t any_returnable_val_id,
     const std::vector<protobufs::ReturnMergingInfo>& returns_merging_info) {
   message_.set_function_id(function_id);
   message_.set_outer_header_id(outer_header_id);
+  message_.set_unreachable_continue_id(unreachable_continue_id);
   message_.set_outer_return_id(outer_return_id);
   message_.set_return_val_id(return_val_id);
   message_.set_any_returnable_val_id(any_returnable_val_id);
@@ -66,7 +68,9 @@
 
   // Check that the fresh ids provided are fresh and distinct.
   std::set<uint32_t> used_fresh_ids;
-  for (uint32_t id : {message_.outer_header_id(), message_.outer_return_id()}) {
+  for (uint32_t id :
+       {message_.outer_header_id(), message_.unreachable_continue_id(),
+        message_.outer_return_id()}) {
     if (!id || !CheckIdIsFreshAndNotUsedByThisTransformation(id, ir_context,
                                                              &used_fresh_ids)) {
       return false;
@@ -499,25 +503,20 @@
 
   fuzzerutil::UpdateModuleIdBound(ir_context, message_.outer_header_id());
 
-  // Add the instruction: OpLoopMerge %outer_return_id %outer_header_id None
-  // The header is the continue block of the outer loop.
+  // Add the instruction:
+  //   OpLoopMerge %outer_return_id %unreachable_continue_id None
   outer_loop_header->AddInstruction(MakeUnique<opt::Instruction>(
       ir_context, SpvOpLoopMerge, 0, 0,
       opt::Instruction::OperandList{
           {SPV_OPERAND_TYPE_ID, {message_.outer_return_id()}},
-          {SPV_OPERAND_TYPE_ID, {message_.outer_header_id()}},
+          {SPV_OPERAND_TYPE_ID, {message_.unreachable_continue_id()}},
           {SPV_OPERAND_TYPE_LOOP_CONTROL, {SpvLoopControlMaskNone}}}));
 
-  // Add conditional branch:
-  //   OpBranchConditional %true %block_after_entry %outer_header_id
-  // This will always branch to %block_after_entry, but it also creates a back
-  // edge for the loop (which is never traversed).
+  // Add unconditional branch to %block_after_entry.
   outer_loop_header->AddInstruction(MakeUnique<opt::Instruction>(
-      ir_context, SpvOpBranchConditional, 0, 0,
+      ir_context, SpvOpBranch, 0, 0,
       opt::Instruction::OperandList{
-          {SPV_OPERAND_TYPE_ID, {constant_true}},
-          {SPV_OPERAND_TYPE_ID, {block_after_entry}},
-          {SPV_OPERAND_TYPE_ID, {message_.outer_header_id()}}}));
+          {SPV_OPERAND_TYPE_ID, {block_after_entry}}}));
 
   // Insert the header right after the entry block.
   function->InsertBasicBlockAfter(std::move(outer_loop_header),
@@ -581,6 +580,24 @@
   // Insert the new return block at the end of the function.
   function->AddBasicBlock(std::move(outer_return_block));
 
+  // Create the unreachable continue block associated with the enclosing loop.
+  auto unreachable_continue_block =
+      MakeUnique<opt::BasicBlock>(MakeUnique<opt::Instruction>(
+          ir_context, SpvOpLabel, 0, message_.unreachable_continue_id(),
+          opt::Instruction::OperandList()));
+
+  fuzzerutil::UpdateModuleIdBound(ir_context,
+                                  message_.unreachable_continue_id());
+
+  // Insert an branch back to the loop header, to create a back edge.
+  unreachable_continue_block->AddInstruction(MakeUnique<opt::Instruction>(
+      ir_context, SpvOpBranch, 0, 0,
+      opt::Instruction::OperandList{
+          {SPV_OPERAND_TYPE_ID, {message_.outer_header_id()}}}));
+
+  // Insert the unreachable continue block at the end of the function.
+  function->AddBasicBlock(std::move(unreachable_continue_block));
+
   // All analyses must be invalidated because the structure of the module was
   // changed.
   ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
@@ -590,13 +607,14 @@
     const {
   std::unordered_set<uint32_t> result;
   result.emplace(message_.outer_header_id());
+  result.emplace(message_.unreachable_continue_id());
   result.emplace(message_.outer_return_id());
   // |message_.return_val_info| can be 0 if the function is void.
   if (message_.return_val_id()) {
     result.emplace(message_.return_val_id());
   }
 
-  for (auto merging_info : message_.return_merging_info()) {
+  for (const auto& merging_info : message_.return_merging_info()) {
     result.emplace(merging_info.is_returning_id());
     // |maybe_return_val_id| can be 0 if the function is void.
     if (merging_info.maybe_return_val_id()) {
diff --git a/source/fuzz/transformation_merge_function_returns.h b/source/fuzz/transformation_merge_function_returns.h
index 4b29936..b3208ac 100644
--- a/source/fuzz/transformation_merge_function_returns.h
+++ b/source/fuzz/transformation_merge_function_returns.h
@@ -22,10 +22,11 @@
 class TransformationMergeFunctionReturns : public Transformation {
  public:
   explicit TransformationMergeFunctionReturns(
-      const protobufs::TransformationMergeFunctionReturns& message);
+      protobufs::TransformationMergeFunctionReturns message);
 
   TransformationMergeFunctionReturns(
-      uint32_t function_id, uint32_t outer_header_id, uint32_t outer_return_id,
+      uint32_t function_id, uint32_t outer_header_id,
+      uint32_t unreachable_continue_id, uint32_t outer_return_id,
       uint32_t return_val_id, uint32_t any_returnable_val_id,
       const std::vector<protobufs::ReturnMergingInfo>& returns_merging_info);
 
@@ -40,7 +41,7 @@
   //   statements, this id will be ignored.
   // - Merge blocks of reachable loops that contain return statements only
   //   consist of OpLabel, OpPhi or OpBranch instructions.
-  // - The model contains OpConstantTrue and OpConstantFalse instructions.
+  // - The module contains OpConstantTrue and OpConstantFalse instructions.
   // - For all merge blocks of reachable loops that contain return statements,
   //   either:
   //   - a mapping is provided in |message_.return_merging_info|, all of the
diff --git a/source/fuzz/transformation_move_block_down.cpp b/source/fuzz/transformation_move_block_down.cpp
index c5ed433..dc1b243 100644
--- a/source/fuzz/transformation_move_block_down.cpp
+++ b/source/fuzz/transformation_move_block_down.cpp
@@ -20,8 +20,8 @@
 namespace fuzz {
 
 TransformationMoveBlockDown::TransformationMoveBlockDown(
-    const spvtools::fuzz::protobufs::TransformationMoveBlockDown& message)
-    : message_(message) {}
+    protobufs::TransformationMoveBlockDown message)
+    : message_(std::move(message)) {}
 
 TransformationMoveBlockDown::TransformationMoveBlockDown(uint32_t id) {
   message_.set_block_id(id);
diff --git a/source/fuzz/transformation_move_block_down.h b/source/fuzz/transformation_move_block_down.h
index 82f2599..cbad945 100644
--- a/source/fuzz/transformation_move_block_down.h
+++ b/source/fuzz/transformation_move_block_down.h
@@ -26,7 +26,7 @@
 class TransformationMoveBlockDown : public Transformation {
  public:
   explicit TransformationMoveBlockDown(
-      const protobufs::TransformationMoveBlockDown& message);
+      protobufs::TransformationMoveBlockDown message);
 
   explicit TransformationMoveBlockDown(uint32_t id);
 
diff --git a/source/fuzz/transformation_move_instruction_down.cpp b/source/fuzz/transformation_move_instruction_down.cpp
index dec0578..c8139e7 100644
--- a/source/fuzz/transformation_move_instruction_down.cpp
+++ b/source/fuzz/transformation_move_instruction_down.cpp
@@ -38,8 +38,8 @@
 }  // namespace
 
 TransformationMoveInstructionDown::TransformationMoveInstructionDown(
-    const protobufs::TransformationMoveInstructionDown& message)
-    : message_(message) {}
+    protobufs::TransformationMoveInstructionDown message)
+    : message_(std::move(message)) {}
 
 TransformationMoveInstructionDown::TransformationMoveInstructionDown(
     const protobufs::InstructionDescriptor& instruction) {
diff --git a/source/fuzz/transformation_move_instruction_down.h b/source/fuzz/transformation_move_instruction_down.h
index 8585225..2a5a8f1 100644
--- a/source/fuzz/transformation_move_instruction_down.h
+++ b/source/fuzz/transformation_move_instruction_down.h
@@ -26,7 +26,7 @@
 class TransformationMoveInstructionDown : public Transformation {
  public:
   explicit TransformationMoveInstructionDown(
-      const protobufs::TransformationMoveInstructionDown& message);
+      protobufs::TransformationMoveInstructionDown message);
 
   explicit TransformationMoveInstructionDown(
       const protobufs::InstructionDescriptor& instruction);
diff --git a/source/fuzz/transformation_mutate_pointer.cpp b/source/fuzz/transformation_mutate_pointer.cpp
index fefedbd..516a0d6 100644
--- a/source/fuzz/transformation_mutate_pointer.cpp
+++ b/source/fuzz/transformation_mutate_pointer.cpp
@@ -21,8 +21,8 @@
 namespace fuzz {
 
 TransformationMutatePointer::TransformationMutatePointer(
-    const protobufs::TransformationMutatePointer& message)
-    : message_(message) {}
+    protobufs::TransformationMutatePointer message)
+    : message_(std::move(message)) {}
 
 TransformationMutatePointer::TransformationMutatePointer(
     uint32_t pointer_id, uint32_t fresh_id,
@@ -92,36 +92,47 @@
   auto* insert_before_inst =
       FindInstruction(message_.insert_before(), ir_context);
   assert(insert_before_inst && "|insert_before| descriptor is invalid");
+  opt::BasicBlock* enclosing_block =
+      ir_context->get_instr_block(insert_before_inst);
 
   auto pointee_type_id = fuzzerutil::GetPointeeTypeIdFromPointerType(
       ir_context, fuzzerutil::GetTypeId(ir_context, message_.pointer_id()));
 
   // Back up the original value.
-  insert_before_inst->InsertBefore(MakeUnique<opt::Instruction>(
+  auto backup_instruction = MakeUnique<opt::Instruction>(
       ir_context, SpvOpLoad, pointee_type_id, message_.fresh_id(),
       opt::Instruction::OperandList{
-          {SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}}));
+          {SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}});
+  auto backup_instruction_ptr = backup_instruction.get();
+  insert_before_inst->InsertBefore(std::move(backup_instruction));
+  ir_context->get_def_use_mgr()->AnalyzeInstDefUse(backup_instruction_ptr);
+  ir_context->set_instr_block(backup_instruction_ptr, enclosing_block);
 
   // Insert a new value.
-  insert_before_inst->InsertBefore(MakeUnique<opt::Instruction>(
+  auto new_value_instruction = MakeUnique<opt::Instruction>(
       ir_context, SpvOpStore, 0, 0,
       opt::Instruction::OperandList{
           {SPV_OPERAND_TYPE_ID, {message_.pointer_id()}},
           {SPV_OPERAND_TYPE_ID,
            {fuzzerutil::MaybeGetZeroConstant(
-               ir_context, *transformation_context, pointee_type_id, true)}}}));
+               ir_context, *transformation_context, pointee_type_id, true)}}});
+  auto new_value_instruction_ptr = new_value_instruction.get();
+  insert_before_inst->InsertBefore(std::move(new_value_instruction));
+  ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_value_instruction_ptr);
+  ir_context->set_instr_block(new_value_instruction_ptr, enclosing_block);
 
   // Restore the original value.
-  insert_before_inst->InsertBefore(MakeUnique<opt::Instruction>(
+  auto restore_instruction = MakeUnique<opt::Instruction>(
       ir_context, SpvOpStore, 0, 0,
       opt::Instruction::OperandList{
           {SPV_OPERAND_TYPE_ID, {message_.pointer_id()}},
-          {SPV_OPERAND_TYPE_ID, {message_.fresh_id()}}}));
+          {SPV_OPERAND_TYPE_ID, {message_.fresh_id()}}});
+  auto restore_instruction_ptr = restore_instruction.get();
+  insert_before_inst->InsertBefore(std::move(restore_instruction));
+  ir_context->get_def_use_mgr()->AnalyzeInstDefUse(restore_instruction_ptr);
+  ir_context->set_instr_block(restore_instruction_ptr, enclosing_block);
 
   fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
-
-  // Make sure analyses represent the correct state of the module.
-  ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
 }
 
 protobufs::Transformation TransformationMutatePointer::ToMessage() const {
diff --git a/source/fuzz/transformation_mutate_pointer.h b/source/fuzz/transformation_mutate_pointer.h
index b9f0965..2c71290 100644
--- a/source/fuzz/transformation_mutate_pointer.h
+++ b/source/fuzz/transformation_mutate_pointer.h
@@ -26,7 +26,7 @@
 class TransformationMutatePointer : public Transformation {
  public:
   explicit TransformationMutatePointer(
-      const protobufs::TransformationMutatePointer& message);
+      protobufs::TransformationMutatePointer message);
 
   explicit TransformationMutatePointer(
       uint32_t pointer_id, uint32_t fresh_id,
diff --git a/source/fuzz/transformation_outline_function.cpp b/source/fuzz/transformation_outline_function.cpp
index 643fd69..84e8ac2 100644
--- a/source/fuzz/transformation_outline_function.cpp
+++ b/source/fuzz/transformation_outline_function.cpp
@@ -22,8 +22,8 @@
 namespace fuzz {
 
 TransformationOutlineFunction::TransformationOutlineFunction(
-    const spvtools::fuzz::protobufs::TransformationOutlineFunction& message)
-    : message_(message) {}
+    protobufs::TransformationOutlineFunction message)
+    : message_(std::move(message)) {}
 
 TransformationOutlineFunction::TransformationOutlineFunction(
     uint32_t entry_block, uint32_t exit_block,
@@ -175,6 +175,19 @@
   // This is achieved by going through every block in the function that contains
   // the region.
   for (auto& block : *entry_block->GetParent()) {
+    if (region_set.count(&block) != 0) {
+      // The block is in the region. Check that it does not have any unreachable
+      // predecessors. If it does, then we do not regard the region as single-
+      // entry-single-exit and hence do not outline it.
+      for (auto pred : ir_context->cfg()->preds(block.id())) {
+        if (!fuzzerutil::BlockIsReachableInItsFunction(
+                ir_context, ir_context->cfg()->block(pred))) {
+          // The predecessor is unreachable.
+          return false;
+        }
+      }
+    }
+
     if (&block == exit_block) {
       // It is OK (and typically expected) for the exit block of the region to
       // have successors outside the region.
diff --git a/source/fuzz/transformation_outline_function.h b/source/fuzz/transformation_outline_function.h
index 36c0daf..94ce556 100644
--- a/source/fuzz/transformation_outline_function.h
+++ b/source/fuzz/transformation_outline_function.h
@@ -30,7 +30,7 @@
 class TransformationOutlineFunction : public Transformation {
  public:
   explicit TransformationOutlineFunction(
-      const protobufs::TransformationOutlineFunction& message);
+      protobufs::TransformationOutlineFunction message);
 
   TransformationOutlineFunction(
       uint32_t entry_block, uint32_t exit_block,
diff --git a/source/fuzz/transformation_permute_function_parameters.cpp b/source/fuzz/transformation_permute_function_parameters.cpp
index a954cc1..5663d72 100644
--- a/source/fuzz/transformation_permute_function_parameters.cpp
+++ b/source/fuzz/transformation_permute_function_parameters.cpp
@@ -23,9 +23,8 @@
 
 TransformationPermuteFunctionParameters::
     TransformationPermuteFunctionParameters(
-        const spvtools::fuzz::protobufs::
-            TransformationPermuteFunctionParameters& message)
-    : message_(message) {}
+        protobufs::TransformationPermuteFunctionParameters message)
+    : message_(std::move(message)) {}
 
 TransformationPermuteFunctionParameters::
     TransformationPermuteFunctionParameters(
diff --git a/source/fuzz/transformation_permute_function_parameters.h b/source/fuzz/transformation_permute_function_parameters.h
index 38de8b1..abb5675 100644
--- a/source/fuzz/transformation_permute_function_parameters.h
+++ b/source/fuzz/transformation_permute_function_parameters.h
@@ -26,7 +26,7 @@
 class TransformationPermuteFunctionParameters : public Transformation {
  public:
   explicit TransformationPermuteFunctionParameters(
-      const protobufs::TransformationPermuteFunctionParameters& message);
+      protobufs::TransformationPermuteFunctionParameters message);
 
   TransformationPermuteFunctionParameters(
       uint32_t function_id, uint32_t function_type_fresh_id,
diff --git a/source/fuzz/transformation_permute_phi_operands.cpp b/source/fuzz/transformation_permute_phi_operands.cpp
index ebd3c86..7ee7a82 100644
--- a/source/fuzz/transformation_permute_phi_operands.cpp
+++ b/source/fuzz/transformation_permute_phi_operands.cpp
@@ -22,8 +22,8 @@
 namespace fuzz {
 
 TransformationPermutePhiOperands::TransformationPermutePhiOperands(
-    const spvtools::fuzz::protobufs::TransformationPermutePhiOperands& message)
-    : message_(message) {}
+    protobufs::TransformationPermutePhiOperands message)
+    : message_(std::move(message)) {}
 
 TransformationPermutePhiOperands::TransformationPermutePhiOperands(
     uint32_t result_id, const std::vector<uint32_t>& permutation) {
@@ -80,9 +80,8 @@
 
   inst->SetInOperands(std::move(permuted_operands));
 
-  // Make sure our changes are analyzed
-  ir_context->InvalidateAnalysesExceptFor(
-      opt::IRContext::Analysis::kAnalysisNone);
+  // Update the def-use manager.
+  ir_context->UpdateDefUse(inst);
 }
 
 protobufs::Transformation TransformationPermutePhiOperands::ToMessage() const {
diff --git a/source/fuzz/transformation_permute_phi_operands.h b/source/fuzz/transformation_permute_phi_operands.h
index 8198b70..1642711 100644
--- a/source/fuzz/transformation_permute_phi_operands.h
+++ b/source/fuzz/transformation_permute_phi_operands.h
@@ -26,7 +26,7 @@
 class TransformationPermutePhiOperands : public Transformation {
  public:
   explicit TransformationPermutePhiOperands(
-      const protobufs::TransformationPermutePhiOperands& message);
+      protobufs::TransformationPermutePhiOperands message);
 
   TransformationPermutePhiOperands(uint32_t result_id,
                                    const std::vector<uint32_t>& permutation);
diff --git a/source/fuzz/transformation_propagate_instruction_down.cpp b/source/fuzz/transformation_propagate_instruction_down.cpp
index ba22e39..7713562 100644
--- a/source/fuzz/transformation_propagate_instruction_down.cpp
+++ b/source/fuzz/transformation_propagate_instruction_down.cpp
@@ -21,8 +21,8 @@
 namespace fuzz {
 
 TransformationPropagateInstructionDown::TransformationPropagateInstructionDown(
-    const protobufs::TransformationPropagateInstructionDown& message)
-    : message_(message) {}
+    protobufs::TransformationPropagateInstructionDown message)
+    : message_(std::move(message)) {}
 
 TransformationPropagateInstructionDown::TransformationPropagateInstructionDown(
     uint32_t block_id, uint32_t phi_fresh_id,
diff --git a/source/fuzz/transformation_propagate_instruction_down.h b/source/fuzz/transformation_propagate_instruction_down.h
index 7eca1ad..560d7dc 100644
--- a/source/fuzz/transformation_propagate_instruction_down.h
+++ b/source/fuzz/transformation_propagate_instruction_down.h
@@ -28,7 +28,7 @@
 class TransformationPropagateInstructionDown : public Transformation {
  public:
   explicit TransformationPropagateInstructionDown(
-      const protobufs::TransformationPropagateInstructionDown& message);
+      protobufs::TransformationPropagateInstructionDown message);
 
   TransformationPropagateInstructionDown(
       uint32_t block_id, uint32_t phi_fresh_id,
diff --git a/source/fuzz/transformation_propagate_instruction_up.cpp b/source/fuzz/transformation_propagate_instruction_up.cpp
index a2cacf4..bf0e663 100644
--- a/source/fuzz/transformation_propagate_instruction_up.cpp
+++ b/source/fuzz/transformation_propagate_instruction_up.cpp
@@ -79,8 +79,8 @@
 }  // namespace
 
 TransformationPropagateInstructionUp::TransformationPropagateInstructionUp(
-    const protobufs::TransformationPropagateInstructionUp& message)
-    : message_(message) {}
+    protobufs::TransformationPropagateInstructionUp message)
+    : message_(std::move(message)) {}
 
 TransformationPropagateInstructionUp::TransformationPropagateInstructionUp(
     uint32_t block_id,
diff --git a/source/fuzz/transformation_propagate_instruction_up.h b/source/fuzz/transformation_propagate_instruction_up.h
index 6354094..0ca051b 100644
--- a/source/fuzz/transformation_propagate_instruction_up.h
+++ b/source/fuzz/transformation_propagate_instruction_up.h
@@ -28,7 +28,7 @@
 class TransformationPropagateInstructionUp : public Transformation {
  public:
   explicit TransformationPropagateInstructionUp(
-      const protobufs::TransformationPropagateInstructionUp& message);
+      protobufs::TransformationPropagateInstructionUp message);
 
   TransformationPropagateInstructionUp(
       uint32_t block_id,
diff --git a/source/fuzz/transformation_push_id_through_variable.cpp b/source/fuzz/transformation_push_id_through_variable.cpp
index cdc40aa..0df1da6 100644
--- a/source/fuzz/transformation_push_id_through_variable.cpp
+++ b/source/fuzz/transformation_push_id_through_variable.cpp
@@ -21,9 +21,8 @@
 namespace fuzz {
 
 TransformationPushIdThroughVariable::TransformationPushIdThroughVariable(
-    const spvtools::fuzz::protobufs::TransformationPushIdThroughVariable&
-        message)
-    : message_(message) {}
+    protobufs::TransformationPushIdThroughVariable message)
+    : message_(std::move(message)) {}
 
 TransformationPushIdThroughVariable::TransformationPushIdThroughVariable(
     uint32_t value_id, uint32_t value_synonym_id, uint32_t variable_id,
@@ -105,6 +104,10 @@
   auto value_instruction =
       ir_context->get_def_use_mgr()->GetDef(message_.value_id());
 
+  opt::Instruction* insert_before =
+      FindInstruction(message_.instruction_descriptor(), ir_context);
+  opt::BasicBlock* enclosing_block = ir_context->get_instr_block(insert_before);
+
   // A pointer type instruction pointing to the value type must be defined.
   auto pointer_type_id = fuzzerutil::MaybeGetPointerType(
       ir_context, value_instruction->type_id(),
@@ -113,36 +116,42 @@
 
   // Adds whether a global or local variable.
   if (message_.variable_storage_class() == SpvStorageClassPrivate) {
-    fuzzerutil::AddGlobalVariable(ir_context, message_.variable_id(),
-                                  pointer_type_id, SpvStorageClassPrivate,
-                                  message_.initializer_id());
+    opt::Instruction* global_variable = fuzzerutil::AddGlobalVariable(
+        ir_context, message_.variable_id(), pointer_type_id,
+        SpvStorageClassPrivate, message_.initializer_id());
+    ir_context->get_def_use_mgr()->AnalyzeInstDefUse(global_variable);
   } else {
-    auto function_id = ir_context
-                           ->get_instr_block(FindInstruction(
-                               message_.instruction_descriptor(), ir_context))
-                           ->GetParent()
-                           ->result_id();
-    fuzzerutil::AddLocalVariable(ir_context, message_.variable_id(),
-                                 pointer_type_id, function_id,
-                                 message_.initializer_id());
+    opt::Function* function =
+        ir_context
+            ->get_instr_block(
+                FindInstruction(message_.instruction_descriptor(), ir_context))
+            ->GetParent();
+    opt::Instruction* local_variable = fuzzerutil::AddLocalVariable(
+        ir_context, message_.variable_id(), pointer_type_id,
+        function->result_id(), message_.initializer_id());
+    ir_context->get_def_use_mgr()->AnalyzeInstDefUse(local_variable);
+    ir_context->set_instr_block(local_variable, &*function->entry());
   }
 
   // First, insert the OpLoad instruction before |instruction_descriptor| and
   // then insert the OpStore instruction before the OpLoad instruction.
   fuzzerutil::UpdateModuleIdBound(ir_context, message_.value_synonym_id());
-  FindInstruction(message_.instruction_descriptor(), ir_context)
-      ->InsertBefore(MakeUnique<opt::Instruction>(
+  opt::Instruction* load_instruction =
+      insert_before->InsertBefore(MakeUnique<opt::Instruction>(
           ir_context, SpvOpLoad, value_instruction->type_id(),
           message_.value_synonym_id(),
           opt::Instruction::OperandList(
-              {{SPV_OPERAND_TYPE_ID, {message_.variable_id()}}})))
-      ->InsertBefore(MakeUnique<opt::Instruction>(
+              {{SPV_OPERAND_TYPE_ID, {message_.variable_id()}}})));
+  opt::Instruction* store_instruction =
+      load_instruction->InsertBefore(MakeUnique<opt::Instruction>(
           ir_context, SpvOpStore, 0, 0,
           opt::Instruction::OperandList(
               {{SPV_OPERAND_TYPE_ID, {message_.variable_id()}},
                {SPV_OPERAND_TYPE_ID, {message_.value_id()}}})));
-
-  ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+  ir_context->get_def_use_mgr()->AnalyzeInstDefUse(store_instruction);
+  ir_context->set_instr_block(store_instruction, enclosing_block);
+  ir_context->get_def_use_mgr()->AnalyzeInstDefUse(load_instruction);
+  ir_context->set_instr_block(load_instruction, enclosing_block);
 
   // We should be able to create a synonym of |value_id| if it's not irrelevant.
   if (fuzzerutil::CanMakeSynonymOf(ir_context, *transformation_context,
diff --git a/source/fuzz/transformation_push_id_through_variable.h b/source/fuzz/transformation_push_id_through_variable.h
index d055825..ec6943c 100644
--- a/source/fuzz/transformation_push_id_through_variable.h
+++ b/source/fuzz/transformation_push_id_through_variable.h
@@ -26,7 +26,7 @@
 class TransformationPushIdThroughVariable : public Transformation {
  public:
   explicit TransformationPushIdThroughVariable(
-      const protobufs::TransformationPushIdThroughVariable& message);
+      protobufs::TransformationPushIdThroughVariable message);
 
   TransformationPushIdThroughVariable(
       uint32_t value_id, uint32_t value_synonym_fresh_id,
diff --git a/source/fuzz/transformation_record_synonymous_constants.cpp b/source/fuzz/transformation_record_synonymous_constants.cpp
index 30ea94b..3278d7d 100644
--- a/source/fuzz/transformation_record_synonymous_constants.cpp
+++ b/source/fuzz/transformation_record_synonymous_constants.cpp
@@ -22,8 +22,8 @@
 
 TransformationRecordSynonymousConstants::
     TransformationRecordSynonymousConstants(
-        const protobufs::TransformationRecordSynonymousConstants& message)
-    : message_(message) {}
+        protobufs::TransformationRecordSynonymousConstants message)
+    : message_(std::move(message)) {}
 
 TransformationRecordSynonymousConstants::
     TransformationRecordSynonymousConstants(uint32_t constant1_id,
diff --git a/source/fuzz/transformation_record_synonymous_constants.h b/source/fuzz/transformation_record_synonymous_constants.h
index 4376c87..d99b0e2 100644
--- a/source/fuzz/transformation_record_synonymous_constants.h
+++ b/source/fuzz/transformation_record_synonymous_constants.h
@@ -24,7 +24,7 @@
 class TransformationRecordSynonymousConstants : public Transformation {
  public:
   explicit TransformationRecordSynonymousConstants(
-      const protobufs::TransformationRecordSynonymousConstants& message);
+      protobufs::TransformationRecordSynonymousConstants message);
 
   TransformationRecordSynonymousConstants(uint32_t constant1_id,
                                           uint32_t constant2_id);
diff --git a/source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.cpp b/source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.cpp
index a257515..e1977a6 100644
--- a/source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.cpp
+++ b/source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.cpp
@@ -27,9 +27,8 @@
 
 TransformationReplaceAddSubMulWithCarryingExtended::
     TransformationReplaceAddSubMulWithCarryingExtended(
-        const spvtools::fuzz::protobufs::
-            TransformationReplaceAddSubMulWithCarryingExtended& message)
-    : message_(message) {}
+        protobufs::TransformationReplaceAddSubMulWithCarryingExtended message)
+    : message_(std::move(message)) {}
 
 TransformationReplaceAddSubMulWithCarryingExtended::
     TransformationReplaceAddSubMulWithCarryingExtended(uint32_t struct_fresh_id,
diff --git a/source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.h b/source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.h
index 243542c..9deb280 100644
--- a/source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.h
+++ b/source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.h
@@ -27,8 +27,7 @@
     : public Transformation {
  public:
   explicit TransformationReplaceAddSubMulWithCarryingExtended(
-      const protobufs::TransformationReplaceAddSubMulWithCarryingExtended&
-          message);
+      protobufs::TransformationReplaceAddSubMulWithCarryingExtended message);
 
   explicit TransformationReplaceAddSubMulWithCarryingExtended(
       uint32_t struct_fresh_id, uint32_t result_id);
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 b458b56..2429351 100644
--- a/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.cpp
+++ b/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.cpp
@@ -111,9 +111,9 @@
 
 TransformationReplaceBooleanConstantWithConstantBinary::
     TransformationReplaceBooleanConstantWithConstantBinary(
-        const spvtools::fuzz::protobufs::
-            TransformationReplaceBooleanConstantWithConstantBinary& message)
-    : message_(message) {}
+        protobufs::TransformationReplaceBooleanConstantWithConstantBinary
+            message)
+    : message_(std::move(message)) {}
 
 TransformationReplaceBooleanConstantWithConstantBinary::
     TransformationReplaceBooleanConstantWithConstantBinary(
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 a0ece7f..97c66bf 100644
--- a/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h
+++ b/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h
@@ -27,7 +27,7 @@
     : public Transformation {
  public:
   explicit TransformationReplaceBooleanConstantWithConstantBinary(
-      const protobufs::TransformationReplaceBooleanConstantWithConstantBinary&
+      protobufs::TransformationReplaceBooleanConstantWithConstantBinary
           message);
 
   TransformationReplaceBooleanConstantWithConstantBinary(
diff --git a/source/fuzz/transformation_replace_branch_from_dead_block_with_exit.cpp b/source/fuzz/transformation_replace_branch_from_dead_block_with_exit.cpp
index e809012..9ea7cb6 100644
--- a/source/fuzz/transformation_replace_branch_from_dead_block_with_exit.cpp
+++ b/source/fuzz/transformation_replace_branch_from_dead_block_with_exit.cpp
@@ -21,9 +21,8 @@
 
 TransformationReplaceBranchFromDeadBlockWithExit::
     TransformationReplaceBranchFromDeadBlockWithExit(
-        const spvtools::fuzz::protobufs::
-            TransformationReplaceBranchFromDeadBlockWithExit& message)
-    : message_(message) {}
+        protobufs::TransformationReplaceBranchFromDeadBlockWithExit message)
+    : message_(std::move(message)) {}
 
 TransformationReplaceBranchFromDeadBlockWithExit::
     TransformationReplaceBranchFromDeadBlockWithExit(uint32_t block_id,
@@ -162,7 +161,10 @@
   if (ir_context->cfg()->preds(successor->id()).size() < 2) {
     return false;
   }
-  return true;
+  // Make sure that domination rules are satisfied when we remove the branch
+  // from the |block| to its |successor|.
+  return fuzzerutil::NewTerminatorPreservesDominationRules(
+      ir_context, block.id(), {ir_context, SpvOpUnreachable});
 }
 
 }  // namespace fuzz
diff --git a/source/fuzz/transformation_replace_branch_from_dead_block_with_exit.h b/source/fuzz/transformation_replace_branch_from_dead_block_with_exit.h
index e1418c9..89667fc 100644
--- a/source/fuzz/transformation_replace_branch_from_dead_block_with_exit.h
+++ b/source/fuzz/transformation_replace_branch_from_dead_block_with_exit.h
@@ -27,8 +27,7 @@
 class TransformationReplaceBranchFromDeadBlockWithExit : public Transformation {
  public:
   explicit TransformationReplaceBranchFromDeadBlockWithExit(
-      const protobufs::TransformationReplaceBranchFromDeadBlockWithExit&
-          message);
+      protobufs::TransformationReplaceBranchFromDeadBlockWithExit message);
 
   TransformationReplaceBranchFromDeadBlockWithExit(uint32_t block_id,
                                                    SpvOp opcode,
@@ -41,13 +40,17 @@
   //   predecessor
   // - |message_.opcode()| must be one of OpKill, OpReturn, OpReturnValue and
   //   OpUnreachable
-  // - |message_.opcode()| can only be OpKill the module's entry points all
+  // - |message_.opcode()| can only be OpKill if the module's entry points all
   //   have Fragment execution mode
   // - |message_.opcode()| can only be OpReturn if the return type of the
   //   function containing the block is void
   // - If |message_.opcode()| is OpReturnValue then |message_.return_value_id|
   //   must be an id that is available at the block terminator and that matches
   //   the return type of the enclosing function
+  // - Domination rules should be preserved when we apply this transformation.
+  //   In particular, if some block appears after the |block_id|'s successor in
+  //   the CFG, then that block cannot dominate |block_id|'s successor when this
+  //   transformation is applied.
   bool IsApplicable(
       opt::IRContext* ir_context,
       const TransformationContext& transformation_context) const override;
diff --git a/source/fuzz/transformation_replace_constant_with_uniform.cpp b/source/fuzz/transformation_replace_constant_with_uniform.cpp
index 95932bf..c6698c0 100644
--- a/source/fuzz/transformation_replace_constant_with_uniform.cpp
+++ b/source/fuzz/transformation_replace_constant_with_uniform.cpp
@@ -22,9 +22,8 @@
 
 TransformationReplaceConstantWithUniform::
     TransformationReplaceConstantWithUniform(
-        const spvtools::fuzz::protobufs::
-            TransformationReplaceConstantWithUniform& message)
-    : message_(message) {}
+        protobufs::TransformationReplaceConstantWithUniform message)
+    : message_(std::move(message)) {}
 
 TransformationReplaceConstantWithUniform::
     TransformationReplaceConstantWithUniform(
@@ -254,28 +253,39 @@
   auto* insert_before_inst = GetInsertBeforeInstruction(ir_context);
   assert(insert_before_inst &&
          "There must exist an insertion point for OpAccessChain and OpLoad");
+  opt::BasicBlock* enclosing_block =
+      ir_context->get_instr_block(insert_before_inst);
 
   // Add an access chain instruction to target the uniform element.
-  insert_before_inst->InsertBefore(
-      MakeAccessChainInstruction(ir_context, constant_type_id));
+  auto access_chain_instruction =
+      MakeAccessChainInstruction(ir_context, constant_type_id);
+  auto access_chain_instruction_ptr = access_chain_instruction.get();
+  insert_before_inst->InsertBefore(std::move(access_chain_instruction));
+  ir_context->get_def_use_mgr()->AnalyzeInstDefUse(
+      access_chain_instruction_ptr);
+  ir_context->set_instr_block(access_chain_instruction_ptr, enclosing_block);
 
   // Add a load from this access chain.
-  insert_before_inst->InsertBefore(
-      MakeLoadInstruction(ir_context, constant_type_id));
+  auto load_instruction = MakeLoadInstruction(ir_context, constant_type_id);
+  auto load_instruction_ptr = load_instruction.get();
+  insert_before_inst->InsertBefore(std::move(load_instruction));
+  ir_context->get_def_use_mgr()->AnalyzeInstDefUse(load_instruction_ptr);
+  ir_context->set_instr_block(load_instruction_ptr, enclosing_block);
 
   // Adjust the instruction containing the usage of the constant so that this
   // usage refers instead to the result of the load.
   instruction_containing_constant_use->SetInOperand(
       message_.id_use_descriptor().in_operand_index(),
       {message_.fresh_id_for_load()});
+  ir_context->get_def_use_mgr()->EraseUseRecordsOfOperandIds(
+      instruction_containing_constant_use);
+  ir_context->get_def_use_mgr()->AnalyzeInstUse(
+      instruction_containing_constant_use);
 
   // Update the module id bound to reflect the new instructions.
   fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id_for_load());
   fuzzerutil::UpdateModuleIdBound(ir_context,
                                   message_.fresh_id_for_access_chain());
-
-  ir_context->InvalidateAnalysesExceptFor(
-      opt::IRContext::Analysis::kAnalysisNone);
 }
 
 protobufs::Transformation TransformationReplaceConstantWithUniform::ToMessage()
diff --git a/source/fuzz/transformation_replace_constant_with_uniform.h b/source/fuzz/transformation_replace_constant_with_uniform.h
index 9e09748..2121092 100644
--- a/source/fuzz/transformation_replace_constant_with_uniform.h
+++ b/source/fuzz/transformation_replace_constant_with_uniform.h
@@ -28,7 +28,7 @@
 class TransformationReplaceConstantWithUniform : public Transformation {
  public:
   explicit TransformationReplaceConstantWithUniform(
-      const protobufs::TransformationReplaceConstantWithUniform& message);
+      protobufs::TransformationReplaceConstantWithUniform message);
 
   TransformationReplaceConstantWithUniform(
       protobufs::IdUseDescriptor id_use,
diff --git a/source/fuzz/transformation_replace_copy_memory_with_load_store.cpp b/source/fuzz/transformation_replace_copy_memory_with_load_store.cpp
index 936b054..de9d1fd 100644
--- a/source/fuzz/transformation_replace_copy_memory_with_load_store.cpp
+++ b/source/fuzz/transformation_replace_copy_memory_with_load_store.cpp
@@ -22,9 +22,8 @@
 
 TransformationReplaceCopyMemoryWithLoadStore::
     TransformationReplaceCopyMemoryWithLoadStore(
-        const spvtools::fuzz::protobufs::
-            TransformationReplaceCopyMemoryWithLoadStore& message)
-    : message_(message) {}
+        protobufs::TransformationReplaceCopyMemoryWithLoadStore message)
+    : message_(std::move(message)) {}
 
 TransformationReplaceCopyMemoryWithLoadStore::
     TransformationReplaceCopyMemoryWithLoadStore(
diff --git a/source/fuzz/transformation_replace_copy_memory_with_load_store.h b/source/fuzz/transformation_replace_copy_memory_with_load_store.h
index 67d349f..55d1e06 100644
--- a/source/fuzz/transformation_replace_copy_memory_with_load_store.h
+++ b/source/fuzz/transformation_replace_copy_memory_with_load_store.h
@@ -26,7 +26,7 @@
 class TransformationReplaceCopyMemoryWithLoadStore : public Transformation {
  public:
   explicit TransformationReplaceCopyMemoryWithLoadStore(
-      const protobufs::TransformationReplaceCopyMemoryWithLoadStore& message);
+      protobufs::TransformationReplaceCopyMemoryWithLoadStore message);
 
   TransformationReplaceCopyMemoryWithLoadStore(
       uint32_t fresh_id, const protobufs::InstructionDescriptor&
diff --git a/source/fuzz/transformation_replace_copy_object_with_store_load.cpp b/source/fuzz/transformation_replace_copy_object_with_store_load.cpp
index 54c99d5..e0643bf 100644
--- a/source/fuzz/transformation_replace_copy_object_with_store_load.cpp
+++ b/source/fuzz/transformation_replace_copy_object_with_store_load.cpp
@@ -22,9 +22,8 @@
 
 TransformationReplaceCopyObjectWithStoreLoad::
     TransformationReplaceCopyObjectWithStoreLoad(
-        const spvtools::fuzz::protobufs::
-            TransformationReplaceCopyObjectWithStoreLoad& message)
-    : message_(message) {}
+        protobufs::TransformationReplaceCopyObjectWithStoreLoad message)
+    : message_(std::move(message)) {}
 
 TransformationReplaceCopyObjectWithStoreLoad::
     TransformationReplaceCopyObjectWithStoreLoad(
@@ -88,6 +87,10 @@
   assert(copy_object_instruction &&
          copy_object_instruction->opcode() == SpvOpCopyObject &&
          "The required OpCopyObject instruction must be defined.");
+
+  opt::BasicBlock* enclosing_block =
+      ir_context->get_instr_block(copy_object_instruction);
+
   // Get id used as a source by the OpCopyObject instruction.
   uint32_t src_operand = copy_object_instruction->GetSingleWordInOperand(0);
   // A pointer type instruction pointing to the value type must be defined.
@@ -98,37 +101,46 @@
 
   // Adds a global or local variable (according to the storage class).
   if (message_.variable_storage_class() == SpvStorageClassPrivate) {
-    fuzzerutil::AddGlobalVariable(ir_context, message_.fresh_variable_id(),
-                                  pointer_type_id, SpvStorageClassPrivate,
-                                  message_.variable_initializer_id());
+    opt::Instruction* new_global = fuzzerutil::AddGlobalVariable(
+        ir_context, message_.fresh_variable_id(), pointer_type_id,
+        SpvStorageClassPrivate, message_.variable_initializer_id());
+    ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_global);
   } else {
-    auto function_id = ir_context->get_instr_block(copy_object_instruction)
-                           ->GetParent()
-                           ->result_id();
-    fuzzerutil::AddLocalVariable(ir_context, message_.fresh_variable_id(),
-                                 pointer_type_id, function_id,
-                                 message_.variable_initializer_id());
+    opt::Function* function =
+        ir_context->get_instr_block(copy_object_instruction)->GetParent();
+    opt::Instruction* new_local = fuzzerutil::AddLocalVariable(
+        ir_context, message_.fresh_variable_id(), pointer_type_id,
+        function->result_id(), message_.variable_initializer_id());
+    ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_local);
+    ir_context->set_instr_block(new_local, &*function->begin());
   }
 
   // First, insert the OpLoad instruction before the OpCopyObject instruction
   // and then insert the OpStore instruction before the OpLoad instruction.
   fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_variable_id());
-  copy_object_instruction
-      ->InsertBefore(MakeUnique<opt::Instruction>(
+  opt::Instruction* load_instruction =
+      copy_object_instruction->InsertBefore(MakeUnique<opt::Instruction>(
           ir_context, SpvOpLoad, copy_object_instruction->type_id(),
           message_.copy_object_result_id(),
           opt::Instruction::OperandList(
-              {{SPV_OPERAND_TYPE_ID, {message_.fresh_variable_id()}}})))
-      ->InsertBefore(MakeUnique<opt::Instruction>(
+              {{SPV_OPERAND_TYPE_ID, {message_.fresh_variable_id()}}})));
+  opt::Instruction* store_instruction =
+      load_instruction->InsertBefore(MakeUnique<opt::Instruction>(
           ir_context, SpvOpStore, 0, 0,
           opt::Instruction::OperandList(
               {{SPV_OPERAND_TYPE_ID, {message_.fresh_variable_id()}},
                {SPV_OPERAND_TYPE_ID, {src_operand}}})));
+
+  // Register the new instructions with the def-use manager, and record their
+  // enclosing block.
+  ir_context->get_def_use_mgr()->AnalyzeInstDefUse(store_instruction);
+  ir_context->get_def_use_mgr()->AnalyzeInstDefUse(load_instruction);
+  ir_context->set_instr_block(store_instruction, enclosing_block);
+  ir_context->set_instr_block(load_instruction, enclosing_block);
+
   // Remove the CopyObject instruction.
   ir_context->KillInst(copy_object_instruction);
 
-  ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
-
   if (!transformation_context->GetFactManager()->IdIsIrrelevant(
           message_.copy_object_result_id()) &&
       !transformation_context->GetFactManager()->IdIsIrrelevant(src_operand)) {
diff --git a/source/fuzz/transformation_replace_copy_object_with_store_load.h b/source/fuzz/transformation_replace_copy_object_with_store_load.h
index a90905c..8c5ce9e 100644
--- a/source/fuzz/transformation_replace_copy_object_with_store_load.h
+++ b/source/fuzz/transformation_replace_copy_object_with_store_load.h
@@ -26,7 +26,7 @@
 class TransformationReplaceCopyObjectWithStoreLoad : public Transformation {
  public:
   explicit TransformationReplaceCopyObjectWithStoreLoad(
-      const protobufs::TransformationReplaceCopyObjectWithStoreLoad& message);
+      protobufs::TransformationReplaceCopyObjectWithStoreLoad message);
 
   TransformationReplaceCopyObjectWithStoreLoad(
       uint32_t copy_object_result_id, uint32_t fresh_variable_id,
diff --git a/source/fuzz/transformation_replace_id_with_synonym.cpp b/source/fuzz/transformation_replace_id_with_synonym.cpp
index 24e079f..92ce751 100644
--- a/source/fuzz/transformation_replace_id_with_synonym.cpp
+++ b/source/fuzz/transformation_replace_id_with_synonym.cpp
@@ -26,9 +26,8 @@
 namespace fuzz {
 
 TransformationReplaceIdWithSynonym::TransformationReplaceIdWithSynonym(
-    const spvtools::fuzz::protobufs::TransformationReplaceIdWithSynonym&
-        message)
-    : message_(message) {}
+    protobufs::TransformationReplaceIdWithSynonym message)
+    : message_(std::move(message)) {}
 
 TransformationReplaceIdWithSynonym::TransformationReplaceIdWithSynonym(
     protobufs::IdUseDescriptor id_use_descriptor, uint32_t synonymous_id) {
@@ -95,8 +94,12 @@
   instruction_to_change->SetInOperand(
       message_.id_use_descriptor().in_operand_index(),
       {message_.synonymous_id()});
-  ir_context->InvalidateAnalysesExceptFor(
-      opt::IRContext::Analysis::kAnalysisNone);
+  ir_context->get_def_use_mgr()->EraseUseRecordsOfOperandIds(
+      instruction_to_change);
+  ir_context->get_def_use_mgr()->AnalyzeInstUse(instruction_to_change);
+
+  // No analyses need to be invalidated, since the transformation is local to a
+  // block, and the def-use analysis has been updated.
 }
 
 protobufs::Transformation TransformationReplaceIdWithSynonym::ToMessage()
diff --git a/source/fuzz/transformation_replace_id_with_synonym.h b/source/fuzz/transformation_replace_id_with_synonym.h
index 3101710..1ac636b 100644
--- a/source/fuzz/transformation_replace_id_with_synonym.h
+++ b/source/fuzz/transformation_replace_id_with_synonym.h
@@ -26,7 +26,7 @@
 class TransformationReplaceIdWithSynonym : public Transformation {
  public:
   explicit TransformationReplaceIdWithSynonym(
-      const protobufs::TransformationReplaceIdWithSynonym& message);
+      protobufs::TransformationReplaceIdWithSynonym message);
 
   TransformationReplaceIdWithSynonym(
       protobufs::IdUseDescriptor id_use_descriptor, uint32_t synonymous_id);
diff --git a/source/fuzz/transformation_replace_irrelevant_id.cpp b/source/fuzz/transformation_replace_irrelevant_id.cpp
index 27f56eb..a71f96a 100644
--- a/source/fuzz/transformation_replace_irrelevant_id.cpp
+++ b/source/fuzz/transformation_replace_irrelevant_id.cpp
@@ -21,8 +21,8 @@
 namespace fuzz {
 
 TransformationReplaceIrrelevantId::TransformationReplaceIrrelevantId(
-    const protobufs::TransformationReplaceIrrelevantId& message)
-    : message_(message) {}
+    protobufs::TransformationReplaceIrrelevantId message)
+    : message_(std::move(message)) {}
 
 TransformationReplaceIrrelevantId::TransformationReplaceIrrelevantId(
     const protobufs::IdUseDescriptor& id_use_descriptor,
@@ -107,9 +107,12 @@
       message_.id_use_descriptor().in_operand_index(),
       {message_.replacement_id()});
 
-  // Invalidate the analyses, since the usage of ids has been changed.
-  ir_context->InvalidateAnalysesExceptFor(
-      opt::IRContext::Analysis::kAnalysisNone);
+  ir_context->get_def_use_mgr()->EraseUseRecordsOfOperandIds(
+      instruction_to_change);
+  ir_context->get_def_use_mgr()->AnalyzeInstUse(instruction_to_change);
+
+  // No analyses need to be invalidated, since the transformation is local to a
+  // block, and the def-use analysis has been updated.
 }
 
 protobufs::Transformation TransformationReplaceIrrelevantId::ToMessage() const {
diff --git a/source/fuzz/transformation_replace_irrelevant_id.h b/source/fuzz/transformation_replace_irrelevant_id.h
index 35b1987..e6210b4 100644
--- a/source/fuzz/transformation_replace_irrelevant_id.h
+++ b/source/fuzz/transformation_replace_irrelevant_id.h
@@ -23,7 +23,7 @@
 class TransformationReplaceIrrelevantId : public Transformation {
  public:
   explicit TransformationReplaceIrrelevantId(
-      const protobufs::TransformationReplaceIrrelevantId& message);
+      protobufs::TransformationReplaceIrrelevantId message);
 
   TransformationReplaceIrrelevantId(
       const protobufs::IdUseDescriptor& id_use_descriptor,
diff --git a/source/fuzz/transformation_replace_linear_algebra_instruction.cpp b/source/fuzz/transformation_replace_linear_algebra_instruction.cpp
index fc73a26..2430cca 100644
--- a/source/fuzz/transformation_replace_linear_algebra_instruction.cpp
+++ b/source/fuzz/transformation_replace_linear_algebra_instruction.cpp
@@ -22,9 +22,8 @@
 
 TransformationReplaceLinearAlgebraInstruction::
     TransformationReplaceLinearAlgebraInstruction(
-        const spvtools::fuzz::protobufs::
-            TransformationReplaceLinearAlgebraInstruction& message)
-    : message_(message) {}
+        protobufs::TransformationReplaceLinearAlgebraInstruction message)
+    : message_(std::move(message)) {}
 
 TransformationReplaceLinearAlgebraInstruction::
     TransformationReplaceLinearAlgebraInstruction(
diff --git a/source/fuzz/transformation_replace_linear_algebra_instruction.h b/source/fuzz/transformation_replace_linear_algebra_instruction.h
index 45f4aa6..0f0c18b 100644
--- a/source/fuzz/transformation_replace_linear_algebra_instruction.h
+++ b/source/fuzz/transformation_replace_linear_algebra_instruction.h
@@ -26,7 +26,7 @@
 class TransformationReplaceLinearAlgebraInstruction : public Transformation {
  public:
   explicit TransformationReplaceLinearAlgebraInstruction(
-      const protobufs::TransformationReplaceLinearAlgebraInstruction& message);
+      protobufs::TransformationReplaceLinearAlgebraInstruction message);
 
   TransformationReplaceLinearAlgebraInstruction(
       const std::vector<uint32_t>& fresh_ids,
diff --git a/source/fuzz/transformation_replace_load_store_with_copy_memory.cpp b/source/fuzz/transformation_replace_load_store_with_copy_memory.cpp
index 6067fca..e75337f 100644
--- a/source/fuzz/transformation_replace_load_store_with_copy_memory.cpp
+++ b/source/fuzz/transformation_replace_load_store_with_copy_memory.cpp
@@ -29,9 +29,8 @@
 
 TransformationReplaceLoadStoreWithCopyMemory::
     TransformationReplaceLoadStoreWithCopyMemory(
-        const spvtools::fuzz::protobufs::
-            TransformationReplaceLoadStoreWithCopyMemory& message)
-    : message_(message) {}
+        protobufs::TransformationReplaceLoadStoreWithCopyMemory message)
+    : message_(std::move(message)) {}
 
 TransformationReplaceLoadStoreWithCopyMemory::
     TransformationReplaceLoadStoreWithCopyMemory(
diff --git a/source/fuzz/transformation_replace_load_store_with_copy_memory.h b/source/fuzz/transformation_replace_load_store_with_copy_memory.h
index 4dd728e..bb4d27e 100644
--- a/source/fuzz/transformation_replace_load_store_with_copy_memory.h
+++ b/source/fuzz/transformation_replace_load_store_with_copy_memory.h
@@ -26,7 +26,7 @@
 class TransformationReplaceLoadStoreWithCopyMemory : public Transformation {
  public:
   explicit TransformationReplaceLoadStoreWithCopyMemory(
-      const protobufs::TransformationReplaceLoadStoreWithCopyMemory& message);
+      protobufs::TransformationReplaceLoadStoreWithCopyMemory message);
 
   TransformationReplaceLoadStoreWithCopyMemory(
       const protobufs::InstructionDescriptor& load_instruction_descriptor,
diff --git a/source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.cpp b/source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.cpp
index f13af7e..84ca1ab 100644
--- a/source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.cpp
+++ b/source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.cpp
@@ -21,9 +21,8 @@
 
 TransformationReplaceOpPhiIdFromDeadPredecessor::
     TransformationReplaceOpPhiIdFromDeadPredecessor(
-        const protobufs::TransformationReplaceOpPhiIdFromDeadPredecessor&
-            message)
-    : message_(message) {}
+        protobufs::TransformationReplaceOpPhiIdFromDeadPredecessor message)
+    : message_(std::move(message)) {}
 
 TransformationReplaceOpPhiIdFromDeadPredecessor::
     TransformationReplaceOpPhiIdFromDeadPredecessor(uint32_t opphi_id,
diff --git a/source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.h b/source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.h
index d26b6b0..87ce8ad 100644
--- a/source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.h
+++ b/source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.h
@@ -23,8 +23,7 @@
 class TransformationReplaceOpPhiIdFromDeadPredecessor : public Transformation {
  public:
   explicit TransformationReplaceOpPhiIdFromDeadPredecessor(
-      const protobufs::TransformationReplaceOpPhiIdFromDeadPredecessor&
-          message);
+      protobufs::TransformationReplaceOpPhiIdFromDeadPredecessor message);
 
   TransformationReplaceOpPhiIdFromDeadPredecessor(uint32_t opphi_id,
                                                   uint32_t pred_label_id,
diff --git a/source/fuzz/transformation_replace_opselect_with_conditional_branch.cpp b/source/fuzz/transformation_replace_opselect_with_conditional_branch.cpp
index 7160d4d..c0e6e44 100644
--- a/source/fuzz/transformation_replace_opselect_with_conditional_branch.cpp
+++ b/source/fuzz/transformation_replace_opselect_with_conditional_branch.cpp
@@ -20,9 +20,8 @@
 namespace fuzz {
 TransformationReplaceOpSelectWithConditionalBranch::
     TransformationReplaceOpSelectWithConditionalBranch(
-        const spvtools::fuzz::protobufs::
-            TransformationReplaceOpSelectWithConditionalBranch& message)
-    : message_(message) {}
+        protobufs::TransformationReplaceOpSelectWithConditionalBranch message)
+    : message_(std::move(message)) {}
 
 TransformationReplaceOpSelectWithConditionalBranch::
     TransformationReplaceOpSelectWithConditionalBranch(
diff --git a/source/fuzz/transformation_replace_opselect_with_conditional_branch.h b/source/fuzz/transformation_replace_opselect_with_conditional_branch.h
index 8ee5c7f..ec926c6 100644
--- a/source/fuzz/transformation_replace_opselect_with_conditional_branch.h
+++ b/source/fuzz/transformation_replace_opselect_with_conditional_branch.h
@@ -24,8 +24,7 @@
     : public Transformation {
  public:
   explicit TransformationReplaceOpSelectWithConditionalBranch(
-      const protobufs::TransformationReplaceOpSelectWithConditionalBranch&
-          message);
+      protobufs::TransformationReplaceOpSelectWithConditionalBranch message);
 
   TransformationReplaceOpSelectWithConditionalBranch(uint32_t select_id,
                                                      uint32_t true_block_id,
diff --git a/source/fuzz/transformation_replace_parameter_with_global.cpp b/source/fuzz/transformation_replace_parameter_with_global.cpp
index cdf7645..caf6716 100644
--- a/source/fuzz/transformation_replace_parameter_with_global.cpp
+++ b/source/fuzz/transformation_replace_parameter_with_global.cpp
@@ -23,8 +23,8 @@
 
 TransformationReplaceParameterWithGlobal::
     TransformationReplaceParameterWithGlobal(
-        const protobufs::TransformationReplaceParameterWithGlobal& message)
-    : message_(message) {}
+        protobufs::TransformationReplaceParameterWithGlobal message)
+    : message_(std::move(message)) {}
 
 TransformationReplaceParameterWithGlobal::
     TransformationReplaceParameterWithGlobal(
diff --git a/source/fuzz/transformation_replace_parameter_with_global.h b/source/fuzz/transformation_replace_parameter_with_global.h
index c2d5f8f..38a9c17 100644
--- a/source/fuzz/transformation_replace_parameter_with_global.h
+++ b/source/fuzz/transformation_replace_parameter_with_global.h
@@ -26,7 +26,7 @@
 class TransformationReplaceParameterWithGlobal : public Transformation {
  public:
   explicit TransformationReplaceParameterWithGlobal(
-      const protobufs::TransformationReplaceParameterWithGlobal& message);
+      protobufs::TransformationReplaceParameterWithGlobal message);
 
   TransformationReplaceParameterWithGlobal(uint32_t function_type_fresh_id,
                                            uint32_t parameter_id,
diff --git a/source/fuzz/transformation_replace_params_with_struct.cpp b/source/fuzz/transformation_replace_params_with_struct.cpp
index 0a135e5..13eeccb 100644
--- a/source/fuzz/transformation_replace_params_with_struct.cpp
+++ b/source/fuzz/transformation_replace_params_with_struct.cpp
@@ -22,8 +22,8 @@
 namespace fuzz {
 
 TransformationReplaceParamsWithStruct::TransformationReplaceParamsWithStruct(
-    const protobufs::TransformationReplaceParamsWithStruct& message)
-    : message_(message) {}
+    protobufs::TransformationReplaceParamsWithStruct message)
+    : message_(std::move(message)) {}
 
 TransformationReplaceParamsWithStruct::TransformationReplaceParamsWithStruct(
     const std::vector<uint32_t>& parameter_id, uint32_t fresh_function_type_id,
diff --git a/source/fuzz/transformation_replace_params_with_struct.h b/source/fuzz/transformation_replace_params_with_struct.h
index afa6b14..705f340 100644
--- a/source/fuzz/transformation_replace_params_with_struct.h
+++ b/source/fuzz/transformation_replace_params_with_struct.h
@@ -28,7 +28,7 @@
 class TransformationReplaceParamsWithStruct : public Transformation {
  public:
   explicit TransformationReplaceParamsWithStruct(
-      const protobufs::TransformationReplaceParamsWithStruct& message);
+      protobufs::TransformationReplaceParamsWithStruct message);
 
   TransformationReplaceParamsWithStruct(
       const std::vector<uint32_t>& parameter_id,
diff --git a/source/fuzz/transformation_set_function_control.cpp b/source/fuzz/transformation_set_function_control.cpp
index 8ab9b8c..02a8c9f 100644
--- a/source/fuzz/transformation_set_function_control.cpp
+++ b/source/fuzz/transformation_set_function_control.cpp
@@ -18,8 +18,8 @@
 namespace fuzz {
 
 TransformationSetFunctionControl::TransformationSetFunctionControl(
-    const spvtools::fuzz::protobufs::TransformationSetFunctionControl& message)
-    : message_(message) {}
+    protobufs::TransformationSetFunctionControl message)
+    : message_(std::move(message)) {}
 
 TransformationSetFunctionControl::TransformationSetFunctionControl(
     uint32_t function_id, uint32_t function_control) {
diff --git a/source/fuzz/transformation_set_function_control.h b/source/fuzz/transformation_set_function_control.h
index 2952cc6..2e16e1c 100644
--- a/source/fuzz/transformation_set_function_control.h
+++ b/source/fuzz/transformation_set_function_control.h
@@ -26,7 +26,7 @@
 class TransformationSetFunctionControl : public Transformation {
  public:
   explicit TransformationSetFunctionControl(
-      const protobufs::TransformationSetFunctionControl& message);
+      protobufs::TransformationSetFunctionControl message);
 
   TransformationSetFunctionControl(uint32_t function_id,
                                    uint32_t function_control);
diff --git a/source/fuzz/transformation_set_loop_control.cpp b/source/fuzz/transformation_set_loop_control.cpp
index b2180d8..1449960 100644
--- a/source/fuzz/transformation_set_loop_control.cpp
+++ b/source/fuzz/transformation_set_loop_control.cpp
@@ -18,8 +18,8 @@
 namespace fuzz {
 
 TransformationSetLoopControl::TransformationSetLoopControl(
-    const spvtools::fuzz::protobufs::TransformationSetLoopControl& message)
-    : message_(message) {}
+    protobufs::TransformationSetLoopControl message)
+    : message_(std::move(message)) {}
 
 TransformationSetLoopControl::TransformationSetLoopControl(
     uint32_t block_id, uint32_t loop_control, uint32_t peel_count,
@@ -77,12 +77,14 @@
     }
   }
 
-  if ((message_.loop_control() &
-       (SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask)) &&
-      !(PeelCountIsSupported(ir_context) &&
-        PartialCountIsSupported(ir_context))) {
-    // At least one of PeelCount or PartialCount is used, but the SPIR-V version
-    // in question does not support these loop controls.
+  // Check that PeelCount and PartialCount are supported if used.
+  if ((message_.loop_control() & SpvLoopControlPeelCountMask) &&
+      !PeelCountIsSupported(ir_context)) {
+    return false;
+  }
+
+  if ((message_.loop_control() & SpvLoopControlPartialCountMask) &&
+      !PartialCountIsSupported(ir_context)) {
     return false;
   }
 
@@ -183,14 +185,16 @@
 
 bool TransformationSetLoopControl::PartialCountIsSupported(
     opt::IRContext* ir_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.
+  // TODO(afd): We capture the environments for which this loop control is
+  //  definitely not supported.  The check should be refined on demand for other
+  //  target environments.
   switch (ir_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:
+    case SPV_ENV_VULKAN_1_0:
+    case SPV_ENV_VULKAN_1_1:
       return false;
     default:
       return true;
@@ -199,14 +203,16 @@
 
 bool TransformationSetLoopControl::PeelCountIsSupported(
     opt::IRContext* ir_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.
+  // TODO(afd): We capture the environments for which this loop control is
+  //  definitely not supported.  The check should be refined on demand for other
+  //  target environments.
   switch (ir_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:
+    case SPV_ENV_VULKAN_1_0:
+    case SPV_ENV_VULKAN_1_1:
       return false;
     default:
       return true;
diff --git a/source/fuzz/transformation_set_loop_control.h b/source/fuzz/transformation_set_loop_control.h
index c3480b1..bc17c8a 100644
--- a/source/fuzz/transformation_set_loop_control.h
+++ b/source/fuzz/transformation_set_loop_control.h
@@ -29,7 +29,7 @@
   const static uint32_t kLoopControlFirstLiteralInOperandIndex = 3;
 
   explicit TransformationSetLoopControl(
-      const protobufs::TransformationSetLoopControl& message);
+      protobufs::TransformationSetLoopControl message);
 
   TransformationSetLoopControl(uint32_t block_id, uint32_t loop_control,
                                uint32_t peel_count, uint32_t partial_count);
diff --git a/source/fuzz/transformation_set_memory_operands_mask.cpp b/source/fuzz/transformation_set_memory_operands_mask.cpp
index deb207a..5a986ad 100644
--- a/source/fuzz/transformation_set_memory_operands_mask.cpp
+++ b/source/fuzz/transformation_set_memory_operands_mask.cpp
@@ -29,9 +29,8 @@
 }  // namespace
 
 TransformationSetMemoryOperandsMask::TransformationSetMemoryOperandsMask(
-    const spvtools::fuzz::protobufs::TransformationSetMemoryOperandsMask&
-        message)
-    : message_(message) {}
+    protobufs::TransformationSetMemoryOperandsMask message)
+    : message_(std::move(message)) {}
 
 TransformationSetMemoryOperandsMask::TransformationSetMemoryOperandsMask(
     const protobufs::InstructionDescriptor& memory_access_instruction,
@@ -53,7 +52,9 @@
                SpvOpCopyMemory ||
            message_.memory_access_instruction().target_instruction_opcode() ==
                SpvOpCopyMemorySized);
-    assert(MultipleMemoryOperandMasksAreSupported(ir_context));
+    assert(MultipleMemoryOperandMasksAreSupported(ir_context) &&
+           "Multiple memory operand masks are not supported for this SPIR-V "
+           "version.");
   }
 
   auto instruction =
@@ -205,14 +206,16 @@
 
 bool TransformationSetMemoryOperandsMask::
     MultipleMemoryOperandMasksAreSupported(opt::IRContext* ir_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.
+  // TODO(afd): We capture the environments for which this loop control is
+  //  definitely not supported.  The check should be refined on demand for other
+  //  target environments.
   switch (ir_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:
+    case SPV_ENV_VULKAN_1_0:
+    case SPV_ENV_VULKAN_1_1:
       return false;
     default:
       return true;
diff --git a/source/fuzz/transformation_set_memory_operands_mask.h b/source/fuzz/transformation_set_memory_operands_mask.h
index 7357b1a..c52fbdb 100644
--- a/source/fuzz/transformation_set_memory_operands_mask.h
+++ b/source/fuzz/transformation_set_memory_operands_mask.h
@@ -26,7 +26,7 @@
 class TransformationSetMemoryOperandsMask : public Transformation {
  public:
   explicit TransformationSetMemoryOperandsMask(
-      const protobufs::TransformationSetMemoryOperandsMask& message);
+      protobufs::TransformationSetMemoryOperandsMask message);
 
   TransformationSetMemoryOperandsMask(
       const protobufs::InstructionDescriptor& memory_access_instruction,
diff --git a/source/fuzz/transformation_set_selection_control.cpp b/source/fuzz/transformation_set_selection_control.cpp
index 625187e..6dddbdf 100644
--- a/source/fuzz/transformation_set_selection_control.cpp
+++ b/source/fuzz/transformation_set_selection_control.cpp
@@ -18,8 +18,8 @@
 namespace fuzz {
 
 TransformationSetSelectionControl::TransformationSetSelectionControl(
-    const spvtools::fuzz::protobufs::TransformationSetSelectionControl& message)
-    : message_(message) {}
+    protobufs::TransformationSetSelectionControl message)
+    : message_(std::move(message)) {}
 
 TransformationSetSelectionControl::TransformationSetSelectionControl(
     uint32_t block_id, uint32_t selection_control) {
diff --git a/source/fuzz/transformation_set_selection_control.h b/source/fuzz/transformation_set_selection_control.h
index 56b5885..93b1904 100644
--- a/source/fuzz/transformation_set_selection_control.h
+++ b/source/fuzz/transformation_set_selection_control.h
@@ -26,7 +26,7 @@
 class TransformationSetSelectionControl : public Transformation {
  public:
   explicit TransformationSetSelectionControl(
-      const protobufs::TransformationSetSelectionControl& message);
+      protobufs::TransformationSetSelectionControl message);
 
   TransformationSetSelectionControl(uint32_t block_id,
                                     uint32_t selection_control);
diff --git a/source/fuzz/transformation_split_block.cpp b/source/fuzz/transformation_split_block.cpp
index b383c40..e15dffa 100644
--- a/source/fuzz/transformation_split_block.cpp
+++ b/source/fuzz/transformation_split_block.cpp
@@ -24,8 +24,8 @@
 namespace fuzz {
 
 TransformationSplitBlock::TransformationSplitBlock(
-    const spvtools::fuzz::protobufs::TransformationSplitBlock& message)
-    : message_(message) {}
+    protobufs::TransformationSplitBlock message)
+    : message_(std::move(message)) {}
 
 TransformationSplitBlock::TransformationSplitBlock(
     const protobufs::InstructionDescriptor& instruction_to_split_before,
@@ -109,24 +109,37 @@
                                                 split_before);
   // The split does not automatically add a branch between the two parts of
   // the original block, so we add one.
-  block_to_split->AddInstruction(MakeUnique<opt::Instruction>(
+  auto branch_instruction = MakeUnique<opt::Instruction>(
       ir_context, SpvOpBranch, 0, 0,
       std::initializer_list<opt::Operand>{opt::Operand(
-          spv_operand_type_t::SPV_OPERAND_TYPE_ID, {message_.fresh_id()})}));
+          spv_operand_type_t::SPV_OPERAND_TYPE_ID, {message_.fresh_id()})});
+  auto branch_instruction_ptr = branch_instruction.get();
+  block_to_split->AddInstruction(std::move(branch_instruction));
+
+  // Inform the def-use manager about the branch instruction, and record its
+  // block.
+  ir_context->get_def_use_mgr()->AnalyzeInstDefUse(branch_instruction_ptr);
+  ir_context->set_instr_block(branch_instruction_ptr, block_to_split);
+
   // 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_to_split](opt::Instruction* phi_inst) {
+  new_bb->ForEachPhiInst([block_to_split,
+                          ir_context](opt::Instruction* phi_inst) {
     assert(
         phi_inst->NumInOperands() == 2 &&
         "Precondition: a block can only be split before an OpPhi if the block"
         "has exactly one predecessor.");
     phi_inst->SetInOperand(1, {block_to_split->id()});
+    ir_context->UpdateDefUse(phi_inst);
   });
 
-  // Invalidate all analyses
+  // We have updated the def-use manager and the instruction to block mapping,
+  // but other analyses (especially control flow-related ones) need to be
+  // recomputed.
   ir_context->InvalidateAnalysesExceptFor(
-      opt::IRContext::Analysis::kAnalysisNone);
+      opt::IRContext::Analysis::kAnalysisDefUse |
+      opt::IRContext::Analysis::kAnalysisInstrToBlockMapping);
 
   // If the block being split was dead, the new block arising from the split is
   // also dead.
diff --git a/source/fuzz/transformation_split_block.h b/source/fuzz/transformation_split_block.h
index 27bf6f8..ace77b5 100644
--- a/source/fuzz/transformation_split_block.h
+++ b/source/fuzz/transformation_split_block.h
@@ -26,7 +26,7 @@
 class TransformationSplitBlock : public Transformation {
  public:
   explicit TransformationSplitBlock(
-      const protobufs::TransformationSplitBlock& message);
+      protobufs::TransformationSplitBlock message);
 
   TransformationSplitBlock(
       const protobufs::InstructionDescriptor& instruction_to_split_before,
diff --git a/source/fuzz/transformation_store.cpp b/source/fuzz/transformation_store.cpp
index 460ca01..f8c6d01 100644
--- a/source/fuzz/transformation_store.cpp
+++ b/source/fuzz/transformation_store.cpp
@@ -20,9 +20,8 @@
 namespace spvtools {
 namespace fuzz {
 
-TransformationStore::TransformationStore(
-    const spvtools::fuzz::protobufs::TransformationStore& message)
-    : message_(message) {}
+TransformationStore::TransformationStore(protobufs::TransformationStore message)
+    : message_(std::move(message)) {}
 
 TransformationStore::TransformationStore(
     uint32_t pointer_id, uint32_t value_id,
@@ -110,13 +109,20 @@
 
 void TransformationStore::Apply(opt::IRContext* ir_context,
                                 TransformationContext* /*unused*/) const {
-  FindInstruction(message_.instruction_to_insert_before(), ir_context)
-      ->InsertBefore(MakeUnique<opt::Instruction>(
-          ir_context, SpvOpStore, 0, 0,
-          opt::Instruction::OperandList(
-              {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}},
-               {SPV_OPERAND_TYPE_ID, {message_.value_id()}}})));
-  ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+  auto insert_before =
+      FindInstruction(message_.instruction_to_insert_before(), ir_context);
+  auto new_instruction = MakeUnique<opt::Instruction>(
+      ir_context, SpvOpStore, 0, 0,
+      opt::Instruction::OperandList(
+          {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}},
+           {SPV_OPERAND_TYPE_ID, {message_.value_id()}}}));
+  auto new_instruction_ptr = new_instruction.get();
+  insert_before->InsertBefore(std::move(new_instruction));
+  // Inform the def-use manager about the new instruction and record its basic
+  // block.
+  ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr);
+  ir_context->set_instr_block(new_instruction_ptr,
+                              ir_context->get_instr_block(insert_before));
 }
 
 protobufs::Transformation TransformationStore::ToMessage() const {
diff --git a/source/fuzz/transformation_store.h b/source/fuzz/transformation_store.h
index 7052048..18ba1d7 100644
--- a/source/fuzz/transformation_store.h
+++ b/source/fuzz/transformation_store.h
@@ -25,7 +25,7 @@
 
 class TransformationStore : public Transformation {
  public:
-  explicit TransformationStore(const protobufs::TransformationStore& message);
+  explicit TransformationStore(protobufs::TransformationStore message);
 
   TransformationStore(
       uint32_t pointer_id, uint32_t value_id,
diff --git a/source/fuzz/transformation_swap_commutable_operands.cpp b/source/fuzz/transformation_swap_commutable_operands.cpp
index b8bdb79..a02e95a 100644
--- a/source/fuzz/transformation_swap_commutable_operands.cpp
+++ b/source/fuzz/transformation_swap_commutable_operands.cpp
@@ -21,9 +21,8 @@
 namespace fuzz {
 
 TransformationSwapCommutableOperands::TransformationSwapCommutableOperands(
-    const spvtools::fuzz::protobufs::TransformationSwapCommutableOperands&
-        message)
-    : message_(message) {}
+    protobufs::TransformationSwapCommutableOperands message)
+    : message_(std::move(message)) {}
 
 TransformationSwapCommutableOperands::TransformationSwapCommutableOperands(
     const protobufs::InstructionDescriptor& instruction_descriptor) {
diff --git a/source/fuzz/transformation_swap_commutable_operands.h b/source/fuzz/transformation_swap_commutable_operands.h
index c291c3e..5e211f1 100644
--- a/source/fuzz/transformation_swap_commutable_operands.h
+++ b/source/fuzz/transformation_swap_commutable_operands.h
@@ -26,7 +26,7 @@
 class TransformationSwapCommutableOperands : public Transformation {
  public:
   explicit TransformationSwapCommutableOperands(
-      const protobufs::TransformationSwapCommutableOperands& message);
+      protobufs::TransformationSwapCommutableOperands message);
 
   TransformationSwapCommutableOperands(
       const protobufs::InstructionDescriptor& instruction_descriptor);
diff --git a/source/fuzz/transformation_swap_conditional_branch_operands.cpp b/source/fuzz/transformation_swap_conditional_branch_operands.cpp
index 866fee6..340836d 100644
--- a/source/fuzz/transformation_swap_conditional_branch_operands.cpp
+++ b/source/fuzz/transformation_swap_conditional_branch_operands.cpp
@@ -22,9 +22,8 @@
 
 TransformationSwapConditionalBranchOperands::
     TransformationSwapConditionalBranchOperands(
-        const spvtools::fuzz::protobufs::
-            TransformationSwapConditionalBranchOperands& message)
-    : message_(message) {}
+        protobufs::TransformationSwapConditionalBranchOperands message)
+    : message_(std::move(message)) {}
 
 TransformationSwapConditionalBranchOperands::
     TransformationSwapConditionalBranchOperands(
@@ -70,11 +69,13 @@
 
   // We are swapping the labels in OpBranchConditional. This means that we must
   // invert the guard as well. We are using OpLogicalNot for that purpose here.
-  iter.InsertBefore(MakeUnique<opt::Instruction>(
+  auto new_instruction = MakeUnique<opt::Instruction>(
       ir_context, SpvOpLogicalNot, condition_inst->type_id(),
       message_.fresh_id(),
       opt::Instruction::OperandList{
-          {SPV_OPERAND_TYPE_ID, {condition_inst->result_id()}}}));
+          {SPV_OPERAND_TYPE_ID, {condition_inst->result_id()}}});
+  auto new_instruction_ptr = new_instruction.get();
+  iter.InsertBefore(std::move(new_instruction));
 
   fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
 
@@ -89,9 +90,13 @@
     std::swap(branch_inst->GetInOperand(3), branch_inst->GetInOperand(4));
   }
 
-  // Make sure the changes are analyzed.
-  ir_context->InvalidateAnalysesExceptFor(
-      opt::IRContext::Analysis::kAnalysisNone);
+  ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr);
+  ir_context->set_instr_block(new_instruction_ptr, block);
+  ir_context->get_def_use_mgr()->EraseUseRecordsOfOperandIds(branch_inst);
+  ir_context->get_def_use_mgr()->AnalyzeInstUse(branch_inst);
+
+  // No analyses need to be invalidated since the transformation is local to a
+  // block and the def-use and instruction-to-block mappings have been updated.
 }
 
 protobufs::Transformation
diff --git a/source/fuzz/transformation_swap_conditional_branch_operands.h b/source/fuzz/transformation_swap_conditional_branch_operands.h
index 022c54a..165ab80 100644
--- a/source/fuzz/transformation_swap_conditional_branch_operands.h
+++ b/source/fuzz/transformation_swap_conditional_branch_operands.h
@@ -26,7 +26,7 @@
 class TransformationSwapConditionalBranchOperands : public Transformation {
  public:
   explicit TransformationSwapConditionalBranchOperands(
-      const protobufs::TransformationSwapConditionalBranchOperands& message);
+      protobufs::TransformationSwapConditionalBranchOperands message);
 
   TransformationSwapConditionalBranchOperands(
       const protobufs::InstructionDescriptor& instruction_descriptor,
diff --git a/source/fuzz/transformation_swap_function_variables.cpp b/source/fuzz/transformation_swap_function_variables.cpp
new file mode 100644
index 0000000..aec32fe
--- /dev/null
+++ b/source/fuzz/transformation_swap_function_variables.cpp
@@ -0,0 +1,93 @@
+// Copyright (c) 2021 Mostafa Ashraf
+//
+// 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_swap_function_variables.h"
+
+#include "source/fuzz/fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationSwapFunctionVariables::TransformationSwapFunctionVariables(
+    protobufs::TransformationSwapFunctionVariables message)
+    : message_(std::move(message)) {}
+
+TransformationSwapFunctionVariables::TransformationSwapFunctionVariables(
+    uint32_t result_id1, uint32_t result_id2) {
+  message_.set_result_id1(result_id1);
+  message_.set_result_id2(result_id2);
+}
+
+bool TransformationSwapFunctionVariables::IsApplicable(
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+  uint32_t result_id1 = message_.result_id1();
+  uint32_t result_id2 = message_.result_id2();
+
+  assert((result_id1 != result_id2) && "Two results ids are equal");
+
+  // The result ids used in the message must refer to instructions.
+  auto instruction1 = ir_context->get_def_use_mgr()->GetDef(result_id1);
+  auto instruction2 = ir_context->get_def_use_mgr()->GetDef(result_id2);
+  if (instruction1 == nullptr || instruction2 == nullptr) {
+    return false;
+  }
+  // Both instructions must be variables.
+  if (instruction1->opcode() != SpvOpVariable ||
+      instruction2->opcode() != SpvOpVariable) {
+    return false;
+  }
+
+  // Both variable instructions must be in some basic block (as they are
+  // function-local variables), and they must be in the same block (as they need
+  // to be variables of the same function).
+  auto* block_1 = ir_context->get_instr_block(result_id1);
+  auto* block_2 = ir_context->get_instr_block(result_id2);
+  if (block_1 == nullptr || block_2 == nullptr) {
+    return false;
+  }
+
+  return block_1 == block_2;
+}
+
+void TransformationSwapFunctionVariables::Apply(
+    opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
+  // The result ids used in the message must refer to instructions.
+  auto instruction1 =
+      ir_context->get_def_use_mgr()->GetDef(message_.result_id1());
+  auto instruction2 =
+      ir_context->get_def_use_mgr()->GetDef(message_.result_id2());
+
+  std::unique_ptr<opt::Instruction> temp_instruction =
+      MakeUnique<opt::Instruction>();
+
+  temp_instruction->InsertBefore(instruction1);
+  instruction1->InsertAfter(instruction2);
+  instruction2->InsertAfter(temp_instruction.get());
+  temp_instruction->RemoveFromList();
+}
+
+protobufs::Transformation TransformationSwapFunctionVariables::ToMessage()
+    const {
+  protobufs::Transformation result;
+  *result.mutable_swap_function_variables() = message_;
+  return result;
+}
+
+std::unordered_set<uint32_t> TransformationSwapFunctionVariables::GetFreshIds()
+    const {
+  return std::unordered_set<uint32_t>();
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/source/fuzz/transformation_swap_function_variables.h b/source/fuzz/transformation_swap_function_variables.h
new file mode 100644
index 0000000..eb383f4
--- /dev/null
+++ b/source/fuzz/transformation_swap_function_variables.h
@@ -0,0 +1,57 @@
+// Copyright (c) 2021 Mostafa Ashraf
+//
+// 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_SWAP_FUNCTION_VARIABLES_H_
+#define SOURCE_FUZZ_TRANSFORMATION_SWAP_FUNCTION_VARIABLES_H_
+
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// A transformation that swaps two variable declaration instructions that appear
+// in the same function.
+class TransformationSwapFunctionVariables : public Transformation {
+ public:
+  explicit TransformationSwapFunctionVariables(
+      protobufs::TransformationSwapFunctionVariables message);
+
+  TransformationSwapFunctionVariables(uint32_t result_id1, uint32_t result_id2);
+
+  // - |message_.result_id1| and |message_.result_id2| must be the ids of
+  //   distinct OpVariable instructions appearing in the same function.
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
+
+  // Swaps two OpVariable instructions with result ids |message_.result_id1|
+  // and |message_.result_id2|.
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+  std::unordered_set<uint32_t> GetFreshIds() const override;
+
+ private:
+  protobufs::TransformationSwapFunctionVariables message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_SWAP_FUNCTION_VARIABLES_H_
diff --git a/source/fuzz/transformation_swap_two_functions.cpp b/source/fuzz/transformation_swap_two_functions.cpp
new file mode 100644
index 0000000..85d9e79
--- /dev/null
+++ b/source/fuzz/transformation_swap_two_functions.cpp
@@ -0,0 +1,72 @@
+// Copyright (c) 2021 Shiyu Liu
+//
+// 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_swap_two_functions.h"
+
+#include "source/opt/function.h"
+#include "source/opt/module.h"
+
+#include "source/fuzz/fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationSwapTwoFunctions::TransformationSwapTwoFunctions(
+    protobufs::TransformationSwapTwoFunctions message)
+    : message_(std::move(message)) {}
+
+TransformationSwapTwoFunctions::TransformationSwapTwoFunctions(uint32_t id1,
+                                                               uint32_t id2) {
+  assert(id1 != id2 && "The two function ids cannot be the same.");
+  message_.set_function_id1(id1);
+  message_.set_function_id2(id2);
+}
+
+bool TransformationSwapTwoFunctions::IsApplicable(
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+  auto func1_ptr = ir_context->GetFunction(message_.function_id1());
+  auto func2_ptr = ir_context->GetFunction(message_.function_id2());
+  return func1_ptr != nullptr && func2_ptr != nullptr;
+}
+
+void TransformationSwapTwoFunctions::Apply(
+    opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
+  opt::Module::iterator func1_it =
+      fuzzerutil::GetFunctionIterator(ir_context, message_.function_id1());
+  opt::Module::iterator func2_it =
+      fuzzerutil::GetFunctionIterator(ir_context, message_.function_id2());
+
+  assert(func1_it != ir_context->module()->end() &&
+         "Could not find function 1.");
+  assert(func2_it != ir_context->module()->end() &&
+         "Could not find function 2.");
+
+  // Two function pointers are all set, swap the two functions within the
+  // module.
+  std::iter_swap(func1_it.Get(), func2_it.Get());
+}
+
+protobufs::Transformation TransformationSwapTwoFunctions::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_swap_two_functions() = message_;
+  return result;
+}
+
+std::unordered_set<uint32_t> TransformationSwapTwoFunctions::GetFreshIds()
+    const {
+  return std::unordered_set<uint32_t>();
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/source/fuzz/transformation_swap_two_functions.h b/source/fuzz/transformation_swap_two_functions.h
new file mode 100644
index 0000000..1b6d6e8
--- /dev/null
+++ b/source/fuzz/transformation_swap_two_functions.h
@@ -0,0 +1,56 @@
+// Copyright (c) 2021 Shiyu Liu
+//
+// 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_SWAP_TWO_FUNCTIONS_H_
+#define SOURCE_FUZZ_TRANSFORMATION_SWAP_TWO_FUNCTIONS_H_
+
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationSwapTwoFunctions : public Transformation {
+ public:
+  explicit TransformationSwapTwoFunctions(
+      protobufs::TransformationSwapTwoFunctions message);
+
+  TransformationSwapTwoFunctions(uint32_t function_id1, uint32_t function_id2);
+
+  // |function_id1| and  |function_id1| should all be existing ids.
+  //  Swap function operation is only permitted if:
+  //  - both ids must be ids of functions.
+  //  - both ids can be found in the module.
+  //  - function_id1 and function_id2 are not the same.
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
+
+  // OpFunction with |function_id1| and |function_id1| are swapped.
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
+
+  std::unordered_set<uint32_t> GetFreshIds() const override;
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  protobufs::TransformationSwapTwoFunctions message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_SWAP_TWO_FUNCTIONS_H_
diff --git a/source/fuzz/transformation_toggle_access_chain_instruction.cpp b/source/fuzz/transformation_toggle_access_chain_instruction.cpp
index 1952a34..34523fe 100644
--- a/source/fuzz/transformation_toggle_access_chain_instruction.cpp
+++ b/source/fuzz/transformation_toggle_access_chain_instruction.cpp
@@ -22,9 +22,8 @@
 
 TransformationToggleAccessChainInstruction::
     TransformationToggleAccessChainInstruction(
-        const spvtools::fuzz::protobufs::
-            TransformationToggleAccessChainInstruction& message)
-    : message_(message) {}
+        protobufs::TransformationToggleAccessChainInstruction message)
+    : message_(std::move(message)) {}
 
 TransformationToggleAccessChainInstruction::
     TransformationToggleAccessChainInstruction(
diff --git a/source/fuzz/transformation_toggle_access_chain_instruction.h b/source/fuzz/transformation_toggle_access_chain_instruction.h
index 977b0d7..be2718b 100644
--- a/source/fuzz/transformation_toggle_access_chain_instruction.h
+++ b/source/fuzz/transformation_toggle_access_chain_instruction.h
@@ -26,7 +26,7 @@
 class TransformationToggleAccessChainInstruction : public Transformation {
  public:
   explicit TransformationToggleAccessChainInstruction(
-      const protobufs::TransformationToggleAccessChainInstruction& message);
+      protobufs::TransformationToggleAccessChainInstruction message);
 
   TransformationToggleAccessChainInstruction(
       const protobufs::InstructionDescriptor& instruction_descriptor);
diff --git a/source/fuzz/transformation_vector_shuffle.cpp b/source/fuzz/transformation_vector_shuffle.cpp
index 05af18e..ac0e3cc 100644
--- a/source/fuzz/transformation_vector_shuffle.cpp
+++ b/source/fuzz/transformation_vector_shuffle.cpp
@@ -21,8 +21,8 @@
 namespace fuzz {
 
 TransformationVectorShuffle::TransformationVectorShuffle(
-    const spvtools::fuzz::protobufs::TransformationVectorShuffle& message)
-    : message_(message) {}
+    protobufs::TransformationVectorShuffle message)
+    : message_(std::move(message)) {}
 
 TransformationVectorShuffle::TransformationVectorShuffle(
     const protobufs::InstructionDescriptor& instruction_to_insert_before,
@@ -130,13 +130,18 @@
 
   // Add a shuffle instruction right before the instruction identified by
   // |message_.instruction_to_insert_before|.
-  FindInstruction(message_.instruction_to_insert_before(), ir_context)
-      ->InsertBefore(MakeUnique<opt::Instruction>(
+  auto insert_before =
+      FindInstruction(message_.instruction_to_insert_before(), ir_context);
+  opt::Instruction* new_instruction =
+      insert_before->InsertBefore(MakeUnique<opt::Instruction>(
           ir_context, SpvOpVectorShuffle, result_type_id, message_.fresh_id(),
           shuffle_operands));
   fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
-  ir_context->InvalidateAnalysesExceptFor(
-      opt::IRContext::Analysis::kAnalysisNone);
+  // Inform the def-use manager about the new instruction and record its basic
+  // block.
+  ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction);
+  ir_context->set_instr_block(new_instruction,
+                              ir_context->get_instr_block(insert_before));
 
   AddDataSynonymFacts(ir_context, transformation_context);
 }
diff --git a/source/fuzz/transformation_vector_shuffle.h b/source/fuzz/transformation_vector_shuffle.h
index cf08a62..7360906 100644
--- a/source/fuzz/transformation_vector_shuffle.h
+++ b/source/fuzz/transformation_vector_shuffle.h
@@ -27,7 +27,7 @@
 class TransformationVectorShuffle : public Transformation {
  public:
   explicit TransformationVectorShuffle(
-      const protobufs::TransformationVectorShuffle& message);
+      protobufs::TransformationVectorShuffle message);
 
   TransformationVectorShuffle(
       const protobufs::InstructionDescriptor& instruction_to_insert_before,
diff --git a/source/fuzz/transformation_wrap_early_terminator_in_function.cpp b/source/fuzz/transformation_wrap_early_terminator_in_function.cpp
index 4c436f5..468d809 100644
--- a/source/fuzz/transformation_wrap_early_terminator_in_function.cpp
+++ b/source/fuzz/transformation_wrap_early_terminator_in_function.cpp
@@ -23,9 +23,8 @@
 
 TransformationWrapEarlyTerminatorInFunction::
     TransformationWrapEarlyTerminatorInFunction(
-        const spvtools::fuzz::protobufs::
-            TransformationWrapEarlyTerminatorInFunction& message)
-    : message_(message) {}
+        protobufs::TransformationWrapEarlyTerminatorInFunction message)
+    : message_(std::move(message)) {}
 
 TransformationWrapEarlyTerminatorInFunction::
     TransformationWrapEarlyTerminatorInFunction(
diff --git a/source/fuzz/transformation_wrap_early_terminator_in_function.h b/source/fuzz/transformation_wrap_early_terminator_in_function.h
index 00151d4..d6e5551 100644
--- a/source/fuzz/transformation_wrap_early_terminator_in_function.h
+++ b/source/fuzz/transformation_wrap_early_terminator_in_function.h
@@ -26,7 +26,7 @@
 class TransformationWrapEarlyTerminatorInFunction : public Transformation {
  public:
   explicit TransformationWrapEarlyTerminatorInFunction(
-      const protobufs::TransformationWrapEarlyTerminatorInFunction& message);
+      protobufs::TransformationWrapEarlyTerminatorInFunction message);
 
   TransformationWrapEarlyTerminatorInFunction(
       uint32_t fresh_id,
diff --git a/source/fuzz/transformation_wrap_region_in_selection.cpp b/source/fuzz/transformation_wrap_region_in_selection.cpp
index 9924e36..01c98cc 100644
--- a/source/fuzz/transformation_wrap_region_in_selection.cpp
+++ b/source/fuzz/transformation_wrap_region_in_selection.cpp
@@ -20,8 +20,8 @@
 namespace fuzz {
 
 TransformationWrapRegionInSelection::TransformationWrapRegionInSelection(
-    const protobufs::TransformationWrapRegionInSelection& message)
-    : message_(message) {}
+    protobufs::TransformationWrapRegionInSelection message)
+    : message_(std::move(message)) {}
 
 TransformationWrapRegionInSelection::TransformationWrapRegionInSelection(
     uint32_t region_entry_block_id, uint32_t region_exit_block_id,
diff --git a/source/fuzz/transformation_wrap_region_in_selection.h b/source/fuzz/transformation_wrap_region_in_selection.h
index 57f4f64..66d16da 100644
--- a/source/fuzz/transformation_wrap_region_in_selection.h
+++ b/source/fuzz/transformation_wrap_region_in_selection.h
@@ -26,7 +26,7 @@
 class TransformationWrapRegionInSelection : public Transformation {
  public:
   explicit TransformationWrapRegionInSelection(
-      const protobufs::TransformationWrapRegionInSelection& message);
+      protobufs::TransformationWrapRegionInSelection message);
 
   TransformationWrapRegionInSelection(uint32_t region_entry_block_id,
                                       uint32_t region_exit_block_id,
diff --git a/source/opcode.cpp b/source/opcode.cpp
index d87e828..c96cde8 100644
--- a/source/opcode.cpp
+++ b/source/opcode.cpp
@@ -417,8 +417,10 @@
     case SpvOpAtomicISub:
     case SpvOpAtomicSMin:
     case SpvOpAtomicUMin:
+    case SpvOpAtomicFMinEXT:
     case SpvOpAtomicSMax:
     case SpvOpAtomicUMax:
+    case SpvOpAtomicFMaxEXT:
     case SpvOpAtomicAnd:
     case SpvOpAtomicOr:
     case SpvOpAtomicXor:
diff --git a/source/operand.cpp b/source/operand.cpp
index 5a69fb2..c00c9b6 100644
--- a/source/operand.cpp
+++ b/source/operand.cpp
@@ -229,6 +229,9 @@
       return "ray query committed intersection type";
     case SPV_OPERAND_TYPE_RAY_QUERY_CANDIDATE_INTERSECTION_TYPE:
       return "ray query candidate intersection type";
+    case SPV_OPERAND_TYPE_PACKED_VECTOR_FORMAT:
+    case SPV_OPERAND_TYPE_OPTIONAL_PACKED_VECTOR_FORMAT:
+      return "packed vector format";
     case SPV_OPERAND_TYPE_IMAGE:
     case SPV_OPERAND_TYPE_OPTIONAL_IMAGE:
       return "image";
@@ -269,6 +272,10 @@
       return "FP denorm mode";
     case SPV_OPERAND_TYPE_FPOPERATION_MODE:
       return "FP operation mode";
+    case SPV_OPERAND_TYPE_QUANTIZATION_MODES:
+      return "quantization mode";
+    case SPV_OPERAND_TYPE_OVERFLOW_MODES:
+      return "overflow mode";
 
     case SPV_OPERAND_TYPE_NONE:
       return "NONE";
@@ -355,6 +362,9 @@
     case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_IMPORTED_ENTITY:
     case SPV_OPERAND_TYPE_FPDENORM_MODE:
     case SPV_OPERAND_TYPE_FPOPERATION_MODE:
+    case SPV_OPERAND_TYPE_QUANTIZATION_MODES:
+    case SPV_OPERAND_TYPE_OVERFLOW_MODES:
+    case SPV_OPERAND_TYPE_PACKED_VECTOR_FORMAT:
       return true;
     default:
       break;
@@ -390,6 +400,7 @@
     case SPV_OPERAND_TYPE_OPTIONAL_TYPED_LITERAL_INTEGER:
     case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_STRING:
     case SPV_OPERAND_TYPE_OPTIONAL_ACCESS_QUALIFIER:
+    case SPV_OPERAND_TYPE_OPTIONAL_PACKED_VECTOR_FORMAT:
     case SPV_OPERAND_TYPE_OPTIONAL_CIV:
       return true;
     default:
diff --git a/source/opt/CMakeLists.txt b/source/opt/CMakeLists.txt
index 14a6bee..88d5658 100644
--- a/source/opt/CMakeLists.txt
+++ b/source/opt/CMakeLists.txt
@@ -62,6 +62,7 @@
   instruction.h
   instruction_list.h
   instrument_pass.h
+  interp_fixup_pass.h
   ir_builder.h
   ir_context.h
   ir_loader.h
@@ -165,6 +166,7 @@
   instruction.cpp
   instruction_list.cpp
   instrument_pass.cpp
+  interp_fixup_pass.cpp
   ir_context.cpp
   ir_loader.cpp
   licm_pass.cpp
diff --git a/source/opt/aggressive_dead_code_elim_pass.cpp b/source/opt/aggressive_dead_code_elim_pass.cpp
index 81b2232..7cffff5 100644
--- a/source/opt/aggressive_dead_code_elim_pass.cpp
+++ b/source/opt/aggressive_dead_code_elim_pass.cpp
@@ -996,6 +996,9 @@
       "SPV_EXT_physical_storage_buffer",
       "SPV_KHR_terminate_invocation",
       "SPV_KHR_shader_clock",
+      "SPV_KHR_vulkan_memory_model",
+      "SPV_KHR_subgroup_uniform_control_flow",
+      "SPV_KHR_integer_dot_product",
   });
 }
 
diff --git a/source/opt/code_sink.cpp b/source/opt/code_sink.cpp
index e49029f..cd77797 100644
--- a/source/opt/code_sink.cpp
+++ b/source/opt/code_sink.cpp
@@ -218,8 +218,10 @@
       case SpvOpAtomicISub:
       case SpvOpAtomicSMin:
       case SpvOpAtomicUMin:
+      case SpvOpAtomicFMinEXT:
       case SpvOpAtomicSMax:
       case SpvOpAtomicUMax:
+      case SpvOpAtomicFMaxEXT:
       case SpvOpAtomicAnd:
       case SpvOpAtomicOr:
       case SpvOpAtomicXor:
diff --git a/source/opt/constants.cpp b/source/opt/constants.cpp
index cf24295..19ca600 100644
--- a/source/opt/constants.cpp
+++ b/source/opt/constants.cpp
@@ -389,6 +389,36 @@
   return cst ? RegisterConstant(std::move(cst)) : nullptr;
 }
 
+const Constant* ConstantManager::GetNumericVectorConstantWithWords(
+    const Vector* type, const std::vector<uint32_t>& literal_words) {
+  const auto* element_type = type->element_type();
+  uint32_t words_per_element = 0;
+  if (const auto* float_type = element_type->AsFloat())
+    words_per_element = float_type->width() / 32;
+  else if (const auto* int_type = element_type->AsInteger())
+    words_per_element = int_type->width() / 32;
+
+  if (words_per_element != 1 && words_per_element != 2) return nullptr;
+
+  if (words_per_element * type->element_count() !=
+      static_cast<uint32_t>(literal_words.size())) {
+    return nullptr;
+  }
+
+  std::vector<uint32_t> element_ids;
+  for (uint32_t i = 0; i < type->element_count(); ++i) {
+    auto first_word = literal_words.begin() + (words_per_element * i);
+    std::vector<uint32_t> const_data(first_word,
+                                     first_word + words_per_element);
+    const analysis::Constant* element_constant =
+        GetConstant(element_type, const_data);
+    auto element_id = GetDefiningInstruction(element_constant)->result_id();
+    element_ids.push_back(element_id);
+  }
+
+  return GetConstant(type, element_ids);
+}
+
 uint32_t ConstantManager::GetFloatConst(float val) {
   Type* float_type = context()->get_type_mgr()->GetFloatType();
   utils::FloatProxy<float> v(val);
diff --git a/source/opt/constants.h b/source/opt/constants.h
index e17ae6b..95d984f 100644
--- a/source/opt/constants.h
+++ b/source/opt/constants.h
@@ -58,7 +58,7 @@
 class Constant {
  public:
   Constant() = delete;
-  virtual ~Constant() {}
+  virtual ~Constant() = default;
 
   // Make a deep copy of this constant.
   virtual std::unique_ptr<Constant> Copy() const = 0;
@@ -506,10 +506,11 @@
   IRContext* context() const { return ctx_; }
 
   // Gets or creates a unique Constant instance of type |type| and a vector of
-  // constant defining words |words|. If a Constant instance existed already in
-  // the constant pool, it returns a pointer to it.  Otherwise, it creates one
-  // using CreateConstant. If a new Constant instance cannot be created, it
-  // returns nullptr.
+  // constant defining words or ids for elements of Vector type
+  // |literal_words_or_ids|. If a Constant instance existed already in the
+  // constant pool, it returns a pointer to it. Otherwise, it creates one using
+  // CreateConstant. If a new Constant instance cannot be created, it returns
+  // nullptr.
   const Constant* GetConstant(
       const Type* type, const std::vector<uint32_t>& literal_words_or_ids);
 
@@ -519,6 +520,14 @@
                                                    literal_words_or_ids.end()));
   }
 
+  // Gets or creates a unique Constant instance of Vector type |type| with
+  // numeric elements and a vector of constant defining words |literal_words|.
+  // If a Constant instance existed already in the constant pool, it returns a
+  // pointer to it. Otherwise, it creates one using CreateConstant. If a new
+  // Constant instance cannot be created, it returns nullptr.
+  const Constant* GetNumericVectorConstantWithWords(
+      const Vector* type, const std::vector<uint32_t>& literal_words);
+
   // Gets or creates a Constant instance to hold the constant value of the given
   // instruction. It returns a pointer to a Constant instance or nullptr if it
   // could not create the constant.
diff --git a/source/opt/eliminate_dead_functions_util.cpp b/source/opt/eliminate_dead_functions_util.cpp
index 6b5234b..1379120 100644
--- a/source/opt/eliminate_dead_functions_util.cpp
+++ b/source/opt/eliminate_dead_functions_util.cpp
@@ -23,9 +23,11 @@
                                    Module::iterator* func_iter) {
   bool first_func = *func_iter == context->module()->begin();
   bool seen_func_end = false;
+  std::unordered_set<Instruction*> to_kill;
   (*func_iter)
       ->ForEachInst(
-          [context, first_func, func_iter, &seen_func_end](Instruction* inst) {
+          [context, first_func, func_iter, &seen_func_end,
+           &to_kill](Instruction* inst) {
             if (inst->opcode() == SpvOpFunctionEnd) {
               seen_func_end = true;
             }
@@ -33,6 +35,7 @@
             // global values if this is the first function.
             if (seen_func_end && inst->opcode() == SpvOpExtInst) {
               assert(inst->IsNonSemanticInstruction());
+              if (to_kill.find(inst) != to_kill.end()) return;
               std::unique_ptr<Instruction> clone(inst->Clone(context));
               context->ForgetUses(inst);
               context->AnalyzeDefUse(clone.get());
@@ -44,12 +47,17 @@
                 prev_func_iter->AddNonSemanticInstruction(std::move(clone));
               }
               inst->ToNop();
-            } else {
-              context->KillNonSemanticInfo(inst);
+            } else if (to_kill.find(inst) == to_kill.end()) {
+              context->CollectNonSemanticTree(inst, &to_kill);
               context->KillInst(inst);
             }
           },
           true, true);
+
+  for (auto* dead : to_kill) {
+    context->KillInst(dead);
+  }
+
   return func_iter->Erase();
 }
 
diff --git a/source/opt/fix_storage_class.cpp b/source/opt/fix_storage_class.cpp
index 03da0d0..04eb132 100644
--- a/source/opt/fix_storage_class.cpp
+++ b/source/opt/fix_storage_class.cpp
@@ -222,6 +222,16 @@
       uint32_t pointee_type_id = GetPointeeTypeId(ptr_inst);
 
       if (obj_type_id != pointee_type_id) {
+        if (context()->get_type_mgr()->GetType(obj_type_id)->AsImage() &&
+            context()->get_type_mgr()->GetType(pointee_type_id)->AsImage()) {
+          // When storing an image, allow the type mismatch
+          // and let the later legalization passes eliminate the OpStore.
+          // This is to support assigning an image to a variable,
+          // where the assigned image does not have a pre-defined
+          // image format.
+          return false;
+        }
+
         uint32_t copy_id = GenerateCopy(obj_inst, pointee_type_id, inst);
         inst->SetInOperand(1, {copy_id});
         context()->UpdateDefUse(inst);
diff --git a/source/opt/folding_rules.cpp b/source/opt/folding_rules.cpp
index 010eec9..e3e926c 100644
--- a/source/opt/folding_rules.cpp
+++ b/source/opt/folding_rules.cpp
@@ -124,6 +124,66 @@
       inst->GetSingleWordInOperand(in_op));
 }
 
+std::vector<uint32_t> ExtractInts(uint64_t val) {
+  std::vector<uint32_t> words;
+  words.push_back(static_cast<uint32_t>(val));
+  words.push_back(static_cast<uint32_t>(val >> 32));
+  return words;
+}
+
+std::vector<uint32_t> GetWordsFromScalarIntConstant(
+    const analysis::IntConstant* c) {
+  assert(c != nullptr);
+  uint32_t width = c->type()->AsInteger()->width();
+  assert(width == 32 || width == 64);
+  if (width == 64) {
+    uint64_t uval = static_cast<uint64_t>(c->GetU64());
+    return ExtractInts(uval);
+  }
+  return {c->GetU32()};
+}
+
+std::vector<uint32_t> GetWordsFromScalarFloatConstant(
+    const analysis::FloatConstant* c) {
+  assert(c != nullptr);
+  uint32_t width = c->type()->AsFloat()->width();
+  assert(width == 32 || width == 64);
+  if (width == 64) {
+    utils::FloatProxy<double> result(c->GetDouble());
+    return result.GetWords();
+  }
+  utils::FloatProxy<float> result(c->GetFloat());
+  return result.GetWords();
+}
+
+std::vector<uint32_t> GetWordsFromNumericScalarOrVectorConstant(
+    analysis::ConstantManager* const_mgr, const analysis::Constant* c) {
+  if (const auto* float_constant = c->AsFloatConstant()) {
+    return GetWordsFromScalarFloatConstant(float_constant);
+  } else if (const auto* int_constant = c->AsIntConstant()) {
+    return GetWordsFromScalarIntConstant(int_constant);
+  } else if (const auto* vec_constant = c->AsVectorConstant()) {
+    std::vector<uint32_t> words;
+    for (const auto* comp : vec_constant->GetComponents()) {
+      auto comp_in_words =
+          GetWordsFromNumericScalarOrVectorConstant(const_mgr, comp);
+      words.insert(words.end(), comp_in_words.begin(), comp_in_words.end());
+    }
+    return words;
+  }
+  return {};
+}
+
+const analysis::Constant* ConvertWordsToNumericScalarOrVectorConstant(
+    analysis::ConstantManager* const_mgr, const std::vector<uint32_t>& words,
+    const analysis::Type* type) {
+  if (type->AsInteger() || type->AsFloat())
+    return const_mgr->GetConstant(type, words);
+  if (const auto* vec_type = type->AsVector())
+    return const_mgr->GetNumericVectorConstantWithWords(vec_type, words);
+  return nullptr;
+}
+
 // Returns the negation of |c|. |c| must be a 32 or 64 bit floating point
 // constant.
 uint32_t NegateFloatingPointConstant(analysis::ConstantManager* const_mgr,
@@ -146,13 +206,6 @@
   return const_mgr->GetDefiningInstruction(negated_const)->result_id();
 }
 
-std::vector<uint32_t> ExtractInts(uint64_t val) {
-  std::vector<uint32_t> words;
-  words.push_back(static_cast<uint32_t>(val));
-  words.push_back(static_cast<uint32_t>(val >> 32));
-  return words;
-}
-
 // Negates the integer constant |c|. Returns the id of the defining instruction.
 uint32_t NegateIntegerConstant(analysis::ConstantManager* const_mgr,
                                const analysis::Constant* c) {
@@ -470,7 +523,7 @@
     float fval = val.getAsFloat();                                           \
     if (!IsValidResult(fval)) return 0;                                      \
     words = val.GetWords();                                                  \
-  }
+  } static_assert(true, "require extra semicolon")
   switch (opcode) {
     case SpvOpFMul:
       FOLD_OP(*);
@@ -522,7 +575,7 @@
       uint32_t val = input1->GetU32() op input2->GetU32(); \
       words.push_back(val);                                \
     }                                                      \
-  }
+  } static_assert(true, "require extra semicalon")
   switch (opcode) {
     case SpvOpIMul:
       FOLD_OP(*);
@@ -1796,6 +1849,35 @@
   };
 }
 
+FoldingRule BitCastScalarOrVector() {
+  return [](IRContext* context, Instruction* inst,
+            const std::vector<const analysis::Constant*>& constants) {
+    assert(inst->opcode() == SpvOpBitcast && constants.size() == 1);
+    if (constants[0] == nullptr) return false;
+
+    const analysis::Type* type =
+        context->get_type_mgr()->GetType(inst->type_id());
+    if (HasFloatingPoint(type) && !inst->IsFloatingPointFoldingAllowed())
+      return false;
+
+    analysis::ConstantManager* const_mgr = context->get_constant_mgr();
+    std::vector<uint32_t> words =
+        GetWordsFromNumericScalarOrVectorConstant(const_mgr, constants[0]);
+    if (words.size() == 0) return false;
+
+    const analysis::Constant* bitcasted_constant =
+        ConvertWordsToNumericScalarOrVectorConstant(const_mgr, words, type);
+    if (!bitcasted_constant) return false;
+
+    auto new_feeder_id =
+        const_mgr->GetDefiningInstruction(bitcasted_constant, inst->type_id())
+            ->result_id();
+    inst->SetOpcode(SpvOpCopyObject);
+    inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {new_feeder_id}}});
+    return true;
+  };
+}
+
 FoldingRule RedundantSelect() {
   // An OpSelect instruction where both values are the same or the condition is
   // constant can be replaced by one of the values
@@ -2423,6 +2505,8 @@
   // Note that the order in which rules are added to the list matters. If a rule
   // applies to the instruction, the rest of the rules will not be attempted.
   // Take that into consideration.
+  rules_[SpvOpBitcast].push_back(BitCastScalarOrVector());
+
   rules_[SpvOpCompositeConstruct].push_back(CompositeExtractFeedingConstruct);
 
   rules_[SpvOpCompositeExtract].push_back(InsertFeedingExtract());
diff --git a/source/opt/graphics_robust_access_pass.cpp b/source/opt/graphics_robust_access_pass.cpp
index 46483e4..1b28f9b 100644
--- a/source/opt/graphics_robust_access_pass.cpp
+++ b/source/opt/graphics_robust_access_pass.cpp
@@ -272,10 +272,11 @@
 
   // Replaces one of the OpAccessChain index operands with a new value.
   // Updates def-use analysis.
-  auto replace_index = [&inst, def_use_mgr](uint32_t operand_index,
-                                            Instruction* new_value) {
+  auto replace_index = [this, &inst, def_use_mgr](uint32_t operand_index,
+                                                  Instruction* new_value) {
     inst.SetOperand(operand_index, {new_value->result_id()});
     def_use_mgr->AnalyzeInstUse(&inst);
+    module_status_.modified = true;
     return SPV_SUCCESS;
   };
 
diff --git a/source/opt/inline_pass.h b/source/opt/inline_pass.h
index abe773a..9a5429b 100644
--- a/source/opt/inline_pass.h
+++ b/source/opt/inline_pass.h
@@ -37,7 +37,7 @@
   using cbb_ptr = const BasicBlock*;
 
  public:
-  virtual ~InlinePass() = default;
+  virtual ~InlinePass() override = default;
 
  protected:
   InlinePass();
diff --git a/source/opt/inst_bindless_check_pass.cpp b/source/opt/inst_bindless_check_pass.cpp
index 0085734..5607239 100644
--- a/source/opt/inst_bindless_check_pass.cpp
+++ b/source/opt/inst_bindless_check_pass.cpp
@@ -27,13 +27,16 @@
 static const int kSpvLoadPtrIdInIdx = 0;
 static const int kSpvAccessChainBaseIdInIdx = 0;
 static const int kSpvAccessChainIndex0IdInIdx = 1;
+static const int kSpvTypeArrayTypeIdInIdx = 0;
 static const int kSpvTypeArrayLengthIdInIdx = 1;
 static const int kSpvConstantValueInIdx = 0;
 static const int kSpvVariableStorageClassInIdx = 0;
+static const int kSpvTypePtrTypeIdInIdx = 1;
 static const int kSpvTypeImageDim = 1;
 static const int kSpvTypeImageDepth = 2;
 static const int kSpvTypeImageArrayed = 3;
 static const int kSpvTypeImageMS = 4;
+static const int kSpvTypeImageSampled = 5;
 }  // anonymous namespace
 
 // Avoid unused variable warning/error on Linux
@@ -206,13 +209,40 @@
         var_inst->GetSingleWordInOperand(kSpvVariableStorageClassInIdx);
     switch (storage_class) {
       case SpvStorageClassUniform:
-      case SpvStorageClassUniformConstant:
       case SpvStorageClassStorageBuffer:
         break;
       default:
         return false;
         break;
     }
+    // Check for deprecated storage block form
+    if (storage_class == SpvStorageClassUniform) {
+      uint32_t var_ty_id = var_inst->type_id();
+      Instruction* var_ty_inst = get_def_use_mgr()->GetDef(var_ty_id);
+      uint32_t ptr_ty_id =
+          var_ty_inst->GetSingleWordInOperand(kSpvTypePtrTypeIdInIdx);
+      Instruction* ptr_ty_inst = get_def_use_mgr()->GetDef(ptr_ty_id);
+      SpvOp ptr_ty_op = ptr_ty_inst->opcode();
+      uint32_t block_ty_id =
+          (ptr_ty_op == SpvOpTypeArray || ptr_ty_op == SpvOpTypeRuntimeArray)
+              ? ptr_ty_inst->GetSingleWordInOperand(kSpvTypeArrayTypeIdInIdx)
+              : ptr_ty_id;
+      assert(get_def_use_mgr()->GetDef(block_ty_id)->opcode() ==
+                 SpvOpTypeStruct &&
+             "unexpected block type");
+      bool block_found = get_decoration_mgr()->FindDecoration(
+          block_ty_id, SpvDecorationBlock,
+          [](const Instruction&) { return true; });
+      if (!block_found) {
+        // If block decoration not found, verify deprecated form of SSBO
+        bool buffer_block_found = get_decoration_mgr()->FindDecoration(
+            block_ty_id, SpvDecorationBufferBlock,
+            [](const Instruction&) { return true; });
+        USE_ASSERT(buffer_block_found && "block decoration not found");
+        storage_class = SpvStorageClassStorageBuffer;
+      }
+    }
+    ref->strg_class = storage_class;
     Instruction* desc_type_inst = GetPointeeTypeInst(var_inst);
     switch (desc_type_inst->opcode()) {
       case SpvOpTypeArray:
@@ -665,8 +695,10 @@
   // for the referenced value.
   Instruction* ult_inst =
       builder.AddBinaryOp(GetBoolId(), SpvOpULessThan, ref_id, init_id);
-  uint32_t error =
-      init_check ? kInstErrorBindlessUninit : kInstErrorBindlessBuffOOB;
+  uint32_t error = init_check ? kInstErrorBindlessUninit
+                              : (ref.strg_class == SpvStorageClassUniform
+                                     ? kInstErrorBuffOOBUniform
+                                     : kInstErrorBuffOOBStorage);
   uint32_t error_id = builder.GetUintConstantId(error);
   GenCheckCode(ult_inst->result_id(), error_id, init_check ? 0 : ref_id,
                init_check ? builder.GetUintConstantId(0u) : init_id, stage_idx,
@@ -732,7 +764,11 @@
   // for the referenced value.
   Instruction* ult_inst =
       builder.AddBinaryOp(GetBoolId(), SpvOpULessThan, coord_id, size_id);
-  uint32_t error_id = builder.GetUintConstantId(kInstErrorBindlessBuffOOB);
+  uint32_t error =
+      (image_ty_inst->GetSingleWordInOperand(kSpvTypeImageSampled) == 2)
+          ? kInstErrorBuffOOBStorageTexel
+          : kInstErrorBuffOOBUniformTexel;
+  uint32_t error_id = builder.GetUintConstantId(error);
   GenCheckCode(ult_inst->result_id(), error_id, coord_id, size_id, stage_idx,
                &ref, new_blocks);
   // Move original block's remaining code into remainder/merge block and add
diff --git a/source/opt/inst_bindless_check_pass.h b/source/opt/inst_bindless_check_pass.h
index a7dff75..cd96180 100644
--- a/source/opt/inst_bindless_check_pass.h
+++ b/source/opt/inst_bindless_check_pass.h
@@ -130,6 +130,7 @@
     uint32_t ptr_id;
     uint32_t var_id;
     uint32_t desc_idx_id;
+    uint32_t strg_class;
     Instruction* ref_inst;
   } RefAnalysis;
 
diff --git a/source/opt/instruction.h b/source/opt/instruction.h
index 4743221..3e557dd 100644
--- a/source/opt/instruction.h
+++ b/source/opt/instruction.h
@@ -209,7 +209,7 @@
   Instruction(Instruction&&);
   Instruction& operator=(Instruction&&);
 
-  virtual ~Instruction() = default;
+  ~Instruction() override = default;
 
   // Returns a newly allocated instruction that has the same operands, result,
   // and type as |this|.  The new instruction is not linked into any list.
diff --git a/source/opt/instruction_list.h b/source/opt/instruction_list.h
index 417cbd7..b3e4274 100644
--- a/source/opt/instruction_list.h
+++ b/source/opt/instruction_list.h
@@ -53,7 +53,7 @@
   }
 
   // Destroy this list and any instructions in the list.
-  inline virtual ~InstructionList();
+  inline ~InstructionList() override;
 
   class iterator : public utils::IntrusiveList<Instruction>::iterator {
    public:
diff --git a/source/opt/interp_fixup_pass.cpp b/source/opt/interp_fixup_pass.cpp
new file mode 100644
index 0000000..ad29e6a
--- /dev/null
+++ b/source/opt/interp_fixup_pass.cpp
@@ -0,0 +1,131 @@
+// Copyright (c) 2021 The Khronos Group Inc.
+// Copyright (c) 2021 Valve Corporation
+// Copyright (c) 2021 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/interp_fixup_pass.h"
+
+#include <set>
+#include <string>
+
+#include "ir_builder.h"
+#include "source/opt/ir_context.h"
+#include "type_manager.h"
+
+namespace spvtools {
+namespace opt {
+
+namespace {
+
+// Input Operand Indices
+static const int kSpvVariableStorageClassInIdx = 0;
+
+// Avoid unused variable warning/error on Linux
+#ifndef NDEBUG
+#define USE_ASSERT(x) assert(x)
+#else
+#define USE_ASSERT(x) ((void)(x))
+#endif
+
+// Folding rule function which attempts to replace |op(OpLoad(a),...)|
+// by |op(a,...)|, where |op| is one of the GLSLstd450 InterpolateAt*
+// instructions. Returns true if replaced, false otherwise.
+bool ReplaceInternalInterpolate(IRContext* ctx, Instruction* inst,
+                                const std::vector<const analysis::Constant*>&) {
+  uint32_t glsl450_ext_inst_id =
+      ctx->get_feature_mgr()->GetExtInstImportId_GLSLstd450();
+  assert(glsl450_ext_inst_id != 0);
+
+  uint32_t ext_opcode = inst->GetSingleWordInOperand(1);
+
+  uint32_t op1_id = inst->GetSingleWordInOperand(2);
+
+  Instruction* load_inst = ctx->get_def_use_mgr()->GetDef(op1_id);
+  if (load_inst->opcode() != SpvOpLoad) return false;
+
+  Instruction* base_inst = load_inst->GetBaseAddress();
+  USE_ASSERT(base_inst->opcode() == SpvOpVariable &&
+             base_inst->GetSingleWordInOperand(kSpvVariableStorageClassInIdx) ==
+                 SpvStorageClassInput &&
+             "unexpected interpolant in InterpolateAt*");
+
+  uint32_t ptr_id = load_inst->GetSingleWordInOperand(0);
+  uint32_t op2_id = (ext_opcode != GLSLstd450InterpolateAtCentroid)
+                        ? inst->GetSingleWordInOperand(3)
+                        : 0;
+
+  Instruction::OperandList new_operands;
+  new_operands.push_back({SPV_OPERAND_TYPE_ID, {glsl450_ext_inst_id}});
+  new_operands.push_back(
+      {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, {ext_opcode}});
+  new_operands.push_back({SPV_OPERAND_TYPE_ID, {ptr_id}});
+  if (op2_id != 0) new_operands.push_back({SPV_OPERAND_TYPE_ID, {op2_id}});
+
+  inst->SetInOperands(std::move(new_operands));
+  ctx->UpdateDefUse(inst);
+  return true;
+}
+
+class InterpFoldingRules : public FoldingRules {
+ public:
+  explicit InterpFoldingRules(IRContext* ctx) : FoldingRules(ctx) {}
+
+ protected:
+  virtual void AddFoldingRules() override {
+    uint32_t extension_id =
+        context()->get_feature_mgr()->GetExtInstImportId_GLSLstd450();
+
+    if (extension_id != 0) {
+      ext_rules_[{extension_id, GLSLstd450InterpolateAtCentroid}].push_back(
+          ReplaceInternalInterpolate);
+      ext_rules_[{extension_id, GLSLstd450InterpolateAtSample}].push_back(
+          ReplaceInternalInterpolate);
+      ext_rules_[{extension_id, GLSLstd450InterpolateAtOffset}].push_back(
+          ReplaceInternalInterpolate);
+    }
+  }
+};
+
+class InterpConstFoldingRules : public ConstantFoldingRules {
+ public:
+  InterpConstFoldingRules(IRContext* ctx) : ConstantFoldingRules(ctx) {}
+
+ protected:
+  virtual void AddFoldingRules() override {}
+};
+
+}  // namespace
+
+Pass::Status InterpFixupPass::Process() {
+  bool changed = false;
+
+  // Traverse the body of the functions to replace instructions that require
+  // the extensions.
+  InstructionFolder folder(
+      context(),
+      std::unique_ptr<InterpFoldingRules>(new InterpFoldingRules(context())),
+      MakeUnique<InterpConstFoldingRules>(context()));
+  for (Function& func : *get_module()) {
+    func.ForEachInst([&changed, &folder](Instruction* inst) {
+      if (folder.FoldInstruction(inst)) {
+        changed = true;
+      }
+    });
+  }
+
+  return changed ? Status::SuccessWithChange : Status::SuccessWithoutChange;
+}
+
+}  // namespace opt
+}  // namespace spvtools
diff --git a/source/opt/interp_fixup_pass.h b/source/opt/interp_fixup_pass.h
new file mode 100644
index 0000000..e112b65
--- /dev/null
+++ b/source/opt/interp_fixup_pass.h
@@ -0,0 +1,54 @@
+// Copyright (c) 2021 The Khronos Group Inc.
+// Copyright (c) 2021 Valve Corporation
+// Copyright (c) 2021 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_INTERP_FIXUP_H
+#define SOURCE_OPT_INTERP_FIXUP_H
+
+#include "source/opt/ir_context.h"
+#include "source/opt/module.h"
+#include "source/opt/pass.h"
+
+namespace spvtools {
+namespace opt {
+
+// Replaces overloaded internal form for GLSLstd450Interpolate* instructions
+// with external form. Specifically, removes OpLoad from the first argument
+// and replaces it with the pointer for the OpLoad. glslang generates the
+// internal form. This pass is called as part of glslang HLSL legalization.
+class InterpFixupPass : public Pass {
+ public:
+  const char* name() const override { return "interp-fixup"; }
+  Status Process() override;
+
+  IRContext::Analysis GetPreservedAnalyses() override {
+    return IRContext::kAnalysisInstrToBlockMapping |
+           IRContext::kAnalysisDecorations | IRContext::kAnalysisCombinators |
+           IRContext::kAnalysisCFG | IRContext::kAnalysisDominatorAnalysis |
+           IRContext::kAnalysisLoopAnalysis | IRContext::kAnalysisNameMap |
+           IRContext::kAnalysisScalarEvolution |
+           IRContext::kAnalysisRegisterPressure |
+           IRContext::kAnalysisValueNumberTable |
+           IRContext::kAnalysisStructuredCFG |
+           IRContext::kAnalysisBuiltinVarId |
+           IRContext::kAnalysisIdToFuncMapping | IRContext::kAnalysisTypes |
+           IRContext::kAnalysisDefUse | IRContext::kAnalysisConstants;
+  }
+};
+
+}  // namespace opt
+}  // namespace spvtools
+
+#endif  // SOURCE_OPT_INTERP_FIXUP_H
diff --git a/source/opt/ir_context.cpp b/source/opt/ir_context.cpp
index 82107b5..03afe6e 100644
--- a/source/opt/ir_context.cpp
+++ b/source/opt/ir_context.cpp
@@ -214,10 +214,10 @@
   return next_instruction;
 }
 
-void IRContext::KillNonSemanticInfo(Instruction* inst) {
+void IRContext::CollectNonSemanticTree(
+    Instruction* inst, std::unordered_set<Instruction*>* to_kill) {
   if (!inst->HasResultId()) return;
   std::vector<Instruction*> work_list;
-  std::vector<Instruction*> to_kill;
   std::unordered_set<Instruction*> seen;
   work_list.push_back(inst);
 
@@ -225,17 +225,13 @@
     auto* i = work_list.back();
     work_list.pop_back();
     get_def_use_mgr()->ForEachUser(
-        i, [&work_list, &to_kill, &seen](Instruction* user) {
+        i, [&work_list, to_kill, &seen](Instruction* user) {
           if (user->IsNonSemanticInstruction() && seen.insert(user).second) {
             work_list.push_back(user);
-            to_kill.push_back(user);
+            to_kill->insert(user);
           }
         });
   }
-
-  for (auto* dead : to_kill) {
-    KillInst(dead);
-  }
 }
 
 bool IRContext::KillDef(uint32_t id) {
diff --git a/source/opt/ir_context.h b/source/opt/ir_context.h
index 5aa25ac..aab3516 100644
--- a/source/opt/ir_context.h
+++ b/source/opt/ir_context.h
@@ -403,8 +403,10 @@
   // instruction exists.
   Instruction* KillInst(Instruction* inst);
 
-  // Removes the non-semantic instruction tree that uses |inst|'s result id.
-  void KillNonSemanticInfo(Instruction* inst);
+  // Collects the non-semantic instruction tree that uses |inst|'s result id
+  // to be killed later.
+  void CollectNonSemanticTree(Instruction* inst,
+                              std::unordered_set<Instruction*>* to_kill);
 
   // Returns true if all of the given analyses are valid.
   bool AreAnalysesValid(Analysis set) { return (set & valid_analyses_) == set; }
diff --git a/source/opt/iterator.h b/source/opt/iterator.h
index 444d457..2280582 100644
--- a/source/opt/iterator.h
+++ b/source/opt/iterator.h
@@ -30,18 +30,14 @@
 // std::unique_ptr managed elements in the vector, behaving like we are using
 // std::vector<|ValueType|>.
 template <typename ValueType, bool IsConst = false>
-class UptrVectorIterator
-    : public std::iterator<std::random_access_iterator_tag,
-                           typename std::conditional<IsConst, const ValueType,
-                                                     ValueType>::type> {
+class UptrVectorIterator {
  public:
-  using super = std::iterator<
-      std::random_access_iterator_tag,
-      typename std::conditional<IsConst, const ValueType, ValueType>::type>;
+  using iterator_category = std::random_access_iterator_tag;
+  using value_type = ValueType;
 
-  using pointer = typename super::pointer;
-  using reference = typename super::reference;
-  using difference_type = typename super::difference_type;
+  using pointer = value_type*;
+  using reference = value_type&;
+  using difference_type = std::ptrdiff_t;
 
   // Type aliases. We need to apply constness properly if |IsConst| is true.
   using Uptr = std::unique_ptr<ValueType>;
@@ -174,11 +170,7 @@
 //
 // Currently this iterator is always an input iterator.
 template <typename SubIterator, typename Predicate>
-class FilterIterator
-    : public std::iterator<
-          std::input_iterator_tag, typename SubIterator::value_type,
-          typename SubIterator::difference_type, typename SubIterator::pointer,
-          typename SubIterator::reference> {
+class FilterIterator {
  public:
   // Iterator interface.
   using iterator_category = typename SubIterator::iterator_category;
diff --git a/source/opt/local_access_chain_convert_pass.cpp b/source/opt/local_access_chain_convert_pass.cpp
index 205cd7a..5c10ece 100644
--- a/source/opt/local_access_chain_convert_pass.cpp
+++ b/source/opt/local_access_chain_convert_pass.cpp
@@ -418,6 +418,8 @@
       "SPV_KHR_ray_query",
       "SPV_EXT_fragment_invocation_density",
       "SPV_KHR_terminate_invocation",
+      "SPV_KHR_subgroup_uniform_control_flow",
+      "SPV_KHR_integer_dot_product",
   });
 }
 
diff --git a/source/opt/local_single_block_elim_pass.cpp b/source/opt/local_single_block_elim_pass.cpp
index 5f35ee1..05ed28a 100644
--- a/source/opt/local_single_block_elim_pass.cpp
+++ b/source/opt/local_single_block_elim_pass.cpp
@@ -270,6 +270,8 @@
       "SPV_EXT_fragment_invocation_density",
       "SPV_EXT_physical_storage_buffer",
       "SPV_KHR_terminate_invocation",
+      "SPV_KHR_subgroup_uniform_control_flow",
+      "SPV_KHR_integer_dot_product",
   });
 }
 
diff --git a/source/opt/local_single_store_elim_pass.cpp b/source/opt/local_single_store_elim_pass.cpp
index d322a2f..7eb4b1f 100644
--- a/source/opt/local_single_store_elim_pass.cpp
+++ b/source/opt/local_single_store_elim_pass.cpp
@@ -123,6 +123,8 @@
       "SPV_EXT_fragment_invocation_density",
       "SPV_EXT_physical_storage_buffer",
       "SPV_KHR_terminate_invocation",
+      "SPV_KHR_subgroup_uniform_control_flow",
+      "SPV_KHR_integer_dot_product",
   });
 }
 bool LocalSingleStoreElimPass::ProcessVariable(Instruction* var_inst) {
diff --git a/source/opt/loop_unroller.cpp b/source/opt/loop_unroller.cpp
index 6cdced4..df68bd2 100644
--- a/source/opt/loop_unroller.cpp
+++ b/source/opt/loop_unroller.cpp
@@ -797,6 +797,9 @@
   for (BasicBlock* block : loop_blocks_inorder_) {
     RemapOperands(block);
   }
+  for (auto& block_itr : blocks_to_add_) {
+    RemapOperands(block_itr.get());
+  }
 
   // Rewrite the last phis, since they may still reference the original phi.
   for (Instruction* last_phi : state_.previous_phis_) {
diff --git a/source/opt/mem_pass.h b/source/opt/mem_pass.h
index dcc16b6..5a77670 100644
--- a/source/opt/mem_pass.h
+++ b/source/opt/mem_pass.h
@@ -38,7 +38,7 @@
 // utility functions and supporting state.
 class MemPass : public Pass {
  public:
-  virtual ~MemPass() = default;
+  virtual ~MemPass() override = default;
 
   // Returns an undef value for the given |var_id|'s type.
   uint32_t GetUndefVal(uint32_t var_id) {
diff --git a/source/opt/optimizer.cpp b/source/opt/optimizer.cpp
index 909442c..a5d10c3 100644
--- a/source/opt/optimizer.cpp
+++ b/source/opt/optimizer.cpp
@@ -155,7 +155,8 @@
           .RegisterPass(CreateVectorDCEPass())
           .RegisterPass(CreateDeadInsertElimPass())
           .RegisterPass(CreateReduceLoadSizePass())
-          .RegisterPass(CreateAggressiveDCEPass());
+          .RegisterPass(CreateAggressiveDCEPass())
+          .RegisterPass(CreateInterpolateFixupPass());
 }
 
 Optimizer& Optimizer::RegisterPerformancePasses() {
@@ -494,6 +495,8 @@
     RegisterPass(CreateWrapOpKillPass());
   } else if (pass_name == "amd-ext-to-khr") {
     RegisterPass(CreateAmdExtToKhrPass());
+  } else if (pass_name == "interpolate-fixup") {
+    RegisterPass(CreateInterpolateFixupPass());
   } else {
     Errorf(consumer(), nullptr, {},
            "Unknown flag '--%s'. Use --help for a list of valid flags",
@@ -925,4 +928,9 @@
       MakeUnique<opt::AmdExtensionToKhrPass>());
 }
 
+Optimizer::PassToken CreateInterpolateFixupPass() {
+  return MakeUnique<Optimizer::PassToken::Impl>(
+      MakeUnique<opt::InterpFixupPass>());
+}
+
 }  // namespace spvtools
diff --git a/source/opt/passes.h b/source/opt/passes.h
index 1bc94c7..bfb34af 100644
--- a/source/opt/passes.h
+++ b/source/opt/passes.h
@@ -46,6 +46,7 @@
 #include "source/opt/inst_bindless_check_pass.h"
 #include "source/opt/inst_buff_addr_check_pass.h"
 #include "source/opt/inst_debug_printf_pass.h"
+#include "source/opt/interp_fixup_pass.h"
 #include "source/opt/licm_pass.h"
 #include "source/opt/local_access_chain_convert_pass.h"
 #include "source/opt/local_redundancy_elimination.h"
diff --git a/source/opt/scalar_replacement_pass.cpp b/source/opt/scalar_replacement_pass.cpp
index ba2d067..4d47bdd 100644
--- a/source/opt/scalar_replacement_pass.cpp
+++ b/source/opt/scalar_replacement_pass.cpp
@@ -861,6 +861,9 @@
           case SpvOpStore:
             if (!CheckStore(user, index)) ok = false;
             break;
+          case SpvOpImageTexelPointer:
+            if (!CheckImageTexelPointer(index)) ok = false;
+            break;
           default:
             ok = false;
             break;
@@ -870,6 +873,10 @@
   return ok;
 }
 
+bool ScalarReplacementPass::CheckImageTexelPointer(uint32_t index) const {
+  return index == 2u;
+}
+
 bool ScalarReplacementPass::CheckLoad(const Instruction* inst,
                                       uint32_t index) const {
   if (index != 2u) return false;
diff --git a/source/opt/scalar_replacement_pass.h b/source/opt/scalar_replacement_pass.h
index 1f6c928..9e9f073 100644
--- a/source/opt/scalar_replacement_pass.h
+++ b/source/opt/scalar_replacement_pass.h
@@ -142,6 +142,10 @@
   // of |inst| and the store is not to volatile memory.
   bool CheckStore(const Instruction* inst, uint32_t index) const;
 
+  // Returns true if |index| is the pointer operand of an OpImageTexelPointer
+  // instruction.
+  bool CheckImageTexelPointer(uint32_t index) const;
+
   // Creates a variable of type |typeId| from the |index|'th element of
   // |varInst|. The new variable is added to |replacements|.  If the variable
   // could not be created, then |nullptr| is appended to |replacements|.
diff --git a/source/opt/type_manager.cpp b/source/opt/type_manager.cpp
index ce9c2c1..7935ad3 100644
--- a/source/opt/type_manager.cpp
+++ b/source/opt/type_manager.cpp
@@ -223,7 +223,7 @@
   case Type::k##kind:                                                     \
     typeInst = MakeUnique<Instruction>(context(), SpvOpType##kind, 0, id, \
                                        std::initializer_list<Operand>{}); \
-    break;
+    break
     DefineParameterlessCase(Void);
     DefineParameterlessCase(Bool);
     DefineParameterlessCase(Sampler);
@@ -513,7 +513,7 @@
 #define DefineNoSubtypeCase(kind)             \
   case Type::k##kind:                         \
     rebuilt_ty.reset(type.Clone().release()); \
-    return type_pool_.insert(std::move(rebuilt_ty)).first->get();
+    return type_pool_.insert(std::move(rebuilt_ty)).first->get()
 
     DefineNoSubtypeCase(Void);
     DefineNoSubtypeCase(Bool);
diff --git a/source/opt/types.h b/source/opt/types.h
index d5be9be..9ecd41a 100644
--- a/source/opt/types.h
+++ b/source/opt/types.h
@@ -101,7 +101,7 @@
 
   Type(Kind k) : kind_(k) {}
 
-  virtual ~Type() {}
+  virtual ~Type() = default;
 
   // Attaches a decoration directly on this type.
   void AddDecoration(std::vector<uint32_t>&& d) {
diff --git a/source/reduce/change_operand_reduction_opportunity.cpp b/source/reduce/change_operand_reduction_opportunity.cpp
index c3f6fd7..18e340b 100644
--- a/source/reduce/change_operand_reduction_opportunity.cpp
+++ b/source/reduce/change_operand_reduction_opportunity.cpp
@@ -14,6 +14,8 @@
 
 #include "source/reduce/change_operand_reduction_opportunity.h"
 
+#include "source/opt/ir_context.h"
+
 namespace spvtools {
 namespace reduce {
 
@@ -26,6 +28,7 @@
 
 void ChangeOperandReductionOpportunity::Apply() {
   inst_->SetOperand(operand_index_, {new_id_});
+  inst_->context()->get_def_use_mgr()->UpdateDefUse(inst_);
 }
 
 }  // namespace reduce
diff --git a/source/reduce/change_operand_to_undef_reduction_opportunity.cpp b/source/reduce/change_operand_to_undef_reduction_opportunity.cpp
index 8e33da6..7cc06a0 100644
--- a/source/reduce/change_operand_to_undef_reduction_opportunity.cpp
+++ b/source/reduce/change_operand_to_undef_reduction_opportunity.cpp
@@ -35,6 +35,7 @@
   assert(operand_type_id);
   auto undef_id = FindOrCreateGlobalUndef(context_, operand_type_id);
   inst_->SetOperand(operand_index_, {undef_id});
+  context_->InvalidateAnalyses(opt::IRContext::kAnalysisDefUse);
 }
 
 }  // namespace reduce
diff --git a/source/reduce/reducer.cpp b/source/reduce/reducer.cpp
index 18eeaeb..16bb94f 100644
--- a/source/reduce/reducer.cpp
+++ b/source/reduce/reducer.cpp
@@ -54,10 +54,10 @@
 }
 
 Reducer::ReductionResultStatus Reducer::Run(
-    std::vector<uint32_t>&& binary_in, std::vector<uint32_t>* binary_out,
+    const std::vector<uint32_t>& binary_in, std::vector<uint32_t>* binary_out,
     spv_const_reducer_options options,
     spv_validator_options validator_options) {
-  std::vector<uint32_t> current_binary(std::move(binary_in));
+  std::vector<uint32_t> current_binary(binary_in);
 
   spvtools::SpirvTools tools(target_env_);
   assert(tools.IsValid() && "Failed to create SPIRV-Tools interface");
@@ -138,13 +138,13 @@
 }
 
 void Reducer::AddReductionPass(
-    std::unique_ptr<ReductionOpportunityFinder>&& finder) {
+    std::unique_ptr<ReductionOpportunityFinder> finder) {
   passes_.push_back(
       spvtools::MakeUnique<ReductionPass>(target_env_, std::move(finder)));
-}
+} 
 
 void Reducer::AddCleanupReductionPass(
-    std::unique_ptr<ReductionOpportunityFinder>&& finder) {
+    std::unique_ptr<ReductionOpportunityFinder> finder) {
   cleanup_passes_.push_back(
       spvtools::MakeUnique<ReductionPass>(target_env_, std::move(finder)));
 }
diff --git a/source/reduce/reducer.h b/source/reduce/reducer.h
index 864ce75..f3ba180 100644
--- a/source/reduce/reducer.h
+++ b/source/reduce/reducer.h
@@ -84,17 +84,17 @@
 
   // Adds a reduction pass based on the given finder to the sequence of passes
   // that will be iterated over.
-  void AddReductionPass(std::unique_ptr<ReductionOpportunityFinder>&& finder);
+  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);
+      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,
+  ReductionResultStatus Run(const std::vector<uint32_t>& binary_in,
                             std::vector<uint32_t>* binary_out,
                             spv_const_reducer_options options,
                             spv_validator_options validator_options);
diff --git a/source/reduce/remove_block_reduction_opportunity.cpp b/source/reduce/remove_block_reduction_opportunity.cpp
index aa48105..55e9576 100644
--- a/source/reduce/remove_block_reduction_opportunity.cpp
+++ b/source/reduce/remove_block_reduction_opportunity.cpp
@@ -20,12 +20,11 @@
 namespace reduce {
 
 RemoveBlockReductionOpportunity::RemoveBlockReductionOpportunity(
-    opt::Function* function, opt::BasicBlock* block)
-    : function_(function), block_(block) {
+    opt::IRContext* context, opt::Function* function, opt::BasicBlock* block)
+    : context_(context), function_(function), block_(block) {
   // precondition:
   assert(block_->begin() != block_->end() &&
-         block_->begin()->context()->get_def_use_mgr()->NumUsers(
-             block_->id()) == 0 &&
+         context_->get_def_use_mgr()->NumUsers(block_->id()) == 0 &&
          "RemoveBlockReductionOpportunity block must have 0 references");
 }
 
@@ -38,10 +37,8 @@
   // We need an iterator pointing to the block, hence the loop.
   for (auto bi = function_->begin(); bi != function_->end(); ++bi) {
     if (bi->id() == block_->id()) {
-      bi->KillAllInsts(true);
       bi.Erase();
-      // Block removal changes the function, but we don't use analyses, so no
-      // need to invalidate them.
+      context_->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
       return;
     }
   }
diff --git a/source/reduce/remove_block_reduction_opportunity.h b/source/reduce/remove_block_reduction_opportunity.h
index 4b358ab..03fede5 100644
--- a/source/reduce/remove_block_reduction_opportunity.h
+++ b/source/reduce/remove_block_reduction_opportunity.h
@@ -27,7 +27,8 @@
 class RemoveBlockReductionOpportunity : public ReductionOpportunity {
  public:
   // Creates the opportunity to remove |block| in |function| in |context|.
-  RemoveBlockReductionOpportunity(opt::Function* function,
+  RemoveBlockReductionOpportunity(opt::IRContext* context,
+                                  opt::Function* function,
                                   opt::BasicBlock* block);
 
   bool PreconditionHolds() override;
@@ -36,6 +37,7 @@
   void Apply() override;
 
  private:
+  opt::IRContext* context_;
   opt::Function* function_;
   opt::BasicBlock* block_;
 };
diff --git a/source/reduce/remove_block_reduction_opportunity_finder.cpp b/source/reduce/remove_block_reduction_opportunity_finder.cpp
index 27a4570..3b13728 100644
--- a/source/reduce/remove_block_reduction_opportunity_finder.cpp
+++ b/source/reduce/remove_block_reduction_opportunity_finder.cpp
@@ -32,8 +32,8 @@
   for (auto* function : GetTargetFunctions(context, target_function)) {
     for (auto bi = function->begin(); bi != function->end(); ++bi) {
       if (IsBlockValidOpportunity(context, function, &bi)) {
-        result.push_back(
-            MakeUnique<RemoveBlockReductionOpportunity>(function, &*bi));
+        result.push_back(MakeUnique<RemoveBlockReductionOpportunity>(
+            context, function, &*bi));
       }
     }
   }
diff --git a/source/reduce/remove_function_reduction_opportunity.cpp b/source/reduce/remove_function_reduction_opportunity.cpp
index ecad670..4b85058 100644
--- a/source/reduce/remove_function_reduction_opportunity.cpp
+++ b/source/reduce/remove_function_reduction_opportunity.cpp
@@ -29,8 +29,8 @@
   for (opt::Module::iterator function_it = context_->module()->begin();
        function_it != context_->module()->end(); ++function_it) {
     if (&*function_it == function_) {
-      opt::eliminatedeadfunctionsutil::EliminateFunction(context_,
-                                                         &function_it);
+      function_it.Erase();
+      context_->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
       return;
     }
   }
diff --git a/source/reduce/remove_struct_member_reduction_opportunity.cpp b/source/reduce/remove_struct_member_reduction_opportunity.cpp
index 787c629..da096e1 100644
--- a/source/reduce/remove_struct_member_reduction_opportunity.cpp
+++ b/source/reduce/remove_struct_member_reduction_opportunity.cpp
@@ -129,6 +129,8 @@
 
   // Remove the member from the struct type.
   struct_type_->RemoveInOperand(member_index_);
+
+  context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
 }
 
 void RemoveStructMemberReductionOpportunity::AdjustAccessedIndices(
diff --git a/source/reduce/simple_conditional_branch_to_branch_reduction_opportunity.cpp b/source/reduce/simple_conditional_branch_to_branch_reduction_opportunity.cpp
index ca17f9e..a2be0c4 100644
--- a/source/reduce/simple_conditional_branch_to_branch_reduction_opportunity.cpp
+++ b/source/reduce/simple_conditional_branch_to_branch_reduction_opportunity.cpp
@@ -51,6 +51,8 @@
       {{SPV_OPERAND_TYPE_ID,
         {conditional_branch_instruction_->GetSingleWordInOperand(
             kTrueBranchOperandIndex)}}});
+  conditional_branch_instruction_->context()->InvalidateAnalysesExceptFor(
+      opt::IRContext::kAnalysisNone);
 }
 
 }  // namespace reduce
diff --git a/source/val/basic_block.h b/source/val/basic_block.h
index 5eea4f9..5af4b9e 100644
--- a/source/val/basic_block.h
+++ b/source/val/basic_block.h
@@ -139,9 +139,14 @@
   /// @brief A BasicBlock dominator iterator class
   ///
   /// This iterator will iterate over the (post)dominators of the block
-  class DominatorIterator
-      : public std::iterator<std::forward_iterator_tag, BasicBlock*> {
+  class DominatorIterator {
    public:
+    using iterator_category = std::forward_iterator_tag;
+    using value_type = BasicBlock*;
+    using pointer = value_type*;
+    using reference = value_type&;
+    using difference_type = std::ptrdiff_t;
+
     /// @brief Constructs the end of dominator iterator
     ///
     /// This will create an iterator which will represent the element
diff --git a/source/val/construct.cpp b/source/val/construct.cpp
index 733856c..5300869 100644
--- a/source/val/construct.cpp
+++ b/source/val/construct.cpp
@@ -78,7 +78,12 @@
   ConstructBlockSet construct_blocks;
   std::unordered_set<BasicBlock*> corresponding_headers;
   for (auto& other : corresponding_constructs()) {
-    corresponding_headers.insert(other->entry_block());
+    // The corresponding header can be the same block as this construct's
+    // header for loops with no loop construct. In those cases, don't add the
+    // loop header as it prevents finding any blocks in the construct.
+    if (type() != ConstructType::kContinue || other->entry_block() != header) {
+      corresponding_headers.insert(other->entry_block());
+    }
   }
   std::vector<BasicBlock*> stack;
   stack.push_back(const_cast<BasicBlock*>(header));
diff --git a/source/val/validate.cpp b/source/val/validate.cpp
index a2e116b..45b6a46 100644
--- a/source/val/validate.cpp
+++ b/source/val/validate.cpp
@@ -143,6 +143,7 @@
       if (_.recursive_entry_points().find(entry_point) !=
           _.recursive_entry_points().end()) {
         return _.diag(SPV_ERROR_INVALID_BINARY, _.FindDef(entry_point))
+               << _.VkErrorID(4634)
                << "Entry points may not have a call graph with cycles.";
       }
     }
diff --git a/source/val/validate_atomics.cpp b/source/val/validate_atomics.cpp
index dd263a7..fa53ca1 100644
--- a/source/val/validate_atomics.cpp
+++ b/source/val/validate_atomics.cpp
@@ -47,25 +47,31 @@
   }
 }
 
-}  // namespace
-
-namespace spvtools {
-namespace val {
-
-// Validates correctness of atomic instructions.
-spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) {
-  const SpvOp opcode = inst->opcode();
-  const uint32_t result_type = inst->type_id();
-  bool is_atomic_float_opcode = false;
-  if (opcode == SpvOpAtomicLoad || opcode == SpvOpAtomicStore ||
-      opcode == SpvOpAtomicFAddEXT || opcode == SpvOpAtomicExchange) {
-    is_atomic_float_opcode = true;
-  }
+bool HasReturnType(uint32_t opcode) {
   switch (opcode) {
-    case SpvOpAtomicLoad:
     case SpvOpAtomicStore:
-    case SpvOpAtomicExchange:
+    case SpvOpAtomicFlagClear:
+      return false;
+      break;
+    default:
+      return true;
+  }
+}
+
+bool HasOnlyFloatReturnType(uint32_t opcode) {
+  switch (opcode) {
     case SpvOpAtomicFAddEXT:
+    case SpvOpAtomicFMinEXT:
+    case SpvOpAtomicFMaxEXT:
+      return true;
+      break;
+    default:
+      return false;
+  }
+}
+
+bool HasOnlyIntReturnType(uint32_t opcode) {
+  switch (opcode) {
     case SpvOpAtomicCompareExchange:
     case SpvOpAtomicCompareExchangeWeak:
     case SpvOpAtomicIIncrement:
@@ -79,123 +85,96 @@
     case SpvOpAtomicAnd:
     case SpvOpAtomicOr:
     case SpvOpAtomicXor:
+      return true;
+      break;
+    default:
+      return false;
+  }
+}
+
+bool HasIntOrFloatReturnType(uint32_t opcode) {
+  switch (opcode) {
+    case SpvOpAtomicLoad:
+    case SpvOpAtomicExchange:
+      return true;
+      break;
+    default:
+      return false;
+  }
+}
+
+bool HasOnlyBoolReturnType(uint32_t opcode) {
+  switch (opcode) {
+    case SpvOpAtomicFlagTestAndSet:
+      return true;
+      break;
+    default:
+      return false;
+  }
+}
+
+}  // namespace
+
+namespace spvtools {
+namespace val {
+
+// Validates correctness of atomic instructions.
+spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) {
+  const SpvOp opcode = inst->opcode();
+  switch (opcode) {
+    case SpvOpAtomicLoad:
+    case SpvOpAtomicStore:
+    case SpvOpAtomicExchange:
+    case SpvOpAtomicFAddEXT:
+    case SpvOpAtomicCompareExchange:
+    case SpvOpAtomicCompareExchangeWeak:
+    case SpvOpAtomicIIncrement:
+    case SpvOpAtomicIDecrement:
+    case SpvOpAtomicIAdd:
+    case SpvOpAtomicISub:
+    case SpvOpAtomicSMin:
+    case SpvOpAtomicUMin:
+    case SpvOpAtomicFMinEXT:
+    case SpvOpAtomicSMax:
+    case SpvOpAtomicUMax:
+    case SpvOpAtomicFMaxEXT:
+    case SpvOpAtomicAnd:
+    case SpvOpAtomicOr:
+    case SpvOpAtomicXor:
     case SpvOpAtomicFlagTestAndSet:
     case SpvOpAtomicFlagClear: {
-      if (_.HasCapability(SpvCapabilityKernel) &&
-          (opcode == SpvOpAtomicLoad || opcode == SpvOpAtomicExchange ||
-           opcode == SpvOpAtomicCompareExchange)) {
-        if (!_.IsFloatScalarType(result_type) &&
-            !_.IsIntScalarType(result_type)) {
+      const uint32_t result_type = inst->type_id();
+
+      // All current atomics only are scalar result
+      // Validate return type first so can just check if pointer type is same
+      // (if applicable)
+      if (HasReturnType(opcode)) {
+        if (HasOnlyFloatReturnType(opcode) &&
+            !_.IsFloatScalarType(result_type)) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
                  << spvOpcodeString(opcode)
-                 << ": expected Result Type to be int or float scalar type";
-        }
-      } else if (opcode == SpvOpAtomicFlagTestAndSet) {
-        if (!_.IsBoolScalarType(result_type)) {
+                 << ": expected Result Type to be float scalar type";
+        } else if (HasOnlyIntReturnType(opcode) &&
+                   !_.IsIntScalarType(result_type)) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << spvOpcodeString(opcode)
+                 << ": expected Result Type to be integer scalar type";
+        } else if (HasIntOrFloatReturnType(opcode) &&
+                   !_.IsFloatScalarType(result_type) &&
+                   !_.IsIntScalarType(result_type)) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << spvOpcodeString(opcode)
+                 << ": expected Result Type to be integer or float scalar type";
+        } else if (HasOnlyBoolReturnType(opcode) &&
+                   !_.IsBoolScalarType(result_type)) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
                  << spvOpcodeString(opcode)
                  << ": expected Result Type to be bool scalar type";
         }
-      } else if (opcode == SpvOpAtomicFlagClear || opcode == SpvOpAtomicStore) {
-        assert(result_type == 0);
-      } else {
-        if (_.IsFloatScalarType(result_type)) {
-          if (is_atomic_float_opcode) {
-            if (opcode == SpvOpAtomicFAddEXT) {
-              if ((_.GetBitWidth(result_type) == 32) &&
-                  (!_.HasCapability(SpvCapabilityAtomicFloat32AddEXT))) {
-                return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                       << spvOpcodeString(opcode)
-                       << ": float add atomics require the AtomicFloat32AddEXT "
-                          "capability";
-              }
-              if ((_.GetBitWidth(result_type) == 64) &&
-                  (!_.HasCapability(SpvCapabilityAtomicFloat64AddEXT))) {
-                return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                       << spvOpcodeString(opcode)
-                       << ": float add atomics require the AtomicFloat64AddEXT "
-                          "capability";
-              }
-            }
-          } else {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << spvOpcodeString(opcode)
-                   << ": expected Result Type to be int scalar type";
-          }
-        } else if (_.IsIntScalarType(result_type) &&
-                   opcode == SpvOpAtomicFAddEXT) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << spvOpcodeString(opcode)
-                 << ": expected Result Type to be float scalar type";
-        } else if (!_.IsFloatScalarType(result_type) &&
-                   !_.IsIntScalarType(result_type)) {
-          switch (opcode) {
-            case SpvOpAtomicFAddEXT:
-              return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                     << spvOpcodeString(opcode)
-                     << ": expected Result Type to be float scalar type";
-            case SpvOpAtomicIIncrement:
-            case SpvOpAtomicIDecrement:
-            case SpvOpAtomicIAdd:
-            case SpvOpAtomicISub:
-            case SpvOpAtomicSMin:
-            case SpvOpAtomicSMax:
-            case SpvOpAtomicUMin:
-            case SpvOpAtomicUMax:
-              return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                     << spvOpcodeString(opcode)
-                     << ": expected Result Type to be integer scalar type";
-            default:
-              return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                     << spvOpcodeString(opcode)
-                     << ": expected Result Type to be int or float scalar type";
-          }
-        }
-
-        if (spvIsVulkanEnv(_.context()->target_env) &&
-            (_.GetBitWidth(result_type) != 32 &&
-             (_.GetBitWidth(result_type) != 64 ||
-              !_.HasCapability(SpvCapabilityInt64ImageEXT)))) {
-          switch (opcode) {
-            case SpvOpAtomicSMin:
-            case SpvOpAtomicUMin:
-            case SpvOpAtomicSMax:
-            case SpvOpAtomicUMax:
-            case SpvOpAtomicAnd:
-            case SpvOpAtomicOr:
-            case SpvOpAtomicXor:
-            case SpvOpAtomicIAdd:
-            case SpvOpAtomicISub:
-            case SpvOpAtomicFAddEXT:
-            case SpvOpAtomicLoad:
-            case SpvOpAtomicStore:
-            case SpvOpAtomicExchange:
-            case SpvOpAtomicIIncrement:
-            case SpvOpAtomicIDecrement:
-            case SpvOpAtomicCompareExchangeWeak:
-            case SpvOpAtomicCompareExchange: {
-              if (_.GetBitWidth(result_type) == 64 &&
-                  _.IsIntScalarType(result_type) &&
-                  !_.HasCapability(SpvCapabilityInt64Atomics))
-                return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                       << spvOpcodeString(opcode)
-                       << ": 64-bit atomics require the Int64Atomics "
-                          "capability";
-            } break;
-            default:
-              return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                     << spvOpcodeString(opcode)
-                     << ": according to the Vulkan spec atomic Result Type "
-                        "needs "
-                        "to be a 32-bit int scalar type";
-          }
-        }
       }
 
-      uint32_t operand_index =
-          opcode == SpvOpAtomicFlagClear || opcode == SpvOpAtomicStore ? 0 : 2;
+      uint32_t operand_index = HasReturnType(opcode) ? 2 : 0;
       const uint32_t pointer_type = _.GetOperandTypeId(inst, operand_index++);
-
       uint32_t data_type = 0;
       uint32_t storage_class = 0;
       if (!_.GetPointerTypeInfo(pointer_type, &data_type, &storage_class)) {
@@ -204,6 +183,14 @@
                << ": expected Pointer to be of type OpTypePointer";
       }
 
+      // Can't use result_type because OpAtomicStore doesn't have a result
+      if (_.GetBitWidth(data_type) == 64 && _.IsIntScalarType(data_type) &&
+          !_.HasCapability(SpvCapabilityInt64Atomics)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << spvOpcodeString(opcode)
+               << ": 64-bit atomics require the Int64Atomics capability";
+      }
+
       // Validate storage class against universal rules
       if (!IsStorageClassAllowedByUniversalRules(storage_class)) {
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
@@ -213,6 +200,7 @@
 
       // Then Shader rules
       if (_.HasCapability(SpvCapabilityShader)) {
+        // Vulkan environment rule
         if (spvIsVulkanEnv(_.context()->target_env)) {
           if ((storage_class != SpvStorageClassUniform) &&
               (storage_class != SpvStorageClassStorageBuffer) &&
@@ -231,6 +219,47 @@
                  << ": Function storage class forbidden when the Shader "
                     "capability is declared.";
         }
+
+        if (opcode == SpvOpAtomicFAddEXT) {
+          // result type being float checked already
+          if ((_.GetBitWidth(result_type) == 32) &&
+              (!_.HasCapability(SpvCapabilityAtomicFloat32AddEXT))) {
+            return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                   << spvOpcodeString(opcode)
+                   << ": float add atomics require the AtomicFloat32AddEXT "
+                      "capability";
+          }
+          if ((_.GetBitWidth(result_type) == 64) &&
+              (!_.HasCapability(SpvCapabilityAtomicFloat64AddEXT))) {
+            return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                   << spvOpcodeString(opcode)
+                   << ": float add atomics require the AtomicFloat64AddEXT "
+                      "capability";
+          }
+        } else if (opcode == SpvOpAtomicFMinEXT ||
+                   opcode == SpvOpAtomicFMaxEXT) {
+          if ((_.GetBitWidth(result_type) == 16) &&
+              (!_.HasCapability(SpvCapabilityAtomicFloat16MinMaxEXT))) {
+            return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                   << spvOpcodeString(opcode)
+                   << ": float min/max atomics require the "
+                      "AtomicFloat16MinMaxEXT capability";
+          }
+          if ((_.GetBitWidth(result_type) == 32) &&
+              (!_.HasCapability(SpvCapabilityAtomicFloat32MinMaxEXT))) {
+            return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                   << spvOpcodeString(opcode)
+                   << ": float min/max atomics require the "
+                      "AtomicFloat32MinMaxEXT capability";
+          }
+          if ((_.GetBitWidth(result_type) == 64) &&
+              (!_.HasCapability(SpvCapabilityAtomicFloat64MinMaxEXT))) {
+            return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                   << spvOpcodeString(opcode)
+                   << ": float min/max atomics require the "
+                      "AtomicFloat64MinMaxEXT capability";
+          }
+        }
       }
 
       // And finally OpenCL environment rules
@@ -254,27 +283,27 @@
         }
       }
 
+      // If result and pointer type are different, need to do special check here
       if (opcode == SpvOpAtomicFlagTestAndSet ||
           opcode == SpvOpAtomicFlagClear) {
         if (!_.IsIntScalarType(data_type) || _.GetBitWidth(data_type) != 32) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
                  << spvOpcodeString(opcode)
-                 << ": expected Pointer to point to a value of 32-bit int type";
+                 << ": expected Pointer to point to a value of 32-bit integer "
+                    "type";
         }
       } else if (opcode == SpvOpAtomicStore) {
         if (!_.IsFloatScalarType(data_type) && !_.IsIntScalarType(data_type)) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
                  << spvOpcodeString(opcode)
-                 << ": expected Pointer to be a pointer to int or float "
+                 << ": expected Pointer to be a pointer to integer or float "
                  << "scalar type";
         }
-      } else {
-        if (data_type != result_type) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << spvOpcodeString(opcode)
-                 << ": expected Pointer to point to a value of type Result "
-                    "Type";
-        }
+      } else if (data_type != result_type) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << spvOpcodeString(opcode)
+               << ": expected Pointer to point to a value of type Result "
+                  "Type";
       }
 
       auto memory_scope = inst->GetOperandAs<const uint32_t>(operand_index++);
@@ -283,14 +312,15 @@
       }
 
       const auto equal_semantics_index = operand_index++;
-      if (auto error = ValidateMemorySemantics(_, inst, equal_semantics_index))
+      if (auto error = ValidateMemorySemantics(_, inst, equal_semantics_index,
+                                               memory_scope))
         return error;
 
       if (opcode == SpvOpAtomicCompareExchange ||
           opcode == SpvOpAtomicCompareExchangeWeak) {
         const auto unequal_semantics_index = operand_index++;
-        if (auto error =
-                ValidateMemorySemantics(_, inst, unequal_semantics_index))
+        if (auto error = ValidateMemorySemantics(
+                _, inst, unequal_semantics_index, memory_scope))
           return error;
 
         // Volatile bits must match for equal and unequal semantics. Previous
diff --git a/source/val/validate_barriers.cpp b/source/val/validate_barriers.cpp
index b499c8c..3a9e3e7 100644
--- a/source/val/validate_barriers.cpp
+++ b/source/val/validate_barriers.cpp
@@ -69,7 +69,7 @@
         return error;
       }
 
-      if (auto error = ValidateMemorySemantics(_, inst, 2)) {
+      if (auto error = ValidateMemorySemantics(_, inst, 2, memory_scope)) {
         return error;
       }
       break;
@@ -82,7 +82,7 @@
         return error;
       }
 
-      if (auto error = ValidateMemorySemantics(_, inst, 1)) {
+      if (auto error = ValidateMemorySemantics(_, inst, 1, memory_scope)) {
         return error;
       }
       break;
@@ -119,7 +119,7 @@
         return error;
       }
 
-      if (auto error = ValidateMemorySemantics(_, inst, 2)) {
+      if (auto error = ValidateMemorySemantics(_, inst, 2, memory_scope)) {
         return error;
       }
       break;
diff --git a/source/val/validate_cfg.cpp b/source/val/validate_cfg.cpp
index a5f6e6a..36f632a 100644
--- a/source/val/validate_cfg.cpp
+++ b/source/val/validate_cfg.cpp
@@ -654,18 +654,15 @@
                << "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.
+      if (!merge) {
+        return _.diag(SPV_ERROR_INVALID_CFG, terminator)
+               << "OpSwitch must be preceeded by an OpSelectionMerge "
+                  "instruction";
+      }
+      // Mark the targets as seen.
       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";
+        seen.insert(target);
       }
     }
   }
diff --git a/source/val/validate_decorations.cpp b/source/val/validate_decorations.cpp
index ed336b4..f076b04 100644
--- a/source/val/validate_decorations.cpp
+++ b/source/val/validate_decorations.cpp
@@ -1619,7 +1619,7 @@
   {                                             \
     spv_result_t e##LINE = (X);                 \
     if (e##LINE != SPV_SUCCESS) return e##LINE; \
-  }
+  } static_assert(true, "require extra semicolon")
 #define PASS_OR_BAIL(X) PASS_OR_BAIL_AT_LINE(X, __LINE__)
 
 // Check rules for decorations where we start from the decoration rather
diff --git a/source/val/validate_extensions.cpp b/source/val/validate_extensions.cpp
index dc8c024..a7167fc 100644
--- a/source/val/validate_extensions.cpp
+++ b/source/val/validate_extensions.cpp
@@ -692,8 +692,8 @@
     if (extension ==
         ExtensionToString(kSPV_KHR_workgroup_memory_explicit_layout)) {
       return _.diag(SPV_ERROR_WRONG_VERSION, inst)
-          << "SPV_KHR_workgroup_memory_explicit_layout extension "
-             "requires SPIR-V version 1.4 or later.";
+             << "SPV_KHR_workgroup_memory_explicit_layout extension "
+                "requires SPIR-V version 1.4 or later.";
     }
   }
 
@@ -1372,7 +1372,16 @@
                  << "or vector type";
         }
 
-        const uint32_t interpolant_type = _.GetOperandTypeId(inst, 4);
+        // If HLSL legalization and first operand is an OpLoad, use load
+        // pointer as the interpolant lvalue. Else use interpolate first
+        // operand.
+        uint32_t interp_id = inst->GetOperandAs<uint32_t>(4);
+        auto* interp_inst = _.FindDef(interp_id);
+        uint32_t interpolant_type = (_.options()->before_hlsl_legalization &&
+                                     interp_inst->opcode() == SpvOpLoad)
+                                        ? _.GetOperandTypeId(interp_inst, 2)
+                                        : _.GetOperandTypeId(inst, 4);
+
         uint32_t interpolant_storage_class = 0;
         uint32_t interpolant_data_type = 0;
         if (!_.GetPointerTypeInfo(interpolant_type, &interpolant_data_type,
diff --git a/source/val/validate_image.cpp b/source/val/validate_image.cpp
index f7ddec7..e5968d0 100644
--- a/source/val/validate_image.cpp
+++ b/source/val/validate_image.cpp
@@ -442,7 +442,8 @@
              << " components, but given " << offset_size;
     }
 
-    if (spvIsVulkanEnv(_.context()->target_env)) {
+    if (!_.options()->before_hlsl_legalization &&
+        spvIsVulkanEnv(_.context()->target_env)) {
       if (opcode != SpvOpImageGather && opcode != SpvOpImageDrefGather &&
           opcode != SpvOpImageSparseGather &&
           opcode != SpvOpImageSparseDrefGather) {
@@ -1457,12 +1458,21 @@
   }
 
   if (opcode == SpvOpImageGather || opcode == SpvOpImageSparseGather) {
-    const uint32_t component_index_type = _.GetOperandTypeId(inst, 4);
+    const uint32_t component = inst->GetOperandAs<uint32_t>(4);
+    const uint32_t component_index_type = _.GetTypeId(component);
     if (!_.IsIntScalarType(component_index_type) ||
         _.GetBitWidth(component_index_type) != 32) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Expected Component to be 32-bit int scalar";
     }
+    if (spvIsVulkanEnv(_.context()->target_env)) {
+      if (!spvOpcodeIsConstant(_.GetIdOpcode(component))) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << _.VkErrorID(4664)
+               << "Expected Component Operand to be a const object for Vulkan "
+                  "environment";
+      }
+    }
   } else {
     assert(opcode == SpvOpImageDrefGather ||
            opcode == SpvOpImageSparseDrefGather);
@@ -1500,8 +1510,8 @@
   if (spvIsVulkanEnv(target_env)) {
     if (_.GetDimension(actual_result_type) != 4) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
-             << "Expected " << GetActualResultTypeStr(opcode)
-             << " to have 4 components";
+             << _.VkErrorID(4780) << "Expected "
+             << GetActualResultTypeStr(opcode) << " to have 4 components";
     }
   }  // Check OpenCL below, after we get the image info.
 
diff --git a/source/val/validate_logicals.cpp b/source/val/validate_logicals.cpp
index 5886dbf..bb35f55 100644
--- a/source/val/validate_logicals.cpp
+++ b/source/val/validate_logicals.cpp
@@ -188,7 +188,7 @@
           case SpvOpTypeStruct: {
             if (!composites) return fail();
             break;
-          };
+          }
 
           default:
             return fail();
diff --git a/source/val/validate_memory.cpp b/source/val/validate_memory.cpp
index 2318c96..a4bc0fa 100644
--- a/source/val/validate_memory.cpp
+++ b/source/val/validate_memory.cpp
@@ -466,6 +466,7 @@
 
   if (!_.IsValidStorageClass(storage_class)) {
     return _.diag(SPV_ERROR_INVALID_BINARY, inst)
+           << _.VkErrorID(4643)
            << "Invalid storage class for target environment";
   }
 
@@ -536,18 +537,14 @@
       if (!IsAllowedTypeOrArrayOfSame(
               _, pointee,
               {SpvOpTypeImage, SpvOpTypeSampler, SpvOpTypeSampledImage,
-               SpvOpTypeAccelerationStructureNV,
-               SpvOpTypeAccelerationStructureKHR, SpvOpTypeRayQueryKHR})) {
+               SpvOpTypeAccelerationStructureKHR})) {
         return _.diag(SPV_ERROR_INVALID_ID, inst)
-               << "UniformConstant OpVariable <id> '" << _.getIdName(inst->id())
-               << "' has illegal type.\n"
-               << "From Vulkan spec, section 14.5.2:\n"
+               << _.VkErrorID(4655) << "UniformConstant OpVariable <id> '"
+               << _.getIdName(inst->id()) << "' has illegal type.\n"
                << "Variables identified with the UniformConstant storage class "
                << "are used only as handles to refer to opaque resources. Such "
                << "variables must be typed as OpTypeImage, OpTypeSampler, "
-               << "OpTypeSampledImage, OpTypeAccelerationStructureNV, "
-                  "OpTypeAccelerationStructureKHR, "
-                  "OpTypeRayQueryKHR, "
+               << "OpTypeSampledImage, OpTypeAccelerationStructureKHR, "
                << "or an array of one of these types.";
       }
     }
diff --git a/source/val/validate_memory_semantics.cpp b/source/val/validate_memory_semantics.cpp
index 6c3e1d4..d918931 100644
--- a/source/val/validate_memory_semantics.cpp
+++ b/source/val/validate_memory_semantics.cpp
@@ -25,7 +25,8 @@
 
 spv_result_t ValidateMemorySemantics(ValidationState_t& _,
                                      const Instruction* inst,
-                                     uint32_t operand_index) {
+                                     uint32_t operand_index,
+                                     uint32_t memory_scope) {
   const SpvOp opcode = inst->opcode();
   const auto id = inst->GetOperandAs<const uint32_t>(operand_index);
   bool is_int32 = false, is_const_int32 = false;
@@ -178,6 +179,18 @@
                 "of the following bits set: Acquire, Release, "
                 "AcquireRelease "
                 "or SequentiallyConsistent";
+    } else if (opcode != SpvOpMemoryBarrier && num_memory_order_set_bits) {
+      // should leave only atomics and control barriers for Vulkan env
+      bool memory_is_int32 = false, memory_is_const_int32 = false;
+      uint32_t memory_value = 0;
+      std::tie(memory_is_int32, memory_is_const_int32, memory_value) =
+          _.EvalInt32IfConst(memory_scope);
+      if (memory_is_int32 && memory_value == SpvScopeInvocation) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << _.VkErrorID(4641) << spvOpcodeString(opcode)
+               << ": Vulkan specification requires Memory Semantics to be None "
+                  "if used with Invocation Memory Scope";
+      }
     }
 
     if (opcode == SpvOpMemoryBarrier && !includes_storage_class) {
diff --git a/source/val/validate_memory_semantics.h b/source/val/validate_memory_semantics.h
index 72a3e10..9e6f93a 100644
--- a/source/val/validate_memory_semantics.h
+++ b/source/val/validate_memory_semantics.h
@@ -22,7 +22,8 @@
 
 spv_result_t ValidateMemorySemantics(ValidationState_t& _,
                                      const Instruction* inst,
-                                     uint32_t operand_index);
+                                     uint32_t operand_index,
+                                     uint32_t memory_scope);
 
 }  // namespace val
 }  // namespace spvtools
diff --git a/source/val/validate_misc.cpp b/source/val/validate_misc.cpp
index 0c30f3c..3bc15ca 100644
--- a/source/val/validate_misc.cpp
+++ b/source/val/validate_misc.cpp
@@ -72,6 +72,37 @@
   return SPV_SUCCESS;
 }
 
+spv_result_t ValidateAssumeTrue(ValidationState_t& _, const Instruction* inst) {
+  const auto operand_type_id = _.GetOperandTypeId(inst, 0);
+  if (!operand_type_id || !_.IsBoolScalarType(operand_type_id)) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "Value operand of OpAssumeTrueKHR must be a boolean scalar";
+  }
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateExpect(ValidationState_t& _, const Instruction* inst) {
+  const auto result_type = inst->type_id();
+  if (!_.IsBoolScalarOrVectorType(result_type) &&
+      !_.IsIntScalarOrVectorType(result_type)) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "Result of OpExpectKHR must be a scalar or vector of integer "
+              "type or boolean type";
+  }
+
+  if (_.GetOperandTypeId(inst, 2) != result_type) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "Type of Value operand of OpExpectKHR does not match the result "
+              "type ";
+  }
+  if (_.GetOperandTypeId(inst, 3) != result_type) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "Type of ExpectedValue operand of OpExpectKHR does not match the "
+              "result type ";
+  }
+  return SPV_SUCCESS;
+}
+
 }  // namespace
 
 spv_result_t MiscPass(ValidationState_t& _, const Instruction* inst) {
@@ -152,6 +183,16 @@
         return error;
       }
       break;
+    case SpvOpAssumeTrueKHR:
+      if (auto error = ValidateAssumeTrue(_, inst)) {
+        return error;
+      }
+      break;
+    case SpvOpExpectKHR:
+      if (auto error = ValidateExpect(_, inst)) {
+        return error;
+      }
+      break;
     default:
       break;
   }
diff --git a/source/val/validate_scopes.cpp b/source/val/validate_scopes.cpp
index a92f7fd..29ba583 100644
--- a/source/val/validate_scopes.cpp
+++ b/source/val/validate_scopes.cpp
@@ -105,21 +105,30 @@
       }
     }
 
-    // If OpControlBarrier is used in fragment, vertex, tessellation evaluation,
-    // or geometry stages, the execution Scope must be Subgroup.
+    // OpControlBarrier must only use Subgroup execution scope for a subset of
+    // execution models.
     if (opcode == SpvOpControlBarrier && value != SpvScopeSubgroup) {
+      std::string errorVUID = _.VkErrorID(4682);
       _.function(inst->function()->id())
-          ->RegisterExecutionModelLimitation([](SpvExecutionModel model,
-                                                std::string* message) {
+          ->RegisterExecutionModelLimitation([errorVUID](
+                                                 SpvExecutionModel model,
+                                                 std::string* message) {
             if (model == SpvExecutionModelFragment ||
                 model == SpvExecutionModelVertex ||
                 model == SpvExecutionModelGeometry ||
-                model == SpvExecutionModelTessellationEvaluation) {
+                model == SpvExecutionModelTessellationEvaluation ||
+                model == SpvExecutionModelRayGenerationKHR ||
+                model == SpvExecutionModelIntersectionKHR ||
+                model == SpvExecutionModelAnyHitKHR ||
+                model == SpvExecutionModelClosestHitKHR ||
+                model == SpvExecutionModelMissKHR) {
               if (message) {
                 *message =
-                    "in Vulkan evironment, OpControlBarrier execution scope "
-                    "must be Subgroup for Fragment, Vertex, Geometry and "
-                    "TessellationEvaluation execution models";
+                    errorVUID +
+                    "in Vulkan environment, OpControlBarrier execution scope "
+                    "must be Subgroup for Fragment, Vertex, Geometry, "
+                    "TessellationEvaluation, RayGeneration, Intersection, "
+                    "AnyHit, ClosestHit, and Miss execution models";
               }
               return false;
             }
@@ -127,11 +136,34 @@
           });
     }
 
+    // Only subset of execution models support Workgroup.
+    if (value == SpvScopeWorkgroup) {
+      std::string errorVUID = _.VkErrorID(4637);
+      _.function(inst->function()->id())
+          ->RegisterExecutionModelLimitation(
+              [errorVUID](SpvExecutionModel model, std::string* message) {
+                if (model != SpvExecutionModelTaskNV &&
+                    model != SpvExecutionModelMeshNV &&
+                    model != SpvExecutionModelTessellationControl &&
+                    model != SpvExecutionModelGLCompute) {
+                  if (message) {
+                    *message =
+                        errorVUID +
+                        "in Vulkan environment, Workgroup execution scope is "
+                        "only for TaskNV, MeshNV, TessellationControl, and "
+                        "GLCompute execution models";
+                  }
+                  return false;
+                }
+                return true;
+              });
+    }
+
     // Vulkan generic rules
     // Scope for execution must be limited to Workgroup or Subgroup
     if (value != SpvScopeWorkgroup && value != SpvScopeSubgroup) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
-             << spvOpcodeString(opcode)
+             << _.VkErrorID(4636) << spvOpcodeString(opcode)
              << ": in Vulkan environment Execution Scope is limited to "
              << "Workgroup and Subgroup";
     }
diff --git a/source/val/validate_type.cpp b/source/val/validate_type.cpp
index 6a5ea3c..612fc5c 100644
--- a/source/val/validate_type.cpp
+++ b/source/val/validate_type.cpp
@@ -427,7 +427,8 @@
   if (spvIsVulkanEnv(_.context()->target_env) &&
       !_.options()->before_hlsl_legalization && ContainsOpaqueType(_, inst)) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
-           << "In " << spvLogStringForEnv(_.context()->target_env)
+           << _.VkErrorID(4667) << "In "
+           << spvLogStringForEnv(_.context()->target_env)
            << ", OpTypeStruct must not contain an opaque type.";
   }
 
@@ -462,6 +463,7 @@
 
   if (!_.IsValidStorageClass(storage_class)) {
     return _.diag(SPV_ERROR_INVALID_BINARY, inst)
+           << _.VkErrorID(4643)
            << "Invalid storage class for target environment";
   }
 
diff --git a/source/val/validation_state.cpp b/source/val/validation_state.cpp
index 10c0fcd..5282163 100644
--- a/source/val/validation_state.cpp
+++ b/source/val/validation_state.cpp
@@ -520,17 +520,39 @@
 void ValidationState_t::RegisterInstruction(Instruction* inst) {
   if (inst->id()) all_definitions_.insert(std::make_pair(inst->id(), inst));
 
-  // If the instruction is using an OpTypeSampledImage as an operand, it should
-  // be recorded. The validator will ensure that all usages of an
-  // OpTypeSampledImage and its definition are in the same basic block.
+  // Some validation checks are easier by getting all the consumers
   for (uint16_t i = 0; i < inst->operands().size(); ++i) {
     const spv_parsed_operand_t& operand = inst->operand(i);
-    if (SPV_OPERAND_TYPE_ID == operand.type) {
+    if ((SPV_OPERAND_TYPE_ID == operand.type) ||
+        (SPV_OPERAND_TYPE_TYPE_ID == operand.type)) {
       const uint32_t operand_word = inst->word(operand.offset);
       Instruction* operand_inst = FindDef(operand_word);
-      if (operand_inst && SpvOpSampledImage == operand_inst->opcode()) {
+      if (!operand_inst) {
+        continue;
+      }
+
+      // If the instruction is using an OpTypeSampledImage as an operand, it
+      // should be recorded. The validator will ensure that all usages of an
+      // OpTypeSampledImage and its definition are in the same basic block.
+      if ((SPV_OPERAND_TYPE_ID == operand.type) &&
+          (SpvOpSampledImage == operand_inst->opcode())) {
         RegisterSampledImageConsumer(operand_word, inst);
       }
+
+      // In order to track storage classes (not Function) used per execution
+      // model we can't use RegisterExecutionModelLimitation on instructions
+      // like OpTypePointer which are going to be in the pre-function section.
+      // Instead just need to register storage class usage for consumers in a
+      // function block.
+      if (inst->function()) {
+        if (operand_inst->opcode() == SpvOpTypePointer) {
+          RegisterStorageClassConsumer(
+              operand_inst->GetOperandAs<SpvStorageClass>(1), inst);
+        } else if (operand_inst->opcode() == SpvOpVariable) {
+          RegisterStorageClassConsumer(
+              operand_inst->GetOperandAs<SpvStorageClass>(2), inst);
+        }
+      }
     }
   }
 }
@@ -550,6 +572,57 @@
   sampled_image_consumers_[sampled_image_id].push_back(consumer);
 }
 
+void ValidationState_t::RegisterStorageClassConsumer(
+    SpvStorageClass storage_class, Instruction* consumer) {
+  if (spvIsVulkanEnv(context()->target_env)) {
+    if (storage_class == SpvStorageClassOutput) {
+      std::string errorVUID = VkErrorID(4644);
+      function(consumer->function()->id())
+          ->RegisterExecutionModelLimitation([errorVUID](
+              SpvExecutionModel model, std::string* message) {
+            if (model == SpvExecutionModelGLCompute ||
+                model == SpvExecutionModelRayGenerationKHR ||
+                model == SpvExecutionModelIntersectionKHR ||
+                model == SpvExecutionModelAnyHitKHR ||
+                model == SpvExecutionModelClosestHitKHR ||
+                model == SpvExecutionModelMissKHR ||
+                model == SpvExecutionModelCallableKHR) {
+              if (message) {
+                *message =
+                    errorVUID +
+                    "in Vulkan evironment, Output Storage Class must not be "
+                    "used in GLCompute, RayGenerationKHR, IntersectionKHR, "
+                    "AnyHitKHR, ClosestHitKHR, MissKHR, or CallableKHR "
+                    "execution models";
+              }
+              return false;
+            }
+            return true;
+          });
+    }
+
+    if (storage_class == SpvStorageClassWorkgroup) {
+      std::string errorVUID = VkErrorID(4645);
+      function(consumer->function()->id())
+          ->RegisterExecutionModelLimitation([errorVUID](
+              SpvExecutionModel model, std::string* message) {
+            if (model != SpvExecutionModelGLCompute &&
+                model != SpvExecutionModelTaskNV &&
+                model != SpvExecutionModelMeshNV) {
+              if (message) {
+                *message =
+                    errorVUID +
+                    "in Vulkan evironment, Workgroup Storage Class is limited "
+                    "to MeshNV, TaskNV, and GLCompute execution model";
+              }
+              return false;
+            }
+            return true;
+          });
+    }
+  }
+}
+
 uint32_t ValidationState_t::getIdBound() const { return id_bound_; }
 
 void ValidationState_t::setIdBound(const uint32_t bound) { id_bound_ = bound; }
@@ -1255,12 +1328,12 @@
       case SpvStorageClassFunction:
       case SpvStorageClassPushConstant:
       case SpvStorageClassPhysicalStorageBuffer:
-      case SpvStorageClassRayPayloadNV:
-      case SpvStorageClassIncomingRayPayloadNV:
-      case SpvStorageClassHitAttributeNV:
-      case SpvStorageClassCallableDataNV:
-      case SpvStorageClassIncomingCallableDataNV:
-      case SpvStorageClassShaderRecordBufferNV:
+      case SpvStorageClassRayPayloadKHR:
+      case SpvStorageClassIncomingRayPayloadKHR:
+      case SpvStorageClassHitAttributeKHR:
+      case SpvStorageClassCallableDataKHR:
+      case SpvStorageClassIncomingCallableDataKHR:
+      case SpvStorageClassShaderRecordBufferKHR:
         return true;
       default:
         return false;
@@ -1676,16 +1749,30 @@
       return VUID_WRAP(VUID-ShadingRateKHR-ShadingRateKHR-04492);
     case 4633:
       return VUID_WRAP(VUID-StandaloneSpirv-None-04633);
+    case 4634:
+      return VUID_WRAP(VUID-StandaloneSpirv-None-04634);
     case 4635:
       return VUID_WRAP(VUID-StandaloneSpirv-None-04635);
+    case 4636:
+      return VUID_WRAP(VUID-StandaloneSpirv-None-04636);
+    case 4637:
+      return VUID_WRAP(VUID-StandaloneSpirv-None-04637);
     case 4638:
       return VUID_WRAP(VUID-StandaloneSpirv-None-04638);
     case 4639:
       return VUID_WRAP(VUID-StandaloneSpirv-None-04639);
     case 4640:
       return VUID_WRAP(VUID-StandaloneSpirv-None-04640);
+    case 4641:
+      return VUID_WRAP(VUID-StandaloneSpirv-None-04641);
     case 4642:
       return VUID_WRAP(VUID-StandaloneSpirv-None-04642);
+    case 4643:
+      return VUID_WRAP(VUID-StandaloneSpirv-None-04643);
+    case 4644:
+      return VUID_WRAP(VUID-StandaloneSpirv-None-04644);
+    case 4645:
+      return VUID_WRAP(VUID-StandaloneSpirv-None-04645);
     case 4651:
       return VUID_WRAP(VUID-StandaloneSpirv-OpVariable-04651);
     case 4652:
@@ -1694,6 +1781,8 @@
       return VUID_WRAP(VUID-StandaloneSpirv-OriginLowerLeft-04653);
     case 4654:
       return VUID_WRAP(VUID-StandaloneSpirv-PixelCenterInteger-04654);
+    case 4655:
+      return VUID_WRAP(VUID-StandaloneSpirv-UniformConstant-04655);
     case 4656:
       return VUID_WRAP(VUID-StandaloneSpirv-OpTypeImage-04656);
     case 4657:
@@ -1706,12 +1795,18 @@
       return VUID_WRAP(VUID-StandaloneSpirv-Offset-04662);
     case 4663:
       return VUID_WRAP(VUID-StandaloneSpirv-Offset-04663);
+    case 4664:
+      return VUID_WRAP(VUID-StandaloneSpirv-OpImageGather-04664);
+    case 4667:
+      return VUID_WRAP(VUID-StandaloneSpirv-None-04667);
     case 4669:
       return VUID_WRAP(VUID-StandaloneSpirv-GLSLShared-04669);
     case 4675:
       return VUID_WRAP(VUID-StandaloneSpirv-FPRoundingMode-04675);
     case 4677:
       return VUID_WRAP(VUID-StandaloneSpirv-Invariant-04677);
+    case 4682:
+      return VUID_WRAP(VUID-StandaloneSpirv-OpControlBarrier-04682);
     case 4683:
       return VUID_WRAP(VUID-StandaloneSpirv-LocalSize-04683);
     case 4685:
@@ -1730,9 +1825,11 @@
       return VUID_WRAP(VUID-StandaloneSpirv-OpMemoryBarrier-04732);
     case 4733:
       return VUID_WRAP(VUID-StandaloneSpirv-OpMemoryBarrier-04733);
+    case 4780:
+      return VUID_WRAP(VUID-StandaloneSpirv-Result-04780);
     default:
       return "";  // unknown id
-  };
+  }
   // clang-format on
 }
 
diff --git a/source/val/validation_state.h b/source/val/validation_state.h
index 8511139..57634bf 100644
--- a/source/val/validation_state.h
+++ b/source/val/validation_state.h
@@ -465,6 +465,10 @@
   void RegisterSampledImageConsumer(uint32_t sampled_image_id,
                                     Instruction* consumer);
 
+  // Record a function's storage class consumer instruction
+  void RegisterStorageClassConsumer(SpvStorageClass storage_class,
+                                    Instruction* consumer);
+
   /// Returns the set of Global Variables.
   std::unordered_set<uint32_t>& global_vars() { return global_vars_; }
 
diff --git a/test/binary_parse_test.cpp b/test/binary_parse_test.cpp
index 93e87bd..9a13f22 100644
--- a/test/binary_parse_test.cpp
+++ b/test/binary_parse_test.cpp
@@ -198,7 +198,7 @@
 
 class BinaryParseTest : public spvtest::TextToBinaryTestBase<::testing::Test> {
  protected:
-  ~BinaryParseTest() { spvDiagnosticDestroy(diagnostic_); }
+  ~BinaryParseTest() override { spvDiagnosticDestroy(diagnostic_); }
 
   void Parse(const SpirvVector& words, spv_result_t expected_result,
              bool flip_words = false) {
diff --git a/test/binary_to_text_test.cpp b/test/binary_to_text_test.cpp
index e8a02fd..9cad966 100644
--- a/test/binary_to_text_test.cpp
+++ b/test/binary_to_text_test.cpp
@@ -36,12 +36,12 @@
  public:
   BinaryToText()
       : context(spvContextCreate(SPV_ENV_UNIVERSAL_1_0)), binary(nullptr) {}
-  ~BinaryToText() {
+  ~BinaryToText() override {
     spvBinaryDestroy(binary);
     spvContextDestroy(context);
   }
 
-  virtual void SetUp() {
+  void SetUp() override {
     const char* textStr = R"(
       OpSource OpenCL_C 12
       OpMemoryModel Physical64 OpenCL
@@ -72,7 +72,7 @@
     ASSERT_EQ(SPV_SUCCESS, error);
   }
 
-  virtual void TearDown() {
+  void TearDown() override {
     spvBinaryDestroy(binary);
     binary = nullptr;
   }
diff --git a/test/enum_string_mapping_test.cpp b/test/enum_string_mapping_test.cpp
index 9bbd8ca..52aa653 100644
--- a/test/enum_string_mapping_test.cpp
+++ b/test/enum_string_mapping_test.cpp
@@ -181,6 +181,8 @@
          {SpvCapabilityDeviceGroup, "DeviceGroup"},
          {SpvCapabilityAtomicFloat32AddEXT, "AtomicFloat32AddEXT"},
          {SpvCapabilityAtomicFloat64AddEXT, "AtomicFloat64AddEXT"},
+         {SpvCapabilityAtomicFloat32MinMaxEXT, "AtomicFloat32MinMaxEXT"},
+         {SpvCapabilityAtomicFloat64MinMaxEXT, "AtomicFloat64MinMaxEXT"},
          {SpvCapabilityMultiView, "MultiView"},
          {SpvCapabilityInt64ImageEXT, "Int64ImageEXT"},
          {SpvCapabilitySampleMaskOverrideCoverageNV,
diff --git a/test/fuzz/CMakeLists.txt b/test/fuzz/CMakeLists.txt
index 2e93293..8acebde 100644
--- a/test/fuzz/CMakeLists.txt
+++ b/test/fuzz/CMakeLists.txt
@@ -17,6 +17,7 @@
   set(SOURCES
           fuzz_test_util.h
 
+          available_instructions_test.cpp
           call_graph_test.cpp
           comparator_deep_blocks_first_test.cpp
           data_synonym_transformation_test.cpp
@@ -30,6 +31,7 @@
           fuzzer_pass_construct_composites_test.cpp
           fuzzer_pass_donate_modules_test.cpp
           fuzzer_pass_outline_functions_test.cpp
+          fuzzerutil_test.cpp
           instruction_descriptor_test.cpp
           fuzzer_pass_test.cpp
           replayer_test.cpp
@@ -113,6 +115,8 @@
           transformation_store_test.cpp
           transformation_swap_commutable_operands_test.cpp
           transformation_swap_conditional_branch_operands_test.cpp
+          transformation_swap_function_variables_test.cpp
+          transformation_swap_two_functions_test.cpp
           transformation_toggle_access_chain_instruction_test.cpp
           transformation_record_synonymous_constants_test.cpp
           transformation_vector_shuffle_test.cpp
diff --git a/test/fuzz/available_instructions_test.cpp b/test/fuzz/available_instructions_test.cpp
new file mode 100644
index 0000000..dc8a3b5
--- /dev/null
+++ b/test/fuzz/available_instructions_test.cpp
@@ -0,0 +1,328 @@
+// Copyright (c) 2021 Alastair F. Donaldson
+//
+// 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/available_instructions.h"
+
+#include "gtest/gtest.h"
+#include "source/fuzz/fuzzer_util.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(AvailableInstructionsTest, BasicTest) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 320
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %8 = OpTypeFloat 32
+          %9 = OpTypePointer Function %8
+         %10 = OpTypeFunction %6 %7 %9
+         %15 = OpTypeVector %8 2
+         %16 = OpTypePointer Private %15
+         %17 = OpVariable %16 Private
+         %18 = OpConstant %8 1
+         %19 = OpConstant %8 2
+         %20 = OpConstantComposite %15 %18 %19
+         %21 = OpTypeVector %8 4
+         %22 = OpTypePointer Private %21
+         %23 = OpVariable %22 Private
+         %24 = OpConstant %8 10
+         %25 = OpConstant %8 20
+         %26 = OpConstant %8 30
+         %27 = OpConstant %8 40
+         %28 = OpConstantComposite %21 %24 %25 %26 %27
+         %31 = OpTypeInt 32 0
+         %32 = OpConstant %31 0
+         %33 = OpTypePointer Private %8
+         %41 = OpTypeBool
+         %46 = OpConstant %6 1
+         %54 = OpConstant %6 10
+         %57 = OpConstant %31 3
+         %61 = OpConstant %6 0
+         %66 = OpConstant %6 3
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %55 = OpVariable %7 Function
+         %56 = OpVariable %9 Function
+         %65 = OpVariable %7 Function
+         %68 = OpVariable %7 Function
+               OpStore %17 %20
+               OpStore %23 %28
+               OpStore %55 %54
+         %58 = OpAccessChain %33 %23 %57
+         %59 = OpLoad %8 %58
+               OpStore %56 %59
+         %60 = OpFunctionCall %6 %13 %55 %56
+        %100 = OpCopyObject %21 %28
+         %62 = OpSGreaterThan %41 %60 %61
+               OpSelectionMerge %64 None
+               OpBranchConditional %62 %63 %67
+         %63 = OpLabel
+               OpStore %65 %66
+        %101 = OpCopyObject %21 %28
+               OpBranch %64
+         %67 = OpLabel
+               OpStore %68 %61
+               OpBranch %69
+         %69 = OpLabel
+               OpLoopMerge %71 %72 None
+               OpBranch %73
+         %73 = OpLabel
+         %74 = OpLoad %6 %68
+         %75 = OpSLessThan %41 %74 %54
+               OpBranchConditional %75 %70 %71
+         %70 = OpLabel
+         %76 = OpLoad %6 %65
+         %77 = OpIAdd %6 %76 %46
+               OpStore %65 %77
+               OpBranch %72
+         %72 = OpLabel
+         %78 = OpLoad %6 %68
+         %79 = OpIAdd %6 %78 %46
+               OpStore %68 %79
+               OpBranch %69
+         %71 = OpLabel
+        %102 = OpCopyObject %21 %28
+               OpBranch %64
+         %64 = OpLabel
+               OpReturn
+               OpFunctionEnd
+         %13 = OpFunction %6 None %10
+         %11 = OpFunctionParameter %7
+         %12 = OpFunctionParameter %9
+         %14 = OpLabel
+         %29 = OpVariable %7 Function
+         %30 = OpLoad %6 %11
+         %34 = OpAccessChain %33 %17 %32
+         %35 = OpLoad %8 %34
+         %36 = OpConvertFToS %6 %35
+         %37 = OpIAdd %6 %30 %36
+               OpStore %29 %37
+         %38 = OpLoad %6 %11
+         %39 = OpLoad %8 %12
+         %40 = OpConvertFToS %6 %39
+         %42 = OpSLessThan %41 %38 %40
+        %103 = OpCopyObject %21 %28
+               OpSelectionMerge %44 None
+               OpBranchConditional %42 %43 %48
+         %43 = OpLabel
+         %45 = OpLoad %6 %29
+         %47 = OpIAdd %6 %45 %46
+               OpStore %29 %47
+               OpBranch %44
+         %48 = OpLabel
+         %49 = OpLoad %6 %29
+         %50 = OpISub %6 %49 %46
+               OpStore %29 %50
+               OpBranch %44
+         %44 = OpLabel
+         %51 = OpLoad %6 %29
+               OpReturnValue %51
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  spvtools::ValidatorOptions validator_options;
+  ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
+                                               kConsoleMessageConsumer));
+
+  opt::Instruction* i1 = context->get_def_use_mgr()->GetDef(55);
+  opt::Instruction* i2 = context->get_def_use_mgr()->GetDef(101);
+  opt::Instruction* i3 = &*context->cfg()->block(67)->begin();
+  opt::Instruction* i4 = context->get_def_use_mgr()->GetDef(74);
+  opt::Instruction* i5 = context->get_def_use_mgr()->GetDef(102);
+  opt::Instruction* i6 = context->get_def_use_mgr()->GetDef(30);
+  opt::Instruction* i7 = context->get_def_use_mgr()->GetDef(47);
+  opt::Instruction* i8 = context->get_def_use_mgr()->GetDef(50);
+  opt::Instruction* i9 = context->get_def_use_mgr()->GetDef(51);
+
+  {
+    AvailableInstructions no_instructions(
+        context.get(),
+        [](opt::IRContext*, opt::Instruction*) -> bool { return false; });
+    for (auto i : {i1, i2, i3, i4, i5, i6, i7, i8, i9}) {
+      auto available = no_instructions.GetAvailableBeforeInstruction(i);
+      ASSERT_EQ(0, available.size());
+      ASSERT_TRUE(available.empty());
+    }
+  }
+  {
+    AvailableInstructions all_instructions(
+        context.get(),
+        [](opt::IRContext*, opt::Instruction*) -> bool { return true; });
+    {
+      auto available = all_instructions.GetAvailableBeforeInstruction(i1);
+      ASSERT_FALSE(available.empty());
+      ASSERT_EQ(30, available.size());
+      ASSERT_EQ(SpvOpTypeVoid, available[0]->opcode());
+      ASSERT_EQ(SpvOpVariable, available[15]->opcode());
+    }
+    {
+      auto available = all_instructions.GetAvailableBeforeInstruction(i2);
+      ASSERT_FALSE(available.empty());
+      ASSERT_EQ(46, available.size());
+      ASSERT_EQ(SpvOpTypeVoid, available[0]->opcode());
+      ASSERT_EQ(SpvOpTypePointer, available[3]->opcode());
+      ASSERT_EQ(SpvOpVariable, available[15]->opcode());
+      ASSERT_EQ(SpvOpFunctionCall, available[40]->opcode());
+      ASSERT_EQ(SpvOpStore, available[45]->opcode());
+    }
+    {
+      auto available = all_instructions.GetAvailableBeforeInstruction(i3);
+      ASSERT_FALSE(available.empty());
+      ASSERT_EQ(45, available.size());
+      ASSERT_EQ(SpvOpTypeVoid, available[0]->opcode());
+      ASSERT_EQ(SpvOpTypePointer, available[3]->opcode());
+      ASSERT_EQ(SpvOpVariable, available[15]->opcode());
+      ASSERT_EQ(SpvOpFunctionCall, available[40]->opcode());
+      ASSERT_EQ(SpvOpBranchConditional, available[44]->opcode());
+    }
+    {
+      auto available = all_instructions.GetAvailableBeforeInstruction(i6);
+      ASSERT_FALSE(available.empty());
+      ASSERT_EQ(33, available.size());
+      ASSERT_EQ(SpvOpTypeVoid, available[0]->opcode());
+      ASSERT_EQ(SpvOpTypeFloat, available[4]->opcode());
+      ASSERT_EQ(SpvOpTypePointer, available[8]->opcode());
+      ASSERT_EQ(SpvOpConstantComposite, available[12]->opcode());
+      ASSERT_EQ(SpvOpConstant, available[16]->opcode());
+      ASSERT_EQ(SpvOpFunctionParameter, available[30]->opcode());
+      ASSERT_EQ(SpvOpFunctionParameter, available[31]->opcode());
+      ASSERT_EQ(SpvOpVariable, available[32]->opcode());
+    }
+  }
+  {
+    AvailableInstructions vector_instructions(
+        context.get(),
+        [](opt::IRContext* ir_context, opt::Instruction* inst) -> bool {
+          return inst->type_id() != 0 && ir_context->get_type_mgr()
+                                                 ->GetType(inst->type_id())
+                                                 ->AsVector() != nullptr;
+        });
+    {
+      auto available = vector_instructions.GetAvailableBeforeInstruction(i4);
+      ASSERT_FALSE(available.empty());
+      ASSERT_EQ(3, available.size());
+      ASSERT_EQ(SpvOpConstantComposite, available[0]->opcode());
+      ASSERT_EQ(SpvOpConstantComposite, available[1]->opcode());
+      ASSERT_EQ(SpvOpCopyObject, available[2]->opcode());
+    }
+    {
+      auto available = vector_instructions.GetAvailableBeforeInstruction(i5);
+      ASSERT_FALSE(available.empty());
+      ASSERT_EQ(3, available.size());
+      ASSERT_EQ(SpvOpConstantComposite, available[0]->opcode());
+      ASSERT_EQ(SpvOpConstantComposite, available[1]->opcode());
+      ASSERT_EQ(SpvOpCopyObject, available[2]->opcode());
+    }
+    {
+      auto available = vector_instructions.GetAvailableBeforeInstruction(i6);
+      ASSERT_FALSE(available.empty());
+      ASSERT_EQ(2, available.size());
+      ASSERT_EQ(SpvOpConstantComposite, available[0]->opcode());
+      ASSERT_EQ(SpvOpConstantComposite, available[1]->opcode());
+    }
+  }
+  {
+    AvailableInstructions integer_add_instructions(
+        context.get(), [](opt::IRContext*, opt::Instruction* inst) -> bool {
+          return inst->opcode() == SpvOpIAdd;
+        });
+    {
+      auto available =
+          integer_add_instructions.GetAvailableBeforeInstruction(i7);
+      ASSERT_FALSE(available.empty());
+      ASSERT_EQ(1, available.size());
+      ASSERT_EQ(SpvOpIAdd, available[0]->opcode());
+    }
+    {
+      auto available =
+          integer_add_instructions.GetAvailableBeforeInstruction(i8);
+      ASSERT_FALSE(available.empty());
+      ASSERT_EQ(1, available.size());
+      ASSERT_EQ(SpvOpIAdd, available[0]->opcode());
+    }
+    {
+      auto available =
+          integer_add_instructions.GetAvailableBeforeInstruction(i9);
+      ASSERT_FALSE(available.empty());
+      ASSERT_EQ(1, available.size());
+      ASSERT_EQ(SpvOpIAdd, available[0]->opcode());
+    }
+  }
+}
+
+TEST(AvailableInstructionsTest, UnreachableBlock) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 320
+               OpName %4 "main"
+               OpName %8 "x"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+               OpStore %8 %9
+         %12 = OpLoad %6 %8
+               OpReturn
+         %10 = OpLabel
+         %11 = OpLoad %6 %8
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  spvtools::ValidatorOptions validator_options;
+  ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
+                                               kConsoleMessageConsumer));
+
+  AvailableInstructions all_instructions(
+      context.get(),
+      [](opt::IRContext*, opt::Instruction*) -> bool { return true; });
+  ASSERT_EQ(7, all_instructions
+                   .GetAvailableBeforeInstruction(
+                       context->get_def_use_mgr()->GetDef(12))
+                   .size());
+
+#ifndef NDEBUG
+  ASSERT_DEATH(all_instructions.GetAvailableBeforeInstruction(
+                   context->get_def_use_mgr()->GetDef(11)),
+               "Availability can only be queried for reachable instructions.");
+#endif
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/test/fuzz/fuzz_test_util.cpp b/test/fuzz/fuzz_test_util.cpp
index 28d3f89..bf0a4ff 100644
--- a/test/fuzz/fuzz_test_util.cpp
+++ b/test/fuzz/fuzz_test_util.cpp
@@ -160,13 +160,19 @@
     const Transformation& transformation, opt::IRContext* ir_context,
     TransformationContext* transformation_context,
     const std::unordered_set<uint32_t>& issued_overflow_ids) {
+  // To ensure that we cover all ToMessage and message-based constructor methods
+  // in our tests, we turn this into a message and back into a transformation,
+  // and use the reconstructed transformation in the rest of the function.
+  auto message = transformation.ToMessage();
+  auto reconstructed_transformation = Transformation::FromMessage(message);
+
   opt::analysis::DefUseManager::IdToDefMap before_transformation =
       ir_context->get_def_use_mgr()->id_to_defs();
-  transformation.Apply(ir_context, transformation_context);
+  reconstructed_transformation->Apply(ir_context, transformation_context);
   opt::analysis::DefUseManager::IdToDefMap after_transformation =
       ir_context->get_def_use_mgr()->id_to_defs();
   std::unordered_set<uint32_t> fresh_ids_for_transformation =
-      transformation.GetFreshIds();
+      reconstructed_transformation->GetFreshIds();
   for (auto& entry : after_transformation) {
     uint32_t id = entry.first;
     bool introduced_by_transformation_message =
diff --git a/test/fuzz/fuzzer_pass_add_opphi_synonyms_test.cpp b/test/fuzz/fuzzer_pass_add_opphi_synonyms_test.cpp
index f7a0996..734f47a 100644
--- a/test/fuzz/fuzzer_pass_add_opphi_synonyms_test.cpp
+++ b/test/fuzz/fuzzer_pass_add_opphi_synonyms_test.cpp
@@ -128,8 +128,8 @@
                                                kConsoleMessageConsumer));
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
-  PseudoRandomGenerator prng(0);
-  FuzzerContext fuzzer_context(&prng, 100);
+  FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100,
+                               false);
   protobufs::TransformationSequence transformation_sequence;
 
   FuzzerPassAddOpPhiSynonyms fuzzer_pass(context.get(), &transformation_context,
diff --git a/test/fuzz/fuzzer_pass_construct_composites_test.cpp b/test/fuzz/fuzzer_pass_construct_composites_test.cpp
index d49d1d6..a02176b 100644
--- a/test/fuzz/fuzzer_pass_construct_composites_test.cpp
+++ b/test/fuzz/fuzzer_pass_construct_composites_test.cpp
@@ -77,7 +77,8 @@
   const auto env = SPV_ENV_UNIVERSAL_1_3;
   const auto consumer = nullptr;
 
-  auto prng = MakeUnique<PseudoRandomGenerator>(0);
+  FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100,
+                               false);
 
   for (uint32_t i = 0; i < 10; i++) {
     const auto context =
@@ -87,7 +88,6 @@
         context.get(), validator_options, kConsoleMessageConsumer));
     TransformationContext transformation_context(
         MakeUnique<FactManager>(context.get()), validator_options);
-    FuzzerContext fuzzer_context(prng.get(), 100);
     protobufs::TransformationSequence transformation_sequence;
 
     FuzzerPassConstructComposites fuzzer_pass(
@@ -158,7 +158,8 @@
   const auto env = SPV_ENV_UNIVERSAL_1_3;
   const auto consumer = nullptr;
 
-  auto prng = MakeUnique<PseudoRandomGenerator>(0);
+  FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100,
+                               false);
 
   for (uint32_t i = 0; i < 10; i++) {
     const auto context =
@@ -168,7 +169,6 @@
         context.get(), validator_options, kConsoleMessageConsumer));
     TransformationContext transformation_context(
         MakeUnique<FactManager>(context.get()), validator_options);
-    FuzzerContext fuzzer_context(prng.get(), 100);
     protobufs::TransformationSequence transformation_sequence;
 
     FuzzerPassConstructComposites fuzzer_pass(
diff --git a/test/fuzz/fuzzer_pass_donate_modules_test.cpp b/test/fuzz/fuzzer_pass_donate_modules_test.cpp
index 1a7cd4a..f11885d 100644
--- a/test/fuzz/fuzzer_pass_donate_modules_test.cpp
+++ b/test/fuzz/fuzzer_pass_donate_modules_test.cpp
@@ -204,8 +204,8 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(recipient_context.get()), validator_options);
 
-  PseudoRandomGenerator prng(0);
-  FuzzerContext fuzzer_context(&prng, 100);
+  FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100,
+                               false);
   protobufs::TransformationSequence transformation_sequence;
 
   FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
@@ -285,8 +285,8 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(recipient_context.get()), validator_options);
 
-  PseudoRandomGenerator prng(0);
-  FuzzerContext fuzzer_context(&prng, 100);
+  FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100,
+                               false);
   protobufs::TransformationSequence transformation_sequence;
 
   FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
@@ -416,8 +416,8 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(recipient_context.get()), validator_options);
 
-  PseudoRandomGenerator prng(0);
-  FuzzerContext fuzzer_context(&prng, 100);
+  FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100,
+                               false);
   protobufs::TransformationSequence transformation_sequence;
 
   FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
@@ -511,8 +511,8 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(recipient_context.get()), validator_options);
 
-  PseudoRandomGenerator prng(0);
-  FuzzerContext fuzzer_context(&prng, 100);
+  FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100,
+                               false);
   protobufs::TransformationSequence transformation_sequence;
 
   FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
@@ -581,8 +581,8 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(recipient_context.get()), validator_options);
 
-  PseudoRandomGenerator prng(0);
-  FuzzerContext fuzzer_context(&prng, 100);
+  FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100,
+                               false);
   protobufs::TransformationSequence transformation_sequence;
 
   FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
@@ -709,8 +709,8 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(recipient_context.get()), validator_options);
 
-  PseudoRandomGenerator prng(0);
-  FuzzerContext fuzzer_context(&prng, 100);
+  FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100,
+                               false);
   protobufs::TransformationSequence transformation_sequence;
 
   FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
@@ -805,8 +805,8 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(recipient_context.get()), validator_options);
 
-  PseudoRandomGenerator prng(0);
-  FuzzerContext fuzzer_context(&prng, 100);
+  FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100,
+                               false);
   protobufs::TransformationSequence transformation_sequence;
 
   FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
@@ -937,8 +937,8 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(recipient_context.get()), validator_options);
 
-  PseudoRandomGenerator prng(0);
-  FuzzerContext fuzzer_context(&prng, 100);
+  FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100,
+                               false);
   protobufs::TransformationSequence transformation_sequence;
 
   FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
@@ -1073,8 +1073,8 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(recipient_context.get()), validator_options);
 
-  PseudoRandomGenerator prng(0);
-  FuzzerContext fuzzer_context(&prng, 100);
+  FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100,
+                               false);
   protobufs::TransformationSequence transformation_sequence;
 
   FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
@@ -1155,8 +1155,8 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(recipient_context.get()), validator_options);
 
-  PseudoRandomGenerator prng(0);
-  FuzzerContext fuzzer_context(&prng, 100);
+  FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100,
+                               false);
   protobufs::TransformationSequence transformation_sequence;
 
   FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
@@ -1242,8 +1242,8 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(recipient_context.get()), validator_options);
 
-  PseudoRandomGenerator prng(0);
-  FuzzerContext fuzzer_context(&prng, 100);
+  FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100,
+                               false);
   protobufs::TransformationSequence transformation_sequence;
 
   FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
@@ -1346,8 +1346,8 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(recipient_context.get()), validator_options);
 
-  PseudoRandomGenerator prng(0);
-  FuzzerContext fuzzer_context(&prng, 100);
+  FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100,
+                               false);
   protobufs::TransformationSequence transformation_sequence;
 
   FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
@@ -1418,8 +1418,8 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(recipient_context.get()), validator_options);
 
-  PseudoRandomGenerator prng(0);
-  FuzzerContext fuzzer_context(&prng, 100);
+  FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100,
+                               false);
   protobufs::TransformationSequence transformation_sequence;
 
   FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
@@ -1528,8 +1528,8 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(recipient_context.get()), validator_options);
 
-  PseudoRandomGenerator prng(0);
-  FuzzerContext fuzzer_context(&prng, 100);
+  FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100,
+                               false);
   protobufs::TransformationSequence transformation_sequence;
 
   FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
@@ -1712,8 +1712,8 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(recipient_context.get()), validator_options);
 
-  PseudoRandomGenerator rng(0);
-  FuzzerContext fuzzer_context(&rng, 100);
+  FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100,
+                               false);
   protobufs::TransformationSequence transformation_sequence;
 
   FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
@@ -1784,8 +1784,8 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(recipient_context.get()), validator_options);
 
-  PseudoRandomGenerator prng(0);
-  FuzzerContext fuzzer_context(&prng, 100);
+  FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100,
+                               false);
   protobufs::TransformationSequence transformation_sequence;
 
   FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
@@ -1941,8 +1941,8 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(recipient_context.get()), validator_options);
 
-  PseudoRandomGenerator rng(0);
-  FuzzerContext fuzzer_context(&rng, 100);
+  FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100,
+                               false);
   protobufs::TransformationSequence transformation_sequence;
 
   FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
@@ -2014,8 +2014,8 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(recipient_context.get()), validator_options);
 
-  PseudoRandomGenerator rng(0);
-  FuzzerContext fuzzer_context(&rng, 100);
+  FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100,
+                               false);
   protobufs::TransformationSequence transformation_sequence;
 
   FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
@@ -2247,8 +2247,8 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(recipient_context.get()), validator_options);
 
-  PseudoRandomGenerator prng(0);
-  FuzzerContext fuzzer_context(&prng, 100);
+  FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100,
+                               false);
   protobufs::TransformationSequence transformation_sequence;
 
   FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
diff --git a/test/fuzz/fuzzer_pass_outline_functions_test.cpp b/test/fuzz/fuzzer_pass_outline_functions_test.cpp
index 576962c..0d2c5bf 100644
--- a/test/fuzz/fuzzer_pass_outline_functions_test.cpp
+++ b/test/fuzz/fuzzer_pass_outline_functions_test.cpp
@@ -124,8 +124,8 @@
                                                kConsoleMessageConsumer));
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
-  PseudoRandomGenerator prng(0);
-  FuzzerContext fuzzer_context(&prng, 100);
+  FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100,
+                               false);
   protobufs::TransformationSequence transformation_sequence;
 
   FuzzerPassOutlineFunctions fuzzer_pass(context.get(), &transformation_context,
@@ -167,8 +167,8 @@
                                                kConsoleMessageConsumer));
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
-  PseudoRandomGenerator prng(0);
-  FuzzerContext fuzzer_context(&prng, 100);
+  FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100,
+                               false);
   protobufs::TransformationSequence transformation_sequence;
 
   FuzzerPassOutlineFunctions fuzzer_pass(context.get(), &transformation_context,
@@ -291,8 +291,8 @@
                                                kConsoleMessageConsumer));
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
-  PseudoRandomGenerator prng(0);
-  FuzzerContext fuzzer_context(&prng, 100);
+  FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100,
+                               false);
   protobufs::TransformationSequence transformation_sequence;
 
   FuzzerPassOutlineFunctions fuzzer_pass(context.get(), &transformation_context,
@@ -458,8 +458,8 @@
                                                kConsoleMessageConsumer));
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
-  PseudoRandomGenerator prng(0);
-  FuzzerContext fuzzer_context(&prng, 100);
+  FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100,
+                               false);
   protobufs::TransformationSequence transformation_sequence;
 
   FuzzerPassOutlineFunctions fuzzer_pass(context.get(), &transformation_context,
diff --git a/test/fuzz/fuzzer_pass_test.cpp b/test/fuzz/fuzzer_pass_test.cpp
index 283aa11..b035de7 100644
--- a/test/fuzz/fuzzer_pass_test.cpp
+++ b/test/fuzz/fuzzer_pass_test.cpp
@@ -87,8 +87,8 @@
   ASSERT_TRUE(dominator_analysis->IsReachable(5));
   ASSERT_FALSE(dominator_analysis->IsReachable(8));
 
-  PseudoRandomGenerator prng(0);
-  FuzzerContext fuzzer_context(&prng, 100);
+  FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100,
+                               false);
   protobufs::TransformationSequence transformations;
   FuzzerPassMock fuzzer_pass_mock(context.get(), &transformation_context,
                                   &fuzzer_context, &transformations);
diff --git a/test/fuzz/fuzzer_replayer_test.cpp b/test/fuzz/fuzzer_replayer_test.cpp
index dc90574..6dc7ffb 100644
--- a/test/fuzz/fuzzer_replayer_test.cpp
+++ b/test/fuzz/fuzzer_replayer_test.cpp
@@ -12,12 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "source/fuzz/fuzzer.h"
-#include "source/fuzz/replayer.h"
-
 #include "gtest/gtest.h"
+#include "source/fuzz/fuzzer.h"
 #include "source/fuzz/fuzzer_util.h"
 #include "source/fuzz/pseudo_random_generator.h"
+#include "source/fuzz/replayer.h"
 #include "source/fuzz/uniform_buffer_element_descriptor.h"
 #include "test/fuzz/fuzz_test_util.h"
 
@@ -1642,37 +1641,53 @@
     });
   }
 
-  std::vector<Fuzzer::RepeatedPassStrategy> strategies{
-      Fuzzer::RepeatedPassStrategy::kSimple,
-      Fuzzer::RepeatedPassStrategy::kLoopedWithRecommendations,
-      Fuzzer::RepeatedPassStrategy::kRandomWithRecommendations};
+  std::vector<RepeatedPassStrategy> strategies{
+      RepeatedPassStrategy::kSimple,
+      RepeatedPassStrategy::kLoopedWithRecommendations,
+      RepeatedPassStrategy::kRandomWithRecommendations};
   uint32_t strategy_index = 0;
   for (uint32_t seed = initial_seed; seed < initial_seed + num_runs; seed++) {
     spvtools::ValidatorOptions validator_options;
+
+    std::unique_ptr<opt::IRContext> ir_context;
+    ASSERT_TRUE(fuzzerutil::BuildIRContext(env, kConsoleMessageConsumer,
+                                           binary_in, validator_options,
+                                           &ir_context));
+
+    auto fuzzer_context = MakeUnique<FuzzerContext>(
+        MakeUnique<PseudoRandomGenerator>(seed),
+        FuzzerContext::GetMinFreshId(ir_context.get()), false);
+
+    auto transformation_context = MakeUnique<TransformationContext>(
+        MakeUnique<FactManager>(ir_context.get()), validator_options);
+    transformation_context->GetFactManager()->AddInitialFacts(
+        kConsoleMessageConsumer, initial_facts);
+
     // Every 4th time we run the fuzzer, enable all fuzzer passes.
     bool enable_all_passes = (seed % 4) == 0;
-    auto fuzzer_result =
-        Fuzzer(env, kConsoleMessageConsumer, binary_in, initial_facts,
-               donor_suppliers, MakeUnique<PseudoRandomGenerator>(seed),
-               enable_all_passes, strategies[strategy_index], true,
-               validator_options)
-            .Run();
+    Fuzzer fuzzer(std::move(ir_context), std::move(transformation_context),
+                  std::move(fuzzer_context), kConsoleMessageConsumer,
+                  donor_suppliers, enable_all_passes,
+                  strategies[strategy_index], true, validator_options);
+    auto fuzzer_result = fuzzer.Run(0);
 
     // Cycle the repeated pass strategy so that we try a different one next time
     // we run the fuzzer.
     strategy_index =
         (strategy_index + 1) % static_cast<uint32_t>(strategies.size());
 
-    ASSERT_EQ(Fuzzer::FuzzerResultStatus::kComplete, fuzzer_result.status);
-    ASSERT_TRUE(t.Validate(fuzzer_result.transformed_binary));
+    ASSERT_NE(Fuzzer::Status::kFuzzerPassLedToInvalidModule,
+              fuzzer_result.status);
+    std::vector<uint32_t> transformed_binary;
+    fuzzer.GetIRContext()->module()->ToBinary(&transformed_binary, true);
+    ASSERT_TRUE(t.Validate(transformed_binary));
 
     auto replayer_result =
-        Replayer(
-            env, kConsoleMessageConsumer, binary_in, initial_facts,
-            fuzzer_result.applied_transformations,
-            static_cast<uint32_t>(
-                fuzzer_result.applied_transformations.transformation_size()),
-            false, validator_options)
+        Replayer(env, kConsoleMessageConsumer, binary_in, initial_facts,
+                 fuzzer.GetTransformationSequence(),
+                 static_cast<uint32_t>(
+                     fuzzer.GetTransformationSequence().transformation_size()),
+                 false, validator_options)
             .Run();
     ASSERT_EQ(Replayer::ReplayerResultStatus::kComplete,
               replayer_result.status);
@@ -1682,12 +1697,12 @@
     // replay should be identical to that which resulted from fuzzing.
     std::string fuzzer_transformations_string;
     std::string replayer_transformations_string;
-    fuzzer_result.applied_transformations.SerializeToString(
+    fuzzer.GetTransformationSequence().SerializeToString(
         &fuzzer_transformations_string);
     replayer_result.applied_transformations.SerializeToString(
         &replayer_transformations_string);
     ASSERT_EQ(fuzzer_transformations_string, replayer_transformations_string);
-    ASSERT_TRUE(IsEqual(env, fuzzer_result.transformed_binary,
+    ASSERT_TRUE(IsEqual(env, transformed_binary,
                         replayer_result.transformed_module.get()));
   }
 }
diff --git a/test/fuzz/fuzzer_shrinker_test.cpp b/test/fuzz/fuzzer_shrinker_test.cpp
index 6d9dad3..e792116 100644
--- a/test/fuzz/fuzzer_shrinker_test.cpp
+++ b/test/fuzz/fuzzer_shrinker_test.cpp
@@ -12,15 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "source/fuzz/fuzzer.h"
-#include "source/fuzz/shrinker.h"
-
 #include <functional>
 #include <vector>
 
 #include "gtest/gtest.h"
+#include "source/fuzz/fuzzer.h"
 #include "source/fuzz/fuzzer_util.h"
 #include "source/fuzz/pseudo_random_generator.h"
+#include "source/fuzz/shrinker.h"
 #include "source/fuzz/uniform_buffer_element_descriptor.h"
 #include "test/fuzz/fuzz_test_util.h"
 
@@ -1044,24 +1043,38 @@
   // Depending on the seed, decide whether to enable all passes and which
   // repeated pass manager to use.
   bool enable_all_passes = (seed % 4) == 0;
-  Fuzzer::RepeatedPassStrategy repeated_pass_strategy;
+  RepeatedPassStrategy repeated_pass_strategy;
   if ((seed % 3) == 0) {
-    repeated_pass_strategy = Fuzzer::RepeatedPassStrategy::kSimple;
+    repeated_pass_strategy = RepeatedPassStrategy::kSimple;
   } else if ((seed % 3) == 1) {
-    repeated_pass_strategy =
-        Fuzzer::RepeatedPassStrategy::kLoopedWithRecommendations;
+    repeated_pass_strategy = RepeatedPassStrategy::kLoopedWithRecommendations;
   } else {
-    repeated_pass_strategy =
-        Fuzzer::RepeatedPassStrategy::kRandomWithRecommendations;
+    repeated_pass_strategy = RepeatedPassStrategy::kRandomWithRecommendations;
   }
 
-  auto fuzzer_result =
-      Fuzzer(env, kConsoleMessageConsumer, binary_in, initial_facts,
-             donor_suppliers, MakeUnique<PseudoRandomGenerator>(seed),
-             enable_all_passes, repeated_pass_strategy, true, validator_options)
-          .Run();
-  ASSERT_EQ(Fuzzer::FuzzerResultStatus::kComplete, fuzzer_result.status);
-  ASSERT_TRUE(t.Validate(fuzzer_result.transformed_binary));
+  std::unique_ptr<opt::IRContext> ir_context;
+  ASSERT_TRUE(fuzzerutil::BuildIRContext(
+      env, kConsoleMessageConsumer, binary_in, validator_options, &ir_context));
+
+  auto fuzzer_context = MakeUnique<FuzzerContext>(
+      MakeUnique<PseudoRandomGenerator>(seed),
+      FuzzerContext::GetMinFreshId(ir_context.get()), false);
+
+  auto transformation_context = MakeUnique<TransformationContext>(
+      MakeUnique<FactManager>(ir_context.get()), validator_options);
+  transformation_context->GetFactManager()->AddInitialFacts(
+      kConsoleMessageConsumer, initial_facts);
+
+  Fuzzer fuzzer(std::move(ir_context), std::move(transformation_context),
+                std::move(fuzzer_context), kConsoleMessageConsumer,
+                donor_suppliers, enable_all_passes, repeated_pass_strategy,
+                true, validator_options);
+  auto fuzzer_result = fuzzer.Run(0);
+  ASSERT_NE(Fuzzer::Status::kFuzzerPassLedToInvalidModule,
+            fuzzer_result.status);
+  std::vector<uint32_t> transformed_binary;
+  fuzzer.GetIRContext()->module()->ToBinary(&transformed_binary, true);
+  ASSERT_TRUE(t.Validate(transformed_binary));
 
   const uint32_t kReasonableStepLimit = 50;
   const uint32_t kSmallStepLimit = 20;
@@ -1069,30 +1082,30 @@
   // With the AlwaysInteresting test, we should quickly shrink to the original
   // binary with no transformations remaining.
   RunAndCheckShrinker(env, binary_in, initial_facts,
-                      fuzzer_result.applied_transformations,
+                      fuzzer.GetTransformationSequence(),
                       AlwaysInteresting().AsFunction(), binary_in, 0,
                       kReasonableStepLimit, validator_options);
 
   // With the OnlyInterestingFirstTime test, no shrinking should be achieved.
   RunAndCheckShrinker(
-      env, binary_in, initial_facts, fuzzer_result.applied_transformations,
-      OnlyInterestingFirstTime().AsFunction(), fuzzer_result.transformed_binary,
+      env, binary_in, initial_facts, fuzzer.GetTransformationSequence(),
+      OnlyInterestingFirstTime().AsFunction(), transformed_binary,
       static_cast<uint32_t>(
-          fuzzer_result.applied_transformations.transformation_size()),
+          fuzzer.GetTransformationSequence().transformation_size()),
       kReasonableStepLimit, validator_options);
 
   // The PingPong test is unpredictable; passing an empty expected binary
   // means that we don't check anything beyond that shrinking completes
   // successfully.
   RunAndCheckShrinker(
-      env, binary_in, initial_facts, fuzzer_result.applied_transformations,
+      env, binary_in, initial_facts, fuzzer.GetTransformationSequence(),
       PingPong().AsFunction(), {}, 0, kSmallStepLimit, validator_options);
 
   // The InterestingThenRandom test is unpredictable; passing an empty
   // expected binary means that we do not check anything about shrinking
   // results.
   RunAndCheckShrinker(
-      env, binary_in, initial_facts, fuzzer_result.applied_transformations,
+      env, binary_in, initial_facts, fuzzer.GetTransformationSequence(),
       InterestingThenRandom(PseudoRandomGenerator(seed)).AsFunction(), {}, 0,
       kSmallStepLimit, validator_options);
 }
diff --git a/test/fuzz/fuzzerutil_test.cpp b/test/fuzz/fuzzerutil_test.cpp
new file mode 100644
index 0000000..7ff9b4d
--- /dev/null
+++ b/test/fuzz/fuzzerutil_test.cpp
@@ -0,0 +1,1568 @@
+// Copyright (c) 2021 Shiyu Liu
+//
+// 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 "gtest/gtest.h"
+#include "source/fuzz/fuzzer_util.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(FuzzerUtilMaybeFindBlockTest, 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
+               OpDecorate %8 RelaxedPrecision
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 1
+         %10 = OpConstant %6 2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+               OpBranch %11
+         %11 = OpLabel
+               OpStore %8 %9
+               OpBranch %12
+         %12 = OpLabel
+               OpStore %8 %10
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const std::unique_ptr<opt::IRContext> context =
+      BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  spvtools::ValidatorOptions validator_options;
+  ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
+                                               kConsoleMessageConsumer));
+
+  // Only blocks with id 11 and 12 can be found.
+  // Should return nullptr when id is not a label or id was not found.
+  uint32_t block_id1 = 11;
+  uint32_t block_id2 = 12;
+  uint32_t block_id3 = 13;
+  uint32_t block_id4 = 8;
+
+  opt::IRContext* ir_context = context.get();
+  // Block with id 11 should be found.
+  ASSERT_TRUE(fuzzerutil::MaybeFindBlock(ir_context, block_id1) != nullptr);
+  // Block with id 12 should be found.
+  ASSERT_TRUE(fuzzerutil::MaybeFindBlock(ir_context, block_id2) != nullptr);
+  // Block with id 13 cannot be found.
+  ASSERT_FALSE(fuzzerutil::MaybeFindBlock(ir_context, block_id3) != nullptr);
+  // Block with id 8 exisits but don't not of type OpLabel.
+  ASSERT_FALSE(fuzzerutil::MaybeFindBlock(ir_context, block_id4) != nullptr);
+}
+
+TEST(FuzzerutilTest, FuzzerUtilMaybeGetBoolConstantTest) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %36
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %8 "b1"
+               OpName %10 "b2"
+               OpName %12 "b3"
+               OpName %13 "b4"
+               OpName %16 "f1"
+               OpName %18 "f2"
+               OpName %20 "cf1"
+               OpName %22 "cf2"
+               OpName %26 "i1"
+               OpName %28 "i2"
+               OpName %30 "ci1"
+               OpName %32 "ci2"
+               OpName %36 "value"
+               OpDecorate %26 RelaxedPrecision
+               OpDecorate %28 RelaxedPrecision
+               OpDecorate %30 RelaxedPrecision
+               OpDecorate %32 RelaxedPrecision
+               OpDecorate %36 Location 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool
+          %7 = OpTypePointer Function %6
+          %9 = OpConstantTrue %6
+         %11 = OpConstantFalse %6
+         %14 = OpTypeFloat 32
+         %15 = OpTypePointer Function %14
+         %17 = OpConstant %14 1.23000002
+         %19 = OpConstant %14 1.11000001
+         %21 = OpConstant %14 2
+         %23 = OpConstant %14 3.29999995
+         %24 = OpTypeInt 32 1
+         %25 = OpTypePointer Function %24
+         %27 = OpConstant %24 1
+         %29 = OpConstant %24 100
+         %31 = OpConstant %24 123
+         %33 = OpConstant %24 1111
+         %35 = OpTypePointer Input %14
+         %36 = OpVariable %35 Input
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %10 = OpVariable %7 Function
+         %12 = OpVariable %7 Function
+         %13 = OpVariable %7 Function
+         %16 = OpVariable %15 Function
+         %18 = OpVariable %15 Function
+         %20 = OpVariable %15 Function
+         %22 = OpVariable %15 Function
+         %26 = OpVariable %25 Function
+         %28 = OpVariable %25 Function
+         %30 = OpVariable %25 Function
+         %32 = OpVariable %25 Function
+               OpStore %8 %9
+               OpStore %10 %11
+               OpStore %12 %9
+               OpStore %13 %11
+               OpStore %16 %17
+               OpStore %18 %19
+               OpStore %20 %21
+               OpStore %22 %23
+               OpStore %26 %27
+               OpStore %28 %29
+               OpStore %30 %31
+               OpStore %32 %33
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const std::unique_ptr<opt::IRContext> context =
+      BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  spvtools::ValidatorOptions validator_options;
+  ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
+                                               kConsoleMessageConsumer));
+  TransformationContext transformation_context(
+      MakeUnique<FactManager>(context.get()), validator_options);
+
+  opt::IRContext* ir_context = context.get();
+  // A bool constant with value false exists and the id is 11.
+  ASSERT_EQ(11, fuzzerutil::MaybeGetBoolConstant(
+                    ir_context, transformation_context, false, false));
+  // A bool constant with value true exists and the id is 9.
+  ASSERT_EQ(9, fuzzerutil::MaybeGetBoolConstant(
+                   ir_context, transformation_context, true, false));
+}
+
+TEST(FuzzerutilTest, FuzzerUtilMaybeGetBoolTypeTest) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %92 %52 %53
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpDecorate %92 BuiltIn FragCoord
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypeFloat 32
+          %8 = OpTypeStruct %6 %7
+          %9 = OpTypePointer Function %8
+         %10 = OpTypeFunction %6 %9
+         %14 = OpConstant %6 0
+         %15 = OpTypePointer Function %6
+         %51 = OpTypePointer Private %6
+         %21 = OpConstant %6 2
+         %23 = OpConstant %6 1
+         %24 = OpConstant %7 1
+         %25 = OpTypePointer Function %7
+         %50 = OpTypePointer Private %7
+         %34 = OpTypeBool
+         %35 = OpConstantFalse %34
+         %60 = OpConstantNull %50
+         %61 = OpUndef %51
+         %52 = OpVariable %50 Private
+         %53 = OpVariable %51 Private
+         %80 = OpConstantComposite %8 %21 %24
+         %90 = OpTypeVector %7 4
+         %91 = OpTypePointer Input %90
+         %92 = OpVariable %91 Input
+         %93 = OpConstantComposite %90 %24 %24 %24 %24
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %20 = OpVariable %9 Function
+         %27 = OpVariable %9 Function
+         %22 = OpAccessChain %15 %20 %14
+         %44 = OpCopyObject %9 %20
+         %26 = OpAccessChain %25 %20 %23
+         %29 = OpFunctionCall %6 %12 %27
+         %30 = OpAccessChain %15 %20 %14
+         %45 = OpCopyObject %15 %30
+         %81 = OpCopyObject %9 %27
+         %33 = OpAccessChain %15 %20 %14
+               OpSelectionMerge %37 None
+               OpBranchConditional %35 %36 %37
+         %36 = OpLabel
+         %38 = OpAccessChain %15 %20 %14
+         %40 = OpAccessChain %15 %20 %14
+         %43 = OpAccessChain %15 %20 %14
+         %82 = OpCopyObject %9 %27
+               OpBranch %37
+         %37 = OpLabel
+               OpReturn
+               OpFunctionEnd
+         %12 = OpFunction %6 None %10
+         %11 = OpFunctionParameter %9
+         %13 = OpLabel
+         %46 = OpCopyObject %9 %11
+         %16 = OpAccessChain %15 %11 %14
+         %95 = OpCopyObject %8 %80
+               OpReturnValue %21
+        %100 = OpLabel
+               OpUnreachable
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const std::unique_ptr<opt::IRContext> context =
+      BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  spvtools::ValidatorOptions validator_options;
+  ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
+                                               kConsoleMessageConsumer));
+
+  opt::IRContext* ir_context = context.get();
+  // A bool type with result id of 34 exists.
+  ASSERT_TRUE(fuzzerutil::MaybeGetBoolType(ir_context));
+}
+
+TEST(FuzzerutilTest, FuzzerUtilMaybeGetCompositeConstantTest) {
+  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 %8 "b1"
+               OpName %10 "b2"
+               OpName %12 "b3"
+               OpName %13 "b4"
+               OpName %16 "f1"
+               OpName %18 "f2"
+               OpName %22 "zc"
+               OpName %24 "i1"
+               OpName %28 "i2"
+               OpName %30 "i3"
+               OpName %32 "i4"
+               OpName %37 "f_arr"
+               OpName %47 "i_arr"
+               OpName %54 "value"
+               OpDecorate %22 RelaxedPrecision
+               OpDecorate %24 RelaxedPrecision
+               OpDecorate %28 RelaxedPrecision
+               OpDecorate %30 RelaxedPrecision
+               OpDecorate %32 RelaxedPrecision
+               OpDecorate %47 RelaxedPrecision
+               OpDecorate %54 Location 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool
+          %7 = OpTypePointer Function %6
+          %9 = OpConstantTrue %6
+         %11 = OpConstantFalse %6
+         %14 = OpTypeFloat 32
+         %15 = OpTypePointer Function %14
+         %17 = OpConstant %14 1.23000002
+         %19 = OpConstant %14 1.11000001
+         %20 = OpTypeInt 32 1
+         %21 = OpTypePointer Function %20
+         %23 = OpConstant %20 0
+         %25 = OpConstant %20 1
+         %26 = OpTypeInt 32 0
+         %27 = OpTypePointer Function %26
+         %29 = OpConstant %26 100
+         %31 = OpConstant %20 -1
+         %33 = OpConstant %20 -99
+         %34 = OpConstant %26 5
+         %35 = OpTypeArray %14 %34
+         %36 = OpTypePointer Function %35
+         %38 = OpConstant %14 5.5
+         %39 = OpConstant %14 4.4000001
+         %40 = OpConstant %14 3.29999995
+         %41 = OpConstant %14 2.20000005
+         %42 = OpConstant %14 1.10000002
+         %43 = OpConstantComposite %35 %38 %39 %40 %41 %42
+         %44 = OpConstant %26 3
+         %45 = OpTypeArray %20 %44
+         %46 = OpTypePointer Function %45
+         %48 = OpConstant %20 3
+         %49 = OpConstant %20 7
+         %50 = OpConstant %20 9
+         %51 = OpConstantComposite %45 %48 %49 %50
+         %53 = OpTypePointer Input %14
+         %54 = OpVariable %53 Input
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %10 = OpVariable %7 Function
+         %12 = OpVariable %7 Function
+         %13 = OpVariable %7 Function
+         %16 = OpVariable %15 Function
+         %18 = OpVariable %15 Function
+         %22 = OpVariable %21 Function
+         %24 = OpVariable %21 Function
+         %28 = OpVariable %27 Function
+         %30 = OpVariable %21 Function
+         %32 = OpVariable %21 Function
+         %37 = OpVariable %36 Function
+         %47 = OpVariable %46 Function
+               OpStore %8 %9
+               OpStore %10 %11
+               OpStore %12 %9
+               OpStore %13 %11
+               OpStore %16 %17
+               OpStore %18 %19
+               OpStore %22 %23
+               OpStore %24 %25
+               OpStore %28 %29
+               OpStore %30 %31
+               OpStore %32 %33
+               OpStore %37 %43
+               OpStore %47 %51
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const std::unique_ptr<opt::IRContext> context =
+      BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  spvtools::ValidatorOptions validator_options;
+  ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
+                                               kConsoleMessageConsumer));
+  TransformationContext transformation_context(
+      MakeUnique<FactManager>(context.get()), validator_options);
+
+  opt::IRContext* ir_context = context.get();
+
+  //      %43 = OpConstantComposite %35 %38 %39 %40 %41 %42
+  //    %51 = OpConstantComposite %45 %48 %49 %50
+  // This should pass as a float array with 5 elements exist and its id is 43.
+  ASSERT_EQ(43, fuzzerutil::MaybeGetCompositeConstant(
+                    ir_context, transformation_context, {38, 39, 40, 41, 42},
+                    35, false));
+  // This should pass as an int array with 3 elements exist and its id is 51.
+  ASSERT_EQ(51,
+            fuzzerutil::MaybeGetCompositeConstant(
+                ir_context, transformation_context, {48, 49, 50}, 45, false));
+  // An int array with 2 elements does not exist.
+  ASSERT_EQ(0, fuzzerutil::MaybeGetCompositeConstant(
+                   ir_context, transformation_context, {48, 49}, 45, false));
+}
+
+TEST(FuzzerutilTest, FuzzerUtilMaybeGetFloatConstantTest) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %36
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %8 "b1"
+               OpName %10 "b2"
+               OpName %12 "b3"
+               OpName %13 "b4"
+               OpName %16 "f1"
+               OpName %18 "f2"
+               OpName %20 "cf1"
+               OpName %22 "cf2"
+               OpName %26 "i1"
+               OpName %28 "i2"
+               OpName %30 "ci1"
+               OpName %32 "ci2"
+               OpName %36 "value"
+               OpDecorate %26 RelaxedPrecision
+               OpDecorate %28 RelaxedPrecision
+               OpDecorate %30 RelaxedPrecision
+               OpDecorate %32 RelaxedPrecision
+               OpDecorate %36 Location 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool
+          %7 = OpTypePointer Function %6
+          %9 = OpConstantTrue %6
+         %11 = OpConstantFalse %6
+         %14 = OpTypeFloat 32
+         %15 = OpTypePointer Function %14
+         %17 = OpConstant %14 1.23000002
+         %19 = OpConstant %14 1.11000001
+         %21 = OpConstant %14 2
+         %23 = OpConstant %14 3.29999995
+         %24 = OpTypeInt 32 1
+         %25 = OpTypePointer Function %24
+         %27 = OpConstant %24 1
+         %29 = OpConstant %24 100
+         %31 = OpConstant %24 123
+         %33 = OpConstant %24 1111
+         %35 = OpTypePointer Input %14
+         %36 = OpVariable %35 Input
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %10 = OpVariable %7 Function
+         %12 = OpVariable %7 Function
+         %13 = OpVariable %7 Function
+         %16 = OpVariable %15 Function
+         %18 = OpVariable %15 Function
+         %20 = OpVariable %15 Function
+         %22 = OpVariable %15 Function
+         %26 = OpVariable %25 Function
+         %28 = OpVariable %25 Function
+         %30 = OpVariable %25 Function
+         %32 = OpVariable %25 Function
+               OpStore %8 %9
+               OpStore %10 %11
+               OpStore %12 %9
+               OpStore %13 %11
+               OpStore %16 %17
+               OpStore %18 %19
+               OpStore %20 %21
+               OpStore %22 %23
+               OpStore %26 %27
+               OpStore %28 %29
+               OpStore %30 %31
+               OpStore %32 %33
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const std::unique_ptr<opt::IRContext> context =
+      BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  spvtools::ValidatorOptions validator_options;
+  ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
+                                               kConsoleMessageConsumer));
+  TransformationContext transformation_context(
+      MakeUnique<FactManager>(context.get()), validator_options);
+
+  opt::IRContext* ir_context = context.get();
+
+  uint32_t word1 = fuzzerutil::FloatToWord(2);
+  uint32_t word2 = fuzzerutil::FloatToWord(1.23f);
+
+  // A 32 bit float constant of value 2 exists and its id is 21.
+  ASSERT_EQ(21, fuzzerutil::MaybeGetFloatConstant(
+                    ir_context, transformation_context,
+                    std::vector<uint32_t>{word1}, 32, false));
+  // A 32 bit float constant of value 1.23 exists and its id is 17.
+  ASSERT_EQ(17, fuzzerutil::MaybeGetFloatConstant(
+                    ir_context, transformation_context,
+                    std::vector<uint32_t>{word2}, 32, false));
+}
+
+TEST(FuzzerutilTest, FuzzerUtilMaybeGetFloatTypeTest) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %92 %52 %53
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpDecorate %92 BuiltIn FragCoord
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypeFloat 32
+          %8 = OpTypeStruct %6 %7
+          %9 = OpTypePointer Function %8
+         %10 = OpTypeFunction %6 %9
+         %14 = OpConstant %6 0
+         %15 = OpTypePointer Function %6
+         %51 = OpTypePointer Private %6
+         %21 = OpConstant %6 2
+         %23 = OpConstant %6 1
+         %24 = OpConstant %7 1
+         %25 = OpTypePointer Function %7
+         %50 = OpTypePointer Private %7
+         %34 = OpTypeBool
+         %35 = OpConstantFalse %34
+         %60 = OpConstantNull %50
+         %61 = OpUndef %51
+         %52 = OpVariable %50 Private
+         %53 = OpVariable %51 Private
+         %80 = OpConstantComposite %8 %21 %24
+         %90 = OpTypeVector %7 4
+         %91 = OpTypePointer Input %90
+         %92 = OpVariable %91 Input
+         %93 = OpConstantComposite %90 %24 %24 %24 %24
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %20 = OpVariable %9 Function
+         %27 = OpVariable %9 Function
+         %22 = OpAccessChain %15 %20 %14
+         %44 = OpCopyObject %9 %20
+         %26 = OpAccessChain %25 %20 %23
+         %29 = OpFunctionCall %6 %12 %27
+         %30 = OpAccessChain %15 %20 %14
+         %45 = OpCopyObject %15 %30
+         %81 = OpCopyObject %9 %27
+         %33 = OpAccessChain %15 %20 %14
+               OpSelectionMerge %37 None
+               OpBranchConditional %35 %36 %37
+         %36 = OpLabel
+         %38 = OpAccessChain %15 %20 %14
+         %40 = OpAccessChain %15 %20 %14
+         %43 = OpAccessChain %15 %20 %14
+         %82 = OpCopyObject %9 %27
+               OpBranch %37
+         %37 = OpLabel
+               OpReturn
+               OpFunctionEnd
+         %12 = OpFunction %6 None %10
+         %11 = OpFunctionParameter %9
+         %13 = OpLabel
+         %46 = OpCopyObject %9 %11
+         %16 = OpAccessChain %15 %11 %14
+         %95 = OpCopyObject %8 %80
+               OpReturnValue %21
+        %100 = OpLabel
+               OpUnreachable
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const std::unique_ptr<opt::IRContext> context =
+      BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  spvtools::ValidatorOptions validator_options;
+  ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
+                                               kConsoleMessageConsumer));
+
+  opt::IRContext* ir_context = context.get();
+  // A float type with width = 32 and result id of 7 exists.
+  ASSERT_EQ(7, fuzzerutil::MaybeGetFloatType(ir_context, 32));
+
+  // A float int type with width = 32 exists, but the id should be 7.
+  ASSERT_NE(5, fuzzerutil::MaybeGetFloatType(ir_context, 32));
+
+  // A float type with width 30 does not exist.
+  ASSERT_EQ(0, fuzzerutil::MaybeGetFloatType(ir_context, 30));
+}
+
+TEST(FuzzerutilTest, FuzzerUtilMaybeGetIntegerConstantFromValueAndTypeTest) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %36
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %8 "b1"
+               OpName %10 "b2"
+               OpName %12 "b3"
+               OpName %13 "b4"
+               OpName %16 "f1"
+               OpName %18 "f2"
+               OpName %22 "zc"
+               OpName %24 "i1"
+               OpName %28 "i2"
+               OpName %30 "i3"
+               OpName %32 "i4"
+               OpName %36 "value"
+               OpDecorate %22 RelaxedPrecision
+               OpDecorate %24 RelaxedPrecision
+               OpDecorate %28 RelaxedPrecision
+               OpDecorate %30 RelaxedPrecision
+               OpDecorate %32 RelaxedPrecision
+               OpDecorate %36 Location 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool
+          %7 = OpTypePointer Function %6
+          %9 = OpConstantTrue %6
+         %11 = OpConstantFalse %6
+         %14 = OpTypeFloat 32
+         %15 = OpTypePointer Function %14
+         %17 = OpConstant %14 1.23000002
+         %19 = OpConstant %14 1.11000001
+         %20 = OpTypeInt 32 1
+         %21 = OpTypePointer Function %20
+         %23 = OpConstant %20 0
+         %25 = OpConstant %20 1
+         %26 = OpTypeInt 32 0
+         %27 = OpTypePointer Function %26
+         %29 = OpConstant %26 100
+         %31 = OpConstant %20 -1
+         %33 = OpConstant %20 -99
+         %35 = OpTypePointer Input %14
+         %36 = OpVariable %35 Input
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %10 = OpVariable %7 Function
+         %12 = OpVariable %7 Function
+         %13 = OpVariable %7 Function
+         %16 = OpVariable %15 Function
+         %18 = OpVariable %15 Function
+         %22 = OpVariable %21 Function
+         %24 = OpVariable %21 Function
+         %28 = OpVariable %27 Function
+         %30 = OpVariable %21 Function
+         %32 = OpVariable %21 Function
+               OpStore %8 %9
+               OpStore %10 %11
+               OpStore %12 %9
+               OpStore %13 %11
+               OpStore %16 %17
+               OpStore %18 %19
+               OpStore %22 %23
+               OpStore %24 %25
+               OpStore %28 %29
+               OpStore %30 %31
+               OpStore %32 %33
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const std::unique_ptr<opt::IRContext> context =
+      BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  spvtools::ValidatorOptions validator_options;
+  ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
+                                               kConsoleMessageConsumer));
+
+  opt::IRContext* ir_context = context.get();
+
+  // A 32 bit signed int constant (with int type id 20) with value 1 exists and
+  // the id is 25.
+  ASSERT_EQ(25, fuzzerutil::MaybeGetIntegerConstantFromValueAndType(ir_context,
+                                                                    1, 20));
+  // A 32 bit unsigned int constant (with int type id 0) with value 100 exists
+  // and the id is 29.
+  ASSERT_EQ(29, fuzzerutil::MaybeGetIntegerConstantFromValueAndType(ir_context,
+                                                                    100, 26));
+  // A 32 bit unsigned int constant with value 50 does not exist.
+  ASSERT_EQ(0, fuzzerutil::MaybeGetIntegerConstantFromValueAndType(ir_context,
+                                                                   50, 26));
+}
+
+TEST(FuzzerutilTest, FuzzerUtilMaybeGetIntegerConstantTest) {
+  std::string shader = R"(
+OpCapability Shader
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %36
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %8 "b1"
+               OpName %10 "b2"
+               OpName %12 "b3"
+               OpName %13 "b4"
+               OpName %16 "f1"
+               OpName %18 "f2"
+               OpName %22 "zc"
+               OpName %24 "i1"
+               OpName %28 "i2"
+               OpName %30 "i3"
+               OpName %32 "i4"
+               OpName %36 "value"
+               OpDecorate %22 RelaxedPrecision
+               OpDecorate %24 RelaxedPrecision
+               OpDecorate %28 RelaxedPrecision
+               OpDecorate %30 RelaxedPrecision
+               OpDecorate %32 RelaxedPrecision
+               OpDecorate %36 Location 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool
+          %7 = OpTypePointer Function %6
+          %9 = OpConstantTrue %6
+         %11 = OpConstantFalse %6
+         %14 = OpTypeFloat 32
+         %15 = OpTypePointer Function %14
+         %17 = OpConstant %14 1.23000002
+         %19 = OpConstant %14 1.11000001
+         %20 = OpTypeInt 32 1
+         %21 = OpTypePointer Function %20
+         %23 = OpConstant %20 0
+         %25 = OpConstant %20 1
+         %26 = OpTypeInt 32 0
+         %27 = OpTypePointer Function %26
+         %29 = OpConstant %26 100
+         %31 = OpConstant %20 -1
+         %33 = OpConstant %20 -99
+         %35 = OpTypePointer Input %14
+         %36 = OpVariable %35 Input
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %10 = OpVariable %7 Function
+         %12 = OpVariable %7 Function
+         %13 = OpVariable %7 Function
+         %16 = OpVariable %15 Function
+         %18 = OpVariable %15 Function
+         %22 = OpVariable %21 Function
+         %24 = OpVariable %21 Function
+         %28 = OpVariable %27 Function
+         %30 = OpVariable %21 Function
+         %32 = OpVariable %21 Function
+               OpStore %8 %9
+               OpStore %10 %11
+               OpStore %12 %9
+               OpStore %13 %11
+               OpStore %16 %17
+               OpStore %18 %19
+               OpStore %22 %23
+               OpStore %24 %25
+               OpStore %28 %29
+               OpStore %30 %31
+               OpStore %32 %33
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const std::unique_ptr<opt::IRContext> context =
+      BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  spvtools::ValidatorOptions validator_options;
+  ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
+                                               kConsoleMessageConsumer));
+  TransformationContext transformation_context(
+      MakeUnique<FactManager>(context.get()), validator_options);
+
+  opt::IRContext* ir_context = context.get();
+
+  // A 32 bit unsigned int constant with value 1 exists and the id is 25.
+  ASSERT_EQ(25, fuzzerutil::MaybeGetIntegerConstant(
+                    ir_context, transformation_context,
+                    std::vector<uint32_t>{1}, 32, true, false));
+  // A 32 bit unsigned int constant with value 100 exists and the id is 29.
+  ASSERT_EQ(29, fuzzerutil::MaybeGetIntegerConstant(
+                    ir_context, transformation_context,
+                    std::vector<uint32_t>{100}, 32, false, false));
+  // A 32 bit signed int constant with value 99 doesn't not exist and should
+  // return 0.
+  ASSERT_EQ(0, fuzzerutil::MaybeGetIntegerConstant(
+                   ir_context, transformation_context,
+                   std::vector<uint32_t>{99}, 32, true, false));
+}
+
+TEST(FuzzerutilTest, FuzzerUtilMaybeGetIntegerTypeTest) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %92 %52 %53
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpDecorate %92 BuiltIn FragCoord
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypeFloat 32
+          %8 = OpTypeStruct %6 %7
+          %9 = OpTypePointer Function %8
+         %10 = OpTypeFunction %6 %9
+         %14 = OpConstant %6 0
+         %15 = OpTypePointer Function %6
+         %51 = OpTypePointer Private %6
+         %21 = OpConstant %6 2
+         %23 = OpConstant %6 1
+         %24 = OpConstant %7 1
+         %25 = OpTypePointer Function %7
+         %50 = OpTypePointer Private %7
+         %34 = OpTypeBool
+         %35 = OpConstantFalse %34
+         %60 = OpConstantNull %50
+         %61 = OpUndef %51
+         %52 = OpVariable %50 Private
+         %53 = OpVariable %51 Private
+         %80 = OpConstantComposite %8 %21 %24
+         %90 = OpTypeVector %7 4
+         %91 = OpTypePointer Input %90
+         %92 = OpVariable %91 Input
+         %93 = OpConstantComposite %90 %24 %24 %24 %24
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %20 = OpVariable %9 Function
+         %27 = OpVariable %9 Function
+         %22 = OpAccessChain %15 %20 %14
+         %44 = OpCopyObject %9 %20
+         %26 = OpAccessChain %25 %20 %23
+         %29 = OpFunctionCall %6 %12 %27
+         %30 = OpAccessChain %15 %20 %14
+         %45 = OpCopyObject %15 %30
+         %81 = OpCopyObject %9 %27
+         %33 = OpAccessChain %15 %20 %14
+               OpSelectionMerge %37 None
+               OpBranchConditional %35 %36 %37
+         %36 = OpLabel
+         %38 = OpAccessChain %15 %20 %14
+         %40 = OpAccessChain %15 %20 %14
+         %43 = OpAccessChain %15 %20 %14
+         %82 = OpCopyObject %9 %27
+               OpBranch %37
+         %37 = OpLabel
+               OpReturn
+               OpFunctionEnd
+         %12 = OpFunction %6 None %10
+         %11 = OpFunctionParameter %9
+         %13 = OpLabel
+         %46 = OpCopyObject %9 %11
+         %16 = OpAccessChain %15 %11 %14
+         %95 = OpCopyObject %8 %80
+               OpReturnValue %21
+        %100 = OpLabel
+               OpUnreachable
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const std::unique_ptr<opt::IRContext> context =
+      BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  spvtools::ValidatorOptions validator_options;
+  ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
+                                               kConsoleMessageConsumer));
+
+  opt::IRContext* ir_context = context.get();
+
+  // A signed int type with width = 32 and result id of 6 exists.
+  ASSERT_EQ(6, fuzzerutil::MaybeGetIntegerType(ir_context, 32, true));
+
+  // A signed int type with width = 32 exists, but the id should be 6.
+  ASSERT_FALSE(fuzzerutil::MaybeGetIntegerType(ir_context, 32, true) == 5);
+
+  // A int type with width = 32 and result id of 6 exists, but it should be a
+  // signed int.
+  ASSERT_EQ(0, fuzzerutil::MaybeGetIntegerType(ir_context, 32, false));
+  // A signed int type with width 30 does not exist.
+  ASSERT_EQ(0, fuzzerutil::MaybeGetIntegerType(ir_context, 30, true));
+  // An unsigned int type with width 22 does not exist.
+  ASSERT_EQ(0, fuzzerutil::MaybeGetIntegerType(ir_context, 22, false));
+}
+
+TEST(FuzzerutilTest, FuzzerUtilMaybeGetPointerTypeTest) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %92 %52 %53
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpDecorate %92 BuiltIn FragCoord
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypeFloat 32
+          %8 = OpTypeStruct %6 %7
+          %9 = OpTypePointer Function %8
+         %10 = OpTypeFunction %6 %9
+         %14 = OpConstant %6 0
+         %15 = OpTypePointer Function %6
+         %51 = OpTypePointer Private %6
+         %21 = OpConstant %6 2
+         %23 = OpConstant %6 1
+         %24 = OpConstant %7 1
+         %25 = OpTypePointer Function %7
+         %50 = OpTypePointer Private %7
+         %34 = OpTypeBool
+         %35 = OpConstantFalse %34
+         %60 = OpConstantNull %50
+         %61 = OpUndef %51
+         %52 = OpVariable %50 Private
+         %53 = OpVariable %51 Private
+         %80 = OpConstantComposite %8 %21 %24
+         %90 = OpTypeVector %7 4
+         %91 = OpTypePointer Input %90
+         %92 = OpVariable %91 Input
+         %93 = OpConstantComposite %90 %24 %24 %24 %24
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %20 = OpVariable %9 Function
+         %27 = OpVariable %9 Function
+         %22 = OpAccessChain %15 %20 %14
+         %44 = OpCopyObject %9 %20
+         %26 = OpAccessChain %25 %20 %23
+         %29 = OpFunctionCall %6 %12 %27
+         %30 = OpAccessChain %15 %20 %14
+         %45 = OpCopyObject %15 %30
+         %81 = OpCopyObject %9 %27
+         %33 = OpAccessChain %15 %20 %14
+               OpSelectionMerge %37 None
+               OpBranchConditional %35 %36 %37
+         %36 = OpLabel
+         %38 = OpAccessChain %15 %20 %14
+         %40 = OpAccessChain %15 %20 %14
+         %43 = OpAccessChain %15 %20 %14
+         %82 = OpCopyObject %9 %27
+               OpBranch %37
+         %37 = OpLabel
+               OpReturn
+               OpFunctionEnd
+         %12 = OpFunction %6 None %10
+         %11 = OpFunctionParameter %9
+         %13 = OpLabel
+         %46 = OpCopyObject %9 %11
+         %16 = OpAccessChain %15 %11 %14
+         %95 = OpCopyObject %8 %80
+               OpReturnValue %21
+        %100 = OpLabel
+               OpUnreachable
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const std::unique_ptr<opt::IRContext> context =
+      BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  spvtools::ValidatorOptions validator_options;
+  ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
+                                               kConsoleMessageConsumer));
+
+  opt::IRContext* ir_context = context.get();
+  auto private_storage_class = SpvStorageClassPrivate;
+  auto function_storage_class = SpvStorageClassFunction;
+  auto input_storage_class = SpvStorageClassInput;
+
+  // A valid pointer must have the correct |pointee_type_id| and |storageClass|.
+  // A function type pointer with id = 9 and pointee type id 8 should be found.
+  ASSERT_EQ(9, fuzzerutil::MaybeGetPointerType(ir_context, 8,
+                                               function_storage_class));
+  // A function type pointer with id = 15 and pointee type id 6 should be found.
+  ASSERT_EQ(15, fuzzerutil::MaybeGetPointerType(ir_context, 6,
+                                                function_storage_class));
+  // A function type pointer with id = 25 and pointee type id 7 should be found.
+  ASSERT_EQ(25, fuzzerutil::MaybeGetPointerType(ir_context, 7,
+                                                function_storage_class));
+
+  // A private type pointer with id=51 and pointee type id 6 should be found.
+  ASSERT_EQ(51, fuzzerutil::MaybeGetPointerType(ir_context, 6,
+                                                private_storage_class));
+  // A function pointer with id=50 and pointee type id 7 should be found.
+  ASSERT_EQ(50, fuzzerutil::MaybeGetPointerType(ir_context, 7,
+                                                private_storage_class));
+
+  // A input type pointer with id=91 and pointee type id 90 should be found.
+  ASSERT_EQ(
+      91, fuzzerutil::MaybeGetPointerType(ir_context, 90, input_storage_class));
+
+  // A pointer with id=91 and pointee type 90 exisits, but the type should be
+  // input.
+  ASSERT_EQ(0, fuzzerutil::MaybeGetPointerType(ir_context, 90,
+                                               function_storage_class));
+  // A input type pointer with id=91 exists but the pointee id should be 90.
+  ASSERT_EQ(
+      0, fuzzerutil::MaybeGetPointerType(ir_context, 89, input_storage_class));
+  // A input type pointer with pointee id 90 exists but result id of the pointer
+  // should be 91.
+  ASSERT_NE(
+      58, fuzzerutil::MaybeGetPointerType(ir_context, 90, input_storage_class));
+}
+
+TEST(FuzzerutilTest, FuzzerUtilMaybeGetScalarConstantTest) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %56
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %8 "b1"
+               OpName %10 "b2"
+               OpName %12 "b3"
+               OpName %13 "b4"
+               OpName %16 "f1"
+               OpName %18 "f2"
+               OpName %22 "zc"
+               OpName %24 "i1"
+               OpName %28 "i2"
+               OpName %30 "i"
+               OpName %32 "i3"
+               OpName %34 "i4"
+               OpName %39 "f_arr"
+               OpName %49 "i_arr"
+               OpName %56 "value"
+               OpDecorate %22 RelaxedPrecision
+               OpDecorate %24 RelaxedPrecision
+               OpDecorate %28 RelaxedPrecision
+               OpDecorate %30 RelaxedPrecision
+               OpDecorate %32 RelaxedPrecision
+               OpDecorate %34 RelaxedPrecision
+               OpDecorate %49 RelaxedPrecision
+               OpDecorate %56 Location 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool
+          %7 = OpTypePointer Function %6
+          %9 = OpConstantTrue %6
+         %11 = OpConstantFalse %6
+         %14 = OpTypeFloat 32
+         %15 = OpTypePointer Function %14
+         %17 = OpConstant %14 1.23000002
+         %19 = OpConstant %14 1.11000001
+         %20 = OpTypeInt 32 1
+         %21 = OpTypePointer Function %20
+         %23 = OpConstant %20 0
+         %25 = OpConstant %20 1
+         %26 = OpTypeInt 32 0
+         %27 = OpTypePointer Function %26
+         %29 = OpConstant %26 100
+         %31 = OpConstant %26 0
+         %33 = OpConstant %20 -1
+         %35 = OpConstant %20 -99
+         %36 = OpConstant %26 5
+         %37 = OpTypeArray %14 %36
+         %38 = OpTypePointer Function %37
+         %40 = OpConstant %14 5.5
+         %41 = OpConstant %14 4.4000001
+         %42 = OpConstant %14 3.29999995
+         %43 = OpConstant %14 2.20000005
+         %44 = OpConstant %14 1.10000002
+         %45 = OpConstantComposite %37 %40 %41 %42 %43 %44
+         %46 = OpConstant %26 3
+         %47 = OpTypeArray %20 %46
+         %48 = OpTypePointer Function %47
+         %50 = OpConstant %20 3
+         %51 = OpConstant %20 7
+         %52 = OpConstant %20 9
+         %53 = OpConstantComposite %47 %50 %51 %52
+         %55 = OpTypePointer Input %14
+         %56 = OpVariable %55 Input
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %10 = OpVariable %7 Function
+         %12 = OpVariable %7 Function
+         %13 = OpVariable %7 Function
+         %16 = OpVariable %15 Function
+         %18 = OpVariable %15 Function
+         %22 = OpVariable %21 Function
+         %24 = OpVariable %21 Function
+         %28 = OpVariable %27 Function
+         %30 = OpVariable %27 Function
+         %32 = OpVariable %21 Function
+         %34 = OpVariable %21 Function
+         %39 = OpVariable %38 Function
+         %49 = OpVariable %48 Function
+               OpStore %8 %9
+               OpStore %10 %11
+               OpStore %12 %9
+               OpStore %13 %11
+               OpStore %16 %17
+               OpStore %18 %19
+               OpStore %22 %23
+               OpStore %24 %25
+               OpStore %28 %29
+               OpStore %30 %31
+               OpStore %32 %33
+               OpStore %34 %35
+               OpStore %39 %45
+               OpStore %49 %53
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const std::unique_ptr<opt::IRContext> context =
+      BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  spvtools::ValidatorOptions validator_options;
+  ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
+                                               kConsoleMessageConsumer));
+  TransformationContext transformation_context(
+      MakeUnique<FactManager>(context.get()), validator_options);
+
+  opt::IRContext* ir_context = context.get();
+
+  std::vector<uint32_t> uint_words1 = fuzzerutil::IntToWords(100, 32, false);
+  std::vector<uint32_t> uint_words2 = fuzzerutil::IntToWords(0, 32, false);
+  std::vector<uint32_t> int_words1 = fuzzerutil::IntToWords(-99, 32, true);
+  std::vector<uint32_t> int_words2 = fuzzerutil::IntToWords(1, 32, true);
+  uint32_t float_word1 = fuzzerutil::FloatToWord(1.11f);
+  uint32_t float_word2 = fuzzerutil::FloatToWord(4.4f);
+
+  // A unsigned int of value 100 that has a scalar type id of 26 exists and its
+  // id is 29.
+  ASSERT_EQ(
+      29, fuzzerutil::MaybeGetScalarConstant(ir_context, transformation_context,
+                                             uint_words1, 26, false));
+  // A unsigned int of value 0 that has a scalar type id of 26 exists and its id
+  // is 29.
+  ASSERT_EQ(
+      31, fuzzerutil::MaybeGetScalarConstant(ir_context, transformation_context,
+                                             uint_words2, 26, false));
+  // A signed int of value -99 that has a scalar type id of 20 exists and its id
+  // is 35.
+  ASSERT_EQ(35, fuzzerutil::MaybeGetScalarConstant(
+                    ir_context, transformation_context, int_words1, 20, false));
+  // A signed int of value 1 that has a scalar type id of 20 exists and its id
+  // is 25.
+  ASSERT_EQ(25, fuzzerutil::MaybeGetScalarConstant(
+                    ir_context, transformation_context, int_words2, 20, false));
+  // A float of value 1.11 that has a scalar type id of 14 exists and its id
+  // is 19.
+  ASSERT_EQ(19, fuzzerutil::MaybeGetScalarConstant(
+                    ir_context, transformation_context,
+                    std::vector<uint32_t>{float_word1}, 14, false));
+  // A signed int of value 1 that has a scalar type id of 20 exists and its id
+  // is 25.
+  ASSERT_EQ(41, fuzzerutil::MaybeGetScalarConstant(
+                    ir_context, transformation_context,
+                    std::vector<uint32_t>{float_word2}, 14, false));
+}
+
+TEST(FuzzerutilTest, FuzzerUtilMaybeGetStructTypeTest) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %92 %52 %53
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpDecorate %92 BuiltIn FragCoord
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypeFloat 32
+          %8 = OpTypeStruct %6 %7
+          %9 = OpTypePointer Function %8
+         %10 = OpTypeFunction %6 %9
+         %14 = OpConstant %6 0
+         %15 = OpTypePointer Function %6
+         %51 = OpTypePointer Private %6
+         %21 = OpConstant %6 2
+         %23 = OpConstant %6 1
+         %24 = OpConstant %7 1
+         %25 = OpTypePointer Function %7
+         %50 = OpTypePointer Private %7
+         %34 = OpTypeBool
+         %35 = OpConstantFalse %34
+         %60 = OpConstantNull %50
+         %61 = OpUndef %51
+         %52 = OpVariable %50 Private
+         %53 = OpVariable %51 Private
+         %80 = OpConstantComposite %8 %21 %24
+         %90 = OpTypeVector %7 4
+         %91 = OpTypePointer Input %90
+         %92 = OpVariable %91 Input
+         %93 = OpConstantComposite %90 %24 %24 %24 %24
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %20 = OpVariable %9 Function
+         %27 = OpVariable %9 Function
+         %22 = OpAccessChain %15 %20 %14
+         %44 = OpCopyObject %9 %20
+         %26 = OpAccessChain %25 %20 %23
+         %29 = OpFunctionCall %6 %12 %27
+         %30 = OpAccessChain %15 %20 %14
+         %45 = OpCopyObject %15 %30
+         %81 = OpCopyObject %9 %27
+         %33 = OpAccessChain %15 %20 %14
+               OpSelectionMerge %37 None
+               OpBranchConditional %35 %36 %37
+         %36 = OpLabel
+         %38 = OpAccessChain %15 %20 %14
+         %40 = OpAccessChain %15 %20 %14
+         %43 = OpAccessChain %15 %20 %14
+         %82 = OpCopyObject %9 %27
+               OpBranch %37
+         %37 = OpLabel
+               OpReturn
+               OpFunctionEnd
+         %12 = OpFunction %6 None %10
+         %11 = OpFunctionParameter %9
+         %13 = OpLabel
+         %46 = OpCopyObject %9 %11
+         %16 = OpAccessChain %15 %11 %14
+         %95 = OpCopyObject %8 %80
+               OpReturnValue %21
+        %100 = OpLabel
+               OpUnreachable
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const std::unique_ptr<opt::IRContext> context =
+      BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  spvtools::ValidatorOptions validator_options;
+  ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
+                                               kConsoleMessageConsumer));
+
+  opt::IRContext* ir_context = context.get();
+
+  // 6 and 7 are all valid ids from OpTypeInt and OpTypeFloat
+  // so the result id of 8 should be found.
+  ASSERT_EQ(8, fuzzerutil::MaybeGetStructType(ir_context,
+                                              std::vector<uint32_t>{6, 7}));
+
+  // |component_type_id| of 16 does not exist in the module, so such a struct
+  // type cannot be found.
+  ASSERT_EQ(0, fuzzerutil::MaybeGetStructType(ir_context,
+                                              std::vector<uint32_t>(6, 16)));
+
+  // |component_type_id| of 10 is of OpTypeFunction type and thus the struct
+  // cannot be found.
+  ASSERT_EQ(0, fuzzerutil::MaybeGetStructType(ir_context,
+                                              std::vector<uint32_t>(6, 10)));
+}
+
+TEST(FuzzerutilTest, FuzzerUtilMaybeGetVectorTypeTest) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %92 %52 %53
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpDecorate %92 BuiltIn FragCoord
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypeFloat 32
+          %8 = OpTypeStruct %6 %7
+          %9 = OpTypePointer Function %8
+         %10 = OpTypeFunction %6 %9
+         %14 = OpConstant %6 0
+         %15 = OpTypePointer Function %6
+         %51 = OpTypePointer Private %6
+         %21 = OpConstant %6 2
+         %23 = OpConstant %6 1
+         %24 = OpConstant %7 1
+         %25 = OpTypePointer Function %7
+         %50 = OpTypePointer Private %7
+         %34 = OpTypeBool
+         %35 = OpConstantFalse %34
+         %60 = OpConstantNull %50
+         %61 = OpUndef %51
+         %52 = OpVariable %50 Private
+         %53 = OpVariable %51 Private
+         %80 = OpConstantComposite %8 %21 %24
+         %90 = OpTypeVector %7 4
+         %91 = OpTypePointer Input %90
+         %92 = OpVariable %91 Input
+         %93 = OpConstantComposite %90 %24 %24 %24 %24
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %20 = OpVariable %9 Function
+         %27 = OpVariable %9 Function
+         %22 = OpAccessChain %15 %20 %14
+         %44 = OpCopyObject %9 %20
+         %26 = OpAccessChain %25 %20 %23
+         %29 = OpFunctionCall %6 %12 %27
+         %30 = OpAccessChain %15 %20 %14
+         %45 = OpCopyObject %15 %30
+         %81 = OpCopyObject %9 %27
+         %33 = OpAccessChain %15 %20 %14
+               OpSelectionMerge %37 None
+               OpBranchConditional %35 %36 %37
+         %36 = OpLabel
+         %38 = OpAccessChain %15 %20 %14
+         %40 = OpAccessChain %15 %20 %14
+         %43 = OpAccessChain %15 %20 %14
+         %82 = OpCopyObject %9 %27
+               OpBranch %37
+         %37 = OpLabel
+               OpReturn
+               OpFunctionEnd
+         %12 = OpFunction %6 None %10
+         %11 = OpFunctionParameter %9
+         %13 = OpLabel
+         %46 = OpCopyObject %9 %11
+         %16 = OpAccessChain %15 %11 %14
+         %95 = OpCopyObject %8 %80
+               OpReturnValue %21
+        %100 = OpLabel
+               OpUnreachable
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const std::unique_ptr<opt::IRContext> context =
+      BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  spvtools::ValidatorOptions validator_options;
+  ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
+                                               kConsoleMessageConsumer));
+
+  opt::IRContext* ir_context = context.get();
+  // The vector type with |element_count| 4 and |component_type_id| 7
+  // is present and has a result id of 90.
+  ASSERT_EQ(90, fuzzerutil::MaybeGetVectorType(ir_context, 7, 4));
+
+  // The vector type with |element_count| 3 and |component_type_id| 7
+  // is not present in the module.
+  ASSERT_EQ(0, fuzzerutil::MaybeGetVectorType(ir_context, 7, 3));
+
+#ifndef NDEBUG
+  // It should abort with |component_type_id| of 100
+  // |component_type_id| must be a valid result id of an OpTypeInt,
+  // OpTypeFloat or OpTypeBool instruction in the module.
+  ASSERT_DEATH(fuzzerutil::MaybeGetVectorType(ir_context, 100, 4),
+               "\\|component_type_id\\| is invalid");
+
+  // It should abort with |element_count| of 5.
+  // |element_count| must be in the range [2,4].
+  ASSERT_DEATH(fuzzerutil::MaybeGetVectorType(ir_context, 7, 5),
+               "Precondition: component count must be in range \\[2, 4\\].");
+#endif
+}
+
+TEST(FuzzerutilTest, FuzzerUtilMaybeGetVoidTypeTest) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %92 %52 %53
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpDecorate %92 BuiltIn FragCoord
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypeFloat 32
+          %8 = OpTypeStruct %6 %7
+          %9 = OpTypePointer Function %8
+         %10 = OpTypeFunction %6 %9
+         %14 = OpConstant %6 0
+         %15 = OpTypePointer Function %6
+         %51 = OpTypePointer Private %6
+         %21 = OpConstant %6 2
+         %23 = OpConstant %6 1
+         %24 = OpConstant %7 1
+         %25 = OpTypePointer Function %7
+         %50 = OpTypePointer Private %7
+         %34 = OpTypeBool
+         %35 = OpConstantFalse %34
+         %60 = OpConstantNull %50
+         %61 = OpUndef %51
+         %52 = OpVariable %50 Private
+         %53 = OpVariable %51 Private
+         %80 = OpConstantComposite %8 %21 %24
+         %90 = OpTypeVector %7 4
+         %91 = OpTypePointer Input %90
+         %92 = OpVariable %91 Input
+         %93 = OpConstantComposite %90 %24 %24 %24 %24
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %20 = OpVariable %9 Function
+         %27 = OpVariable %9 Function
+         %22 = OpAccessChain %15 %20 %14
+         %44 = OpCopyObject %9 %20
+         %26 = OpAccessChain %25 %20 %23
+         %29 = OpFunctionCall %6 %12 %27
+         %30 = OpAccessChain %15 %20 %14
+         %45 = OpCopyObject %15 %30
+         %81 = OpCopyObject %9 %27
+         %33 = OpAccessChain %15 %20 %14
+               OpSelectionMerge %37 None
+               OpBranchConditional %35 %36 %37
+         %36 = OpLabel
+         %38 = OpAccessChain %15 %20 %14
+         %40 = OpAccessChain %15 %20 %14
+         %43 = OpAccessChain %15 %20 %14
+         %82 = OpCopyObject %9 %27
+               OpBranch %37
+         %37 = OpLabel
+               OpReturn
+               OpFunctionEnd
+         %12 = OpFunction %6 None %10
+         %11 = OpFunctionParameter %9
+         %13 = OpLabel
+         %46 = OpCopyObject %9 %11
+         %16 = OpAccessChain %15 %11 %14
+         %95 = OpCopyObject %8 %80
+               OpReturnValue %21
+        %100 = OpLabel
+               OpUnreachable
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const std::unique_ptr<opt::IRContext> context =
+      BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  spvtools::ValidatorOptions validator_options;
+  ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
+                                               kConsoleMessageConsumer));
+
+  opt::IRContext* ir_context = context.get();
+  // A void type with a result id of 2 can be found.
+  ASSERT_EQ(2, fuzzerutil::MaybeGetVoidType(ir_context));
+}
+
+TEST(FuzzerutilTest, FuzzerUtilMaybeGetZeroConstantTest) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %56
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %8 "b1"
+               OpName %10 "b2"
+               OpName %12 "b3"
+               OpName %13 "b4"
+               OpName %16 "f1"
+               OpName %18 "f2"
+               OpName %22 "zc"
+               OpName %24 "i1"
+               OpName %28 "i2"
+               OpName %30 "i"
+               OpName %32 "i3"
+               OpName %34 "i4"
+               OpName %39 "f_arr"
+               OpName %49 "i_arr"
+               OpName %56 "value"
+               OpDecorate %22 RelaxedPrecision
+               OpDecorate %24 RelaxedPrecision
+               OpDecorate %28 RelaxedPrecision
+               OpDecorate %30 RelaxedPrecision
+               OpDecorate %32 RelaxedPrecision
+               OpDecorate %34 RelaxedPrecision
+               OpDecorate %49 RelaxedPrecision
+               OpDecorate %56 Location 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool
+          %7 = OpTypePointer Function %6
+          %9 = OpConstantTrue %6
+         %11 = OpConstantFalse %6
+         %14 = OpTypeFloat 32
+         %15 = OpTypePointer Function %14
+         %17 = OpConstant %14 1.23000002
+         %19 = OpConstant %14 1.11000001
+         %20 = OpTypeInt 32 1
+         %21 = OpTypePointer Function %20
+         %23 = OpConstant %20 0
+         %25 = OpConstant %20 1
+         %26 = OpTypeInt 32 0
+         %27 = OpTypePointer Function %26
+         %29 = OpConstant %26 100
+         %31 = OpConstant %26 0
+         %33 = OpConstant %20 -1
+         %35 = OpConstant %20 -99
+         %36 = OpConstant %26 5
+         %37 = OpTypeArray %14 %36
+         %38 = OpTypePointer Function %37
+         %40 = OpConstant %14 5.5
+         %41 = OpConstant %14 4.4000001
+         %42 = OpConstant %14 3.29999995
+         %43 = OpConstant %14 2.20000005
+         %44 = OpConstant %14 1.10000002
+         %45 = OpConstantComposite %37 %40 %41 %42 %43 %44
+         %46 = OpConstant %26 3
+         %47 = OpTypeArray %20 %46
+         %48 = OpTypePointer Function %47
+         %50 = OpConstant %20 3
+         %51 = OpConstant %20 7
+         %52 = OpConstant %20 9
+         %53 = OpConstantComposite %47 %50 %51 %52
+         %55 = OpTypePointer Input %14
+         %56 = OpVariable %55 Input
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %10 = OpVariable %7 Function
+         %12 = OpVariable %7 Function
+         %13 = OpVariable %7 Function
+         %16 = OpVariable %15 Function
+         %18 = OpVariable %15 Function
+         %22 = OpVariable %21 Function
+         %24 = OpVariable %21 Function
+         %28 = OpVariable %27 Function
+         %30 = OpVariable %27 Function
+         %32 = OpVariable %21 Function
+         %34 = OpVariable %21 Function
+         %39 = OpVariable %38 Function
+         %49 = OpVariable %48 Function
+               OpStore %8 %9
+               OpStore %10 %11
+               OpStore %12 %9
+               OpStore %13 %11
+               OpStore %16 %17
+               OpStore %18 %19
+               OpStore %22 %23
+               OpStore %24 %25
+               OpStore %28 %29
+               OpStore %30 %31
+               OpStore %32 %33
+               OpStore %34 %35
+               OpStore %39 %45
+               OpStore %49 %53
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const std::unique_ptr<opt::IRContext> context =
+      BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  spvtools::ValidatorOptions validator_options;
+  ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
+                                               kConsoleMessageConsumer));
+  TransformationContext transformation_context(
+      MakeUnique<FactManager>(context.get()), validator_options);
+
+  opt::IRContext* ir_context = context.get();
+
+  // The id of a boolean constant will be returned give boolean type id 6.
+  uint32_t maybe_bool_id = fuzzerutil::MaybeGetZeroConstant(
+      ir_context, transformation_context, 6, false);
+  // The id of a 32 bit float constant will be returned given the float type
+  // id 14.
+  uint32_t maybe_float_id = fuzzerutil::MaybeGetZeroConstant(
+      ir_context, transformation_context, 14, false);
+  uint32_t maybe_signed_int_id = fuzzerutil::MaybeGetZeroConstant(
+      ir_context, transformation_context, 20, false);
+  uint32_t maybe_unsigned_int_id = fuzzerutil::MaybeGetZeroConstant(
+      ir_context, transformation_context, 26, false);
+
+  // Lists of possible ids for float, signed int, unsigned int and array.
+  std::vector<uint32_t> float_ids{17, 19};
+  std::vector<uint32_t> signed_int_ids{23, 25, 31, 33};
+
+  ASSERT_TRUE(maybe_bool_id == 9 || maybe_bool_id == 11);
+  ASSERT_TRUE(std::find(signed_int_ids.begin(), signed_int_ids.end(),
+                        maybe_signed_int_id) != signed_int_ids.end());
+
+  // There is a unsigned int typed zero constant and its id is 31.
+  ASSERT_EQ(31, maybe_unsigned_int_id);
+
+  // There is no zero float constant.
+  ASSERT_TRUE(std::find(float_ids.begin(), float_ids.end(), maybe_float_id) ==
+              float_ids.end());
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/test/fuzz/shrinker_test.cpp b/test/fuzz/shrinker_test.cpp
index 42cd182..447ebec 100644
--- a/test/fuzz/shrinker_test.cpp
+++ b/test/fuzz/shrinker_test.cpp
@@ -163,8 +163,8 @@
   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
       donor_ir_context.get(), validator_options, kConsoleMessageConsumer));
 
-  PseudoRandomGenerator random_generator(0);
-  FuzzerContext fuzzer_context(&random_generator, 100);
+  FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100,
+                               false);
   TransformationContext transformation_context(
       MakeUnique<FactManager>(variant_ir_context.get()), validator_options);
 
@@ -341,8 +341,8 @@
   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
       donor_ir_context.get(), validator_options, kConsoleMessageConsumer));
 
-  PseudoRandomGenerator random_generator(0);
-  FuzzerContext fuzzer_context(&random_generator, 100);
+  FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100,
+                               false);
   TransformationContext transformation_context(
       MakeUnique<FactManager>(variant_ir_context.get()), validator_options);
 
@@ -365,7 +365,6 @@
           if (inst->opcode() == SpvOpCopyObject) {
             copy_object_count++;
           }
-
         });
     return copy_object_count >= 8;
   };
diff --git a/test/fuzz/transformation_access_chain_test.cpp b/test/fuzz/transformation_access_chain_test.cpp
index 5c43127..e791981 100644
--- a/test/fuzz/transformation_access_chain_test.cpp
+++ b/test/fuzz/transformation_access_chain_test.cpp
@@ -127,6 +127,16 @@
   transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
       54);
 
+  // Check the case where the index type is not a 32-bit integer.
+  TransformationAccessChain invalid_index_example1(
+      101, 28, {29}, MakeInstructionDescriptor(42, SpvOpReturn, 0));
+
+  // Since the index  is not a 32-bit integer type but a 32-bit float type,
+  // ValidIndexComposite should return false and thus the transformation is not
+  // applicable.
+  ASSERT_FALSE(invalid_index_example1.IsApplicable(context.get(),
+                                                   transformation_context));
+
   // Bad: id is not fresh
   ASSERT_FALSE(TransformationAccessChain(
                    43, 43, {80}, MakeInstructionDescriptor(24, SpvOpLoad, 0))
@@ -304,6 +314,20 @@
     ASSERT_FALSE(
         transformation_context.GetFactManager()->PointeeValueIsIrrelevant(107));
   }
+  {
+    // Check the case where the access chain's base pointer has the irrelevant
+    // pointee fact; the resulting access chain should inherit this fact.
+    TransformationAccessChain transformation(
+        107, 54, {}, MakeInstructionDescriptor(24, SpvOpLoad, 0));
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    ApplyAndCheckFreshIds(transformation, context.get(),
+                          &transformation_context);
+    ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
+        context.get(), validator_options, kConsoleMessageConsumer));
+    ASSERT_TRUE(
+        transformation_context.GetFactManager()->PointeeValueIsIrrelevant(54));
+  }
 
   std::string after_transformation = R"(
                OpCapability Shader
@@ -383,6 +407,7 @@
          %23 = OpConvertFToS %10 %22
         %100 = OpAccessChain %70 %43 %80
         %106 = OpAccessChain %11 %14
+        %107 = OpAccessChain %53 %54
          %24 = OpLoad %10 %14
          %25 = OpIAdd %10 %23 %24
                OpReturnValue %25
@@ -391,6 +416,44 @@
   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
 }
 
+TEST(TransformationAccessChainTest, StructIndexMustBeConstant) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 320
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+         %20 = OpUndef %6
+          %7 = OpTypeStruct %6 %6
+          %8 = OpTypePointer Function %7
+         %10 = OpConstant %6 0
+         %11 = OpConstant %6 2
+         %12 = OpTypePointer Function %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %9 = OpVariable %8 Function
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  spvtools::ValidatorOptions validator_options;
+  ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
+                                               kConsoleMessageConsumer));
+  TransformationContext transformation_context(
+      MakeUnique<FactManager>(context.get()), validator_options);
+  // Bad: %9 is a pointer to a struct, but %20 is not a constant.
+  ASSERT_FALSE(TransformationAccessChain(
+                   100, 9, {20}, MakeInstructionDescriptor(9, SpvOpReturn, 0))
+                   .IsApplicable(context.get(), transformation_context));
+}
+
 TEST(TransformationAccessChainTest, IsomorphicStructs) {
   std::string shader = R"(
                OpCapability Shader
diff --git a/test/fuzz/transformation_add_bit_instruction_synonym_test.cpp b/test/fuzz/transformation_add_bit_instruction_synonym_test.cpp
index fa8f7bf..d6b2ce5 100644
--- a/test/fuzz/transformation_add_bit_instruction_synonym_test.cpp
+++ b/test/fuzz/transformation_add_bit_instruction_synonym_test.cpp
@@ -937,6 +937,131 @@
       MakeDataDescriptor(166, {}), MakeDataDescriptor(39, {})));
 }
 
+TEST(TransformationAddBitInstructionSynonymTest, DifferentSingedness) {
+  std::string reference_shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %37 "main"
+
+; Types
+          %2 = OpTypeInt 32 0
+        %200 = OpTypeInt 32 1
+          %3 = OpTypeVoid
+          %4 = OpTypeFunction %3
+
+; Constants
+          %5 = OpConstant %2 0
+          %6 = OpConstant %2 1
+          %7 = OpConstant %2 2
+          %8 = OpConstant %2 3
+          %9 = OpConstant %2 4
+         %10 = OpConstant %2 5
+         %11 = OpConstant %2 6
+         %12 = OpConstant %2 7
+         %13 = OpConstant %2 8
+         %14 = OpConstant %2 9
+         %15 = OpConstant %2 10
+         %16 = OpConstant %2 11
+         %17 = OpConstant %2 12
+         %18 = OpConstant %2 13
+         %19 = OpConstant %2 14
+         %20 = OpConstant %2 15
+         %21 = OpConstant %2 16
+         %22 = OpConstant %2 17
+         %23 = OpConstant %2 18
+         %24 = OpConstant %2 19
+         %25 = OpConstant %2 20
+         %26 = OpConstant %2 21
+         %27 = OpConstant %2 22
+         %28 = OpConstant %2 23
+         %29 = OpConstant %2 24
+         %30 = OpConstant %2 25
+         %31 = OpConstant %2 26
+         %32 = OpConstant %2 27
+         %33 = OpConstant %2 28
+         %34 = OpConstant %2 29
+         %35 = OpConstant %2 30
+         %36 = OpConstant %2 31
+         %45 = OpConstant %200 32
+
+; main function
+         %37 = OpFunction %3 None %4
+         %38 = OpLabel
+         %39 = OpNot %200 %5 ; bit instruction
+         %40 = OpBitwiseOr %200 %6 %45  ; bit instruction
+         %41 = OpBitwiseAnd %2 %5 %6 ; bit instruction
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_5;
+  const auto consumer = nullptr;
+  const auto context =
+      BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
+  spvtools::ValidatorOptions validator_options;
+  ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
+                                               kConsoleMessageConsumer));
+  TransformationContext transformation_context(
+      MakeUnique<FactManager>(context.get()), validator_options);
+
+  // Invalid because the sign of id 200 result is not equal to the sign of id 5
+  // operand in OpNot.
+  auto transformation = TransformationAddBitInstructionSynonym(
+      39, {300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312,
+           313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325,
+           326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338,
+           339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351,
+           352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364,
+           365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377,
+           378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390,
+           391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403,
+           404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416,
+           417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427});
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
+
+  // Invalid because the sign of two operands not the same and the first operand
+  // sign not equal the result sign in OpBitwiseOr.
+  transformation = TransformationAddBitInstructionSynonym(
+      40, {300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312,
+           313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325,
+           326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338,
+           339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351,
+           352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364,
+           365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377,
+           378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390,
+           391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403,
+           404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416,
+           417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427});
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
+
+  // Successful transformation
+  {
+    // Instruction operands are the same and it's equal with the result sign in
+    // OpBitwiseAnd bitwise operation.
+    transformation = TransformationAddBitInstructionSynonym(
+        41, {46,  47,  48,  49,  50,  51,  52,  53,  54,  55,  56,  57,  58,
+             59,  60,  61,  62,  63,  64,  65,  66,  67,  68,  69,  70,  71,
+             72,  73,  74,  75,  76,  77,  78,  79,  80,  81,  82,  83,  84,
+             85,  86,  87,  88,  89,  90,  91,  92,  93,  94,  95,  96,  97,
+             98,  99,  100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110,
+             111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123,
+             124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136,
+             137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
+             150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162,
+             163, 164, 165, 166, 167, 168, 169, 170, 171, 172});
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+
+    ApplyAndCheckFreshIds(transformation, context.get(),
+                          &transformation_context);
+    ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
+        context.get(), validator_options, kConsoleMessageConsumer));
+  }
+}
+
 }  // namespace
 }  // namespace fuzz
 }  // namespace spvtools
diff --git a/test/fuzz/transformation_add_constant_boolean_test.cpp b/test/fuzz/transformation_add_constant_boolean_test.cpp
index 3506db6..bd8d91c 100644
--- a/test/fuzz/transformation_add_constant_boolean_test.cpp
+++ b/test/fuzz/transformation_add_constant_boolean_test.cpp
@@ -46,6 +46,7 @@
   spvtools::ValidatorOptions validator_options;
   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
                                                kConsoleMessageConsumer));
+
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
   // True and false can both be added as neither is present.
@@ -68,7 +69,14 @@
   auto add_false = TransformationAddConstantBoolean(8, false, false);
 
   ASSERT_TRUE(add_true.IsApplicable(context.get(), transformation_context));
+  ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(7));
+  ASSERT_EQ(nullptr, context->get_constant_mgr()->FindDeclaredConstant(7));
   ApplyAndCheckFreshIds(add_true, context.get(), &transformation_context);
+  ASSERT_EQ(SpvOpConstantTrue, context->get_def_use_mgr()->GetDef(7)->opcode());
+  ASSERT_TRUE(context->get_constant_mgr()
+                  ->FindDeclaredConstant(7)
+                  ->AsBoolConstant()
+                  ->value());
   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
                                                kConsoleMessageConsumer));
 
diff --git a/test/fuzz/transformation_add_constant_composite_test.cpp b/test/fuzz/transformation_add_constant_composite_test.cpp
index 2c296fb..e5cbeec 100644
--- a/test/fuzz/transformation_add_constant_composite_test.cpp
+++ b/test/fuzz/transformation_add_constant_composite_test.cpp
@@ -82,10 +82,30 @@
   ASSERT_FALSE(TransformationAddConstantComposite(100, 39, {11, 12}, false)
                    .IsApplicable(context.get(), transformation_context));
 
-  TransformationAddConstantComposite transformations[] = {
-      // %100 = OpConstantComposite %7 %11 %12
-      TransformationAddConstantComposite(100, 7, {11, 12}, false),
+  {
+    // %100 = OpConstantComposite %7 %11 %12
+    TransformationAddConstantComposite transformation(100, 7, {11, 12}, false);
+    ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(100));
+    ASSERT_EQ(nullptr, context->get_constant_mgr()->FindDeclaredConstant(100));
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    ApplyAndCheckFreshIds(transformation, context.get(),
+                          &transformation_context);
+    ASSERT_EQ(SpvOpConstantComposite,
+              context->get_def_use_mgr()->GetDef(100)->opcode());
+    ASSERT_EQ(0.0F, context->get_constant_mgr()
+                        ->FindDeclaredConstant(100)
+                        ->AsVectorConstant()
+                        ->GetComponents()[0]
+                        ->GetFloat());
+    ASSERT_EQ(1.0F, context->get_constant_mgr()
+                        ->FindDeclaredConstant(100)
+                        ->AsVectorConstant()
+                        ->GetComponents()[1]
+                        ->GetFloat());
+  }
 
+  TransformationAddConstantComposite transformations[] = {
       // %101 = OpConstantComposite %7 %14 %15
       TransformationAddConstantComposite(101, 7, {14, 15}, false),
 
diff --git a/test/fuzz/transformation_add_constant_null_test.cpp b/test/fuzz/transformation_add_constant_null_test.cpp
index ce20a67..1553e9f 100644
--- a/test/fuzz/transformation_add_constant_null_test.cpp
+++ b/test/fuzz/transformation_add_constant_null_test.cpp
@@ -78,9 +78,23 @@
   ASSERT_FALSE(TransformationAddConstantNull(100, 22).IsApplicable(
       context.get(), transformation_context));
 
+  {
+    // %100 = OpConstantNull %6
+    TransformationAddConstantNull transformation(100, 6);
+    ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(100));
+    ASSERT_EQ(nullptr, context->get_constant_mgr()->FindDeclaredConstant(100));
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    ApplyAndCheckFreshIds(transformation, context.get(),
+                          &transformation_context);
+    ASSERT_EQ(SpvOpConstantNull,
+              context->get_def_use_mgr()->GetDef(100)->opcode());
+    ASSERT_EQ(
+        0.0F,
+        context->get_constant_mgr()->FindDeclaredConstant(100)->GetFloat());
+  }
+
   TransformationAddConstantNull transformations[] = {
-      // %100 = OpConstantNull %6
-      TransformationAddConstantNull(100, 6),
 
       // %101 = OpConstantNull %7
       TransformationAddConstantNull(101, 7),
diff --git a/test/fuzz/transformation_add_constant_scalar_test.cpp b/test/fuzz/transformation_add_constant_scalar_test.cpp
index a153fb1..00c0541 100644
--- a/test/fuzz/transformation_add_constant_scalar_test.cpp
+++ b/test/fuzz/transformation_add_constant_scalar_test.cpp
@@ -114,6 +114,11 @@
   transformation = TransformationAddConstantScalar(19, 5, {0, 1, 2}, false);
   ASSERT_FALSE(
       transformation.IsApplicable(context.get(), transformation_context));
+
+  // Tests |words| having 2 words for a 32-bit float type.
+  transformation = TransformationAddConstantScalar(19, 4, {0, 1}, false);
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationAddConstantScalarTest, Apply) {
@@ -171,7 +176,11 @@
       MakeUnique<FactManager>(context.get()), validator_options);
   // Adds 32-bit unsigned integer (1 logical operand with 1 word).
   auto transformation = TransformationAddConstantScalar(19, 2, {4}, false);
+  ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(19));
+  ASSERT_EQ(nullptr, context->get_constant_mgr()->FindDeclaredConstant(19));
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
+  ASSERT_EQ(SpvOpConstant, context->get_def_use_mgr()->GetDef(19)->opcode());
+  ASSERT_EQ(4, context->get_constant_mgr()->FindDeclaredConstant(19)->GetU32());
   auto* constant_instruction = context->get_def_use_mgr()->GetDef(19);
   EXPECT_EQ(constant_instruction->NumInOperands(), 1);
   EXPECT_EQ(constant_instruction->NumInOperandWords(), 1);
diff --git a/test/fuzz/transformation_add_dead_block_test.cpp b/test/fuzz/transformation_add_dead_block_test.cpp
index 687f00c..3c9e6b4 100644
--- a/test/fuzz/transformation_add_dead_block_test.cpp
+++ b/test/fuzz/transformation_add_dead_block_test.cpp
@@ -142,6 +142,99 @@
   ASSERT_TRUE(IsEqual(env, variant_shader, context.get()));
 }
 
+TEST(TransformationAddDeadBlockTest, ApplicableWithFalseCondition) {
+  std::string reference_shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %6 "main"
+               OpExecutionMode %6 OriginUpperLeft
+
+; Types
+          %2 = OpTypeBool
+          %3 = OpTypeVoid
+          %4 = OpTypeFunction %3
+
+; Constants
+          %5 = OpConstantFalse %2
+
+; main function
+          %6 = OpFunction %3 None %4
+          %7 = OpLabel
+               OpSelectionMerge %11 None
+               OpBranchConditional %5 %8 %9
+          %8 = OpLabel
+               OpBranch %10
+          %9 = OpLabel
+               OpBranch %10
+         %10 = OpLabel
+               OpBranch %11
+         %11 = OpLabel
+               OpBranch %13
+         %12 = OpLabel
+               OpBranch %13
+         %13 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context =
+      BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
+  spvtools::ValidatorOptions validator_options;
+  ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
+                                               kConsoleMessageConsumer));
+  TransformationContext transformation_context(
+      MakeUnique<FactManager>(context.get()), validator_options);
+  auto transformation = TransformationAddDeadBlock(14, 11, false);
+
+  ASSERT_TRUE(
+      transformation.IsApplicable(context.get(), transformation_context));
+  ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
+
+  std::string variant_shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %6 "main"
+               OpExecutionMode %6 OriginUpperLeft
+
+; Types
+          %2 = OpTypeBool
+          %3 = OpTypeVoid
+          %4 = OpTypeFunction %3
+
+; Constants
+          %5 = OpConstantFalse %2
+
+; main function
+          %6 = OpFunction %3 None %4
+          %7 = OpLabel
+               OpSelectionMerge %11 None
+               OpBranchConditional %5 %8 %9
+          %8 = OpLabel
+               OpBranch %10
+          %9 = OpLabel
+               OpBranch %10
+         %10 = OpLabel
+               OpBranch %11
+         %11 = OpLabel
+               OpSelectionMerge %13 None
+               OpBranchConditional %5 %14 %13
+         %14 = OpLabel
+               OpBranch %13
+         %12 = OpLabel
+               OpBranch %13
+         %13 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
+                                               kConsoleMessageConsumer));
+  ASSERT_TRUE(IsEqual(env, variant_shader, context.get()));
+}
+
 TEST(TransformationAddDeadBlockTest, TargetBlockMustNotBeSelectionMerge) {
   std::string shader = R"(
                OpCapability Shader
diff --git a/test/fuzz/transformation_add_global_undef_test.cpp b/test/fuzz/transformation_add_global_undef_test.cpp
index c3a49e4..03b9157 100644
--- a/test/fuzz/transformation_add_global_undef_test.cpp
+++ b/test/fuzz/transformation_add_global_undef_test.cpp
@@ -63,9 +63,18 @@
   ASSERT_FALSE(TransformationAddGlobalUndef(100, 3).IsApplicable(
       context.get(), transformation_context));
 
+  {
+    // %100 = OpUndef %6
+    TransformationAddGlobalUndef transformation(100, 6);
+    ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(100));
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    ApplyAndCheckFreshIds(transformation, context.get(),
+                          &transformation_context);
+    ASSERT_EQ(SpvOpUndef, context->get_def_use_mgr()->GetDef(100)->opcode());
+  }
+
   TransformationAddGlobalUndef transformations[] = {
-      // %100 = OpUndef %6
-      TransformationAddGlobalUndef(100, 6),
 
       // %101 = OpUndef %7
       TransformationAddGlobalUndef(101, 7),
diff --git a/test/fuzz/transformation_add_global_variable_test.cpp b/test/fuzz/transformation_add_global_variable_test.cpp
index eb958a7..9531ade 100644
--- a/test/fuzz/transformation_add_global_variable_test.cpp
+++ b/test/fuzz/transformation_add_global_variable_test.cpp
@@ -118,11 +118,24 @@
                                                14, false)
                    .IsApplicable(context.get(), transformation_context));
 
-  TransformationAddGlobalVariable transformations[] = {
-      // %100 = OpVariable %12 Private
-      TransformationAddGlobalVariable(100, 12, SpvStorageClassPrivate, 16,
-                                      true),
+  {
+    // %100 = OpVariable %12 Private
+    ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(100));
+    TransformationAddGlobalVariable transformation(
+        100, 12, SpvStorageClassPrivate, 16, true);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    ApplyAndCheckFreshIds(transformation, context.get(),
+                          &transformation_context);
+    ASSERT_EQ(SpvOpVariable, context->get_def_use_mgr()->GetDef(100)->opcode());
+    ASSERT_EQ(
+        SpvStorageClassPrivate,
+        static_cast<SpvStorageClass>(
+            context->get_def_use_mgr()->GetDef(100)->GetSingleWordInOperand(
+                0)));
+  }
 
+  TransformationAddGlobalVariable transformations[] = {
       // %101 = OpVariable %10 Private
       TransformationAddGlobalVariable(101, 10, SpvStorageClassPrivate, 40,
                                       false),
@@ -225,12 +238,10 @@
           %8 = OpTypeVector %6 2
           %9 = OpTypePointer Function %6
          %10 = OpTypePointer Private %6
-         %20 = OpTypePointer Uniform %6
          %11 = OpTypePointer Function %7
          %12 = OpTypePointer Private %7
          %13 = OpTypePointer Private %8
          %14 = OpVariable %10 Private
-         %15 = OpVariable %20 Uniform
          %16 = OpConstant %7 1
          %17 = OpTypePointer Private %10
          %18 = OpTypeBool
@@ -246,48 +257,96 @@
                OpFunctionEnd
   )";
 
-  const auto env = SPV_ENV_UNIVERSAL_1_4;
-  const auto consumer = nullptr;
-  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
-  spvtools::ValidatorOptions validator_options;
-  ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
-                                               kConsoleMessageConsumer));
-  TransformationContext transformation_context(
-      MakeUnique<FactManager>(context.get()), validator_options);
-  TransformationAddGlobalVariable transformations[] = {
-      // %100 = OpVariable %12 Private
-      TransformationAddGlobalVariable(100, 12, SpvStorageClassPrivate, 16,
-                                      true),
+  for (auto env : {SPV_ENV_UNIVERSAL_1_4, SPV_ENV_UNIVERSAL_1_5,
+                   SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_VULKAN_1_2}) {
+    const auto consumer = nullptr;
+    const auto context =
+        BuildModule(env, consumer, shader, kFuzzAssembleOption);
+    spvtools::ValidatorOptions validator_options;
+    ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
+        context.get(), validator_options, kConsoleMessageConsumer));
+    TransformationContext transformation_context(
+        MakeUnique<FactManager>(context.get()), validator_options);
+    TransformationAddGlobalVariable transformations[] = {
+        // %100 = OpVariable %12 Private
+        TransformationAddGlobalVariable(100, 12, SpvStorageClassPrivate, 16,
+                                        true),
 
-      // %101 = OpVariable %12 Private %16
-      TransformationAddGlobalVariable(101, 12, SpvStorageClassPrivate, 16,
-                                      false),
+        // %101 = OpVariable %12 Private %16
+        TransformationAddGlobalVariable(101, 12, SpvStorageClassPrivate, 16,
+                                        false),
 
-      // %102 = OpVariable %19 Private %21
-      TransformationAddGlobalVariable(102, 19, SpvStorageClassPrivate, 21,
-                                      true)};
+        // %102 = OpVariable %19 Private %21
+        TransformationAddGlobalVariable(102, 19, SpvStorageClassPrivate, 21,
+                                        true)};
 
-  for (auto& transformation : transformations) {
+    for (auto& transformation : transformations) {
+      ASSERT_TRUE(
+          transformation.IsApplicable(context.get(), transformation_context));
+      ApplyAndCheckFreshIds(transformation, context.get(),
+                            &transformation_context);
+    }
     ASSERT_TRUE(
-        transformation.IsApplicable(context.get(), transformation_context));
-    ApplyAndCheckFreshIds(transformation, context.get(),
-                          &transformation_context);
-  }
-  ASSERT_TRUE(
-      transformation_context.GetFactManager()->PointeeValueIsIrrelevant(100));
-  ASSERT_TRUE(
-      transformation_context.GetFactManager()->PointeeValueIsIrrelevant(102));
-  ASSERT_FALSE(
-      transformation_context.GetFactManager()->PointeeValueIsIrrelevant(101));
-  ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
-                                               kConsoleMessageConsumer));
+        transformation_context.GetFactManager()->PointeeValueIsIrrelevant(100));
+    ASSERT_TRUE(
+        transformation_context.GetFactManager()->PointeeValueIsIrrelevant(102));
+    ASSERT_FALSE(
+        transformation_context.GetFactManager()->PointeeValueIsIrrelevant(101));
+    ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
+        context.get(), validator_options, kConsoleMessageConsumer));
 
-  std::string after_transformation = R"(
+    std::string after_transformation_enlarged_interface = R"(
+                 OpCapability Shader
+            %1 = OpExtInstImport "GLSL.std.450"
+                 OpMemoryModel Logical GLSL450
+                 OpEntryPoint Fragment %4 "m1" %100 %101 %102
+                 OpEntryPoint Vertex %5 "m2" %100 %101 %102
+                 OpExecutionMode %4 OriginUpperLeft
+                 OpSource ESSL 310
+            %2 = OpTypeVoid
+            %3 = OpTypeFunction %2
+            %6 = OpTypeFloat 32
+            %7 = OpTypeInt 32 1
+            %8 = OpTypeVector %6 2
+            %9 = OpTypePointer Function %6
+           %10 = OpTypePointer Private %6
+           %11 = OpTypePointer Function %7
+           %12 = OpTypePointer Private %7
+           %13 = OpTypePointer Private %8
+           %14 = OpVariable %10 Private
+           %16 = OpConstant %7 1
+           %17 = OpTypePointer Private %10
+           %18 = OpTypeBool
+           %19 = OpTypePointer Private %18
+           %21 = OpConstantTrue %18
+          %100 = OpVariable %12 Private %16
+          %101 = OpVariable %12 Private %16
+          %102 = OpVariable %19 Private %21
+            %4 = OpFunction %2 None %3
+           %30 = OpLabel
+                 OpReturn
+                 OpFunctionEnd
+            %5 = OpFunction %2 None %3
+           %31 = OpLabel
+                 OpReturn
+                 OpFunctionEnd
+    )";
+
+    ASSERT_TRUE(
+        IsEqual(env, after_transformation_enlarged_interface, context.get()));
+  }
+}
+
+TEST(TransformationAddGlobalVariableTest,
+     TestEntryPointInterfaceNoEnlargement) {
+  // This checks that when global variables are added to a SPIR-V 1.3- module,
+  // they are not added to entry points of that module.
+  std::string shader = R"(
                OpCapability Shader
           %1 = OpExtInstImport "GLSL.std.450"
                OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %4 "m1" %100 %101 %102
-               OpEntryPoint Vertex %5 "m2" %100 %101 %102
+               OpEntryPoint Fragment %4 "m1"
+               OpEntryPoint Vertex %5 "m2"
                OpExecutionMode %4 OriginUpperLeft
                OpSource ESSL 310
           %2 = OpTypeVoid
@@ -297,20 +356,15 @@
           %8 = OpTypeVector %6 2
           %9 = OpTypePointer Function %6
          %10 = OpTypePointer Private %6
-         %20 = OpTypePointer Uniform %6
          %11 = OpTypePointer Function %7
          %12 = OpTypePointer Private %7
          %13 = OpTypePointer Private %8
          %14 = OpVariable %10 Private
-         %15 = OpVariable %20 Uniform
          %16 = OpConstant %7 1
          %17 = OpTypePointer Private %10
          %18 = OpTypeBool
          %19 = OpTypePointer Private %18
          %21 = OpConstantTrue %18
-        %100 = OpVariable %12 Private %16
-        %101 = OpVariable %12 Private %16
-        %102 = OpVariable %19 Private %21
           %4 = OpFunction %2 None %3
          %30 = OpLabel
                OpReturn
@@ -320,7 +374,86 @@
                OpReturn
                OpFunctionEnd
   )";
-  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+
+  for (auto env :
+       {SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1, SPV_ENV_UNIVERSAL_1_2,
+        SPV_ENV_UNIVERSAL_1_3, SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1}) {
+    const auto consumer = nullptr;
+    const auto context =
+        BuildModule(env, consumer, shader, kFuzzAssembleOption);
+    spvtools::ValidatorOptions validator_options;
+    ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
+        context.get(), validator_options, kConsoleMessageConsumer));
+    TransformationContext transformation_context(
+        MakeUnique<FactManager>(context.get()), validator_options);
+    TransformationAddGlobalVariable transformations[] = {
+        // %100 = OpVariable %12 Private
+        TransformationAddGlobalVariable(100, 12, SpvStorageClassPrivate, 16,
+                                        true),
+
+        // %101 = OpVariable %12 Private %16
+        TransformationAddGlobalVariable(101, 12, SpvStorageClassPrivate, 16,
+                                        false),
+
+        // %102 = OpVariable %19 Private %21
+        TransformationAddGlobalVariable(102, 19, SpvStorageClassPrivate, 21,
+                                        true)};
+
+    for (auto& transformation : transformations) {
+      ASSERT_TRUE(
+          transformation.IsApplicable(context.get(), transformation_context));
+      ApplyAndCheckFreshIds(transformation, context.get(),
+                            &transformation_context);
+    }
+    ASSERT_TRUE(
+        transformation_context.GetFactManager()->PointeeValueIsIrrelevant(100));
+    ASSERT_TRUE(
+        transformation_context.GetFactManager()->PointeeValueIsIrrelevant(102));
+    ASSERT_FALSE(
+        transformation_context.GetFactManager()->PointeeValueIsIrrelevant(101));
+    ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
+        context.get(), validator_options, kConsoleMessageConsumer));
+
+    std::string after_transformation_fixed_interface = R"(
+                 OpCapability Shader
+            %1 = OpExtInstImport "GLSL.std.450"
+                 OpMemoryModel Logical GLSL450
+                 OpEntryPoint Fragment %4 "m1"
+                 OpEntryPoint Vertex %5 "m2"
+                 OpExecutionMode %4 OriginUpperLeft
+                 OpSource ESSL 310
+            %2 = OpTypeVoid
+            %3 = OpTypeFunction %2
+            %6 = OpTypeFloat 32
+            %7 = OpTypeInt 32 1
+            %8 = OpTypeVector %6 2
+            %9 = OpTypePointer Function %6
+           %10 = OpTypePointer Private %6
+           %11 = OpTypePointer Function %7
+           %12 = OpTypePointer Private %7
+           %13 = OpTypePointer Private %8
+           %14 = OpVariable %10 Private
+           %16 = OpConstant %7 1
+           %17 = OpTypePointer Private %10
+           %18 = OpTypeBool
+           %19 = OpTypePointer Private %18
+           %21 = OpConstantTrue %18
+          %100 = OpVariable %12 Private %16
+          %101 = OpVariable %12 Private %16
+          %102 = OpVariable %19 Private %21
+            %4 = OpFunction %2 None %3
+           %30 = OpLabel
+                 OpReturn
+                 OpFunctionEnd
+            %5 = OpFunction %2 None %3
+           %31 = OpLabel
+                 OpReturn
+                 OpFunctionEnd
+    )";
+
+    ASSERT_TRUE(
+        IsEqual(env, after_transformation_fixed_interface, context.get()));
+  }
 }
 
 TEST(TransformationAddGlobalVariableTest, TestAddingWorkgroupGlobals) {
diff --git a/test/fuzz/transformation_add_local_variable_test.cpp b/test/fuzz/transformation_add_local_variable_test.cpp
index ed57a28..de88573 100644
--- a/test/fuzz/transformation_add_local_variable_test.cpp
+++ b/test/fuzz/transformation_add_local_variable_test.cpp
@@ -98,10 +98,14 @@
   // %105 = OpVariable %50 Function %51
   {
     TransformationAddLocalVariable transformation(105, 50, 4, 51, true);
+    ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(105));
+    ASSERT_EQ(nullptr, context->get_instr_block(105));
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
                           &transformation_context);
+    ASSERT_EQ(SpvOpVariable, context->get_def_use_mgr()->GetDef(105)->opcode());
+    ASSERT_EQ(5, context->get_instr_block(105)->id());
   }
 
   // %104 = OpVariable %41 Function %46
diff --git a/test/fuzz/transformation_add_parameter_test.cpp b/test/fuzz/transformation_add_parameter_test.cpp
index 7b2a15f..2ae60c9 100644
--- a/test/fuzz/transformation_add_parameter_test.cpp
+++ b/test/fuzz/transformation_add_parameter_test.cpp
@@ -35,6 +35,10 @@
           %7 = OpTypeBool
          %11 = OpTypeInt 32 1
          %16 = OpTypeFloat 32
+         %51 = OpConstant %11 2
+         %52 = OpTypeArray %16 %51
+         %53 = OpConstant %16 7
+         %54 = OpConstantComposite %52 %53 %53
           %3 = OpTypeFunction %2
           %6 = OpTypeFunction %7 %7
           %8 = OpConstant %11 23
@@ -141,6 +145,14 @@
     ASSERT_TRUE(transformation_context.GetFactManager()->IdIsIrrelevant(60));
   }
   {
+    TransformationAddParameter correct(9, 68, 52, {{{13, 54}}}, 69);
+    ASSERT_TRUE(correct.IsApplicable(context.get(), transformation_context));
+    ApplyAndCheckFreshIds(correct, context.get(), &transformation_context);
+    ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
+        context.get(), validator_options, kConsoleMessageConsumer));
+    ASSERT_TRUE(transformation_context.GetFactManager()->IdIsIrrelevant(68));
+  }
+  {
     TransformationAddParameter correct(17, 62, 7, {{}}, 63);
     ASSERT_TRUE(correct.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(correct, context.get(), &transformation_context);
@@ -177,6 +189,10 @@
           %7 = OpTypeBool
          %11 = OpTypeInt 32 1
          %16 = OpTypeFloat 32
+         %51 = OpConstant %11 2
+         %52 = OpTypeArray %16 %51
+         %53 = OpConstant %16 7
+         %54 = OpConstantComposite %52 %53 %53
           %3 = OpTypeFunction %2
           %8 = OpConstant %11 23
          %12 = OpConstantTrue %7
@@ -188,11 +204,11 @@
          %41 = OpTypeStruct %11 %16
          %42 = OpConstantComposite %41 %8 %32
          %44 = OpTypeFunction %2 %41 %7
-          %6 = OpTypeFunction %7 %7 %11
+          %6 = OpTypeFunction %7 %7 %11 %52
          %65 = OpTypeFunction %2 %31
           %4 = OpFunction %2 None %3
           %5 = OpLabel
-         %13 = OpFunctionCall %7 %9 %12 %8
+         %13 = OpFunctionCall %7 %9 %12 %8 %54
                OpReturn
                OpFunctionEnd
 
@@ -200,6 +216,7 @@
           %9 = OpFunction %7 None %6
          %14 = OpFunctionParameter %7
          %60 = OpFunctionParameter %11
+         %68 = OpFunctionParameter %52
          %10 = OpLabel
                OpReturnValue %12
                OpFunctionEnd
diff --git a/test/fuzz/transformation_add_synonym_test.cpp b/test/fuzz/transformation_add_synonym_test.cpp
index 3803fa3..314b003 100644
--- a/test/fuzz/transformation_add_synonym_test.cpp
+++ b/test/fuzz/transformation_add_synonym_test.cpp
@@ -197,6 +197,7 @@
          %37 = OpTypeVector %36 2
          %38 = OpConstantTrue %36
          %39 = OpConstantComposite %37 %38 %38
+         %40 = OpConstant %6 37
           %4 = OpFunction %2 None %3
           %5 = OpLabel
                OpReturn
@@ -249,6 +250,29 @@
       ++fresh_id;
     }
   }
+  {
+    TransformationAddSynonym transformation(
+        40, protobufs::TransformationAddSynonym::BITWISE_OR, fresh_id,
+        insert_before);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    ApplyAndCheckFreshIds(transformation, context.get(),
+                          &transformation_context);
+    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+        MakeDataDescriptor(40, {}), MakeDataDescriptor(fresh_id, {})));
+    ++fresh_id;
+  }
+  {
+    TransformationAddSynonym transformation(
+        40, protobufs::TransformationAddSynonym::BITWISE_XOR, fresh_id,
+        insert_before);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    ApplyAndCheckFreshIds(transformation, context.get(),
+                          &transformation_context);
+    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+        MakeDataDescriptor(40, {}), MakeDataDescriptor(fresh_id, {})));
+  }
 
   std::string expected_shader = R"(
                OpCapability Shader
@@ -289,6 +313,7 @@
          %37 = OpTypeVector %36 2
          %38 = OpConstantTrue %36
          %39 = OpConstantComposite %37 %38 %38
+         %40 = OpConstant %6 37
           %4 = OpFunction %2 None %3
           %5 = OpLabel
          %50 = OpIAdd %6 %9 %7
@@ -303,6 +328,8 @@
          %59 = OpFMul %14 %17 %16
          %60 = OpFMul %18 %23 %20
          %61 = OpIMul %24 %29 %26
+         %62 = OpBitwiseOr %6 %40 %7
+         %63 = OpBitwiseXor %6 %40 %7
                OpReturn
                OpFunctionEnd
   )";
diff --git a/test/fuzz/transformation_add_type_array_test.cpp b/test/fuzz/transformation_add_type_array_test.cpp
index ab4ed9a..2ef8200 100644
--- a/test/fuzz/transformation_add_type_array_test.cpp
+++ b/test/fuzz/transformation_add_type_array_test.cpp
@@ -90,19 +90,34 @@
   ASSERT_FALSE(TransformationAddTypeArray(100, 11, 17)
                    .IsApplicable(context.get(), transformation_context));
 
-  TransformationAddTypeArray transformations[] = {
-      // %100 = OpTypeArray %10 %16
-      TransformationAddTypeArray(100, 10, 16),
-
-      // %101 = OpTypeArray %7 %12
-      TransformationAddTypeArray(101, 7, 12)};
-
-  for (auto& transformation : transformations) {
+  {
+    // %100 = OpTypeArray %10 %16
+    TransformationAddTypeArray transformation(100, 10, 16);
+    ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(100));
+    ASSERT_EQ(nullptr, context->get_type_mgr()->GetType(100));
     ASSERT_TRUE(
         transformation.IsApplicable(context.get(), transformation_context));
     ApplyAndCheckFreshIds(transformation, context.get(),
                           &transformation_context);
+    ASSERT_EQ(SpvOpTypeArray,
+              context->get_def_use_mgr()->GetDef(100)->opcode());
+    ASSERT_NE(nullptr, context->get_type_mgr()->GetType(100)->AsArray());
   }
+
+  {
+    // %101 = OpTypeArray %7 %12
+    TransformationAddTypeArray transformation(101, 7, 12);
+    ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(101));
+    ASSERT_EQ(nullptr, context->get_type_mgr()->GetType(101));
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    ApplyAndCheckFreshIds(transformation, context.get(),
+                          &transformation_context);
+    ASSERT_EQ(SpvOpTypeArray,
+              context->get_def_use_mgr()->GetDef(100)->opcode());
+    ASSERT_NE(nullptr, context->get_type_mgr()->GetType(100)->AsArray());
+  }
+
   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
                                                kConsoleMessageConsumer));
 
diff --git a/test/fuzz/transformation_add_type_boolean_test.cpp b/test/fuzz/transformation_add_type_boolean_test.cpp
index 88d9f5b..a8e657b 100644
--- a/test/fuzz/transformation_add_type_boolean_test.cpp
+++ b/test/fuzz/transformation_add_type_boolean_test.cpp
@@ -52,9 +52,13 @@
       context.get(), transformation_context));
 
   auto add_type_bool = TransformationAddTypeBoolean(100);
+  ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(100));
+  ASSERT_EQ(nullptr, context->get_type_mgr()->GetType(100));
   ASSERT_TRUE(
       add_type_bool.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(add_type_bool, context.get(), &transformation_context);
+  ASSERT_EQ(SpvOpTypeBool, context->get_def_use_mgr()->GetDef(100)->opcode());
+  ASSERT_NE(nullptr, context->get_type_mgr()->GetType(100)->AsBool());
   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
                                                kConsoleMessageConsumer));
 
diff --git a/test/fuzz/transformation_add_type_float_test.cpp b/test/fuzz/transformation_add_type_float_test.cpp
index 235d61b..9275bec 100644
--- a/test/fuzz/transformation_add_type_float_test.cpp
+++ b/test/fuzz/transformation_add_type_float_test.cpp
@@ -61,7 +61,8 @@
   ASSERT_FALSE(
       transformation.IsApplicable(context.get(), transformation_context));
 
-  // Tests existing 16-bit float type.
+  // The transformation is not applicable because there is already a 16-bit
+  // float type declared in the module.
   transformation = TransformationAddTypeFloat(7, 16);
   ASSERT_FALSE(
       transformation.IsApplicable(context.get(), transformation_context));
@@ -70,6 +71,19 @@
   transformation = TransformationAddTypeFloat(7, 32);
   ASSERT_TRUE(
       transformation.IsApplicable(context.get(), transformation_context));
+
+  // By default, SPIR-V does not support 64-bit float types.
+  // Below we add such capability, so the test should now pass.
+  context.get()->get_feature_mgr()->AddCapability(SpvCapabilityFloat64);
+  ASSERT_TRUE(TransformationAddTypeFloat(7, 64).IsApplicable(
+      context.get(), transformation_context));
+
+#ifndef NDEBUG
+  // Should not be able to add float type of width different from 16/32/64
+  ASSERT_DEATH(TransformationAddTypeFloat(7, 20).IsApplicable(
+                   context.get(), transformation_context),
+               "Unexpected float type width");
+#endif
 }
 
 TEST(TransformationAddTypeFloatTest, Apply) {
@@ -103,15 +117,27 @@
       MakeUnique<FactManager>(context.get()), validator_options);
   // Adds 16-bit float type.
   auto transformation = TransformationAddTypeFloat(6, 16);
+  ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(6));
+  ASSERT_EQ(nullptr, context->get_type_mgr()->GetType(6));
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
+  ASSERT_EQ(SpvOpTypeFloat, context->get_def_use_mgr()->GetDef(6)->opcode());
+  ASSERT_NE(nullptr, context->get_type_mgr()->GetType(6)->AsFloat());
 
   // Adds 32-bit float type.
   transformation = TransformationAddTypeFloat(7, 32);
+  ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(7));
+  ASSERT_EQ(nullptr, context->get_type_mgr()->GetType(7));
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
+  ASSERT_EQ(SpvOpTypeFloat, context->get_def_use_mgr()->GetDef(7)->opcode());
+  ASSERT_NE(nullptr, context->get_type_mgr()->GetType(7)->AsFloat());
 
   // Adds 64-bit float type.
   transformation = TransformationAddTypeFloat(8, 64);
+  ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(8));
+  ASSERT_EQ(nullptr, context->get_type_mgr()->GetType(8));
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
+  ASSERT_EQ(SpvOpTypeFloat, context->get_def_use_mgr()->GetDef(8)->opcode());
+  ASSERT_NE(nullptr, context->get_type_mgr()->GetType(8)->AsFloat());
 
   std::string variant_shader = R"(
          OpCapability Shader
diff --git a/test/fuzz/transformation_add_type_int_test.cpp b/test/fuzz/transformation_add_type_int_test.cpp
index ee4e799..ed8e00a 100644
--- a/test/fuzz/transformation_add_type_int_test.cpp
+++ b/test/fuzz/transformation_add_type_int_test.cpp
@@ -85,6 +85,29 @@
   transformation = TransformationAddTypeInt(7, 32, true);
   ASSERT_TRUE(
       transformation.IsApplicable(context.get(), transformation_context));
+
+  // By default SPIR-V does not support 16-bit integers.
+  // Below we add such capability, so the test should now be succesful.
+  context.get()->get_feature_mgr()->AddCapability(SpvCapabilityInt16);
+  ASSERT_TRUE(TransformationAddTypeInt(7, 16, true)
+                  .IsApplicable(context.get(), transformation_context));
+
+  // By default SPIR-V does not support 64-bit integers.
+  // Below we add such capability, so the test should now pass.
+  context.get()->get_feature_mgr()->AddCapability(SpvCapabilityInt64);
+  ASSERT_TRUE(TransformationAddTypeInt(7, 64, true)
+                  .IsApplicable(context.get(), transformation_context));
+
+#ifndef NDEBUG
+  // Should not be able to add signed/unsigned integers of width different from
+  // 16/32/64 bits.
+  ASSERT_DEATH(TransformationAddTypeInt(7, 20, false)
+                   .IsApplicable(context.get(), transformation_context),
+               "Unexpected integer type width");
+  ASSERT_DEATH(TransformationAddTypeInt(12, 15, false)
+                   .IsApplicable(context.get(), transformation_context),
+               "Unexpected integer type width");
+#endif
 }
 
 TEST(TransformationAddTypeIntTest, Apply) {
@@ -118,8 +141,14 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
   // Adds signed 8-bit integer type.
+  // For this transformation we also check that the def-use manager and type
+  // manager are updated appropriately.
   auto transformation = TransformationAddTypeInt(6, 8, true);
+  ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(6));
+  ASSERT_EQ(nullptr, context->get_type_mgr()->GetType(6));
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
+  ASSERT_EQ(SpvOpTypeInt, context->get_def_use_mgr()->GetDef(6)->opcode());
+  ASSERT_NE(nullptr, context->get_type_mgr()->GetType(6)->AsInteger());
 
   // Adds signed 16-bit integer type.
   transformation = TransformationAddTypeInt(7, 16, true);
diff --git a/test/fuzz/transformation_add_type_matrix_test.cpp b/test/fuzz/transformation_add_type_matrix_test.cpp
index 926e983..df0111e 100644
--- a/test/fuzz/transformation_add_type_matrix_test.cpp
+++ b/test/fuzz/transformation_add_type_matrix_test.cpp
@@ -63,10 +63,21 @@
   ASSERT_FALSE(TransformationAddTypeMatrix(100, 11, 2)
                    .IsApplicable(context.get(), transformation_context));
 
-  TransformationAddTypeMatrix transformations[] = {
-      // %100 = OpTypeMatrix %8 2
-      TransformationAddTypeMatrix(100, 8, 2),
+  {
+    // %100 = OpTypeMatrix %8 2
+    TransformationAddTypeMatrix transformation(100, 8, 2);
+    ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(100));
+    ASSERT_EQ(nullptr, context->get_type_mgr()->GetType(100));
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    ApplyAndCheckFreshIds(transformation, context.get(),
+                          &transformation_context);
+    ASSERT_EQ(SpvOpTypeMatrix,
+              context->get_def_use_mgr()->GetDef(100)->opcode());
+    ASSERT_NE(nullptr, context->get_type_mgr()->GetType(100)->AsMatrix());
+  }
 
+  TransformationAddTypeMatrix transformations[] = {
       // %101 = OpTypeMatrix %8 3
       TransformationAddTypeMatrix(101, 8, 3),
 
diff --git a/test/fuzz/transformation_add_type_pointer_test.cpp b/test/fuzz/transformation_add_type_pointer_test.cpp
index 985e904..b9072e3 100644
--- a/test/fuzz/transformation_add_type_pointer_test.cpp
+++ b/test/fuzz/transformation_add_type_pointer_test.cpp
@@ -133,10 +133,24 @@
   ASSERT_FALSE(bad_result_id_is_not_fresh.IsApplicable(context.get(),
                                                        transformation_context));
 
+  {
+    auto& transformation = good_new_private_pointer_to_t;
+    ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(101));
+    ASSERT_EQ(nullptr, context->get_type_mgr()->GetType(101));
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    ApplyAndCheckFreshIds(transformation, context.get(),
+                          &transformation_context);
+    ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
+        context.get(), validator_options, kConsoleMessageConsumer));
+    ASSERT_EQ(SpvOpTypePointer,
+              context->get_def_use_mgr()->GetDef(101)->opcode());
+    ASSERT_NE(nullptr, context->get_type_mgr()->GetType(101)->AsPointer());
+  }
+
   for (auto& transformation :
-       {good_new_private_pointer_to_t, good_new_uniform_pointer_to_t,
-        good_another_function_pointer_to_s, good_new_uniform_pointer_to_s,
-        good_another_private_pointer_to_float,
+       {good_new_uniform_pointer_to_t, good_another_function_pointer_to_s,
+        good_new_uniform_pointer_to_s, good_another_private_pointer_to_float,
         good_new_private_pointer_to_private_pointer_to_float,
         good_new_uniform_pointer_to_vec2,
         good_new_private_pointer_to_uniform_pointer_to_vec2}) {
diff --git a/test/fuzz/transformation_add_type_struct_test.cpp b/test/fuzz/transformation_add_type_struct_test.cpp
index b57bab2..7fb91ab 100644
--- a/test/fuzz/transformation_add_type_struct_test.cpp
+++ b/test/fuzz/transformation_add_type_struct_test.cpp
@@ -63,10 +63,21 @@
   ASSERT_FALSE(TransformationAddTypeStruct(100, {3}).IsApplicable(
       context.get(), transformation_context));
 
-  TransformationAddTypeStruct transformations[] = {
-      // %100 = OpTypeStruct %6 %7 %8 %9 %10 %11
-      TransformationAddTypeStruct(100, {6, 7, 8, 9, 10, 11}),
+  {
+    // %100 = OpTypeStruct %6 %7 %8 %9 %10 %11
+    TransformationAddTypeStruct transformation(100, {6, 7, 8, 9, 10, 11});
+    ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(100));
+    ASSERT_EQ(nullptr, context->get_type_mgr()->GetType(100));
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    ApplyAndCheckFreshIds(transformation, context.get(),
+                          &transformation_context);
+    ASSERT_EQ(SpvOpTypeStruct,
+              context->get_def_use_mgr()->GetDef(100)->opcode());
+    ASSERT_NE(nullptr, context->get_type_mgr()->GetType(100)->AsStruct());
+  }
 
+  TransformationAddTypeStruct transformations[] = {
       // %101 = OpTypeStruct
       TransformationAddTypeStruct(101, {}),
 
diff --git a/test/fuzz/transformation_add_type_vector_test.cpp b/test/fuzz/transformation_add_type_vector_test.cpp
index a49ba6e..755bc4a 100644
--- a/test/fuzz/transformation_add_type_vector_test.cpp
+++ b/test/fuzz/transformation_add_type_vector_test.cpp
@@ -57,10 +57,21 @@
   ASSERT_FALSE(TransformationAddTypeVector(100, 1, 2).IsApplicable(
       context.get(), transformation_context));
 
-  TransformationAddTypeVector transformations[] = {
-      // %100 = OpTypeVector %6 2
-      TransformationAddTypeVector(100, 6, 2),
+  {
+    // %100 = OpTypeVector %6 2
+    TransformationAddTypeVector transformation(100, 6, 2);
+    ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(100));
+    ASSERT_EQ(nullptr, context->get_type_mgr()->GetType(100));
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    ApplyAndCheckFreshIds(transformation, context.get(),
+                          &transformation_context);
+    ASSERT_EQ(SpvOpTypeVector,
+              context->get_def_use_mgr()->GetDef(100)->opcode());
+    ASSERT_NE(nullptr, context->get_type_mgr()->GetType(100)->AsVector());
+  }
 
+  TransformationAddTypeVector transformations[] = {
       // %101 = OpTypeVector %7 3
       TransformationAddTypeVector(101, 7, 3),
 
diff --git a/test/fuzz/transformation_composite_construct_test.cpp b/test/fuzz/transformation_composite_construct_test.cpp
index edbfe3b..3c5f731 100644
--- a/test/fuzz/transformation_composite_construct_test.cpp
+++ b/test/fuzz/transformation_composite_construct_test.cpp
@@ -142,12 +142,29 @@
   TransformationCompositeConstruct make_vec2_array_length_3_bad(
       37, {41, 45, 27, 27}, MakeInstructionDescriptor(46, SpvOpAccessChain, 0),
       200);
+  // The first component does not correspond to an instruction with a result
+  // type so this check should return false.
+  TransformationCompositeConstruct make_vec2_array_length_3_nores(
+      37, {2, 45, 27}, MakeInstructionDescriptor(46, SpvOpAccessChain, 0), 200);
   ASSERT_TRUE(make_vec2_array_length_3.IsApplicable(context.get(),
                                                     transformation_context));
   ASSERT_FALSE(make_vec2_array_length_3_bad.IsApplicable(
       context.get(), transformation_context));
+  ASSERT_FALSE(make_vec2_array_length_3_nores.IsApplicable(
+      context.get(), transformation_context));
+  ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(200));
+  ASSERT_EQ(nullptr, context->get_instr_block(200));
+  uint32_t num_uses_of_41_before = context->get_def_use_mgr()->NumUses(41);
+  uint32_t num_uses_of_45_before = context->get_def_use_mgr()->NumUses(45);
+  uint32_t num_uses_of_27_before = context->get_def_use_mgr()->NumUses(27);
   ApplyAndCheckFreshIds(make_vec2_array_length_3, context.get(),
                         &transformation_context);
+  ASSERT_EQ(SpvOpCompositeConstruct,
+            context->get_def_use_mgr()->GetDef(200)->opcode());
+  ASSERT_EQ(34, context->get_instr_block(200)->id());
+  ASSERT_EQ(num_uses_of_41_before + 1, context->get_def_use_mgr()->NumUses(41));
+  ASSERT_EQ(num_uses_of_45_before + 1, context->get_def_use_mgr()->NumUses(45));
+  ASSERT_EQ(num_uses_of_27_before + 1, context->get_def_use_mgr()->NumUses(27));
   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
                                                kConsoleMessageConsumer));
   ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
@@ -412,9 +429,15 @@
   // Bad: %35 is mat4x3, not mat3x4.
   TransformationCompositeConstruct make_mat34_bad(
       35, {25, 28, 31}, MakeInstructionDescriptor(31, SpvOpReturn, 0), 200);
+  // The first component does not correspond to an instruction with a result
+  // type so this check should return false.
+  TransformationCompositeConstruct make_mat34_nores(
+      32, {2, 28, 31}, MakeInstructionDescriptor(31, SpvOpReturn, 0), 200);
   ASSERT_TRUE(make_mat34.IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       make_mat34_bad.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      make_mat34_nores.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(make_mat34, context.get(), &transformation_context);
   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
                                                kConsoleMessageConsumer));
@@ -625,9 +648,15 @@
   // Bad: Too few fields to make the struct.
   TransformationCompositeConstruct make_inner_bad(
       9, {25}, MakeInstructionDescriptor(57, SpvOpAccessChain, 0), 200);
+  // The first component does not correspond to an instruction with a result
+  // type so this check should return false.
+  TransformationCompositeConstruct make_inner_nores(
+      9, {2, 19}, MakeInstructionDescriptor(57, SpvOpAccessChain, 0), 200);
   ASSERT_TRUE(make_inner.IsApplicable(context.get(), transformation_context));
   ASSERT_FALSE(
       make_inner_bad.IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      make_inner_nores.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(make_inner, context.get(), &transformation_context);
   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
                                                kConsoleMessageConsumer));
diff --git a/test/fuzz/transformation_composite_extract_test.cpp b/test/fuzz/transformation_composite_extract_test.cpp
index 383a4db..1df5591 100644
--- a/test/fuzz/transformation_composite_extract_test.cpp
+++ b/test/fuzz/transformation_composite_extract_test.cpp
@@ -143,10 +143,18 @@
 
   TransformationCompositeExtract transformation_1(
       MakeInstructionDescriptor(36, SpvOpConvertFToS, 0), 201, 100, {2});
+  ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(201));
+  ASSERT_EQ(nullptr, context->get_instr_block(201));
+  uint32_t num_uses_of_100_before = context->get_def_use_mgr()->NumUses(100);
   ASSERT_TRUE(
       transformation_1.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation_1, context.get(),
                         &transformation_context);
+  ASSERT_EQ(SpvOpCompositeExtract,
+            context->get_def_use_mgr()->GetDef(201)->opcode());
+  ASSERT_EQ(15, context->get_instr_block(201)->id());
+  ASSERT_EQ(num_uses_of_100_before + 1,
+            context->get_def_use_mgr()->NumUses(100));
   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
                                                kConsoleMessageConsumer));
 
diff --git a/test/fuzz/transformation_equation_instruction_test.cpp b/test/fuzz/transformation_equation_instruction_test.cpp
index 654fffc..5b5033d 100644
--- a/test/fuzz/transformation_equation_instruction_test.cpp
+++ b/test/fuzz/transformation_equation_instruction_test.cpp
@@ -102,8 +102,12 @@
       14, SpvOpSNegate, {7}, return_instruction);
   ASSERT_TRUE(
       transformation1.IsApplicable(context.get(), transformation_context));
+  ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(14));
+  ASSERT_EQ(nullptr, context->get_instr_block(14));
   ApplyAndCheckFreshIds(transformation1, context.get(),
                         &transformation_context);
+  ASSERT_EQ(SpvOpSNegate, context->get_def_use_mgr()->GetDef(14)->opcode());
+  ASSERT_EQ(13, context->get_instr_block(14)->id());
   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
                                                kConsoleMessageConsumer));
 
diff --git a/test/fuzz/transformation_flatten_conditional_branch_test.cpp b/test/fuzz/transformation_flatten_conditional_branch_test.cpp
index 1a0ff6a..800af0e 100644
--- a/test/fuzz/transformation_flatten_conditional_branch_test.cpp
+++ b/test/fuzz/transformation_flatten_conditional_branch_test.cpp
@@ -1334,20 +1334,24 @@
                OpFunctionEnd
   )";
 
-  const auto env = SPV_ENV_UNIVERSAL_1_3;
-  const auto consumer = nullptr;
-  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
-  spvtools::ValidatorOptions validator_options;
-  ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
-                                               kConsoleMessageConsumer));
+  for (auto env :
+       {SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1, SPV_ENV_UNIVERSAL_1_2,
+        SPV_ENV_UNIVERSAL_1_3, SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1}) {
+    const auto consumer = nullptr;
+    const auto context =
+        BuildModule(env, consumer, shader, kFuzzAssembleOption);
+    spvtools::ValidatorOptions validator_options;
+    ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
+        context.get(), validator_options, kConsoleMessageConsumer));
 
-  TransformationContext transformation_context(
-      MakeUnique<FactManager>(context.get()), validator_options);
+    TransformationContext transformation_context(
+        MakeUnique<FactManager>(context.get()), validator_options);
 
-  auto transformation =
-      TransformationFlattenConditionalBranch(5, true, 0, 0, 0, {});
-  ASSERT_FALSE(
-      transformation.IsApplicable(context.get(), transformation_context));
+    auto transformation =
+        TransformationFlattenConditionalBranch(5, true, 0, 0, 0, {});
+    ASSERT_FALSE(
+        transformation.IsApplicable(context.get(), transformation_context));
+  }
 }
 
 TEST(TransformationFlattenConditionalBranchTest,
diff --git a/test/fuzz/transformation_merge_function_returns_test.cpp b/test/fuzz/transformation_merge_function_returns_test.cpp
index e60d345..400d49a 100644
--- a/test/fuzz/transformation_merge_function_returns_test.cpp
+++ b/test/fuzz/transformation_merge_function_returns_test.cpp
@@ -147,12 +147,12 @@
       MakeUnique<FactManager>(context.get()), validator_options);
 
   // Function %1 does not exist.
-  ASSERT_FALSE(TransformationMergeFunctionReturns(1, 100, 101, 0, 0, {{}})
+  ASSERT_FALSE(TransformationMergeFunctionReturns(1, 100, 200, 101, 0, 0, {{}})
                    .IsApplicable(context.get(), transformation_context));
 
   // The entry block (%22) of function %15 does not branch unconditionally to
   // the following block.
-  ASSERT_FALSE(TransformationMergeFunctionReturns(16, 100, 101, 0, 0, {{}})
+  ASSERT_FALSE(TransformationMergeFunctionReturns(16, 100, 200, 101, 0, 0, {{}})
                    .IsApplicable(context.get(), transformation_context));
 
   // Block %28 is the merge block of a loop containing a return instruction, but
@@ -160,7 +160,7 @@
   // not OpLabel, OpPhi or OpBranch).
   ASSERT_FALSE(
       TransformationMergeFunctionReturns(
-          18, 100, 101, 0, 0, {{MakeReturnMergingInfo(29, 102, 0, {{}})}})
+          18, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(29, 102, 0, {{}})}})
           .IsApplicable(context.get(), transformation_context));
 
   // Block %34 is the merge block of a loop containing a return instruction, but
@@ -168,21 +168,24 @@
   // that are not OpLabel, OpPhi or OpBranch).
   ASSERT_FALSE(
       TransformationMergeFunctionReturns(
-          20, 100, 101, 0, 0, {{MakeReturnMergingInfo(35, 102, 0, {{}})}})
+          20, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(35, 102, 0, {{}})}})
           .IsApplicable(context.get(), transformation_context));
 
   // Id %1000 cannot be found in the module and there is no id of the correct
   // type (float) available at the end of the entry block of function %21.
-  ASSERT_FALSE(TransformationMergeFunctionReturns(22, 100, 101, 102, 1000, {{}})
-                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationMergeFunctionReturns(22, 100, 200, 101, 102, 1000, {{}})
+          .IsApplicable(context.get(), transformation_context));
 
   // Id %47 is of type float, while function %45 has return type int.
-  ASSERT_FALSE(TransformationMergeFunctionReturns(45, 100, 101, 102, 47, {{}})
-                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationMergeFunctionReturns(45, 100, 200, 101, 102, 47, {{}})
+          .IsApplicable(context.get(), transformation_context));
 
   // Id %50 is not available at the end of the entry block of function %45.
-  ASSERT_FALSE(TransformationMergeFunctionReturns(45, 100, 101, 102, 50, {{}})
-                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationMergeFunctionReturns(45, 100, 200, 101, 102, 50, {{}})
+          .IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationMergeFunctionReturnsTest, MissingBooleans) {
@@ -235,8 +238,9 @@
     TransformationContext transformation_context(
         MakeUnique<FactManager>(context.get()), validator_options);
 
-    ASSERT_FALSE(TransformationMergeFunctionReturns(3, 100, 101, 0, 0, {{}})
-                     .IsApplicable(context.get(), transformation_context));
+    ASSERT_FALSE(
+        TransformationMergeFunctionReturns(3, 100, 200, 101, 0, 0, {{}})
+            .IsApplicable(context.get(), transformation_context));
   }
   {
     // OpConstantFalse is missing.
@@ -287,8 +291,9 @@
     TransformationContext transformation_context(
         MakeUnique<FactManager>(context.get()), validator_options);
 
-    ASSERT_FALSE(TransformationMergeFunctionReturns(3, 100, 101, 0, 0, {{}})
-                     .IsApplicable(context.get(), transformation_context));
+    ASSERT_FALSE(
+        TransformationMergeFunctionReturns(3, 100, 200, 101, 0, 0, {{}})
+            .IsApplicable(context.get(), transformation_context));
   }
 }
 
@@ -386,59 +391,65 @@
   // Fresh id %100 is used twice.
   ASSERT_FALSE(
       TransformationMergeFunctionReturns(
-          17, 100, 100, 0, 0, {{MakeReturnMergingInfo(20, 101, 0, {{}})}})
+          17, 100, 200, 100, 0, 0, {{MakeReturnMergingInfo(20, 101, 0, {{}})}})
           .IsApplicable(context.get(), transformation_context));
 
   // Fresh id %100 is used twice.
   ASSERT_FALSE(
       TransformationMergeFunctionReturns(
-          17, 100, 101, 0, 0, {{MakeReturnMergingInfo(20, 100, 0, {{}})}})
+          17, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(20, 100, 0, {{}})}})
           .IsApplicable(context.get(), transformation_context));
 
   // %0 cannot be a fresh id for the new merge block.
   ASSERT_FALSE(
       TransformationMergeFunctionReturns(
-          17, 100, 0, 0, 0, {{MakeReturnMergingInfo(20, 101, 0, {{}})}})
+          17, 100, 200, 0, 0, 0, {{MakeReturnMergingInfo(20, 101, 0, {{}})}})
+          .IsApplicable(context.get(), transformation_context));
+
+  // %0 cannot be a fresh id for the new continue block.
+  ASSERT_FALSE(
+      TransformationMergeFunctionReturns(
+          17, 100, 0, 200, 0, 0, {{MakeReturnMergingInfo(20, 101, 0, {{}})}})
           .IsApplicable(context.get(), transformation_context));
 
   // %0 cannot be a fresh id for the new header block.
   ASSERT_FALSE(
       TransformationMergeFunctionReturns(
-          17, 0, 100, 0, 0, {{MakeReturnMergingInfo(20, 101, 0, {{}})}})
+          17, 0, 200, 100, 0, 0, {{MakeReturnMergingInfo(20, 101, 0, {{}})}})
           .IsApplicable(context.get(), transformation_context));
 
   // %0 cannot be a fresh id for the new |is_returning| instruction in an
   // existing merge block.
   ASSERT_FALSE(
       TransformationMergeFunctionReturns(
-          17, 100, 101, 0, 0, {{MakeReturnMergingInfo(20, 0, 0, {{}})}})
+          17, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(20, 0, 0, {{}})}})
           .IsApplicable(context.get(), transformation_context));
 
   // %0 cannot be a fresh id for the new |return_val| instruction in the new
   // return block.
-  ASSERT_FALSE(
-      TransformationMergeFunctionReturns(
-          14, 100, 101, 0, 10, {{MakeReturnMergingInfo(27, 102, 103, {{}})}})
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationMergeFunctionReturns(
+                   14, 100, 200, 101, 0, 10,
+                   {{MakeReturnMergingInfo(27, 102, 103, {{}})}})
+                   .IsApplicable(context.get(), transformation_context));
 
   // %0 cannot be a fresh id for the new |maybe_return_val| instruction in an
   // existing merge block, inside a non-void function.
-  ASSERT_FALSE(
-      TransformationMergeFunctionReturns(
-          14, 100, 101, 102, 10, {{MakeReturnMergingInfo(27, 103, 0, {{}})}})
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationMergeFunctionReturns(
+                   14, 100, 200, 101, 102, 10,
+                   {{MakeReturnMergingInfo(27, 103, 0, {{}})}})
+                   .IsApplicable(context.get(), transformation_context));
 
   // Fresh id %102 is repeated.
-  ASSERT_FALSE(
-      TransformationMergeFunctionReturns(
-          14, 100, 101, 102, 10, {{MakeReturnMergingInfo(27, 102, 104, {{}})}})
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationMergeFunctionReturns(
+                   14, 100, 200, 101, 102, 10,
+                   {{MakeReturnMergingInfo(27, 102, 104, {{}})}})
+                   .IsApplicable(context.get(), transformation_context));
 
   // Id %11 (type int) does not have the correct type (float) for OpPhi
   // instruction %31.
   ASSERT_FALSE(
       TransformationMergeFunctionReturns(
-          16, 100, 101, 0, 0,
+          16, 100, 200, 101, 0, 0,
           {{MakeReturnMergingInfo(36, 103, 104, {{{31, 11}, {32, 11}}})}})
           .IsApplicable(context.get(), transformation_context));
 
@@ -446,21 +457,21 @@
   // instruction %31.
   ASSERT_FALSE(
       TransformationMergeFunctionReturns(
-          16, 100, 101, 0, 0,
+          16, 100, 200, 101, 0, 0,
           {{MakeReturnMergingInfo(36, 102, 0, {{{31, 11}, {32, 11}}})}})
           .IsApplicable(context.get(), transformation_context));
 
   // Id %43 is not available at the end of the entry block.
   ASSERT_FALSE(
       TransformationMergeFunctionReturns(
-          16, 100, 101, 0, 0,
+          16, 100, 200, 101, 0, 0,
           {{MakeReturnMergingInfo(36, 102, 0, {{{31, 44}, {32, 11}}})}})
           .IsApplicable(context.get(), transformation_context));
 
   // There is not a mapping for id %31 (float OpPhi instruction in a loop merge
   // block) and no suitable id is available at the end of the entry block.
   ASSERT_FALSE(TransformationMergeFunctionReturns(
-                   16, 100, 101, 0, 0,
+                   16, 100, 200, 101, 0, 0,
                    {{MakeReturnMergingInfo(36, 102, 0, {{{32, 11}}})}})
                    .IsApplicable(context.get(), transformation_context));
 
@@ -468,7 +479,7 @@
   // available at the end of the entry block.
   ASSERT_FALSE(
       TransformationMergeFunctionReturns(
-          16, 100, 101, 0, 0,
+          16, 100, 200, 101, 0, 0,
           {{MakeReturnMergingInfo(36, 102, 0, {{{31, 1000}, {32, 11}}})}})
           .IsApplicable(context.get(), transformation_context));
 }
@@ -560,7 +571,7 @@
 
   // The 0s are allowed because the function's return type is void.
   auto transformation1 =
-      TransformationMergeFunctionReturns(14, 100, 101, 0, 0, {{}});
+      TransformationMergeFunctionReturns(14, 100, 200, 101, 0, 0, {{}});
   ASSERT_TRUE(
       transformation1.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation1, context.get(),
@@ -570,13 +581,14 @@
 
   // %12 is available at the end of the entry block of %19 (it is a global
   // variable).
-  ASSERT_TRUE(TransformationMergeFunctionReturns(19, 110, 111, 112, 12, {{}})
-                  .IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(
+      TransformationMergeFunctionReturns(19, 110, 210, 111, 112, 12, {{}})
+          .IsApplicable(context.get(), transformation_context));
 
   // %1000 cannot be found in the module, but there is a suitable id available
   // at the end of the entry block (%12).
   auto transformation2 =
-      TransformationMergeFunctionReturns(19, 110, 111, 112, 1000, {{}});
+      TransformationMergeFunctionReturns(19, 110, 210, 111, 112, 1000, {{}});
   ASSERT_TRUE(
       transformation2.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation2, context.get(),
@@ -586,13 +598,14 @@
 
   // %27 is available at the end of the entry block of %26 (it is a function
   // parameter).
-  ASSERT_TRUE(TransformationMergeFunctionReturns(26, 120, 121, 122, 27, {{}})
-                  .IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(
+      TransformationMergeFunctionReturns(26, 120, 220, 121, 122, 27, {{}})
+          .IsApplicable(context.get(), transformation_context));
 
   // %1000 cannot be found in the module, but there is a suitable id available
   // at the end of the entry block (%27).
   auto transformation3 =
-      TransformationMergeFunctionReturns(26, 120, 121, 122, 1000, {{}});
+      TransformationMergeFunctionReturns(26, 120, 220, 121, 122, 1000, {{}});
   ASSERT_TRUE(
       transformation3.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation3, context.get(),
@@ -602,13 +615,14 @@
 
   // %35 is available at the end of the entry block of %33 (it is in the entry
   // block).
-  ASSERT_TRUE(TransformationMergeFunctionReturns(26, 130, 131, 132, 27, {{}})
-                  .IsApplicable(context.get(), transformation_context));
+  ASSERT_TRUE(
+      TransformationMergeFunctionReturns(26, 130, 230, 131, 132, 27, {{}})
+          .IsApplicable(context.get(), transformation_context));
 
   // %1000 cannot be found in the module, but there is a suitable id available
   // at the end of the entry block (%35).
   auto transformation4 =
-      TransformationMergeFunctionReturns(33, 130, 131, 132, 1000, {{}});
+      TransformationMergeFunctionReturns(33, 130, 230, 131, 132, 1000, {{}});
   ASSERT_TRUE(
       transformation4.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation4, context.get(),
@@ -642,8 +656,8 @@
          %15 = OpLabel
                OpBranch %100
         %100 = OpLabel
-               OpLoopMerge %101 %100 None
-               OpBranchConditional %11 %16 %100
+               OpLoopMerge %101 %200 None
+               OpBranch %16
          %16 = OpLabel
                OpSelectionMerge %17 None
                OpBranchConditional %11 %18 %17
@@ -653,13 +667,15 @@
                OpBranch %101
         %101 = OpLabel
                OpReturn
+        %200 = OpLabel
+               OpBranch %100
                OpFunctionEnd
          %19 = OpFunction %5 None %6
          %20 = OpLabel
                OpBranch %110
         %110 = OpLabel
-               OpLoopMerge %111 %110 None
-               OpBranchConditional %11 %21 %110
+               OpLoopMerge %111 %210 None
+               OpBranch %21
          %21 = OpLabel
                OpSelectionMerge %22 None
                OpBranchConditional %11 %23 %24
@@ -673,14 +689,16 @@
         %111 = OpLabel
         %112 = OpPhi %5 %12 %23 %25 %24
                OpReturnValue %112
+        %210 = OpLabel
+               OpBranch %110
                OpFunctionEnd
          %26 = OpFunction %7 None %8
          %27 = OpFunctionParameter %7
          %28 = OpLabel
                OpBranch %120
         %120 = OpLabel
-               OpLoopMerge %121 %120 None
-               OpBranchConditional %11 %29 %120
+               OpLoopMerge %121 %220 None
+               OpBranch %29
          %29 = OpLabel
                OpSelectionMerge %30 None
                OpBranchConditional %11 %31 %30
@@ -692,14 +710,16 @@
         %121 = OpLabel
         %122 = OpPhi %7 %27 %30 %32 %31
                OpReturnValue %122
+        %220 = OpLabel
+               OpBranch %120
                OpFunctionEnd
          %33 = OpFunction %7 None %9
          %34 = OpLabel
          %35 = OpConvertSToF %7 %12
                OpBranch %130
         %130 = OpLabel
-               OpLoopMerge %131 %130 None
-               OpBranchConditional %11 %36 %130
+               OpLoopMerge %131 %230 None
+               OpBranch %36
          %36 = OpLabel
                OpSelectionMerge %37 None
                OpBranchConditional %11 %38 %37
@@ -711,6 +731,8 @@
         %131 = OpLabel
         %132 = OpPhi %7 %35 %37 %39 %38
                OpReturnValue %132
+        %230 = OpLabel
+               OpBranch %130
                OpFunctionEnd
 )";
 
@@ -743,8 +765,8 @@
          %15 = OpLabel
                OpBranch %16
          %16 = OpLabel
-               OpLoopMerge %17 %16 None
-               OpBranchConditional %8 %18 %16
+               OpLoopMerge %17 %916 None
+               OpBranch %18
          %18 = OpLabel
                OpLoopMerge %19 %20 None
                OpBranchConditional %8 %19 %21
@@ -767,8 +789,8 @@
          %28 = OpLabel
                OpBranch %29
          %29 = OpLabel
-               OpLoopMerge %30 %29 None
-               OpBranchConditional %8 %30 %29
+               OpLoopMerge %30 %929 None
+               OpBranch %30
          %30 = OpLabel
                OpLoopMerge %31 %32 None
                OpBranch %33
@@ -792,6 +814,10 @@
                OpBranch %38
          %38 = OpLabel
                OpReturnValue %12
+        %916 = OpLabel
+               OpBranch %16
+        %929 = OpLabel
+               OpBranch %29
                OpFunctionEnd
 )";
 
@@ -805,7 +831,7 @@
       MakeUnique<FactManager>(context.get()), validator_options);
 
   auto transformation = TransformationMergeFunctionReturns(
-      14, 100, 101, 102, 11,
+      14, 100, 200, 101, 102, 11,
       {{MakeReturnMergingInfo(19, 103, 104, {{}}),
         MakeReturnMergingInfo(17, 105, 106, {{}}),
         MakeReturnMergingInfo(31, 107, 108, {{{35, 10}, {36, 12}}}),
@@ -841,11 +867,11 @@
          %15 = OpLabel
                OpBranch %100
         %100 = OpLabel
-               OpLoopMerge %101 %100 None
-               OpBranchConditional %8 %16 %100
+               OpLoopMerge %101 %200 None
+               OpBranch %16
          %16 = OpLabel
-               OpLoopMerge %17 %16 None
-               OpBranchConditional %8 %18 %16
+               OpLoopMerge %17 %916 None
+               OpBranch %18
          %18 = OpLabel
                OpLoopMerge %19 %20 None
                OpBranchConditional %8 %19 %21
@@ -872,8 +898,8 @@
          %28 = OpLabel
                OpBranch %29
          %29 = OpLabel
-               OpLoopMerge %30 %29 None
-               OpBranchConditional %8 %30 %29
+               OpLoopMerge %30 %929 None
+               OpBranch %30
          %30 = OpLabel
                OpLoopMerge %31 %32 None
                OpBranch %33
@@ -901,9 +927,15 @@
                OpBranchConditional %109 %101 %38
          %38 = OpLabel
                OpBranch %101
+        %916 = OpLabel
+               OpBranch %16
+        %929 = OpLabel
+               OpBranch %29
         %101 = OpLabel
         %102 = OpPhi %5 %106 %17 %110 %23 %12 %38
                OpReturnValue %102
+        %200 = OpLabel
+               OpBranch %100
                OpFunctionEnd
 )";
 
@@ -997,7 +1029,7 @@
   // No mapping from merge block %16 to fresh ids is given, so overflow ids are
   // needed.
   auto transformation1 =
-      TransformationMergeFunctionReturns(12, 100, 101, 102, 10, {{}});
+      TransformationMergeFunctionReturns(12, 100, 200, 101, 102, 10, {{}});
 
 #ifndef NDEBUG
   ASSERT_DEATH(
@@ -1016,7 +1048,7 @@
   // No mapping from merge block %27 to fresh ids is given, so overflow ids are
   // needed.
   auto transformation2 =
-      TransformationMergeFunctionReturns(24, 110, 111, 0, 0, {{}});
+      TransformationMergeFunctionReturns(24, 110, 210, 111, 0, 0, {{}});
 
 #ifndef NDEBUG
   ASSERT_DEATH(
@@ -1054,8 +1086,8 @@
          %13 = OpLabel
                OpBranch %100
         %100 = OpLabel
-               OpLoopMerge %101 %100 None
-               OpBranchConditional %8 %14 %100
+               OpLoopMerge %101 %200 None
+               OpBranch %14
          %14 = OpLabel
          %15 = OpIAdd %5 %10 %10
                OpLoopMerge %16 %17 None
@@ -1081,13 +1113,15 @@
         %101 = OpLabel
         %102 = OpPhi %5 %1001 %16 %22 %23
                OpReturnValue %102
+        %200 = OpLabel
+               OpBranch %100
                OpFunctionEnd
          %24 = OpFunction %3 None %4
          %25 = OpLabel
                OpBranch %110
         %110 = OpLabel
-               OpLoopMerge %111 %110 None
-               OpBranchConditional %8 %26 %110
+               OpLoopMerge %111 %210 None
+               OpBranch %26
          %26 = OpLabel
                OpLoopMerge %27 %28 None
                OpBranch %29
@@ -1110,6 +1144,8 @@
                OpBranch %111
         %111 = OpLabel
                OpReturn
+        %210 = OpLabel
+               OpBranch %110
                OpFunctionEnd
 )";
 
@@ -1179,7 +1215,7 @@
   // corresponding mapping.
 
   auto transformation = TransformationMergeFunctionReturns(
-      12, 101, 102, 0, 0,
+      12, 101, 200, 102, 0, 0,
       {{MakeReturnMergingInfo(17, 103, 0, {{{25, 7}, {35, 8}}})}});
   ASSERT_TRUE(
       transformation.IsApplicable(context.get(), transformation_context));
@@ -1212,8 +1248,8 @@
          %15 = OpConvertSToF %10 %13
                OpBranch %101
         %101 = OpLabel
-               OpLoopMerge %102 %101 None
-               OpBranchConditional %6 %16 %101
+               OpLoopMerge %102 %200 None
+               OpBranch %16
          %16 = OpLabel
                OpLoopMerge %17 %18 None
                OpBranch %19
@@ -1238,6 +1274,8 @@
                OpBranch %102
         %102 = OpLabel
                OpReturn
+        %200 = OpLabel
+               OpBranch %101
                OpFunctionEnd
 )";
 
@@ -1324,14 +1362,14 @@
   // block %14 is added.
   ASSERT_FALSE(
       TransformationMergeFunctionReturns(
-          2, 100, 101, 0, 0, {{MakeReturnMergingInfo(10, 102, 103, {{}})}})
+          2, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(10, 102, 103, {{}})}})
           .IsApplicable(context.get(), transformation_context));
 
   // In function %18, The definition of id %26 will still dominate its use in
   // instruction %27 (inside merge block %21), because %27 is an OpPhi
   // instruction.
   auto transformation = TransformationMergeFunctionReturns(
-      18, 100, 101, 0, 0, {{MakeReturnMergingInfo(21, 102, 103, {{}})}});
+      18, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(21, 102, 103, {{}})}});
   ASSERT_TRUE(
       transformation.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
@@ -1376,8 +1414,8 @@
          %19 = OpLabel
                OpBranch %100
         %100 = OpLabel
-               OpLoopMerge %101 %100 None
-               OpBranchConditional %6 %20 %100
+               OpLoopMerge %101 %200 None
+               OpBranch %20
          %20 = OpLabel
                OpLoopMerge %21 %22 None
                OpBranch %23
@@ -1399,6 +1437,8 @@
                OpBranch %101
         %101 = OpLabel
                OpReturn
+        %200 = OpLabel
+               OpBranch %100
                OpFunctionEnd
 )";
 
@@ -1482,17 +1522,17 @@
   // block %14 to merge block %10 is added.
   ASSERT_FALSE(
       TransformationMergeFunctionReturns(
-          2, 100, 101, 0, 0, {{MakeReturnMergingInfo(10, 102, 103, {{}})}})
+          2, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(10, 102, 103, {{}})}})
           .IsApplicable(context.get(), transformation_context));
 
   // In function %18, the definition of id %26 will not dominate its use in
   // instruction %28 (inside block %27) after a new branch from return
   // block %25 to merge block %21 is added.
-  ASSERT_FALSE(TransformationMergeFunctionReturns(
-                   2, 100, 101, 0, 0,
-                   {{MakeReturnMergingInfo(10, 102, 0, {{}}),
-                     MakeReturnMergingInfo(21, 103, 0, {{}})}})
-                   .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(
+      TransformationMergeFunctionReturns(
+          2, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(10, 102, 0, {{}}),
+                                    MakeReturnMergingInfo(21, 103, 0, {{}})}})
+          .IsApplicable(context.get(), transformation_context));
 }
 
 TEST(TransformationMergeFunctionReturnsTest, RespectDominanceRules3) {
@@ -1556,7 +1596,7 @@
   // fact that the id definition dominates the uses does not depend on it
   // dominating the merge block.
   auto transformation = TransformationMergeFunctionReturns(
-      2, 100, 101, 0, 0, {{MakeReturnMergingInfo(10, 102, 103, {{}})}});
+      2, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(10, 102, 103, {{}})}});
   ASSERT_TRUE(
       transformation.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
@@ -1579,8 +1619,8 @@
           %8 = OpLabel
                OpBranch %100
         %100 = OpLabel
-               OpLoopMerge %101 %100 None
-               OpBranchConditional %6 %9 %100
+               OpLoopMerge %101 %200 None
+               OpBranch %9
           %9 = OpLabel
                OpLoopMerge %10 %11 None
                OpBranch %12
@@ -1608,6 +1648,8 @@
                OpBranch %101
         %101 = OpLabel
                OpReturn
+        %200 = OpLabel
+               OpBranch %100
                OpFunctionEnd
 )";
 
@@ -1700,7 +1742,7 @@
   // instruction %19 after the transformation is applied, because %13 dominates
   // all of the return blocks.
   auto transformation = TransformationMergeFunctionReturns(
-      2, 100, 101, 0, 0, {{MakeReturnMergingInfo(10, 102, 103, {{}})}});
+      2, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(10, 102, 103, {{}})}});
   ASSERT_TRUE(
       transformation.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
@@ -1710,10 +1752,10 @@
   // In function %20, the definition of id %28 will not dominate its use in
   // instruction %32 after the transformation is applied, because %28 dominates
   // only one of the return blocks.
-  ASSERT_FALSE(
-      TransformationMergeFunctionReturns(
-          20, 100, 101, 0, 0, {{MakeReturnMergingInfo(23, 102, 103, {{}})}})
-          .IsApplicable(context.get(), transformation_context));
+  ASSERT_FALSE(TransformationMergeFunctionReturns(
+                   20, 100, 200, 101, 0, 0,
+                   {{MakeReturnMergingInfo(23, 102, 103, {{}})}})
+                   .IsApplicable(context.get(), transformation_context));
 
   std::string after_transformation = R"(
                OpCapability Shader
@@ -1731,8 +1773,8 @@
           %8 = OpLabel
                OpBranch %100
         %100 = OpLabel
-               OpLoopMerge %101 %100 None
-               OpBranchConditional %6 %9 %100
+               OpLoopMerge %101 %200 None
+               OpBranch %9
           %9 = OpLabel
                OpLoopMerge %10 %11 None
                OpBranch %12
@@ -1759,6 +1801,8 @@
                OpBranch %101
         %101 = OpLabel
                OpReturn
+        %200 = OpLabel
+               OpBranch %100
                OpFunctionEnd
          %20 = OpFunction %3 None %4
          %21 = OpLabel
@@ -1830,7 +1874,7 @@
       MakeUnique<FactManager>(context.get()), validator_options);
 
   auto transformation =
-      TransformationMergeFunctionReturns(2, 100, 101, 0, 0, {});
+      TransformationMergeFunctionReturns(2, 100, 200, 101, 0, 0, {});
   ASSERT_TRUE(
       transformation.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
@@ -1863,8 +1907,8 @@
           %8 = OpLabel
                OpBranch %100
         %100 = OpLabel
-               OpLoopMerge %101 %100 None
-               OpBranchConditional %6 %9 %100
+               OpLoopMerge %101 %200 None
+               OpBranch %9
           %9 = OpLabel
          %10 = OpPhi %5 %6 %100
                OpSelectionMerge %11 None
@@ -1875,6 +1919,8 @@
                OpBranch %101
         %101 = OpLabel
                OpReturn
+        %200 = OpLabel
+               OpBranch %100
                OpFunctionEnd
 )";
 
diff --git a/test/fuzz/transformation_outline_function_test.cpp b/test/fuzz/transformation_outline_function_test.cpp
index c567680..cc62049 100644
--- a/test/fuzz/transformation_outline_function_test.cpp
+++ b/test/fuzz/transformation_outline_function_test.cpp
@@ -3207,6 +3207,45 @@
   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
 }
 
+TEST(TransformationOutlineFunctionTest, NoOutlineWithUnreachableBlocks) {
+  // This checks that outlining will not be performed if a node in the region
+  // has an unreachable predecessor.
+
+  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
+          %4 = OpFunction %2 None %3
+          %7 = OpLabel
+               OpBranch %5
+          %5 = OpLabel
+               OpReturn
+          %6 = OpLabel
+               OpBranch %5
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  spvtools::ValidatorOptions validator_options;
+  ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
+                                               kConsoleMessageConsumer));
+  TransformationContext transformation_context(
+      MakeUnique<FactManager>(context.get()), validator_options);
+  TransformationOutlineFunction transformation(5, 5, /* not relevant */ 200,
+                                               100, 101, 102, 103,
+                                               /* not relevant */ 201, {}, {});
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
+}
+
 }  // namespace
 }  // namespace fuzz
 }  // namespace spvtools
diff --git a/test/fuzz/transformation_permute_phi_operands_test.cpp b/test/fuzz/transformation_permute_phi_operands_test.cpp
index 2843cfc..0774dcf 100644
--- a/test/fuzz/transformation_permute_phi_operands_test.cpp
+++ b/test/fuzz/transformation_permute_phi_operands_test.cpp
@@ -60,6 +60,7 @@
                OpBranch %17
          %17 = OpLabel
          %25 = OpPhi %6 %20 %16 %24 %21
+         %30 = OpIAdd %6 %25 %25
                OpStore %8 %25
                OpReturn
                OpFunctionEnd
@@ -105,6 +106,47 @@
       transformation.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
 
+  // Check that the def-use manager knows that the phi instruction's ids have
+  // been permuted.
+  std::vector<std::pair<uint32_t, uint32_t>> phi_operand_to_new_operand_index =
+      {{20, 4}, {16, 5}, {24, 2}, {21, 3}};
+  for (std::pair<uint32_t, uint32_t>& entry :
+       phi_operand_to_new_operand_index) {
+    context->get_def_use_mgr()->WhileEachUse(
+        entry.first,
+        [&entry](opt::Instruction* inst, uint32_t operand_index) -> bool {
+          if (inst->result_id() == 25) {
+            EXPECT_EQ(entry.second, operand_index);
+            return false;
+          }
+          return true;
+        });
+  }
+  bool found_use_in_store = false;
+  bool found_use_in_add_lhs = false;
+  bool found_use_in_add_rhs = false;
+  context->get_def_use_mgr()->ForEachUse(
+      25, [&found_use_in_store, &found_use_in_add_lhs, &found_use_in_add_rhs](
+              opt::Instruction* inst, uint32_t operand_index) {
+        if (inst->opcode() == SpvOpStore) {
+          ASSERT_FALSE(found_use_in_store);
+          found_use_in_store = true;
+        } else {
+          ASSERT_EQ(SpvOpIAdd, inst->opcode());
+          if (operand_index == 2) {
+            ASSERT_FALSE(found_use_in_add_lhs);
+            found_use_in_add_lhs = true;
+          } else {
+            ASSERT_EQ(3, operand_index);
+            ASSERT_FALSE(found_use_in_add_rhs);
+            found_use_in_add_rhs = true;
+          }
+        }
+      });
+  ASSERT_TRUE(found_use_in_store);
+  ASSERT_TRUE(found_use_in_add_lhs);
+  ASSERT_TRUE(found_use_in_add_rhs);
+
   std::string after_transformation = R"(
                OpCapability Shader
           %1 = OpExtInstImport "GLSL.std.450"
@@ -142,6 +184,7 @@
                OpBranch %17
          %17 = OpLabel
          %25 = OpPhi %6 %24 %21 %20 %16
+         %30 = OpIAdd %6 %25 %25
                OpStore %8 %25
                OpReturn
                OpFunctionEnd
diff --git a/test/fuzz/transformation_push_id_through_variable_test.cpp b/test/fuzz/transformation_push_id_through_variable_test.cpp
index cd45c4c..7487cab 100644
--- a/test/fuzz/transformation_push_id_through_variable_test.cpp
+++ b/test/fuzz/transformation_push_id_through_variable_test.cpp
@@ -341,7 +341,26 @@
   auto transformation = TransformationPushIdThroughVariable(
       value_id, value_synonym_id, variable_id, variable_storage_class,
       initializer_id, instruction_descriptor);
+  ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(value_synonym_id));
+  ASSERT_EQ(nullptr, context->get_instr_block(value_synonym_id));
+  ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(variable_id));
+  ASSERT_EQ(nullptr, context->get_instr_block(variable_id));
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
+  ASSERT_EQ(SpvOpLoad,
+            context->get_def_use_mgr()->GetDef(value_synonym_id)->opcode());
+  ASSERT_EQ(36, context->get_instr_block(value_synonym_id)->id());
+  ASSERT_EQ(SpvOpVariable,
+            context->get_def_use_mgr()->GetDef(variable_id)->opcode());
+  ASSERT_EQ(5, context->get_instr_block(variable_id)->id());
+  uint32_t variable_use_count = 0;
+  context->get_def_use_mgr()->ForEachUse(
+      variable_id,
+      [&variable_use_count](opt::Instruction* inst, uint32_t /*unused*/) {
+        ASSERT_TRUE(inst->opcode() == SpvOpLoad ||
+                    inst->opcode() == SpvOpStore);
+        variable_use_count++;
+      });
+  ASSERT_EQ(2, variable_use_count);
 
   value_id = 21;
   value_synonym_id = 102;
diff --git a/test/fuzz/transformation_replace_branch_from_dead_block_with_exit_test.cpp b/test/fuzz/transformation_replace_branch_from_dead_block_with_exit_test.cpp
index 4532503..6bba14f 100644
--- a/test/fuzz/transformation_replace_branch_from_dead_block_with_exit_test.cpp
+++ b/test/fuzz/transformation_replace_branch_from_dead_block_with_exit_test.cpp
@@ -566,6 +566,302 @@
   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
 }
 
+TEST(TransformationReplaceBranchFromDeadBlockWithExitTest,
+     DominatorAfterDeadBlockSuccessor) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 320
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool
+          %7 = OpConstantFalse %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpSelectionMerge %8 None
+               OpBranchConditional %7 %9 %10
+          %9 = OpLabel
+               OpBranch %11
+         %11 = OpLabel
+         %12 = OpCopyObject %6 %7
+               OpBranch %8
+         %10 = OpLabel
+               OpBranch %13
+          %8 = OpLabel
+               OpReturn
+         %13 = OpLabel
+               OpBranch %8
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  spvtools::ValidatorOptions validator_options;
+  ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
+                                               kConsoleMessageConsumer));
+  TransformationContext transformation_context(
+      MakeUnique<FactManager>(context.get()), validator_options);
+
+  transformation_context.GetFactManager()->AddFactBlockIsDead(9);
+  transformation_context.GetFactManager()->AddFactBlockIsDead(11);
+
+  ASSERT_FALSE(
+      TransformationReplaceBranchFromDeadBlockWithExit(11, SpvOpUnreachable, 0)
+          .IsApplicable(context.get(), transformation_context));
+}
+
+TEST(TransformationReplaceBranchFromDeadBlockWithExitTest,
+     UnreachableSuccessor) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 320
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool
+          %7 = OpConstantFalse %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpSelectionMerge %8 None
+               OpBranchConditional %7 %9 %10
+          %9 = OpLabel
+               OpBranch %11
+         %11 = OpLabel
+         %12 = OpCopyObject %6 %7
+               OpBranch %8
+         %10 = OpLabel
+               OpReturn
+          %8 = OpLabel
+               OpReturn
+         %13 = OpLabel
+               OpBranch %8
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  spvtools::ValidatorOptions validator_options;
+  ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
+                                               kConsoleMessageConsumer));
+  TransformationContext transformation_context(
+      MakeUnique<FactManager>(context.get()), validator_options);
+
+  transformation_context.GetFactManager()->AddFactBlockIsDead(9);
+  transformation_context.GetFactManager()->AddFactBlockIsDead(11);
+
+  TransformationReplaceBranchFromDeadBlockWithExit transformation(
+      11, SpvOpUnreachable, 0);
+  ASSERT_TRUE(
+      transformation.IsApplicable(context.get(), transformation_context));
+  transformation.Apply(context.get(), &transformation_context);
+  ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
+                                               kConsoleMessageConsumer));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 320
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool
+          %7 = OpConstantFalse %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpSelectionMerge %8 None
+               OpBranchConditional %7 %9 %10
+          %9 = OpLabel
+               OpBranch %11
+         %11 = OpLabel
+         %12 = OpCopyObject %6 %7
+               OpUnreachable
+         %10 = OpLabel
+               OpReturn
+          %8 = OpLabel
+               OpReturn
+         %13 = OpLabel
+               OpBranch %8
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationReplaceBranchFromDeadBlockWithExitTest,
+     DeadBlockAfterItsSuccessor) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 320
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool
+          %7 = OpConstantTrue %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpSelectionMerge %8 None
+               OpBranchConditional %7 %9 %10
+          %9 = OpLabel
+               OpBranch %11
+         %11 = OpLabel
+         %12 = OpCopyObject %6 %7
+               OpBranch %8
+         %10 = OpLabel
+               OpBranch %13
+          %8 = OpLabel
+               OpReturn
+         %13 = OpLabel
+               OpBranch %8
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  spvtools::ValidatorOptions validator_options;
+  ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
+                                               kConsoleMessageConsumer));
+  TransformationContext transformation_context(
+      MakeUnique<FactManager>(context.get()), validator_options);
+
+  transformation_context.GetFactManager()->AddFactBlockIsDead(10);
+  transformation_context.GetFactManager()->AddFactBlockIsDead(13);
+
+  TransformationReplaceBranchFromDeadBlockWithExit transformation(
+      13, SpvOpUnreachable, 0);
+  ASSERT_TRUE(
+      transformation.IsApplicable(context.get(), transformation_context));
+  transformation.Apply(context.get(), &transformation_context);
+  ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
+                                               kConsoleMessageConsumer));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 320
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool
+          %7 = OpConstantTrue %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpSelectionMerge %8 None
+               OpBranchConditional %7 %9 %10
+          %9 = OpLabel
+               OpBranch %11
+         %11 = OpLabel
+         %12 = OpCopyObject %6 %7
+               OpBranch %8
+         %10 = OpLabel
+               OpBranch %13
+          %8 = OpLabel
+               OpReturn
+         %13 = OpLabel
+               OpUnreachable
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationReplaceBranchFromDeadBlockWithExitTest,
+     BranchToOuterMergeBlock) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 320
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool
+          %7 = OpConstantTrue %6
+         %15 = OpTypeInt 32 0
+         %14 = OpUndef %15
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpSelectionMerge %8 None
+               OpSwitch %14 %9 1 %8
+          %9 = OpLabel
+               OpSelectionMerge %10 None
+               OpBranchConditional %7 %11 %10
+          %8 = OpLabel
+               OpReturn
+         %11 = OpLabel
+               OpBranch %8
+         %10 = OpLabel
+               OpBranch %8
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  spvtools::ValidatorOptions validator_options;
+  ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
+                                               kConsoleMessageConsumer));
+  TransformationContext transformation_context(
+      MakeUnique<FactManager>(context.get()), validator_options);
+
+  transformation_context.GetFactManager()->AddFactBlockIsDead(10);
+
+  TransformationReplaceBranchFromDeadBlockWithExit transformation(
+      10, SpvOpUnreachable, 0);
+  ASSERT_TRUE(
+      transformation.IsApplicable(context.get(), transformation_context));
+  transformation.Apply(context.get(), &transformation_context);
+  ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
+                                               kConsoleMessageConsumer));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 320
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool
+          %7 = OpConstantTrue %6
+         %15 = OpTypeInt 32 0
+         %14 = OpUndef %15
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpSelectionMerge %8 None
+               OpSwitch %14 %9 1 %8
+          %9 = OpLabel
+               OpSelectionMerge %10 None
+               OpBranchConditional %7 %11 %10
+          %8 = OpLabel
+               OpReturn
+         %11 = OpLabel
+               OpBranch %8
+         %10 = OpLabel
+               OpUnreachable
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
 }  // namespace
 }  // namespace fuzz
 }  // namespace spvtools
diff --git a/test/fuzz/transformation_replace_id_with_synonym_test.cpp b/test/fuzz/transformation_replace_id_with_synonym_test.cpp
index 5c10fc5..629a00e 100644
--- a/test/fuzz/transformation_replace_id_with_synonym_test.cpp
+++ b/test/fuzz/transformation_replace_id_with_synonym_test.cpp
@@ -303,10 +303,18 @@
   auto global_constant_synonym = TransformationReplaceIdWithSynonym(
       MakeIdUseDescriptor(19, MakeInstructionDescriptor(47, SpvOpStore, 0), 1),
       210);
+  uint32_t num_uses_of_original_id_before_replacement =
+      context->get_def_use_mgr()->NumUses(19);
+  uint32_t num_uses_of_synonym_before_replacement =
+      context->get_def_use_mgr()->NumUses(210);
   ASSERT_TRUE(global_constant_synonym.IsApplicable(context.get(),
                                                    transformation_context));
   ApplyAndCheckFreshIds(global_constant_synonym, context.get(),
                         &transformation_context);
+  ASSERT_EQ(num_uses_of_original_id_before_replacement - 1,
+            context->get_def_use_mgr()->NumUses(19));
+  ASSERT_EQ(num_uses_of_synonym_before_replacement + 1,
+            context->get_def_use_mgr()->NumUses(210));
   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
                                                kConsoleMessageConsumer));
 
diff --git a/test/fuzz/transformation_set_loop_control_test.cpp b/test/fuzz/transformation_set_loop_control_test.cpp
index 3312a67..88b4aab 100644
--- a/test/fuzz/transformation_set_loop_control_test.cpp
+++ b/test/fuzz/transformation_set_loop_control_test.cpp
@@ -947,7 +947,9 @@
 
   for (auto env :
        {SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1, SPV_ENV_UNIVERSAL_1_2,
-        SPV_ENV_UNIVERSAL_1_3, SPV_ENV_UNIVERSAL_1_4, SPV_ENV_UNIVERSAL_1_5}) {
+        SPV_ENV_UNIVERSAL_1_3, SPV_ENV_UNIVERSAL_1_4, SPV_ENV_UNIVERSAL_1_5,
+        SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_1_SPIRV_1_4,
+        SPV_ENV_VULKAN_1_2}) {
     const auto consumer = nullptr;
     const auto context =
         BuildModule(env, consumer, shader, kFuzzAssembleOption);
@@ -956,23 +958,33 @@
         context.get(), validator_options, kConsoleMessageConsumer));
     TransformationContext transformation_context(
         MakeUnique<FactManager>(context.get()), validator_options);
-    TransformationSetLoopControl transformation(
-        10, SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask, 4, 4);
+    TransformationSetLoopControl peel_count(10, SpvLoopControlPeelCountMask, 4,
+                                            0);
+    TransformationSetLoopControl partial_count(
+        10, SpvLoopControlPartialCountMask, 0, 4);
 
     switch (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:
+      case SPV_ENV_VULKAN_1_0:
+      case SPV_ENV_VULKAN_1_1:
         // PeelCount and PartialCount were introduced in SPIRV 1.4, so are not
         // valid in the context of older versions.
         ASSERT_FALSE(
-            transformation.IsApplicable(context.get(), transformation_context));
+            peel_count.IsApplicable(context.get(), transformation_context));
+        ASSERT_FALSE(
+            partial_count.IsApplicable(context.get(), transformation_context));
         break;
       case SPV_ENV_UNIVERSAL_1_4:
       case SPV_ENV_UNIVERSAL_1_5:
+      case SPV_ENV_VULKAN_1_1_SPIRV_1_4:
+      case SPV_ENV_VULKAN_1_2:
         ASSERT_TRUE(
-            transformation.IsApplicable(context.get(), transformation_context));
+            peel_count.IsApplicable(context.get(), transformation_context));
+        ASSERT_TRUE(
+            partial_count.IsApplicable(context.get(), transformation_context));
         break;
       default:
         assert(false && "Unhandled environment");
diff --git a/test/fuzz/transformation_set_memory_operands_mask_test.cpp b/test/fuzz/transformation_set_memory_operands_mask_test.cpp
index 4763d7a..29e57f4 100644
--- a/test/fuzz/transformation_set_memory_operands_mask_test.cpp
+++ b/test/fuzz/transformation_set_memory_operands_mask_test.cpp
@@ -90,187 +90,205 @@
                OpFunctionEnd
   )";
 
-  const auto env = SPV_ENV_UNIVERSAL_1_3;
-  const auto consumer = nullptr;
-  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
-  spvtools::ValidatorOptions validator_options;
-  ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
-                                               kConsoleMessageConsumer));
-  TransformationContext transformation_context(
-      MakeUnique<FactManager>(context.get()), validator_options);
-  // Not OK: the instruction is not a memory access.
-  ASSERT_FALSE(TransformationSetMemoryOperandsMask(
-                   MakeInstructionDescriptor(21, SpvOpAccessChain, 0),
-                   SpvMemoryAccessMaskNone, 0)
-                   .IsApplicable(context.get(), transformation_context));
+  for (auto env :
+       {SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1, SPV_ENV_UNIVERSAL_1_2,
+        SPV_ENV_UNIVERSAL_1_3, SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1}) {
+    const auto consumer = nullptr;
+    const auto context =
+        BuildModule(env, consumer, shader, kFuzzAssembleOption);
+    spvtools::ValidatorOptions validator_options;
+    ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
+        context.get(), validator_options, kConsoleMessageConsumer));
+    TransformationContext transformation_context(
+        MakeUnique<FactManager>(context.get()), validator_options);
 
-  // Not OK to remove Aligned
-  ASSERT_FALSE(TransformationSetMemoryOperandsMask(
-                   MakeInstructionDescriptor(147, SpvOpLoad, 0),
-                   SpvMemoryAccessVolatileMask | SpvMemoryAccessNontemporalMask,
-                   0)
-                   .IsApplicable(context.get(), transformation_context));
+#ifndef NDEBUG
+    {
+      // Not OK: multiple operands are not supported pre SPIR-V 1.4.
+      TransformationSetMemoryOperandsMask transformation(
+          MakeInstructionDescriptor(21, SpvOpCopyMemory, 3),
+          SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 1);
+      ASSERT_DEATH(
+          transformation.IsApplicable(context.get(), transformation_context),
+          "Multiple memory operand masks are not supported");
+    }
+#endif
 
-  {
-    TransformationSetMemoryOperandsMask transformation(
-        MakeInstructionDescriptor(147, SpvOpLoad, 0),
-        SpvMemoryAccessAlignedMask | SpvMemoryAccessVolatileMask, 0);
-    ASSERT_TRUE(
-        transformation.IsApplicable(context.get(), transformation_context));
-    ApplyAndCheckFreshIds(transformation, context.get(),
-                          &transformation_context);
+    // Not OK: the instruction is not a memory access.
+    ASSERT_FALSE(TransformationSetMemoryOperandsMask(
+                     MakeInstructionDescriptor(21, SpvOpAccessChain, 0),
+                     SpvMemoryAccessMaskNone, 0)
+                     .IsApplicable(context.get(), transformation_context));
+
+    // Not OK to remove Aligned
+    ASSERT_FALSE(
+        TransformationSetMemoryOperandsMask(
+            MakeInstructionDescriptor(147, SpvOpLoad, 0),
+            SpvMemoryAccessVolatileMask | SpvMemoryAccessNontemporalMask, 0)
+            .IsApplicable(context.get(), transformation_context));
+
+    {
+      TransformationSetMemoryOperandsMask transformation(
+          MakeInstructionDescriptor(147, SpvOpLoad, 0),
+          SpvMemoryAccessAlignedMask | SpvMemoryAccessVolatileMask, 0);
+      ASSERT_TRUE(
+          transformation.IsApplicable(context.get(), transformation_context));
+      ApplyAndCheckFreshIds(transformation, context.get(),
+                            &transformation_context);
+    }
+
+    // Not OK to remove Aligned
+    ASSERT_FALSE(TransformationSetMemoryOperandsMask(
+                     MakeInstructionDescriptor(21, SpvOpCopyMemory, 0),
+                     SpvMemoryAccessMaskNone, 0)
+                     .IsApplicable(context.get(), transformation_context));
+
+    // OK: leaves the mask as is
+    ASSERT_TRUE(TransformationSetMemoryOperandsMask(
+                    MakeInstructionDescriptor(21, SpvOpCopyMemory, 0),
+                    SpvMemoryAccessAlignedMask, 0)
+                    .IsApplicable(context.get(), transformation_context));
+
+    {
+      // OK: adds Nontemporal and Volatile
+      TransformationSetMemoryOperandsMask transformation(
+          MakeInstructionDescriptor(21, SpvOpCopyMemory, 0),
+          SpvMemoryAccessAlignedMask | SpvMemoryAccessNontemporalMask |
+              SpvMemoryAccessVolatileMask,
+          0);
+      ASSERT_TRUE(
+          transformation.IsApplicable(context.get(), transformation_context));
+      ApplyAndCheckFreshIds(transformation, context.get(),
+                            &transformation_context);
+    }
+
+    // Not OK to remove Volatile
+    ASSERT_FALSE(TransformationSetMemoryOperandsMask(
+                     MakeInstructionDescriptor(21, SpvOpCopyMemory, 1),
+                     SpvMemoryAccessNontemporalMask, 0)
+                     .IsApplicable(context.get(), transformation_context));
+
+    // Not OK to add Aligned
+    ASSERT_FALSE(TransformationSetMemoryOperandsMask(
+                     MakeInstructionDescriptor(21, SpvOpCopyMemory, 1),
+                     SpvMemoryAccessAlignedMask | SpvMemoryAccessVolatileMask,
+                     0)
+                     .IsApplicable(context.get(), transformation_context));
+
+    {
+      // OK: adds Nontemporal
+      TransformationSetMemoryOperandsMask transformation(
+          MakeInstructionDescriptor(21, SpvOpCopyMemory, 1),
+          SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 0);
+      ASSERT_TRUE(
+          transformation.IsApplicable(context.get(), transformation_context));
+      ApplyAndCheckFreshIds(transformation, context.get(),
+                            &transformation_context);
+    }
+
+    {
+      // OK: adds Nontemporal (creates new operand)
+      TransformationSetMemoryOperandsMask transformation(
+          MakeInstructionDescriptor(21, SpvOpCopyMemory, 2),
+          SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 0);
+      ASSERT_TRUE(
+          transformation.IsApplicable(context.get(), transformation_context));
+      ApplyAndCheckFreshIds(transformation, context.get(),
+                            &transformation_context);
+    }
+
+    {
+      // OK: adds Nontemporal and Volatile
+      TransformationSetMemoryOperandsMask transformation(
+          MakeInstructionDescriptor(138, SpvOpCopyMemory, 0),
+          SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 0);
+      ASSERT_TRUE(
+          transformation.IsApplicable(context.get(), transformation_context));
+      ApplyAndCheckFreshIds(transformation, context.get(),
+                            &transformation_context);
+    }
+
+    {
+      // OK: removes Nontemporal, adds Volatile
+      TransformationSetMemoryOperandsMask transformation(
+          MakeInstructionDescriptor(148, SpvOpStore, 0),
+          SpvMemoryAccessVolatileMask, 0);
+      ASSERT_TRUE(
+          transformation.IsApplicable(context.get(), transformation_context));
+      ApplyAndCheckFreshIds(transformation, context.get(),
+                            &transformation_context);
+    }
+
+    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
+                 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()));
   }
-
-  // Not OK to remove Aligned
-  ASSERT_FALSE(TransformationSetMemoryOperandsMask(
-                   MakeInstructionDescriptor(21, SpvOpCopyMemory, 0),
-                   SpvMemoryAccessMaskNone, 0)
-                   .IsApplicable(context.get(), transformation_context));
-
-  // OK: leaves the mask as is
-  ASSERT_TRUE(TransformationSetMemoryOperandsMask(
-                  MakeInstructionDescriptor(21, SpvOpCopyMemory, 0),
-                  SpvMemoryAccessAlignedMask, 0)
-                  .IsApplicable(context.get(), transformation_context));
-
-  {
-    // OK: adds Nontemporal and Volatile
-    TransformationSetMemoryOperandsMask transformation(
-        MakeInstructionDescriptor(21, SpvOpCopyMemory, 0),
-        SpvMemoryAccessAlignedMask | SpvMemoryAccessNontemporalMask |
-            SpvMemoryAccessVolatileMask,
-        0);
-    ASSERT_TRUE(
-        transformation.IsApplicable(context.get(), transformation_context));
-    ApplyAndCheckFreshIds(transformation, context.get(),
-                          &transformation_context);
-  }
-
-  // Not OK to remove Volatile
-  ASSERT_FALSE(TransformationSetMemoryOperandsMask(
-                   MakeInstructionDescriptor(21, SpvOpCopyMemory, 1),
-                   SpvMemoryAccessNontemporalMask, 0)
-                   .IsApplicable(context.get(), transformation_context));
-
-  // Not OK to add Aligned
-  ASSERT_FALSE(TransformationSetMemoryOperandsMask(
-                   MakeInstructionDescriptor(21, SpvOpCopyMemory, 1),
-                   SpvMemoryAccessAlignedMask | SpvMemoryAccessVolatileMask, 0)
-                   .IsApplicable(context.get(), transformation_context));
-
-  {
-    // OK: adds Nontemporal
-    TransformationSetMemoryOperandsMask transformation(
-        MakeInstructionDescriptor(21, SpvOpCopyMemory, 1),
-        SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 0);
-    ASSERT_TRUE(
-        transformation.IsApplicable(context.get(), transformation_context));
-    ApplyAndCheckFreshIds(transformation, context.get(),
-                          &transformation_context);
-  }
-
-  {
-    // OK: adds Nontemporal (creates new operand)
-    TransformationSetMemoryOperandsMask transformation(
-        MakeInstructionDescriptor(21, SpvOpCopyMemory, 2),
-        SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 0);
-    ASSERT_TRUE(
-        transformation.IsApplicable(context.get(), transformation_context));
-    ApplyAndCheckFreshIds(transformation, context.get(),
-                          &transformation_context);
-  }
-
-  {
-    // OK: adds Nontemporal and Volatile
-    TransformationSetMemoryOperandsMask transformation(
-        MakeInstructionDescriptor(138, SpvOpCopyMemory, 0),
-        SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 0);
-    ASSERT_TRUE(
-        transformation.IsApplicable(context.get(), transformation_context));
-    ApplyAndCheckFreshIds(transformation, context.get(),
-                          &transformation_context);
-  }
-
-  {
-    // OK: removes Nontemporal, adds Volatile
-    TransformationSetMemoryOperandsMask transformation(
-        MakeInstructionDescriptor(148, SpvOpStore, 0),
-        SpvMemoryAccessVolatileMask, 0);
-    ASSERT_TRUE(
-        transformation.IsApplicable(context.get(), transformation_context));
-    ApplyAndCheckFreshIds(transformation, context.get(),
-                          &transformation_context);
-  }
-
-  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
-               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) {
+TEST(TransformationSetMemoryOperandsMaskTest, Spirv14OrHigher) {
   std::string shader = R"(
                OpCapability Shader
           %1 = OpExtInstImport "GLSL.std.450"
@@ -339,180 +357,183 @@
                OpFunctionEnd
   )";
 
-  const auto env = SPV_ENV_UNIVERSAL_1_4;
-  const auto consumer = nullptr;
-  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
-  spvtools::ValidatorOptions validator_options;
-  ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
-                                               kConsoleMessageConsumer));
-  TransformationContext transformation_context(
-      MakeUnique<FactManager>(context.get()), validator_options);
-  {
-    TransformationSetMemoryOperandsMask transformation(
-        MakeInstructionDescriptor(21, SpvOpCopyMemory, 0),
-        SpvMemoryAccessAlignedMask | SpvMemoryAccessVolatileMask, 1);
-    // Bad: cannot remove aligned
-    ASSERT_FALSE(TransformationSetMemoryOperandsMask(
-                     MakeInstructionDescriptor(21, SpvOpCopyMemory, 0),
-                     SpvMemoryAccessVolatileMask, 1)
-                     .IsApplicable(context.get(), transformation_context));
-    ASSERT_TRUE(
-        transformation.IsApplicable(context.get(), transformation_context));
-    ApplyAndCheckFreshIds(transformation, context.get(),
-                          &transformation_context);
-  }
+  for (auto env : {SPV_ENV_UNIVERSAL_1_4, SPV_ENV_UNIVERSAL_1_5,
+                   SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_VULKAN_1_2}) {
+    const auto consumer = nullptr;
+    const auto context =
+        BuildModule(env, consumer, shader, kFuzzAssembleOption);
+    spvtools::ValidatorOptions validator_options;
+    ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
+        context.get(), validator_options, kConsoleMessageConsumer));
+    TransformationContext transformation_context(
+        MakeUnique<FactManager>(context.get()), validator_options);
+    {
+      TransformationSetMemoryOperandsMask transformation(
+          MakeInstructionDescriptor(21, SpvOpCopyMemory, 0),
+          SpvMemoryAccessAlignedMask | SpvMemoryAccessVolatileMask, 1);
+      // Bad: cannot remove aligned
+      ASSERT_FALSE(TransformationSetMemoryOperandsMask(
+                       MakeInstructionDescriptor(21, SpvOpCopyMemory, 0),
+                       SpvMemoryAccessVolatileMask, 1)
+                       .IsApplicable(context.get(), transformation_context));
+      ASSERT_TRUE(
+          transformation.IsApplicable(context.get(), transformation_context));
+      ApplyAndCheckFreshIds(transformation, context.get(),
+                            &transformation_context);
+    }
 
-  {
-    TransformationSetMemoryOperandsMask transformation(
-        MakeInstructionDescriptor(21, SpvOpCopyMemory, 1),
-        SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 1);
-    // Bad: cannot remove volatile
-    ASSERT_FALSE(TransformationSetMemoryOperandsMask(
-                     MakeInstructionDescriptor(21, SpvOpCopyMemory, 1),
-                     SpvMemoryAccessNontemporalMask, 0)
-                     .IsApplicable(context.get(), transformation_context));
-    ASSERT_TRUE(
-        transformation.IsApplicable(context.get(), transformation_context));
-    ApplyAndCheckFreshIds(transformation, context.get(),
-                          &transformation_context);
-  }
+    {
+      TransformationSetMemoryOperandsMask transformation(
+          MakeInstructionDescriptor(21, SpvOpCopyMemory, 1),
+          SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 1);
+      // Bad: cannot remove volatile
+      ASSERT_FALSE(TransformationSetMemoryOperandsMask(
+                       MakeInstructionDescriptor(21, SpvOpCopyMemory, 1),
+                       SpvMemoryAccessNontemporalMask, 0)
+                       .IsApplicable(context.get(), transformation_context));
+      ASSERT_TRUE(
+          transformation.IsApplicable(context.get(), transformation_context));
+      ApplyAndCheckFreshIds(transformation, context.get(),
+                            &transformation_context);
+    }
 
-  {
-    // Creates the first operand.
-    TransformationSetMemoryOperandsMask transformation(
-        MakeInstructionDescriptor(21, SpvOpCopyMemory, 2),
-        SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 0);
-    ASSERT_TRUE(
-        transformation.IsApplicable(context.get(), transformation_context));
-    ApplyAndCheckFreshIds(transformation, context.get(),
-                          &transformation_context);
-  }
+    {
+      // Creates the first operand.
+      TransformationSetMemoryOperandsMask transformation(
+          MakeInstructionDescriptor(21, SpvOpCopyMemory, 2),
+          SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 0);
+      ASSERT_TRUE(
+          transformation.IsApplicable(context.get(), transformation_context));
+      ApplyAndCheckFreshIds(transformation, context.get(),
+                            &transformation_context);
+    }
 
-  {
-    // Creates both operands.
-    TransformationSetMemoryOperandsMask transformation(
-        MakeInstructionDescriptor(21, SpvOpCopyMemory, 3),
-        SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 1);
-    ASSERT_TRUE(
-        transformation.IsApplicable(context.get(), transformation_context));
-    ApplyAndCheckFreshIds(transformation, context.get(),
-                          &transformation_context);
-  }
+    {
+      // Creates both operands.
+      TransformationSetMemoryOperandsMask transformation(
+          MakeInstructionDescriptor(21, SpvOpCopyMemory, 3),
+          SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 1);
+      ASSERT_TRUE(
+          transformation.IsApplicable(context.get(), transformation_context));
+      ApplyAndCheckFreshIds(transformation, context.get(),
+                            &transformation_context);
+    }
 
-  {
-    TransformationSetMemoryOperandsMask transformation(
-        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(), transformation_context));
-    ASSERT_TRUE(
-        transformation.IsApplicable(context.get(), transformation_context));
-    ApplyAndCheckFreshIds(transformation, context.get(),
-                          &transformation_context);
-  }
+    {
+      TransformationSetMemoryOperandsMask transformation(
+          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(), transformation_context));
+      ASSERT_TRUE(
+          transformation.IsApplicable(context.get(), transformation_context));
+      ApplyAndCheckFreshIds(transformation, context.get(),
+                            &transformation_context);
+    }
 
-  {
-    TransformationSetMemoryOperandsMask transformation(
-        MakeInstructionDescriptor(138, SpvOpCopyMemory, 1),
-        SpvMemoryAccessVolatileMask, 1);
-    ASSERT_TRUE(
-        transformation.IsApplicable(context.get(), transformation_context));
-    ApplyAndCheckFreshIds(transformation, context.get(),
-                          &transformation_context);
-  }
+    {
+      TransformationSetMemoryOperandsMask transformation(
+          MakeInstructionDescriptor(138, SpvOpCopyMemory, 1),
+          SpvMemoryAccessVolatileMask, 1);
+      ASSERT_TRUE(
+          transformation.IsApplicable(context.get(), transformation_context));
+      ApplyAndCheckFreshIds(transformation, context.get(),
+                            &transformation_context);
+    }
 
-  {
-    TransformationSetMemoryOperandsMask transformation(
-        MakeInstructionDescriptor(147, SpvOpLoad, 0),
-        SpvMemoryAccessVolatileMask | SpvMemoryAccessAlignedMask, 0);
-    ASSERT_TRUE(
-        transformation.IsApplicable(context.get(), transformation_context));
-    ApplyAndCheckFreshIds(transformation, context.get(),
-                          &transformation_context);
-  }
+    {
+      TransformationSetMemoryOperandsMask transformation(
+          MakeInstructionDescriptor(147, SpvOpLoad, 0),
+          SpvMemoryAccessVolatileMask | SpvMemoryAccessAlignedMask, 0);
+      ASSERT_TRUE(
+          transformation.IsApplicable(context.get(), transformation_context));
+      ApplyAndCheckFreshIds(transformation, context.get(),
+                            &transformation_context);
+    }
 
-  {
-    TransformationSetMemoryOperandsMask transformation(
-        MakeInstructionDescriptor(148, SpvOpStore, 0), SpvMemoryAccessMaskNone,
-        0);
-    ASSERT_TRUE(
-        transformation.IsApplicable(context.get(), transformation_context));
-    ApplyAndCheckFreshIds(transformation, context.get(),
-                          &transformation_context);
-  }
+    {
+      TransformationSetMemoryOperandsMask transformation(
+          MakeInstructionDescriptor(148, SpvOpStore, 0),
+          SpvMemoryAccessMaskNone, 0);
+      ASSERT_TRUE(
+          transformation.IsApplicable(context.get(), transformation_context));
+      ApplyAndCheckFreshIds(transformation, context.get(),
+                            &transformation_context);
+    }
 
-  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
-               OpCopyMemory %133 %12 Nontemporal|Volatile
-               OpCopyMemory %133 %12 None Nontemporal|Volatile
-        %136 = OpAccessChain %135 %17 %30
-        %138 = OpAccessChain %24 %12 %19
-               OpCopyMemory %138 %136 None Aligned|Nontemporal 16
-               OpCopyMemory %138 %136 Aligned 16 Volatile
-        %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()));
+    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
+                 OpCopyMemory %133 %12 Nontemporal|Volatile
+                 OpCopyMemory %133 %12 None Nontemporal|Volatile
+          %136 = OpAccessChain %135 %17 %30
+          %138 = OpAccessChain %24 %12 %19
+                 OpCopyMemory %138 %136 None Aligned|Nontemporal 16
+                 OpCopyMemory %138 %136 Aligned 16 Volatile
+          %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
diff --git a/test/fuzz/transformation_swap_conditional_branch_operands_test.cpp b/test/fuzz/transformation_swap_conditional_branch_operands_test.cpp
index e7a8732..6133a7a 100644
--- a/test/fuzz/transformation_swap_conditional_branch_operands_test.cpp
+++ b/test/fuzz/transformation_swap_conditional_branch_operands_test.cpp
@@ -91,9 +91,31 @@
 
   TransformationSwapConditionalBranchOperands transformation(
       MakeInstructionDescriptor(15, SpvOpBranchConditional, 0), 26);
+  ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(26));
+  ASSERT_EQ(nullptr, context->get_instr_block(26));
   ASSERT_TRUE(
       transformation.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
+  ASSERT_EQ(SpvOpLogicalNot, context->get_def_use_mgr()->GetDef(26)->opcode());
+  ASSERT_EQ(5, context->get_instr_block(26)->id());
+  ASSERT_EQ(1, context->get_def_use_mgr()->NumUses(26));
+
+  // Check that the def-use manager knows that the conditional branch operands
+  // have been swapped.
+  std::vector<std::pair<uint32_t, uint32_t>> phi_operand_to_new_operand_index =
+      {{16, 2}, {21, 1}};
+  for (std::pair<uint32_t, uint32_t>& entry :
+       phi_operand_to_new_operand_index) {
+    context->get_def_use_mgr()->WhileEachUse(
+        entry.first,
+        [&entry](opt::Instruction* inst, uint32_t operand_index) -> bool {
+          if (inst->opcode() == SpvOpBranchConditional) {
+            EXPECT_EQ(entry.second, operand_index);
+            return false;
+          }
+          return true;
+        });
+  }
 
   std::string after_transformation = R"(
                OpCapability Shader
diff --git a/test/fuzz/transformation_swap_function_variables_test.cpp b/test/fuzz/transformation_swap_function_variables_test.cpp
new file mode 100644
index 0000000..8132aa4
--- /dev/null
+++ b/test/fuzz/transformation_swap_function_variables_test.cpp
@@ -0,0 +1,288 @@
+// Copyright (c) 2021 Mostafa Ashraf
+//
+// 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_swap_function_variables.h"
+
+#include "gtest/gtest.h"
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationSwapFunctionVariables, NotApplicable) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 320
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %8 = OpTypeFloat 32
+          %9 = OpTypePointer Function %8
+         %10 = OpTypeVector %8 2
+         %11 = OpTypePointer Function %10
+         %12 = OpTypeVector %8 3
+         %13 = OpTypeMatrix %12 3
+         %14 = OpTypePointer Function %13
+         %15 = OpTypeFunction %2 %7 %9 %11 %14 %7 %7
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %24 = OpVariable %7 Function
+         %25 = OpVariable %9 Function
+         %26 = OpVariable %11 Function
+         %27 = OpVariable %14 Function
+         %28 = OpVariable %7 Function
+         %29 = OpVariable %7 Function
+         %30 = OpVariable %7 Function
+         %32 = OpVariable %9 Function
+         %34 = OpVariable %11 Function
+         %36 = OpVariable %14 Function
+         %38 = OpVariable %7 Function
+         %40 = OpVariable %7 Function
+         %31 = OpLoad %6 %24
+               OpStore %30 %31
+         %33 = OpLoad %8 %25
+               OpStore %32 %33
+         %35 = OpLoad %10 %26
+               OpStore %34 %35
+         %37 = OpLoad %13 %27
+               OpStore %36 %37
+         %39 = OpLoad %6 %28
+               OpStore %38 %39
+         %41 = OpLoad %6 %29
+               OpStore %40 %41
+         %42 = OpFunctionCall %2 %22 %30 %32 %34 %36 %38 %40
+               OpReturn
+               OpFunctionEnd
+         %22 = OpFunction %2 None %15
+         %16 = OpFunctionParameter %7
+         %17 = OpFunctionParameter %9
+         %18 = OpFunctionParameter %11
+         %19 = OpFunctionParameter %14
+         %20 = OpFunctionParameter %7
+         %21 = OpFunctionParameter %7
+         %23 = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_5;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  spvtools::ValidatorOptions validator_options;
+
+  ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
+                                               kConsoleMessageConsumer));
+  TransformationContext transformation_context(
+      MakeUnique<FactManager>(context.get()), validator_options);
+
+#ifndef NDEBUG
+  // Can't swap variable with itself.
+  ASSERT_DEATH(TransformationSwapFunctionVariables(7, 7).IsApplicable(
+                   context.get(), transformation_context),
+               "Two results ids are equal");
+#endif
+
+  // Invalid because 200 is not the id of an instruction.
+  ASSERT_FALSE(TransformationSwapFunctionVariables(1, 200).IsApplicable(
+      context.get(), transformation_context));
+  // Invalid because 5 is not the id of an instruction.
+  ASSERT_FALSE(TransformationSwapFunctionVariables(5, 24).IsApplicable(
+      context.get(), transformation_context));
+  // Can't swap two instructions from two different blocks.
+  ASSERT_FALSE(TransformationSwapFunctionVariables(16, 26).IsApplicable(
+      context.get(), transformation_context));
+}
+
+TEST(TransformationSwapFunctionVariables, IsApplicable) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 320
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %8 = OpTypeFloat 32
+          %9 = OpTypePointer Function %8
+         %10 = OpTypeVector %8 2
+         %11 = OpTypePointer Function %10
+         %12 = OpTypeVector %8 3
+         %13 = OpTypeMatrix %12 3
+         %14 = OpTypePointer Function %13
+         %15 = OpTypeFunction %2 %7 %9 %11 %14 %7 %7
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %24 = OpVariable %7 Function
+         %25 = OpVariable %9 Function
+         %26 = OpVariable %11 Function
+         %27 = OpVariable %14 Function
+         %28 = OpVariable %7 Function
+         %29 = OpVariable %7 Function
+         %30 = OpVariable %7 Function
+         %32 = OpVariable %9 Function
+         %34 = OpVariable %11 Function
+         %36 = OpVariable %14 Function
+         %38 = OpVariable %7 Function
+         %40 = OpVariable %7 Function
+         %31 = OpLoad %6 %24
+               OpStore %30 %31
+         %33 = OpLoad %8 %25
+               OpStore %32 %33
+         %35 = OpLoad %10 %26
+               OpStore %34 %35
+         %37 = OpLoad %13 %27
+               OpStore %36 %37
+         %39 = OpLoad %6 %28
+               OpStore %38 %39
+         %41 = OpLoad %6 %29
+               OpStore %40 %41
+         %42 = OpFunctionCall %2 %22 %30 %32 %34 %36 %38 %40
+               OpReturn
+               OpFunctionEnd
+         %22 = OpFunction %2 None %15
+         %16 = OpFunctionParameter %7
+         %17 = OpFunctionParameter %9
+         %18 = OpFunctionParameter %11
+         %19 = OpFunctionParameter %14
+         %20 = OpFunctionParameter %7
+         %21 = OpFunctionParameter %7
+         %23 = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_5;
+  const auto consumer = nullptr;
+  // Get Unique pointer of IRContext.
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  spvtools::ValidatorOptions validator_options;
+
+  ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
+                                               kConsoleMessageConsumer));
+  TransformationContext transformation_context(
+      MakeUnique<FactManager>(context.get()), validator_options);
+
+  // Successful transformations
+  {
+    auto first_instruction = context->get_def_use_mgr()->GetDef(24);
+    auto second_instruction = context->get_def_use_mgr()->GetDef(28);
+    // Swap two OpVariable instructions in the same function.
+    TransformationSwapFunctionVariables transformation(24, 28);
+
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+
+    ApplyAndCheckFreshIds(transformation, context.get(),
+                          &transformation_context);
+
+    ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
+        context.get(), validator_options, kConsoleMessageConsumer));
+
+    ASSERT_EQ(first_instruction, context->get_def_use_mgr()->GetDef(24));
+    ASSERT_EQ(second_instruction, context->get_def_use_mgr()->GetDef(28));
+  }
+  {
+    auto first_instruction = context->get_def_use_mgr()->GetDef(38);
+    auto second_instruction = context->get_def_use_mgr()->GetDef(40);
+    // Swap two OpVariable instructions in the same function.
+    TransformationSwapFunctionVariables transformation(38, 40);
+
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+
+    ApplyAndCheckFreshIds(transformation, context.get(),
+                          &transformation_context);
+
+    ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
+        context.get(), validator_options, kConsoleMessageConsumer));
+
+    ASSERT_EQ(first_instruction, context->get_def_use_mgr()->GetDef(38));
+    ASSERT_EQ(second_instruction, context->get_def_use_mgr()->GetDef(40));
+  }
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 320
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %8 = OpTypeFloat 32
+          %9 = OpTypePointer Function %8
+         %10 = OpTypeVector %8 2
+         %11 = OpTypePointer Function %10
+         %12 = OpTypeVector %8 3
+         %13 = OpTypeMatrix %12 3
+         %14 = OpTypePointer Function %13
+         %15 = OpTypeFunction %2 %7 %9 %11 %14 %7 %7
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %28 = OpVariable %7 Function
+         %25 = OpVariable %9 Function
+         %26 = OpVariable %11 Function
+         %27 = OpVariable %14 Function
+         %24 = OpVariable %7 Function
+         %29 = OpVariable %7 Function
+         %30 = OpVariable %7 Function
+         %32 = OpVariable %9 Function
+         %34 = OpVariable %11 Function
+         %36 = OpVariable %14 Function
+         %40 = OpVariable %7 Function
+         %38 = OpVariable %7 Function
+         %31 = OpLoad %6 %24
+               OpStore %30 %31
+         %33 = OpLoad %8 %25
+               OpStore %32 %33
+         %35 = OpLoad %10 %26
+               OpStore %34 %35
+         %37 = OpLoad %13 %27
+               OpStore %36 %37
+         %39 = OpLoad %6 %28
+               OpStore %38 %39
+         %41 = OpLoad %6 %29
+               OpStore %40 %41
+         %42 = OpFunctionCall %2 %22 %30 %32 %34 %36 %38 %40
+               OpReturn
+               OpFunctionEnd
+         %22 = OpFunction %2 None %15
+         %16 = OpFunctionParameter %7
+         %17 = OpFunctionParameter %9
+         %18 = OpFunctionParameter %11
+         %19 = OpFunctionParameter %14
+         %20 = OpFunctionParameter %7
+         %21 = OpFunctionParameter %7
+         %23 = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/test/fuzz/transformation_swap_two_functions_test.cpp b/test/fuzz/transformation_swap_two_functions_test.cpp
new file mode 100644
index 0000000..38d6a07
--- /dev/null
+++ b/test/fuzz/transformation_swap_two_functions_test.cpp
@@ -0,0 +1,244 @@
+// Copyright (c) 2021 Shiyu Liu
+//
+// 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_swap_two_functions.h"
+
+#include "gtest/gtest.h"
+#include "source/fuzz/fuzzer_util.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationSwapTwoFunctionsTest, SimpleTest) {
+  //   float multiplyBy2(in float value) {
+  //     return value*2.0;
+  //   }
+
+  //   float multiplyBy4(in float value) {
+  //     return multiplyBy2(value)*2.0;
+  //   }
+
+  //   float multiplyBy8(in float value) {
+  //     return multiplyBy2(value)*multiplyBy4(value);
+  //   }
+
+  //   layout(location=0) in float value;
+  //   void main() { //4
+  //     multiplyBy2(3.7); //10
+  //     multiplyBy4(3.9); //13
+  //     multiplyBy8(5.0); //16
+  //   }
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %48
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %10 "multiplyBy2(f1;"
+               OpName %9 "value"
+               OpName %13 "multiplyBy4(f1;"
+               OpName %12 "value"
+               OpName %16 "multiplyBy8(f1;"
+               OpName %15 "value"
+               OpName %23 "param"
+               OpName %29 "param"
+               OpName %32 "param"
+               OpName %39 "param"
+               OpName %42 "param"
+               OpName %45 "param"
+               OpName %48 "value"
+               OpDecorate %48 Location 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypePointer Function %6
+          %8 = OpTypeFunction %6 %7
+         %19 = OpConstant %6 2
+         %38 = OpConstant %6 3.70000005
+         %41 = OpConstant %6 3.9000001
+         %44 = OpConstant %6 5
+         %47 = OpTypePointer Input %6
+         %48 = OpVariable %47 Input
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %39 = OpVariable %7 Function
+         %42 = OpVariable %7 Function
+         %45 = OpVariable %7 Function
+               OpStore %39 %38
+         %40 = OpFunctionCall %6 %10 %39
+               OpStore %42 %41
+         %43 = OpFunctionCall %6 %13 %42
+               OpStore %45 %44
+         %46 = OpFunctionCall %6 %16 %45
+               OpReturn
+               OpFunctionEnd
+         %10 = OpFunction %6 None %8
+          %9 = OpFunctionParameter %7
+         %11 = OpLabel
+         %18 = OpLoad %6 %9
+         %20 = OpFMul %6 %18 %19
+               OpReturnValue %20
+               OpFunctionEnd
+         %13 = OpFunction %6 None %8
+         %12 = OpFunctionParameter %7
+         %14 = OpLabel
+         %23 = OpVariable %7 Function
+         %24 = OpLoad %6 %12
+               OpStore %23 %24
+         %25 = OpFunctionCall %6 %10 %23
+         %26 = OpFMul %6 %25 %19
+               OpReturnValue %26
+               OpFunctionEnd
+         %16 = OpFunction %6 None %8
+         %15 = OpFunctionParameter %7
+         %17 = OpLabel
+         %29 = OpVariable %7 Function
+         %32 = OpVariable %7 Function
+         %30 = OpLoad %6 %15
+               OpStore %29 %30
+         %31 = OpFunctionCall %6 %10 %29
+         %33 = OpLoad %6 %15
+               OpStore %32 %33
+         %34 = OpFunctionCall %6 %13 %32
+         %35 = OpFMul %6 %31 %34
+               OpReturnValue %35
+               OpFunctionEnd
+  )";
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  spvtools::ValidatorOptions validator_options;
+
+  // Check context validity.
+  ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
+                                               kConsoleMessageConsumer));
+
+  TransformationContext transformation_context(
+      MakeUnique<FactManager>(context.get()), validator_options);
+
+#ifndef NDEBUG
+  // Function should not swap with itself.
+  ASSERT_DEATH(TransformationSwapTwoFunctions(4, 4).IsApplicable(
+                   context.get(), transformation_context),
+               "The two function ids cannot be the same.");
+#endif
+
+  // Function with id 29 does not exist.
+  ASSERT_FALSE(TransformationSwapTwoFunctions(10, 29).IsApplicable(
+      context.get(), transformation_context));
+
+  // Function with id 30 does not exist.
+  ASSERT_FALSE(TransformationSwapTwoFunctions(30, 13).IsApplicable(
+      context.get(), transformation_context));
+
+  // Both functions with id 5 and 6 do not exist.
+  ASSERT_FALSE(TransformationSwapTwoFunctions(5, 6).IsApplicable(
+      context.get(), transformation_context));
+
+  // Function with result_id 10 and 13 should swap successfully.
+  auto swap_test5 = TransformationSwapTwoFunctions(10, 13);
+  ASSERT_TRUE(swap_test5.IsApplicable(context.get(), transformation_context));
+  ApplyAndCheckFreshIds(swap_test5, context.get(), &transformation_context);
+  ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
+                                               kConsoleMessageConsumer));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %48
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %10 "multiplyBy2(f1;"
+               OpName %9 "value"
+               OpName %13 "multiplyBy4(f1;"
+               OpName %12 "value"
+               OpName %16 "multiplyBy8(f1;"
+               OpName %15 "value"
+               OpName %23 "param"
+               OpName %29 "param"
+               OpName %32 "param"
+               OpName %39 "param"
+               OpName %42 "param"
+               OpName %45 "param"
+               OpName %48 "value"
+               OpDecorate %48 Location 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypePointer Function %6
+          %8 = OpTypeFunction %6 %7
+         %19 = OpConstant %6 2
+         %38 = OpConstant %6 3.70000005
+         %41 = OpConstant %6 3.9000001
+         %44 = OpConstant %6 5
+         %47 = OpTypePointer Input %6
+         %48 = OpVariable %47 Input
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %39 = OpVariable %7 Function
+         %42 = OpVariable %7 Function
+         %45 = OpVariable %7 Function
+               OpStore %39 %38
+         %40 = OpFunctionCall %6 %10 %39
+               OpStore %42 %41
+         %43 = OpFunctionCall %6 %13 %42
+               OpStore %45 %44
+         %46 = OpFunctionCall %6 %16 %45
+               OpReturn
+               OpFunctionEnd
+         %13 = OpFunction %6 None %8
+         %12 = OpFunctionParameter %7
+         %14 = OpLabel
+         %23 = OpVariable %7 Function
+         %24 = OpLoad %6 %12
+               OpStore %23 %24
+         %25 = OpFunctionCall %6 %10 %23
+         %26 = OpFMul %6 %25 %19
+               OpReturnValue %26
+               OpFunctionEnd
+         %10 = OpFunction %6 None %8
+         %9 = OpFunctionParameter %7
+         %11 = OpLabel
+         %18 = OpLoad %6 %9
+         %20 = OpFMul %6 %18 %19
+               OpReturnValue %20
+               OpFunctionEnd
+         %16 = OpFunction %6 None %8
+         %15 = OpFunctionParameter %7
+         %17 = OpLabel
+         %29 = OpVariable %7 Function
+         %32 = OpVariable %7 Function
+         %30 = OpLoad %6 %15
+               OpStore %29 %30
+         %31 = OpFunctionCall %6 %10 %29
+         %33 = OpLoad %6 %15
+               OpStore %32 %33
+         %34 = OpFunctionCall %6 %13 %32
+         %35 = OpFMul %6 %31 %34
+               OpReturnValue %35
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/test/operand_capabilities_test.cpp b/test/operand_capabilities_test.cpp
index 0aec791..6f83dfe 100644
--- a/test/operand_capabilities_test.cpp
+++ b/test/operand_capabilities_test.cpp
@@ -199,7 +199,8 @@
                 CASE1(STORAGE_CLASS, StorageClassOutput, Shader),
                 CASE0(STORAGE_CLASS, StorageClassWorkgroup),
                 CASE0(STORAGE_CLASS, StorageClassCrossWorkgroup),
-                CASE1(STORAGE_CLASS, StorageClassPrivate, Shader),
+                CASE2(STORAGE_CLASS, StorageClassPrivate, Shader,
+                      VectorComputeINTEL),
                 CASE0(STORAGE_CLASS, StorageClassFunction),
                 CASE1(STORAGE_CLASS, StorageClassGeneric,
                       GenericPointer),  // Bug 14287
diff --git a/test/opt/CMakeLists.txt b/test/opt/CMakeLists.txt
index 79cb3fc..f65d2ff 100644
--- a/test/opt/CMakeLists.txt
+++ b/test/opt/CMakeLists.txt
@@ -57,6 +57,7 @@
        inst_debug_printf_test.cpp
        instruction_list_test.cpp
        instruction_test.cpp
+       interp_fixup_test.cpp
        ir_builder.cpp
        ir_context_test.cpp
        ir_loader_test.cpp
diff --git a/test/opt/aggressive_dead_code_elim_test.cpp b/test/opt/aggressive_dead_code_elim_test.cpp
index 972e6e5..5b4291d 100644
--- a/test/opt/aggressive_dead_code_elim_test.cpp
+++ b/test/opt/aggressive_dead_code_elim_test.cpp
@@ -3948,12 +3948,15 @@
 OpLoopMerge %14 %15 None
 OpBranch %16
 %16 = OpLabel
-OpSwitch %13 %14 0 %17 1 %15
+OpSelectionMerge %18 None
+OpSwitch %13 %18 0 %17 1 %15
 %17 = OpLabel
 OpStore %3 %uint_1
 OpBranch %15
 %15 = OpLabel
 OpBranch %12
+%18 = OpLabel
+OpBranch %14
 %14 = OpLabel
 OpStore %3 %uint_0
 OpReturn
diff --git a/test/opt/block_merge_test.cpp b/test/opt/block_merge_test.cpp
index 7381908..140a5c0 100644
--- a/test/opt/block_merge_test.cpp
+++ b/test/opt/block_merge_test.cpp
@@ -744,6 +744,7 @@
 ; CHECK: OpLoopMerge [[merge:%\w+]] [[cont:%\w+]] None
 ; CHECK-NEXT: OpBranch [[ret:%\w+]]
 ; CHECK: [[ret:%\w+]] = OpLabel
+; CHECK-NEXT: OpSelectionMerge
 ; CHECK-NEXT: OpSwitch
 ; CHECK-DAG: [[cont]] = OpLabel
 ; CHECK-DAG: [[merge]] = OpLabel
@@ -763,6 +764,7 @@
 OpLoopMerge %3 %4 None
 OpBranch %5
 %5 = OpLabel
+OpSelectionMerge %6 None
 OpSwitch %int_0 %6
 %6 = OpLabel
 OpReturn
diff --git a/test/opt/dead_branch_elim_test.cpp b/test/opt/dead_branch_elim_test.cpp
index 41ce31d..f89befb 100644
--- a/test/opt/dead_branch_elim_test.cpp
+++ b/test/opt/dead_branch_elim_test.cpp
@@ -2722,6 +2722,7 @@
 ; CHECK: [[bb1]] = OpLabel
 ; CHECK-NEXT: OpBranch [[bb2:%\w+]]
 ; CHECK: [[bb2]] = OpLabel
+; CHECK-NEXT: OpSelectionMerge
 ; CHECK-NEXT: OpSwitch {{%\w+}} [[bb3:%\w+]] 0 [[loop_merge]] 1 [[bb3:%\w+]]
 ; CHECK: [[bb3]] = OpLabel
 ; CHECK-NEXT: OpBranch [[sel_merge:%\w+]]
@@ -2739,6 +2740,7 @@
 OpSelectionMerge %sel_merge None
 OpBranchConditional %true %bb2 %bb4
 %bb2 = OpLabel
+OpSelectionMerge %bb3 None
 OpSwitch %undef_int %bb3 0 %loop_merge 1 %bb3
 %bb3 = OpLabel
 OpBranch %sel_merge
@@ -2782,6 +2784,7 @@
 ; CHECK: [[bb1]] = OpLabel
 ; CHECK-NEXT: OpBranch [[bb2:%\w+]]
 ; CHECK: [[bb2]] = OpLabel
+; CHECK-NEXT: OpSelectionMerge
 ; CHECK-NEXT: OpSwitch {{%\w+}} [[bb3:%\w+]] 0 [[loop_cont]] 1 [[bb3:%\w+]]
 ; CHECK: [[bb3]] = OpLabel
 ; CHECK-NEXT: OpBranch [[sel_merge:%\w+]]
@@ -2799,6 +2802,7 @@
 OpSelectionMerge %sel_merge None
 OpBranchConditional %true %bb2 %bb4
 %bb2 = OpLabel
+OpSelectionMerge %bb3 None
 OpSwitch %undef_int %bb3 0 %cont 1 %bb3
 %bb3 = OpLabel
 OpBranch %sel_merge
diff --git a/test/opt/eliminate_dead_functions_test.cpp b/test/opt/eliminate_dead_functions_test.cpp
index 96ecdc6..96deb2a 100644
--- a/test/opt/eliminate_dead_functions_test.cpp
+++ b/test/opt/eliminate_dead_functions_test.cpp
@@ -439,6 +439,84 @@
   SinglePassRunAndMatch<EliminateDeadFunctionsPass>(text, true);
 }
 
+TEST_F(EliminateDeadFunctionsBasicTest, NonSemanticInfoRemoveDebugPrintf) {
+  const std::string text = R"(
+; CHECK-NOT: %foo_ = OpFunction %void None % 3
+; CHECK-NOT: % 7 = OpLabel
+; CHECK-NOT: %c = OpVariable %_ptr_Function_v4float Function
+; CHECK-NOT: % 22 = OpAccessChain %_ptr_UniformConstant_13 %samplers %int_0
+; CHECK-NOT: % 23 = OpLoad % 13 % 22
+; CHECK-NOT: % 27 = OpImageSampleExplicitLod %v4float % 23 % 26 Lod %float_0
+; CHECK-NOT: OpStore %c % 27
+; CHECK-NOT: % 31 = OpAccessChain %_ptr_Function_float %c %uint_0
+; CHECK-NOT: % 32 = OpLoad %float %31
+; CHECK-NOT: % 34 = OpExtInst %void %33 1 % 28 % 32
+OpCapability RayTracingKHR
+OpExtension "SPV_KHR_non_semantic_info"
+OpExtension "SPV_KHR_ray_tracing"
+%1 = OpExtInstImport "GLSL.std.450"
+%33 = OpExtInstImport "NonSemantic.DebugPrintf"
+OpMemoryModel Logical GLSL450
+OpEntryPoint ClosestHitNV %main "main" %samplers
+%28 = OpString "%f"
+OpSource GLSL 460
+OpSourceExtension "GL_EXT_debug_printf"
+OpName %main "main"
+OpName %foo_ "foo("
+OpName %c "c"
+OpName %samplers "samplers"
+OpDecorate %samplers DescriptorSet 0
+OpDecorate %samplers Binding 0
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%12 = OpTypeImage %float 3D 0 0 0 1 Unknown
+%13 = OpTypeSampledImage %12
+%uint = OpTypeInt 32 0
+%uint_1 = OpConstant %uint 1
+%_arr_13_uint_1 = OpTypeArray %13 %uint_1
+%_ptr_UniformConstant__arr_13_uint_1 = OpTypePointer UniformConstant %_arr_13_uint_1
+%samplers = OpVariable %_ptr_UniformConstant__arr_13_uint_1 UniformConstant
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13
+%v3float = OpTypeVector %float 3
+%float_0 = OpConstant %float 0
+%26 = OpConstantComposite %v3float %float_0 %float_0 %float_0
+%uint_0 = OpConstant %uint 0
+%_ptr_Function_float = OpTypePointer Function %float
+%main = OpFunction %void None %3
+%5 = OpLabel
+%36 = OpVariable %_ptr_Function_v4float Function
+%38 = OpAccessChain %_ptr_UniformConstant_13 %samplers %int_0
+%39 = OpLoad %13 %38
+%40 = OpImageSampleExplicitLod %v4float %39 %26 Lod %float_0
+OpStore %36 %40
+%41 = OpAccessChain %_ptr_Function_float %36 %uint_0
+%42 = OpLoad %float %41
+%43 = OpExtInst %void %33 1 %28 %42
+OpReturn
+OpFunctionEnd
+%foo_ = OpFunction %void None %3
+%7 = OpLabel
+%c = OpVariable %_ptr_Function_v4float Function
+%22 = OpAccessChain %_ptr_UniformConstant_13 %samplers %int_0
+%23 = OpLoad %13 %22
+%27 = OpImageSampleExplicitLod %v4float %23 %26 Lod %float_0
+OpStore %c %27
+%31 = OpAccessChain %_ptr_Function_float %c %uint_0
+%32 = OpLoad %float %31
+%34 = OpExtInst %void %33 1 %28 %32
+OpReturn
+OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_VULKAN_1_2);
+  SinglePassRunAndMatch<EliminateDeadFunctionsPass>(text, true);
+}
+
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools
diff --git a/test/opt/fix_storage_class_test.cpp b/test/opt/fix_storage_class_test.cpp
index 4c8504a..1c0101a 100644
--- a/test/opt/fix_storage_class_test.cpp
+++ b/test/opt/fix_storage_class_test.cpp
@@ -528,6 +528,48 @@
   SinglePassRunAndMatch<FixStorageClass>(text, false);
 }
 
+TEST_F(FixStorageClassTest, AllowImageFormatMismatch) {
+  const std::string text = R"(OpCapability Shader
+OpCapability SampledBuffer
+OpCapability ImageBuffer
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpSource HLSL 600
+OpName %type_buffer_image "type.buffer.image"
+OpName %Buf "Buf"
+OpName %main "main"
+OpName %src_main "src.main"
+OpName %bb_entry "bb.entry"
+OpName %type_buffer_image_0 "type.buffer.image"
+OpName %b "b"
+OpDecorate %Buf DescriptorSet 0
+OpDecorate %Buf Binding 0
+%float = OpTypeFloat 32
+%type_buffer_image = OpTypeImage %float Buffer 2 0 0 2 Rgba16f
+%_ptr_UniformConstant_type_buffer_image = OpTypePointer UniformConstant %type_buffer_image
+%void = OpTypeVoid
+%11 = OpTypeFunction %void
+%type_buffer_image_0 = OpTypeImage %float Buffer 2 0 0 2 Rgba32f
+%_ptr_Function_type_buffer_image_0 = OpTypePointer Function %type_buffer_image_0
+%Buf = OpVariable %_ptr_UniformConstant_type_buffer_image UniformConstant
+%main = OpFunction %void None %11
+%13 = OpLabel
+%14 = OpFunctionCall %void %src_main
+OpReturn
+OpFunctionEnd
+%src_main = OpFunction %void None %11
+%bb_entry = OpLabel
+%b = OpVariable %_ptr_Function_type_buffer_image_0 Function
+%15 = OpLoad %type_buffer_image %Buf
+OpStore %b %15
+OpReturn
+OpFunctionEnd
+)";
+
+  SinglePassRunAndCheck<FixStorageClass>(text, text, false, false);
+}
+
 using FixTypeTest = PassTest<::testing::Test>;
 
 TEST_F(FixTypeTest, FixAccessChain) {
diff --git a/test/opt/fold_test.cpp b/test/opt/fold_test.cpp
index bb6098c..8457bbf 100644
--- a/test/opt/fold_test.cpp
+++ b/test/opt/fold_test.cpp
@@ -141,6 +141,7 @@
 %v4int = OpTypeVector %int 4
 %v4float = OpTypeVector %float 4
 %v4double = OpTypeVector %double 4
+%v2uint = OpTypeVector %uint 2
 %v2float = OpTypeVector %float 2
 %v2double = OpTypeVector %double 2
 %v2half = OpTypeVector %half 2
@@ -191,6 +192,7 @@
 %v2int_2_3 = OpConstantComposite %v2int %int_2 %int_3
 %v2int_3_2 = OpConstantComposite %v2int %int_3 %int_2
 %v2int_4_4 = OpConstantComposite %v2int %int_4 %int_4
+%v2int_min_max = OpConstantComposite %v2int %int_min %int_max
 %v2bool_null = OpConstantNull %v2bool
 %v2bool_true_false = OpConstantComposite %v2bool %true %false
 %v2bool_false_true = OpConstantComposite %v2bool %false %true
@@ -258,6 +260,15 @@
 %v4double_1_1_1_0p5 = OpConstantComposite %v4double %double_1 %double_1 %double_1 %double_0p5
 %v4double_null = OpConstantNull %v4double
 %v4float_n1_2_1_3 = OpConstantComposite %v4float %float_n1 %float_2 %float_1 %float_3
+%uint_0x3f800000 = OpConstant %uint 0x3f800000
+%uint_0xbf800000 = OpConstant %uint 0xbf800000
+%v2uint_0x3f800000_0xbf800000 = OpConstantComposite %v2uint %uint_0x3f800000 %uint_0xbf800000
+%long_0xbf8000003f800000 = OpConstant %long 0xbf8000003f800000
+%int_0x3FF00000 = OpConstant %int 0x3FF00000
+%int_0x00000000 = OpConstant %int 0x00000000
+%int_0xC05FD666 = OpConstant %int 0xC05FD666
+%int_0x66666666 = OpConstant %int 0x66666666
+%v4int_0x3FF00000_0x00000000_0xC05FD666_0x66666666 = OpConstantComposite %v4int %int_0x00000000 %int_0x3FF00000 %int_0x66666666 %int_0xC05FD666
 )";
 
   return header;
@@ -708,7 +719,31 @@
           "%2 = OpExtInst %uint %1 UClamp %uint_2 %undef %uint_1\n" +
           "OpReturn\n" +
           "OpFunctionEnd",
-      2, 1)
+      2, 1),
+    // Test case 46: Bit-cast int 0 to unsigned int
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpBitcast %uint %int_0\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 0),
+    // Test case 47: Bit-cast int -24 to unsigned int
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpBitcast %uint %int_n24\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, static_cast<uint32_t>(-24)),
+    // Test case 48: Bit-cast float 1.0f to unsigned int
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpBitcast %uint %float_1\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, static_cast<uint32_t>(0x3f800000))
 ));
 // clang-format on
 
@@ -790,10 +825,72 @@
           "%2 = OpVectorShuffle %v2int %v2int_null %v2int_2_3 0 4294967295 \n" +
           "OpReturn\n" +
           "OpFunctionEnd",
-      2, {0,0})
+      2, {0,0}),
+    // Test case 4: fold bit-cast int -24 to unsigned int
+    InstructionFoldingCase<std::vector<uint32_t>>(
+      Header() + "%main = OpFunction %void None %void_func\n" +
+          "%main_lab = OpLabel\n" +
+          "%n = OpVariable %_ptr_int Function\n" +
+          "%load = OpLoad %int %n\n" +
+          "%2 = OpBitcast %v2uint %v2int_min_max\n" +
+          "OpReturn\n" +
+          "OpFunctionEnd",
+      2, {2147483648, 2147483647})
 ));
 // clang-format on
 
+using DoubleVectorInstructionFoldingTest =
+    ::testing::TestWithParam<InstructionFoldingCase<std::vector<double>>>;
+
+TEST_P(DoubleVectorInstructionFoldingTest, Case) {
+  const auto& tc = GetParam();
+
+  // Build module.
+  std::unique_ptr<IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, tc.test_body,
+                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  ASSERT_NE(nullptr, context);
+
+  // Fold the instruction to test.
+  analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
+  Instruction* inst = def_use_mgr->GetDef(tc.id_to_fold);
+  bool succeeded = context->get_instruction_folder().FoldInstruction(inst);
+
+  // Make sure the instruction folded as expected.
+  EXPECT_TRUE(succeeded);
+  if (succeeded && inst != nullptr) {
+    EXPECT_EQ(inst->opcode(), 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]->GetDouble());
+      }
+    }
+  }
+}
+
+// clang-format off
+INSTANTIATE_TEST_SUITE_P(TestCase, DoubleVectorInstructionFoldingTest,
+::testing::Values(
+   // Test case 0: bit-cast int {0x3FF00000,0x00000000,0xC05FD666,0x66666666}
+   //              to double vector
+   InstructionFoldingCase<std::vector<double>>(
+       Header() + "%main = OpFunction %void None %void_func\n" +
+           "%main_lab = OpLabel\n" +
+           "%2 = OpBitcast %v2double %v4int_0x3FF00000_0x00000000_0xC05FD666_0x66666666\n" +
+           "OpReturn\n" +
+           "OpFunctionEnd",
+       2, {1.0,-127.35})
+));
+
 using FloatVectorInstructionFoldingTest =
     ::testing::TestWithParam<InstructionFoldingCase<std::vector<float>>>;
 
@@ -843,7 +940,24 @@
            "%2 = OpExtInst %v2float %1 FMix %v2float_2_3 %v2float_0_0 %v2float_0p2_0p5\n" +
            "OpReturn\n" +
            "OpFunctionEnd",
-       2, {1.6f,1.5f})
+       2, {1.6f,1.5f}),
+   // Test case 1: bit-cast unsigned int vector {0x3f800000, 0xbf800000} to
+   //              float vector
+   InstructionFoldingCase<std::vector<float>>(
+       Header() + "%main = OpFunction %void None %void_func\n" +
+           "%main_lab = OpLabel\n" +
+           "%2 = OpBitcast %v2float %v2uint_0x3f800000_0xbf800000\n" +
+           "OpReturn\n" +
+           "OpFunctionEnd",
+       2, {1.0f,-1.0f}),
+   // Test case 2: bit-cast long int 0xbf8000003f800000 to float vector
+   InstructionFoldingCase<std::vector<float>>(
+       Header() + "%main = OpFunction %void None %void_func\n" +
+           "%main_lab = OpLabel\n" +
+           "%2 = OpBitcast %v2float %long_0xbf8000003f800000\n" +
+           "OpReturn\n" +
+           "OpFunctionEnd",
+       2, {1.0f,-1.0f})
 ));
 // clang-format on
 using BooleanInstructionFoldingTest =
diff --git a/test/opt/graphics_robust_access_test.cpp b/test/opt/graphics_robust_access_test.cpp
index 4b2cd44..3c23347 100644
--- a/test/opt/graphics_robust_access_test.cpp
+++ b/test/opt/graphics_robust_access_test.cpp
@@ -1548,6 +1548,105 @@
                                                   true);
 }
 
+TEST_F(GraphicsRobustAccessTest, ReplaceIndexReportsChanged) {
+  // A ClusterFuzz generated shader that triggered a
+  // "Binary size unexpectedly changed despite the optimizer saying there was no
+  // change" assertion.
+  // See https://github.com/KhronosGroup/SPIRV-Tools/issues/4166.
+  std::string shader = R"(
+; SPIR-V
+; Version: 1.0
+; Generator: Google Shaderc over Glslang; 245
+; Bound: 41
+; Schema: 0
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "else" %gl_GlobalInvocationID
+               OpExecutionMode %main LocalSize 1 1 3338665985
+               OpSource GLSL 450
+               OpSourceExtension "GL_GOOGLE_cpp_style_line_directive"
+               OpSourceExtension "GL_GOOGLE_include_directive"
+               OpName %main "main"
+               OpName %index "index"
+               OpName %gl_GlobalInvocationID "gl_GlobalInvocationID"
+               OpName %S "S"
+               OpMemberName %_struct_24 0 ""
+               OpMemberName %_struct_24 1 ""
+               OpName %Dst "Dst"
+               OpMemberName %Dst 0 "s"
+               OpName %dst "dst"
+               OpName %Src "Src"
+               OpMemberName %Src 0 "s"
+               OpName %src "src"
+               OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId
+               OpMemberDecorate %_struct_24 0 Offset 64
+               OpMemberDecorate %_struct_24 1 Offset 8
+               OpDecorate %_arr__struct_24_uint_1 ArrayStride 16
+               OpMemberDecorate %Dst 0 Offset 0
+               OpDecorate %Dst BufferBlock
+               OpDecorate %dst DescriptorSet 0
+               OpDecorate %dst Binding 1
+               OpDecorate %_arr__struct_24_uint_1_0 ArrayStride 16
+               OpMemberDecorate %Src 0 Offset 0
+               OpDecorate %Src Block
+               OpDecorate %src DescriptorSet 0
+               OpDecorate %src Binding 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+%_ptr_Function_uint = OpTypePointer Function %uint
+     %v3uint = OpTypeVector %uint 3
+%_ptr_Input_v3uint = OpTypePointer Input %v3uint
+%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input
+  %uint_4864 = OpConstant %uint 4864
+%_ptr_Input_uint = OpTypePointer Input %uint
+     %uint_1 = OpConstant %uint 1
+       %bool = OpTypeBool
+     %v2uint = OpTypeVector %uint 2
+ %_struct_24 = OpTypeStruct %_ptr_Input_uint %v2uint
+%_arr__struct_24_uint_1 = OpTypeArray %_struct_24 %uint_1
+        %Dst = OpTypeStruct %_arr__struct_24_uint_1
+%_ptr_Uniform_Dst = OpTypePointer Uniform %Dst
+        %dst = OpVariable %_ptr_Uniform_Dst Uniform
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+%_arr__struct_24_uint_1_0 = OpTypeArray %_struct_24 %uint_1
+        %Src = OpTypeStruct %_arr__struct_24_uint_1_0
+%_ptr_Uniform_Src = OpTypePointer Uniform %Src
+        %src = OpVariable %_ptr_Uniform_Src Uniform
+%_ptr_Uniform__struct_24 = OpTypePointer Uniform %_struct_24
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+      %index = OpVariable %_ptr_Function_uint Function
+         %14 = OpAccessChain %_ptr_Input_uint %gl_GlobalInvocationID %uint_4864
+         %15 = OpLoad %uint %14
+               OpStore %index %15
+         %16 = OpLoad %uint %index
+          %S = OpUGreaterThanEqual %bool %16 %uint_1
+               OpSelectionMerge %21 None
+               OpBranchConditional %S %20 %21
+         %20 = OpLabel
+               OpReturn
+         %21 = OpLabel
+         %31 = OpLoad %uint %index
+         %36 = OpLoad %uint %index
+         %38 = OpAccessChain %_ptr_Uniform__struct_24 %src %int_0 %36
+         %39 = OpLoad %_struct_24 %38
+         %40 = OpAccessChain %_ptr_Uniform__struct_24 %dst %int_0 %31
+               OpStore %40 %39
+               OpReturn
+               OpFunctionEnd
+)";
+
+  std::vector<uint32_t> optimized_bin;
+  auto status = spvtools::opt::Pass::Status::Failure;
+  std::tie(optimized_bin, status) =
+      SinglePassRunToBinary<GraphicsRobustAccessPass>(shader, false);
+  // Check whether the pass returns the correct modification indication.
+  EXPECT_EQ(status, spvtools::opt::Pass::Status::SuccessWithChange);
+}
+
 // TODO(dneto): Test access chain index wider than 64 bits?
 // TODO(dneto): Test struct access chain index wider than 64 bits?
 // TODO(dneto): OpImageTexelPointer
diff --git a/test/opt/inst_bindless_check_test.cpp b/test/opt/inst_bindless_check_test.cpp
index f189962..1a42329 100644
--- a/test/opt/inst_bindless_check_test.cpp
+++ b/test/opt/inst_bindless_check_test.cpp
@@ -7308,15 +7308,15 @@
  ;CHECK: %_ptr_StorageBuffer__struct_128 = OpTypePointer StorageBuffer %_struct_128
  ;CHECK:         %130 = OpVariable %_ptr_StorageBuffer__struct_128 StorageBuffer
  ;CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
- ;CHECK:      %uint_3 = OpConstant %uint 3
+ ;CHECK:     %uint_4 = OpConstant %uint 4
  ;CHECK:         %148 = OpTypeFunction %void %uint %uint %uint %uint %uint
  ;CHECK: %_struct_155 = OpTypeStruct %uint %_runtimearr_uint
  ;CHECK: %_ptr_StorageBuffer__struct_155 = OpTypePointer StorageBuffer %_struct_155
  ;CHECK:        %157 = OpVariable %_ptr_StorageBuffer__struct_155 StorageBuffer
  ;CHECK:    %uint_11 = OpConstant %uint 11
- ;CHECK:      %uint_4 = OpConstant %uint 4
  ;CHECK:    %uint_23 = OpConstant %uint 23
  ;CHECK:     %uint_2 = OpConstant %uint 2
+ ;CHECK:      %uint_3 = OpConstant %uint 3
  ;CHECK:%_ptr_Input_v4float = OpTypePointer Input %v4float
  ;CHECK:%gl_FragCoord = OpVariable %_ptr_Input_v4float Input
  ;CHECK:     %v4uint = OpTypeVector %uint 4
@@ -7352,7 +7352,7 @@
  ;CHECK:        %146 = OpLoad %v2float %86
  ;CHECK:               OpBranch %143
  ;CHECK:        %145 = OpLabel
- ;CHECK:        %201 = OpFunctionCall %void %147 %uint_71 %uint_3 %uint_0 %119 %140
+ ;CHECK:        %201 = OpFunctionCall %void %147 %uint_71 %uint_4 %uint_0 %119 %140
  ;CHECK:               OpBranch %143
  ;CHECK:        %143 = OpLabel
  ;CHECK:        %203 = OpPhi %v2float %146 %144 %202 %145
@@ -7369,7 +7369,7 @@
  ;CHECK:        %209 = OpLoad %v2float %89
  ;CHECK:               OpBranch %206
  ;CHECK:        %208 = OpLabel
- ;CHECK:        %211 = OpFunctionCall %void %147 %uint_75 %uint_3 %uint_0 %204 %140
+ ;CHECK:        %211 = OpFunctionCall %void %147 %uint_75 %uint_4 %uint_0 %204 %140
  ;CHECK:               OpBranch %206
  ;CHECK:        %206 = OpLabel
  ;CHECK:        %212 = OpPhi %v2float %209 %207 %202 %208
@@ -7409,49 +7409,49 @@
  ;CHECK:        %153 = OpFunctionParameter %uint
  ;CHECK:        %154 = OpLabel
  ;CHECK:        %158 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_0
- ;CHECK:        %161 = OpAtomicIAdd %uint %158 %uint_4 %uint_0 %uint_11
- ;CHECK:        %162 = OpIAdd %uint %161 %uint_11
- ;CHECK:        %163 = OpArrayLength %uint %157 1
- ;CHECK:        %164 = OpULessThanEqual %bool %162 %163
- ;CHECK:               OpSelectionMerge %165 None
- ;CHECK:               OpBranchConditional %164 %166 %165
- ;CHECK:        %166 = OpLabel
- ;CHECK:        %167 = OpIAdd %uint %161 %uint_0
- ;CHECK:        %168 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %167
- ;CHECK:               OpStore %168 %uint_11
- ;CHECK:        %170 = OpIAdd %uint %161 %uint_1
- ;CHECK:        %171 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %170
- ;CHECK:               OpStore %171 %uint_23
- ;CHECK:        %173 = OpIAdd %uint %161 %uint_2
- ;CHECK:        %174 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %173
- ;CHECK:               OpStore %174 %149
- ;CHECK:        %175 = OpIAdd %uint %161 %uint_3
+ ;CHECK:        %160 = OpAtomicIAdd %uint %158 %uint_4 %uint_0 %uint_11
+ ;CHECK:        %161 = OpIAdd %uint %160 %uint_11
+ ;CHECK:        %162 = OpArrayLength %uint %157 1
+ ;CHECK:        %163 = OpULessThanEqual %bool %161 %162
+ ;CHECK:               OpSelectionMerge %164 None
+ ;CHECK:               OpBranchConditional %163 %165 %164
+ ;CHECK:        %165 = OpLabel
+ ;CHECK:        %166 = OpIAdd %uint %160 %uint_0
+ ;CHECK:        %167 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %166
+ ;CHECK:               OpStore %167 %uint_11
+ ;CHECK:        %169 = OpIAdd %uint %160 %uint_1
+ ;CHECK:        %170 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %169
+ ;CHECK:               OpStore %170 %uint_23
+ ;CHECK:        %172 = OpIAdd %uint %160 %uint_2
+ ;CHECK:        %173 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %172
+ ;CHECK:               OpStore %173 %149
+ ;CHECK:        %175 = OpIAdd %uint %160 %uint_3
  ;CHECK:        %176 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %175
  ;CHECK:               OpStore %176 %uint_4
  ;CHECK:        %179 = OpLoad %v4float %gl_FragCoord
  ;CHECK:        %181 = OpBitcast %v4uint %179
  ;CHECK:        %182 = OpCompositeExtract %uint %181 0
- ;CHECK:        %183 = OpIAdd %uint %161 %uint_4
+ ;CHECK:        %183 = OpIAdd %uint %160 %uint_4
  ;CHECK:        %184 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %183
  ;CHECK:               OpStore %184 %182
  ;CHECK:        %185 = OpCompositeExtract %uint %181 1
- ;CHECK:        %187 = OpIAdd %uint %161 %uint_5
+ ;CHECK:        %187 = OpIAdd %uint %160 %uint_5
  ;CHECK:        %188 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %187
  ;CHECK:               OpStore %188 %185
- ;CHECK:        %189 = OpIAdd %uint %161 %uint_7
+ ;CHECK:        %189 = OpIAdd %uint %160 %uint_7
  ;CHECK:        %190 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %189
  ;CHECK:               OpStore %190 %150
- ;CHECK:        %192 = OpIAdd %uint %161 %uint_8
+ ;CHECK:        %192 = OpIAdd %uint %160 %uint_8
  ;CHECK:        %193 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %192
  ;CHECK:               OpStore %193 %151
- ;CHECK:        %195 = OpIAdd %uint %161 %uint_9
+ ;CHECK:        %195 = OpIAdd %uint %160 %uint_9
  ;CHECK:        %196 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %195
  ;CHECK:               OpStore %196 %152
- ;CHECK:        %198 = OpIAdd %uint %161 %uint_10
+ ;CHECK:        %198 = OpIAdd %uint %160 %uint_10
  ;CHECK:        %199 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %198
  ;CHECK:               OpStore %199 %153
- ;CHECK:               OpBranch %165
- ;CHECK:        %165 = OpLabel
+ ;CHECK:               OpBranch %164
+ ;CHECK:        %164 = OpLabel
  ;CHECK:               OpReturn
  ;CHECK:               OpFunctionEnd
  )";
@@ -7596,14 +7596,14 @@
 ;CHECK:        %113 = OpVariable %_ptr_StorageBuffer__struct_111 StorageBuffer
 ;CHECK:%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
 ;CHECK:       %bool = OpTypeBool
-;CHECK:     %uint_3 = OpConstant %uint 3
+;CHECK:     %uint_4 = OpConstant %uint 4
 ;CHECK:        %132 = OpTypeFunction %void %uint %uint %uint %uint %uint
 ;CHECK:%_struct_139 = OpTypeStruct %uint %_runtimearr_uint
 ;CHECK:%_ptr_StorageBuffer__struct_139 = OpTypePointer StorageBuffer %_struct_139
 ;CHECK:        %141 = OpVariable %_ptr_StorageBuffer__struct_139 StorageBuffer
 ;CHECK:    %uint_11 = OpConstant %uint 11
-;CHECK:     %uint_4 = OpConstant %uint 4
 ;CHECK:    %uint_23 = OpConstant %uint 23
+;CHECK:     %uint_3 = OpConstant %uint 3
 ;CHECK:%_ptr_Input_v4float = OpTypePointer Input %v4float
 ;CHECK:%gl_FragCoord = OpVariable %_ptr_Input_v4float Input
 ;CHECK:     %v4uint = OpTypeVector %uint 4
@@ -7637,7 +7637,7 @@
 ;CHECK:        %130 = OpLoad %v2float %81
 ;CHECK:               OpBranch %127
 ;CHECK:        %129 = OpLabel
-;CHECK:        %184 = OpFunctionCall %void %131 %uint_78 %uint_3 %uint_0 %101 %123
+;CHECK:        %184 = OpFunctionCall %void %131 %uint_78 %uint_4 %uint_0 %101 %123
 ;CHECK:               OpBranch %127
 ;CHECK:        %127 = OpLabel
 ;CHECK:        %186 = OpPhi %v2float %130 %128 %185 %129
@@ -7674,49 +7674,49 @@
 ;CHECK:        %137 = OpFunctionParameter %uint
 ;CHECK:        %138 = OpLabel
 ;CHECK:        %142 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_0
-;CHECK:        %145 = OpAtomicIAdd %uint %142 %uint_4 %uint_0 %uint_11
-;CHECK:        %146 = OpIAdd %uint %145 %uint_11
-;CHECK:        %147 = OpArrayLength %uint %141 1
-;CHECK:        %148 = OpULessThanEqual %bool %146 %147
-;CHECK:               OpSelectionMerge %149 None
-;CHECK:               OpBranchConditional %148 %150 %149
-;CHECK:        %150 = OpLabel
-;CHECK:        %151 = OpIAdd %uint %145 %uint_0
-;CHECK:        %152 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %151
-;CHECK:               OpStore %152 %uint_11
-;CHECK:        %154 = OpIAdd %uint %145 %uint_1
-;CHECK:        %155 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %154
-;CHECK:               OpStore %155 %uint_23
-;CHECK:        %156 = OpIAdd %uint %145 %uint_2
-;CHECK:        %157 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %156
-;CHECK:               OpStore %157 %133
-;CHECK:        %158 = OpIAdd %uint %145 %uint_3
+;CHECK:        %144 = OpAtomicIAdd %uint %142 %uint_4 %uint_0 %uint_11
+;CHECK:        %145 = OpIAdd %uint %144 %uint_11
+;CHECK:        %146 = OpArrayLength %uint %141 1
+;CHECK:        %147 = OpULessThanEqual %bool %145 %146
+;CHECK:               OpSelectionMerge %148 None
+;CHECK:               OpBranchConditional %147 %149 %148
+;CHECK:        %149 = OpLabel
+;CHECK:        %150 = OpIAdd %uint %144 %uint_0
+;CHECK:        %151 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %150
+;CHECK:               OpStore %151 %uint_11
+;CHECK:        %153 = OpIAdd %uint %144 %uint_1
+;CHECK:        %154 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %153
+;CHECK:               OpStore %154 %uint_23
+;CHECK:        %155 = OpIAdd %uint %144 %uint_2
+;CHECK:        %156 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %155
+;CHECK:               OpStore %156 %133
+;CHECK:        %158 = OpIAdd %uint %144 %uint_3
 ;CHECK:        %159 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %158
 ;CHECK:               OpStore %159 %uint_4
 ;CHECK:        %162 = OpLoad %v4float %gl_FragCoord
 ;CHECK:        %164 = OpBitcast %v4uint %162
 ;CHECK:        %165 = OpCompositeExtract %uint %164 0
-;CHECK:        %166 = OpIAdd %uint %145 %uint_4
+;CHECK:        %166 = OpIAdd %uint %144 %uint_4
 ;CHECK:        %167 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %166
 ;CHECK:               OpStore %167 %165
 ;CHECK:        %168 = OpCompositeExtract %uint %164 1
-;CHECK:        %170 = OpIAdd %uint %145 %uint_5
+;CHECK:        %170 = OpIAdd %uint %144 %uint_5
 ;CHECK:        %171 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %170
 ;CHECK:               OpStore %171 %168
-;CHECK:        %172 = OpIAdd %uint %145 %uint_7
+;CHECK:        %172 = OpIAdd %uint %144 %uint_7
 ;CHECK:        %173 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %172
 ;CHECK:               OpStore %173 %134
-;CHECK:        %175 = OpIAdd %uint %145 %uint_8
+;CHECK:        %175 = OpIAdd %uint %144 %uint_8
 ;CHECK:        %176 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %175
 ;CHECK:               OpStore %176 %135
-;CHECK:        %178 = OpIAdd %uint %145 %uint_9
+;CHECK:        %178 = OpIAdd %uint %144 %uint_9
 ;CHECK:        %179 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %178
 ;CHECK:               OpStore %179 %136
-;CHECK:        %181 = OpIAdd %uint %145 %uint_10
+;CHECK:        %181 = OpIAdd %uint %144 %uint_10
 ;CHECK:        %182 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %181
 ;CHECK:               OpStore %182 %137
-;CHECK:               OpBranch %149
-;CHECK:        %149 = OpLabel
+;CHECK:               OpBranch %148
+;CHECK:        %148 = OpLabel
 ;CHECK:               OpReturn
 ;CHECK:               OpFunctionEnd
  )";
@@ -7743,11 +7743,11 @@
                OpExecutionMode %MainPs OriginUpperLeft
                OpSource HLSL 500
                OpName %MainPs "MainPs"
-               OpName %PerBatchEnvMapConstantBuffer_t
-"PerBatchEnvMapConstantBuffer_t" OpMemberName %PerBatchEnvMapConstantBuffer_t 0
-"g_matEnvMapWorldToLocal" OpMemberName %PerBatchEnvMapConstantBuffer_t 1
-"g_vEnvironmentMapBoxMins" OpMemberName %PerBatchEnvMapConstantBuffer_t 2
-"g_TexOff" OpName %_BindlessFastEnvMapCB_PS_t "_BindlessFastEnvMapCB_PS_t"
+               OpName %PerBatchEnvMapConstantBuffer_t "PerBatchEnvMapConstantBuffer_t"
+               OpMemberName %PerBatchEnvMapConstantBuffer_t 0 "g_matEnvMapWorldToLocal"
+               OpMemberName %PerBatchEnvMapConstantBuffer_t 1 "g_vEnvironmentMapBoxMins"
+               OpMemberName %PerBatchEnvMapConstantBuffer_t 2 "g_TexOff"
+               OpName %_BindlessFastEnvMapCB_PS_t "_BindlessFastEnvMapCB_PS_t"
                OpMemberName %_BindlessFastEnvMapCB_PS_t 0 "g_envMapConstants"
                OpName %_ ""
                OpName %PerViewPushConst_t "PerViewPushConst_t"
@@ -7831,15 +7831,15 @@
 ;CHECK:        %113 = OpVariable %_ptr_StorageBuffer__struct_111 StorageBuffer
 ;CHECK:%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
 ;CHECK:       %bool = OpTypeBool
-;CHECK:     %uint_3 = OpConstant %uint 3
+;CHECK:     %uint_4 = OpConstant %uint 4
 ;CHECK:        %135 = OpTypeFunction %void %uint %uint %uint %uint %uint
 ;CHECK:%_struct_142 = OpTypeStruct %uint %_runtimearr_uint
 ;CHECK:%_ptr_StorageBuffer__struct_142 = OpTypePointer StorageBuffer %_struct_142
 ;CHECK:        %144 = OpVariable %_ptr_StorageBuffer__struct_142 StorageBuffer
 ;CHECK:    %uint_11 = OpConstant %uint 11
-;CHECK:     %uint_4 = OpConstant %uint 4
 ;CHECK:     %uint_1 = OpConstant %uint 1
 ;CHECK:    %uint_23 = OpConstant %uint 23
+;CHECK:     %uint_3 = OpConstant %uint 3
 ;CHECK:%_ptr_Input_v4float = OpTypePointer Input %v4float
 ;CHECK:%gl_FragCoord = OpVariable %_ptr_Input_v4float Input
 ;CHECK:     %v4uint = OpTypeVector %uint 4
@@ -7878,13 +7878,15 @@
 ;CHECK:        %133 = OpLoad %v2float %81
 ;CHECK:               OpBranch %130
 ;CHECK:        %132 = OpLabel
-;CHECK:        %188 = OpFunctionCall %void %134 %uint_78 %uint_3 %uint_0 %101 %126
+;CHECK:        %188 = OpFunctionCall %void %134 %uint_78 %uint_4 %uint_0 %101 %126
 ;CHECK:               OpBranch %130
 ;CHECK:        %130 = OpLabel
 ;CHECK:        %190 = OpPhi %v2float %133 %131 %189 %132
 ;CHECK:         %86 = OpFAdd %v2float %66 %190
-         %87 = OpLoad %46 %g_tColor %88 = OpLoad %50 %g_sAniso %89 =
-               OpSampledImage %54 %87 %88 %91 = OpImageSampleImplicitLod %v4float %89 %86
+         %87 = OpLoad %46 %g_tColor
+         %88 = OpLoad %50 %g_sAniso
+         %89 = OpSampledImage %54 %87 %88
+         %91 = OpImageSampleImplicitLod %v4float %89 %86
                OpStore %_entryPointOutput_vColor %91
 ;CHECK-NOT: %91 = OpImageSampleImplicitLod %v4float %89 %86
 ;CHECK-NOT:       OpStore %_entryPointOutput_vColor %91
@@ -7931,49 +7933,49 @@
 ;CHECK:        %140 = OpFunctionParameter %uint
 ;CHECK:        %141 = OpLabel
 ;CHECK:        %145 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_0
-;CHECK:        %148 = OpAtomicIAdd %uint %145 %uint_4 %uint_0 %uint_11
-;CHECK:        %149 = OpIAdd %uint %148 %uint_11
-;CHECK:        %150 = OpArrayLength %uint %144 1
-;CHECK:        %151 = OpULessThanEqual %bool %149 %150
-;CHECK:               OpSelectionMerge %152 None
-;CHECK:               OpBranchConditional %151 %153 %152
-;CHECK:        %153 = OpLabel
-;CHECK:        %154 = OpIAdd %uint %148 %uint_0
-;CHECK:        %156 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %154
-;CHECK:               OpStore %156 %uint_11
-;CHECK:        %158 = OpIAdd %uint %148 %uint_1
-;CHECK:        %159 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %158
-;CHECK:               OpStore %159 %uint_23
-;CHECK:        %160 = OpIAdd %uint %148 %uint_2
-;CHECK:        %161 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %160
-;CHECK:               OpStore %161 %136
-;CHECK:        %162 = OpIAdd %uint %148 %uint_3
+;CHECK:        %147 = OpAtomicIAdd %uint %145 %uint_4 %uint_0 %uint_11
+;CHECK:        %148 = OpIAdd %uint %147 %uint_11
+;CHECK:        %149 = OpArrayLength %uint %144 1
+;CHECK:        %150 = OpULessThanEqual %bool %148 %149
+;CHECK:               OpSelectionMerge %151 None
+;CHECK:               OpBranchConditional %150 %152 %151
+;CHECK:        %152 = OpLabel
+;CHECK:        %153 = OpIAdd %uint %147 %uint_0
+;CHECK:        %155 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %153
+;CHECK:               OpStore %155 %uint_11
+;CHECK:        %157 = OpIAdd %uint %147 %uint_1
+;CHECK:        %158 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %157
+;CHECK:               OpStore %158 %uint_23
+;CHECK:        %159 = OpIAdd %uint %147 %uint_2
+;CHECK:        %160 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %159
+;CHECK:               OpStore %160 %136
+;CHECK:        %162 = OpIAdd %uint %147 %uint_3
 ;CHECK:        %163 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %162
 ;CHECK:               OpStore %163 %uint_4
 ;CHECK:        %166 = OpLoad %v4float %gl_FragCoord
 ;CHECK:        %168 = OpBitcast %v4uint %166
 ;CHECK:        %169 = OpCompositeExtract %uint %168 0
-;CHECK:        %170 = OpIAdd %uint %148 %uint_4
+;CHECK:        %170 = OpIAdd %uint %147 %uint_4
 ;CHECK:        %171 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %170
 ;CHECK:               OpStore %171 %169
 ;CHECK:        %172 = OpCompositeExtract %uint %168 1
-;CHECK:        %174 = OpIAdd %uint %148 %uint_5
+;CHECK:        %174 = OpIAdd %uint %147 %uint_5
 ;CHECK:        %175 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %174
 ;CHECK:               OpStore %175 %172
-;CHECK:        %176 = OpIAdd %uint %148 %uint_7
+;CHECK:        %176 = OpIAdd %uint %147 %uint_7
 ;CHECK:        %177 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %176
 ;CHECK:               OpStore %177 %137
-;CHECK:        %179 = OpIAdd %uint %148 %uint_8
+;CHECK:        %179 = OpIAdd %uint %147 %uint_8
 ;CHECK:        %180 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %179
 ;CHECK:               OpStore %180 %138
-;CHECK:        %182 = OpIAdd %uint %148 %uint_9
+;CHECK:        %182 = OpIAdd %uint %147 %uint_9
 ;CHECK:        %183 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %182
 ;CHECK:               OpStore %183 %139
-;CHECK:        %185 = OpIAdd %uint %148 %uint_10
+;CHECK:        %185 = OpIAdd %uint %147 %uint_10
 ;CHECK:        %186 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %185
 ;CHECK:               OpStore %186 %140
-;CHECK:               OpBranch %152
-;CHECK:        %152 = OpLabel
+;CHECK:               OpBranch %151
+;CHECK:        %151 = OpLabel
 ;CHECK:               OpReturn
 ;CHECK:               OpFunctionEnd
  )";
@@ -8344,15 +8346,15 @@
 ;CHECK:         %69 = OpVariable %_ptr_StorageBuffer__struct_67 StorageBuffer
 ;CHECK:%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
 ;CHECK:       %bool = OpTypeBool
-;CHECK:     %uint_3 = OpConstant %uint 3
+;CHECK:     %uint_4 = OpConstant %uint 4
 ;CHECK:         %88 = OpTypeFunction %void %uint %uint %uint %uint %uint
 ;CHECK: %_struct_95 = OpTypeStruct %uint %_runtimearr_uint
 ;CHECK:%_ptr_StorageBuffer__struct_95 = OpTypePointer StorageBuffer %_struct_95
 ;CHECK:         %97 = OpVariable %_ptr_StorageBuffer__struct_95 StorageBuffer
 ;CHECK:    %uint_11 = OpConstant %uint 11
-;CHECK:     %uint_4 = OpConstant %uint 4
 ;CHECK:    %uint_23 = OpConstant %uint 23
 ;CHECK:     %uint_2 = OpConstant %uint 2
+;CHECK:     %uint_3 = OpConstant %uint 3
 ;CHECK:%_ptr_Input_v4float = OpTypePointer Input %v4float
 ;CHECK:%gl_FragCoord = OpVariable %_ptr_Input_v4float Input
 ;CHECK:     %v4uint = OpTypeVector %uint 4
@@ -8389,7 +8391,7 @@
 ;CHECK:         %86 = OpLoad %v2float %41
 ;CHECK:               OpBranch %83
 ;CHECK:         %85 = OpLabel
-;CHECK:        %141 = OpFunctionCall %void %87 %uint_81 %uint_3 %uint_0 %58 %79
+;CHECK:        %141 = OpFunctionCall %void %87 %uint_81 %uint_4 %uint_0 %58 %79
 ;CHECK:               OpBranch %83
 ;CHECK:         %83 = OpLabel
 ;CHECK:        %143 = OpPhi %v2float %86 %84 %142 %85
@@ -8424,49 +8426,49 @@
 ;CHECK:         %93 = OpFunctionParameter %uint
 ;CHECK:         %94 = OpLabel
 ;CHECK:         %98 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_0
-;CHECK:        %101 = OpAtomicIAdd %uint %98 %uint_4 %uint_0 %uint_11
-;CHECK:        %102 = OpIAdd %uint %101 %uint_11
-;CHECK:        %103 = OpArrayLength %uint %97 1
-;CHECK:        %104 = OpULessThanEqual %bool %102 %103
-;CHECK:               OpSelectionMerge %105 None
-;CHECK:               OpBranchConditional %104 %106 %105
-;CHECK:        %106 = OpLabel
-;CHECK:        %107 = OpIAdd %uint %101 %uint_0
-;CHECK:        %108 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %107
-;CHECK:               OpStore %108 %uint_11
-;CHECK:        %110 = OpIAdd %uint %101 %uint_1
-;CHECK:        %111 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %110
-;CHECK:               OpStore %111 %uint_23
-;CHECK:        %113 = OpIAdd %uint %101 %uint_2
-;CHECK:        %114 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %113
-;CHECK:               OpStore %114 %89
-;CHECK:        %115 = OpIAdd %uint %101 %uint_3
+;CHECK:        %100 = OpAtomicIAdd %uint %98 %uint_4 %uint_0 %uint_11
+;CHECK:        %101 = OpIAdd %uint %100 %uint_11
+;CHECK:        %102 = OpArrayLength %uint %97 1
+;CHECK:        %103 = OpULessThanEqual %bool %101 %102
+;CHECK:               OpSelectionMerge %104 None
+;CHECK:               OpBranchConditional %103 %105 %104
+;CHECK:        %105 = OpLabel
+;CHECK:        %106 = OpIAdd %uint %100 %uint_0
+;CHECK:        %107 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %106
+;CHECK:               OpStore %107 %uint_11
+;CHECK:        %109 = OpIAdd %uint %100 %uint_1
+;CHECK:        %110 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %109
+;CHECK:               OpStore %110 %uint_23
+;CHECK:        %112 = OpIAdd %uint %100 %uint_2
+;CHECK:        %113 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %112
+;CHECK:               OpStore %113 %89
+;CHECK:        %115 = OpIAdd %uint %100 %uint_3
 ;CHECK:        %116 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %115
 ;CHECK:               OpStore %116 %uint_4
 ;CHECK:        %119 = OpLoad %v4float %gl_FragCoord
 ;CHECK:        %121 = OpBitcast %v4uint %119
 ;CHECK:        %122 = OpCompositeExtract %uint %121 0
-;CHECK:        %123 = OpIAdd %uint %101 %uint_4
+;CHECK:        %123 = OpIAdd %uint %100 %uint_4
 ;CHECK:        %124 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %123
 ;CHECK:               OpStore %124 %122
 ;CHECK:        %125 = OpCompositeExtract %uint %121 1
-;CHECK:        %127 = OpIAdd %uint %101 %uint_5
+;CHECK:        %127 = OpIAdd %uint %100 %uint_5
 ;CHECK:        %128 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %127
 ;CHECK:               OpStore %128 %125
-;CHECK:        %129 = OpIAdd %uint %101 %uint_7
+;CHECK:        %129 = OpIAdd %uint %100 %uint_7
 ;CHECK:        %130 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %129
 ;CHECK:               OpStore %130 %90
-;CHECK:        %132 = OpIAdd %uint %101 %uint_8
+;CHECK:        %132 = OpIAdd %uint %100 %uint_8
 ;CHECK:        %133 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %132
 ;CHECK:               OpStore %133 %91
-;CHECK:        %135 = OpIAdd %uint %101 %uint_9
+;CHECK:        %135 = OpIAdd %uint %100 %uint_9
 ;CHECK:        %136 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %135
 ;CHECK:               OpStore %136 %92
-;CHECK:        %138 = OpIAdd %uint %101 %uint_10
+;CHECK:        %138 = OpIAdd %uint %100 %uint_10
 ;CHECK:        %139 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %138
 ;CHECK:               OpStore %139 %93
-;CHECK:               OpBranch %105
-;CHECK:        %105 = OpLabel
+;CHECK:               OpBranch %104
+;CHECK:        %104 = OpLabel
 ;CHECK:               OpReturn
 ;CHECK:               OpFunctionEnd
  )";
@@ -8557,6 +8559,7 @@
 %_ptr_Input_v4float = OpTypePointer Input %v4float
  %a_position = OpVariable %_ptr_Input_v4float Input
 ;CHECK;     %uint_0 = OpConstant %uint 0
+;CHECK;     %uint_16 = OpConstant %uint 16
 ;CHECK;     %uint_4 = OpConstant %uint 4
 ;CHECK;     %uint_3 = OpConstant %uint 3
 ;CHECK;         %37 = OpTypeFunction %uint %uint %uint %uint
@@ -8583,10 +8586,13 @@
 ;CHECK;    %uint_10 = OpConstant %uint 10
 ;CHECK;    %uint_45 = OpConstant %uint 45
 ;CHECK;        %115 = OpConstantNull %float
-;CHECK;    %uint_27 = OpConstant %uint 27
        %main = OpFunction %void None %3
           %5 = OpLabel
 ;CHECK:         %55 = OpFunctionCall %uint %36 %uint_1 %uint_0 %uint_0
+;CHECK:               OpBranch %26
+;CHECK:         %26 = OpLabel
+;CHECK:               OpBranch %25
+;CHECK:         %25 = OpLabel
          %20 = OpAccessChain %_ptr_Uniform_float %_ %int_0 %int_2 %uint_1
          %21 = OpLoad %float %20
 ;CHECK-NOT:     %21 = OpLoad %float %20
@@ -8602,7 +8608,7 @@
 ;CHECK:         %61 = OpLoad %float %20
 ;CHECK:               OpBranch %58
 ;CHECK:         %60 = OpLabel
-;CHECK:        %114 = OpFunctionCall %void %62 %uint_45 %uint_3 %uint_0 %35 %55
+;CHECK:        %114 = OpFunctionCall %void %62 %uint_45 %uint_4 %uint_0 %35 %55
 ;CHECK:               OpBranch %58
 ;CHECK:         %58 = OpLabel
 ;CHECK:        %116 = OpPhi %float %61 %59 %115 %60
@@ -8790,9 +8796,14 @@
 ;CHECK:     %uint_9 = OpConstant %uint 9
 ;CHECK:    %uint_10 = OpConstant %uint 10
 ;CHECK:    %uint_45 = OpConstant %uint 45
+;CHECK:        %114 = OpConstantNull %float
 %main = OpFunction %void None %3
           %5 = OpLabel
 ;CHECK:         %55 = OpFunctionCall %uint %36 %uint_1 %uint_0 %uint_0
+;CHECK:               OpBranch %26
+;CHECK:         %26 = OpLabel
+;CHECK:               OpBranch %25
+;CHECK:         %25 = OpLabel
          %20 = OpAccessChain %_ptr_Uniform_float %_ %int_0 %int_2 %uint_1
          %21 = OpLoad %float %20
 ;CHECK-NOT:     %21 = OpLoad %float %20
@@ -8808,7 +8819,7 @@
 ;CHECK:         %61 = OpLoad %float %20
 ;CHECK:               OpBranch %58
 ;CHECK:         %60 = OpLabel
-;CHECK:        %113 = OpFunctionCall %void %62 %uint_45 %uint_3 %uint_0 %35 %55
+;CHECK:        %113 = OpFunctionCall %void %62 %uint_45 %uint_4 %uint_0 %35 %55
 ;CHECK:               OpBranch %58
 ;CHECK:         %58 = OpLabel
 ;CHECK:        %115 = OpPhi %float %61 %59 %114 %60
@@ -9032,7 +9043,7 @@
 ;CHECK:         %70 = OpLoad %v2float %25
 ;CHECK:               OpBranch %67
 ;CHECK:         %69 = OpLabel
-;CHECK:        %123 = OpFunctionCall %void %71 %uint_51 %uint_3 %uint_0 %43 %64
+;CHECK:        %123 = OpFunctionCall %void %71 %uint_51 %uint_4 %uint_0 %43 %64
 ;CHECK:               OpBranch %67
 ;CHECK:         %67 = OpLabel
 ;CHECK:        %125 = OpPhi %v2float %70 %68 %124 %69
@@ -9167,7 +9178,7 @@
 ;CHECK:           %uint = OpTypeInt 32 0
 ;CHECK:         %uint_0 = OpConstant %uint 0
 ;CHECK:           %bool = OpTypeBool
-;CHECK:         %uint_3 = OpConstant %uint 3
+;CHECK:         %uint_7 = OpConstant %uint 7
 ;CHECK:             %35 = OpTypeFunction %void %uint %uint %uint %uint %uint
 ;CHECK:    %_runtimearr_uint = OpTypeRuntimeArray %uint
 ;CHECK:     %_struct_43 = OpTypeStruct %uint %_runtimearr_uint
@@ -9179,11 +9190,11 @@
 ;CHECK:         %uint_1 = OpConstant %uint 1
 ;CHECK:        %uint_23 = OpConstant %uint 23
 ;CHECK:         %uint_2 = OpConstant %uint 2
+;CHECK:         %uint_3 = OpConstant %uint 3
 ;CHECK:    %_ptr_Input_v4float = OpTypePointer Input %v4float
 ;CHECK:    %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
 ;CHECK:         %v4uint = OpTypeVector %uint 4
 ;CHECK:         %uint_5 = OpConstant %uint 5
-;CHECK:         %uint_7 = OpConstant %uint 7
 ;CHECK:         %uint_8 = OpConstant %uint 8
 ;CHECK:         %uint_9 = OpConstant %uint 9
 ;CHECK:        %uint_10 = OpConstant %uint 10
@@ -9213,7 +9224,7 @@
 ;CHECK:             %33 = OpImageRead %v4float %32 %17
 ;CHECK:                   OpBranch %29
 ;CHECK:             %31 = OpLabel
-;CHECK:             %92 = OpFunctionCall %void %34 %uint_33 %uint_3 %uint_0 %23 %25
+;CHECK:             %92 = OpFunctionCall %void %34 %uint_33 %uint_7 %uint_0 %23 %25
 ;CHECK:                   OpBranch %29
 ;CHECK:             %29 = OpLabel
 ;CHECK:             %94 = OpPhi %v4float %33 %30 %93 %31
@@ -9244,19 +9255,19 @@
 ;CHECK:             %63 = OpIAdd %uint %50 %uint_2
 ;CHECK:             %64 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %63
 ;CHECK:                   OpStore %64 %36
-;CHECK:             %65 = OpIAdd %uint %50 %uint_3
-;CHECK:             %66 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %65
-;CHECK:                   OpStore %66 %uint_4
-;CHECK:             %69 = OpLoad %v4float %gl_FragCoord
-;CHECK:             %71 = OpBitcast %v4uint %69
-;CHECK:             %72 = OpCompositeExtract %uint %71 0
-;CHECK:             %73 = OpIAdd %uint %50 %uint_4
-;CHECK:             %74 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %73
-;CHECK:                   OpStore %74 %72
-;CHECK:             %75 = OpCompositeExtract %uint %71 1
-;CHECK:             %77 = OpIAdd %uint %50 %uint_5
-;CHECK:             %78 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %77
-;CHECK:                   OpStore %78 %75
+;CHECK:             %66 = OpIAdd %uint %50 %uint_3
+;CHECK:             %67 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %66
+;CHECK:                   OpStore %67 %uint_4
+;CHECK:             %70 = OpLoad %v4float %gl_FragCoord
+;CHECK:             %72 = OpBitcast %v4uint %70
+;CHECK:             %73 = OpCompositeExtract %uint %72 0
+;CHECK:             %74 = OpIAdd %uint %50 %uint_4
+;CHECK:             %75 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %74
+;CHECK:                   OpStore %75 %73
+;CHECK:             %76 = OpCompositeExtract %uint %72 1
+;CHECK:             %78 = OpIAdd %uint %50 %uint_5
+;CHECK:             %79 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %78
+;CHECK:                   OpStore %79 %76
 ;CHECK:             %80 = OpIAdd %uint %50 %uint_7
 ;CHECK:             %81 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %80
 ;CHECK:                   OpStore %81 %37
@@ -9336,7 +9347,7 @@
 ;CHECK:           %uint = OpTypeInt 32 0
 ;CHECK:         %uint_0 = OpConstant %uint 0
 ;CHECK:           %bool = OpTypeBool
-;CHECK:         %uint_3 = OpConstant %uint 3
+;CHECK:         %uint_7 = OpConstant %uint 7
 ;CHECK:             %34 = OpTypeFunction %void %uint %uint %uint %uint %uint
 ;CHECK:    %_runtimearr_uint = OpTypeRuntimeArray %uint
 ;CHECK:     %_struct_42 = OpTypeStruct %uint %_runtimearr_uint
@@ -9348,11 +9359,11 @@
 ;CHECK:         %uint_1 = OpConstant %uint 1
 ;CHECK:        %uint_23 = OpConstant %uint 23
 ;CHECK:         %uint_2 = OpConstant %uint 2
+;CHECK:         %uint_3 = OpConstant %uint 3
 ;CHECK:    %_ptr_Input_v4float = OpTypePointer Input %v4float
 ;CHECK:    %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
 ;CHECK:         %v4uint = OpTypeVector %uint 4
 ;CHECK:         %uint_5 = OpConstant %uint 5
-;CHECK:         %uint_7 = OpConstant %uint 7
 ;CHECK:         %uint_8 = OpConstant %uint 8
 ;CHECK:         %uint_9 = OpConstant %uint 9
 ;CHECK:        %uint_10 = OpConstant %uint 10
@@ -9380,7 +9391,7 @@
 ;CHECK:                   OpImageWrite %32 %14 %18
 ;CHECK:                   OpBranch %29
 ;CHECK:             %31 = OpLabel
-;CHECK:             %91 = OpFunctionCall %void %33 %uint_34 %uint_3 %uint_0 %23 %25
+;CHECK:             %91 = OpFunctionCall %void %33 %uint_34 %uint_7 %uint_0 %23 %25
 ;CHECK:                   OpBranch %29
 ;CHECK:             %29 = OpLabel
                           OpReturn
@@ -9409,19 +9420,19 @@
 ;CHECK:             %62 = OpIAdd %uint %49 %uint_2
 ;CHECK:             %63 = OpAccessChain %_ptr_StorageBuffer_uint %44 %uint_1 %62
 ;CHECK:                   OpStore %63 %35
-;CHECK:             %64 = OpIAdd %uint %49 %uint_3
-;CHECK:             %65 = OpAccessChain %_ptr_StorageBuffer_uint %44 %uint_1 %64
-;CHECK:                   OpStore %65 %uint_4
-;CHECK:             %68 = OpLoad %v4float %gl_FragCoord
-;CHECK:             %70 = OpBitcast %v4uint %68
-;CHECK:             %71 = OpCompositeExtract %uint %70 0
-;CHECK:             %72 = OpIAdd %uint %49 %uint_4
-;CHECK:             %73 = OpAccessChain %_ptr_StorageBuffer_uint %44 %uint_1 %72
-;CHECK:                   OpStore %73 %71
-;CHECK:             %74 = OpCompositeExtract %uint %70 1
-;CHECK:             %76 = OpIAdd %uint %49 %uint_5
-;CHECK:             %77 = OpAccessChain %_ptr_StorageBuffer_uint %44 %uint_1 %76
-;CHECK:                   OpStore %77 %74
+;CHECK:             %65 = OpIAdd %uint %49 %uint_3
+;CHECK:             %66 = OpAccessChain %_ptr_StorageBuffer_uint %44 %uint_1 %65
+;CHECK:                   OpStore %66 %uint_4
+;CHECK:             %69 = OpLoad %v4float %gl_FragCoord
+;CHECK:             %71 = OpBitcast %v4uint %69
+;CHECK:             %72 = OpCompositeExtract %uint %71 0
+;CHECK:             %73 = OpIAdd %uint %49 %uint_4
+;CHECK:             %74 = OpAccessChain %_ptr_StorageBuffer_uint %44 %uint_1 %73
+;CHECK:                   OpStore %74 %72
+;CHECK:             %75 = OpCompositeExtract %uint %71 1
+;CHECK:             %77 = OpIAdd %uint %49 %uint_5
+;CHECK:             %78 = OpAccessChain %_ptr_StorageBuffer_uint %44 %uint_1 %77
+;CHECK:                   OpStore %78 %75
 ;CHECK:             %79 = OpIAdd %uint %49 %uint_7
 ;CHECK:             %80 = OpAccessChain %_ptr_StorageBuffer_uint %44 %uint_1 %79
 ;CHECK:                   OpStore %80 %36
@@ -9500,7 +9511,7 @@
 ;CHECK:           %uint = OpTypeInt 32 0
 ;CHECK:         %uint_0 = OpConstant %uint 0
 ;CHECK:           %bool = OpTypeBool
-;CHECK:         %uint_3 = OpConstant %uint 3
+;CHECK:         %uint_6 = OpConstant %uint 6
 ;CHECK:             %35 = OpTypeFunction %void %uint %uint %uint %uint %uint
 ;CHECK:    %_runtimearr_uint = OpTypeRuntimeArray %uint
 ;CHECK:     %_struct_43 = OpTypeStruct %uint %_runtimearr_uint
@@ -9512,6 +9523,7 @@
 ;CHECK:         %uint_1 = OpConstant %uint 1
 ;CHECK:        %uint_23 = OpConstant %uint 23
 ;CHECK:         %uint_2 = OpConstant %uint 2
+;CHECK:         %uint_3 = OpConstant %uint 3
 ;CHECK:    %_ptr_Input_v4float = OpTypePointer Input %v4float
 ;CHECK:    %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
 ;CHECK:         %v4uint = OpTypeVector %uint 4
@@ -9521,7 +9533,7 @@
 ;CHECK:         %uint_9 = OpConstant %uint 9
 ;CHECK:        %uint_10 = OpConstant %uint 10
 ;CHECK:        %uint_32 = OpConstant %uint 32
-;CHECK:             %93 = OpConstantNull %v4float
+;CHECK:             %94 = OpConstantNull %v4float
                   %main = OpFunction %void None %3
                      %5 = OpLabel
 ;CHECK:                   OpBranch %21
@@ -9546,11 +9558,11 @@
 ;CHECK:             %33 = OpImageFetch %v4float %32 %17
 ;CHECK:                   OpBranch %29
 ;CHECK:             %31 = OpLabel
-;CHECK:             %92 = OpFunctionCall %void %34 %uint_32 %uint_3 %uint_0 %23 %25
+;CHECK:             %93 = OpFunctionCall %void %34 %uint_32 %uint_6 %uint_0 %23 %25
 ;CHECK:                   OpBranch %29
 ;CHECK:             %29 = OpLabel
-;CHECK:             %94 = OpPhi %v4float %33 %30 %93 %31
-;CHECK:                   OpStore %x %94
+;CHECK:             %95 = OpPhi %v4float %33 %30 %94 %31
+;CHECK:                   OpStore %x %95
                           OpReturn
                           OpFunctionEnd
 ;CHECK:             %34 = OpFunction %void None %35
@@ -9577,31 +9589,31 @@
 ;CHECK:             %63 = OpIAdd %uint %50 %uint_2
 ;CHECK:             %64 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %63
 ;CHECK:                   OpStore %64 %36
-;CHECK:             %65 = OpIAdd %uint %50 %uint_3
-;CHECK:             %66 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %65
-;CHECK:                   OpStore %66 %uint_4
-;CHECK:             %69 = OpLoad %v4float %gl_FragCoord
-;CHECK:             %71 = OpBitcast %v4uint %69
-;CHECK:             %72 = OpCompositeExtract %uint %71 0
-;CHECK:             %73 = OpIAdd %uint %50 %uint_4
-;CHECK:             %74 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %73
-;CHECK:                   OpStore %74 %72
-;CHECK:             %75 = OpCompositeExtract %uint %71 1
-;CHECK:             %77 = OpIAdd %uint %50 %uint_5
-;CHECK:             %78 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %77
-;CHECK:                   OpStore %78 %75
-;CHECK:             %80 = OpIAdd %uint %50 %uint_7
-;CHECK:             %81 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %80
-;CHECK:                   OpStore %81 %37
-;CHECK:             %83 = OpIAdd %uint %50 %uint_8
-;CHECK:             %84 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %83
-;CHECK:                   OpStore %84 %38
-;CHECK:             %86 = OpIAdd %uint %50 %uint_9
-;CHECK:             %87 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %86
-;CHECK:                   OpStore %87 %39
-;CHECK:             %89 = OpIAdd %uint %50 %uint_10
-;CHECK:             %90 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %89
-;CHECK:                   OpStore %90 %40
+;CHECK:             %66 = OpIAdd %uint %50 %uint_3
+;CHECK:             %67 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %66
+;CHECK:                   OpStore %67 %uint_4
+;CHECK:             %70 = OpLoad %v4float %gl_FragCoord
+;CHECK:             %72 = OpBitcast %v4uint %70
+;CHECK:             %73 = OpCompositeExtract %uint %72 0
+;CHECK:             %74 = OpIAdd %uint %50 %uint_4
+;CHECK:             %75 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %74
+;CHECK:                   OpStore %75 %73
+;CHECK:             %76 = OpCompositeExtract %uint %72 1
+;CHECK:             %78 = OpIAdd %uint %50 %uint_5
+;CHECK:             %79 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %78
+;CHECK:                   OpStore %79 %76
+;CHECK:             %81 = OpIAdd %uint %50 %uint_7
+;CHECK:             %82 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %81
+;CHECK:                   OpStore %82 %37
+;CHECK:             %84 = OpIAdd %uint %50 %uint_8
+;CHECK:             %85 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %84
+;CHECK:                   OpStore %85 %38
+;CHECK:             %87 = OpIAdd %uint %50 %uint_9
+;CHECK:             %88 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %87
+;CHECK:                   OpStore %88 %39
+;CHECK:             %90 = OpIAdd %uint %50 %uint_10
+;CHECK:             %91 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %90
+;CHECK:                   OpStore %91 %40
 ;CHECK:                   OpBranch %54
 ;CHECK:             %54 = OpLabel
 ;CHECK:                   OpReturn
@@ -9669,7 +9681,7 @@
 ;CHECK:           %uint = OpTypeInt 32 0
 ;CHECK:         %uint_0 = OpConstant %uint 0
 ;CHECK:           %bool = OpTypeBool
-;CHECK:         %uint_3 = OpConstant %uint 3
+;CHECK:         %uint_6 = OpConstant %uint 6
 ;CHECK:             %38 = OpTypeFunction %void %uint %uint %uint %uint %uint
 ;CHECK:    %_runtimearr_uint = OpTypeRuntimeArray %uint
 ;CHECK:     %_struct_46 = OpTypeStruct %uint %_runtimearr_uint
@@ -9681,6 +9693,7 @@
 ;CHECK:         %uint_1 = OpConstant %uint 1
 ;CHECK:        %uint_23 = OpConstant %uint 23
 ;CHECK:         %uint_2 = OpConstant %uint 2
+;CHECK:         %uint_3 = OpConstant %uint 3
 ;CHECK:    %_ptr_Input_v4float = OpTypePointer Input %v4float
 ;CHECK:    %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
 ;CHECK:         %v4uint = OpTypeVector %uint 4
@@ -9690,7 +9703,7 @@
 ;CHECK:         %uint_9 = OpConstant %uint 9
 ;CHECK:        %uint_10 = OpConstant %uint 10
 ;CHECK:        %uint_34 = OpConstant %uint 34
-;CHECK:             %96 = OpConstantNull %v4float
+;CHECK:             %97 = OpConstantNull %v4float
                   %main = OpFunction %void None %3
                      %5 = OpLabel
 ;CHECK:                   OpBranch %23
@@ -9717,11 +9730,11 @@
 ;CHECK:             %36 = OpImageFetch %v4float %35 %18
 ;CHECK:                   OpBranch %31
 ;CHECK:             %33 = OpLabel
-;CHECK:             %95 = OpFunctionCall %void %37 %uint_34 %uint_3 %uint_0 %25 %27
+;CHECK:             %96 = OpFunctionCall %void %37 %uint_34 %uint_6 %uint_0 %25 %27
 ;CHECK:                   OpBranch %31
 ;CHECK:             %31 = OpLabel
-;CHECK:             %97 = OpPhi %v4float %36 %32 %96 %33
-;CHECK:                   OpStore %x %97
+;CHECK:             %98 = OpPhi %v4float %36 %32 %97 %33
+;CHECK:                   OpStore %x %98
                           OpReturn
                           OpFunctionEnd
 ;CHECK:             %37 = OpFunction %void None %38
@@ -9748,31 +9761,31 @@
 ;CHECK:             %66 = OpIAdd %uint %53 %uint_2
 ;CHECK:             %67 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %66
 ;CHECK:                   OpStore %67 %39
-;CHECK:             %68 = OpIAdd %uint %53 %uint_3
-;CHECK:             %69 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %68
-;CHECK:                   OpStore %69 %uint_4
-;CHECK:             %72 = OpLoad %v4float %gl_FragCoord
-;CHECK:             %74 = OpBitcast %v4uint %72
-;CHECK:             %75 = OpCompositeExtract %uint %74 0
-;CHECK:             %76 = OpIAdd %uint %53 %uint_4
-;CHECK:             %77 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %76
-;CHECK:                   OpStore %77 %75
-;CHECK:             %78 = OpCompositeExtract %uint %74 1
-;CHECK:             %80 = OpIAdd %uint %53 %uint_5
-;CHECK:             %81 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %80
-;CHECK:                   OpStore %81 %78
-;CHECK:             %83 = OpIAdd %uint %53 %uint_7
-;CHECK:             %84 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %83
-;CHECK:                   OpStore %84 %40
-;CHECK:             %86 = OpIAdd %uint %53 %uint_8
-;CHECK:             %87 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %86
-;CHECK:                   OpStore %87 %41
-;CHECK:             %89 = OpIAdd %uint %53 %uint_9
-;CHECK:             %90 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %89
-;CHECK:                   OpStore %90 %42
-;CHECK:             %92 = OpIAdd %uint %53 %uint_10
-;CHECK:             %93 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %92
-;CHECK:                   OpStore %93 %43
+;CHECK:             %69 = OpIAdd %uint %53 %uint_3
+;CHECK:             %70 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %69
+;CHECK:                   OpStore %70 %uint_4
+;CHECK:             %73 = OpLoad %v4float %gl_FragCoord
+;CHECK:             %75 = OpBitcast %v4uint %73
+;CHECK:             %76 = OpCompositeExtract %uint %75 0
+;CHECK:             %77 = OpIAdd %uint %53 %uint_4
+;CHECK:             %78 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %77
+;CHECK:                   OpStore %78 %76
+;CHECK:             %79 = OpCompositeExtract %uint %75 1
+;CHECK:             %81 = OpIAdd %uint %53 %uint_5
+;CHECK:             %82 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %81
+;CHECK:                   OpStore %82 %79
+;CHECK:             %84 = OpIAdd %uint %53 %uint_7
+;CHECK:             %85 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %84
+;CHECK:                   OpStore %85 %40
+;CHECK:             %87 = OpIAdd %uint %53 %uint_8
+;CHECK:             %88 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %87
+;CHECK:                   OpStore %88 %41
+;CHECK:             %90 = OpIAdd %uint %53 %uint_9
+;CHECK:             %91 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %90
+;CHECK:                   OpStore %91 %42
+;CHECK:             %93 = OpIAdd %uint %53 %uint_10
+;CHECK:             %94 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %93
+;CHECK:                   OpStore %94 %43
 ;CHECK:                   OpBranch %57
 ;CHECK:             %57 = OpLabel
 ;CHECK:                   OpReturn
@@ -9847,7 +9860,7 @@
 ;CHECK:           %uint = OpTypeInt 32 0
 ;CHECK:         %uint_0 = OpConstant %uint 0
 ;CHECK:           %bool = OpTypeBool
-;CHECK:         %uint_3 = OpConstant %uint 3
+;CHECK:         %uint_6 = OpConstant %uint 6
 ;CHECK:             %44 = OpTypeFunction %void %uint %uint %uint %uint %uint
 ;CHECK:    %_runtimearr_uint = OpTypeRuntimeArray %uint
 ;CHECK:     %_struct_52 = OpTypeStruct %uint %_runtimearr_uint
@@ -9859,6 +9872,7 @@
 ;CHECK:         %uint_1 = OpConstant %uint 1
 ;CHECK:        %uint_23 = OpConstant %uint 23
 ;CHECK:         %uint_2 = OpConstant %uint 2
+;CHECK:         %uint_3 = OpConstant %uint 3
 ;CHECK:    %_ptr_Input_v4float = OpTypePointer Input %v4float
 ;CHECK:    %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
 ;CHECK:         %v4uint = OpTypeVector %uint 4
@@ -9868,7 +9882,7 @@
 ;CHECK:         %uint_9 = OpConstant %uint 9
 ;CHECK:        %uint_10 = OpConstant %uint 10
 ;CHECK:        %uint_42 = OpConstant %uint 42
-;CHECK:            %102 = OpConstantNull %v4float
+;CHECK:            %103 = OpConstantNull %v4float
                   %main = OpFunction %void None %3
                      %5 = OpLabel
 ;CHECK:                   OpBranch %28
@@ -9898,11 +9912,11 @@
 ;CHECK:             %42 = OpImageFetch %v4float %41 %23
 ;CHECK:                   OpBranch %36
 ;CHECK:             %38 = OpLabel
-;CHECK:            %101 = OpFunctionCall %void %43 %uint_42 %uint_3 %uint_0 %30 %32
+;CHECK:            %102 = OpFunctionCall %void %43 %uint_42 %uint_6 %uint_0 %30 %32
 ;CHECK:                   OpBranch %36
 ;CHECK:             %36 = OpLabel
-;CHECK:            %103 = OpPhi %v4float %42 %37 %102 %38
-;CHECK:                   OpStore %x %103
+;CHECK:            %104 = OpPhi %v4float %42 %37 %103 %38
+;CHECK:                   OpStore %x %104
                           OpReturn
                           OpFunctionEnd
 ;CHECK:             %43 = OpFunction %void None %44
@@ -9929,31 +9943,31 @@
 ;CHECK:             %72 = OpIAdd %uint %59 %uint_2
 ;CHECK:             %73 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %72
 ;CHECK:                   OpStore %73 %45
-;CHECK:             %74 = OpIAdd %uint %59 %uint_3
-;CHECK:             %75 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %74
-;CHECK:                   OpStore %75 %uint_4
-;CHECK:             %78 = OpLoad %v4float %gl_FragCoord
-;CHECK:             %80 = OpBitcast %v4uint %78
-;CHECK:             %81 = OpCompositeExtract %uint %80 0
-;CHECK:             %82 = OpIAdd %uint %59 %uint_4
-;CHECK:             %83 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %82
-;CHECK:                   OpStore %83 %81
-;CHECK:             %84 = OpCompositeExtract %uint %80 1
-;CHECK:             %86 = OpIAdd %uint %59 %uint_5
-;CHECK:             %87 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %86
-;CHECK:                   OpStore %87 %84
-;CHECK:             %89 = OpIAdd %uint %59 %uint_7
-;CHECK:             %90 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %89
-;CHECK:                   OpStore %90 %46
-;CHECK:             %92 = OpIAdd %uint %59 %uint_8
-;CHECK:             %93 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %92
-;CHECK:                   OpStore %93 %47
-;CHECK:             %95 = OpIAdd %uint %59 %uint_9
-;CHECK:             %96 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %95
-;CHECK:                   OpStore %96 %48
-;CHECK:             %98 = OpIAdd %uint %59 %uint_10
-;CHECK:             %99 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %98
-;CHECK:                   OpStore %99 %49
+;CHECK:             %75 = OpIAdd %uint %59 %uint_3
+;CHECK:             %76 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %75
+;CHECK:                   OpStore %76 %uint_4
+;CHECK:             %79 = OpLoad %v4float %gl_FragCoord
+;CHECK:             %81 = OpBitcast %v4uint %79
+;CHECK:             %82 = OpCompositeExtract %uint %81 0
+;CHECK:             %83 = OpIAdd %uint %59 %uint_4
+;CHECK:             %84 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %83
+;CHECK:                   OpStore %84 %82
+;CHECK:             %85 = OpCompositeExtract %uint %81 1
+;CHECK:             %87 = OpIAdd %uint %59 %uint_5
+;CHECK:             %88 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %87
+;CHECK:                   OpStore %88 %85
+;CHECK:             %90 = OpIAdd %uint %59 %uint_7
+;CHECK:             %91 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %90
+;CHECK:                   OpStore %91 %46
+;CHECK:             %93 = OpIAdd %uint %59 %uint_8
+;CHECK:             %94 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %93
+;CHECK:                   OpStore %94 %47
+;CHECK:             %96 = OpIAdd %uint %59 %uint_9
+;CHECK:             %97 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %96
+;CHECK:                   OpStore %97 %48
+;CHECK:             %99 = OpIAdd %uint %59 %uint_10
+;CHECK:             %100 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %99
+;CHECK:                   OpStore %100 %49
 ;CHECK:                   OpBranch %63
 ;CHECK:             %63 = OpLabel
 ;CHECK:                   OpReturn
diff --git a/test/opt/instruction_list_test.cpp b/test/opt/instruction_list_test.cpp
index e745790..2c3c242 100644
--- a/test/opt/instruction_list_test.cpp
+++ b/test/opt/instruction_list_test.cpp
@@ -35,7 +35,7 @@
  public:
   TestInstruction() : Instruction() { created_instructions_.push_back(this); }
 
-  ~TestInstruction() { deleted_instructions_.push_back(this); }
+  ~TestInstruction() override{ deleted_instructions_.push_back(this); }
 
   static std::vector<TestInstruction*> created_instructions_;
   static std::vector<TestInstruction*> deleted_instructions_;
diff --git a/test/opt/interp_fixup_test.cpp b/test/opt/interp_fixup_test.cpp
new file mode 100644
index 0000000..a43a29c
--- /dev/null
+++ b/test/opt/interp_fixup_test.cpp
@@ -0,0 +1,172 @@
+// Copyright (c) 2021 The Khronos Group Inc.
+// Copyright (c) 2021 Valve Corporation
+// Copyright (c) 2021 LunarG Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "test/opt/pass_fixture.h"
+#include "test/opt/pass_utils.h"
+
+namespace spvtools {
+namespace opt {
+namespace {
+
+using InterpFixupTest = PassTest<::testing::Test>;
+
+using ::testing::HasSubstr;
+
+TEST_F(InterpFixupTest, FixInterpAtSample) {
+  const std::string text = R"(
+               OpCapability Shader
+               OpCapability InterpolationFunction
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %MainPs "MainPs" %i_vPositionOs %_entryPointOutput
+               OpExecutionMode %MainPs OriginUpperLeft
+               OpSource HLSL 500
+               OpName %MainPs "MainPs"
+               OpName %i_vPositionOs "i.vPositionOs"
+               OpName %_entryPointOutput "@entryPointOutput"
+               OpDecorate %i_vPositionOs Location 0
+               OpDecorate %_entryPointOutput Location 0
+       %void = OpTypeVoid
+          %6 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+    %float_0 = OpConstant %float 0
+         %10 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
+       %uint = OpTypeInt 32 0
+     %uint_0 = OpConstant %uint 0
+     %uint_4 = OpConstant %uint 4
+       %bool = OpTypeBool
+        %int = OpTypeInt 32 1
+      %int_1 = OpConstant %int 1
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%i_vPositionOs = OpVariable %_ptr_Input_v4float Input
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%_entryPointOutput = OpVariable %_ptr_Output_v4float Output
+     %MainPs = OpFunction %void None %6
+         %19 = OpLabel
+         %20 = OpLoad %v4float %i_vPositionOs
+               OpBranch %21
+         %21 = OpLabel
+         %22 = OpPhi %v4float %10 %19 %23 %24
+         %25 = OpPhi %uint %uint_0 %19 %26 %24
+         %27 = OpULessThan %bool %25 %uint_4
+               OpLoopMerge %28 %24 None
+               OpBranchConditional %27 %24 %28
+         %24 = OpLabel
+         %29 = OpExtInst %v4float %1 InterpolateAtSample %20 %25
+;CHECK:  %29 = OpExtInst %v4float %1 InterpolateAtSample %i_vPositionOs %25
+         %30 = OpCompositeExtract %float %29 0
+         %31 = OpCompositeExtract %float %22 0
+         %32 = OpFAdd %float %31 %30
+         %23 = OpCompositeInsert %v4float %32 %22 0
+         %26 = OpIAdd %uint %25 %int_1
+               OpBranch %21
+         %28 = OpLabel
+               OpStore %_entryPointOutput %22
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<InterpFixupPass>(text, false);
+}
+
+TEST_F(InterpFixupTest, FixInterpAtCentroid) {
+  const std::string text = R"(
+               OpCapability Shader
+               OpCapability InterpolationFunction
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %MainPs "MainPs" %i_vPositionOs %_entryPointOutput
+               OpExecutionMode %MainPs OriginUpperLeft
+               OpSource HLSL 500
+               OpName %MainPs "MainPs"
+               OpName %i_vPositionOs "i.vPositionOs"
+               OpName %_entryPointOutput "@entryPointOutput"
+               OpDecorate %i_vPositionOs Location 0
+               OpDecorate %_entryPointOutput Location 0
+       %void = OpTypeVoid
+          %6 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+    %float_0 = OpConstant %float 0
+         %10 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%i_vPositionOs = OpVariable %_ptr_Input_v4float Input
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%_entryPointOutput = OpVariable %_ptr_Output_v4float Output
+     %MainPs = OpFunction %void None %6
+         %13 = OpLabel
+         %14 = OpLoad %v4float %i_vPositionOs
+         %15 = OpExtInst %v4float %1 InterpolateAtCentroid %14
+;CHECK:  %15 = OpExtInst %v4float %1 InterpolateAtCentroid %i_vPositionOs
+         %16 = OpCompositeExtract %float %15 0
+         %17 = OpCompositeInsert %v4float %16 %10 0
+               OpStore %_entryPointOutput %17
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<InterpFixupPass>(text, false);
+}
+
+TEST_F(InterpFixupTest, FixInterpAtOffset) {
+  const std::string text = R"(
+               OpCapability Shader
+               OpCapability InterpolationFunction
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %MainPs "MainPs" %i_vPositionOs %_entryPointOutput
+               OpExecutionMode %MainPs OriginUpperLeft
+               OpSource HLSL 500
+               OpName %MainPs "MainPs"
+               OpName %i_vPositionOs "i.vPositionOs"
+               OpName %_entryPointOutput "@entryPointOutput"
+               OpDecorate %i_vPositionOs Location 0
+               OpDecorate %_entryPointOutput Location 0
+       %void = OpTypeVoid
+          %6 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+    %float_0 = OpConstant %float 0
+         %10 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
+    %v2float = OpTypeVector %float 2
+%float_0_0625 = OpConstant %float 0.0625
+         %13 = OpConstantComposite %v2float %float_0_0625 %float_0_0625
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%i_vPositionOs = OpVariable %_ptr_Input_v4float Input
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%_entryPointOutput = OpVariable %_ptr_Output_v4float Output
+     %MainPs = OpFunction %void None %6
+         %16 = OpLabel
+         %17 = OpLoad %v4float %i_vPositionOs
+         %18 = OpExtInst %v4float %1 InterpolateAtOffset %17 %13
+;CHECK:  %18 = OpExtInst %v4float %1 InterpolateAtOffset %i_vPositionOs %13
+         %19 = OpCompositeExtract %float %18 0
+         %20 = OpCompositeInsert %v4float %19 %10 0
+               OpStore %_entryPointOutput %20
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<InterpFixupPass>(text, false);
+}
+
+}  // namespace
+}  // namespace opt
+}  // namespace spvtools
diff --git a/test/opt/loop_optimizations/unroll_simple.cpp b/test/opt/loop_optimizations/unroll_simple.cpp
index 016316a..6a3cb6e 100644
--- a/test/opt/loop_optimizations/unroll_simple.cpp
+++ b/test/opt/loop_optimizations/unroll_simple.cpp
@@ -3401,6 +3401,215 @@
   SinglePassRunAndCheck<LoopUnroller>(shader, output, false);
 }
 
+TEST_F(PassClassTest, UnrollWithPhiReferencesPhi) {
+  const std::string text = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main" %color
+               OpExecutionMode %main OriginUpperLeft
+               OpSource HLSL 600
+               OpName %main "main"
+               OpName %color "color"
+               OpDecorate %color Location 0
+       %uint = OpTypeInt 32 0
+      %float = OpTypeFloat 32
+    %float_0 = OpConstant %float 0
+    %float_1 = OpConstant %float 1
+     %uint_1 = OpConstant %uint 1
+     %uint_3 = OpConstant %uint 3
+       %void = OpTypeVoid
+         %11 = OpTypeFunction %void
+       %bool = OpTypeBool
+    %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+      %color = OpVariable %_ptr_Output_v4float Output
+       %main = OpFunction %void None %11
+         %15 = OpLabel
+               OpBranch %16
+         %16 = OpLabel
+         %17 = OpPhi %float %float_0 %15 %18 %19
+         %18 = OpPhi %float %float_1 %15 %20 %19
+         %21 = OpPhi %uint %uint_1 %15 %22 %19
+         %23 = OpULessThanEqual %bool %21 %uint_3
+               OpLoopMerge %24 %19 Unroll
+               OpBranchConditional %23 %25 %24
+         %25 = OpLabel
+
+; First loop iteration
+; CHECK: [[next_phi1_0:%\w+]] = OpFSub %float %float_1 %float_0
+
+; Second loop iteration
+; CHECK: [[next_phi1_1:%\w+]] = OpFSub %float [[next_phi1_0]] %float_1
+
+; Third loop iteration
+; CHECK:                        OpFSub %float [[next_phi1_1]] [[next_phi1_0]]
+
+         %20 = OpFSub %float %18 %17
+               OpBranch %19
+         %19 = OpLabel
+         %22 = OpIAdd %uint %21 %uint_1
+               OpBranch %16
+         %24 = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+
+  std::unique_ptr<IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
+                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  Module* module = context->module();
+  EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n"
+                             << text << std::endl;
+
+  LoopUnroller loop_unroller;
+  SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER |
+                        SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES);
+  SinglePassRunAndMatch<LoopUnroller>(text, true);
+}
+
+TEST_F(PassClassTest, UnrollWithDoublePhiReferencesPhi) {
+  const std::string text = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main" %color
+               OpExecutionMode %main OriginUpperLeft
+               OpSource HLSL 600
+               OpName %main "main"
+               OpName %color "color"
+               OpDecorate %color Location 0
+       %uint = OpTypeInt 32 0
+      %float = OpTypeFloat 32
+    %float_0 = OpConstant %float 0
+    %float_1 = OpConstant %float 1
+     %uint_1 = OpConstant %uint 1
+     %uint_3 = OpConstant %uint 3
+       %void = OpTypeVoid
+         %11 = OpTypeFunction %void
+       %bool = OpTypeBool
+    %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+      %color = OpVariable %_ptr_Output_v4float Output
+       %main = OpFunction %void None %11
+         %15 = OpLabel
+               OpBranch %16
+         %16 = OpLabel
+         %17 = OpPhi %float %float_1 %15 %18 %19
+         %18 = OpPhi %float %float_0 %15 %20 %19
+         %20 = OpPhi %float %float_1 %15 %21 %19
+         %22 = OpPhi %uint %uint_1 %15 %23 %19
+         %24 = OpULessThanEqual %bool %22 %uint_3
+               OpLoopMerge %25 %19 Unroll
+               OpBranchConditional %24 %26 %25
+         %26 = OpLabel
+
+; First loop iteration
+; CHECK: [[next_phi1_0:%\w+]] = OpFSub %float %float_1 %float_0
+; CHECK:                        OpFMul %float %float_1
+
+; Second loop iteration
+; CHECK: [[next_phi1_1:%\w+]] = OpFSub %float [[next_phi1_0]] %float_1
+; CHECK:                        OpFMul %float %float_0
+
+; Third loop iteration
+; CHECK:                        OpFSub %float [[next_phi1_1]] [[next_phi1_0]]
+; CHECK:                        OpFMul %float %float_1
+
+         %21 = OpFSub %float %20 %18
+         %27 = OpFMul %float %17 %21
+               OpBranch %19
+         %19 = OpLabel
+         %23 = OpIAdd %uint %22 %uint_1
+               OpBranch %16
+         %25 = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+
+  std::unique_ptr<IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
+                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  Module* module = context->module();
+  EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n"
+                             << text << std::endl;
+
+  LoopUnroller loop_unroller;
+  SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER |
+                        SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES);
+  SinglePassRunAndMatch<LoopUnroller>(text, true);
+}
+
+TEST_F(PassClassTest, PartialUnrollWithPhiReferencesPhi) {
+  // With LocalMultiStoreElimPass
+  const std::string text = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main" %color
+               OpExecutionMode %main OriginUpperLeft
+               OpSource HLSL 600
+               OpName %main "main"
+               OpName %color "color"
+               OpDecorate %color Location 0
+       %uint = OpTypeInt 32 0
+      %float = OpTypeFloat 32
+    %float_0 = OpConstant %float 0
+    %float_1 = OpConstant %float 1
+     %uint_1 = OpConstant %uint 1
+     %uint_3 = OpConstant %uint 3
+       %void = OpTypeVoid
+         %11 = OpTypeFunction %void
+       %bool = OpTypeBool
+    %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+      %color = OpVariable %_ptr_Output_v4float Output
+       %main = OpFunction %void None %11
+         %15 = OpLabel
+               OpBranch %16
+         %16 = OpLabel
+         %17 = OpPhi %float %float_0 %15 %18 %19
+         %18 = OpPhi %float %float_1 %15 %20 %19
+         %21 = OpPhi %uint %uint_1 %15 %22 %19
+         %23 = OpULessThanEqual %bool %21 %uint_3
+               OpLoopMerge %24 %19 Unroll
+               OpBranchConditional %23 %25 %24
+         %25 = OpLabel
+
+; CHECK: [[phi0_0:%\w+]] = OpPhi {{%\w+}} {{%\w+}} {{%\w+}} [[phi1_0:%\w+]]
+; CHECK:      [[phi1_0]] = OpPhi {{%\w+}} {{%\w+}} {{%\w+}} [[sub:%\w+]]
+
+; CHECK: [[sub]] = OpFSub {{%\w+}} [[phi1_0]] [[phi0_0]]
+
+; CHECK: [[phi0_1:%\w+]] = OpPhi {{%\w+}} [[phi0_0]]
+; CHECK: [[phi1_1:%\w+]] = OpPhi {{%\w+}} [[phi1_0]]
+
+; CHECK: [[sub:%\w+]] = OpFSub {{%\w+}} [[phi1_1]] [[phi0_1]]
+
+; CHECK: OpFSub {{%\w+}} [[sub]] [[phi1_1]]
+
+         %20 = OpFSub %float %18 %17
+               OpBranch %19
+         %19 = OpLabel
+         %22 = OpIAdd %uint %21 %uint_1
+               OpBranch %16
+         %24 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  std::unique_ptr<IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
+                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  Module* module = context->module();
+  EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n"
+                             << text << std::endl;
+
+  LoopUnroller loop_unroller;
+  SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
+  SinglePassRunAndMatch<PartialUnrollerTestPass<2>>(text, true);
+}
+
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools
diff --git a/test/opt/scalar_replacement_test.cpp b/test/opt/scalar_replacement_test.cpp
index 1073855..8115f5f 100644
--- a/test/opt/scalar_replacement_test.cpp
+++ b/test/opt/scalar_replacement_test.cpp
@@ -2197,6 +2197,46 @@
   SinglePassRunAndMatch<ScalarReplacementPass>(text, true);
 }
 
+TEST_F(ScalarReplacementTest, ImageTexelPointer) {
+  // Test whether the scalar replacement correctly checks the
+  // OpImageTexelPointer user of an aggregate with an image type.
+  const std::string text = R"(
+;
+; CHECK: [[imgTy:%\w+]] = OpTypeImage %uint Buffer 2 0 0 2 R32ui
+; CHECK: [[ptrImgTy:%\w+]] = OpTypePointer Function [[imgTy]]
+; CHECK: [[img:%\w+]] = OpVariable [[ptrImgTy]] Function
+; CHECK: [[imgTexelPtr:%\w+]] = OpImageTexelPointer {{%\w+}} [[img]] %uint_0 %uint_0
+; CHECK: OpAtomicIAdd %uint [[imgTexelPtr]] %uint_1 %uint_0 %uint_1
+;
+OpCapability Shader
+OpCapability SampledBuffer
+OpCapability ImageBuffer
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %1 "main"
+OpExecutionMode %1 LocalSize 64 1 1
+%void = OpTypeVoid
+%uint = OpTypeInt 32 0
+%uint_0 = OpConstant %uint 0
+%uint_1 = OpConstant %uint 1
+%_ptr_Image_uint = OpTypePointer Image %uint
+%type_buffer_image = OpTypeImage %uint Buffer 2 0 0 2 R32ui
+%_ptr_Function_type_buffer_image = OpTypePointer Function %type_buffer_image
+%image_struct = OpTypeStruct %type_buffer_image %type_buffer_image
+%_ptr_Function_image_struct = OpTypePointer Function %image_struct
+%func = OpTypeFunction %void
+%1 = OpFunction %void None %func
+%2 = OpLabel
+%3 = OpVariable %_ptr_Function_image_struct Function
+%4 = OpAccessChain %_ptr_Function_type_buffer_image %3 %uint_1
+%5 = OpImageTexelPointer %_ptr_Image_uint %4 %uint_0 %uint_0
+%6 = OpAtomicIAdd %uint %5 %uint_1 %uint_0 %uint_1
+OpReturn
+OpFunctionEnd
+  )";
+
+  SinglePassRunAndMatch<ScalarReplacementPass>(text, false);
+}
+
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools
diff --git a/test/test_fixture.h b/test/test_fixture.h
index 436993e..0c5bfc9 100644
--- a/test/test_fixture.h
+++ b/test/test_fixture.h
@@ -46,7 +46,7 @@
     text = {textStr, strlen(textStr)};
   }
 
-  virtual ~TextToBinaryTestBase() {
+  ~TextToBinaryTestBase() override {
     DestroyBinary();
     if (diagnostic) spvDiagnosticDestroy(diagnostic);
   }
diff --git a/test/text_to_binary.extension_test.cpp b/test/text_to_binary.extension_test.cpp
index 023763b..0857984 100644
--- a/test/text_to_binary.extension_test.cpp
+++ b/test/text_to_binary.extension_test.cpp
@@ -905,5 +905,118 @@
              MakeInstruction(SpvOpDecorate, {1, 5300})},
         })));
 
+// SPV_KHR_linkonce_odr
+
+INSTANTIATE_TEST_SUITE_P(
+    SPV_KHR_linkonce_odr, ExtensionRoundTripTest,
+    Combine(
+        Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_3, SPV_ENV_VULKAN_1_0,
+               SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_2),
+        ValuesIn(std::vector<AssemblyCase>{
+            {"OpExtension \"SPV_KHR_linkonce_odr\"\n",
+             MakeInstruction(SpvOpExtension,
+                             MakeVector("SPV_KHR_linkonce_odr"))},
+            {"OpDecorate %1 LinkageAttributes \"foobar\" LinkOnceODR\n",
+             MakeInstruction(SpvOpDecorate,
+                             Concatenate({{1, SpvDecorationLinkageAttributes},
+                                          MakeVector("foobar"),
+                                          {SpvLinkageTypeLinkOnceODR}}))},
+        })));
+
+// SPV_KHR_expect_assume
+
+INSTANTIATE_TEST_SUITE_P(
+    SPV_KHR_expect_assume, ExtensionRoundTripTest,
+    Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_3,
+                   SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_2),
+            ValuesIn(std::vector<AssemblyCase>{
+                {"OpExtension \"SPV_KHR_expect_assume\"\n",
+                 MakeInstruction(SpvOpExtension,
+                                 MakeVector("SPV_KHR_expect_assume"))},
+                {"OpAssumeTrueKHR %1\n",
+                 MakeInstruction(SpvOpAssumeTrueKHR, {1})}})));
+// SPV_KHR_subgroup_uniform_control_flow
+
+INSTANTIATE_TEST_SUITE_P(
+    SPV_KHR_subgroup_uniform_control_flow, ExtensionRoundTripTest,
+    Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_3,
+                   SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_2),
+            ValuesIn(std::vector<AssemblyCase>{
+                {"OpExtension \"SPV_KHR_subgroup_uniform_control_flow\"\n",
+                 MakeInstruction(
+                     SpvOpExtension,
+                     MakeVector("SPV_KHR_subgroup_uniform_control_flow"))},
+                {"OpExecutionMode %1 SubgroupUniformControlFlowKHR\n",
+                 MakeInstruction(
+                     SpvOpExecutionMode,
+                     {1, SpvExecutionModeSubgroupUniformControlFlowKHR})},
+            })));
+
+// SPV_KHR_integer_dot_product
+
+INSTANTIATE_TEST_SUITE_P(
+    SPV_KHR_integer_dot_product, ExtensionRoundTripTest,
+    Combine(
+        Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_5, SPV_ENV_VULKAN_1_0,
+               SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_2),
+        ValuesIn(std::vector<AssemblyCase>{
+            {"OpExtension \"SPV_KHR_integer_dot_product\"\n",
+             MakeInstruction(SpvOpExtension,
+                             MakeVector("SPV_KHR_integer_dot_product"))},
+            {"OpCapability DotProductInputAllKHR\n",
+             MakeInstruction(SpvOpCapability,
+                             {SpvCapabilityDotProductInputAllKHR})},
+            {"OpCapability DotProductInput4x8BitKHR\n",
+             MakeInstruction(SpvOpCapability,
+                             {SpvCapabilityDotProductInput4x8BitKHR})},
+            {"OpCapability DotProductInput4x8BitPackedKHR\n",
+             MakeInstruction(SpvOpCapability,
+                             {SpvCapabilityDotProductInput4x8BitPackedKHR})},
+            {"OpCapability DotProductKHR\n",
+             MakeInstruction(SpvOpCapability, {SpvCapabilityDotProductKHR})},
+            {"%2 = OpSDotKHR %1 %3 %4\n",
+             MakeInstruction(SpvOpSDotKHR, {1, 2, 3, 4})},
+            {"%2 = OpSDotKHR %1 %3 %4 PackedVectorFormat4x8BitKHR\n",
+             MakeInstruction(
+                 SpvOpSDotKHR,
+                 {1, 2, 3, 4,
+                  SpvPackedVectorFormatPackedVectorFormat4x8BitKHR})},
+            {"%2 = OpUDotKHR %1 %3 %4\n",
+             MakeInstruction(SpvOpUDotKHR, {1, 2, 3, 4})},
+            {"%2 = OpUDotKHR %1 %3 %4 PackedVectorFormat4x8BitKHR\n",
+             MakeInstruction(
+                 SpvOpUDotKHR,
+                 {1, 2, 3, 4,
+                  SpvPackedVectorFormatPackedVectorFormat4x8BitKHR})},
+            {"%2 = OpSUDotKHR %1 %3 %4\n",
+             MakeInstruction(SpvOpSUDotKHR, {1, 2, 3, 4})},
+            {"%2 = OpSUDotKHR %1 %3 %4 PackedVectorFormat4x8BitKHR\n",
+             MakeInstruction(
+                 SpvOpSUDotKHR,
+                 {1, 2, 3, 4,
+                  SpvPackedVectorFormatPackedVectorFormat4x8BitKHR})},
+            {"%2 = OpSDotAccSatKHR %1 %3 %4 %5\n",
+             MakeInstruction(SpvOpSDotAccSatKHR, {1, 2, 3, 4, 5})},
+            {"%2 = OpSDotAccSatKHR %1 %3 %4 %5 PackedVectorFormat4x8BitKHR\n",
+             MakeInstruction(
+                 SpvOpSDotAccSatKHR,
+                 {1, 2, 3, 4, 5,
+                  SpvPackedVectorFormatPackedVectorFormat4x8BitKHR})},
+            {"%2 = OpUDotAccSatKHR %1 %3 %4 %5\n",
+             MakeInstruction(SpvOpUDotAccSatKHR, {1, 2, 3, 4, 5})},
+            {"%2 = OpUDotAccSatKHR %1 %3 %4 %5 PackedVectorFormat4x8BitKHR\n",
+             MakeInstruction(
+                 SpvOpUDotAccSatKHR,
+                 {1, 2, 3, 4, 5,
+                  SpvPackedVectorFormatPackedVectorFormat4x8BitKHR})},
+            {"%2 = OpSUDotAccSatKHR %1 %3 %4 %5\n",
+             MakeInstruction(SpvOpSUDotAccSatKHR, {1, 2, 3, 4, 5})},
+            {"%2 = OpSUDotAccSatKHR %1 %3 %4 %5 PackedVectorFormat4x8BitKHR\n",
+             MakeInstruction(
+                 SpvOpSUDotAccSatKHR,
+                 {1, 2, 3, 4, 5,
+                  SpvPackedVectorFormatPackedVectorFormat4x8BitKHR})},
+        })));
+
 }  // namespace
 }  // namespace spvtools
diff --git a/test/text_to_binary.mode_setting_test.cpp b/test/text_to_binary.mode_setting_test.cpp
index 8ddf421..647bb3d 100644
--- a/test/text_to_binary.mode_setting_test.cpp
+++ b/test/text_to_binary.mode_setting_test.cpp
@@ -189,6 +189,7 @@
                 {CASE(OutputTriangleStrip), {}},
                 {CASE(VecTypeHint), {96}},
                 {CASE(ContractionOff), {}},
+                {CASE(SubgroupUniformControlFlowKHR), {}},
             })));
 
 INSTANTIATE_TEST_SUITE_P(
diff --git a/test/val/CMakeLists.txt b/test/val/CMakeLists.txt
index b17d1cb..8324964 100644
--- a/test/val/CMakeLists.txt
+++ b/test/val/CMakeLists.txt
@@ -38,6 +38,10 @@
        val_entry_point.cpp
        val_explicit_reserved_test.cpp
        val_extensions_test.cpp
+       val_extension_spv_khr_expect_assume.cpp
+       val_extension_spv_khr_linkonce_odr.cpp
+       val_extension_spv_khr_subgroup_uniform_control_flow.cpp
+       val_extension_spv_khr_integer_dot_product.cpp
        val_extension_spv_khr_terminate_invocation.cpp
        val_ext_inst_test.cpp
        ${VAL_TEST_COMMON_SRCS}
diff --git a/test/val/val_atomics_test.cpp b/test/val/val_atomics_test.cpp
index fccfabc..fc3aedb 100644
--- a/test/val/val_atomics_test.cpp
+++ b/test/val/val_atomics_test.cpp
@@ -95,12 +95,13 @@
 std::string GenerateShaderCode(
     const std::string& body,
     const std::string& capabilities_and_extensions = "",
+    const std::string& extra_defs = "",
     const std::string& memory_model = "GLSL450") {
   const std::string execution = R"(
 OpEntryPoint Fragment %main "main"
 OpExecutionMode %main OriginUpperLeft
 )";
-  const std::string defintions = R"(
+  const std::string definitions = R"(
 %u64 = OpTypeInt 64 0
 %s64 = OpTypeInt 64 1
 
@@ -113,19 +114,20 @@
 %s64_var = OpVariable %s64_ptr Workgroup
 )";
   return GenerateShaderCodeImpl(
-      body, "OpCapability Int64\n" + capabilities_and_extensions, defintions,
-      memory_model, execution);
+      body, "OpCapability Int64\n" + capabilities_and_extensions,
+      definitions + extra_defs, memory_model, execution);
 }
 
 std::string GenerateShaderComputeCode(
     const std::string& body,
     const std::string& capabilities_and_extensions = "",
+    const std::string& extra_defs = "",
     const std::string& memory_model = "GLSL450") {
   const std::string execution = R"(
 OpEntryPoint GLCompute %main "main"
 OpExecutionMode %main LocalSize 32 1 1
 )";
-  const std::string defintions = R"(
+  const std::string definitions = R"(
 %u64 = OpTypeInt 64 0
 %s64 = OpTypeInt 64 1
 
@@ -138,8 +140,8 @@
 %s64_var = OpVariable %s64_ptr Workgroup
 )";
   return GenerateShaderCodeImpl(
-      body, "OpCapability Int64\n" + capabilities_and_extensions, defintions,
-      memory_model, execution);
+      body, "OpCapability Int64\n" + capabilities_and_extensions,
+      definitions + extra_defs, memory_model, execution);
 }
 
 std::string GenerateKernelCode(
@@ -222,7 +224,6 @@
   const std::string body = R"(
 %val1 = OpAtomicLoad %u32 %u32_var %device %relaxed
 %val2 = OpAtomicLoad %u32 %u32_var %workgroup %acquire
-%val3 = OpAtomicLoad %u64 %u64_var %subgroup %sequentially_consistent
 )";
 
   CompileSuccessfully(GenerateShaderCode(body));
@@ -233,23 +234,56 @@
   const std::string body = R"(
 %val1 = OpAtomicLoad %f32 %f32_var %device %relaxed
 %val2 = OpAtomicLoad %u32 %u32_var %workgroup %sequentially_consistent
-%val3 = OpAtomicLoad %u64 %u64_var %subgroup %acquire
 )";
 
   CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
+TEST_F(ValidateAtomics, AtomicLoadInt64ShaderSuccess) {
+  const std::string body = R"(
+%val1 = OpAtomicLoad %u64 %u64_var %subgroup %sequentially_consistent
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body, "OpCapability Int64Atomics\n"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateAtomics, AtomicLoadInt64KernelSuccess) {
+  const std::string body = R"(
+%val1 = OpAtomicLoad %u64 %u64_var %subgroup %acquire
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body, "OpCapability Int64Atomics\n"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
 TEST_F(ValidateAtomics, AtomicLoadInt32VulkanSuccess) {
   const std::string body = R"(
 %val1 = OpAtomicLoad %u32 %u32_var %device %relaxed
 %val2 = OpAtomicLoad %u32 %u32_var %workgroup %acquire
+%val3 = OpAtomicLoad %u32 %u32_var %invocation %relaxed
 )";
 
   CompileSuccessfully(GenerateShaderComputeCode(body), SPV_ENV_VULKAN_1_0);
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
 }
 
+TEST_F(ValidateAtomics, AtomicLoadVulkanWrongStorageClass) {
+  const std::string body = R"(
+%val1 = OpAtomicLoad %u32 %u32_var %device %relaxed
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-None-04645"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("in Vulkan evironment, Workgroup Storage Class is limited to "
+                "MeshNV, TaskNV, and GLCompute execution model"));
+}
+
 TEST_F(ValidateAtomics, AtomicAddIntVulkanWrongType1) {
   const std::string body = R"(
 %val1 = OpAtomicIAdd %f32 %f32_var %device %relaxed %f32_1
@@ -259,7 +293,7 @@
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("AtomicIAdd: "
-                        "expected Result Type to be int scalar type"));
+                        "expected Result Type to be integer scalar type"));
 }
 
 TEST_F(ValidateAtomics, AtomicAddIntVulkanWrongType2) {
@@ -287,6 +321,32 @@
                 "AtomicFloat32AddEXT AtomicFloat64AddEXT"));
 }
 
+TEST_F(ValidateAtomics, AtomicMinFloatVulkan) {
+  const std::string body = R"(
+%val1 = OpAtomicFMinEXT %f32 %f32_var %device %relaxed %f32_1
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_CAPABILITY, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Opcode AtomicFMinEXT requires one of these capabilities: "
+                "AtomicFloat32MinMaxEXT AtomicFloat64MinMaxEXT AtomicFloat16MinMaxEXT"));
+}
+
+TEST_F(ValidateAtomics, AtomicMaxFloatVulkan) {
+  const std::string body = R"(
+%val1 = OpAtomicFMaxEXT %f32 %f32_var %device %relaxed %f32_1
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_CAPABILITY, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Opcode AtomicFMaxEXT requires one of these capabilities: "
+                "AtomicFloat32MinMaxEXT AtomicFloat64MinMaxEXT AtomicFloat16MinMaxEXT"));
+}
+
 TEST_F(ValidateAtomics, AtomicAddFloatVulkanWrongType1) {
   const std::string body = R"(
 %val1 = OpAtomicFAddEXT %f32vec4 %f32vec4_var %device %relaxed %f32_1
@@ -303,6 +363,38 @@
                         "expected Result Type to be float scalar type"));
 }
 
+TEST_F(ValidateAtomics, AtomicMinFloatVulkanWrongType1) {
+  const std::string body = R"(
+%val1 = OpAtomicFMinEXT %f32vec4 %f32vec4_var %device %relaxed %f32_1
+)";
+  const std::string extra = R"(
+OpCapability AtomicFloat32MinMaxEXT
+OpExtension "SPV_EXT_shader_atomic_float_min_max"
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body, extra), SPV_ENV_VULKAN_1_0);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("AtomicFMinEXT: "
+                        "expected Result Type to be float scalar type"));
+}
+
+TEST_F(ValidateAtomics, AtomicMaxFloatVulkanWrongType1) {
+  const std::string body = R"(
+%val1 = OpAtomicFMaxEXT %f32vec4 %f32vec4_var %device %relaxed %f32_1
+)";
+  const std::string extra = R"(
+OpCapability AtomicFloat32MinMaxEXT
+OpExtension "SPV_EXT_shader_atomic_float_min_max"
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body, extra), SPV_ENV_VULKAN_1_0);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("AtomicFMaxEXT: "
+                        "expected Result Type to be float scalar type"));
+}
+
 TEST_F(ValidateAtomics, AtomicAddFloatVulkanWrongType2) {
   const std::string body = R"(
 %val1 = OpAtomicFAddEXT %u32 %u32_var %device %relaxed %u32_1
@@ -319,6 +411,38 @@
                         "expected Result Type to be float scalar type"));
 }
 
+TEST_F(ValidateAtomics, AtomicMinFloatVulkanWrongType2) {
+  const std::string body = R"(
+%val1 = OpAtomicFMinEXT %u32 %u32_var %device %relaxed %u32_1
+)";
+  const std::string extra = R"(
+OpCapability AtomicFloat32MinMaxEXT
+OpExtension "SPV_EXT_shader_atomic_float_min_max"
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body, extra), SPV_ENV_VULKAN_1_0);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("AtomicFMinEXT: "
+                        "expected Result Type to be float scalar type"));
+}
+
+TEST_F(ValidateAtomics, AtomicMaxFloatVulkanWrongType2) {
+  const std::string body = R"(
+%val1 = OpAtomicFMaxEXT %u32 %u32_var %device %relaxed %u32_1
+)";
+  const std::string extra = R"(
+OpCapability AtomicFloat32MinMaxEXT
+OpExtension "SPV_EXT_shader_atomic_float_min_max"
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body, extra), SPV_ENV_VULKAN_1_0);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("AtomicFMaxEXT: "
+                        "expected Result Type to be float scalar type"));
+}
+
 TEST_F(ValidateAtomics, AtomicAddFloatVulkanWrongType3) {
   const std::string body = R"(
 %val1 = OpAtomicFAddEXT %u64 %u64_var %device %relaxed %u64_1
@@ -335,6 +459,38 @@
                         "expected Result Type to be float scalar type"));
 }
 
+TEST_F(ValidateAtomics, AtomicMinFloatVulkanWrongType3) {
+  const std::string body = R"(
+%val1 = OpAtomicFMinEXT %u64 %u64_var %device %relaxed %u64_1
+)";
+  const std::string extra = R"(
+OpCapability AtomicFloat32MinMaxEXT
+OpExtension "SPV_EXT_shader_atomic_float_min_max"
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body, extra), SPV_ENV_VULKAN_1_0);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("AtomicFMinEXT: "
+                        "expected Result Type to be float scalar type"));
+}
+
+TEST_F(ValidateAtomics, AtomicMaxFloatVulkanWrongType3) {
+  const std::string body = R"(
+%val1 = OpAtomicFMaxEXT %u64 %u64_var %device %relaxed %u64_1
+)";
+  const std::string extra = R"(
+OpCapability AtomicFloat32MinMaxEXT
+OpExtension "SPV_EXT_shader_atomic_float_min_max"
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body, extra), SPV_ENV_VULKAN_1_0);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("AtomicFMaxEXT: "
+                        "expected Result Type to be float scalar type"));
+}
+
 TEST_F(ValidateAtomics, AtomicAddFloatVulkanWrongCapability) {
   const std::string body = R"(
 %val1 = OpAtomicFAddEXT %f32 %f32_var %device %relaxed %f32_1
@@ -351,16 +507,162 @@
                         "require the AtomicFloat32AddEXT capability"));
 }
 
+TEST_F(ValidateAtomics, AtomicMinFloatVulkanWrongCapability) {
+  const std::string body = R"(
+%val1 = OpAtomicFMinEXT %f32 %f32_var %device %relaxed %f32_1
+)";
+  const std::string extra = R"(
+OpCapability AtomicFloat64MinMaxEXT
+OpExtension "SPV_EXT_shader_atomic_float_min_max"
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body, extra), SPV_ENV_VULKAN_1_0);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("AtomicFMinEXT: float min/max atomics "
+                        "require the AtomicFloat32MinMaxEXT capability"));
+}
+
+TEST_F(ValidateAtomics, AtomicMaxFloatVulkanWrongCapability) {
+  const std::string body = R"(
+%val1 = OpAtomicFMaxEXT %f32 %f32_var %device %relaxed %f32_1
+)";
+  const std::string extra = R"(
+OpCapability AtomicFloat64MinMaxEXT
+OpExtension "SPV_EXT_shader_atomic_float_min_max"
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body, extra), SPV_ENV_VULKAN_1_0);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("AtomicFMaxEXT: float min/max atomics "
+                        "require the AtomicFloat32MinMaxEXT capability"));
+}
+
 TEST_F(ValidateAtomics, AtomicAddFloatVulkanSuccess) {
   const std::string body = R"(
 %val1 = OpAtomicFAddEXT %f32 %f32_var %device %relaxed %f32_1
+%val2 = OpAtomicFAddEXT %f32 %f32_var %invocation %relaxed %f32_1
 )";
   const std::string extra = R"(
 OpCapability AtomicFloat32AddEXT
 OpExtension "SPV_EXT_shader_atomic_float_add"
 )";
 
-  CompileSuccessfully(GenerateShaderCode(body, extra), SPV_ENV_VULKAN_1_0);
+  CompileSuccessfully(GenerateShaderComputeCode(body, extra),
+                      SPV_ENV_VULKAN_1_0);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateAtomics, AtomicMinFloat16VulkanSuccess) {
+  const std::string defs = R"(
+%f16 = OpTypeFloat 16
+%f16_1 = OpConstant %f16 1
+%f16_ptr = OpTypePointer Workgroup %f16
+%f16_var = OpVariable %f16_ptr Workgroup
+)";
+  const std::string body = R"(
+%val1 = OpAtomicFMinEXT %f16 %f16_var %device %relaxed %f16_1
+)";
+  const std::string extra = R"(
+OpCapability Float16
+OpCapability AtomicFloat16MinMaxEXT
+OpExtension "SPV_EXT_shader_atomic_float_min_max"
+)";
+
+  CompileSuccessfully(GenerateShaderComputeCode(body, extra, defs),
+                      SPV_ENV_VULKAN_1_0);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateAtomics, AtomicMaxFloat16VulkanSuccess) {
+  const std::string defs = R"(
+%f16 = OpTypeFloat 16
+%f16_1 = OpConstant %f16 1
+%f16_ptr = OpTypePointer Workgroup %f16
+%f16_var = OpVariable %f16_ptr Workgroup
+)";
+  const std::string body = R"(
+%val1 = OpAtomicFMaxEXT %f16 %f16_var %device %relaxed %f16_1
+)";
+  const std::string extra = R"(
+OpCapability Float16
+OpCapability AtomicFloat16MinMaxEXT
+OpExtension "SPV_EXT_shader_atomic_float_min_max"
+)";
+
+  CompileSuccessfully(GenerateShaderComputeCode(body, extra, defs),
+                      SPV_ENV_VULKAN_1_0);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateAtomics, AtomicMinFloat32VulkanSuccess) {
+  const std::string body = R"(
+%val1 = OpAtomicFMinEXT %f32 %f32_var %device %relaxed %f32_1
+)";
+  const std::string extra = R"(
+OpCapability AtomicFloat32MinMaxEXT
+OpExtension "SPV_EXT_shader_atomic_float_min_max"
+)";
+
+  CompileSuccessfully(GenerateShaderComputeCode(body, extra),
+                      SPV_ENV_VULKAN_1_0);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateAtomics, AtomicMaxFloat32VulkanSuccess) {
+  const std::string body = R"(
+%val1 = OpAtomicFMaxEXT %f32 %f32_var %device %relaxed %f32_1
+)";
+  const std::string extra = R"(
+OpCapability AtomicFloat32MinMaxEXT
+OpExtension "SPV_EXT_shader_atomic_float_min_max"
+)";
+
+  CompileSuccessfully(GenerateShaderComputeCode(body, extra),
+                      SPV_ENV_VULKAN_1_0);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateAtomics, AtomicMinFloat64VulkanSuccess) {
+  const std::string defs = R"(
+%f64 = OpTypeFloat 64
+%f64_1 = OpConstant %f64 1
+%f64_ptr = OpTypePointer Workgroup %f64
+%f64_var = OpVariable %f64_ptr Workgroup
+)";
+  const std::string body = R"(
+%val1 = OpAtomicFMinEXT %f64 %f64_var %device %relaxed %f64_1
+)";
+  const std::string extra = R"(
+OpCapability Float64
+OpCapability AtomicFloat64MinMaxEXT
+OpExtension "SPV_EXT_shader_atomic_float_min_max"
+)";
+
+  CompileSuccessfully(GenerateShaderComputeCode(body, extra, defs),
+                      SPV_ENV_VULKAN_1_0);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateAtomics, AtomicMaxFloat64VulkanSuccess) {
+  const std::string defs = R"(
+%f64 = OpTypeFloat 64
+%f64_1 = OpConstant %f64 1
+%f64_ptr = OpTypePointer Workgroup %f64
+%f64_var = OpVariable %f64_ptr Workgroup
+)";
+  const std::string body = R"(
+%val1 = OpAtomicFMaxEXT %f64 %f64_var %device %relaxed %f64_1
+)";
+  const std::string extra = R"(
+OpCapability Float64
+OpCapability AtomicFloat64MinMaxEXT
+OpExtension "SPV_EXT_shader_atomic_float_min_max"
+)";
+
+  CompileSuccessfully(GenerateShaderComputeCode(body, extra, defs),
+                      SPV_ENV_VULKAN_1_0);
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
 }
 
@@ -374,12 +676,27 @@
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
 }
 
-TEST_F(ValidateAtomics, AtomicStoreFloatVulkan) {
+TEST_F(ValidateAtomics, AtomicStoreVulkanWrongStorageClass) {
   const std::string body = R"(
 OpAtomicStore %f32_var %device %relaxed %f32_1
 )";
 
   CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-None-04645"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("in Vulkan evironment, Workgroup Storage Class is limited to "
+                "MeshNV, TaskNV, and GLCompute execution model"));
+}
+
+TEST_F(ValidateAtomics, AtomicStoreFloatVulkan) {
+  const std::string body = R"(
+OpAtomicStore %f32_var %device %relaxed %f32_1
+)";
+
+  CompileSuccessfully(GenerateShaderComputeCode(body), SPV_ENV_VULKAN_1_0);
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
 }
 
@@ -388,7 +705,7 @@
 %val2 = OpAtomicExchange %f32 %f32_var %device %relaxed %f32_0
 )";
 
-  CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
+  CompileSuccessfully(GenerateShaderComputeCode(body), SPV_ENV_VULKAN_1_0);
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
 }
 
@@ -396,6 +713,7 @@
   const std::string body = R"(
   %val1 = OpAtomicLoad %u64 %u64_var %device %relaxed
   %val2 = OpAtomicLoad %u64 %u64_var %workgroup %acquire
+  %val3 = OpAtomicLoad %u64 %u64_var %invocation %relaxed
   )";
 
   CompileSuccessfully(
@@ -515,6 +833,21 @@
                 "Release, AcquireRelease and SequentiallyConsistent"));
 }
 
+TEST_F(ValidateAtomics, AtomicLoadVulkanInvocationSemantics) {
+  const std::string body = R"(
+%val1 = OpAtomicLoad %u32 %u32_var %invocation %acquire
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-None-04641"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("AtomicLoad: Vulkan specification requires Memory Semantics to "
+                "be None if used with Invocation Memory Scope"));
+}
+
 TEST_F(ValidateAtomics, AtomicLoadShaderFloat) {
   const std::string body = R"(
 %val1 = OpAtomicLoad %f32 %f32_var %device %relaxed
@@ -537,6 +870,45 @@
           "AtomicLoad: 64-bit atomics require the Int64Atomics capability"));
 }
 
+TEST_F(ValidateAtomics, AtomicLoadKernelInt64) {
+  const std::string body = R"(
+%val1 = OpAtomicLoad %u64 %u64_var %device %relaxed
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "AtomicLoad: 64-bit atomics require the Int64Atomics capability"));
+}
+
+TEST_F(ValidateAtomics, AtomicStoreVulkanInt64) {
+  const std::string body = R"(
+OpAtomicStore %u64_var %device %relaxed %u64_1
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "AtomicStore: 64-bit atomics require the Int64Atomics capability"));
+}
+
+TEST_F(ValidateAtomics, AtomicStoreKernelInt64) {
+  const std::string body = R"(
+OpAtomicStore %u64_var %device %relaxed %u64_1
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "AtomicStore: 64-bit atomics require the Int64Atomics capability"));
+}
+
 TEST_F(ValidateAtomics, VK_KHR_shader_atomic_int64Success) {
   const std::string body = R"(
 %val1 = OpAtomicUMin %u64 %u64_var %device %relaxed %u64_1
@@ -568,8 +940,9 @@
 OpAtomicStore %s64_var %device %relaxed %s64_1
 )";
 
-  CompileSuccessfully(GenerateShaderCode(body, "OpCapability Int64Atomics\n"),
-                      SPV_ENV_VULKAN_1_0);
+  CompileSuccessfully(
+      GenerateShaderComputeCode(body, "OpCapability Int64Atomics\n"),
+      SPV_ENV_VULKAN_1_0);
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
 }
 
@@ -593,9 +966,10 @@
 
   CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("AtomicLoad: "
-                        "expected Result Type to be int or float scalar type"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("AtomicLoad: "
+                "expected Result Type to be integer or float scalar type"));
 }
 
 TEST_F(ValidateAtomics, AtomicLoadWrongPointerType) {
@@ -668,9 +1042,10 @@
 TEST_F(ValidateAtomics, AtomicStoreVulkanSuccess) {
   const std::string body = R"(
 OpAtomicStore %u32_var %device %release %u32_1
+OpAtomicStore %u32_var %invocation %relaxed %u32_1
 )";
 
-  CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
+  CompileSuccessfully(GenerateShaderComputeCode(body), SPV_ENV_VULKAN_1_0);
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
 }
 
@@ -719,6 +1094,21 @@
                 "Acquire, AcquireRelease and SequentiallyConsistent"));
 }
 
+TEST_F(ValidateAtomics, AtomicStoreVulkanInvocationSemantics) {
+  const std::string body = R"(
+OpAtomicStore %u32_var %invocation %acquire %u32_1
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-None-04641"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("AtomicStore: Vulkan specification requires Memory Semantics "
+                "to be None if used with Invocation Memory Scope"));
+}
+
 TEST_F(ValidateAtomics, AtomicStoreWrongPointerType) {
   const std::string body = R"(
 OpAtomicStore %f32_1 %device %relaxed %f32_1
@@ -740,9 +1130,9 @@
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(
       getDiagnosticString(),
-      HasSubstr("AtomicStore: "
-                "expected Pointer to be a pointer to int or float scalar "
-                "type"));
+      HasSubstr(
+          "AtomicStore: "
+          "expected Pointer to be a pointer to integer or float scalar type"));
 }
 
 TEST_F(ValidateAtomics, AtomicStoreWrongPointerStorageTypeForOpenCL) {
@@ -848,9 +1238,10 @@
 
   CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("AtomicExchange: "
-                        "expected Result Type to be int or float scalar type"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("AtomicExchange: "
+                "expected Result Type to be integer or float scalar type"));
 }
 
 TEST_F(ValidateAtomics, AtomicExchangeWrongPointerType) {
@@ -918,6 +1309,22 @@
                         "expected Value to be of type Result Type"));
 }
 
+TEST_F(ValidateAtomics, AtomicExchangeVulkanInvocationSemantics) {
+  const std::string body = R"(
+OpAtomicStore %u32_var %invocation %relaxed %u32_1
+%val2 = OpAtomicExchange %u32 %u32_var %invocation %acquire %u32_0
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-None-04641"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("AtomicExchange: Vulkan specification requires Memory "
+                "Semantics to be None if used with Invocation Memory Scope"));
+}
+
 TEST_F(ValidateAtomics, AtomicCompareExchangeShaderSuccess) {
   const std::string body = R"(
 OpAtomicStore %u32_var %device %relaxed %u32_1
@@ -930,10 +1337,8 @@
 
 TEST_F(ValidateAtomics, AtomicCompareExchangeKernelSuccess) {
   const std::string body = R"(
-OpAtomicStore %f32_var %device %relaxed %f32_1
-%val2 = OpAtomicCompareExchange %f32 %f32_var %device %relaxed %relaxed %f32_0 %f32_1
 OpAtomicStore %u32_var %device %relaxed %u32_1
-%val4 = OpAtomicCompareExchange %u32 %u32_var %device %relaxed %relaxed %u32_0 %u32_0
+%val2 = OpAtomicCompareExchange %u32 %u32_var %device %relaxed %relaxed %u32_0 %u32_0
 )";
 
   CompileSuccessfully(GenerateKernelCode(body));
@@ -950,7 +1355,7 @@
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("AtomicCompareExchange: "
-                        "expected Result Type to be int scalar type"));
+                        "expected Result Type to be integer scalar type"));
 }
 
 TEST_F(ValidateAtomics, AtomicCompareExchangeWrongResultType) {
@@ -963,7 +1368,7 @@
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("AtomicCompareExchange: "
-                        "expected Result Type to be int or float scalar type"));
+                        "expected Result Type to be integer scalar type"));
 }
 
 TEST_F(ValidateAtomics, AtomicCompareExchangeWrongPointerType) {
@@ -981,7 +1386,7 @@
 TEST_F(ValidateAtomics, AtomicCompareExchangeWrongPointerDataType) {
   const std::string body = R"(
 OpStore %f32vec4_var %f32vec4_0000
-%val2 = OpAtomicCompareExchange %f32 %f32vec4_var %device %relaxed %relaxed %f32_0 %f32_1
+%val2 = OpAtomicCompareExchange %u32 %f32vec4_var %device %relaxed %relaxed %u32_0 %u32_0
 )";
 
   CompileSuccessfully(GenerateKernelCode(body));
@@ -994,11 +1399,11 @@
 
 TEST_F(ValidateAtomics, AtomicCompareExchangeWrongScopeType) {
   const std::string body = R"(
-OpAtomicStore %f32_var %device %relaxed %f32_1
-%val2 = OpAtomicCompareExchange %f32 %f32_var %f32_1 %relaxed %relaxed %f32_0 %f32_0
+OpAtomicStore %u64_var %device %relaxed %u64_1
+%val2 = OpAtomicCompareExchange %u64 %u64_var %u64_1 %relaxed %relaxed %u32_0 %u32_0
 )";
 
-  CompileSuccessfully(GenerateKernelCode(body));
+  CompileSuccessfully(GenerateKernelCode(body, "OpCapability Int64Atomics\n"));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("AtomicCompareExchange: expected scope to be a 32-bit "
@@ -1007,8 +1412,8 @@
 
 TEST_F(ValidateAtomics, AtomicCompareExchangeWrongMemorySemanticsType1) {
   const std::string body = R"(
-OpAtomicStore %f32_var %device %relaxed %f32_1
-%val2 = OpAtomicCompareExchange %f32 %f32_var %device %f32_1 %relaxed %f32_0 %f32_0
+OpAtomicStore %u32_var %device %relaxed %u32_1
+%val2 = OpAtomicCompareExchange %u32 %u32_var %device %f32_1 %relaxed %u32_0 %u32_0
 )";
 
   CompileSuccessfully(GenerateKernelCode(body));
@@ -1020,8 +1425,8 @@
 
 TEST_F(ValidateAtomics, AtomicCompareExchangeWrongMemorySemanticsType2) {
   const std::string body = R"(
-OpAtomicStore %f32_var %device %relaxed %f32_1
-%val2 = OpAtomicCompareExchange %f32 %f32_var %device %relaxed %f32_1 %f32_0 %f32_0
+OpAtomicStore %u32_var %device %relaxed %u32_1
+%val2 = OpAtomicCompareExchange %u32 %u32_var %device %relaxed %f32_1 %u32_0 %u32_0
 )";
 
   CompileSuccessfully(GenerateKernelCode(body));
@@ -1033,8 +1438,8 @@
 
 TEST_F(ValidateAtomics, AtomicCompareExchangeUnequalRelease) {
   const std::string body = R"(
-OpAtomicStore %f32_var %device %relaxed %f32_1
-%val2 = OpAtomicCompareExchange %f32 %f32_var %device %relaxed %release %f32_0 %f32_0
+OpAtomicStore %u32_var %device %relaxed %u32_1
+%val2 = OpAtomicCompareExchange %u32 %u32_var %device %relaxed %release %u32_0 %u32_0
 )";
 
   CompileSuccessfully(GenerateKernelCode(body));
@@ -1046,8 +1451,8 @@
 
 TEST_F(ValidateAtomics, AtomicCompareExchangeWrongValueType) {
   const std::string body = R"(
-OpAtomicStore %f32_var %device %relaxed %f32_1
-%val2 = OpAtomicCompareExchange %f32 %f32_var %device %relaxed %relaxed %u32_0 %f32_1
+OpAtomicStore %u32_var %device %relaxed %u32_1
+%val2 = OpAtomicCompareExchange %u32 %u32_var %device %relaxed %relaxed %f32_1 %u32_0
 )";
 
   CompileSuccessfully(GenerateKernelCode(body));
@@ -1059,8 +1464,8 @@
 
 TEST_F(ValidateAtomics, AtomicCompareExchangeWrongComparatorType) {
   const std::string body = R"(
-OpAtomicStore %f32_var %device %relaxed %f32_1
-%val2 = OpAtomicCompareExchange %f32 %f32_var %device %relaxed %relaxed %f32_0 %u32_1
+OpAtomicStore %u32_var %device %relaxed %u32_1
+%val2 = OpAtomicCompareExchange %u32 %u32_var %device %relaxed %relaxed %u32_0 %f32_0
 )";
 
   CompileSuccessfully(GenerateKernelCode(body));
@@ -1090,7 +1495,39 @@
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("AtomicCompareExchangeWeak: "
-                        "expected Result Type to be int scalar type"));
+                        "expected Result Type to be integer scalar type"));
+}
+
+TEST_F(ValidateAtomics, AtomicCompareExchangeVulkanInvocationSemanticsEqual) {
+  const std::string body = R"(
+OpAtomicStore %u32_var %device %relaxed %u32_1
+%val2 = OpAtomicCompareExchange %u32 %u32_var %invocation %release %relaxed %u32_0 %u32_0
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-None-04641"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("AtomicCompareExchange: Vulkan specification requires Memory "
+                "Semantics to be None if used with Invocation Memory Scope"));
+}
+
+TEST_F(ValidateAtomics, AtomicCompareExchangeVulkanInvocationSemanticsUnequal) {
+  const std::string body = R"(
+OpAtomicStore %u32_var %device %relaxed %u32_1
+%val2 = OpAtomicCompareExchange %u32 %u32_var %invocation %relaxed %acquire %u32_0 %u32_0
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-None-04641"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("AtomicCompareExchange: Vulkan specification requires Memory "
+                "Semantics to be None if used with Invocation Memory Scope"));
 }
 
 TEST_F(ValidateAtomics, AtomicArithmeticsSuccess) {
@@ -1157,7 +1594,7 @@
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr("AtomicFlagTestAndSet: "
-                "expected Pointer to point to a value of 32-bit int type"));
+                "expected Pointer to point to a value of 32-bit integer type"));
 }
 
 TEST_F(ValidateAtomics, AtomicFlagTestAndSetNotInt32Pointer) {
@@ -1165,12 +1602,12 @@
 %val1 = OpAtomicFlagTestAndSet %bool %u64_var %device %relaxed
 )";
 
-  CompileSuccessfully(GenerateKernelCode(body));
+  CompileSuccessfully(GenerateKernelCode(body, "OpCapability Int64Atomics\n"));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr("AtomicFlagTestAndSet: "
-                "expected Pointer to point to a value of 32-bit int type"));
+                "expected Pointer to point to a value of 32-bit integer type"));
 }
 
 TEST_F(ValidateAtomics, AtomicFlagTestAndSetWrongScopeType) {
@@ -1231,7 +1668,7 @@
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr("AtomicFlagClear: "
-                "expected Pointer to point to a value of 32-bit int type"));
+                "expected Pointer to point to a value of 32-bit integer type"));
 }
 
 TEST_F(ValidateAtomics, AtomicFlagClearNotInt32Pointer) {
@@ -1239,12 +1676,12 @@
 OpAtomicFlagClear %u64_var %device %relaxed
 )";
 
-  CompileSuccessfully(GenerateKernelCode(body));
+  CompileSuccessfully(GenerateKernelCode(body, "OpCapability Int64Atomics\n"));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr("AtomicFlagClear: "
-                "expected Pointer to point to a value of 32-bit int type"));
+                "expected Pointer to point to a value of 32-bit integer type"));
 }
 
 TEST_F(ValidateAtomics, AtomicFlagClearWrongScopeType) {
@@ -1342,7 +1779,7 @@
 OpExtension "SPV_KHR_vulkan_memory_model"
 )";
 
-  CompileSuccessfully(GenerateShaderCode(body, extra, "VulkanKHR"),
+  CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"),
                       SPV_ENV_UNIVERSAL_1_3);
   EXPECT_EQ(SPV_ERROR_INVALID_DATA,
             ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
@@ -1361,7 +1798,7 @@
 OpExtension "SPV_KHR_vulkan_memory_model"
 )";
 
-  CompileSuccessfully(GenerateShaderCode(body, extra, "VulkanKHR"),
+  CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"),
                       SPV_ENV_UNIVERSAL_1_3);
   EXPECT_EQ(SPV_ERROR_INVALID_DATA,
             ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
@@ -1381,7 +1818,7 @@
 OpExtension "SPV_KHR_vulkan_memory_model"
 )";
 
-  CompileSuccessfully(GenerateShaderCode(body, extra, "VulkanKHR"),
+  CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"),
                       SPV_ENV_UNIVERSAL_1_3);
   EXPECT_EQ(SPV_ERROR_INVALID_DATA,
             ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
@@ -1401,7 +1838,7 @@
 OpExtension "SPV_KHR_vulkan_memory_model"
 )";
 
-  CompileSuccessfully(GenerateShaderCode(body, extra, "VulkanKHR"),
+  CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"),
                       SPV_ENV_UNIVERSAL_1_3);
   EXPECT_EQ(SPV_ERROR_INVALID_DATA,
             ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
@@ -1421,7 +1858,7 @@
 OpExtension "SPV_KHR_vulkan_memory_model"
 )";
 
-  CompileSuccessfully(GenerateShaderCode(body, extra, "VulkanKHR"),
+  CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"),
                       SPV_ENV_UNIVERSAL_1_3);
   EXPECT_EQ(SPV_ERROR_INVALID_DATA,
             ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
@@ -1441,7 +1878,7 @@
 OpExtension "SPV_KHR_vulkan_memory_model"
 )";
 
-  CompileSuccessfully(GenerateShaderCode(body, extra, "VulkanKHR"),
+  CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"),
                       SPV_ENV_UNIVERSAL_1_3);
   EXPECT_EQ(SPV_ERROR_INVALID_DATA,
             ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
@@ -1461,7 +1898,7 @@
 OpExtension "SPV_KHR_vulkan_memory_model"
 )";
 
-  CompileSuccessfully(GenerateShaderCode(body, extra, "VulkanKHR"),
+  CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"),
                       SPV_ENV_UNIVERSAL_1_3);
   EXPECT_EQ(SPV_ERROR_INVALID_DATA,
             ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
@@ -1480,7 +1917,7 @@
 OpExtension "SPV_KHR_vulkan_memory_model"
 )";
 
-  CompileSuccessfully(GenerateShaderCode(body, extra, "VulkanKHR"),
+  CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"),
                       SPV_ENV_UNIVERSAL_1_3);
   EXPECT_EQ(SPV_ERROR_INVALID_DATA,
             ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
@@ -1499,7 +1936,7 @@
 OpExtension "SPV_KHR_vulkan_memory_model"
 )";
 
-  CompileSuccessfully(GenerateShaderCode(body, extra, "VulkanKHR"),
+  CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"),
                       SPV_ENV_UNIVERSAL_1_3);
   EXPECT_EQ(SPV_ERROR_INVALID_DATA,
             ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
@@ -1518,7 +1955,7 @@
 OpExtension "SPV_KHR_vulkan_memory_model"
 )";
 
-  CompileSuccessfully(GenerateShaderCode(body, extra, "VulkanKHR"),
+  CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"),
                       SPV_ENV_UNIVERSAL_1_3);
   EXPECT_EQ(SPV_ERROR_INVALID_DATA,
             ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
@@ -1537,7 +1974,28 @@
 OpExtension "SPV_KHR_vulkan_memory_model"
 )";
 
-  CompileSuccessfully(GenerateShaderCode(body, extra, "VulkanKHR"),
+  CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"),
+                      SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("SequentiallyConsistent memory semantics cannot be "
+                        "used with the VulkanKHR memory model."));
+}
+
+TEST_F(ValidateAtomics, VulkanMemoryModelBanSequentiallyConsistentAtomicFMinEXT) {
+  const std::string body = R"(
+%max = OpAtomicFMinEXT %f32 %f32_var %workgroup %sequentially_consistent %f32_0
+)";
+
+  const std::string extra = R"(
+OpCapability VulkanMemoryModelKHR
+OpCapability AtomicFloat32MinMaxEXT
+OpExtension "SPV_KHR_vulkan_memory_model"
+OpExtension "SPV_EXT_shader_atomic_float_min_max"
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"),
                       SPV_ENV_UNIVERSAL_1_3);
   EXPECT_EQ(SPV_ERROR_INVALID_DATA,
             ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
@@ -1556,7 +2014,7 @@
 OpExtension "SPV_KHR_vulkan_memory_model"
 )";
 
-  CompileSuccessfully(GenerateShaderCode(body, extra, "VulkanKHR"),
+  CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"),
                       SPV_ENV_UNIVERSAL_1_3);
   EXPECT_EQ(SPV_ERROR_INVALID_DATA,
             ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
@@ -1575,7 +2033,28 @@
 OpExtension "SPV_KHR_vulkan_memory_model"
 )";
 
-  CompileSuccessfully(GenerateShaderCode(body, extra, "VulkanKHR"),
+  CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"),
+                      SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("SequentiallyConsistent memory semantics cannot be "
+                        "used with the VulkanKHR memory model."));
+}
+
+TEST_F(ValidateAtomics, VulkanMemoryModelBanSequentiallyConsistentAtomicFMaxEXT) {
+  const std::string body = R"(
+%max = OpAtomicFMaxEXT %f32 %f32_var %workgroup %sequentially_consistent %f32_0
+)";
+
+  const std::string extra = R"(
+OpCapability VulkanMemoryModelKHR
+OpCapability AtomicFloat32MinMaxEXT
+OpExtension "SPV_KHR_vulkan_memory_model"
+OpExtension "SPV_EXT_shader_atomic_float_min_max"
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"),
                       SPV_ENV_UNIVERSAL_1_3);
   EXPECT_EQ(SPV_ERROR_INVALID_DATA,
             ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
@@ -1594,7 +2073,7 @@
 OpExtension "SPV_KHR_vulkan_memory_model"
 )";
 
-  CompileSuccessfully(GenerateShaderCode(body, extra, "VulkanKHR"),
+  CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"),
                       SPV_ENV_UNIVERSAL_1_3);
   EXPECT_EQ(SPV_ERROR_INVALID_DATA,
             ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
@@ -1613,7 +2092,7 @@
 OpExtension "SPV_KHR_vulkan_memory_model"
 )";
 
-  CompileSuccessfully(GenerateShaderCode(body, extra, "VulkanKHR"),
+  CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"),
                       SPV_ENV_UNIVERSAL_1_3);
   EXPECT_EQ(SPV_ERROR_INVALID_DATA,
             ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
@@ -1632,7 +2111,7 @@
 OpExtension "SPV_KHR_vulkan_memory_model"
 )";
 
-  CompileSuccessfully(GenerateShaderCode(body, extra, "VulkanKHR"),
+  CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"),
                       SPV_ENV_UNIVERSAL_1_3);
   EXPECT_EQ(SPV_ERROR_INVALID_DATA,
             ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
@@ -1856,7 +2335,7 @@
 OpExtension "SPV_KHR_vulkan_memory_model"
 )";
 
-  CompileSuccessfully(GenerateShaderCode(body, extra, "VulkanKHR"),
+  CompileSuccessfully(GenerateShaderComputeCode(body, extra, "", "VulkanKHR"),
                       SPV_ENV_VULKAN_1_1);
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1));
 }
@@ -1984,7 +2463,7 @@
 OpExtension "SPV_KHR_vulkan_memory_model"
 )";
 
-  CompileSuccessfully(GenerateShaderCode(body, extra, "VulkanKHR"),
+  CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"),
                       SPV_ENV_UNIVERSAL_1_3);
   EXPECT_EQ(SPV_ERROR_INVALID_DATA,
             ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
@@ -2004,7 +2483,7 @@
 OpExtension "SPV_KHR_vulkan_memory_model"
 )";
 
-  CompileSuccessfully(GenerateShaderCode(body, extra, "VulkanKHR"),
+  CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"),
                       SPV_ENV_UNIVERSAL_1_3);
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
 }
diff --git a/test/val/val_barriers_test.cpp b/test/val/val_barriers_test.cpp
index 46f5b5a..1178ca0 100644
--- a/test/val/val_barriers_test.cpp
+++ b/test/val/val_barriers_test.cpp
@@ -346,6 +346,8 @@
   CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
   EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-None-04636"));
+  EXPECT_THAT(getDiagnosticString(),
               HasSubstr("ControlBarrier: in Vulkan environment Execution Scope "
                         "is limited to Workgroup and Subgroup"));
 }
@@ -388,9 +390,10 @@
                         "cannot be CrossDevice"));
 }
 
-TEST_F(ValidateBarriers, OpControlBarrierVulkan1p1WorkgroupNonComputeFailure) {
+TEST_F(ValidateBarriers,
+       OpControlBarrierVulkan1p1WorkgroupNonComputeMemoryFailure) {
   const std::string body = R"(
-OpControlBarrier %workgroup %workgroup %acquire
+OpControlBarrier %subgroup %workgroup %acquire
 )";
 
   CompileSuccessfully(GenerateVulkanVertexShaderCode(body), SPV_ENV_VULKAN_1_1);
@@ -402,7 +405,23 @@
                         "and GLCompute execution model"));
 }
 
-TEST_F(ValidateBarriers, OpControlBarrierVulkan1p1WorkgroupNonComputeSuccess) {
+TEST_F(ValidateBarriers,
+       OpControlBarrierVulkan1p1WorkgroupNonComputeExecutionFailure) {
+  const std::string body = R"(
+OpControlBarrier %workgroup %subgroup %acquire
+)";
+
+  CompileSuccessfully(GenerateVulkanVertexShaderCode(body), SPV_ENV_VULKAN_1_1);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-None-04637"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("in Vulkan environment, Workgroup execution scope is "
+                        "only for TaskNV, MeshNV, TessellationControl, and "
+                        "GLCompute execution models"));
+}
+
+TEST_F(ValidateBarriers, OpControlBarrierVulkan1p1WorkgroupComputeSuccess) {
   const std::string body = R"(
 OpControlBarrier %workgroup %workgroup %acquire
 )";
@@ -411,6 +430,39 @@
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1));
 }
 
+TEST_F(ValidateBarriers, OpControlBarrierVulkan1p1WorkgroupNonComputeSuccess) {
+  const std::string body = R"(
+OpControlBarrier %subgroup %subgroup %acquire
+)";
+
+  CompileSuccessfully(GenerateVulkanVertexShaderCode(body), SPV_ENV_VULKAN_1_1);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+}
+
+TEST_F(ValidateBarriers, OpControlBarrierVulkanInvocationSuccess) {
+  const std::string body = R"(
+OpControlBarrier %workgroup %invocation %none
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateBarriers, OpControlBarrierVulkanInvocationFailure) {
+  const std::string body = R"(
+OpControlBarrier %workgroup %invocation %acquire
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-None-04641"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("ControlBarrier: Vulkan specification requires Memory "
+                "Semantics to be None if used with Invocation Memory Scope"));
+}
+
 TEST_F(ValidateBarriers, OpControlBarrierAcquireAndRelease) {
   const std::string body = R"(
 OpControlBarrier %device %device %acquire_and_release_uniform
@@ -459,9 +511,13 @@
                       SPV_ENV_VULKAN_1_1);
   ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
   EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("OpControlBarrier execution scope must be Subgroup for "
-                        "Fragment, Vertex, Geometry and TessellationEvaluation "
-                        "execution models"));
+              AnyVUID("VUID-StandaloneSpirv-OpControlBarrier-04682"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "OpControlBarrier execution scope must be Subgroup for Fragment, "
+          "Vertex, Geometry, TessellationEvaluation, RayGeneration, "
+          "Intersection, AnyHit, ClosestHit, and Miss execution models"));
 }
 
 TEST_F(ValidateBarriers, OpControlBarrierSubgroupExecutionFragment1p0) {
@@ -497,9 +553,13 @@
                       SPV_ENV_VULKAN_1_1);
   ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
   EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("OpControlBarrier execution scope must be Subgroup for "
-                        "Fragment, Vertex, Geometry and TessellationEvaluation "
-                        "execution models"));
+              AnyVUID("VUID-StandaloneSpirv-OpControlBarrier-04682"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "OpControlBarrier execution scope must be Subgroup for Fragment, "
+          "Vertex, Geometry, TessellationEvaluation, RayGeneration, "
+          "Intersection, AnyHit, ClosestHit, and Miss execution models"));
 }
 
 TEST_F(ValidateBarriers, OpControlBarrierSubgroupExecutionVertex1p0) {
@@ -537,9 +597,13 @@
       SPV_ENV_VULKAN_1_1);
   ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
   EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("OpControlBarrier execution scope must be Subgroup for "
-                        "Fragment, Vertex, Geometry and TessellationEvaluation "
-                        "execution models"));
+              AnyVUID("VUID-StandaloneSpirv-OpControlBarrier-04682"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "OpControlBarrier execution scope must be Subgroup for Fragment, "
+          "Vertex, Geometry, TessellationEvaluation, RayGeneration, "
+          "Intersection, AnyHit, ClosestHit, and Miss execution models"));
 }
 
 TEST_F(ValidateBarriers, OpControlBarrierSubgroupExecutionGeometry1p0) {
@@ -580,9 +644,13 @@
                       SPV_ENV_VULKAN_1_1);
   ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
   EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("OpControlBarrier execution scope must be Subgroup for "
-                        "Fragment, Vertex, Geometry and TessellationEvaluation "
-                        "execution models"));
+              AnyVUID("VUID-StandaloneSpirv-OpControlBarrier-04682"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "OpControlBarrier execution scope must be Subgroup for Fragment, "
+          "Vertex, Geometry, TessellationEvaluation, RayGeneration, "
+          "Intersection, AnyHit, ClosestHit, and Miss execution models"));
 }
 
 TEST_F(ValidateBarriers,
@@ -1464,6 +1532,8 @@
 
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_1));
   EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-None-04636"));
+  EXPECT_THAT(getDiagnosticString(),
               HasSubstr("in Vulkan environment Execution Scope is limited to "
                         "Workgroup and Subgroup"));
 }
diff --git a/test/val/val_builtins_test.cpp b/test/val/val_builtins_test.cpp
index bbcdbb1..2116fff 100644
--- a/test/val/val_builtins_test.cpp
+++ b/test/val/val_builtins_test.cpp
@@ -3658,23 +3658,25 @@
 
 INSTANTIATE_TEST_SUITE_P(
     SubgroupInvocationIdAndSizeNotU32, ValidateVulkanSubgroupBuiltIns,
-    Combine(Values("SubgroupLocalInvocationId", "SubgroupSize"),
-            Values("GLCompute"), Values("Input"), Values("%f32"),
-            Values("VUID-SubgroupLocalInvocationId-SubgroupLocalInvocationId-"
-                   "04381 VUID-SubgroupSize-SubgroupSize-04383"),
-            Values(TestResult(SPV_ERROR_INVALID_DATA,
-                              "needs to be a 32-bit int"))));
+    Combine(
+        Values("SubgroupLocalInvocationId", "SubgroupSize"),
+        Values("GLCompute"), Values("Input"), Values("%f32"),
+        Values("VUID-SubgroupLocalInvocationId-SubgroupLocalInvocationId-04381 "
+               "VUID-SubgroupSize-SubgroupSize-04383"),
+        Values(TestResult(SPV_ERROR_INVALID_DATA,
+                          "needs to be a 32-bit int"))));
 
 INSTANTIATE_TEST_SUITE_P(
     SubgroupInvocationIdAndSizeNotInput, ValidateVulkanSubgroupBuiltIns,
-    Combine(Values("SubgroupLocalInvocationId", "SubgroupSize"),
-            Values("GLCompute"), Values("Output", "Workgroup", "Private"),
-            Values("%u32"),
-            Values("VUID-SubgroupLocalInvocationId-SubgroupLocalInvocationId-"
-                   "04380 VUID-SubgroupSize-SubgroupSize-04382"),
-            Values(TestResult(
-                SPV_ERROR_INVALID_DATA,
-                "to be only used for variables with Input storage class"))));
+    Combine(
+        Values("SubgroupLocalInvocationId", "SubgroupSize"),
+        Values("GLCompute"), Values("Output", "Workgroup", "Private"),
+        Values("%u32"),
+        Values("VUID-SubgroupLocalInvocationId-SubgroupLocalInvocationId-04380 "
+               "VUID-SubgroupSize-SubgroupSize-04382"),
+        Values(TestResult(
+            SPV_ERROR_INVALID_DATA,
+            "to be only used for variables with Input storage class"))));
 
 INSTANTIATE_TEST_SUITE_P(
     SubgroupInvocationIdAndSizeOk, ValidateVulkanSubgroupBuiltIns,
diff --git a/test/val/val_cfg_test.cpp b/test/val/val_cfg_test.cpp
index 9698fb1..6ae2ee6 100644
--- a/test/val/val_cfg_test.cpp
+++ b/test/val/val_cfg_test.cpp
@@ -3520,7 +3520,10 @@
 
   CompileSuccessfully(text);
   EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(), HasSubstr("Selection must be structured"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "OpSwitch must be preceeded by an OpSelectionMerge instruction"));
 }
 
 TEST_F(ValidateCFG, MissingMergeSwitchBad2) {
@@ -3544,7 +3547,10 @@
 
   CompileSuccessfully(text);
   EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(), HasSubstr("Selection must be structured"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "OpSwitch must be preceeded by an OpSelectionMerge instruction"));
 }
 
 TEST_F(ValidateCFG, MissingMergeOneBranchToMergeGood) {
@@ -3594,7 +3600,7 @@
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
-TEST_F(ValidateCFG, MissingMergeOneTargetSwitchGood) {
+TEST_F(ValidateCFG, MissingMergeOneTargetSwitchBad) {
   const std::string text = R"(
 OpCapability Shader
 OpCapability Linkage
@@ -3612,10 +3618,14 @@
 )";
 
   CompileSuccessfully(text);
-  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+  EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "OpSwitch must be preceeded by an OpSelectionMerge instruction"));
 }
 
-TEST_F(ValidateCFG, MissingMergeOneUnseenTargetSwitchGood) {
+TEST_F(ValidateCFG, MissingMergeOneUnseenTargetSwitchBad) {
   const std::string text = R"(
 OpCapability Shader
 OpCapability Linkage
@@ -3640,7 +3650,11 @@
 )";
 
   CompileSuccessfully(text);
-  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+  EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "OpSwitch must be preceeded by an OpSelectionMerge instruction"));
 }
 
 TEST_F(ValidateCFG, MissingMergeLoopBreakGood) {
@@ -4350,6 +4364,41 @@
               HasSubstr("OpPhi must not have void result type"));
 }
 
+TEST_F(ValidateCFG, InvalidExitSingleBlockLoop) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpName %5 "BAD"
+%void = OpTypeVoid
+%bool = OpTypeBool
+%undef = OpUndef %bool
+%void_fn = OpTypeFunction %void
+%fn = OpFunction %void None %void_fn
+%1 = OpLabel
+OpBranch %2
+%2 = OpLabel
+OpLoopMerge %3 %4 None
+OpBranchConditional %undef %3 %5
+%5 = OpLabel
+OpLoopMerge %6 %5 None
+OpBranchConditional %undef %5 %4
+%6 = OpLabel
+OpReturn
+%4 = OpLabel
+OpBranch %2
+%3 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text);
+  EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("block <ID> 1[%BAD] exits the continue headed by <ID> "
+                        "1[%BAD], but not via a structured exit"));
+}
+
 }  // namespace
 }  // namespace val
 }  // namespace spvtools
diff --git a/test/val/val_decoration_test.cpp b/test/val/val_decoration_test.cpp
index cbf6d7c..096fd17 100644
--- a/test/val/val_decoration_test.cpp
+++ b/test/val/val_decoration_test.cpp
@@ -4992,6 +4992,8 @@
   EXPECT_EQ(SPV_ERROR_INVALID_DATA,
             ValidateInstructions(SPV_ENV_VULKAN_1_1_SPIRV_1_4));
   EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-None-04636"));
+  EXPECT_THAT(getDiagnosticString(),
               HasSubstr(": in Vulkan environment Execution Scope is limited to "
                         "Workgroup and Subgroup"));
 }
diff --git a/test/val/val_ext_inst_test.cpp b/test/val/val_ext_inst_test.cpp
index 683a76f..b73ec34 100644
--- a/test/val/val_ext_inst_test.cpp
+++ b/test/val/val_ext_inst_test.cpp
@@ -5204,6 +5204,49 @@
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
+TEST_F(ValidateExtInst, GlslStd450InterpolateAtCentroidInternalSuccess) {
+  const std::string body = R"(
+%ld1  = OpLoad %f32 %f32_input
+%val1 = OpExtInst %f32 %extinst InterpolateAtCentroid %ld1
+%ld2  = OpLoad %f32vec2 %f32vec2_input
+%val2 = OpExtInst %f32vec2 %extinst InterpolateAtCentroid %ld2
+)";
+
+  CompileSuccessfully(
+      GenerateShaderCode(body, "OpCapability InterpolationFunction\n"));
+  getValidatorOptions()->before_hlsl_legalization = true;
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateExtInst, GlslStd450InterpolateAtCentroidInternalInvalidDataF32) {
+  const std::string body = R"(
+%ld1  = OpLoad %f32 %f32_input
+%val1 = OpExtInst %f32 %extinst InterpolateAtCentroid %ld1
+)";
+
+  CompileSuccessfully(
+      GenerateShaderCode(body, "OpCapability InterpolationFunction\n"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 InterpolateAtCentroid: "
+                        "expected Interpolant to be a pointer"));
+}
+
+TEST_F(ValidateExtInst,
+       GlslStd450InterpolateAtCentroidInternalInvalidDataF32Vec2) {
+  const std::string body = R"(
+%ld2  = OpLoad %f32vec2 %f32vec2_input
+%val2 = OpExtInst %f32vec2 %extinst InterpolateAtCentroid %ld2
+)";
+
+  CompileSuccessfully(
+      GenerateShaderCode(body, "OpCapability InterpolationFunction\n"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 InterpolateAtCentroid: "
+                        "expected Interpolant to be a pointer"));
+}
+
 TEST_F(ValidateExtInst, GlslStd450InterpolateAtCentroidNoCapability) {
   const std::string body = R"(
 %val1 = OpExtInst %f32 %extinst InterpolateAtCentroid %f32_input
@@ -5308,6 +5351,49 @@
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
+TEST_F(ValidateExtInst, GlslStd450InterpolateAtSampleInternalSuccess) {
+  const std::string body = R"(
+%ld1  = OpLoad %f32 %f32_input
+%val1 = OpExtInst %f32 %extinst InterpolateAtSample %ld1 %u32_1
+%ld2  = OpLoad %f32vec2 %f32vec2_input
+%val2 = OpExtInst %f32vec2 %extinst InterpolateAtSample %ld2 %u32_1
+)";
+
+  CompileSuccessfully(
+      GenerateShaderCode(body, "OpCapability InterpolationFunction\n"));
+  getValidatorOptions()->before_hlsl_legalization = true;
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateExtInst, GlslStd450InterpolateAtSampleInternalInvalidDataF32) {
+  const std::string body = R"(
+%ld1  = OpLoad %f32 %f32_input
+%val1 = OpExtInst %f32 %extinst InterpolateAtSample %ld1 %u32_1
+)";
+
+  CompileSuccessfully(
+      GenerateShaderCode(body, "OpCapability InterpolationFunction\n"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 InterpolateAtSample: "
+                        "expected Interpolant to be a pointer"));
+}
+
+TEST_F(ValidateExtInst,
+       GlslStd450InterpolateAtSampleInternalInvalidDataF32Vec2) {
+  const std::string body = R"(
+%ld2  = OpLoad %f32vec2 %f32vec2_input
+%val2 = OpExtInst %f32vec2 %extinst InterpolateAtSample %ld2 %u32_1
+)";
+
+  CompileSuccessfully(
+      GenerateShaderCode(body, "OpCapability InterpolationFunction\n"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 InterpolateAtSample: "
+                        "expected Interpolant to be a pointer"));
+}
+
 TEST_F(ValidateExtInst, GlslStd450InterpolateAtSampleNoCapability) {
   const std::string body = R"(
 %val1 = OpExtInst %f32 %extinst InterpolateAtSample %f32_input %u32_1
@@ -5438,6 +5524,48 @@
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
+TEST_F(ValidateExtInst, GlslStd450InterpolateAtOffsetInternalSuccess) {
+  const std::string body = R"(
+%ld1  = OpLoad %f32 %f32_input
+%val1 = OpExtInst %f32 %extinst InterpolateAtOffset %ld1 %f32vec2_01
+%ld2  = OpLoad %f32vec2 %f32vec2_input
+%val2 = OpExtInst %f32vec2 %extinst InterpolateAtOffset %ld2 %f32vec2_01
+)";
+
+  CompileSuccessfully(
+      GenerateShaderCode(body, "OpCapability InterpolationFunction\n"));
+  getValidatorOptions()->before_hlsl_legalization = true;
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateExtInst, GlslStd450InterpolateAtOffsetInternalInvalidDataF32) {
+  const std::string body = R"(
+%ld1  = OpLoad %f32 %f32_input
+%val1 = OpExtInst %f32 %extinst InterpolateAtOffset %ld1 %f32vec2_01
+)";
+
+  CompileSuccessfully(
+      GenerateShaderCode(body, "OpCapability InterpolationFunction\n"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 InterpolateAtOffset: "
+                        "expected Interpolant to be a pointer"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450InterpolateAtOffsetInternalInvalidDataF32Vec2) {
+  const std::string body = R"(
+%ld2  = OpLoad %f32vec2 %f32vec2_input
+%val2 = OpExtInst %f32vec2 %extinst InterpolateAtOffset %ld2 %f32vec2_01
+)";
+
+  CompileSuccessfully(
+      GenerateShaderCode(body, "OpCapability InterpolationFunction\n"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 InterpolateAtOffset: "
+                        "expected Interpolant to be a pointer"));
+}
+
 TEST_F(ValidateExtInst, GlslStd450InterpolateAtOffsetNoCapability) {
   const std::string body = R"(
 %val1 = OpExtInst %f32 %extinst InterpolateAtOffset %f32_input %f32vec2_01
diff --git a/test/val/val_extension_spv_khr_expect_assume.cpp b/test/val/val_extension_spv_khr_expect_assume.cpp
new file mode 100644
index 0000000..6ece15d
--- /dev/null
+++ b/test/val/val_extension_spv_khr_expect_assume.cpp
@@ -0,0 +1,310 @@
+// Copyright (c) 2020 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.
+
+// Tests for OpExtension validator rules.
+
+#include <string>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "source/enum_string_mapping.h"
+#include "source/extensions.h"
+#include "source/spirv_target_env.h"
+#include "test/test_fixture.h"
+#include "test/unit_spirv.h"
+#include "test/val/val_fixtures.h"
+
+namespace spvtools {
+namespace val {
+namespace {
+
+using ::testing::HasSubstr;
+using ::testing::Values;
+using ::testing::ValuesIn;
+
+using ValidateSpvExpectAssumeKHR = spvtest::ValidateBase<bool>;
+
+TEST_F(ValidateSpvExpectAssumeKHR, Valid) {
+  const std::string str = R"(
+    OpCapability Kernel
+    OpCapability Addresses
+    OpCapability Linkage
+    OpCapability ExpectAssumeKHR
+    OpExtension "SPV_KHR_expect_assume"
+    OpMemoryModel Physical32 OpenCL
+
+    %void = OpTypeVoid
+    %voidfn = OpTypeFunction %void
+
+    %bool = OpTypeBool
+    %true = OpConstantTrue %bool
+    %undef = OpUndef %bool
+
+    %uint = OpTypeInt 32 0
+    %uint_1 = OpConstant %uint 1
+    %uint_2 = OpConstant %uint 2
+
+    %v2bool = OpTypeVector %bool 2
+    %v2uint = OpTypeVector %uint 2
+
+    %null_v2bool = OpConstantNull %v2bool
+    %null_v2uint = OpConstantNull %v2uint
+
+    %main = OpFunction %void None %voidfn
+    %entry = OpLabel
+    OpAssumeTrueKHR %true
+    OpAssumeTrueKHR %undef       ; probably undefined behaviour
+    %bool_val = OpExpectKHR %bool %true %true
+    %uint_val = OpExpectKHR %uint %uint_1 %uint_2 ; a bad expectation
+    %v2bool_val = OpExpectKHR %v2bool %null_v2bool %null_v2bool
+    %v2uint_val = OpExpectKHR %v2uint %null_v2uint %null_v2uint
+    OpReturn
+    OpFunctionEnd
+
+)";
+  CompileSuccessfully(str.c_str());
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateSpvExpectAssumeKHR, RequiresExtension) {
+  const std::string str = R"(
+    OpCapability Kernel
+    OpCapability Addresses
+    OpCapability Linkage
+    OpCapability ExpectAssumeKHR
+    OpMemoryModel Physical32 OpenCL
+
+    %void = OpTypeVoid
+    %voidfn = OpTypeFunction %void
+
+    %bool = OpTypeBool
+    %true = OpConstantTrue %bool
+    %undef = OpUndef %bool
+
+    %uint = OpTypeInt 32 0
+    %uint_1 = OpConstant %uint 1
+    %uint_2 = OpConstant %uint 2
+
+    %main = OpFunction %void None %voidfn
+    %entry = OpLabel
+    OpAssumeTrueKHR %true
+    OpAssumeTrueKHR %undef       ; probably undefined behaviour
+    %val = OpExpectKHR %uint %uint_1 %uint_2 ; a bad expectation
+    OpReturn
+    OpFunctionEnd
+
+)";
+  CompileSuccessfully(str.c_str());
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Capability: operand ExpectAssumeKHR(5629) requires "
+                        "one of these extensions: SPV_KHR_expect_assume"));
+}
+
+TEST_F(ValidateSpvExpectAssumeKHR,
+       AssumeTrueKHR_RequiresExpectAssumeCapability) {
+  const std::string str = R"(
+    OpCapability Kernel
+    OpCapability Addresses
+    OpCapability Linkage
+    OpExtension "SPV_KHR_expect_assume"
+    OpMemoryModel Physical32 OpenCL
+
+    %void = OpTypeVoid
+    %voidfn = OpTypeFunction %void
+
+    %bool = OpTypeBool
+    %true = OpConstantTrue %bool
+    %undef = OpUndef %bool
+
+    %uint = OpTypeInt 32 0
+    %uint_1 = OpConstant %uint 1
+    %uint_2 = OpConstant %uint 2
+
+    %main = OpFunction %void None %voidfn
+    %entry = OpLabel
+    OpAssumeTrueKHR %true
+    OpAssumeTrueKHR %undef       ; probably undefined behaviour
+    OpReturn
+    OpFunctionEnd
+)";
+  CompileSuccessfully(str.c_str());
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Opcode AssumeTrueKHR requires one of these "
+                        "capabilities: ExpectAssumeKHR \n"
+                        "  OpAssumeTrueKHR %true\n"));
+}
+
+TEST_F(ValidateSpvExpectAssumeKHR, AssumeTrueKHR_OperandMustBeBool) {
+  const std::string str = R"(
+    OpCapability Kernel
+    OpCapability Addresses
+    OpCapability Linkage
+    OpCapability ExpectAssumeKHR
+    OpExtension "SPV_KHR_expect_assume"
+    OpMemoryModel Physical32 OpenCL
+
+    %void = OpTypeVoid
+    %voidfn = OpTypeFunction %void
+
+    %bool = OpTypeBool
+    %true = OpConstantTrue %bool
+    %undef = OpUndef %bool
+
+    %uint = OpTypeInt 32 0
+    %uint_1 = OpConstant %uint 1
+    %uint_2 = OpConstant %uint 2
+
+    %main = OpFunction %void None %voidfn
+    %entry = OpLabel
+    OpAssumeTrueKHR %uint_1 ; bad type
+    OpReturn
+    OpFunctionEnd
+)";
+  CompileSuccessfully(str.c_str());
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Value operand of OpAssumeTrueKHR must be a boolean scalar\n"
+                "  OpAssumeTrueKHR %uint_1\n"));
+}
+
+TEST_F(ValidateSpvExpectAssumeKHR, ExpectKHR_RequiresExpectAssumeCapability) {
+  const std::string str = R"(
+    OpCapability Kernel
+    OpCapability Addresses
+    OpCapability Linkage
+    OpExtension "SPV_KHR_expect_assume"
+    OpMemoryModel Physical32 OpenCL
+
+    %void = OpTypeVoid
+    %voidfn = OpTypeFunction %void
+
+    %bool = OpTypeBool
+    %true = OpConstantTrue %bool
+    %undef = OpUndef %bool
+
+    %uint = OpTypeInt 32 0
+    %uint_1 = OpConstant %uint 1
+    %uint_2 = OpConstant %uint 2
+
+    %main = OpFunction %void None %voidfn
+    %entry = OpLabel
+    %val = OpExpectKHR %uint %uint_1 %uint_2 ; a bad expectation
+    OpReturn
+    OpFunctionEnd
+)";
+  CompileSuccessfully(str.c_str());
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Opcode ExpectKHR requires one of these capabilities: "
+                        "ExpectAssumeKHR \n"
+                        "  %11 = OpExpectKHR %uint %uint_1 %uint_2\n"));
+}
+
+TEST_F(ValidateSpvExpectAssumeKHR, ExpectKHR_ResultMustBeBoolOrIntScalar) {
+  const std::string str = R"(
+    OpCapability Kernel
+    OpCapability Addresses
+    OpCapability Linkage
+    OpCapability ExpectAssumeKHR
+    OpExtension "SPV_KHR_expect_assume"
+    OpMemoryModel Physical32 OpenCL
+
+    %void = OpTypeVoid
+    %voidfn = OpTypeFunction %void
+
+    %float = OpTypeFloat 32
+
+    %float_0 = OpConstant %float 0
+
+    %main = OpFunction %void None %voidfn
+    %entry = OpLabel
+    %val = OpExpectKHR %float %float_0 %float_0
+    OpReturn
+    OpFunctionEnd
+)";
+  CompileSuccessfully(str.c_str());
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Result of OpExpectKHR must be a scalar or vector of "
+                        "integer type or boolean type\n"
+                        "  %7 = OpExpectKHR %float %float_0 %float_0\n"));
+}
+
+TEST_F(ValidateSpvExpectAssumeKHR, ExpectKHR_Value0MustMatchResultType) {
+  const std::string str = R"(
+    OpCapability Kernel
+    OpCapability Addresses
+    OpCapability Linkage
+    OpCapability ExpectAssumeKHR
+    OpExtension "SPV_KHR_expect_assume"
+    OpMemoryModel Physical32 OpenCL
+
+    %void = OpTypeVoid
+    %voidfn = OpTypeFunction %void
+
+    %uint = OpTypeInt 32 0
+    %float = OpTypeFloat 32
+    %float_0 = OpConstant %float 0
+
+    %main = OpFunction %void None %voidfn
+    %entry = OpLabel
+    %val = OpExpectKHR %uint %float_0 %float_0
+    OpReturn
+    OpFunctionEnd
+)";
+  CompileSuccessfully(str.c_str());
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Type of Value operand of OpExpectKHR does not match "
+                        "the result type \n"
+                        "  %8 = OpExpectKHR %uint %float_0 %float_0\n"));
+}
+
+TEST_F(ValidateSpvExpectAssumeKHR, ExpectKHR_Value1MustMatchResultType) {
+  const std::string str = R"(
+    OpCapability Kernel
+    OpCapability Addresses
+    OpCapability Linkage
+    OpCapability ExpectAssumeKHR
+    OpExtension "SPV_KHR_expect_assume"
+    OpMemoryModel Physical32 OpenCL
+
+    %void = OpTypeVoid
+    %voidfn = OpTypeFunction %void
+
+    %uint = OpTypeInt 32 0
+    %uint_0 = OpConstant %uint 0
+    %float = OpTypeFloat 32
+    %float_0 = OpConstant %float 0
+
+    %main = OpFunction %void None %voidfn
+    %entry = OpLabel
+    %val = OpExpectKHR %uint %uint_0 %float_0
+    OpReturn
+    OpFunctionEnd
+)";
+  CompileSuccessfully(str.c_str());
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Type of ExpectedValue operand of OpExpectKHR does not "
+                        "match the result type \n"
+                        "  %9 = OpExpectKHR %uint %uint_0 %float_0\n"));
+}
+
+}  // namespace
+}  // namespace val
+}  // namespace spvtools
diff --git a/test/val/val_extension_spv_khr_integer_dot_product.cpp b/test/val/val_extension_spv_khr_integer_dot_product.cpp
new file mode 100644
index 0000000..e0e6896
--- /dev/null
+++ b/test/val/val_extension_spv_khr_integer_dot_product.cpp
@@ -0,0 +1,1330 @@
+// Copyright (c) 2020 Google Inc.
+// Copyright (c) 2021 Arm Ltd.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <ostream>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "source/enum_string_mapping.h"
+#include "source/extensions.h"
+#include "source/spirv_target_env.h"
+#include "test/test_fixture.h"
+#include "test/unit_spirv.h"
+#include "test/val/val_fixtures.h"
+
+namespace spvtools {
+namespace val {
+namespace {
+
+using ::testing::HasSubstr;
+using ::testing::Values;
+
+struct Case {
+  std::vector<std::string> caps;
+  std::string inst;
+  std::string result_type;
+  std::string op0_type;
+  std::string op1_type;
+  std::string acc_type;  // can be empty
+  bool packed;
+  std::string expected_error;  // empty for no error.
+};
+
+inline std::ostream& operator<<(std::ostream& out, Case c) {
+  out << "\nSPV_KHR_integer_dot_product Case{{";
+  bool first = true;
+  for (const auto& cap : c.caps) {
+    if (!first) {
+      out << " ";
+    }
+    first = false;
+    out << cap;
+  }
+  out << "} ";
+  out << c.inst << " ";
+  out << c.result_type << " ";
+  out << c.op0_type << " ";
+  out << c.op1_type << " ";
+  out << "'" << c.acc_type << "' ";
+  out << (c.packed ? "packed " : "unpacked ");
+  out << "err'" << c.expected_error << "'";
+  return out;
+}
+
+std::string AssemblyForCase(const Case& c) {
+  std::ostringstream ss;
+  ss << "OpCapability Shader\n";
+  for (auto& cap : c.caps) {
+    ss << "OpCapability " << cap << "\n";
+  }
+  ss << R"(
+  OpExtension "SPV_KHR_integer_dot_product"
+  OpMemoryModel Logical Simple
+  OpEntryPoint GLCompute %main "main"
+  OpExecutionMode %main LocalSize 1 1 1
+
+  %void = OpTypeVoid
+  %voidfn = OpTypeFunction %void
+  %uint = OpTypeInt 32 0
+  %int = OpTypeInt 32 1
+
+  %v2uint = OpTypeVector %uint 2
+  %v3uint = OpTypeVector %uint 3
+  %v4uint = OpTypeVector %uint 4
+  %v2int = OpTypeVector %int 2
+  %v3int = OpTypeVector %int 3
+  %v4int = OpTypeVector %int 4
+
+  %uint_0 = OpConstant %uint 0
+  %uint_1 = OpConstant %uint 1
+  %int_0 = OpConstant %int 0
+  %int_1 = OpConstant %int 1
+
+  %v2uint_0 = OpConstantComposite %v2uint %uint_0 %uint_0
+  %v2uint_1 = OpConstantComposite %v2uint %uint_1 %uint_1
+  %v3uint_0 = OpConstantComposite %v3uint %uint_0 %uint_0 %uint_0
+  %v3uint_1 = OpConstantComposite %v3uint %uint_1 %uint_1 %uint_1
+  %v4uint_0 = OpConstantComposite %v4uint %uint_0 %uint_0 %uint_0 %uint_0
+  %v4uint_1 = OpConstantComposite %v4uint %uint_1 %uint_1 %uint_1 %uint_1
+
+  %v2int_0 = OpConstantComposite %v2int %int_0 %int_0
+  %v2int_1 = OpConstantComposite %v2int %int_1 %int_1
+  %v3int_0 = OpConstantComposite %v3int %int_0 %int_0 %int_0
+  %v3int_1 = OpConstantComposite %v3int %int_1 %int_1 %int_1
+  %v4int_0 = OpConstantComposite %v4int %int_0 %int_0 %int_0 %int_0
+  %v4int_1 = OpConstantComposite %v4int %int_1 %int_1 %int_1 %int_1
+)";
+
+  bool use8bit = false;
+  for (auto& cap : c.caps) {
+    if (cap == "DotProductInput4x8BitKHR") {
+      use8bit = true;
+    }
+    if (cap == "Int8") {
+      use8bit = true;
+    }
+  }
+  if (use8bit) {
+    ss << R"(
+         %uchar = OpTypeInt 8 0
+         %char = OpTypeInt 8 1
+
+         %v4uchar = OpTypeVector %uchar 4
+         %v4char = OpTypeVector %char 4
+
+         %uchar_0 = OpConstant %uchar 0
+         %uchar_1 = OpConstant %uchar 1
+         %char_0 = OpConstant %char 0
+         %char_1 = OpConstant %char 1
+
+         %v4uchar_0 = OpConstantComposite %v4uchar %uchar_0 %uint_0 %uchar_0 %uchar_0
+         %v4uchar_1 = OpConstantComposite %v4uchar %uchar_1 %uchar_1 %uchar_1 %uchar_1
+         %v4char_0 = OpConstantComposite %v4char %char_0 %char_0 %char_0 %char_0
+         %v4char_1 = OpConstantComposite %v4char %char_1 %char_1 %char_1 %char_1
+
+         )";
+  }
+
+  ss << R"(
+
+  %main = OpFunction %void None %voidfn
+  %entry = OpLabel
+  %result = )"
+     << c.inst << " " << c.result_type << " ";
+  ss << c.op0_type << "_0 ";
+  ss << c.op1_type << "_1 ";
+  if (!c.acc_type.empty()) {
+    ss << c.acc_type << "_0 ";
+  }
+  if (c.packed) {
+    ss << "PackedVectorFormat4x8BitKHR";
+  }
+  ss << "\nOpReturn\nOpFunctionEnd\n\n";
+  return ss.str();
+}
+
+using ValidateSpvKHRIntegerDotProduct = spvtest::ValidateBase<Case>;
+
+TEST_P(ValidateSpvKHRIntegerDotProduct, Valid) {
+  const auto& c = GetParam();
+  const auto& assembly = AssemblyForCase(c);
+  CompileSuccessfully(assembly);
+  if (c.expected_error.empty()) {
+    EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()) << getDiagnosticString();
+  } else {
+    EXPECT_NE(SPV_SUCCESS, ValidateInstructions());
+    EXPECT_THAT(getDiagnosticString(), HasSubstr(c.expected_error));
+  }
+}
+
+// UDot
+INSTANTIATE_TEST_SUITE_P(
+    Valid_UDot, ValidateSpvKHRIntegerDotProduct,
+    ::testing::Values(Case{{"DotProductKHR", "DotProductInputAllKHR"},
+                           "OpUDotKHR",
+                           "%uint",
+                           "%v2uint",
+                           "%v2uint",
+                           "",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInputAllKHR"},
+                           "OpUDotKHR",
+                           "%uint",
+                           "%v3uint",
+                           "%v3uint",
+                           "",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInputAllKHR"},
+                           "OpUDotKHR",
+                           "%uint",
+                           "%v4uint",
+                           "%v4uint",
+                           "",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"},
+                           "OpUDotKHR",
+                           "%uchar",  // match width
+                           "%v4uchar",
+                           "%v4uchar",
+                           "",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"},
+                           "OpUDotKHR",
+                           "%uint",  // wider width
+                           "%v4uchar",
+                           "%v4uchar",
+                           "",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInput4x8BitKHR"},
+                           "OpUDotKHR",
+                           "%uchar",  // match width
+                           "%v4uchar",
+                           "%v4uchar",
+                           "",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInput4x8BitKHR"},
+                           "OpUDotKHR",
+                           "%uint",  // wider width
+                           "%v4uchar",
+                           "%v4uchar",
+                           "",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR",
+                            "Int8"},
+                           "OpUDotKHR",
+                           "%uchar",  // matches packed component type
+                           "%uint",
+                           "%uint",
+                           "",
+                           true,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR"},
+                           "OpUDotKHR",
+                           "%uint",
+                           "%uint",
+                           "%uint",
+                           "",
+                           true,
+                           ""}));
+
+// SDot result signed args signed signed
+INSTANTIATE_TEST_SUITE_P(
+    Valid_SDot_signed_signed_signed, ValidateSpvKHRIntegerDotProduct,
+    ::testing::Values(Case{{"DotProductKHR", "DotProductInputAllKHR"},
+                           "OpSDotKHR",
+                           "%int",
+                           "%v2int",
+                           "%v2int",
+                           "",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInputAllKHR"},
+                           "OpSDotKHR",
+                           "%int",
+                           "%v3int",
+                           "%v3int",
+                           "",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInputAllKHR"},
+                           "OpSDotKHR",
+                           "%int",
+                           "%v4int",
+                           "%v4int",
+                           "",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"},
+                           "OpSDotKHR",
+                           "%char",  // match width
+                           "%v4char",
+                           "%v4char",
+                           "",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"},
+                           "OpSDotKHR",
+                           "%int",  // wider width
+                           "%v4char",
+                           "%v4char",
+                           "",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInput4x8BitKHR"},
+                           "OpSDotKHR",
+                           "%char",  // match width
+                           "%v4char",
+                           "%v4char",
+                           "",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInput4x8BitKHR"},
+                           "OpSDotKHR",
+                           "%int",  // wider width
+                           "%v4char",
+                           "%v4char",
+                           "",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR",
+                            "Int8"},
+                           "OpSDotKHR",
+                           "%char",  // matches packed component type
+                           "%int",
+                           "%int",
+                           "",
+                           true,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR"},
+                           "OpSDotKHR",
+                           "%int",
+                           "%int",
+                           "%int",
+                           "",
+                           true,
+                           ""}));
+
+// SDot result unsigned args signed unsigned
+INSTANTIATE_TEST_SUITE_P(
+    Valid_SDot_unsigned_signed_unsigned, ValidateSpvKHRIntegerDotProduct,
+    ::testing::Values(Case{{"DotProductKHR", "DotProductInputAllKHR"},
+                           "OpSDotKHR",
+                           "%uint",
+                           "%v2int",
+                           "%v2uint",
+                           "",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInputAllKHR"},
+                           "OpSDotKHR",
+                           "%uint",
+                           "%v3int",
+                           "%v3uint",
+                           "",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInputAllKHR"},
+                           "OpSDotKHR",
+                           "%uint",
+                           "%v4int",
+                           "%v4uint",
+                           "",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"},
+                           "OpSDotKHR",
+                           "%uchar",  // match width
+                           "%v4char",
+                           "%v4uchar",
+                           "",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"},
+                           "OpSDotKHR",
+                           "%uint",  // wider width
+                           "%v4char",
+                           "%v4uchar",
+                           "",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInput4x8BitKHR"},
+                           "OpSDotKHR",
+                           "%uchar",  // match width
+                           "%v4char",
+                           "%v4uchar",
+                           "",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInput4x8BitKHR"},
+                           "OpSDotKHR",
+                           "%uint",  // wider width
+                           "%v4char",
+                           "%v4uchar",
+                           "",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR",
+                            "Int8"},
+                           "OpSDotKHR",
+                           "%uchar",  // matches packed component type
+                           "%int",
+                           "%uint",
+                           "",
+                           true,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR"},
+                           "OpSDotKHR",
+                           "%uint",
+                           "%int",
+                           "%uint",
+                           "",
+                           true,
+                           ""}));
+
+// SDot result signed args signed unsigned
+INSTANTIATE_TEST_SUITE_P(
+    Valid_SDot_signed_signed_unsigned, ValidateSpvKHRIntegerDotProduct,
+    ::testing::Values(Case{{"DotProductKHR", "DotProductInputAllKHR"},
+                           "OpSDotKHR",
+                           "%int",
+                           "%v2int",
+                           "%v2uint",
+                           "",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInputAllKHR"},
+                           "OpSDotKHR",
+                           "%int",
+                           "%v3int",
+                           "%v3uint",
+                           "",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInputAllKHR"},
+                           "OpSDotKHR",
+                           "%int",
+                           "%v4int",
+                           "%v4uint",
+                           "",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"},
+                           "OpSDotKHR",
+                           "%char",  // match width
+                           "%v4char",
+                           "%v4uchar",
+                           "",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"},
+                           "OpSDotKHR",
+                           "%int",  // wider width
+                           "%v4char",
+                           "%v4uchar",
+                           "",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInput4x8BitKHR"},
+                           "OpSDotKHR",
+                           "%char",  // match width
+                           "%v4char",
+                           "%v4uchar",
+                           "",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInput4x8BitKHR"},
+                           "OpSDotKHR",
+                           "%int",  // wider width
+                           "%v4char",
+                           "%v4uchar",
+                           "",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR",
+                            "Int8"},
+                           "OpSDotKHR",
+                           "%char",  // matches packed component type
+                           "%int",
+                           "%uint",
+                           "",
+                           true,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR"},
+                           "OpSDotKHR",
+                           "%int",
+                           "%int",
+                           "%uint",
+                           "",
+                           true,
+                           ""}));
+
+// SUDot result signed args unsigned unsigned
+INSTANTIATE_TEST_SUITE_P(
+    Valid_SUDot_signed_unsigned_unsigned, ValidateSpvKHRIntegerDotProduct,
+    ::testing::Values(Case{{"DotProductKHR", "DotProductInputAllKHR"},
+                           "OpSUDotKHR",
+                           "%int",
+                           "%v2uint",
+                           "%v2uint",
+                           "",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInputAllKHR"},
+                           "OpSUDotKHR",
+                           "%int",
+                           "%v3uint",
+                           "%v3uint",
+                           "",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInputAllKHR"},
+                           "OpSUDotKHR",
+                           "%int",
+                           "%v4uint",
+                           "%v4uint",
+                           "",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"},
+                           "OpSUDotKHR",
+                           "%char",  // match width
+                           "%v4uchar",
+                           "%v4uchar",
+                           "",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"},
+                           "OpSUDotKHR",
+                           "%int",  // wider width
+                           "%v4uchar",
+                           "%v4uchar",
+                           "",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInput4x8BitKHR"},
+                           "OpSUDotKHR",
+                           "%char",  // match width
+                           "%v4uchar",
+                           "%v4uchar",
+                           "",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInput4x8BitKHR"},
+                           "OpSUDotKHR",
+                           "%int",  // wider width
+                           "%v4uchar",
+                           "%v4uchar",
+                           "",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR",
+                            "Int8"},
+                           "OpSUDotKHR",
+                           "%char",  // matches packed component type
+                           "%uint",
+                           "%uint",
+                           "",
+                           true,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR"},
+                           "OpSUDotKHR",
+                           "%int",
+                           "%uint",
+                           "%uint",
+                           "",
+                           true,
+                           ""}));
+
+// SUDot result signed args signed unsigned
+INSTANTIATE_TEST_SUITE_P(
+    Valid_SUDot_signed_signed_unsigned, ValidateSpvKHRIntegerDotProduct,
+    ::testing::Values(Case{{"DotProductKHR", "DotProductInputAllKHR"},
+                           "OpSUDotKHR",
+                           "%int",
+                           "%v2int",
+                           "%v2uint",
+                           "",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInputAllKHR"},
+                           "OpSUDotKHR",
+                           "%int",
+                           "%v3int",
+                           "%v3uint",
+                           "",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInputAllKHR"},
+                           "OpSUDotKHR",
+                           "%int",
+                           "%v4int",
+                           "%v4uint",
+                           "",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"},
+                           "OpSUDotKHR",
+                           "%char",  // match width
+                           "%v4char",
+                           "%v4uchar",
+                           "",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"},
+                           "OpSUDotKHR",
+                           "%int",  // wider width
+                           "%v4char",
+                           "%v4uchar",
+                           "",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInput4x8BitKHR"},
+                           "OpSUDotKHR",
+                           "%char",  // match width
+                           "%v4char",
+                           "%v4uchar",
+                           "",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInput4x8BitKHR"},
+                           "OpSUDotKHR",
+                           "%int",  // wider width
+                           "%v4char",
+                           "%v4uchar",
+                           "",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR",
+                            "Int8"},
+                           "OpSUDotKHR",
+                           "%char",  // matches packed component type
+                           "%int",
+                           "%uint",
+                           "",
+                           true,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR"},
+                           "OpSUDotKHR",
+                           "%int",
+                           "%int",
+                           "%uint",
+                           "",
+                           true,
+                           ""}));
+
+// SUDot result unsigned args unsigned unsigned
+INSTANTIATE_TEST_SUITE_P(
+    Valid_SUDot_unsigned_unsigned_unsigned, ValidateSpvKHRIntegerDotProduct,
+    ::testing::Values(Case{{"DotProductKHR", "DotProductInputAllKHR"},
+                           "OpSUDotKHR",
+                           "%uint",
+                           "%v2uint",
+                           "%v2uint",
+                           "",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInputAllKHR"},
+                           "OpSUDotKHR",
+                           "%uint",
+                           "%v3uint",
+                           "%v3uint",
+                           "",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInputAllKHR"},
+                           "OpSUDotKHR",
+                           "%uint",
+                           "%v4uint",
+                           "%v4uint",
+                           "",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"},
+                           "OpSUDotKHR",
+                           "%uchar",  // match width
+                           "%v4uchar",
+                           "%v4uchar",
+                           "",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"},
+                           "OpSUDotKHR",
+                           "%uint",  // wider width
+                           "%v4uchar",
+                           "%v4uchar",
+                           "",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInput4x8BitKHR"},
+                           "OpSUDotKHR",
+                           "%uchar",  // match width
+                           "%v4uchar",
+                           "%v4uchar",
+                           "",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInput4x8BitKHR"},
+                           "OpSUDotKHR",
+                           "%uint",  // wider width
+                           "%v4uchar",
+                           "%v4uchar",
+                           "",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR",
+                            "Int8"},
+                           "OpSUDotKHR",
+                           "%uchar",  // matches packed component type
+                           "%uint",
+                           "%uint",
+                           "",
+                           true,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR"},
+                           "OpSUDotKHR",
+                           "%uint",
+                           "%uint",
+                           "%uint",
+                           "",
+                           true,
+                           ""}));
+
+// UDotAccSat
+INSTANTIATE_TEST_SUITE_P(
+    Valid_UDotAccSat, ValidateSpvKHRIntegerDotProduct,
+    ::testing::Values(Case{{"DotProductKHR", "DotProductInputAllKHR"},
+                           "OpUDotAccSatKHR",
+                           "%uint",
+                           "%v2uint",
+                           "%v2uint",
+                           "%uint",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInputAllKHR"},
+                           "OpUDotAccSatKHR",
+                           "%uint",
+                           "%v3uint",
+                           "%v3uint",
+                           "%uint",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInputAllKHR"},
+                           "OpUDotAccSatKHR",
+                           "%uint",
+                           "%v4uint",
+                           "%v4uint",
+                           "%uint",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"},
+                           "OpUDotAccSatKHR",
+                           "%uchar",  // match width
+                           "%v4uchar",
+                           "%v4uchar",
+                           "%uint",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"},
+                           "OpUDotAccSatKHR",
+                           "%uint",  // wider width
+                           "%v4uchar",
+                           "%v4uchar",
+                           "%uint",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInput4x8BitKHR"},
+                           "OpUDotAccSatKHR",
+                           "%uchar",  // match width
+                           "%v4uchar",
+                           "%v4uchar",
+                           "%uchar",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInput4x8BitKHR"},
+                           "OpUDotAccSatKHR",
+                           "%uint",  // wider width
+                           "%v4uchar",
+                           "%v4uchar",
+                           "%uint",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR",
+                            "Int8"},
+                           "OpUDotAccSatKHR",
+                           "%uchar",  // matches packed component type
+                           "%uint",
+                           "%uint",
+                           "%uchar",
+                           true,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR"},
+                           "OpUDotAccSatKHR",
+                           "%uint",
+                           "%uint",
+                           "%uint",
+                           "%uint",
+                           true,
+                           ""}));
+
+// SDotAccSat result signed args signed signed
+INSTANTIATE_TEST_SUITE_P(
+    Valid_SDotAccSat_signed_signed_signed, ValidateSpvKHRIntegerDotProduct,
+    ::testing::Values(Case{{"DotProductKHR", "DotProductInputAllKHR"},
+                           "OpSDotAccSatKHR",
+                           "%int",
+                           "%v2int",
+                           "%v2int",
+                           "%int",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInputAllKHR"},
+                           "OpSDotAccSatKHR",
+                           "%int",
+                           "%v3int",
+                           "%v3int",
+                           "%int",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInputAllKHR"},
+                           "OpSDotAccSatKHR",
+                           "%int",
+                           "%v4int",
+                           "%v4int",
+                           "%int",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"},
+                           "OpSDotAccSatKHR",
+                           "%char",  // match width
+                           "%v4char",
+                           "%v4char",
+                           "%char",  // match width
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"},
+                           "OpSDotAccSatKHR",
+                           "%int",  // wider width
+                           "%v4char",
+                           "%v4char",
+                           "%int",  // wider width
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInput4x8BitKHR"},
+                           "OpSDotAccSatKHR",
+                           "%char",  // match width
+                           "%v4char",
+                           "%v4char",
+                           "%char",  // match width
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInput4x8BitKHR"},
+                           "OpSDotAccSatKHR",
+                           "%int",  // wider width
+                           "%v4char",
+                           "%v4char",
+                           "%int",  // wider width
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR",
+                            "Int8"},
+                           "OpSDotAccSatKHR",
+                           "%char",  // matches packed component type
+                           "%int",
+                           "%int",
+                           "%char",  // matches packed component type
+                           true,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR"},
+                           "OpSDotAccSatKHR",
+                           "%int",
+                           "%int",
+                           "%int",
+                           "%int",
+                           true,
+                           ""}));
+
+// SDotAccSat result unsigned args signed unsigned
+INSTANTIATE_TEST_SUITE_P(
+    Valid_SDotAccSat_unsigned_signed_unsigned, ValidateSpvKHRIntegerDotProduct,
+    ::testing::Values(Case{{"DotProductKHR", "DotProductInputAllKHR"},
+                           "OpSDotAccSatKHR",
+                           "%uint",
+                           "%v2int",
+                           "%v2uint",
+                           "%uint",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInputAllKHR"},
+                           "OpSDotAccSatKHR",
+                           "%uint",
+                           "%v3int",
+                           "%v3uint",
+                           "%uint",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInputAllKHR"},
+                           "OpSDotAccSatKHR",
+                           "%uint",
+                           "%v4int",
+                           "%v4uint",
+                           "%uint",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"},
+                           "OpSDotAccSatKHR",
+                           "%uchar",  // match width
+                           "%v4char",
+                           "%v4uchar",
+                           "%uchar",  // match width
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"},
+                           "OpSDotAccSatKHR",
+                           "%uint",  // wider width
+                           "%v4char",
+                           "%v4uchar",
+                           "%uint",  // wider width
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInput4x8BitKHR"},
+                           "OpSDotAccSatKHR",
+                           "%uchar",  // match width
+                           "%v4char",
+                           "%v4uchar",
+                           "%uchar",  // match width
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInput4x8BitKHR"},
+                           "OpSDotAccSatKHR",
+                           "%uint",  // wider width
+                           "%v4char",
+                           "%v4uchar",
+                           "%uint",  // wider width
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR",
+                            "Int8"},
+                           "OpSDotAccSatKHR",
+                           "%uchar",  // matches packed component type
+                           "%int",
+                           "%uint",
+                           "%uchar",  // matches packed component type
+                           true,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR"},
+                           "OpSDotAccSatKHR",
+                           "%uint",
+                           "%int",
+                           "%uint",
+                           "%uint",
+                           true,
+                           ""}));
+
+// SDotAccSat result signed args signed unsigned
+INSTANTIATE_TEST_SUITE_P(
+    Valid_SDotAccSat_signed_signed_unsigned, ValidateSpvKHRIntegerDotProduct,
+    ::testing::Values(Case{{"DotProductKHR", "DotProductInputAllKHR"},
+                           "OpSDotAccSatKHR",
+                           "%int",
+                           "%v2int",
+                           "%v2uint",
+                           "%int",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInputAllKHR"},
+                           "OpSDotAccSatKHR",
+                           "%int",
+                           "%v3int",
+                           "%v3uint",
+                           "%int",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInputAllKHR"},
+                           "OpSDotAccSatKHR",
+                           "%int",
+                           "%v4int",
+                           "%v4uint",
+                           "%int",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"},
+                           "OpSDotAccSatKHR",
+                           "%char",  // match width
+                           "%v4char",
+                           "%v4uchar",
+                           "%char",  // match width
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"},
+                           "OpSDotAccSatKHR",
+                           "%int",  // wider width
+                           "%v4char",
+                           "%v4uchar",
+                           "%int",  // wider width
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInput4x8BitKHR"},
+                           "OpSDotAccSatKHR",
+                           "%char",  // match width
+                           "%v4char",
+                           "%v4uchar",
+                           "%char",  // match width
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInput4x8BitKHR"},
+                           "OpSDotAccSatKHR",
+                           "%int",  // wider width
+                           "%v4char",
+                           "%v4uchar",
+                           "%int",  // wider width
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR",
+                            "Int8"},
+                           "OpSDotAccSatKHR",
+                           "%char",  // matches packed component type
+                           "%int",
+                           "%uint",
+                           "%char",  // matches packed component type
+                           true,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR"},
+                           "OpSDotAccSatKHR",
+                           "%int",
+                           "%int",
+                           "%uint",
+                           "%int",
+                           true,
+                           ""}));
+
+// SUDotAccSat result signed args unsigned unsigned
+INSTANTIATE_TEST_SUITE_P(
+    Valid_SUDotAccSat_signed_unsigned_unsigned, ValidateSpvKHRIntegerDotProduct,
+    ::testing::Values(Case{{"DotProductKHR", "DotProductInputAllKHR"},
+                           "OpSUDotAccSatKHR",
+                           "%int",
+                           "%v2uint",
+                           "%v2uint",
+                           "%int",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInputAllKHR"},
+                           "OpSUDotAccSatKHR",
+                           "%int",
+                           "%v3uint",
+                           "%v3uint",
+                           "%int",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInputAllKHR"},
+                           "OpSUDotAccSatKHR",
+                           "%int",
+                           "%v4uint",
+                           "%v4uint",
+                           "%int",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"},
+                           "OpSUDotAccSatKHR",
+                           "%char",  // match width
+                           "%v4uchar",
+                           "%v4uchar",
+                           "%char",  // match width
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"},
+                           "OpSUDotAccSatKHR",
+                           "%int",  // wider width
+                           "%v4uchar",
+                           "%v4uchar",
+                           "%int",  // wider width
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInput4x8BitKHR"},
+                           "OpSUDotAccSatKHR",
+                           "%char",  // match width
+                           "%v4uchar",
+                           "%v4uchar",
+                           "%char",  // match width
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInput4x8BitKHR"},
+                           "OpSUDotAccSatKHR",
+                           "%int",  // wider width
+                           "%v4uchar",
+                           "%v4uchar",
+                           "%int",  // wider width
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR",
+                            "Int8"},
+                           "OpSUDotAccSatKHR",
+                           "%char",  // matches packed component type
+                           "%uint",
+                           "%uint",
+                           "%char",  // matches packed component type
+                           true,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR"},
+                           "OpSUDotAccSatKHR",
+                           "%int",
+                           "%uint",
+                           "%uint",
+                           "%int",
+                           true,
+                           ""}));
+
+// SUDotAccSat result signed args signed unsigned
+INSTANTIATE_TEST_SUITE_P(
+    Valid_SUDotAccSat_signed_signed_unsigned, ValidateSpvKHRIntegerDotProduct,
+    ::testing::Values(Case{{"DotProductKHR", "DotProductInputAllKHR"},
+                           "OpSUDotAccSatKHR",
+                           "%int",
+                           "%v2int",
+                           "%v2uint",
+                           "%int",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInputAllKHR"},
+                           "OpSUDotAccSatKHR",
+                           "%int",
+                           "%v3int",
+                           "%v3uint",
+                           "%int",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInputAllKHR"},
+                           "OpSUDotAccSatKHR",
+                           "%int",
+                           "%v4int",
+                           "%v4uint",
+                           "%int",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"},
+                           "OpSUDotAccSatKHR",
+                           "%char",  // match width
+                           "%v4char",
+                           "%v4uchar",
+                           "%char",  // match width
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"},
+                           "OpSUDotAccSatKHR",
+                           "%int",  // wider width
+                           "%v4char",
+                           "%v4uchar",
+                           "%int",  // wider width
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInput4x8BitKHR"},
+                           "OpSUDotAccSatKHR",
+                           "%char",  // match width
+                           "%v4char",
+                           "%v4uchar",
+                           "%char",  // match width
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInput4x8BitKHR"},
+                           "OpSUDotAccSatKHR",
+                           "%int",  // wider width
+                           "%v4char",
+                           "%v4uchar",
+                           "%int",  // wider width
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR",
+                            "Int8"},
+                           "OpSUDotAccSatKHR",
+                           "%char",  // matches packed component type
+                           "%int",
+                           "%uint",
+                           "%char",  // matches packed component type
+                           true,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR"},
+                           "OpSUDotAccSatKHR",
+                           "%int",
+                           "%int",
+                           "%uint",
+                           "%int",
+                           true,
+                           ""}));
+
+// SUDotAccSat result unsigned args unsigned unsigned
+INSTANTIATE_TEST_SUITE_P(
+    Valid_SUDotAccSat_unsigned_unsigned_unsigned,
+    ValidateSpvKHRIntegerDotProduct,
+    ::testing::Values(Case{{"DotProductKHR", "DotProductInputAllKHR"},
+                           "OpSUDotAccSatKHR",
+                           "%uint",
+                           "%v2uint",
+                           "%v2uint",
+                           "%uint",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInputAllKHR"},
+                           "OpSUDotAccSatKHR",
+                           "%uint",
+                           "%v3uint",
+                           "%v3uint",
+                           "%uint",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInputAllKHR"},
+                           "OpSUDotAccSatKHR",
+                           "%uint",
+                           "%v4uint",
+                           "%v4uint",
+                           "%uint",
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"},
+                           "OpSUDotAccSatKHR",
+                           "%uchar",  // match width
+                           "%v4uchar",
+                           "%v4uchar",
+                           "%uchar",  // match width
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"},
+                           "OpSUDotAccSatKHR",
+                           "%uint",  // wider width
+                           "%v4uchar",
+                           "%v4uchar",
+                           "%uint",  // wider width
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInput4x8BitKHR"},
+                           "OpSUDotAccSatKHR",
+                           "%uchar",  // match width
+                           "%v4uchar",
+                           "%v4uchar",
+                           "%uchar",  // match width
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInput4x8BitKHR"},
+                           "OpSUDotAccSatKHR",
+                           "%uint",  // wider width
+                           "%v4uchar",
+                           "%v4uchar",
+                           "%uint",  // wider width
+                           false,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR",
+                            "Int8"},
+                           "OpSUDotAccSatKHR",
+                           "%uchar",  // matches packed component type
+                           "%uint",
+                           "%uint",
+                           "%uchar",  // matches packed component type
+                           true,
+                           ""},
+                      Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR"},
+                           "OpSUDotAccSatKHR",
+                           "%uint",
+                           "%uint",
+                           "%uint",
+                           "%uint",
+                           true,
+                           ""}));
+
+using ValidateSpvKHRIntegerDotProductSimple = ::testing::Test;
+
+TEST(ValidateSpvKHRIntegerDotProductSimple, DISABLED_RequiresExtension) {
+  FAIL();
+}
+
+TEST(ValidateSpvKHRIntegerDotProductSimple, DISABLED_Invalid_ResultTooNarrow) {
+  // Test across all the instructions.
+  FAIL();
+}
+
+TEST(ValidateSpvKHRIntegerDotProductSimple,
+     DISABLED_Invalid_UDot_OperandTypesMatch) {
+  FAIL();
+}
+
+TEST(ValidateSpvKHRIntegerDotProductSimple,
+     DISABLED_Invalid_SDot_OperandTypesMatchExceptSignedness) {
+  FAIL();
+}
+
+TEST(ValidateSpvKHRIntegerDotProductSimple,
+     DISABLED_Invalid_SUDot_OperandTypesMatchExceptSignedness) {
+  FAIL();
+}
+
+TEST(ValidateSpvKHRIntegerDotProductSimple,
+     DISABLED_Invalid_UDotAccSat_OperandTypesMatch) {
+  FAIL();
+}
+
+TEST(ValidateSpvKHRIntegerDotProductSimple,
+     DISABLED_Invalid_SDotAccSat_OperandTypesMatchExceptSignedness) {
+  FAIL();
+}
+
+TEST(ValidateSpvKHRIntegerDotProductSimple,
+     DISABLED_Invalid_SUDotAccSat_OperandTypesMatchExceptSignedness) {
+  FAIL();
+}
+
+TEST(ValidateSpvKHRIntegerDotProductSimple,
+     DISABLED_Invalid_UDot_RequiresUnsigned) {
+  FAIL();
+}
+
+TEST(ValidateSpvKHRIntegerDotProductSimple,
+     DISABLED_Invalid_SUDot_RequiresUnsignedSecondArg) {
+  FAIL();
+}
+
+TEST(ValidateSpvKHRIntegerDotProductSimple,
+     DISABLED_Invalid_UDotAccSat_RequiresUnsigned) {
+  FAIL();
+}
+
+TEST(ValidateSpvKHRIntegerDotProductSimple,
+     DISABLED_Invalid_SUDotAccSat_RequiresUnsignedSecondArg) {
+  FAIL();
+}
+
+TEST(ValidateSpvKHRIntegerDotProductSimple,
+     DISABLED_Invalid_VectorOperandsDisallowPackedFormat) {
+  FAIL();
+}
+
+TEST(ValidateSpvKHRIntegerDotProductSimple,
+     DISABLED_Invalid_ScalarOperandsRequirePackedFormat) {
+  FAIL();
+}
+
+// TODO(dneto): Test valid cases with other scalar integer types
+// TODO(dneto): Test valid cases of length-8 vectors
+// TODO(dneto): Test valid cases of length-16 vectors
+
+}  // namespace
+}  // namespace val
+}  // namespace spvtools
diff --git a/test/val/val_extension_spv_khr_linkonce_odr.cpp b/test/val/val_extension_spv_khr_linkonce_odr.cpp
new file mode 100644
index 0000000..ac15558
--- /dev/null
+++ b/test/val/val_extension_spv_khr_linkonce_odr.cpp
@@ -0,0 +1,100 @@
+// Copyright (c) 2020 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.
+
+// Tests for OpExtension validator rules.
+
+#include <string>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "source/enum_string_mapping.h"
+#include "source/extensions.h"
+#include "source/spirv_target_env.h"
+#include "test/test_fixture.h"
+#include "test/unit_spirv.h"
+#include "test/val/val_fixtures.h"
+
+namespace spvtools {
+namespace val {
+namespace {
+
+using ::testing::HasSubstr;
+using ::testing::Values;
+using ::testing::ValuesIn;
+
+using ValidateSpvKHRLinkOnceODR = spvtest::ValidateBase<bool>;
+
+TEST_F(ValidateSpvKHRLinkOnceODR, Valid) {
+  const std::string str = R"(
+    OpCapability Kernel
+    OpCapability Addresses
+    OpCapability Linkage
+    OpExtension "SPV_KHR_linkonce_odr"
+    OpMemoryModel Physical32 OpenCL
+    OpDecorate %var LinkageAttributes "foobar" LinkOnceODR
+
+    %uint = OpTypeInt 32 0
+    %ptr = OpTypePointer CrossWorkgroup %uint
+    %var = OpVariable %ptr CrossWorkgroup
+
+)";
+  CompileSuccessfully(str.c_str());
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateSpvKHRLinkOnceODR, RequiresExtension) {
+  const std::string str = R"(
+    OpCapability Kernel
+    OpCapability Addresses
+    OpCapability Linkage
+    OpMemoryModel Physical32 OpenCL
+    OpDecorate %var LinkageAttributes "foobar" LinkOnceODR
+
+    %uint = OpTypeInt 32 0
+    %ptr = OpTypePointer CrossWorkgroup %uint
+    %var = OpVariable %ptr CrossWorkgroup
+)";
+  CompileSuccessfully(str.c_str());
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("4th operand of Decorate: operand LinkOnceODR(2) requires one "
+                "of these extensions: SPV_KHR_linkonce_odr \n"
+                "  OpDecorate %1 LinkageAttributes \"foobar\" LinkOnceODR\n"));
+}
+
+TEST_F(ValidateSpvKHRLinkOnceODR, RequiresLinkageCapability) {
+  const std::string str = R"(
+    OpCapability Kernel
+    OpCapability Addresses
+    OpExtension "SPV_KHR_linkonce_odr"
+    OpMemoryModel Physical32 OpenCL
+    OpDecorate %var LinkageAttributes "foobar" LinkOnceODR
+
+    %uint = OpTypeInt 32 0
+    %ptr = OpTypePointer CrossWorkgroup %uint
+    %var = OpVariable %ptr CrossWorkgroup
+)";
+  CompileSuccessfully(str.c_str());
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Operand 2 of Decorate requires one of these capabilities: Linkage \n"
+          "  OpDecorate %1 LinkageAttributes \"foobar\" LinkOnceODR"));
+}
+
+}  // namespace
+}  // namespace val
+}  // namespace spvtools
diff --git a/test/val/val_extension_spv_khr_subgroup_uniform_control_flow.cpp b/test/val/val_extension_spv_khr_subgroup_uniform_control_flow.cpp
new file mode 100644
index 0000000..f528cb9
--- /dev/null
+++ b/test/val/val_extension_spv_khr_subgroup_uniform_control_flow.cpp
@@ -0,0 +1,110 @@
+// Copyright (c) 2021 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.
+
+// Tests for OpExtension validator rules.
+
+#include <string>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "source/enum_string_mapping.h"
+#include "source/extensions.h"
+#include "source/spirv_target_env.h"
+#include "test/test_fixture.h"
+#include "test/unit_spirv.h"
+#include "test/val/val_fixtures.h"
+
+namespace spvtools {
+namespace val {
+namespace {
+
+using ::testing::HasSubstr;
+using ::testing::Values;
+using ::testing::ValuesIn;
+
+using ValidateSpvKHRSubgroupUniformControlFlow = spvtest::ValidateBase<bool>;
+
+TEST_F(ValidateSpvKHRSubgroupUniformControlFlow, Valid) {
+  const std::string str = R"(
+    OpCapability Shader
+    OpExtension "SPV_KHR_subgroup_uniform_control_flow"
+    OpMemoryModel Logical Simple
+    OpEntryPoint GLCompute %main "main"
+    OpExecutionMode %main LocalSize 1 1 1
+    OpExecutionMode %main SubgroupUniformControlFlowKHR
+    
+    %void    = OpTypeVoid
+    %void_fn = OpTypeFunction %void
+
+    %main = OpFunction %void None %void_fn
+    %entry = OpLabel
+    OpReturn
+    OpFunctionEnd
+)";
+  CompileSuccessfully(str.c_str());
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateSpvKHRSubgroupUniformControlFlow, RequiresExtension) {
+  const std::string str = R"(
+    OpCapability Shader
+    OpMemoryModel Logical Simple
+    OpEntryPoint GLCompute %main "main"
+    OpExecutionMode %main LocalSize 1 1 1
+    OpExecutionMode %main SubgroupUniformControlFlowKHR
+    
+    %void    = OpTypeVoid
+    %void_fn = OpTypeFunction %void
+
+    %main = OpFunction %void None %void_fn
+    %entry = OpLabel
+    OpReturn
+    OpFunctionEnd
+)";
+  CompileSuccessfully(str.c_str());
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("2nd operand of ExecutionMode: operand "
+                        "SubgroupUniformControlFlowKHR(4421) "
+                        "requires one of these extensions: "
+                        "SPV_KHR_subgroup_uniform_control_flow"));
+}
+
+TEST_F(ValidateSpvKHRSubgroupUniformControlFlow, RequiresShaderCapability) {
+  const std::string str = R"(
+    OpCapability Kernel
+    OpCapability Addresses
+    OpExtension "SPV_KHR_subgroup_uniform_control_flow"
+    OpMemoryModel Physical32 OpenCL
+    OpEntryPoint Kernel %main "main"
+    OpExecutionMode %main SubgroupUniformControlFlowKHR
+    
+    %void    = OpTypeVoid
+    %void_fn = OpTypeFunction %void
+
+    %main = OpFunction %void None %void_fn
+    %entry = OpLabel
+    OpReturn
+    OpFunctionEnd
+)";
+  CompileSuccessfully(str.c_str());
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Operand 2 of ExecutionMode requires one of these "
+                        "capabilities: Shader"));
+}
+
+}  // namespace
+}  // namespace val
+}  // namespace spvtools
diff --git a/test/val/val_id_test.cpp b/test/val/val_id_test.cpp
index c65d171..dd4c952 100644
--- a/test/val/val_id_test.cpp
+++ b/test/val/val_id_test.cpp
@@ -835,7 +835,7 @@
         position_(spv_position_t{0, 0, 0}),
         diagnostic_(spvDiagnosticCreate(&position_, "")) {}
 
-  ~OpTypeArrayLengthTest() { spvDiagnosticDestroy(diagnostic_); }
+  ~OpTypeArrayLengthTest() override { spvDiagnosticDestroy(diagnostic_); }
 
   // Runs spvValidate() on v, printing any errors via spvDiagnosticPrint().
   spv_result_t Val(const SpirvVector& v, const std::string& expected_err = "") {
@@ -1056,6 +1056,8 @@
   CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_0);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
   EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-None-04667"));
+  EXPECT_THAT(getDiagnosticString(),
               HasSubstr("OpTypeStruct must not contain an opaque type"));
 }
 
diff --git a/test/val/val_image_test.cpp b/test/val/val_image_test.cpp
index 5030e41..701e35e 100644
--- a/test/val/val_image_test.cpp
+++ b/test/val/val_image_test.cpp
@@ -80,6 +80,7 @@
 %private_image_u32_buffer_0002_r32ui
 %private_image_u32_spd_0002
 %private_image_f32_buffer_0002_r32ui
+%input_flat_u32
 )";
 
   ss << capabilities_and_extensions;
@@ -121,6 +122,8 @@
 OpDecorate %uniform_image_f32_cube_0102_rgba32f Binding 3
 OpDecorate %uniform_sampler DescriptorSet 3
 OpDecorate %uniform_sampler Binding 0
+OpDecorate %input_flat_u32 Flat
+OpDecorate %input_flat_u32 Location 0
 )";
   }
 
@@ -294,6 +297,9 @@
 %ptr_Image_f32 = OpTypePointer Image %f32
 %ptr_image_f32_buffer_0002_r32ui = OpTypePointer Private %type_image_f32_buffer_0002_r32ui
 %private_image_f32_buffer_0002_r32ui = OpVariable %ptr_image_f32_buffer_0002_r32ui Private
+
+%ptr_input_flat_u32 = OpTypePointer Input %u32
+%input_flat_u32 = OpVariable %ptr_input_flat_u32 Input
 )";
 
   if (env == SPV_ENV_UNIVERSAL_1_0) {
@@ -1785,6 +1791,20 @@
                         "OpImage*Gather operations"));
 }
 
+TEST_F(ValidateImage, SampleImplicitLodVulkanOffsetWrongBeforeLegalization) {
+  const std::string body = R"(
+%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001
+%sampler = OpLoad %type_sampler %uniform_sampler
+%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler
+%res4 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec4_0000 Offset %s32vec2_01
+)";
+
+  CompileSuccessfully(
+      GenerateShaderCode(body, "", "Fragment", "", SPV_ENV_VULKAN_1_0).c_str());
+  getValidatorOptions()->before_hlsl_legalization = true;
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
 TEST_F(ValidateImage, SampleImplicitLodMoreThanOneOffset) {
   const std::string body = R"(
 %img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001
@@ -3016,6 +3036,40 @@
               HasSubstr("Expected Component to be 32-bit int scalar"));
 }
 
+TEST_F(ValidateImage, GatherComponentSuccessVulkan) {
+  const std::string body = R"(
+%img = OpLoad %type_image_f32_cube_0101 %uniform_image_f32_cube_0101
+%sampler = OpLoad %type_sampler %uniform_sampler
+%simg = OpSampledImage %type_sampled_image_f32_cube_0101 %img %sampler
+%res1 = OpImageGather %f32vec4 %simg %f32vec4_0000 %u32_0
+)";
+
+  spv_target_env env = SPV_ENV_VULKAN_1_0;
+  CompileSuccessfully(GenerateShaderCode(body, "", "Fragment", "", env).c_str(),
+                      env);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(env));
+}
+
+TEST_F(ValidateImage, GatherComponentNotConstantVulkan) {
+  const std::string body = R"(
+%input_u32 = OpLoad %u32 %input_flat_u32
+%img = OpLoad %type_image_f32_cube_0101 %uniform_image_f32_cube_0101
+%sampler = OpLoad %type_sampler %uniform_sampler
+%simg = OpSampledImage %type_sampled_image_f32_cube_0101 %img %sampler
+%res1 = OpImageGather %f32vec4 %simg %f32vec4_0000 %input_u32
+)";
+
+  spv_target_env env = SPV_ENV_VULKAN_1_0;
+  CompileSuccessfully(GenerateShaderCode(body, "", "Fragment", "", env).c_str(),
+                      env);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(env));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-OpImageGather-04664"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Expected Component Operand to be a const object for "
+                        "Vulkan environment"));
+}
+
 TEST_F(ValidateImage, GatherDimCube) {
   const std::string body = R"(
 %img = OpLoad %type_image_f32_cube_0101 %uniform_image_f32_cube_0101
@@ -3335,6 +3389,8 @@
           .c_str());
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
   EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-Result-04780"));
+  EXPECT_THAT(getDiagnosticString(),
               HasSubstr("Expected Result Type to have 4 components"));
 }
 
@@ -5775,6 +5831,11 @@
 OpCapability Int64ImageEXT
 OpExtension "SPV_EXT_shader_image_int64"
 )";
+static const std::string capabilities_and_extensions_image64_atomic = R"(
+OpCapability Int64Atomics
+OpCapability Int64ImageEXT
+OpExtension "SPV_EXT_shader_image_int64"
+)";
 static const std::string declarations_image64 = R"(
 %type_image_u64_buffer_0002_r64ui = OpTypeImage %u64 Buffer 0 0 0 2 R64ui
 %ptr_Image_u64 = OpTypePointer Image %u64
@@ -5814,11 +5875,11 @@
 %sum = OpAtomicIAdd %u64 %texel_ptr %u32_1 %u32_0 %u64_1
 )";
 
-  CompileSuccessfully(GenerateShaderCode(body,
-                                         capabilities_and_extensions_image64,
-                                         "Fragment", "", SPV_ENV_UNIVERSAL_1_3,
-                                         "GLSL450", declarations_image64)
-                          .c_str());
+  CompileSuccessfully(
+      GenerateShaderCode(body, capabilities_and_extensions_image64_atomic,
+                         "Fragment", "", SPV_ENV_UNIVERSAL_1_3, "GLSL450",
+                         declarations_image64)
+          .c_str());
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
@@ -5828,11 +5889,11 @@
 %sum = OpAtomicIAdd %u64 %texel_ptr %u32_1 %u32_0 %u64_1
 )";
 
-  CompileSuccessfully(GenerateShaderCode(body,
-                                         capabilities_and_extensions_image64,
-                                         "Fragment", "", SPV_ENV_UNIVERSAL_1_3,
-                                         "GLSL450", declarations_image64)
-                          .c_str());
+  CompileSuccessfully(
+      GenerateShaderCode(body, capabilities_and_extensions_image64_atomic,
+                         "Fragment", "", SPV_ENV_UNIVERSAL_1_3, "GLSL450",
+                         declarations_image64)
+          .c_str());
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("Expected Result Type to be OpTypePointer"));
@@ -5844,11 +5905,11 @@
 %sum = OpAtomicIAdd %u64 %texel_ptr %u32_1 %u32_0 %u64_1
 )";
 
-  CompileSuccessfully(GenerateShaderCode(body,
-                                         capabilities_and_extensions_image64,
-                                         "Fragment", "", SPV_ENV_UNIVERSAL_1_3,
-                                         "GLSL450", declarations_image64)
-                          .c_str());
+  CompileSuccessfully(
+      GenerateShaderCode(body, capabilities_and_extensions_image64_atomic,
+                         "Fragment", "", SPV_ENV_UNIVERSAL_1_3, "GLSL450",
+                         declarations_image64)
+          .c_str());
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("Expected Result Type to be OpTypePointer whose "
@@ -5861,11 +5922,11 @@
 %sum = OpAtomicIAdd %u64 %texel_ptr %u32_1 %u32_0 %u64_1
 )";
 
-  CompileSuccessfully(GenerateShaderCode(body,
-                                         capabilities_and_extensions_image64,
-                                         "Fragment", "", SPV_ENV_UNIVERSAL_1_3,
-                                         "GLSL450", declarations_image64)
-                          .c_str());
+  CompileSuccessfully(
+      GenerateShaderCode(body, capabilities_and_extensions_image64_atomic,
+                         "Fragment", "", SPV_ENV_UNIVERSAL_1_3, "GLSL450",
+                         declarations_image64)
+          .c_str());
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("Expected Sample for Image with MS 0 to be a valid "
diff --git a/test/val/val_limits_test.cpp b/test/val/val_limits_test.cpp
index 0ef61e2..8fb80a4 100644
--- a/test/val/val_limits_test.cpp
+++ b/test/val/val_limits_test.cpp
@@ -212,6 +212,7 @@
 %5 = OpFunction %1 None %2
 %7 = OpLabel
 %8 = OpIAdd %3 %4 %4
+     OpSelectionMerge %10 None
      OpSwitch %4 %10)";
 
   // Now add the (literal, label) pairs
@@ -240,6 +241,7 @@
 %5 = OpFunction %1 None %2
 %7 = OpLabel
 %8 = OpIAdd %3 %4 %4
+     OpSelectionMerge %10 None
      OpSwitch %4 %10)";
 
   // Now add the (literal, label) pairs
@@ -271,6 +273,7 @@
 %5 = OpFunction %1 None %2
 %7 = OpLabel
 %8 = OpIAdd %3 %4 %4
+     OpSelectionMerge %10 None
      OpSwitch %4 %10)";
 
   // Now add the (literal, label) pairs
@@ -301,6 +304,7 @@
 %5 = OpFunction %1 None %2
 %7 = OpLabel
 %8 = OpIAdd %3 %4 %4
+     OpSelectionMerge %10 None
      OpSwitch %4 %10)";
 
   // Now add the (literal, label) pairs
diff --git a/test/val/val_memory_test.cpp b/test/val/val_memory_test.cpp
index 958476c..9799b40 100644
--- a/test/val/val_memory_test.cpp
+++ b/test/val/val_memory_test.cpp
@@ -61,14 +61,14 @@
 )";
   CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-UniformConstant-04655"));
   EXPECT_THAT(
       getDiagnosticString(),
-      HasSubstr("From Vulkan spec, section 14.5.2:\n"
-                "Variables identified with the UniformConstant storage class "
+      HasSubstr("Variables identified with the UniformConstant storage class "
                 "are used only as handles to refer to opaque resources. Such "
                 "variables must be typed as OpTypeImage, OpTypeSampler, "
-                "OpTypeSampledImage, OpTypeAccelerationStructureNV, "
-                "OpTypeAccelerationStructureKHR, OpTypeRayQueryKHR, "
+                "OpTypeSampledImage, OpTypeAccelerationStructureKHR, "
                 "or an array of one of these types."));
 }
 
@@ -115,14 +115,14 @@
 )";
   CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-UniformConstant-04655"));
   EXPECT_THAT(
       getDiagnosticString(),
-      HasSubstr("From Vulkan spec, section 14.5.2:\n"
-                "Variables identified with the UniformConstant storage class "
+      HasSubstr("Variables identified with the UniformConstant storage class "
                 "are used only as handles to refer to opaque resources. Such "
                 "variables must be typed as OpTypeImage, OpTypeSampler, "
-                "OpTypeSampledImage, OpTypeAccelerationStructureNV, "
-                "OpTypeAccelerationStructureKHR, OpTypeRayQueryKHR, "
+                "OpTypeSampledImage, OpTypeAccelerationStructureKHR, "
                 "or an array of one of these types."));
 }
 
diff --git a/test/val/val_misc_test.cpp b/test/val/val_misc_test.cpp
index 499b5b2..b0e46bf 100644
--- a/test/val/val_misc_test.cpp
+++ b/test/val/val_misc_test.cpp
@@ -273,6 +273,30 @@
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("Cannot create undefined values with void type"));
 }
+
+TEST_F(ValidateMisc, VulkanInvalidStorageClass) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %func "shader"
+%int = OpTypeInt 32 0
+%ptr = OpTypePointer CrossWorkgroup %int
+%var = OpVariable %ptr CrossWorkgroup
+%void   = OpTypeVoid
+%void_f = OpTypeFunction %void
+%func   = OpFunction %void None %void_f
+%label  = OpLabel
+          OpReturn
+          OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+  ASSERT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-None-04643"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Invalid storage class for target environment"));
+}
 }  // namespace
 }  // namespace val
 }  // namespace spvtools
diff --git a/test/val/val_state_test.cpp b/test/val/val_state_test.cpp
index b2d2604..65cb1c3 100644
--- a/test/val/val_state_test.cpp
+++ b/test/val/val_state_test.cpp
@@ -43,7 +43,7 @@
         options_(spvValidatorOptionsCreate()),
         state_(context_, options_, kFakeBinary, 0, 1) {}
 
-  ~ValidationStateTest() {
+  ~ValidationStateTest() override {
     spvContextDestroy(context_);
     spvValidatorOptionsDestroy(options_);
   }
diff --git a/test/val/val_storage_test.cpp b/test/val/val_storage_test.cpp
index e6f98bf..35f6a8d 100644
--- a/test/val/val_storage_test.cpp
+++ b/test/val/val_storage_test.cpp
@@ -30,6 +30,7 @@
 using ValidateStorage = spvtest::ValidateBase<std::string>;
 using ValidateStorageClass =
     spvtest::ValidateBase<std::tuple<std::string, bool, bool, std::string>>;
+using ValidateStorageExecutionModel = spvtest::ValidateBase<std::string>;
 
 TEST_F(ValidateStorage, FunctionStorageInsideFunction) {
   char str[] = R"(
@@ -250,6 +251,46 @@
               HasSubstr("OpFunctionCall Argument <id> '"));
 }
 
+TEST_P(ValidateStorageExecutionModel, VulkanOutsideStoreFailure) {
+  std::stringstream ss;
+  ss << R"(
+              OpCapability Shader
+              OpCapability RayTracingKHR
+              OpExtension "SPV_KHR_ray_tracing"
+              OpMemoryModel Logical GLSL450
+              OpEntryPoint )"
+     << GetParam() << R"(  %func "func" %output
+              OpDecorate %output Location 0
+%intt       = OpTypeInt 32 0
+%int0       = OpConstant %intt 0
+%voidt      = OpTypeVoid
+%vfunct     = OpTypeFunction %voidt
+%outputptrt = OpTypePointer Output %intt
+%output     = OpVariable %outputptrt Output
+%func       = OpFunction %voidt None %vfunct
+%funcl      = OpLabel
+              OpStore %output %int0
+              OpReturn
+              OpFunctionEnd
+)";
+
+  CompileSuccessfully(ss.str(), SPV_ENV_VULKAN_1_0);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-None-04644"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("in Vulkan evironment, Output Storage Class must not be used "
+                "in GLCompute, RayGenerationKHR, IntersectionKHR, AnyHitKHR, "
+                "ClosestHitKHR, MissKHR, or CallableKHR execution models"));
+}
+
+INSTANTIATE_TEST_SUITE_P(MatrixExecutionModel, ValidateStorageExecutionModel,
+                         ::testing::Values("RayGenerationKHR",
+                                           "IntersectionKHR", "AnyHitKHR",
+                                           "ClosestHitKHR", "MissKHR",
+                                           "CallableKHR"));
+
 }  // namespace
 }  // namespace val
 }  // namespace spvtools
diff --git a/test/val/val_validation_state_test.cpp b/test/val/val_validation_state_test.cpp
index 7a38d3a..3dd9e64 100644
--- a/test/val/val_validation_state_test.cpp
+++ b/test/val/val_validation_state_test.cpp
@@ -257,6 +257,8 @@
   EXPECT_EQ(SPV_ERROR_INVALID_BINARY,
             ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1));
   EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-None-04634"));
+  EXPECT_THAT(getDiagnosticString(),
               HasSubstr("Entry points may not have a call graph with cycles.\n "
                         " %1 = OpFunction %void Pure|Const %3\n"));
 }
@@ -274,6 +276,8 @@
   EXPECT_EQ(SPV_ERROR_INVALID_BINARY,
             ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1));
   EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-None-04634"));
+  EXPECT_THAT(getDiagnosticString(),
               HasSubstr("Entry points may not have a call graph with cycles.\n "
                         " %1 = OpFunction %void Pure|Const %3\n"));
 }
diff --git a/tools/as/as.cpp b/tools/as/as.cpp
index f6e9629..c8a4445 100644
--- a/tools/as/as.cpp
+++ b/tools/as/as.cpp
@@ -129,7 +129,7 @@
   }
 
   std::vector<char> contents;
-  if (!ReadFile<char>(inFile, "r", &contents)) return 1;
+  if (!ReadTextFile<char>(inFile, &contents)) return 1;
 
   spv_binary binary;
   spv_diagnostic diagnostic = nullptr;
diff --git a/tools/cfg/cfg.cpp b/tools/cfg/cfg.cpp
index ce7f1c2..a93488d 100644
--- a/tools/cfg/cfg.cpp
+++ b/tools/cfg/cfg.cpp
@@ -104,7 +104,7 @@
 
   // Read the input binary.
   std::vector<uint32_t> contents;
-  if (!ReadFile<uint32_t>(inFile, "rb", &contents)) return 1;
+  if (!ReadBinaryFile<uint32_t>(inFile, &contents)) return 1;
   spv_context context = spvContextCreate(kDefaultEnvironment);
   spv_diagnostic diagnostic = nullptr;
 
diff --git a/tools/dis/dis.cpp b/tools/dis/dis.cpp
index bdeeef1..64380db 100644
--- a/tools/dis/dis.cpp
+++ b/tools/dis/dis.cpp
@@ -180,7 +180,7 @@
 
   // Read the input binary.
   std::vector<uint32_t> contents;
-  if (!ReadFile<uint32_t>(inFile, "rb", &contents)) return 1;
+  if (!ReadBinaryFile<uint32_t>(inFile, &contents)) return 1;
 
   // If printing to standard output, then spvBinaryToText should
   // do the printing.  In particular, colour printing on Windows is
diff --git a/tools/fuzz/fuzz.cpp b/tools/fuzz/fuzz.cpp
index 5ee0fb1..422bea5 100644
--- a/tools/fuzz/fuzz.cpp
+++ b/tools/fuzz/fuzz.cpp
@@ -39,6 +39,8 @@
 
 namespace {
 
+enum class FuzzingTarget { kSpirv, kWgsl };
+
 // Check that the std::system function can actually be used.
 bool CheckExecuteCommand() {
   int res = std::system(nullptr);
@@ -141,6 +143,12 @@
                  that was used previously.
                - simple: each time a fuzzer pass is requested, one is provided
                  at random from the set of enabled passes.
+  --fuzzing-target=
+              This option will adjust probabilities of applying certain
+              transformations s.t. the module always remains valid according
+              to the semantics of some fuzzing target. Available targets:
+              - spir-v - module is valid according to the SPIR-V spec.
+              - wgsl - module is valid according to the WGSL spec.
   --replay
                File from which to read a sequence of transformations to replay
                (instead of fuzzing)
@@ -204,15 +212,15 @@
     std::vector<std::string>* interestingness_test,
     std::string* shrink_transformations_file,
     std::string* shrink_temp_file_prefix,
-    spvtools::fuzz::Fuzzer::RepeatedPassStrategy* repeated_pass_strategy,
-    spvtools::FuzzerOptions* fuzzer_options,
+    spvtools::fuzz::RepeatedPassStrategy* repeated_pass_strategy,
+    FuzzingTarget* fuzzing_target, spvtools::FuzzerOptions* fuzzer_options,
     spvtools::ValidatorOptions* validator_options) {
   uint32_t positional_arg_index = 0;
   bool only_positional_arguments_remain = false;
   bool force_render_red = false;
 
   *repeated_pass_strategy =
-      spvtools::fuzz::Fuzzer::RepeatedPassStrategy::kLoopedWithRecommendations;
+      spvtools::fuzz::RepeatedPassStrategy::kLoopedWithRecommendations;
 
   for (int argi = 1; argi < argc; ++argi) {
     const char* cur_arg = argv[argi];
@@ -250,14 +258,14 @@
                               sizeof("--repeated-pass-strategy=") - 1)) {
         std::string strategy = spvtools::utils::SplitFlagArgs(cur_arg).second;
         if (strategy == "looped") {
-          *repeated_pass_strategy = spvtools::fuzz::Fuzzer::
-              RepeatedPassStrategy::kLoopedWithRecommendations;
+          *repeated_pass_strategy =
+              spvtools::fuzz::RepeatedPassStrategy::kLoopedWithRecommendations;
         } else if (strategy == "random") {
-          *repeated_pass_strategy = spvtools::fuzz::Fuzzer::
-              RepeatedPassStrategy::kRandomWithRecommendations;
+          *repeated_pass_strategy =
+              spvtools::fuzz::RepeatedPassStrategy::kRandomWithRecommendations;
         } else if (strategy == "simple") {
           *repeated_pass_strategy =
-              spvtools::fuzz::Fuzzer::RepeatedPassStrategy::kSimple;
+              spvtools::fuzz::RepeatedPassStrategy::kSimple;
         } else {
           std::stringstream ss;
           ss << "Unknown repeated pass strategy '" << strategy << "'"
@@ -266,6 +274,20 @@
           spvtools::Error(FuzzDiagnostic, nullptr, {}, ss.str().c_str());
           return {FuzzActions::STOP, 1};
         }
+      } else if (0 == strncmp(cur_arg, "--fuzzing-target=",
+                              sizeof("--fuzzing-target=") - 1)) {
+        std::string target = spvtools::utils::SplitFlagArgs(cur_arg).second;
+        if (target == "spir-v") {
+          *fuzzing_target = FuzzingTarget::kSpirv;
+        } else if (target == "wgsl") {
+          *fuzzing_target = FuzzingTarget::kWgsl;
+        } else {
+          std::stringstream ss;
+          ss << "Unknown fuzzing target '" << target << "'" << std::endl;
+          ss << "Valid options are 'spir-v' and 'wgsl'.";
+          spvtools::Error(FuzzDiagnostic, nullptr, {}, ss.str().c_str());
+          return {FuzzActions::STOP, 1};
+        }
       } else if (0 == strncmp(cur_arg, "--replay-range=",
                               sizeof("--replay-range=") - 1)) {
         const auto split_flag = spvtools::utils::SplitFlagArgs(cur_arg);
@@ -549,8 +571,8 @@
           const std::vector<uint32_t>& binary_in,
           const spvtools::fuzz::protobufs::FactSequence& initial_facts,
           const std::string& donors,
-          spvtools::fuzz::Fuzzer::RepeatedPassStrategy repeated_pass_strategy,
-          std::vector<uint32_t>* binary_out,
+          spvtools::fuzz::RepeatedPassStrategy repeated_pass_strategy,
+          FuzzingTarget fuzzing_target, std::vector<uint32_t>* binary_out,
           spvtools::fuzz::protobufs::TransformationSequence*
               transformations_applied) {
   auto message_consumer = spvtools::utils::CLIMessageConsumer;
@@ -568,8 +590,8 @@
         [donor_filename, message_consumer,
          target_env]() -> std::unique_ptr<spvtools::opt::IRContext> {
           std::vector<uint32_t> donor_binary;
-          if (!ReadFile<uint32_t>(donor_filename.c_str(), "rb",
-                                  &donor_binary)) {
+          if (!ReadBinaryFile<uint32_t>(donor_filename.c_str(),
+                                        &donor_binary)) {
             return nullptr;
           }
           return spvtools::BuildModule(target_env, message_consumer,
@@ -578,24 +600,46 @@
         });
   }
 
-  auto fuzz_result =
-      spvtools::fuzz::Fuzzer(
-          target_env, message_consumer, binary_in, initial_facts,
-          donor_suppliers,
-          spvtools::MakeUnique<spvtools::fuzz::PseudoRandomGenerator>(
-              fuzzer_options->has_random_seed
-                  ? fuzzer_options->random_seed
-                  : static_cast<uint32_t>(std::random_device()())),
-          fuzzer_options->all_passes_enabled, repeated_pass_strategy,
-          fuzzer_options->fuzzer_pass_validation_enabled, validator_options)
-          .Run();
-  *binary_out = std::move(fuzz_result.transformed_binary);
-  *transformations_applied = std::move(fuzz_result.applied_transformations);
-  if (fuzz_result.status !=
-      spvtools::fuzz::Fuzzer::FuzzerResultStatus::kComplete) {
+  std::unique_ptr<spvtools::opt::IRContext> ir_context;
+  if (!spvtools::fuzz::fuzzerutil::BuildIRContext(target_env, message_consumer,
+                                                  binary_in, validator_options,
+                                                  &ir_context)) {
+    spvtools::Error(FuzzDiagnostic, nullptr, {}, "Initial binary is invalid");
+    return false;
+  }
+
+  assert((fuzzing_target == FuzzingTarget::kWgsl ||
+          fuzzing_target == FuzzingTarget::kSpirv) &&
+         "Not all fuzzing targets are handled");
+  auto fuzzer_context = spvtools::MakeUnique<spvtools::fuzz::FuzzerContext>(
+      spvtools::MakeUnique<spvtools::fuzz::PseudoRandomGenerator>(
+          fuzzer_options->has_random_seed
+              ? fuzzer_options->random_seed
+              : static_cast<uint32_t>(std::random_device()())),
+      spvtools::fuzz::FuzzerContext::GetMinFreshId(ir_context.get()),
+      fuzzing_target == FuzzingTarget::kWgsl);
+
+  auto transformation_context =
+      spvtools::MakeUnique<spvtools::fuzz::TransformationContext>(
+          spvtools::MakeUnique<spvtools::fuzz::FactManager>(ir_context.get()),
+          validator_options);
+  transformation_context->GetFactManager()->AddInitialFacts(message_consumer,
+                                                            initial_facts);
+
+  spvtools::fuzz::Fuzzer fuzzer(
+      std::move(ir_context), std::move(transformation_context),
+      std::move(fuzzer_context), message_consumer, donor_suppliers,
+      fuzzer_options->all_passes_enabled, repeated_pass_strategy,
+      fuzzer_options->fuzzer_pass_validation_enabled, validator_options);
+  auto fuzz_result = fuzzer.Run(0);
+  if (fuzz_result.status ==
+      spvtools::fuzz::Fuzzer::Status::kFuzzerPassLedToInvalidModule) {
     spvtools::Error(FuzzDiagnostic, nullptr, {}, "Error running fuzzer");
     return false;
   }
+
+  fuzzer.GetIRContext()->module()->ToBinary(binary_out, true);
+  *transformations_applied = fuzzer.GetTransformationSequence();
   return true;
 }
 
@@ -656,7 +700,8 @@
   std::vector<std::string> interestingness_test;
   std::string shrink_transformations_file;
   std::string shrink_temp_file_prefix = "temp_";
-  spvtools::fuzz::Fuzzer::RepeatedPassStrategy repeated_pass_strategy;
+  spvtools::fuzz::RepeatedPassStrategy repeated_pass_strategy;
+  auto fuzzing_target = FuzzingTarget::kSpirv;
 
   spvtools::FuzzerOptions fuzzer_options;
   spvtools::ValidatorOptions validator_options;
@@ -665,14 +710,15 @@
       ParseFlags(argc, argv, &in_binary_file, &out_binary_file, &donors_file,
                  &replay_transformations_file, &interestingness_test,
                  &shrink_transformations_file, &shrink_temp_file_prefix,
-                 &repeated_pass_strategy, &fuzzer_options, &validator_options);
+                 &repeated_pass_strategy, &fuzzing_target, &fuzzer_options,
+                 &validator_options);
 
   if (status.action == FuzzActions::STOP) {
     return status.code;
   }
 
   std::vector<uint32_t> binary_in;
-  if (!ReadFile<uint32_t>(in_binary_file.c_str(), "rb", &binary_in)) {
+  if (!ReadBinaryFile<uint32_t>(in_binary_file.c_str(), &binary_in)) {
     return 1;
   }
 
@@ -703,16 +749,16 @@
 
   switch (status.action) {
     case FuzzActions::FORCE_RENDER_RED:
-      if (!spvtools::fuzz::ForceRenderRed(target_env, validator_options,
-                                          binary_in, initial_facts,
-                                          &binary_out)) {
+      if (!spvtools::fuzz::ForceRenderRed(
+              target_env, validator_options, binary_in, initial_facts,
+              spvtools::utils::CLIMessageConsumer, &binary_out)) {
         return 1;
       }
       break;
     case FuzzActions::FUZZ:
       if (!Fuzz(target_env, fuzzer_options, validator_options, binary_in,
-                initial_facts, donors_file, repeated_pass_strategy, &binary_out,
-                &transformations_applied)) {
+                initial_facts, donors_file, repeated_pass_strategy,
+                fuzzing_target, &binary_out, &transformations_applied)) {
         return 1;
       }
       break;
diff --git a/tools/io.h b/tools/io.h
index f9cfd9d..aff9eab 100644
--- a/tools/io.h
+++ b/tools/io.h
@@ -20,45 +20,101 @@
 #include <cstring>
 #include <vector>
 
-// Appends the content from the file named as |filename| to |data|, assuming
-// each element in the file is of type |T|. The file is opened with the given
-// |mode|. If |filename| is nullptr or "-", reads from the standard input, but
-// reopened with the given mode. If any error occurs, writes error messages to
-// standard error and returns false.
+#if defined(SPIRV_WINDOWS)
+#include <fcntl.h>
+#include <io.h>
+
+#define SET_STDIN_TO_BINARY_MODE() _setmode(_fileno(stdin), O_BINARY);
+#define SET_STDIN_TO_TEXT_MODE() _setmode(_fileno(stdin), O_TEXT);
+#else
+#define SET_STDIN_TO_BINARY_MODE()
+#define SET_STDIN_TO_TEXT_MODE()
+#endif
+
+// Appends the contents of the |file| to |data|, assuming each element in the
+// file is of type |T|.
 template <typename T>
-bool ReadFile(const char* filename, const char* mode, std::vector<T>* data) {
+void ReadFile(FILE* file, std::vector<T>* data) {
+  if (file == nullptr) return;
+
   const int buf_size = 1024;
-  const bool use_file = filename && strcmp("-", filename);
-  if (FILE* fp =
-          (use_file ? fopen(filename, mode) : freopen(nullptr, mode, stdin))) {
-    T buf[buf_size];
-    while (size_t len = fread(buf, sizeof(T), buf_size, fp)) {
-      data->insert(data->end(), buf, buf + len);
-    }
-    if (ftell(fp) == -1L) {
-      if (ferror(fp)) {
-        fprintf(stderr, "error: error reading file '%s'\n", filename);
-        if (use_file) fclose(fp);
-        return false;
-      }
-    } else {
-      if (sizeof(T) != 1 && (ftell(fp) % sizeof(T))) {
-        fprintf(
-            stderr,
-            "error: file size should be a multiple of %zd; file '%s' corrupt\n",
-            sizeof(T), filename);
-        if (use_file) fclose(fp);
-        return false;
-      }
-    }
-    if (use_file) fclose(fp);
-  } else {
+  T buf[buf_size];
+  while (size_t len = fread(buf, sizeof(T), buf_size, file)) {
+    data->insert(data->end(), buf, buf + len);
+  }
+}
+
+// Returns true if |file| has encountered an error opening the file or reading
+// the file as a series of element of type |T|. If there was an error, writes an
+// error message to standard error.
+template <class T>
+bool WasFileCorrectlyRead(FILE* file, const char* filename) {
+  if (file == nullptr) {
     fprintf(stderr, "error: file does not exist '%s'\n", filename);
     return false;
   }
+
+  if (ftell(file) == -1L) {
+    if (ferror(file)) {
+      fprintf(stderr, "error: error reading file '%s'\n", filename);
+      return false;
+    }
+  } else {
+    if (sizeof(T) != 1 && (ftell(file) % sizeof(T))) {
+      fprintf(
+          stderr,
+          "error: file size should be a multiple of %zd; file '%s' corrupt\n",
+          sizeof(T), filename);
+      return false;
+    }
+  }
   return true;
 }
 
+// Appends the contents of the file named |filename| to |data|, assuming
+// each element in the file is of type |T|. The file is opened as a binary file
+// If |filename| is nullptr or "-", reads from the standard input, but
+// reopened as a binary file. If any error occurs, writes error messages to
+// standard error and returns false.
+template <typename T>
+bool ReadBinaryFile(const char* filename, std::vector<T>* data) {
+  const bool use_file = filename && strcmp("-", filename);
+  FILE* fp = nullptr;
+  if (use_file) {
+    fp = fopen(filename, "rb");
+  } else {
+    SET_STDIN_TO_BINARY_MODE();
+    fp = stdin;
+  }
+
+  ReadFile(fp, data);
+  bool succeeded = WasFileCorrectlyRead<T>(fp, filename);
+  if (use_file) fclose(fp);
+  return succeeded;
+}
+
+// Appends the contents of the file named |filename| to |data|, assuming
+// each element in the file is of type |T|. The file is opened as a text file
+// If |filename| is nullptr or "-", reads from the standard input, but
+// reopened as a text file. If any error occurs, writes error messages to
+// standard error and returns false.
+template <typename T>
+bool ReadTextFile(const char* filename, std::vector<T>* data) {
+  const bool use_file = filename && strcmp("-", filename);
+  FILE* fp = nullptr;
+  if (use_file) {
+    fp = fopen(filename, "r");
+  } else {
+    SET_STDIN_TO_TEXT_MODE();
+    fp = stdin;
+  }
+
+  ReadFile(fp, data);
+  bool succeeded = WasFileCorrectlyRead<T>(fp, filename);
+  if (use_file) fclose(fp);
+  return succeeded;
+}
+
 // Writes the given |data| into the file named as |filename| using the given
 // |mode|, assuming |data| is an array of |count| elements of type |T|. If
 // |filename| is nullptr or "-", writes to standard output. If any error occurs,
diff --git a/tools/link/linker.cpp b/tools/link/linker.cpp
index 1956f59..359e803 100644
--- a/tools/link/linker.cpp
+++ b/tools/link/linker.cpp
@@ -130,7 +130,7 @@
 
   std::vector<std::vector<uint32_t>> contents(inFiles.size());
   for (size_t i = 0u; i < inFiles.size(); ++i) {
-    if (!ReadFile<uint32_t>(inFiles[i], "rb", &contents[i])) return 1;
+    if (!ReadBinaryFile<uint32_t>(inFiles[i], &contents[i])) return 1;
   }
 
   const spvtools::MessageConsumer consumer = [](spv_message_level_t level,
diff --git a/tools/opt/opt.cpp b/tools/opt/opt.cpp
index 6999b39..b9339ab 100644
--- a/tools/opt/opt.cpp
+++ b/tools/opt/opt.cpp
@@ -807,7 +807,7 @@
   }
 
   std::vector<uint32_t> binary;
-  if (!ReadFile<uint32_t>(in_file, "rb", &binary)) {
+  if (!ReadBinaryFile<uint32_t>(in_file, &binary)) {
     return 1;
   }
 
diff --git a/tools/reduce/reduce.cpp b/tools/reduce/reduce.cpp
index 49a5efe..4447b35 100644
--- a/tools/reduce/reduce.cpp
+++ b/tools/reduce/reduce.cpp
@@ -318,7 +318,7 @@
   reducer.SetMessageConsumer(spvtools::utils::CLIMessageConsumer);
 
   std::vector<uint32_t> binary_in;
-  if (!ReadFile<uint32_t>(in_binary_file.c_str(), "rb", &binary_in)) {
+  if (!ReadBinaryFile<uint32_t>(in_binary_file.c_str(), &binary_in)) {
     return 1;
   }
 
diff --git a/tools/sva/yarn.lock b/tools/sva/yarn.lock
index c46b901..4924690 100644
--- a/tools/sva/yarn.lock
+++ b/tools/sva/yarn.lock
@@ -938,9 +938,9 @@
     path-exists "^3.0.0"
 
 lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14:
-  version "4.17.19"
-  resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b"
-  integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==
+  version "4.17.21"
+  resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
+  integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
 
 log-symbols@2.2.0:
   version "2.2.0"
diff --git a/tools/val/val.cpp b/tools/val/val.cpp
index 5450023..21a7d8f 100644
--- a/tools/val/val.cpp
+++ b/tools/val/val.cpp
@@ -186,7 +186,7 @@
   }
 
   std::vector<uint32_t> contents;
-  if (!ReadFile<uint32_t>(inFile, "rb", &contents)) return 1;
+  if (!ReadBinaryFile<uint32_t>(inFile, &contents)) return 1;
 
   spvtools::SpirvTools tools(target_env);
   tools.SetMessageConsumer(spvtools::utils::CLIMessageConsumer);
diff --git a/utils/check_copyright.py b/utils/check_copyright.py
index b15bc20..c525123 100755
--- a/utils/check_copyright.py
+++ b/utils/check_copyright.py
@@ -36,8 +36,11 @@
            'André Perez Maselco',
            'Vasyl Teliman',
            'Advanced Micro Devices, Inc.',
-           'Stefano Milizia']
-CURRENT_YEAR='2020'
+           'Stefano Milizia',
+           'Alastair F. Donaldson',
+           'Mostafa Ashraf',
+           'Shiyu Liu']
+CURRENT_YEAR='2021'
 
 YEARS = '(2014-2016|2015-2016|2015-2020|2016|2016-2017|2017|2017-2019|2018|2019|2020|2021)'
 COPYRIGHT_RE = re.compile(
@@ -128,7 +131,7 @@
         update_file = False
         for line in fileinput.input(file, inplace=1):
             emit = True
-            if state is 0:
+            if state == 0:
                 if COPYRIGHT_RE.search(line):
                     state = 1
                 elif skip(line):
@@ -139,7 +142,7 @@
                     sys.stdout.write(licensed)
                     # Assume there isn't a previous license notice.
                     state = 1
-            elif state is 1:
+            elif state == 1:
                 if MIT_BEGIN_RE.search(line):
                     state = 2
                     emit = False
@@ -147,7 +150,7 @@
                     # Assume an Apache license is preceded by a copyright
                     # notice.  So just emit it like the rest of the file.
                     state = 9
-            elif state is 2:
+            elif state == 2:
                 # Replace the MIT license with Apache 2
                 emit = False
                 if MIT_END_RE.search(line):
diff --git a/utils/check_symbol_exports.py b/utils/check_symbol_exports.py
index bcd77da..7795d72 100755
--- a/utils/check_symbol_exports.py
+++ b/utils/check_symbol_exports.py
@@ -12,7 +12,13 @@
 # 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.
-"""Checks names of global exports from a library."""
+"""Ensures that all externally visible functions in the library have an appropriate name
+
+Appropriate function names are:
+  - names starting with spv,
+  - anything in a namespace,
+  - functions added by the protobuf compiler,
+  - and weak definitions of new and delete."""
 
 import os.path
 import re
@@ -46,14 +52,16 @@
     exports are namespaced or begin with spv (in either C or C++ styles)
     then return 0.  Otherwise emit a message and return 1."""
 
-    # The pattern for a global symbol record
-    symbol_pattern = re.compile(r'^[0-aA-Fa-f]+ g *F \.text.*[0-9A-Fa-f]+ +(.*)')
+    # The pattern for an externally visible symbol record
+    symbol_pattern = re.compile(r'^[0-aA-Fa-f]+ +([wg]) *F \.text.*[0-9A-Fa-f]+ +(.*)')
 
     # Ok patterns are as follows, assuming Itanium name mangling:
     #   spv[A-Z]          :  extern "C" symbol starting with spv
     #   _ZN               :  something in a namespace
+    #   _ZSt              :  something in the standard namespace
+    #   _ZZN              :  something in a local scope and namespace
     #   _Z[0-9]+spv[A-Z_] :  C++ symbol starting with spv[A-Z_]
-    symbol_ok_pattern = re.compile(r'^(spv[A-Z]|_ZN|_Z[0-9]+spv[A-Z_])')
+    symbol_ok_pattern = re.compile(r'^(spv[A-Z]|_ZN|_ZSt|_ZZN|_Z[0-9]+spv[A-Z_])')
 
     # In addition, the following pattern allowlists global functions that are added
     # by the protobuf compiler:
@@ -61,18 +69,31 @@
     #   - InitDefaults_spvtoolsfuzz_2eproto()
     symbol_allowlist_pattern = re.compile(r'_Z[0-9]+(InitDefaults|AddDescriptors)_spvtoolsfuzz_2eprotov')
 
+    symbol_is_new_or_delete = re.compile(r'^(_Zna|_Znw|_Zdl|_Zda)')
+    # Compilaion for Arm has various thunks for constructors, destructors, vtables.
+    # They are weak.
+    symbol_is_thunk = re.compile(r'^_ZT')
+
+    # This occurs in NDK builds.
+    symbol_is_hidden = re.compile(r'^\.hidden ')
+
     seen = set()
     result = 0
     for line in command_output(['objdump', '-t', library], '.').split('\n'):
         match = symbol_pattern.search(line)
         if match:
-            symbol = match.group(1)
+            linkage = match.group(1)
+            symbol = match.group(2)
             if symbol not in seen:
                 seen.add(symbol)
                 #print("look at '{}'".format(symbol))
-                if not (symbol_allowlist_pattern.match(symbol) or symbol_ok_pattern.match(symbol)):
-                    print('{}: error: Unescaped exported symbol: {}'.format(PROG, symbol))
-                    result = 1
+                if not (symbol_is_new_or_delete.match(symbol) and linkage == 'w'):
+                    if not (symbol_is_thunk.match(symbol) and linkage == 'w'):
+                        if not (symbol_allowlist_pattern.match(symbol) or
+                                symbol_ok_pattern.match(symbol) or
+                                symbol_is_hidden.match(symbol)):
+                            print('{}: error: Unescaped exported symbol: {}'.format(PROG, symbol))
+                            result = 1
     return result
 
 
diff --git a/utils/generate_grammar_tables.py b/utils/generate_grammar_tables.py
index 2a67733..9ccf410 100755
--- a/utils/generate_grammar_tables.py
+++ b/utils/generate_grammar_tables.py
@@ -523,17 +523,17 @@
     enums = [generate_enum_operand_kind(e, exts) for e in enums]
     exts_arrays = generate_extension_arrays(exts)
 
-    # We have three operand kinds that requires their optional counterpart to
+    # We have a few operand kinds that require their optional counterpart to
     # exist in the operand info table.
-    three_optional_enums = ['ImageOperands', 'AccessQualifier', 'MemoryAccess']
-    three_optional_enums = [e for e in enums if e[0] in three_optional_enums]
-    enums.extend(three_optional_enums)
+    optional_enums = ['ImageOperands', 'AccessQualifier', 'MemoryAccess', 'PackedVectorFormat']
+    optional_enums = [e for e in enums if e[0] in optional_enums]
+    enums.extend(optional_enums)
 
     enum_kinds, enum_names, enum_entries = zip(*enums)
-    # Mark the last three as optional ones.
-    enum_quantifiers = [''] * (len(enums) - 3) + ['?'] * 3
+    # Mark the last few as optional ones.
+    enum_quantifiers = [''] * (len(enums) - len(optional_enums)) + ['?'] * len(optional_enums)
     # And we don't want redefinition of them.
-    enum_entries = enum_entries[:-3]
+    enum_entries = enum_entries[:-len(optional_enums)]
     enum_kinds = [convert_operand_kind(e)
                   for e in zip(enum_kinds, enum_quantifiers)]
     table_entries = zip(enum_kinds, enum_names, enum_names)
@@ -601,7 +601,7 @@
         '      return "{extension}";\n'
     function += ''.join([template.format(extension=extension)
                          for extension in extensions])
-    function += '  };\n\n  return "";\n}'
+    function += '  }\n\n  return "";\n}'
     return function
 
 
@@ -647,7 +647,7 @@
     function += '    case SpvCapabilityMax:\n' \
         '      assert(0 && "Attempting to convert SpvCapabilityMax to string");\n' \
         '      return "";\n'
-    function += '  };\n\n  return "";\n}'
+    function += '  }\n\n  return "";\n}'
     return function
 
 
diff --git a/utils/vscode/README.md b/utils/vscode/README.md
index bc02211..d7aa2b4 100644
--- a/utils/vscode/README.md
+++ b/utils/vscode/README.md
@@ -1,12 +1,20 @@
 # Visual Studio Code extension for SPIR-V disassembly files
 
-This directory holds a Visual Studio Code extension adding syntax highlighting for SPIR-V assembly files (`.spvasm`)
+This directory holds a Visual Studio Code language server extension for SPIR-V assembly files (`.spvasm`)
+
+The extension supports:
+* Syntax highlighting
+* Jump to definition
+* Find all references
+* Symbol renaming
+* Operand hover information
+* Formatting
 
 ## Dependencies
 
 In order to build and install the Visual Studio Code language server extension, you will need to install and have on your `PATH` the following dependencies:
 * [`npm`](https://www.npmjs.com/)
-* [`golang`](https://golang.org/)
+* [`golang 1.16+`](https://golang.org/)
 
 ## Installing (macOS / Linux)
 
diff --git a/utils/vscode/go.mod b/utils/vscode/go.mod
new file mode 100644
index 0000000..ea4901a
--- /dev/null
+++ b/utils/vscode/go.mod
@@ -0,0 +1,8 @@
+module github.com/KhronosGroup/SPIRV-Tools/utils/vscode
+
+go 1.16
+
+require (
+	github.com/pkg/errors v0.9.1
+	golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1
+)
diff --git a/utils/vscode/go.sum b/utils/vscode/go.sum
new file mode 100644
index 0000000..328c857
--- /dev/null
+++ b/utils/vscode/go.sum
@@ -0,0 +1,4 @@
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
diff --git a/utils/vscode/install.bat b/utils/vscode/install.bat
index 21a52ec..aa06fa9 100644
--- a/utils/vscode/install.bat
+++ b/utils/vscode/install.bat
@@ -23,7 +23,7 @@
 copy %ROOT_PATH%\package.json %EXT_PATH%
 copy %ROOT_PATH%\spirv.json %EXT_PATH%
 
-go build -o %EXT_PATH%\langsvr %ROOT_PATH%\src\langsvr.go
+go build -o %EXT_PATH%\langsvr.exe %ROOT_PATH%\src\langsvr.go
 
 @pushd %EXT_PATH%
 call npm install
diff --git a/utils/vscode/install.sh b/utils/vscode/install.sh
index 01fc914..de54c62 100755
--- a/utils/vscode/install.sh
+++ b/utils/vscode/install.sh
@@ -18,15 +18,17 @@
 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
+pushd ${ROOT_PATH}
+    go run ./src/tools/gen-grammar.go --cache ./cache --template ./spirv.json.tmpl --out ./spirv.json
+    go run ./src/tools/gen-grammar.go --cache ./cache --template ./src/schema/schema.go.tmpl --out ./src/schema/schema.go
 
-mkdir -p ${EXT_PATH}
-cp ${ROOT_PATH}/extension.js ${EXT_PATH}
-cp ${ROOT_PATH}/package.json ${EXT_PATH}
-cp ${ROOT_PATH}/spirv.json ${EXT_PATH}
+    mkdir -p ${EXT_PATH}
+    cp ./extension.js ${EXT_PATH}
+    cp ./package.json ${EXT_PATH}
+    cp ./spirv.json ${EXT_PATH}
 
-go build -o ${EXT_PATH}/langsvr ${ROOT_PATH}/src/langsvr.go
+    go build -o ${EXT_PATH}/langsvr ./src/langsvr.go
+popd
 
 cd ${EXT_PATH}
 npm install
diff --git a/utils/vscode/spirv.json b/utils/vscode/spirv.json
index 7999b52..2e88296 100644
--- a/utils/vscode/spirv.json
+++ b/utils/vscode/spirv.json
@@ -1,7 +1,7 @@
 {
 	"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.",
+	"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" },
@@ -11,6 +11,7 @@
 		{ "include": "#BitEnum_MemorySemantics" },
 		{ "include": "#BitEnum_MemoryAccess" },
 		{ "include": "#BitEnum_KernelProfilingInfo" },
+		{ "include": "#BitEnum_RayFlags" },
 		{ "include": "#ValueEnum_SourceLanguage" },
 		{ "include": "#ValueEnum_ExecutionModel" },
 		{ "include": "#ValueEnum_AddressingModel" },
@@ -33,6 +34,9 @@
 		{ "include": "#ValueEnum_GroupOperation" },
 		{ "include": "#ValueEnum_KernelEnqueueFlags" },
 		{ "include": "#ValueEnum_Capability" },
+		{ "include": "#ValueEnum_RayQueryIntersection" },
+		{ "include": "#ValueEnum_RayQueryCommittedIntersectionType" },
+		{ "include": "#ValueEnum_RayQueryCandidateIntersectionType" },
 		{ "include": "#BitEnum_DebugInfoFlags" },
 		{ "include": "#ValueEnum_DebugBaseTypeAttributeEncoding" },
 		{ "include": "#ValueEnum_DebugCompositeType" },
@@ -80,12 +84,16 @@
 			"match": "\\b(None|CmdExecTime)\\b",
 			"name": "keyword.spirv"
 		},
+		"BitEnum_RayFlags": {
+			"match": "\\b(NoneKHR|OpaqueKHR|NoOpaqueKHR|TerminateOnFirstHitKHR|SkipClosestHitShaderKHR|CullBackFacingTrianglesKHR|CullFrontFacingTrianglesKHR|CullOpaqueKHR|CullNoOpaqueKHR|SkipTrianglesKHR|SkipAABBsKHR)\\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",
+			"match": "\\b(Vertex|TessellationControl|TessellationEvaluation|Geometry|Fragment|GLCompute|Kernel|TaskNV|MeshNV|RayGenerationNV|RayGenerationKHR|IntersectionNV|IntersectionKHR|AnyHitNV|AnyHitKHR|ClosestHitNV|ClosestHitKHR|MissNV|MissKHR|CallableNV|CallableKHR)\\b",
 			"name": "keyword.spirv"
 		},
 		"ValueEnum_AddressingModel": {
@@ -101,7 +109,7 @@
 			"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",
+			"match": "\\b(UniformConstant|Input|Uniform|Output|Workgroup|CrossWorkgroup|Private|Function|Generic|PushConstant|AtomicCounter|Image|StorageBuffer|CallableDataNV|CallableDataKHR|IncomingCallableDataNV|IncomingCallableDataKHR|RayPayloadNV|RayPayloadKHR|HitAttributeNV|HitAttributeKHR|IncomingRayPayloadNV|IncomingRayPayloadKHR|ShaderRecordBufferNV|ShaderRecordBufferKHR|PhysicalStorageBuffer|PhysicalStorageBufferEXT)\\b",
 			"name": "keyword.spirv"
 		},
 		"ValueEnum_Dim": {
@@ -149,11 +157,11 @@
 			"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",
+			"match": "\\b(Position|PointSize|ClipDistance|CullDistance|VertexId|InstanceId|PrimitiveId|InvocationId|Layer|ViewportIndex|TessLevelOuter|TessLevelInner|TessCoord|PatchVertices|FragCoord|PointCoord|FrontFacing|SampleId|SamplePosition|SampleMask|FragDepth|HelperInvocation|NumWorkgroups|WorkgroupSize|WorkgroupId|LocalInvocationId|GlobalInvocationId|LocalInvocationIndex|WorkDim|GlobalSize|EnqueuedWorkgroupSize|GlobalOffset|GlobalLinearId|SubgroupSize|SubgroupMaxSize|NumSubgroups|NumEnqueuedSubgroups|SubgroupId|SubgroupLocalInvocationId|VertexIndex|InstanceIndex|SubgroupEqMask|SubgroupGeMask|SubgroupGtMask|SubgroupLeMask|SubgroupLtMask|SubgroupEqMaskKHR|SubgroupGeMaskKHR|SubgroupGtMaskKHR|SubgroupLeMaskKHR|SubgroupLtMaskKHR|BaseVertex|BaseInstance|DrawIndex|DeviceIndex|ViewIndex|BaryCoordNoPerspAMD|BaryCoordNoPerspCentroidAMD|BaryCoordNoPerspSampleAMD|BaryCoordSmoothAMD|BaryCoordSmoothCentroidAMD|BaryCoordSmoothSampleAMD|BaryCoordPullModelAMD|FragStencilRefEXT|ViewportMaskNV|SecondaryPositionNV|SecondaryViewportMaskNV|PositionPerViewNV|ViewportMaskPerViewNV|FullyCoveredEXT|TaskCountNV|PrimitiveCountNV|PrimitiveIndicesNV|ClipDistancePerViewNV|CullDistancePerViewNV|LayerPerViewNV|MeshViewCountNV|MeshViewIndicesNV|BaryCoordNV|BaryCoordNoPerspNV|FragSizeEXT|FragmentSizeNV|FragInvocationCountEXT|InvocationsPerPixelNV|LaunchIdNV|LaunchIdKHR|LaunchSizeNV|LaunchSizeKHR|WorldRayOriginNV|WorldRayOriginKHR|WorldRayDirectionNV|WorldRayDirectionKHR|ObjectRayOriginNV|ObjectRayOriginKHR|ObjectRayDirectionNV|ObjectRayDirectionKHR|RayTminNV|RayTminKHR|RayTmaxNV|RayTmaxKHR|InstanceCustomIndexNV|InstanceCustomIndexKHR|ObjectToWorldNV|ObjectToWorldKHR|WorldToObjectNV|WorldToObjectKHR|HitTNV|HitTKHR|HitKindNV|HitKindKHR|IncomingRayFlagsNV|IncomingRayFlagsKHR|RayGeometryIndexKHR|WarpsPerSMNV|SMCountNV|WarpIDNV|SMIDNV)\\b",
 			"name": "keyword.spirv"
 		},
 		"ValueEnum_Scope": {
-			"match": "\\b(CrossDevice|Device|Workgroup|Subgroup|Invocation|QueueFamily|QueueFamilyKHR)\\b",
+			"match": "\\b(CrossDevice|Device|Workgroup|Subgroup|Invocation|QueueFamily|QueueFamilyKHR|ShaderCallKHR)\\b",
 			"name": "keyword.spirv"
 		},
 		"ValueEnum_GroupOperation": {
@@ -165,7 +173,19 @@
 			"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",
+			"match": "\\b(Matrix|Shader|Geometry|Tessellation|Addresses|Linkage|Kernel|Vector16|Float16Buffer|Float16|Float64|Int64|Int64Atomics|ImageBasic|ImageReadWrite|ImageMipmap|Pipes|Groups|DeviceEnqueue|LiteralSampler|AtomicStorage|Int16|TessellationPointSize|GeometryPointSize|ImageGatherExtended|StorageImageMultisample|UniformBufferArrayDynamicIndexing|SampledImageArrayDynamicIndexing|StorageBufferArrayDynamicIndexing|StorageImageArrayDynamicIndexing|ClipDistance|CullDistance|ImageCubeArray|SampleRateShading|ImageRect|SampledRect|GenericPointer|Int8|InputAttachment|SparseResidency|MinLod|Sampled1D|Image1D|SampledCubeArray|SampledBuffer|ImageBuffer|ImageMSArray|StorageImageExtendedFormats|ImageQuery|DerivativeControl|InterpolationFunction|TransformFeedback|GeometryStreams|StorageImageReadWithoutFormat|StorageImageWriteWithoutFormat|MultiViewport|SubgroupDispatch|NamedBarrier|PipeStorage|GroupNonUniform|GroupNonUniformVote|GroupNonUniformArithmetic|GroupNonUniformBallot|GroupNonUniformShuffle|GroupNonUniformShuffleRelative|GroupNonUniformClustered|GroupNonUniformQuad|ShaderLayer|ShaderViewportIndex|SubgroupBallotKHR|DrawParameters|SubgroupVoteKHR|StorageBuffer16BitAccess|StorageUniformBufferBlock16|UniformAndStorageBuffer16BitAccess|StorageUniform16|StoragePushConstant16|StorageInputOutput16|DeviceGroup|MultiView|VariablePointersStorageBuffer|VariablePointers|AtomicStorageOps|SampleMaskPostDepthCoverage|StorageBuffer8BitAccess|UniformAndStorageBuffer8BitAccess|StoragePushConstant8|DenormPreserve|DenormFlushToZero|SignedZeroInfNanPreserve|RoundingModeRTE|RoundingModeRTZ|RayQueryProvisionalKHR|RayTraversalPrimitiveCullingProvisionalKHR|Float16ImageAMD|ImageGatherBiasLodAMD|FragmentMaskAMD|StencilExportEXT|ImageReadWriteLodAMD|ShaderClockKHR|SampleMaskOverrideCoverageNV|GeometryShaderPassthroughNV|ShaderViewportIndexLayerEXT|ShaderViewportIndexLayerNV|ShaderViewportMaskNV|ShaderStereoViewNV|PerViewAttributesNV|FragmentFullyCoveredEXT|MeshShadingNV|ImageFootprintNV|FragmentBarycentricNV|ComputeDerivativeGroupQuadsNV|FragmentDensityEXT|ShadingRateNV|GroupNonUniformPartitionedNV|ShaderNonUniform|ShaderNonUniformEXT|RuntimeDescriptorArray|RuntimeDescriptorArrayEXT|InputAttachmentArrayDynamicIndexing|InputAttachmentArrayDynamicIndexingEXT|UniformTexelBufferArrayDynamicIndexing|UniformTexelBufferArrayDynamicIndexingEXT|StorageTexelBufferArrayDynamicIndexing|StorageTexelBufferArrayDynamicIndexingEXT|UniformBufferArrayNonUniformIndexing|UniformBufferArrayNonUniformIndexingEXT|SampledImageArrayNonUniformIndexing|SampledImageArrayNonUniformIndexingEXT|StorageBufferArrayNonUniformIndexing|StorageBufferArrayNonUniformIndexingEXT|StorageImageArrayNonUniformIndexing|StorageImageArrayNonUniformIndexingEXT|InputAttachmentArrayNonUniformIndexing|InputAttachmentArrayNonUniformIndexingEXT|UniformTexelBufferArrayNonUniformIndexing|UniformTexelBufferArrayNonUniformIndexingEXT|StorageTexelBufferArrayNonUniformIndexing|StorageTexelBufferArrayNonUniformIndexingEXT|RayTracingNV|VulkanMemoryModel|VulkanMemoryModelKHR|VulkanMemoryModelDeviceScope|VulkanMemoryModelDeviceScopeKHR|PhysicalStorageBufferAddresses|PhysicalStorageBufferAddressesEXT|ComputeDerivativeGroupLinearNV|RayTracingProvisionalKHR|CooperativeMatrixNV|FragmentShaderSampleInterlockEXT|FragmentShaderShadingRateInterlockEXT|ShaderSMBuiltinsNV|FragmentShaderPixelInterlockEXT|DemoteToHelperInvocationEXT|SubgroupShuffleINTEL|SubgroupBufferBlockIOINTEL|SubgroupImageBlockIOINTEL|SubgroupImageMediaBlockIOINTEL|IntegerFunctions2INTEL|SubgroupAvcMotionEstimationINTEL|SubgroupAvcMotionEstimationIntraINTEL|SubgroupAvcMotionEstimationChromaINTEL)\\b",
+			"name": "keyword.spirv"
+		},
+		"ValueEnum_RayQueryIntersection": {
+			"match": "\\b(RayQueryCandidateIntersectionKHR|RayQueryCommittedIntersectionKHR)\\b",
+			"name": "keyword.spirv"
+		},
+		"ValueEnum_RayQueryCommittedIntersectionType": {
+			"match": "\\b(RayQueryCommittedIntersectionNoneKHR|RayQueryCommittedIntersectionTriangleKHR|RayQueryCommittedIntersectionGeneratedKHR)\\b",
+			"name": "keyword.spirv"
+		},
+		"ValueEnum_RayQueryCandidateIntersectionType": {
+			"match": "\\b(RayQueryCandidateIntersectionTriangleKHR|RayQueryCandidateIntersectionAABBKHR)\\b",
 			"name": "keyword.spirv"
 		},
 		"BitEnum_DebugInfoFlags": {
diff --git a/utils/vscode/src/langsvr.go b/utils/vscode/src/langsvr.go
index d1b80dc..b76e35f 100644
--- a/utils/vscode/src/langsvr.go
+++ b/utils/vscode/src/langsvr.go
@@ -28,11 +28,15 @@
 	"sync"
 	"unicode/utf8"
 
-	"./parser"
-	"./schema"
+	"github.com/KhronosGroup/SPIRV-Tools/utils/vscode/src/parser"
+	"github.com/KhronosGroup/SPIRV-Tools/utils/vscode/src/schema"
 
-	"./lsp/jsonrpc2"
-	lsp "./lsp/protocol"
+	"github.com/KhronosGroup/SPIRV-Tools/utils/vscode/src/lsp/jsonrpc2"
+	lsp "github.com/KhronosGroup/SPIRV-Tools/utils/vscode/src/lsp/protocol"
+)
+
+const (
+	enableDebugLogging = false
 )
 
 // rSpy is a reader 'spy' that wraps an io.Reader, and logs all data that passes
@@ -63,12 +67,13 @@
 
 // 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.SetOutput(ioutil.Discard)
+	if enableDebugLogging {
+		// create a log file in the executable's directory.
+		if logfile, err := os.Create(path.Join(path.Dir(os.Args[0]), "log.txt")); err == nil {
+			defer logfile.Close()
+			log.SetOutput(logfile)
+		}
 	}
 
 	log.Println("language server started")
@@ -407,13 +412,15 @@
 			}
 		}
 
-		// Every good file ends with a new line.
-		sb.WriteString("\n")
+		formatted := sb.String()
+
+		// Every good file ends with a single new line.
+		formatted = strings.TrimRight(formatted, "\n") + "\n"
 
 		return []lsp.TextEdit{
-			lsp.TextEdit{
+			{
 				Range:   rangeToLSP(f.fullRange),
-				NewText: sb.String(),
+				NewText: formatted,
 			},
 		}, nil
 	}
diff --git a/utils/vscode/src/lsp/protocol/log.go b/utils/vscode/src/lsp/protocol/log.go
index f245881..2fd7bbb 100644
--- a/utils/vscode/src/lsp/protocol/log.go
+++ b/utils/vscode/src/lsp/protocol/log.go
@@ -23,7 +23,7 @@
 	"sync"
 	"time"
 
-	"../jsonrpc2"
+	"github.com/KhronosGroup/SPIRV-Tools/utils/vscode/src/lsp/jsonrpc2"
 )
 
 type loggingStream struct {
diff --git a/utils/vscode/src/lsp/protocol/protocol.go b/utils/vscode/src/lsp/protocol/protocol.go
index e396c83..886b0aa 100644
--- a/utils/vscode/src/lsp/protocol/protocol.go
+++ b/utils/vscode/src/lsp/protocol/protocol.go
@@ -19,7 +19,7 @@
 	"encoding/json"
 	"log"
 
-	"../jsonrpc2"
+	"github.com/KhronosGroup/SPIRV-Tools/utils/vscode/src/lsp/jsonrpc2"
 )
 
 const (
diff --git a/utils/vscode/src/lsp/protocol/span.go b/utils/vscode/src/lsp/protocol/span.go
index 33cc2a6..799c228 100644
--- a/utils/vscode/src/lsp/protocol/span.go
+++ b/utils/vscode/src/lsp/protocol/span.go
@@ -19,7 +19,8 @@
 import (
 	"fmt"
 
-	"../span"
+	"github.com/KhronosGroup/SPIRV-Tools/utils/vscode/src/lsp/span"
+
 	errors "golang.org/x/xerrors"
 )
 
diff --git a/utils/vscode/src/lsp/protocol/tsclient.go b/utils/vscode/src/lsp/protocol/tsclient.go
index 2f9beef..f68d63d 100644
--- a/utils/vscode/src/lsp/protocol/tsclient.go
+++ b/utils/vscode/src/lsp/protocol/tsclient.go
@@ -19,7 +19,7 @@
 	"encoding/json"
 	"log"
 
-	"../jsonrpc2"
+	"github.com/KhronosGroup/SPIRV-Tools/utils/vscode/src/lsp/jsonrpc2"
 )
 
 type Client interface {
diff --git a/utils/vscode/src/lsp/protocol/tsserver.go b/utils/vscode/src/lsp/protocol/tsserver.go
index d760501..37e8c6a 100644
--- a/utils/vscode/src/lsp/protocol/tsserver.go
+++ b/utils/vscode/src/lsp/protocol/tsserver.go
@@ -19,7 +19,7 @@
 	"encoding/json"
 	"log"
 
-	"../jsonrpc2"
+	"github.com/KhronosGroup/SPIRV-Tools/utils/vscode/src/lsp/jsonrpc2"
 )
 
 type Server interface {
diff --git a/utils/vscode/src/parser/parser.go b/utils/vscode/src/parser/parser.go
index 260a616..cc6f333 100644
--- a/utils/vscode/src/parser/parser.go
+++ b/utils/vscode/src/parser/parser.go
@@ -23,7 +23,7 @@
 	"unicode"
 	"unicode/utf8"
 
-	"../schema"
+	"github.com/KhronosGroup/SPIRV-Tools/utils/vscode/src/schema"
 )
 
 // Type is an enumerator of token types.
diff --git a/utils/vscode/src/schema/schema.go b/utils/vscode/src/schema/schema.go
index 0fde3fe..ed02de4 100755
--- a/utils/vscode/src/schema/schema.go
+++ b/utils/vscode/src/schema/schema.go
@@ -12,7 +12,7 @@
 // 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
+// Generated by gen-grammar.go --template=./src/schema/schema.go.tmpl --out=./src/schema/schema.go
 // Do not modify this file directly.
 
 package schema
@@ -477,7 +477,7 @@
 		"OpTraceRayKHR": OpTraceRayKHR,
 		"OpTypeAccelerationStructureNV": OpTypeAccelerationStructureNV,
 		"OpTypeAccelerationStructureKHR": OpTypeAccelerationStructureKHR,
-		"OpTypeRayQueryKHR": OpTypeRayQueryKHR,
+		"OpTypeRayQueryProvisionalKHR": OpTypeRayQueryProvisionalKHR,
 		"OpRayQueryInitializeKHR": OpRayQueryInitializeKHR,
 		"OpRayQueryTerminateKHR": OpRayQueryTerminateKHR,
 		"OpRayQueryGenerateIntersectionKHR": OpRayQueryGenerateIntersectionKHR,
@@ -10807,8 +10807,8 @@
 			}, 
 		},
 	}
-	OpTypeRayQueryKHR = &Opcode {
-		Opname:   "OpTypeRayQueryKHR",
+	OpTypeRayQueryProvisionalKHR = &Opcode {
+		Opname:   "OpTypeRayQueryProvisionalKHR",
 		Class:    "Reserved",
 		Opcode:   4472,
 		Operands: []Operand {
@@ -20183,77 +20183,77 @@
 			Enumerant{
 				Enumerant:    "NoneKHR",
 				Value:        0x0000,
-				Capabilities: []string{"RayQueryKHR","RayTracingKHR",},
+				Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "",
 			},
 			Enumerant{
 				Enumerant:    "OpaqueKHR",
 				Value:        0x0001,
-				Capabilities: []string{"RayQueryKHR","RayTracingKHR",},
+				Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "",
 			},
 			Enumerant{
 				Enumerant:    "NoOpaqueKHR",
 				Value:        0x0002,
-				Capabilities: []string{"RayQueryKHR","RayTracingKHR",},
+				Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "",
 			},
 			Enumerant{
 				Enumerant:    "TerminateOnFirstHitKHR",
 				Value:        0x0004,
-				Capabilities: []string{"RayQueryKHR","RayTracingKHR",},
+				Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "",
 			},
 			Enumerant{
 				Enumerant:    "SkipClosestHitShaderKHR",
 				Value:        0x0008,
-				Capabilities: []string{"RayQueryKHR","RayTracingKHR",},
+				Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "",
 			},
 			Enumerant{
 				Enumerant:    "CullBackFacingTrianglesKHR",
 				Value:        0x0010,
-				Capabilities: []string{"RayQueryKHR","RayTracingKHR",},
+				Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "",
 			},
 			Enumerant{
 				Enumerant:    "CullFrontFacingTrianglesKHR",
 				Value:        0x0020,
-				Capabilities: []string{"RayQueryKHR","RayTracingKHR",},
+				Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "",
 			},
 			Enumerant{
 				Enumerant:    "CullOpaqueKHR",
 				Value:        0x0040,
-				Capabilities: []string{"RayQueryKHR","RayTracingKHR",},
+				Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "",
 			},
 			Enumerant{
 				Enumerant:    "CullNoOpaqueKHR",
 				Value:        0x0080,
-				Capabilities: []string{"RayQueryKHR","RayTracingKHR",},
+				Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "",
 			},
 			Enumerant{
 				Enumerant:    "SkipTrianglesKHR",
 				Value:        0x0100,
-				Capabilities: []string{"RayTraversalPrimitiveCullingKHR",},
+				Capabilities: []string{"RayTraversalPrimitiveCullingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "",
 			},
 			Enumerant{
 				Enumerant:    "SkipAABBsKHR",
 				Value:        0x0200,
-				Capabilities: []string{"RayTraversalPrimitiveCullingKHR",},
+				Capabilities: []string{"RayTraversalPrimitiveCullingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "",
 			},
@@ -20379,84 +20379,84 @@
 			Enumerant{
 				Enumerant:    "RayGenerationNV",
 				Value:        5313,
-				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "RayGenerationKHR",
 				Value:        5313,
-				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "IntersectionNV",
 				Value:        5314,
-				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "IntersectionKHR",
 				Value:        5314,
-				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "AnyHitNV",
 				Value:        5315,
-				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "AnyHitKHR",
 				Value:        5315,
-				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "ClosestHitNV",
 				Value:        5316,
-				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "ClosestHitKHR",
 				Value:        5316,
-				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "MissNV",
 				Value:        5317,
-				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "MissKHR",
 				Value:        5317,
-				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "CallableNV",
 				Value:        5318,
-				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "CallableKHR",
 				Value:        5318,
-				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
@@ -21044,84 +21044,84 @@
 			Enumerant{
 				Enumerant:    "CallableDataNV",
 				Value:        5328,
-				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "CallableDataKHR",
 				Value:        5328,
-				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "IncomingCallableDataNV",
 				Value:        5329,
-				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "IncomingCallableDataKHR",
 				Value:        5329,
-				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "RayPayloadNV",
 				Value:        5338,
-				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "RayPayloadKHR",
 				Value:        5338,
-				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "HitAttributeNV",
 				Value:        5339,
-				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "HitAttributeKHR",
 				Value:        5339,
-				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "IncomingRayPayloadNV",
 				Value:        5342,
-				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "IncomingRayPayloadKHR",
 				Value:        5342,
-				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "ShaderRecordBufferNV",
 				Value:        5343,
-				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "ShaderRecordBufferKHR",
 				Value:        5343,
-				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
@@ -22507,7 +22507,7 @@
 			Enumerant{
 				Enumerant:    "PrimitiveId",
 				Value:        7,
-				Capabilities: []string{"Geometry","Tessellation","RayTracingNV","RayTracingKHR",},
+				Capabilities: []string{"Geometry","Tessellation","RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "",
 			},
@@ -23053,203 +23053,203 @@
 			Enumerant{
 				Enumerant:    "LaunchIdNV",
 				Value:        5319,
-				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "LaunchIdKHR",
 				Value:        5319,
-				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "LaunchSizeNV",
 				Value:        5320,
-				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "LaunchSizeKHR",
 				Value:        5320,
-				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "WorldRayOriginNV",
 				Value:        5321,
-				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "WorldRayOriginKHR",
 				Value:        5321,
-				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "WorldRayDirectionNV",
 				Value:        5322,
-				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "WorldRayDirectionKHR",
 				Value:        5322,
-				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "ObjectRayOriginNV",
 				Value:        5323,
-				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "ObjectRayOriginKHR",
 				Value:        5323,
-				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "ObjectRayDirectionNV",
 				Value:        5324,
-				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "ObjectRayDirectionKHR",
 				Value:        5324,
-				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "RayTminNV",
 				Value:        5325,
-				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "RayTminKHR",
 				Value:        5325,
-				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "RayTmaxNV",
 				Value:        5326,
-				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "RayTmaxKHR",
 				Value:        5326,
-				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "InstanceCustomIndexNV",
 				Value:        5327,
-				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "InstanceCustomIndexKHR",
 				Value:        5327,
-				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "ObjectToWorldNV",
 				Value:        5330,
-				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "ObjectToWorldKHR",
 				Value:        5330,
-				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "WorldToObjectNV",
 				Value:        5331,
-				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "WorldToObjectKHR",
 				Value:        5331,
-				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "HitTNV",
 				Value:        5332,
-				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "HitTKHR",
 				Value:        5332,
-				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "HitKindNV",
 				Value:        5333,
-				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "HitKindKHR",
 				Value:        5333,
-				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "IncomingRayFlagsNV",
 				Value:        5351,
-				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "IncomingRayFlagsKHR",
 				Value:        5351,
-				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "RayGeometryIndexKHR",
 				Value:        5352,
-				Capabilities: []string{"RayTracingKHR",},
+				Capabilities: []string{"RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
@@ -23340,7 +23340,7 @@
 			Enumerant{
 				Enumerant:    "ShaderCallKHR",
 				Value:        6,
-				Capabilities: []string{"RayTracingKHR",},
+				Capabilities: []string{"RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "",
 			},
@@ -24080,16 +24080,16 @@
 				Version:      "1.4",
 			},
 			Enumerant{
-				Enumerant:    "RayQueryKHR",
+				Enumerant:    "RayQueryProvisionalKHR",
 				Value:        4471,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
-				Enumerant:    "RayTraversalPrimitiveCullingKHR",
+				Enumerant:    "RayTraversalPrimitiveCullingProvisionalKHR",
 				Value:        4478,
-				Capabilities: []string{"RayQueryKHR","RayTracingKHR",},
+				Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
@@ -24465,7 +24465,7 @@
 				Version:      "None",
 			},
 			Enumerant{
-				Enumerant:    "RayTracingKHR",
+				Enumerant:    "RayTracingProvisionalKHR",
 				Value:        5353,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
@@ -24579,14 +24579,14 @@
 			Enumerant{
 				Enumerant:    "RayQueryCandidateIntersectionKHR",
 				Value:        0,
-				Capabilities: []string{"RayQueryKHR",},
+				Capabilities: []string{"RayQueryProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "",
 			},
 			Enumerant{
 				Enumerant:    "RayQueryCommittedIntersectionKHR",
 				Value:        1,
-				Capabilities: []string{"RayQueryKHR",},
+				Capabilities: []string{"RayQueryProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "",
 			},
@@ -24600,21 +24600,21 @@
 			Enumerant{
 				Enumerant:    "RayQueryCommittedIntersectionNoneKHR",
 				Value:        0,
-				Capabilities: []string{"RayQueryKHR",},
+				Capabilities: []string{"RayQueryProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "",
 			},
 			Enumerant{
 				Enumerant:    "RayQueryCommittedIntersectionTriangleKHR",
 				Value:        1,
-				Capabilities: []string{"RayQueryKHR",},
+				Capabilities: []string{"RayQueryProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "",
 			},
 			Enumerant{
 				Enumerant:    "RayQueryCommittedIntersectionGeneratedKHR",
 				Value:        2,
-				Capabilities: []string{"RayQueryKHR",},
+				Capabilities: []string{"RayQueryProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "",
 			},
@@ -24628,14 +24628,14 @@
 			Enumerant{
 				Enumerant:    "RayQueryCandidateIntersectionTriangleKHR",
 				Value:        0,
-				Capabilities: []string{"RayQueryKHR",},
+				Capabilities: []string{"RayQueryProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "",
 			},
 			Enumerant{
 				Enumerant:    "RayQueryCandidateIntersectionAABBKHR",
 				Value:        1,
-				Capabilities: []string{"RayQueryKHR",},
+				Capabilities: []string{"RayQueryProvisionalKHR",},
 				Parameters:   []Parameter{},
 				Version:      "",
 			},
diff --git a/utils/vscode/src/tools/gen-grammar.go b/utils/vscode/src/tools/gen-grammar.go
index 200f695..a1289ef 100644
--- a/utils/vscode/src/tools/gen-grammar.go
+++ b/utils/vscode/src/tools/gen-grammar.go
@@ -31,7 +31,7 @@
 
 	"github.com/pkg/errors"
 
-	"../grammar"
+	"github.com/KhronosGroup/SPIRV-Tools/utils/vscode/src/grammar"
 )
 
 type grammarDefinition struct {
@@ -54,7 +54,7 @@
 			url:  "https://raw.githubusercontent.com/KhronosGroup/SPIRV-Headers/master/include/spirv/unified1/extinst.opencl.std.100.grammar.json",
 		}, {
 			name: "OpenCL.DebugInfo.100",
-			url:  "https://raw.githubusercontent.com/KhronosGroup/SPIRV-Tools/master/source/extinst.opencl.debuginfo.100.grammar.json",
+			url:  "https://raw.githubusercontent.com/KhronosGroup/SPIRV-Headers/master/include/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json",
 		},
 	}
 
@@ -106,7 +106,7 @@
 	for _, ext := range extensionGrammars {
 		root, err := parseGrammar(ext)
 		if err != nil {
-			return errors.Wrap(err, "Failed to parse extension grammar file")
+			return errors.Wrap(err, "Failed to parse extension grammar file: "+ext.name)
 		}
 		args.Extensions = append(args.Extensions, extension{Root: root, Name: ext.name})
 		args.All.Instructions = append(args.All.Instructions, root.Instructions...)